Browse Source

added level-terrain tool
moved hint text to resource bundle

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7649 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

bre..ns 14 years ago
parent
commit
860a73c6ca

+ 6 - 0
jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties

@@ -98,3 +98,9 @@ TerrainEditorTopComponent.paintingPanel.border.title=Painting
 TerrainEditorTopComponent.triPlanarCheckBox.toolTipText=Enable if you have a lot of vertical surfaces. It will look nice but lower performance
 TerrainEditorTopComponent.triPlanarCheckBox.text=Tri-planar mapping
 TerrainEditorTopComponent.jButton1.text=Create Skybox
+TerrainEditorTopComponent.levelTerrainButton.text=
+
+TerrainEditorTopComponent.toolHint.none=
+TerrainEditorTopComponent.toolHint.default=Switch between camera and tool controls by holding down SHIFT
+TerrainEditorTopComponent.toolHint.smooth=
+TerrainEditorTopComponent.toolHint.level=Right click to set desired height value, left click to adjust height to that desired value.

+ 13 - 7
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainCameraController.java

@@ -107,7 +107,7 @@ public class TerrainCameraController extends AbstractCameraController {
             movedR = true;
             if (isTerrainEditButtonEnabled() && !forceCameraControls) {
                 if (leftMouse)
-                    terrainEditToolActivated = true;//toolController.doTerrainEditToolActivated();
+                    terrainEditToolActivated = true;
             }
             else {
                 if (leftMouse) {
@@ -122,7 +122,7 @@ public class TerrainCameraController extends AbstractCameraController {
             movedR = true;
             if (isTerrainEditButtonEnabled() && !forceCameraControls) {
                 if (leftMouse)
-                    terrainEditToolActivated = true;//toolController.doTerrainEditToolActivated();
+                    terrainEditToolActivated = true;
             }
             else {
                 if (leftMouse) {
@@ -137,7 +137,7 @@ public class TerrainCameraController extends AbstractCameraController {
             movedR = true;
             if (isTerrainEditButtonEnabled() && !forceCameraControls) {
                 if (leftMouse)
-                    terrainEditToolActivated = true;//toolController.doTerrainEditToolActivated();
+                    terrainEditToolActivated = true;
             }
             else {
                 if (leftMouse) {
@@ -152,7 +152,7 @@ public class TerrainCameraController extends AbstractCameraController {
             movedR = true;
             if (isTerrainEditButtonEnabled() && !forceCameraControls) {
                 if (leftMouse)
-                    terrainEditToolActivated = true;//toolController.doTerrainEditToolActivated();
+                    terrainEditToolActivated = true;
             }
             else {
                 if (leftMouse) {
@@ -181,7 +181,12 @@ public class TerrainCameraController extends AbstractCameraController {
         if (button == 0) {
             if (isTerrainEditButtonEnabled() && !forceCameraControls) {
                 if (leftMouse)
-                    terrainEditToolActivated = true;//toolController.doTerrainEditToolActivated();
+                    terrainEditToolActivated = true;
+            }
+        }
+        if (button == 1) {
+            if (isTerrainEditButtonEnabled() && !forceCameraControls) {
+                toolController.doTerrainEditToolAlternateActivated();
             }
         }
     }
@@ -199,7 +204,8 @@ public class TerrainCameraController extends AbstractCameraController {
             if (lastModifyTime >= toolModifyRate) {
                 
                 lastModifyTime = 0;
-                toolController.doTerrainEditToolActivated();
+                if (terrainEditToolActivated)
+                    toolController.doTerrainEditToolActivated();
                 terrainEditToolActivated = false;
                 lastModifyTime = app.getContext().getTimer().getTime();
             }
@@ -222,7 +228,7 @@ public class TerrainCameraController extends AbstractCameraController {
     /**
      * Find where on the terrain the mouse intersects.
      */
-    private Vector3f getTerrainCollisionPoint() {
+    protected Vector3f getTerrainCollisionPoint() {
 
         if (editorController.getTerrain(null) == null)
             return null;

+ 140 - 50
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorController.java

@@ -88,8 +88,11 @@ public class TerrainEditorController {
     private final int BASE_TEXTURE_COUNT = NUM_ALPHA_TEXTURES; // add any others here, like a global specular map
     protected final int MAX_TEXTURE_LAYERS = 7-BASE_TEXTURE_COUNT; // 16 max, minus the ones we are reserving
 
+    // level terrain settings
+    private Vector3f levelTerrainDesiredHeight;
+    private float levelTerrainSnapThreshold = 0.01f;
 
-    //private DataObjectSaveNode alphaDataObject;
+    
 
     protected SaveCookie terrainSaveCookie = new SaveCookie() {
       public void save() throws IOException {
@@ -177,38 +180,38 @@ public class TerrainEditorController {
         float zStepAmount = ((Node)terrain).getLocalScale().z;
 
         for (int z=-radiusStepsZ; z<radiusStepsZ; z++) {
-			for (int x=-radiusStepsZ; x<radiusStepsX; x++) {
+            for (int x=-radiusStepsZ; x<radiusStepsX; x++) {
 
                 float locX = worldLoc.x + (x*xStepAmount);
                 float locZ = worldLoc.z + (z*zStepAmount);
 
-				if (isInRadius(locX-worldLoc.x,locZ-worldLoc.z,radius)) {
-                    // see if it is in the radius of the tool
-					float h = calculateHeight(radius, heightFactor, locX-worldLoc.x, locZ-worldLoc.z);
-
-					// increase the height
-					terrain.adjustHeight(new Vector2f(locX, locZ), h);
-				}
-			}
-		}
+                // see if it is in the radius of the tool
+                if (isInRadius(locX-worldLoc.x,locZ-worldLoc.z,radius)) {
+                    // adjust height based on radius of the tool
+                    float h = calculateHeight(radius, heightFactor, locX-worldLoc.x, locZ-worldLoc.z);
+                    // increase the height
+                    terrain.adjustHeight(new Vector2f(locX, locZ), h);
+                }
+            }
+        }
 
         ((Node)terrain).updateModelBound(); // or else we won't collide with it where we just edited
         
     }
 
     /**
-	 * See if the X,Y coordinate is in the radius of the circle. It is assumed
-	 * that the "grid" being tested is located at 0,0 and its dimensions are 2*radius.
-	 * @param x
-	 * @param z
-	 * @param radius
-	 * @return
-	 */
-	private boolean isInRadius(float x, float y, float radius) {
-		Vector2f point = new Vector2f(x,y);
-		// return true if the distance is less than equal to the radius
-		return Math.abs(point.length()) <= radius;
-	}
+     * See if the X,Y coordinate is in the radius of the circle. It is assumed
+     * that the "grid" being tested is located at 0,0 and its dimensions are 2*radius.
+     * @param x
+     * @param z
+     * @param radius
+     * @return
+     */
+    private boolean isInRadius(float x, float y, float radius) {
+        Vector2f point = new Vector2f(x,y);
+        // return true if the distance is less than equal to the radius
+        return Math.abs(point.length()) <= radius;
+    }
 
     /**
      * Interpolate the height value based on its distance from the center (how far along
@@ -222,18 +225,21 @@ public class TerrainEditorController {
      * @return the adjusted height value
      */
     private float calculateHeight(float radius, float heightFactor, float x, float z) {
-        // find percentage for each 'unit' in radius
+        float val = calculateRadiusPercent(radius, x, z);
+        return heightFactor * val;
+    }
+
+    private float calculateRadiusPercent(float radius, float x, float z) {
+         // find percentage for each 'unit' in radius
         Vector2f point = new Vector2f(x,z);
         float val = Math.abs(point.length()) / radius;
-        val = 1 - val;
-        return heightFactor * val;
-	}
+        val = 1f - val;
+        return val;
+    }
 
     public void cleanup() {
-        final Node node = jmeRootNode.getLookup().lookup(Node.class);
         terrainNode = null;
         rootNode = null;
-//        alphaDataObject = null;
     }
 
     /**
@@ -410,17 +416,6 @@ public class TerrainEditorController {
         */
     }
 
-    /*private void doSetAlphaTexture(int layer, Texture tex) {
-        int alphaIdx = layer/4; // 4 = rgba = 4 textures
-
-        Terrain terrain = (Terrain) getTerrain(null);
-        if (terrain == null)
-            return;
-        if (alphaIdx == 0)
-            terrain.getMaterial().setTexture("AlphaMap", tex);
-        else
-            terrain.getMaterial().setTexture("AlphaMap_"+alphaIdx, tex);
-    }*/
 
     /**
      * Set the diffuse texture at the specified layer.
@@ -760,21 +755,12 @@ public class TerrainEditorController {
 
         parent.attachChild(terrain);
 
-//        doCreateAlphaSaveDataObject();
-
         setNeedsSave(true);
 
         return terrain;
     }
 
-    public void saveAlphaImages(final Terrain terrain) {
-        SceneApplication.getApplication().enqueue(new Callable<Object>() {
-            public Object call() throws Exception {
-                doSaveAlphaImages(terrain);
-                return null;
-            }
-        });
-    }
+    
 
     /**
      * Save the terrain's alpha maps to disk, in the Textures/terrain-alpha/ directory
@@ -1195,6 +1181,110 @@ public class TerrainEditorController {
         setNeedsSave(true);
     }
 
+    /**
+     * Level the terrain to the desired height.
+     * It will pull down or raise terrain towards the desired height, still
+     * using the radius of the tool and the weight. There are some slight rounding
+     * errors that are coorected with float epsilon testing.
+     * @param markerLocation
+     * @param heightToolRadius
+     * @param heightAmount
+     */
+    protected void doLevelTerrain(Vector3f worldLoc, float radius, float heightWeight) {
+        if (levelTerrainDesiredHeight == null)
+            return;
+
+        float desiredHeight = levelTerrainDesiredHeight.y;
+
+        Terrain terrain = (Terrain) getTerrain(null);
+        if (terrain == null)
+            return;
+
+        setNeedsSave(true);
+
+        int radiusStepsX = (int)(radius / ((Node)terrain).getLocalScale().x);
+        int radiusStepsZ = (int)(radius / ((Node)terrain).getLocalScale().z);
+
+        float xStepAmount = ((Node)terrain).getLocalScale().x;
+        float zStepAmount = ((Node)terrain).getLocalScale().z;
+
+        List<Vector2f> locs = new ArrayList<Vector2f>();
+        List<Float> heights = new ArrayList<Float>();
+
+        for (int z=-radiusStepsZ; z<radiusStepsZ; z++) {
+            for (int x=-radiusStepsZ; x<radiusStepsX; x++) {
+
+                float locX = worldLoc.x + (x*xStepAmount);
+                float locZ = worldLoc.z + (z*zStepAmount);
+                
+                // see if it is in the radius of the tool
+                if (isInRadius(locX-worldLoc.x,locZ-worldLoc.z,radius)) {
+
+                    Vector2f terrainLoc = new Vector2f(locX, locZ);
+                    // adjust height based on radius of the tool
+                    float terrainHeightAtLoc = terrain.getHeightmapHeight(terrainLoc)*terrain.getSpatial().getWorldScale().y;
+                    float radiusWeight = calculateRadiusPercent(radius, locX-worldLoc.x, locZ-worldLoc.z);
+
+                    float epsilon = 0.1f*heightWeight; // rounding error for snapping
+                    
+                    float adj = 0;
+                    if (terrainHeightAtLoc < desiredHeight)
+                        adj = 1;
+                    else if (terrainHeightAtLoc > desiredHeight)
+                        adj = -1;
+                            
+                    adj *= radiusWeight * heightWeight;
+
+                    // test if adjusting too far and then cap it
+                    if (adj > 0 && floatGreaterThan((terrainHeightAtLoc + adj), desiredHeight, epsilon))
+                        adj = desiredHeight - terrainHeightAtLoc;
+                    else if (adj < 0 && floatLessThan((terrainHeightAtLoc + adj), desiredHeight, epsilon))
+                        adj = terrainHeightAtLoc - desiredHeight;
+  
+                    if (!floatEquals(adj, 0, 0.001f)) {
+                        locs.add(terrainLoc);
+                        heights.add(adj);
+                    }
+                    
+                }
+            }
+        }
+        // do the actual height adjustment
+        terrain.adjustHeight(locs, heights);
+        
+        ((Node)terrain).updateModelBound(); // or else we won't collide with it where we just edited
+
+    }
+
+    private int compareFloat(float a, float b, float epsilon) {
+        if (floatEquals(a, b, epsilon))
+            return 0;
+        else if (floatLessThan(a, b, epsilon))
+            return -1;
+        else
+            return 1;
+    }
+
+    private boolean floatEquals(float a, float b, float epsilon) {
+        return a == b ? true : Math.abs(a - b) < epsilon;
+    }
+
+    private boolean floatLessThan(float a, float b, float epsilon) {
+        return b - a > epsilon;
+    }
+
+    private boolean floatGreaterThan(float a, float b, float epsilon) {
+        return a - b > epsilon;
+    }
+
+    protected void doSetLevelTerrainDesiredHeight(Vector3f point) {
+        this.levelTerrainDesiredHeight = point;
+    }
+
+    public Vector3f doGetLevelTerrainDesiredHeight() {
+        return levelTerrainDesiredHeight;
+    }
+
 
 
 

+ 16 - 0
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorTopComponent.form

@@ -536,6 +536,22 @@
                 <Property name="enabled" type="boolean" value="false"/>
               </Properties>
             </Component>
+            <Component class="javax.swing.JToggleButton" name="levelTerrainButton">
+              <Properties>
+                <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+                  <Image iconType="3" name="/com/jme3/gde/terraineditor/icon_terrain-level.png"/>
+                </Property>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/jme3/gde/terraineditor/Bundle.properties" key="TerrainEditorTopComponent.levelTerrainButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+                <Property name="focusable" type="boolean" value="false"/>
+                <Property name="horizontalTextPosition" type="int" value="0"/>
+                <Property name="verticalTextPosition" type="int" value="3"/>
+              </Properties>
+              <Events>
+                <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="levelTerrainButtonActionPerformed"/>
+              </Events>
+            </Component>
             <Component class="javax.swing.JToolBar$Separator" name="jSeparator2">
             </Component>
             <Component class="javax.swing.JToggleButton" name="paintButton">

+ 39 - 10
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorTopComponent.java

@@ -179,9 +179,13 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
 
     private void setHintText(TerrainEditButton terrainEditButton) {
         if (TerrainEditButton.none.equals(terrainEditButton) )
-            hintTextArea.setText("");
+            hintTextArea.setText(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.toolHint.none"));
+        else if (TerrainEditButton.levelTerrain.equals(terrainEditButton) )
+            hintTextArea.setText(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.toolHint.level"));
+        else if (TerrainEditButton.smoothTerrain.equals(terrainEditButton) )
+            hintTextArea.setText(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.toolHint.smooth"));
         else
-            hintTextArea.setText("Switch between camera and tool controls by holding down SHIFT");
+            hintTextArea.setText(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.toolHint.default"));
     }
 
 
@@ -222,6 +226,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
         lowerTerrainButton = new javax.swing.JToggleButton();
         smoothTerrainButton = new javax.swing.JToggleButton();
         roughTerrainButton = new javax.swing.JToggleButton();
+        levelTerrainButton = new javax.swing.JToggleButton();
         jSeparator2 = new javax.swing.JToolBar.Separator();
         paintButton = new javax.swing.JToggleButton();
         eraseButton = new javax.swing.JToggleButton();
@@ -247,11 +252,11 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
         hintPanel.setLayout(hintPanelLayout);
         hintPanelLayout.setHorizontalGroup(
             hintPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 189, Short.MAX_VALUE)
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE)
         );
         hintPanelLayout.setVerticalGroup(
             hintPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 131, Short.MAX_VALUE)
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 151, Short.MAX_VALUE)
         );
 
         toolSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.toolSettingsPanel.border.title"))); // NOI18N
@@ -306,7 +311,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
                 .addGroup(toolSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addComponent(heightLabel)
                     .addComponent(heightSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addContainerGap(56, Short.MAX_VALUE))
+                .addContainerGap(76, Short.MAX_VALUE))
         );
 
         paintingPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.paintingPanel.border.title"))); // NOI18N
@@ -357,7 +362,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
             .addGroup(paintingPanelLayout.createSequentialGroup()
                 .addGroup(paintingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, paintingPanelLayout.createSequentialGroup()
-                        .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 277, Short.MAX_VALUE)
+                        .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 425, Short.MAX_VALUE)
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                         .addGroup(paintingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                             .addComponent(removeTextureButton, 0, 0, Short.MAX_VALUE)
@@ -366,7 +371,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
                         .addComponent(remainingTexTitleLabel)
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                         .addComponent(remainingTexturesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 78, Short.MAX_VALUE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 228, Short.MAX_VALUE)
                         .addComponent(triPlanarCheckBox)))
                 .addContainerGap())
         );
@@ -378,7 +383,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
                         .addComponent(addTextureButton)
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                         .addComponent(removeTextureButton))
-                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 106, Short.MAX_VALUE))
+                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 126, Short.MAX_VALUE))
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addGroup(paintingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(remainingTexTitleLabel)
@@ -419,7 +424,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
                 .addComponent(genEntropiesButton)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jButton1)
-                .addContainerGap(79, Short.MAX_VALUE))
+                .addContainerGap(99, Short.MAX_VALUE))
         );
 
         jToolBar1.setFloatable(false);
@@ -475,6 +480,18 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
         roughTerrainButton.setActionCommand(org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.roughTerrainButton.actionCommand")); // NOI18N
         roughTerrainButton.setEnabled(false);
         jToolBar1.add(roughTerrainButton);
+
+        levelTerrainButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/terraineditor/icon_terrain-level.png"))); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(levelTerrainButton, org.openide.util.NbBundle.getMessage(TerrainEditorTopComponent.class, "TerrainEditorTopComponent.levelTerrainButton.text")); // NOI18N
+        levelTerrainButton.setFocusable(false);
+        levelTerrainButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        levelTerrainButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        levelTerrainButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                levelTerrainButtonActionPerformed(evt);
+            }
+        });
+        jToolBar1.add(levelTerrainButton);
         jToolBar1.add(jSeparator2);
 
         terrainModButtonGroup.add(paintButton);
@@ -517,7 +534,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
                 .addComponent(terrainOpsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(hintPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 1019, Short.MAX_VALUE)
+            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 1261, Short.MAX_VALUE)
         );
         jPanel1Layout.setVerticalGroup(
             jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -645,6 +662,16 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
         addSpatial("Skybox");
     }//GEN-LAST:event_jButton1ActionPerformed
 
+    private void levelTerrainButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_levelTerrainButtonActionPerformed
+        if (levelTerrainButton.isSelected()) {
+            toolController.setTerrainEditButtonState(TerrainEditButton.levelTerrain);
+            setHintText(TerrainEditButton.levelTerrain);
+        } else {
+            toolController.setTerrainEditButtonState(TerrainEditButton.none);
+            setHintText(TerrainEditButton.none);
+        }
+    }//GEN-LAST:event_levelTerrainButtonActionPerformed
+
     // Variables declaration - do not modify//GEN-BEGIN:variables
     private javax.swing.JButton addTextureButton;
     private javax.swing.JButton createTerrainButton;
@@ -661,6 +688,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
     private javax.swing.JToolBar.Separator jSeparator1;
     private javax.swing.JToolBar.Separator jSeparator2;
     private javax.swing.JToolBar jToolBar1;
+    private javax.swing.JToggleButton levelTerrainButton;
     private javax.swing.JToggleButton lowerTerrainButton;
     private javax.swing.JToggleButton paintButton;
     private javax.swing.JPanel paintingPanel;
@@ -997,6 +1025,7 @@ public final class TerrainEditorTopComponent extends TopComponent implements Sce
             camController.setToolController(toolController);
             camController.setEditorController(editorController);
             toolController.setEditorController(editorController);
+            toolController.setCameraController(camController);
             editorController.setToolController(toolController);
 
             toolController.setHeightToolRadius(radiusSlider.getValue());

+ 78 - 13
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainToolController.java

@@ -63,13 +63,22 @@ public class TerrainToolController extends SceneToolController {
     private JmeSpatial jmeRootNode;
     private TerrainEditButton currentEditButtonState = TerrainEditButton.none;
     private Geometry marker;
+    private Geometry markerSmall;
     private TerrainEditorController editorController;
+    private TerrainCameraController cameraController;
     private float heightToolRadius;
-    private float heightToolHeight;
     private float heightAmount;
+    private float levelAmount;
     private float paintAmount;
     private int selectedTextureIndex = -1;
 
+    private final ColorRGBA terrainHeightColor = ColorRGBA.Green;
+    private final ColorRGBA terrainPaintColor = ColorRGBA.Yellow;
+    private final ColorRGBA terrainEraseColor = ColorRGBA.Cyan;
+    private final ColorRGBA terrainSmoothColor = ColorRGBA.Brown;
+    private final ColorRGBA terrainLevelColor = ColorRGBA.Orange;
+    private final ColorRGBA terrainLevelMarkColor = ColorRGBA.Red;
+
     public TerrainToolController(Node toolsNode, AssetManager manager, JmeNode rootNode) {
         super(toolsNode, manager);
         this.jmeRootNode = rootNode;
@@ -79,12 +88,16 @@ public class TerrainToolController extends SceneToolController {
         this.editorController = editorController;
     }
 
+    public void setCameraController(TerrainCameraController cameraController) {
+        this.cameraController = cameraController;
+    }
+
     /**
      * assumes [0,200]
      */
     public void setHeightToolHeight(float heightToolHeight) {
-        this.heightToolHeight = heightToolHeight;
         this.heightAmount = heightToolHeight/100f;
+        this.levelAmount = heightToolHeight/200f;
         this.paintAmount = heightToolHeight/200f;
     }
 
@@ -107,9 +120,18 @@ public class TerrainToolController extends SceneToolController {
         marker.setMesh(m);
         Material mat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
         mat.getAdditionalRenderState().setWireframe(true);
-        mat.setColor("Color", ColorRGBA.Green);
+        mat.setColor("Color", ColorRGBA.LightGray);
         marker.setMaterial(mat);
         marker.setLocalTranslation(0,0,0);
+
+        markerSmall = new Geometry("edit marker");
+        Mesh m2 = new Sphere(8, 8, 0.5f);
+        markerSmall.setMesh(m2);
+        Material mat2 = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat2.getAdditionalRenderState().setWireframe(false);
+        mat2.setColor("Color", ColorRGBA.Red);
+        markerSmall.setMaterial(mat2);
+        markerSmall.setLocalTranslation(0,0,0);
     }
 
     protected void setMarkerRadius(float radius) {
@@ -121,16 +143,18 @@ public class TerrainToolController extends SceneToolController {
     }
 
     public void setTerrainEditButtonState(final TerrainEditButton state) {
-
+        Logger.getLogger(this.getClass().getName()).info("Edit button state set: "+state);
         currentEditButtonState = state;
         if (state == TerrainEditButton.none) {
             hideEditTool();
         } else if (state == TerrainEditButton.raiseTerrain || state == TerrainEditButton.lowerTerrain) {
             showEditTool(state);
-            Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("TERRAIN HEIGHT state");
+        } else if (state == TerrainEditButton.levelTerrain) {
+            showEditTool(state);
+        } else if (state == TerrainEditButton.smoothTerrain) {
+            showEditTool(state);
         } else if (state == TerrainEditButton.paintTerrain || state == TerrainEditButton.eraseTerrain) {
             showEditTool(state);
-            Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("PAINT TERRAIN state");
         }
     }
 
@@ -147,6 +171,8 @@ public class TerrainToolController extends SceneToolController {
 
     private void doHideEditTool() {
         marker.removeFromParent();
+        markerSmall.removeFromParent();
+        editorController.doSetLevelTerrainDesiredHeight(null);
     }
 
     public void showEditTool(final TerrainEditButton terrainEditButton) {
@@ -160,12 +186,28 @@ public class TerrainToolController extends SceneToolController {
         
     }
 
-    private void doShowEditTool(TerrainEditButton terrainEditButton) {
-        //TODO show different tool marker depending on terrainEditButton type
+    /**
+     * show different tool marker depending on terrainEditButton type
+     * @param state
+     */
+    private void doShowEditTool(TerrainEditButton state) {
         
         toolsNode.attachChild(marker);
+        markerSmall.removeFromParent(); // reset, turn it off
+        editorController.doSetLevelTerrainDesiredHeight(null); // disable the level marker height
 
-        
+        if (state == TerrainEditButton.raiseTerrain || state == TerrainEditButton.lowerTerrain) {
+            marker.getMaterial().setColor("Color", terrainHeightColor);
+        } else if (state == TerrainEditButton.paintTerrain) {
+            marker.getMaterial().setColor("Color", terrainPaintColor);
+        } else if (state == TerrainEditButton.eraseTerrain) {
+            marker.getMaterial().setColor("Color", terrainEraseColor);
+        } else if (state == TerrainEditButton.levelTerrain) {
+            toolsNode.attachChild(markerSmall);
+            marker.getMaterial().setColor("Color", terrainLevelColor);
+        } else if (state == TerrainEditButton.smoothTerrain) {
+            marker.getMaterial().setColor("Color", terrainSmoothColor);
+        }
     }
 
     public void setEditToolSize(final float size) {
@@ -203,25 +245,48 @@ public class TerrainToolController extends SceneToolController {
     }
 
     /**
+     * Primary mouse button hit.
      * raise/lower/paint the terrain
      */
     public void doTerrainEditToolActivated() {
 
         if (TerrainEditButton.raiseTerrain == getCurrentEditButtonState() ) {
             editorController.doModifyTerrainHeight(getMarkerLocation(), heightToolRadius, heightAmount);
-            Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("terrain raise height");
         }
         else if (TerrainEditButton.lowerTerrain == getCurrentEditButtonState() ) {
             editorController.doModifyTerrainHeight(getMarkerLocation(), heightToolRadius, -heightAmount);
-            Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("terrain lower height");
+        }
+        else if (TerrainEditButton.smoothTerrain == getCurrentEditButtonState() ) {
+
+        }
+        else if (TerrainEditButton.levelTerrain == getCurrentEditButtonState() ) {
+            if (editorController.doGetLevelTerrainDesiredHeight() == null) {
+                Vector3f point = cameraController.getTerrainCollisionPoint();
+                if (point != null)
+                     editorController.doSetLevelTerrainDesiredHeight(point);
+            }
+            editorController.doLevelTerrain(getMarkerLocation(), heightToolRadius, levelAmount);
         }
         else if(TerrainEditButton.paintTerrain == getCurrentEditButtonState()) {
             editorController.doPaintTexture(selectedTextureIndex, getMarkerLocation(), heightToolRadius, paintAmount);
-            Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("terrain paint");
         }
         else if (TerrainEditButton.eraseTerrain == getCurrentEditButtonState() ) {
             editorController.doPaintTexture(selectedTextureIndex, getMarkerLocation(), heightToolRadius, -paintAmount);
-            Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("terrain erase");
+        }
+    }
+
+    /**
+     * Alternate mouse button hit.
+     */
+    public void doTerrainEditToolAlternateActivated() {
+        Logger.getLogger(this.getClass().getName()).info("Alternate tool activated ");
+        if (TerrainEditButton.levelTerrain == getCurrentEditButtonState() ) {
+            
+            Vector3f point = cameraController.getTerrainCollisionPoint();
+            if (point != null) {
+                editorController.doSetLevelTerrainDesiredHeight(point);
+                markerSmall.setLocalTranslation(point);
+            }
         }
     }
 }

BIN
jme3-terrain-editor/src/com/jme3/gde/terraineditor/icon_terrain-level.png