diff --git a/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/AbstractStatefulGLToolAction.java b/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/AbstractStatefulGLToolAction.java new file mode 100644 index 000000000..cc58497a9 --- /dev/null +++ b/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/nodes/actions/AbstractStatefulGLToolAction.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.core.sceneexplorer.nodes.actions; + +import com.jme3.gde.core.scene.SceneApplication; +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit; +import com.jme3.gde.core.undoredo.SceneUndoRedoManager; +import java.util.concurrent.Callable; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import org.openide.loaders.DataObject; +import org.openide.util.Lookup; + +/** + * Similar as AbstractToolAction but this one is executed from the GL thread. + * This is also allowed to be stateful. + * + * @author Brent Owens + */ +public abstract class AbstractStatefulGLToolAction { + + protected String name = "*"; + + public void actionPerformed(final AbstractSceneExplorerNode rootNode, final DataObject dataObject) { + SceneApplication.getApplication().enqueue(new Callable() { + + public Void call() throws Exception { + doActionPerformed(rootNode, dataObject); + return null; + } + }); + } + + public void doActionPerformed(final AbstractSceneExplorerNode rootNode, final DataObject dataObject) { + + final Object object = doApplyTool(rootNode); + if (object!=null) { + Lookup lookup = Lookup.getDefault() ; + SceneUndoRedoManager manager = lookup.lookup(SceneUndoRedoManager.class); + + AbstractUndoableSceneEdit undoer = new AbstractUndoableSceneEdit() { + + @Override + public void sceneUndo() throws CannotUndoException { + doUndoTool(rootNode,object); + setModified(rootNode, dataObject); + } + + @Override + public void sceneRedo() throws CannotRedoException { + doApplyTool(rootNode); + setModified(rootNode, dataObject); + } + + }; + manager.addEdit(this, undoer); + setModified(rootNode, dataObject); + } + + } + + protected void setModified(final AbstractSceneExplorerNode rootNode, final DataObject dataObject) { + java.awt.EventQueue.invokeLater(new Runnable() { + + public void run() { + dataObject.setModified(true); + rootNode.refresh(true); + } + }); + } + + protected abstract Object doApplyTool(AbstractSceneExplorerNode rootNode); + + protected abstract void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject); +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties index 64ee8e3bf..07b2c49bd 100644 --- a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties @@ -99,9 +99,9 @@ TerrainEditorTopComponent.triPlanarCheckBox.toolTipText=Enable if you have a lot TerrainEditorTopComponent.triPlanarCheckBox.text=Tri-planar mapping TerrainEditorTopComponent.jButton1.text=Create Skybox TerrainEditorTopComponent.levelTerrainButton.text= +TerrainEditorTopComponent.levelTerrainButton.toolTipText=Level terrain 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. -TerrainEditorTopComponent.levelTerrainButton.toolTipText=Level terrain + diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorController.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorController.java index 9e27ec7c8..4cedc00fb 100644 --- a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorController.java +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorController.java @@ -38,9 +38,11 @@ import com.jme3.bounding.BoundingBox; import com.jme3.gde.core.assets.AssetDataObject; import com.jme3.gde.core.assets.ProjectAssetManager; import com.jme3.gde.core.scene.SceneApplication; +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial; +import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit; +import com.jme3.gde.core.undoredo.SceneUndoRedoManager; import com.jme3.material.MatParam; -import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; @@ -50,25 +52,25 @@ import com.jme3.terrain.ProgressMonitor; import com.jme3.terrain.Terrain; import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainQuad; -import com.jme3.texture.Image; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; import com.jme3.util.SkyFactory; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -import java.util.logging.Logger; import javax.imageio.ImageIO; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; import jme3tools.converters.ImageToAwt; import org.openide.cookies.SaveCookie; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.util.Exceptions; +import org.openide.util.Lookup; /** * Modifies the actual terrain in the scene. @@ -89,10 +91,6 @@ 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; - protected SaveCookie terrainSaveCookie = new SaveCookie() { @@ -133,7 +131,7 @@ public class TerrainEditorController { currentFileObject.setModified(state); } - protected Node getTerrain(Spatial root) { + public Node getTerrain(Spatial root) { if (terrainNode != null) return terrainNode; @@ -166,7 +164,7 @@ public class TerrainEditorController { * @param radius of the tool, terrain in this radius will be affected * @param heightFactor the amount to adjust the height by */ - protected void doModifyTerrainHeight(Vector3f worldLoc, float radius, float heightFactor) { + public void doModifyTerrainHeight(Vector3f worldLoc, float radius, float heightFactor) { Terrain terrain = (Terrain) getTerrain(null); if (terrain == null) @@ -180,6 +178,9 @@ public class TerrainEditorController { float xStepAmount = ((Node)terrain).getLocalScale().x; float zStepAmount = ((Node)terrain).getLocalScale().z; + List locs = new ArrayList(); + List heights = new ArrayList(); + for (int z=-radiusStepsZ; z() { public Terrain call() throws Exception { - return doCreateTerrain(parent, totalSize, patchSize, alphaTextureSize, heightmapData, sceneName); + return doCreateTerrain(parent, totalSize, patchSize, alphaTextureSize, heightmapData, sceneName, jmeNodeParent); } }).get(); return terrain; @@ -700,7 +706,8 @@ public class TerrainEditorController { int patchSize, int alphaTextureSize, float[] heightmapData, - String sceneName) throws IOException + String sceneName, + JmeSpatial jmeNodeParent) throws IOException { AssetManager manager = SceneApplication.getApplication().getAssetManager(); @@ -750,18 +757,52 @@ public class TerrainEditorController { // add the lod control List cameras = new ArrayList(); - cameras.add(SceneApplication.getApplication().getCamera()); + cameras.add(SceneApplication.getApplication().getCamera()); TerrainLodControl control = new TerrainLodControl(terrain, cameras); - //terrain.addControl(control); // removing this until we figure out a way to have it get the cameras when saved/loaded + //terrain.addControl(control); // removing this until we figure out a way to have it get the cameras when saved/loaded parent.attachChild(terrain); setNeedsSave(true); + addSpatialUndo(parent, terrain, jmeNodeParent); + return terrain; } - + private void addSpatialUndo(final Node undoParent, final Spatial undoSpatial, final AbstractSceneExplorerNode parentNode) { + //add undo + if (undoParent != null && undoSpatial != null) { + Lookup.getDefault().lookup(SceneUndoRedoManager.class).addEdit(this, new AbstractUndoableSceneEdit() { + + @Override + public void sceneUndo() throws CannotUndoException { + //undo stuff here + undoSpatial.removeFromParent(); + } + + @Override + public void sceneRedo() throws CannotRedoException { + //redo stuff here + undoParent.attachChild(undoSpatial); + } + + @Override + public void awtRedo() { + if (parentNode != null) { + parentNode.refresh(true); + } + } + + @Override + public void awtUndo() { + if (parentNode != null) { + parentNode.refresh(true); + } + } + }); + } + } /** * Save the terrain's alpha maps to disk, in the Textures/terrain-alpha/ directory @@ -894,181 +935,7 @@ public class TerrainEditorController { } return false; } - - /** - * Paint the texture at the specified location - * @param selectedTextureIndex the texture to paint - * @param markerLocation the location - * @param toolRadius radius of the brush tool - * @param toolWeight brush weight [0,1] - */ - public void doPaintTexture(int selectedTextureIndex, Vector3f markerLocation, float toolRadius, float toolWeight) { - if (selectedTextureIndex < 0 || markerLocation == null) - return; - - Terrain terrain = (Terrain) getTerrain(null); - if (terrain == null) - return; - - - setNeedsSave(true); - - Texture tex = doGetAlphaTextureFromDiffuse(terrain, selectedTextureIndex); - Image image = tex.getImage(); - - Vector2f UV = terrain.getPointPercentagePosition(markerLocation.x, markerLocation.z); - - // get the radius of the brush in pixel-percent - float brushSize = toolRadius/((TerrainQuad)terrain).getTotalSize(); - int texIndex = selectedTextureIndex - ((selectedTextureIndex/4)*4); // selectedTextureIndex/4 is an int floor, do not simplify the equation - boolean erase = toolWeight<0; - if (erase) - toolWeight *= -1; - - doPaintAction(texIndex, image, UV, true, brushSize, erase, toolWeight); - - tex.getImage().setUpdateNeeded(); - } - - /** - * Goes through each pixel in the image. At each pixel it looks to see if the UV mouse coordinate is within the - * of the brush. If it is in the brush radius, it gets the existing color from that pixel so it can add/subtract to/from it. - * Essentially it does a radius check and adds in a fade value. It does this to the color value returned by the - * first pixel color query. - * Next it sets the color of that pixel. If it was within the radius, the color will change. If it was outside - * the radius, then nothing will change, the color will be the same; but it will set it nonetheless. Not efficient. - * - * If the mouse is being dragged with the button down, then the dragged value should be set to true. This will reduce - * the intensity of the brush to 10% of what it should be per spray. Otherwise it goes to 100% opacity within a few pixels. - * This makes it work a little more realistically. - * - * @param image to manipulate - * @param uv the world x,z coordinate - * @param dragged true if the mouse button is down and it is being dragged, use to reduce brush intensity - * @param radius in percentage so it can be translated to the image dimensions - * @param erase true if the tool should remove the paint instead of add it - * @param fadeFalloff the percentage of the radius when the paint begins to start fading - */ - protected void doPaintAction(int texIndex, Image image, Vector2f uv, boolean dragged, float radius, boolean erase, float fadeFalloff){ - Vector2f texuv = new Vector2f(); - ColorRGBA color = ColorRGBA.Black; - - float width = image.getWidth(); - float height = image.getHeight(); - - int minx = (int) (uv.x*width - radius*width); // convert percents to pixels to limit how much we iterate - int maxx = (int) (uv.x*width + radius*width); - int miny = (int) (uv.y*height - radius*height); - int maxy = (int) (uv.y*height + radius*height); - - Logger.getLogger(TerrainEditorTopComponent.class.getName()).info("Paint "+uv ); - float radiusSquared = radius*radius; - float radiusFalloff = radius*fadeFalloff; - // go through each pixel, in the radius of the tool, in the image - for (int y = miny; y < maxy; y++){ - for (int x = minx; x < maxx; x++){ - - texuv.set((float)x / width, (float)y / height);// gets the position in percentage so it can compare with the mouse UV coordinate - - float dist = texuv.distanceSquared(uv); - if (dist < radiusSquared ) { // if the pixel is within the distance of the radius, set a color (distance times intensity) - manipulatePixel(image, x, y, color, false); // gets the color at that location (false means don't write to the buffer) - - // calculate the fade falloff intensity - float intensity = 0.1f; - if (dist > radiusFalloff) { - float dr = radius - radiusFalloff; // falloff to radius length - float d2 = dist - radiusFalloff; // dist minus falloff - d2 = d2/dr; // dist percentage of falloff length - intensity = 1-d2; // fade out more the farther away it is - } - - //if (dragged) - // intensity = intensity*0.1f; // magical divide it by 10 to reduce its intensity when mouse is dragged - - if (erase) { - switch (texIndex) { - case 0: - color.r -= intensity; break; - case 1: - color.g -= intensity; break; - case 2: - color.b -= intensity; break; - case 3: - color.a -= intensity; break; - } - } else { - switch (texIndex) { - case 0: - color.r += intensity; break; - case 1: - color.g += intensity; break; - case 2: - color.b += intensity; break; - case 3: - color.a += intensity; break; - } - } - color.clamp(); - - manipulatePixel(image, x, y, color, true); // set the new color - } - - } - } - - image.getData(0).rewind(); - } - - /** - * We are only using RGBA8 images for alpha textures right now. - * @param image to get/set the color on - * @param x location - * @param y location - * @param color color to get/set - * @param write to write the color or not - */ - protected void manipulatePixel(Image image, int x, int y, ColorRGBA color, boolean write){ - ByteBuffer buf = image.getData(0); - int width = image.getWidth(); - - int position = (y * width + x) * 4; - - if ( position> buf.capacity()-1 || position<0 ) - return; - - if (write) { - switch (image.getFormat()){ - case RGBA8: - buf.position( position ); - buf.put(float2byte(color.r)) - .put(float2byte(color.g)) - .put(float2byte(color.b)) - .put(float2byte(color.a)); - return; - default: - throw new UnsupportedOperationException("Image format: "+image.getFormat()); - } - } else { - switch (image.getFormat()){ - case RGBA8: - buf.position( position ); - color.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get())); - return; - default: - throw new UnsupportedOperationException("Image format: "+image.getFormat()); - } - } - - } - - private float byte2float(byte b){ - return ((float)(b & 0xFF)) / 255f; - } - - private byte float2byte(float f){ - return (byte) (f * 255f); - } + /** * How many textures are currently being used. @@ -1182,191 +1049,6 @@ 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 locs = new ArrayList(); - List heights = new ArrayList(); - - for (int z=-radiusStepsZ; z 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; - } - - /** - * Smooth bumps in the terrain by averaging the height in the tool radius. - * The smoothAmount affects how many neighbour points are averaged, The smaller - * the value, then only the smaller bumps will disappear. A large value will - * smooth larger hills - * @param markerLocation - * @param heightToolRadius - * @param smoothAmount - */ - protected void doSmoothTerrain(Vector3f worldLoc, float radius, float weight) { - 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 locs = new ArrayList(); - List heights = new ArrayList(); - - for (int z=-radiusStepsZ; z() { - - public Object call() throws Exception { - doHideEditTool(); - return null; - } - }); - } - - private void doHideEditTool() { - marker.removeFromParent(); - markerSmall.removeFromParent(); - editorController.doSetLevelTerrainDesiredHeight(null); + public void setTerrainEditButtonState(final TerrainTool tool) { + showEditTool(tool); + } - public void showEditTool(final TerrainEditButton terrainEditButton) { + public void showEditTool(final TerrainTool terrainEditButton) { SceneApplication.getApplication().enqueue(new Callable() { public Object call() throws Exception { @@ -190,25 +119,17 @@ public class TerrainToolController extends SceneToolController { /** * show different tool marker depending on terrainEditButton type - * @param state */ - private void doShowEditTool(TerrainEditButton state) { + private void doShowEditTool(TerrainTool tool) { + // remove the old tool markers + if (terrainTool != null) + terrainTool.hideMarkers(); - 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); + terrainTool = tool; + if (terrainTool != null) { + terrainTool.radiusChanged(toolRadius); + terrainTool.weightChanged(toolWeight); + terrainTool.activate(manager, toolsNode); } } @@ -223,57 +144,39 @@ public class TerrainToolController extends SceneToolController { } private void doSetEditToolSize(float size) { - for (Entry e: marker.getMesh().getBuffers()) - ((VertexBuffer)e.getValue()).resetObject(); - ((Sphere)marker.getMesh()).updateGeometry(8, 8, size); + if (terrainTool != null) + terrainTool.radiusChanged(size); } public void doMoveEditTool(Vector3f pos) { - if (marker != null) { - marker.setLocalTranslation(pos); - //System.out.println(marker.getLocalTranslation()); + if (terrainTool != null) { + terrainTool.markerMoved(pos); } } public Vector3f getMarkerLocation() { - if (marker != null) - return marker.getLocalTranslation(); - else - return null; + if (terrainTool != null) { + return terrainTool.getMarkerPrimaryLocation(); + } + return null; } public boolean isTerrainEditButtonEnabled() { - return getCurrentEditButtonState() != TerrainEditButton.none; + return terrainTool != null; } /** * Primary mouse button hit. - * raise/lower/paint the terrain + * raise/lower/paint... the terrain */ public void doTerrainEditToolActivated() { - if (TerrainEditButton.raiseTerrain == getCurrentEditButtonState() ) { - editorController.doModifyTerrainHeight(getMarkerLocation(), heightToolRadius, heightAmount); - } - else if (TerrainEditButton.lowerTerrain == getCurrentEditButtonState() ) { - editorController.doModifyTerrainHeight(getMarkerLocation(), heightToolRadius, -heightAmount); - } - else if (TerrainEditButton.smoothTerrain == getCurrentEditButtonState() ) { - editorController.doSmoothTerrain(getMarkerLocation(), heightToolRadius, smoothAmount); - } - else if (TerrainEditButton.levelTerrain == getCurrentEditButtonState() ) { - if (editorController.doGetLevelTerrainDesiredHeight() == null) { - Vector3f point = cameraController.getTerrainCollisionPoint(); - if (point != null) - editorController.doSetLevelTerrainDesiredHeight(point); + if (terrainTool != null) { + Vector3f point = getMarkerLocation(); + if (point != null) { + terrainTool.actionPrimary(point, selectedTextureIndex, jmeRootNode, editorController.getCurrentDataObject()); } - editorController.doLevelTerrain(getMarkerLocation(), heightToolRadius, levelAmount); - } - else if(TerrainEditButton.paintTerrain == getCurrentEditButtonState()) { - editorController.doPaintTexture(selectedTextureIndex, getMarkerLocation(), heightToolRadius, paintAmount); - } - else if (TerrainEditButton.eraseTerrain == getCurrentEditButtonState() ) { - editorController.doPaintTexture(selectedTextureIndex, getMarkerLocation(), heightToolRadius, -paintAmount); + } } @@ -281,14 +184,15 @@ public class TerrainToolController extends SceneToolController { * Alternate mouse button hit. */ public void doTerrainEditToolAlternateActivated() { - Logger.getLogger(this.getClass().getName()).info("Alternate tool activated "); - if (TerrainEditButton.levelTerrain == getCurrentEditButtonState() ) { - + + if (terrainTool != null) { Vector3f point = cameraController.getTerrainCollisionPoint(); if (point != null) { - editorController.doSetLevelTerrainDesiredHeight(point); - markerSmall.setLocalTranslation(point); + terrainTool.actionSecondary(point, selectedTextureIndex, jmeRootNode, editorController.getCurrentDataObject()); } + } + } + } diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/AbstractTerrainToolAction.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/AbstractTerrainToolAction.java new file mode 100644 index 000000000..64b891b1b --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/AbstractTerrainToolAction.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.actions.AbstractStatefulGLToolAction; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.terrain.Terrain; + +/** + * Helps find the terrain in the scene + * @author Brent Owens + */ +public abstract class AbstractTerrainToolAction extends AbstractStatefulGLToolAction { + + protected Terrain getTerrain(Spatial root) { + + // is this the terrain? + if (root instanceof Terrain && root instanceof Node) { + return (Terrain)root; + } + + if (root instanceof Node) { + Node n = (Node) root; + for (Spatial c : n.getChildren()) { + if (c instanceof Node){ + Terrain res = getTerrain(c); + if (res != null) + return res; + } + } + } + + return null; + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/EraseTerrainTool.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/EraseTerrainTool.java new file mode 100644 index 000000000..a40a2a84a --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/EraseTerrainTool.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import org.openide.loaders.DataObject; + +/** + * Erase the texture on the terrain + * + * @author Brent Owens + */ +public class EraseTerrainTool extends TerrainTool { + + @Override + public void actionPrimary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + if (radius == 0 || weight == 0) + return; + PaintTerrainToolAction action = new PaintTerrainToolAction(point, radius, -weight, textureIndex); // negate the weight + action.doActionPerformed(rootNode, dataObject); + } + + @Override + public void actionSecondary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + // do nothing + } + + @Override + public void addMarkerPrimary(Node parent) { + super.addMarkerPrimary(parent); + markerPrimary.getMaterial().setColor("Color", ColorRGBA.Cyan); + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LevelTerrainTool.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LevelTerrainTool.java new file mode 100644 index 000000000..35f35d005 --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LevelTerrainTool.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.asset.AssetManager; +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import org.openide.loaders.DataObject; + +/** + * Level the terrain. Uses a desired height point set by the secondary + * action (right mouse button) and raises/lowers the terrain to that + * desired height. + * + * @author Brent Owens + */ +public class LevelTerrainTool extends TerrainTool { + + private Vector3f desiredHeight; + + + public LevelTerrainTool() { + toolHintTextKey = "TerrainEditorTopComponent.toolHint.level"; + } + + @Override + public void activate(AssetManager manager, Node parent) { + super.activate(manager, parent); + addMarkerSecondary(parent); + } + + @Override + public void actionPrimary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + if (radius == 0 || weight == 0) + return; + if (desiredHeight == null) + desiredHeight = point.clone(); + LevelTerrainToolAction action = new LevelTerrainToolAction(point, radius, weight, desiredHeight); + action.doActionPerformed(rootNode, dataObject); + } + + @Override + public void actionSecondary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + desiredHeight = point; + markerSecondary.setLocalTranslation(desiredHeight); + } + + @Override + public void addMarkerPrimary(Node parent) { + super.addMarkerPrimary(parent); + markerPrimary.getMaterial().setColor("Color", ColorRGBA.Red); + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LevelTerrainToolAction.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LevelTerrainToolAction.java new file mode 100644 index 000000000..96b862abd --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LevelTerrainToolAction.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.terrain.Terrain; +import java.util.ArrayList; +import java.util.List; + +/** + * Level the terrain to a desired height, executed from the OpenGL thread. + * 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 corrected with float epsilon testing. + * + * @author Brent Owens + */ +public class LevelTerrainToolAction extends AbstractTerrainToolAction { + + private Vector3f worldLoc; + private float radius; + private float height; + private Vector3f levelTerrainLocation; + + List undoLocs; + List undoHeights; + + public LevelTerrainToolAction(Vector3f markerLocation, float radius, float height, Vector3f levelTerrainLocation) { + this.worldLoc = markerLocation.clone(); + this.radius = radius; + this.height = height; + this.levelTerrainLocation = levelTerrainLocation; + name = "Level terrain"; + } + + @Override + protected Object doApplyTool(AbstractSceneExplorerNode rootNode) { + Terrain terrain = getTerrain(rootNode.getLookup().lookup(Node.class)); + if (terrain == null) + return null; + modifyHeight(terrain, radius, height); + return terrain; + } + + @Override + protected void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject) { + if (undoObject == null) + return; + if (undoLocs == null || undoHeights == null) + return; + resetHeight((Terrain)undoObject, undoLocs, undoHeights); + } + + private void modifyHeight(Terrain terrain, float radius, float height) { + if (levelTerrainLocation == null) + return; + + float desiredHeight = levelTerrainLocation.y; + + 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 locs = new ArrayList(); + List heights = new ArrayList(); + + for (int z=-radiusStepsZ; z desiredHeight) + adj = -1; + + adj *= radiusWeight * height; + + // test if adjusting too far and then cap it + if (adj > 0 && ToolUtils.floatGreaterThan((terrainHeightAtLoc + adj), desiredHeight, epsilon)) + adj = desiredHeight - terrainHeightAtLoc; + else if (adj < 0 && ToolUtils.floatLessThan((terrainHeightAtLoc + adj), desiredHeight, epsilon)) + adj = terrainHeightAtLoc - desiredHeight; + + if (!ToolUtils.floatEquals(adj, 0, 0.001f)) { + locs.add(terrainLoc); + heights.add(adj); + } + + } + } + } + undoLocs = locs; + undoHeights = heights; + + // 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 void resetHeight(Terrain terrain, List undoLocs, List undoHeights) { + List neg = new ArrayList(); + for (Float f : undoHeights) + neg.add( f * -1f ); + + terrain.adjustHeight(undoLocs, neg); + ((Node)terrain).updateModelBound(); + } + +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LowerTerrainTool.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LowerTerrainTool.java new file mode 100644 index 000000000..b8da41b5b --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/LowerTerrainTool.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import org.openide.loaders.DataObject; + +/** + * Lowers the terrain + * + * @author Brent Owens + */ +public class LowerTerrainTool extends TerrainTool { + + @Override + public void actionPrimary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + if (radius == 0 || weight == 0) + return; + RaiseTerrainToolAction action = new RaiseTerrainToolAction(point, radius, -weight); // negative weight + action.doActionPerformed(rootNode, dataObject); + } + + @Override + public void actionSecondary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + // no secondary option + } + + @Override + public void addMarkerPrimary(Node parent) { + super.addMarkerPrimary(parent); + markerPrimary.getMaterial().setColor("Color", ColorRGBA.Green); + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/PaintTerrainTool.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/PaintTerrainTool.java new file mode 100644 index 000000000..faa2b8cac --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/PaintTerrainTool.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import org.openide.loaders.DataObject; + +/** + * + * @author Brent Owens + */ +public class PaintTerrainTool extends TerrainTool { + + @Override + public void actionPrimary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + if (radius == 0 || weight == 0) + return; + PaintTerrainToolAction action = new PaintTerrainToolAction(point, radius, weight, textureIndex); + action.doActionPerformed(rootNode, dataObject); + } + + @Override + public void actionSecondary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + // do nothing + } + + @Override + public void addMarkerPrimary(Node parent) { + super.addMarkerPrimary(parent); + markerPrimary.getMaterial().setColor("Color", ColorRGBA.Cyan); + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/PaintTerrainToolAction.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/PaintTerrainToolAction.java new file mode 100644 index 000000000..7fb61ae33 --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/PaintTerrainToolAction.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.material.MatParam; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.terrain.Terrain; +import com.jme3.terrain.geomipmap.TerrainQuad; +import com.jme3.texture.Image; +import com.jme3.texture.Texture; +import java.nio.ByteBuffer; + +/** + * Paint the texture at the specified location. + * + * @author Brent Owens + */ +public class PaintTerrainToolAction extends AbstractTerrainToolAction { + + private Vector3f worldLoc; + private float radius; + private float weight; + private int selectedTextureIndex; + + public PaintTerrainToolAction(Vector3f markerLocation, float radius, float weight, int selectedTextureIndex) { + this.worldLoc = markerLocation.clone(); + this.radius = radius; + this.weight = weight; + this.selectedTextureIndex = selectedTextureIndex; + name = "Paint terrain"; + } + + @Override + protected Object doApplyTool(AbstractSceneExplorerNode rootNode) { + Terrain terrain = getTerrain(rootNode.getLookup().lookup(Node.class)); + if (terrain == null) + return null; + paintTexture(terrain, worldLoc, radius, weight, selectedTextureIndex); + return terrain; + } + + @Override + protected void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject) { + if (undoObject == null) + return; + paintTexture((Terrain)undoObject, worldLoc, radius, -weight, selectedTextureIndex); + } + + public void paintTexture(Terrain terrain, Vector3f markerLocation, float toolRadius, float toolWeight, int selectedTextureIndex) { + if (selectedTextureIndex < 0 || markerLocation == null) + return; + + int alphaIdx = selectedTextureIndex/4; // 4 = rgba = 4 textures + Texture tex = getAlphaTexture(terrain, alphaIdx); + Image image = tex.getImage(); + + Vector2f UV = terrain.getPointPercentagePosition(markerLocation.x, markerLocation.z); + + // get the radius of the brush in pixel-percent + float brushSize = toolRadius/((TerrainQuad)terrain).getTotalSize(); + int texIndex = selectedTextureIndex - ((selectedTextureIndex/4)*4); // selectedTextureIndex/4 is an int floor, do not simplify the equation + boolean erase = toolWeight<0; + if (erase) + toolWeight *= -1; + + doPaintAction(texIndex, image, UV, true, brushSize, erase, toolWeight); + + tex.getImage().setUpdateNeeded(); + } + + private Texture getAlphaTexture(Terrain terrain, int alphaLayer) { + if (terrain == null) + return null; + MatParam matParam = null; + if (alphaLayer == 0) + matParam = terrain.getMaterial().getParam("AlphaMap"); + else if(alphaLayer == 1) + matParam = terrain.getMaterial().getParam("AlphaMap_1"); + else if(alphaLayer == 2) + matParam = terrain.getMaterial().getParam("AlphaMap_2"); + + if (matParam == null || matParam.getValue() == null) { + return null; + } + Texture tex = (Texture) matParam.getValue(); + return tex; + } + + /** + * Goes through each pixel in the image. At each pixel it looks to see if the UV mouse coordinate is within the + * of the brush. If it is in the brush radius, it gets the existing color from that pixel so it can add/subtract to/from it. + * Essentially it does a radius check and adds in a fade value. It does this to the color value returned by the + * first pixel color query. + * Next it sets the color of that pixel. If it was within the radius, the color will change. If it was outside + * the radius, then nothing will change, the color will be the same; but it will set it nonetheless. Not efficient. + * + * If the mouse is being dragged with the button down, then the dragged value should be set to true. This will reduce + * the intensity of the brush to 10% of what it should be per spray. Otherwise it goes to 100% opacity within a few pixels. + * This makes it work a little more realistically. + * + * @param image to manipulate + * @param uv the world x,z coordinate + * @param dragged true if the mouse button is down and it is being dragged, use to reduce brush intensity + * @param radius in percentage so it can be translated to the image dimensions + * @param erase true if the tool should remove the paint instead of add it + * @param fadeFalloff the percentage of the radius when the paint begins to start fading + */ + protected void doPaintAction(int texIndex, Image image, Vector2f uv, boolean dragged, float radius, boolean erase, float fadeFalloff){ + Vector2f texuv = new Vector2f(); + ColorRGBA color = ColorRGBA.Black; + + float width = image.getWidth(); + float height = image.getHeight(); + + int minx = (int) (uv.x*width - radius*width); // convert percents to pixels to limit how much we iterate + int maxx = (int) (uv.x*width + radius*width); + int miny = (int) (uv.y*height - radius*height); + int maxy = (int) (uv.y*height + radius*height); + + float radiusSquared = radius*radius; + float radiusFalloff = radius*fadeFalloff; + // go through each pixel, in the radius of the tool, in the image + for (int y = miny; y < maxy; y++){ + for (int x = minx; x < maxx; x++){ + + texuv.set((float)x / width, (float)y / height);// gets the position in percentage so it can compare with the mouse UV coordinate + + float dist = texuv.distanceSquared(uv); + if (dist < radiusSquared ) { // if the pixel is within the distance of the radius, set a color (distance times intensity) + manipulatePixel(image, x, y, color, false); // gets the color at that location (false means don't write to the buffer) + + // calculate the fade falloff intensity + float intensity = 0.1f; + if (dist > radiusFalloff) { + float dr = radius - radiusFalloff; // falloff to radius length + float d2 = dist - radiusFalloff; // dist minus falloff + d2 = d2/dr; // dist percentage of falloff length + intensity = 1-d2; // fade out more the farther away it is + } + + //if (dragged) + // intensity = intensity*0.1f; // magical divide it by 10 to reduce its intensity when mouse is dragged + + if (erase) { + switch (texIndex) { + case 0: + color.r -= intensity; break; + case 1: + color.g -= intensity; break; + case 2: + color.b -= intensity; break; + case 3: + color.a -= intensity; break; + } + } else { + switch (texIndex) { + case 0: + color.r += intensity; break; + case 1: + color.g += intensity; break; + case 2: + color.b += intensity; break; + case 3: + color.a += intensity; break; + } + } + color.clamp(); + + manipulatePixel(image, x, y, color, true); // set the new color + } + + } + } + + image.getData(0).rewind(); + } + + /** + * We are only using RGBA8 images for alpha textures right now. + * @param image to get/set the color on + * @param x location + * @param y location + * @param color color to get/set + * @param write to write the color or not + */ + protected void manipulatePixel(Image image, int x, int y, ColorRGBA color, boolean write){ + ByteBuffer buf = image.getData(0); + int width = image.getWidth(); + + int position = (y * width + x) * 4; + + if ( position> buf.capacity()-1 || position<0 ) + return; + + if (write) { + switch (image.getFormat()){ + case RGBA8: + buf.position( position ); + buf.put(float2byte(color.r)) + .put(float2byte(color.g)) + .put(float2byte(color.b)) + .put(float2byte(color.a)); + return; + default: + throw new UnsupportedOperationException("Image format: "+image.getFormat()); + } + } else { + switch (image.getFormat()){ + case RGBA8: + buf.position( position ); + color.set(byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get()), byte2float(buf.get())); + return; + default: + throw new UnsupportedOperationException("Image format: "+image.getFormat()); + } + } + + } + + private float byte2float(byte b){ + return ((float)(b & 0xFF)) / 255f; + } + + private byte float2byte(float f){ + return (byte) (f * 255f); + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/RaiseTerrainTool.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/RaiseTerrainTool.java new file mode 100644 index 000000000..8fe8bc597 --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/RaiseTerrainTool.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.asset.AssetManager; +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import org.openide.loaders.DataObject; + +/** + * Raise the terrain + * + * @author Brent Owens + */ +public class RaiseTerrainTool extends TerrainTool { + + + @Override + public void actionPrimary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + if (radius == 0 || weight == 0) + return; + RaiseTerrainToolAction action = new RaiseTerrainToolAction(point, radius, weight); + action.doActionPerformed(rootNode, dataObject); + } + + @Override + public void actionSecondary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject) { + // no secondary option + } + + + @Override + public void addMarkerPrimary(Node parent) { + super.addMarkerPrimary(parent); + markerPrimary.getMaterial().setColor("Color", ColorRGBA.Green); + } + +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/RaiseTerrainToolAction.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/RaiseTerrainToolAction.java new file mode 100644 index 000000000..4fa23cf40 --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/RaiseTerrainToolAction.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.gde.terraineditor.tools; + +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.terrain.Terrain; +import java.util.ArrayList; +import java.util.List; + +/** + * Raise/lower the terrain, executed from the OpenGL thread. + * + * @author Brent Owens + */ +public class RaiseTerrainToolAction extends AbstractTerrainToolAction { + + private Vector3f worldLoc; + private float radius; + private float height; + + public RaiseTerrainToolAction(Vector3f markerLocation, float radius, float height) { + this.worldLoc = markerLocation.clone(); + this.radius = radius; + this.height = height; + name = "Raise terrain"; + } + + @Override + protected Object doApplyTool(AbstractSceneExplorerNode rootNode) { + Terrain terrain = getTerrain(rootNode.getLookup().lookup(Node.class)); + if (terrain == null) + return null; + modifyHeight(terrain, radius, height); + return terrain; + } + + @Override + protected void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject) { + if (undoObject == null) + return; + modifyHeight((Terrain)undoObject, radius, -height); + } + + private void modifyHeight(Terrain terrain, float radius, float heightDir) { + + 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 locs = new ArrayList(); + List heights = new ArrayList(); + + for (int z=-radiusStepsZ; z undoLocs; + List undoHeights; + + public SmoothTerrainToolAction(Vector3f markerLocation, float radius, float height) { + this.worldLoc = markerLocation.clone(); + this.radius = radius; + this.height = height; + name = "Smooth terrain"; + } + + @Override + protected Object doApplyTool(AbstractSceneExplorerNode rootNode) { + Terrain terrain = getTerrain(rootNode.getLookup().lookup(Node.class)); + if (terrain == null) + return null; + modifyHeight(terrain, radius, height); + return terrain; + } + + @Override + protected void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject) { + if (undoObject == null) + return; + if (undoLocs == null || undoHeights == null) + return; + resetHeight((Terrain)undoObject, undoLocs, undoHeights); + } + + private void modifyHeight(Terrain terrain, float radius, float height) { + + 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 locs = new ArrayList(); + List heights = new ArrayList(); + + for (int z=-radiusStepsZ; z undoLocs, List undoHeights) { + List neg = new ArrayList(); + for (Float f : undoHeights) + neg.add( f * -1f ); + + terrain.adjustHeight(undoLocs, neg); + ((Node)terrain).updateModelBound(); + } +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/TerrainTool.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/TerrainTool.java new file mode 100644 index 000000000..e6b8017e6 --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/TerrainTool.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.gde.terraineditor.tools; + +import com.jme3.asset.AssetManager; +import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.VertexBuffer; +import com.jme3.scene.shape.Sphere; +import com.jme3.util.IntMap.Entry; +import org.openide.loaders.DataObject; + +/** + * Modifies the terrain in some way. + * It has a primary and secondary action, activated from left and right mouse button respectively. + * It will also attach tool geometries to the scene so the user can see where they are editing. + * + * @author Brent Owens + */ +public abstract class TerrainTool { + + protected AssetManager manager; + protected Geometry markerPrimary; + protected Geometry markerSecondary; + protected float radius; + protected float weight; + protected float maxToolSize = 20; // override in sub classes + + // the key to load the tool hint text from the resource bundle + protected String toolHintTextKey = "TerrainEditorTopComponent.toolHint.default"; + + /** + * The tool was selected, start showing the marker. + * @param manager + * @param parent node that the marker will attach to + */ + public void activate(AssetManager manager, Node parent) { + this.manager = manager; + addMarkerPrimary(parent); + } + + /** + * The primary action for the tool gets activated + */ + public abstract void actionPrimary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject); + + /** + * The secondary action for the tool gets activated + */ + public abstract void actionSecondary(Vector3f point, int textureIndex, AbstractSceneExplorerNode rootNode, DataObject dataObject); + + /** + * Location of the primary editor marker + */ + public Vector3f getMarkerPrimaryLocation() { + if (markerPrimary != null) + return markerPrimary.getLocalTranslation(); + else + return null; + } + + /** + * Move the marker to a new location, usually follows the mouse + * @param newLoc + */ + public void markerMoved(Vector3f newLoc) { + if (markerPrimary != null) + markerPrimary.setLocalTranslation(newLoc); + } + + /** + * The radius of the tool has changed, so update the marker + * @param radius percentage of the max radius + */ + public void radiusChanged(float radius) { + this.radius = maxToolSize*radius; + + if (markerPrimary != null) { + for (Entry e: markerPrimary.getMesh().getBuffers()) + ((VertexBuffer)e.getValue()).resetObject(); + ((Sphere)markerPrimary.getMesh()).updateGeometry(8, 8, this.radius); + } + } + + /** + * The weight of the tool has changed. Optionally change + * the marker look. + * @param weight percent + */ + public void weightChanged(float weight) { + this.weight = weight; + } + + /** + * Create the primary marker mesh, follows the mouse. + * @param parent it will attach to + */ + public void addMarkerPrimary(Node parent) { + if (markerPrimary == null) { + markerPrimary = new Geometry("edit marker primary"); + Mesh m = new Sphere(8, 8, radius); + markerPrimary.setMesh(m); + Material mat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.getAdditionalRenderState().setWireframe(true); + markerPrimary.setMaterial(mat); + markerPrimary.setLocalTranslation(0,0,0); + mat.setColor("Color", ColorRGBA.LightGray); + } + parent.attachChild(markerPrimary); + } + + /** + * Create the secondary marker mesh, placed + * with the right mouse button. + * @param parent it will attach to + */ + public void addMarkerSecondary(Node parent) { + if (markerSecondary == null) { + markerSecondary = new Geometry("edit marker secondary"); + Mesh m2 = new Sphere(8, 8, 0.5f); + markerSecondary.setMesh(m2); + Material mat2 = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat2.getAdditionalRenderState().setWireframe(false); + markerSecondary.setMaterial(mat2); + markerSecondary.setLocalTranslation(0,0,0); + mat2.setColor("Color", ColorRGBA.Red); + } + parent.attachChild(markerSecondary); + } + + /** + * Remove the markers from the scene. + */ + public void hideMarkers() { + if (markerPrimary != null) + markerPrimary.removeFromParent(); + if (markerSecondary != null) + markerSecondary.removeFromParent(); + } + + public String getToolHintTextKey() { + return toolHintTextKey; + } + + public void setToolHintTextKey(String toolHintTextKey) { + this.toolHintTextKey = toolHintTextKey; + } + + +} diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/ToolUtils.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/ToolUtils.java new file mode 100644 index 000000000..20f905370 --- /dev/null +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/tools/ToolUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2009-2011 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.gde.terraineditor.tools; + +import com.jme3.math.Vector2f; + +/** + * Handy utilities for the editor tools + * @author Brent Owens + */ +public class ToolUtils { + + /** + * 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 + */ + public static 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 + * the radius it is). + * The farther from the center, the less the height will be. + * This produces a linear height falloff. + * @param radius of the tool + * @param heightFactor potential height value to be adjusted + * @param x location + * @param z location + * @return the adjusted height value + */ + public static float calculateHeight(float radius, float heightFactor, float x, float z) { + float val = calculateRadiusPercent(radius, x, z); + return heightFactor * val; + } + + public static 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 = 1f - val; + return val; + } + + public static 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; + } + + public static boolean floatEquals(float a, float b, float epsilon) { + return a == b ? true : Math.abs(a - b) < epsilon; + } + + public static boolean floatLessThan(float a, float b, float epsilon) { + return b - a > epsilon; + } + + public static boolean floatGreaterThan(float a, float b, float epsilon) { + return a - b > epsilon; + } +}