Map Editor

@Sploreg Here is brief of my work on last the couple of weeks:


  • Adjusted Level and Slope tool when the precision was turned on to apply the UNDO aciton correctly.
  • Added an option to change the current primary marker to a Box, so you can adjust things with more precision when needed.
  • Updated the current existing tools so this work correctly.
  • Removed unecessary computation when precision was turned on.
  • Updated Slope tool so now it creates a real slope, instead of create a hole. Now the angle is correctly calculated no matter the distance from the current point to the vector represented by one marker minus the other. This works perfectly with the ‘lock’ option turned on (maybe this should be changed to just work with that).
  • Fixed a bug to prevent throwing exception while trying to erase the “absolute” field on Level tool.
  • Fixed a small bug on the Tools action that was using only Z radiusStep on the for loops for both X and Z.
  • Fixed not updating when you checked Absolute on Level tool.
  • Added a Border tool to create borders on the Terrain.



    TODO:


  • Fix the texture painting to work with the BOX mesh.
  • Create a way to do things in staight line (like pressing shift on windows Paint while drawning with pencil).



    Here goes a link patch diff for the current modifications. https://dl.dropbox.com/u/3279456/newpatch.diff
1 Like

@Sploreg did you see the patch diff?

yea I’m applying it as we speak. I’ve got lots of broken builds and leftover fiddling I have to undo, I have no clue what I was playing with before I went on holidays :stuck_out_tongue:

The three border tool classes are missing from the patch. They are listed there but have no code, I guess cause they aren’t added in your local svn, so they might have to be posted separately.

What does the border tool do?

Also could you post some tooltips for the checkbox fields of the tools, in particular: Precision, Absolute, Lock? I can add them in.

The slope tool is working great by the way.

@Sploreg Ok, got fixed. The svn was saying that the Border classes were already part of version control (long time I don’t use SVN, didn’t remeber how to get these common problems fixed)



The border tool creates a border on the terrain by raising the edges of the terrain, usefull for creating borders (can’t describe better then that!)



The toooltips were added, and if you edit one, then all tooltips of the same type will be updated. They all share the same key in the Bundle file (please, check typo, as I don’t have a good english)



Yeah, finally the Slope tool is working pretty neat! I have already started creating some good levels with it! I just loved the precision checkbox, using it in the Slope tool and in the Level tool (and the absolute) I get some pretty good control on the terrain!



Now, the other thing I loved was the Square mesh for the primary marker, this has helped me a lot on creating some symetrich terrains.



I was thinking in placing somewhere a panel to get ‘Extra Tools’, where the non-general tools would be located. For example, the Border tool isn’t very usefull in most cases, maybe it doesn’t deserve a place along with the other tools. Same might be applied for the Square mesh.



TODO:


  • Fix the texture painting to work with the BOX mesh.
  • Create a way to do things in staight line (like pressing shift on windows Paint while drawning with pencil).



    Here goes a link patch diff for the current modifications (I just updated the previous link, I hope it doesn’t break something there. If it does, revert the modifications from the last diff please). https://dl.dropbox.com/u/3279456/newpatch.diff
2 Likes

Thanks man.

Alright I committed most of the changes. A few notes/issues/thoughts:

  • I renamed a couple fields on the UI
  • The, now named ‘contain’, checkbox is a great idea. It only works on one end though, the other end (upper end) is not bounded.
  • I removed the square box for now, only because I think we should think about adding custom shapes. I’m leaning towards ‘no’ for custom shapes, as I don’t see it being all that useful to invest the time, and they can do most of what they need with square and circle. I also want to juggle around the layout of the form first before I find a place to add it in. But it will get added for sure, it’s a useful feature.
  • The bounds tool is neat, but I don’t see it being part of the core terrain tool suite either. However I think adding a type of area bounds to most tools could be useful, I will have to think about how it will work with the tools and how much it would get used. In particular in terms of TerrainGrid which will be integrated into the SDK terrain editor, then the user could make bounded areas in many places.



    All in all, it’s really coming together.

    Keep up the great work! I will talk to Erlend about getting you a contributor tag soon :slight_smile:
@Sploreg said:
Thanks man.
Alright I committed most of the changes. A few notes/issues/thoughts:
- I renamed a couple fields on the UI
- The, now named 'contain', checkbox is a great idea. It only works on one end though, the other end (upper end) is not bounded.
- I removed the square box for now, only because I think we should think about adding custom shapes. I'm leaning towards 'no' for custom shapes, as I don't see it being all that useful to invest the time, and they can do most of what they need with square and circle. I also want to juggle around the layout of the form first before I find a place to add it in. But it will get added for sure, it's a useful feature.
- The bounds tool is neat, but I don't see it being part of the core terrain tool suite either. However I think adding a type of area bounds to most tools could be useful, I will have to think about how it will work with the tools and how much it would get used. In particular in terms of TerrainGrid which will be integrated into the SDK terrain editor, then the user could make bounded areas in many places.

All in all, it's really coming together.
Keep up the great work! I will talk to Erlend about getting you a contributor tag soon :)


I totally agree with the 2 shapes, square and circle, after thinknig a bit, the other shapes makes no sense. As you can almost single edit the heightmap with the minimum radius, and the square and circle are pretty generic, anyone can build anything with them.

And that's a good idea also to do with the bounds tool, as the current implementation is pretty specific.

I'll wait then to fix the texture pinting tool when the custom shapes is decided, for now I'll work on the straight lines feature. After that is done, I'll fix all tools to work correctly when a Translation/Rotation/Scale is applied, on both the terrain and the parent node, it's kind of broken for now.
1 Like

Already implemented the straight line feature for all tools ^^

1 Like

kickass! Send me the diff and I can add it in.

If you could put together some images or even a video to showcase the impact of these latest feature additions that would make it a lot easier to see up front what has improved. Also I would have something pretty to push out to the interwebs :stuck_out_tongue:

@erlend_sh said:
If you could put together some images or even a video to showcase the impact of these latest feature additions that would make it a lot easier to see up front what has improved. Also I would have something pretty to push out to the interwebs :P


Good idea! I'll do when I'm finished, I still have some tweaks to do! ^^

@Sploreg said:
kickass! Send me the diff and I can add it in.


Can you give your opinion if this is ok. I put the logic on TerrainTool on the same method that the sub-classes can override. Maybe this method should be final and then it would call the method that sub-classes can override, and the sub-classes can only change useStraightLine boolean.

[patch]Index: com/jme3/gde/terraineditor/tools/TerrainTool.java
===================================================================
--- com/jme3/gde/terraineditor/tools/TerrainTool.java (revisĂŁo 9682)
+++ com/jme3/gde/terraineditor/tools/TerrainTool.java (cĂłpia de trabalho)
@@ -34,6 +34,7 @@
import com.jme3.asset.AssetManager;
import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode;
import com.jme3.gde.terraineditor.ExtraToolParams;
+import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
@@ -62,6 +63,10 @@
protected float radius;
protected float weight;
protected float maxToolSize = 20; // override in sub classes
+ protected boolean useStraightLine = true;
+ private boolean doStraightline = false;
+ private Vector3f startPress;
+ private Vector3f axis;
private Meshes mesh;

public static enum Meshes {
@@ -94,7 +99,15 @@
/**
* Key was pressed.
*/
- public void keyPressed(KeyInputEvent kie) {}
+ public void keyPressed(KeyInputEvent kie) {
+ if (useStraightLine && kie.getKeyCode() == KeyInput.KEY_LCONTROL) {
+ doStraightline = kie.isPressed();
+ if (!doStraightline) { // Clean the values
+ startPress = null;
+ axis = null;
+ }
+ }
+ }

/**
* Location of the primary editor marker
@@ -111,8 +124,36 @@
* @param newLoc
*/
public void markerMoved(Vector3f newLoc) {
- if (markerPrimary != null)
- markerPrimary.setLocalTranslation(newLoc);
+ if (markerPrimary != null) {
+ if (!useStraightLine || (!doStraightline && useStraightLine))
+ markerPrimary.setLocalTranslation(newLoc); // if we're not using the straight line feature or the key isn't pressed
+ else {
+ if (startPress == null)
+ startPress = newLoc.clone(); // the user just started presseing
+ else {
+ Vector3f sub = newLoc.subtract(startPress);
+ if (axis == null) {
+ // grab the axis that the user is moving
+ Vector3f closest = Vector3f.UNIT_X;
+ float dist = sub.distance(closest);
+ float ddist;
+ if ((ddist = sub.distance(Vector3f.UNIT_Z)) < dist) {
+ closest = Vector3f.UNIT_Z;
+ dist = ddist;
+ }
+ if ((ddist = sub.distance(Vector3f.UNIT_Z.negate())) < dist) {
+ closest = Vector3f.UNIT_Z;
+ dist = ddist;
+ }
+ if (sub.distance(Vector3f.UNIT_X.negate()) < dist) {
+ closest = Vector3f.UNIT_X;
+ }
+ axis = closest;
+ }
+ markerPrimary.setLocalTranslation(sub.mult(axis).add(startPress)); // move the marker in straight line
+ }
+ }
+ }
}

/**
Index: com/jme3/gde/terraineditor/tools/SlopeTerrainTool.java
===================================================================
--- com/jme3/gde/terraineditor/tools/SlopeTerrainTool.java (revisĂŁo 9682)
+++ com/jme3/gde/terraineditor/tools/SlopeTerrainTool.java (cĂłpia de trabalho)
@@ -47,6 +47,7 @@
import com.jme3.scene.Spatial;
import com.jme3.scene.control.BillboardControl;
import com.jme3.scene.shape.Line;
+import com.jme3.scene.shape.Quad;
import com.jme3.scene.shape.Sphere;
import org.openide.loaders.DataObject;

@@ -123,6 +124,7 @@

@Override
public void keyPressed(KeyInputEvent kie) {
+ super.keyPressed(kie);
switch (kie.getKeyCode()) {
case KeyInput.KEY_LCONTROL:
leftCtrl = kie.isPressed();[/patch]
1 Like

I will change the ‘useStraightLine’ to a method useStraightLine() that the tools can override. To start it will return false in the TerrainTool since a munch of the tools won’t be able to use it. I can foresee it being a statefull method in the subclasses.

The actual angle code can be in the TerrainTool and be overridable, the way you have it now.

I knew you would have a better solution!

hehe



I just committed the changes, so do an update.

Would you be able to add 45 degree angle support too? I think that will cover most use cases for what people will want to do with snapped angles.

I’m out of ideas. I can’t solve the problem that happens on directions that isn’t 90Âș, this only happens in 45Âș, it just doesn’t go as I thought it would do it. It should be doing right, but somehow it isn’t travelling the correct lenght :confused:



[patch]# This patch file was generated by NetBeans IDE

It uses platform neutral UTF-8 encoding and n newlines.

— Base (BASE)

+++ Locally Modified (Based On LOCAL)

@@ -46,6 +46,8 @@

import com.jme3.scene.shape.Box;

import com.jme3.scene.shape.Sphere;

import com.jme3.util.IntMap.Entry;

+import java.util.HashMap;

+import java.util.Map;

import org.openide.loaders.DataObject;



/**

@@ -57,6 +59,14 @@

*/

public abstract class TerrainTool {


  • private static final Vector3f[] axisVectors = {
  •    Vector3f.UNIT_X, Vector3f.UNIT_Z,<br />
    
  •    Vector3f.UNIT_X.negate(), Vector3f.UNIT_Z.negate(),<br />
    
  •    Vector3f.UNIT_X.add(Vector3f.UNIT_Z),<br />
    
  •    Vector3f.UNIT_X.add(Vector3f.UNIT_Z.negate()),<br />
    
  •    Vector3f.UNIT_X.negate().addLocal(Vector3f.UNIT_Z.negate()),<br />
    
  •    Vector3f.UNIT_X.negate().addLocal(Vector3f.UNIT_Z)<br />
    
  • };

    protected AssetManager manager;

    protected Geometry markerPrimary;

    protected Geometry markerSecondary;

    @@ -67,6 +77,7 @@

    private Vector3f startPress;

    private Vector3f axis;

    private Meshes mesh;
  • private final Map<Vector3f, Float> cachedMap = new HashMap<Vector3f, Float>(); // caching only



    public static enum Meshes {

    Box, Sphere

    @@ -75,6 +86,11 @@

    // the key to load the tool hint text from the resource bundle

    protected String toolHintTextKey = "TerrainEditorTopComponent.toolHint.default";


  • public TerrainTool() {
  •    for (Vector3f v : axisVectors)<br />
    
  •        cachedMap.put(v, Float.MAX_VALUE);<br />
    
  • }

    +

    /**
  • The tool was selected, start showing the marker.
  • @param manager

    @@ -140,23 +156,22 @@

    Vector3f sub = newLoc.subtract(startPress);

    if (axis == null) {

    // grab the axis that the user is moving
  •                    Vector3f closest = Vector3f.UNIT_X;<br />
    
  •                    float dist = sub.distance(closest);<br />
    
  •                    float ddist;<br />
    
  •                    if ((ddist = sub.distance(Vector3f.UNIT_Z)) &lt; dist) {<br />
    
  •                        closest = Vector3f.UNIT_Z;<br />
    
  •                        dist = ddist;<br />
    
  •                    for (Vector3f v : cachedMap.keySet()) {<br />
    
  •                        cachedMap.put(v, sub.distance(v));<br />
    

}

  •                    if ((ddist = sub.distance(Vector3f.UNIT_Z.negate())) &lt; dist) {<br />
    
  •                        closest = Vector3f.UNIT_Z;<br />
    
  •                        dist = ddist;<br />
    
  •                    Vector3f closest = null;<br />
    
  •                    float dist = Float.MAX_VALUE;<br />
    
  •                    for (Map.Entry&lt;Vector3f,Float&gt; entry : cachedMap.entrySet()) {<br />
    
  •                        if (entry.getValue() &lt; dist) {<br />
    
  •                            dist = entry.getValue();<br />
    
  •                            closest = entry.getKey();<br />
    

}

  •                    if (sub.distance(Vector3f.UNIT_X.negate()) &lt; dist) {<br />
    
  •                        closest = Vector3f.UNIT_X;<br />
    

+

}

  •                    axis = closest;<br />
    
  •                    axis = closest.clone();<br />
    

}

  •                markerPrimary.setLocalTranslation(sub.mult(axis).add(startPress)); // move the marker in straight line<br />
    
  •                Vector3f add = startPress.add(sub.project(axis));<br />
    
  •                markerPrimary.setLocalTranslation(add); // move the marker in straight line<br />
    

}

}

}

[/patch]

Can aneone give me a hint why isn’t this working? I could swear doing markerPrimary.setLocalTranslation(sub.mult(axis).add(startPress)) would work, but for some reason, it doesn’t, and I don’t know why.



Ley me show you the relevant code. Assume that useStraightLine() returns true. startPress and axis are Vector3f in the class scope, and cachedMap is also in the class scope.



keyPressed() is called when a key is pressed, and MarkerMoved is called when the mouse moves in the screen.



[java]

private static final Vector3f[] axisVectors = {

Vector3f.UNIT_X, Vector3f.UNIT_Z,

Vector3f.UNIT_X.negate(), Vector3f.UNIT_Z.negate(),

Vector3f.UNIT_X.add(Vector3f.UNIT_Z),

Vector3f.UNIT_X.add(Vector3f.UNIT_Z.negate()),

Vector3f.UNIT_X.negate().addLocal(Vector3f.UNIT_Z.negate()),

Vector3f.UNIT_X.negate().addLocal(Vector3f.UNIT_Z)

};



public TerrainTool() {

for (Vector3f v : axisVectors)

cachedMap.put(v, Float.MAX_VALUE);

}



public void keyPressed(KeyInputEvent kie) {

if (useStraightLine() && (kie.getKeyCode() == KeyInput.KEY_LCONTROL || kie.getKeyCode() == KeyInput.KEY_RCONTROL)) {

doStraightline = kie.isPressed();

if (!doStraightline) { // Clean the values

startPress = null;

axis = null;

}

}

}



public void markerMoved(Vector3f newLoc) {

if (markerPrimary != null) {

if (!useStraightLine() || (!doStraightline && useStraightLine()))

markerPrimary.setLocalTranslation(newLoc); // if we’re not using the straight line feature or the key isn’t pressed

else {

if (startPress == null)

startPress = newLoc.clone(); // the user just started presseing

else {

Vector3f sub = newLoc.subtract(startPress);

if (axis == null) {

// grab the axis that the user is moving

for (Vector3f v : cachedMap.keySet()) {

cachedMap.put(v, sub.distance(v));

}

Vector3f closest = null;

float dist = Float.MAX_VALUE;

for (Map.Entry<Vector3f,Float> entry : cachedMap.entrySet()) {

if (entry.getValue() < dist) {

dist = entry.getValue();

closest = entry.getKey();

}



}

axis = closest.clone();

}

Vector3f add = startPress.add(sub.project(axis));

markerPrimary.setLocalTranslation(add); // move the marker in straight line

}

}

}

}

[/java]

I will take a look in a day, I just got swamped at work :confused:

@Sploreg I have set to an absolute distance, but I think that a good distance should be something about 0,5-1% of the terrain’s size, at minimum 1 and maximum 5. That’s why I set 3, it worked well for various terrain sizes.



[patch]# This patch file was generated by NetBeans IDE

It uses platform neutral UTF-8 encoding and n newlines.

— Base (BASE)

+++ Locally Modified (Based On LOCAL)

@@ -46,6 +46,8 @@

import com.jme3.scene.shape.Box;

import com.jme3.scene.shape.Sphere;

import com.jme3.util.IntMap.Entry;

+import java.util.HashMap;

+import java.util.Map;

import org.openide.loaders.DataObject;



/**

@@ -57,6 +59,16 @@

*/

public abstract class TerrainTool {


  • private static final Vector3f[] axisVectors = {
  •    Vector3f.UNIT_X,<br />
    
  •    Vector3f.UNIT_Z,<br />
    
  •    Vector3f.UNIT_X.negate(),<br />
    
  •    Vector3f.UNIT_Z.negate(),<br />
    
  •    Vector3f.UNIT_X.add(Vector3f.UNIT_Z).normalize(),<br />
    
  •    Vector3f.UNIT_X.add(Vector3f.UNIT_Z.negate()).normalize(),<br />
    
  •    Vector3f.UNIT_X.negate().addLocal(Vector3f.UNIT_Z.negate()).normalize(),<br />
    
  •    Vector3f.UNIT_X.negate().addLocal(Vector3f.UNIT_Z).normalize()<br />
    
  • };

    protected AssetManager manager;

    protected Geometry markerPrimary;

    protected Geometry markerSecondary;

    @@ -67,6 +79,7 @@

    private Vector3f startPress;

    private Vector3f axis;

    private Meshes mesh;
  • private final Map<Vector3f, Float> cachedMap = new HashMap<Vector3f, Float>(); // caching only



    public static enum Meshes {

    Box, Sphere

    @@ -75,6 +88,11 @@

    // the key to load the tool hint text from the resource bundle

    protected String toolHintTextKey = "TerrainEditorTopComponent.toolHint.default";


  • public TerrainTool() {
  •    for (Vector3f v : axisVectors)<br />
    
  •        cachedMap.put(v, Float.MAX_VALUE);<br />
    
  • }

    +

    /**
  • The tool was selected, start showing the marker.
  • @param manager

    @@ -138,25 +156,24 @@

    startPress = newLoc.clone(); // the user just started presseing

    else {

    Vector3f sub = newLoc.subtract(startPress);
  •                if (axis == null) {<br />
    
  •                if (axis == null &amp;&amp; newLoc.distance(startPress) &gt; 3f) {<br />
    

// grab the axis that the user is moving

  •                    Vector3f closest = Vector3f.UNIT_X;<br />
    
  •                    float dist = sub.distance(closest);<br />
    
  •                    float ddist;<br />
    
  •                    if ((ddist = sub.distance(Vector3f.UNIT_Z)) &lt; dist) {<br />
    
  •                        closest = Vector3f.UNIT_Z;<br />
    
  •                        dist = ddist;<br />
    
  •                    for (Vector3f v : cachedMap.keySet()) {<br />
    
  •                        cachedMap.put(v, sub.distance(v));<br />
    

}

  •                    if ((ddist = sub.distance(Vector3f.UNIT_Z.negate())) &lt; dist) {<br />
    
  •                        closest = Vector3f.UNIT_Z;<br />
    
  •                        dist = ddist;<br />
    
  •                    Vector3f closest = null;<br />
    
  •                    float dist = Float.MAX_VALUE;<br />
    
  •                    for (Map.Entry&lt;Vector3f,Float&gt; entry : cachedMap.entrySet()) {<br />
    
  •                        if (entry.getValue() &lt; dist) {<br />
    
  •                            dist = entry.getValue();<br />
    
  •                            closest = entry.getKey();<br />
    

}

  •                    if (sub.distance(Vector3f.UNIT_X.negate()) &lt; dist) {<br />
    
  •                        closest = Vector3f.UNIT_X;<br />
    

+

}

  •                    axis = closest;<br />
    
  •                    axis = closest.clone();<br />
    

}

  •                markerPrimary.setLocalTranslation(sub.mult(axis).add(startPress)); // move the marker in straight line<br />
    
  •                if (axis != null)<br />
    
  •                    markerPrimary.setLocalTranslation(startPress.add(sub.project(axis))); // move the marker in straight line<br />
    

}

}

}

[/patch]

1 Like

So what might be going on is that the 4 vectors on the axis are always going to be slightly closer than the vectors at 45 degrees, so they will always be picked first. Try normalizing the four 45 degree points and then they will have a fair chance at the distance test. You could also check and make sure the marker has moved at least “A” amount before testing against the axis; I’m not sure the best way to go about doing that, maybe just a counter so that it takes the 2nd position of the marker and not the first since the first movement might be very slight and not directly the angle you want to go.