A complete 3D game development suite written purely in Java.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jmonkeyengine/sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java

509 lines
20 KiB

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.gde.scenecomposer;
import com.jme3.asset.AssetManager;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingVolume;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.GhostControl;
import com.jme3.bullet.control.PhysicsControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.control.VehicleControl;
import com.jme3.bullet.util.DebugShapeFactory;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
import com.jme3.gde.core.undoredo.SceneUndoRedoManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Ray;
import com.jme3.math.Transform;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.debug.WireBox;
import com.jme3.scene.shape.Quad;
import java.util.concurrent.Callable;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
/**
*
* @author Brent Owens
*/
public abstract class SceneEditTool {
protected SceneComposerToolController toolController;
protected AssetManager manager;
protected Camera camera;
private boolean overrideCameraControl = false; // if true, you cannot pan/zoom unless you hold SHIFT
// the key to load the tool hint text from the resource bundle
protected String toolHintTextKey = "SceneComposerTopComponent.toolHint.default"; // not used yet
protected Spatial selectedSpatial;
protected Spatial selectionShape;
protected Node toolNode;
protected Node onTopToolNode;
protected Node axisMarker;
protected Material redMat, blueMat, greenMat, yellowMat, cyanMat, magentaMat, orangeMat;
protected enum AxisMarkerPickType {axisOnly, planeOnly, axisAndPlane};
protected AxisMarkerPickType axisPickType;
/**
* The tool was selected, start showing the marker.
* @param manager
* @param toolNode: parent node that the marker will attach to
*/
public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
this.manager = manager;
this.toolController = toolController;
this.selectedSpatial = selectedSpatial;
addMarker(toolNode, onTopToolNode);
}
protected void addMarker(Node toolNode, Node onTopToolNode) {
this.toolNode = toolNode;
this.onTopToolNode = onTopToolNode;
if (axisMarker == null) {
axisMarker = createAxisMarker();
}
axisMarker.removeFromParent();
this.onTopToolNode.attachChild(axisMarker);
setDefaultAxisMarkerColors();
// create and add the selection shape
if (selectionShape != null)
selectionShape.removeFromParent();
selectionShape = createSelectionShape(toolNode, selectedSpatial);
if (selectionShape != null) {
setDefaultSelectionShapeColors();
this.toolNode.attachChild(selectionShape);
axisMarker.setLocalTranslation(selectedSpatial.getWorldTranslation());
selectionShape.setLocalTranslation(selectedSpatial.getWorldTranslation());
}
}
protected void replaceSelectionShape(Spatial spatial) {
if (spatial != null) {
if (selectionShape != null)
selectionShape.removeFromParent();
selectedSpatial = spatial;
toolController.setSelected(spatial);
selectionShape = createSelectionShape(toolNode, selectedSpatial);
setDefaultSelectionShapeColors();
toolNode.attachChild(selectionShape);
}
else {
if (selectionShape != null)
selectionShape.removeFromParent();
selectionShape = null;
}
}
/**
* Remove the marker from it's parent (the tools node)
*/
public void hideMarker() {
if (axisMarker != null)
axisMarker.removeFromParent();
if (selectionShape != null)
selectionShape.removeFromParent();
}
public boolean isOverrideCameraControl() {
return overrideCameraControl;
}
public void setOverrideCameraControl(boolean overrideCameraControl) {
this.overrideCameraControl = overrideCameraControl;
}
/**
* Called when the selected spatial has been modified
* outside of the tool.
*/
public void updateToolsTransformation(final Spatial spatial) {
if (selectionShape == null)
return;
// has anything changed?
if (!selectionShape.getLocalTranslation().equals(spatial.getWorldTranslation()) &&
!selectionShape.getLocalRotation().equals(spatial.getWorldRotation()) &&
!selectionShape.getLocalScale().equals(spatial.getWorldScale()))
return;
// something has updated, so update the tools
selectionShape.setLocalTranslation(spatial.getWorldTranslation());
selectionShape.setLocalRotation(spatial.getWorldRotation());
selectionShape.setLocalScale(selectedSpatial.getWorldScale());
SceneApplication.getApplication().enqueue(new Callable<Object>() {
public Object call() throws Exception {
axisMarker.setLocalTranslation(spatial.getWorldTranslation());
axisMarker.setLocalRotation(selectedSpatial.getWorldRotation());
return null;
}
});
}
/**
* The primary action for the tool gets activated
*/
public abstract void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject);
/**
* The secondary action for the tool gets activated
*/
public abstract void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject);
/**
* Called when the mouse is moved but not dragged (ie no buttons are pressed)
*/
public abstract void mouseMoved(Vector2f screenCoord);
/**
* Called when the mouse is moved while the primary button is down
*/
public abstract void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject);
/**
* Called when the mouse is moved while the secondary button is down
*/
public abstract void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject);
/**
* Call when an action is performed that requires the scene to be saved
* and an undo can be performed
* @param undoer your implementation, probably with a begin and end state for undoing
*/
protected void actionPerformed(AbstractUndoableSceneEdit undoer) {
Lookup.getDefault().lookup(SceneUndoRedoManager.class).addEdit(this, undoer);
toolController.setNeedsSave(true);
}
/**
* Given the mouse coordinates, pick the geometry that is closest to the camera.
* @param jmeRootNode to pick from
* @return the selected spatial, or null if nothing
*/
protected Spatial pickWorldSpatial(Camera cam, Vector2f mouseLoc, JmeNode jmeRootNode) {
Node rootNode = jmeRootNode.getLookup().lookup(Node.class);
CollisionResult cr = pick(cam, mouseLoc, rootNode);
if (cr != null)
return cr.getGeometry();
else
return null;
}
/**
* Given the mouse coordinate, pick the world location where the mouse intersects
* a geometry.
* @param jmeRootNode to pick from
* @return the location of the pick, or null if nothing collided with the mouse
*/
protected Vector3f pickWorldLocation(Camera cam, Vector2f mouseLoc, JmeNode jmeRootNode) {
Node rootNode = jmeRootNode.getLookup().lookup(Node.class);
return pickWorldLocation(cam, mouseLoc, rootNode);
}
protected Vector3f pickWorldLocation(Camera cam, Vector2f mouseLoc, Node rootNode) {
CollisionResult cr = pick(cam, mouseLoc, rootNode);
if (cr != null)
return cr.getContactPoint();
else
return null;
}
/**
* Pick a part of the axis marker. The result is a Vector3f that represents
* what part of the axis was selected.
* For example if (1,0,0) is returned, then the X-axis pole was selected.
* If (0,1,1) is returned, then the Y-Z plane was selected.
*
* @return null if it did not intersect the marker
*/
protected Vector3f pickAxisMarker(Camera cam, Vector2f mouseLoc, AxisMarkerPickType pickType) {
if (axisMarker == null)
return null;
CollisionResult cr = pick(cam, mouseLoc, axisMarker);
if (cr == null || cr.getGeometry() == null)
return null;
if (pickType == AxisMarkerPickType.planeOnly) {
if ("quadXY".equals(cr.getGeometry().getName()) ) {
return new Vector3f(1,1,0);
} else if ("quadXZ".equals(cr.getGeometry().getName()) ) {
return new Vector3f(1,0,1);
} else if ("quadYZ".equals(cr.getGeometry().getName()) ) {
return new Vector3f(0,1,1);
}
}
else if (pickType == AxisMarkerPickType.axisOnly) {
if ("arrowX".equals(cr.getGeometry().getName()) ) {
return new Vector3f(1,0,0);
} else if ("arrowY".equals(cr.getGeometry().getName()) ) {
return new Vector3f(0,1,0);
} else if ("arrowZ".equals(cr.getGeometry().getName()) ) {
return new Vector3f(0,1,0);
}
} else if (pickType == AxisMarkerPickType.axisAndPlane) {
if ("arrowX".equals(cr.getGeometry().getName()) ) {
return new Vector3f(1,0,0);
} else if ("arrowY".equals(cr.getGeometry().getName()) ) {
return new Vector3f(0,1,0);
} else if ("arrowZ".equals(cr.getGeometry().getName()) ) {
return new Vector3f(0,1,0);
} else if ("quadXY".equals(cr.getGeometry().getName()) ) {
return new Vector3f(1,1,0);
} else if ("quadXZ".equals(cr.getGeometry().getName()) ) {
return new Vector3f(1,0,1);
} else if ("quadYZ".equals(cr.getGeometry().getName()) ) {
return new Vector3f(0,1,1);
}
}
return null;
}
private CollisionResult pick(Camera cam, Vector2f mouseLoc, Node node) {
CollisionResults results = new CollisionResults();
Ray ray = new Ray();
Vector3f pos = cam.getWorldCoordinates(mouseLoc, 0).clone();
Vector3f dir = cam.getWorldCoordinates(mouseLoc, 0.1f).clone();
dir.subtractLocal(pos).normalizeLocal();
ray.setOrigin(pos);
ray.setDirection(dir);
node.collideWith(ray, results);
CollisionResult result = results.getClosestCollision();
return result;
}
/**
* Show what axis or plane the mouse is currently over and will affect.
* @param axisMarkerPickType
*/
protected void highlightAxisMarker(Camera camera, Vector2f screenCoord, AxisMarkerPickType axisMarkerPickType) {
setDefaultAxisMarkerColors();
Vector3f picked = pickAxisMarker(camera, screenCoord, axisPickType);
if (picked == null)
return;
if (picked.equals(new Vector3f(1,0,0)))
axisMarker.getChild("arrowX").setMaterial(orangeMat);
else if (picked.equals(new Vector3f(0,1,0)))
axisMarker.getChild("arrowY").setMaterial(orangeMat);
else if (picked.equals(new Vector3f(0,0,1)))
axisMarker.getChild("arrowZ").setMaterial(orangeMat);
else if (picked.equals(new Vector3f(1,1,0)))
axisMarker.getChild("quadXY").setMaterial(orangeMat);
else if (picked.equals(new Vector3f(1,0,1)))
axisMarker.getChild("quadXZ").setMaterial(orangeMat);
else if (picked.equals(new Vector3f(0,1,1)))
axisMarker.getChild("quadYZ").setMaterial(orangeMat);
}
/**
* Create the axis marker that is selectable
*/
protected Node createAxisMarker() {
float size = 2;
float arrowSize = size;
float planeSize = size*0.7f;
Quaternion YAW090 = new Quaternion().fromAngleAxis(-FastMath.PI/2, new Vector3f(0,1,0));
Quaternion PITCH090 = new Quaternion().fromAngleAxis(FastMath.PI/2, new Vector3f(1,0,0));
redMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
redMat.getAdditionalRenderState().setWireframe(true);
redMat.setColor("Color", ColorRGBA.Red);
//redMat.getAdditionalRenderState().setDepthTest(false);
greenMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
greenMat.getAdditionalRenderState().setWireframe(true);
greenMat.setColor("Color", ColorRGBA.Green);
//greenMat.getAdditionalRenderState().setDepthTest(false);
blueMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
blueMat.getAdditionalRenderState().setWireframe(true);
blueMat.setColor("Color", ColorRGBA.Blue);
//blueMat.getAdditionalRenderState().setDepthTest(false);
yellowMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
yellowMat.getAdditionalRenderState().setWireframe(false);
yellowMat.setColor("Color", new ColorRGBA(1f, 1f, 0f, 0.25f));
yellowMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
yellowMat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
//yellowMat.getAdditionalRenderState().setDepthTest(false);
cyanMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
cyanMat.getAdditionalRenderState().setWireframe(false);
cyanMat.setColor("Color", new ColorRGBA(0f, 1f, 1f, 0.25f));
cyanMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
cyanMat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
//cyanMat.getAdditionalRenderState().setDepthTest(false);
magentaMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
magentaMat.getAdditionalRenderState().setWireframe(false);
magentaMat.setColor("Color", new ColorRGBA(1f, 0f, 1f, 0.25f));
magentaMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
magentaMat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
//magentaMat.getAdditionalRenderState().setDepthTest(false);
orangeMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
orangeMat.getAdditionalRenderState().setWireframe(false);
orangeMat.setColor("Color", new ColorRGBA(251f/255f, 130f/255f, 0f, 0.4f));
orangeMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
orangeMat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
Node axis = new Node();
// create arrows
Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize,0,0)));
Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0,arrowSize,0)));
Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0,0,arrowSize)));
axis.attachChild(arrowX);
axis.attachChild(arrowY);
axis.attachChild(arrowZ);
// create planes
Geometry quadXY = new Geometry("quadXY", new Quad(planeSize, planeSize) );
Geometry quadXZ = new Geometry("quadXZ", new Quad(planeSize, planeSize) );
quadXZ.setLocalRotation(PITCH090);
Geometry quadYZ = new Geometry("quadYZ", new Quad(planeSize, planeSize) );
quadYZ.setLocalRotation(YAW090);
axis.attachChild(quadXY);
axis.attachChild(quadXZ);
axis.attachChild(quadYZ);
axis.setModelBound(new BoundingBox());
return axis;
}
protected void setDefaultAxisMarkerColors() {
axisMarker.getChild("arrowX").setMaterial(redMat);
axisMarker.getChild("arrowY").setMaterial(blueMat);
axisMarker.getChild("arrowZ").setMaterial(greenMat);
axisMarker.getChild("quadXY").setMaterial(yellowMat);
axisMarker.getChild("quadXZ").setMaterial(magentaMat);
axisMarker.getChild("quadYZ").setMaterial(cyanMat);
}
protected void setDefaultSelectionShapeColors() {
if (selectionShape != null) {
Material mat = new Material(SceneApplication.getApplication().getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.getAdditionalRenderState().setWireframe(true);
mat.setColor("Color", new ColorRGBA(0.8f,0.8f,0.8f,0.3f));
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
selectionShape.setMaterial(mat);
}
}
protected Spatial createSelectionShape(Node toolNode, Spatial spat) {
if (spat == null)
return null;
if (selectionShape != null) {
selectionShape.removeFromParent();
selectionShape = null;
}
if (spat instanceof Geometry) {
return getGeometrySelection(toolNode, (Geometry) spat);
} else if (spat.getControl(PhysicsControl.class) != null) {
return getPhysicsSelection(toolNode, spat);
} else {
return getBoxSelection(toolNode, spat);
}
}
protected Geometry getGeometrySelection(Node toolNode, Geometry geom) {
Mesh mesh = geom.getMesh();
if (mesh == null) {
return null;
}
Geometry selectionGeometry = new Geometry("selection_geometry_sceneviewer", mesh);
selectionGeometry.setLocalTransform(geom.getWorldTransform());
toolNode.attachChild(selectionGeometry);
return selectionGeometry;
}
protected Geometry getBoxSelection(Node toolNode, Spatial geom) {
BoundingVolume bound = geom.getWorldBound();
if (bound instanceof BoundingBox) {
BoundingBox bbox = (BoundingBox) bound;
Vector3f extent = new Vector3f();
bbox.getExtent(extent);
WireBox wireBox=new WireBox();
wireBox.fromBoundingBox(bbox);
Geometry selectionGeometry = new Geometry("selection_geometry_sceneviewer", wireBox);
selectionGeometry.setLocalTransform(geom.getWorldTransform());
toolNode.attachChild(selectionGeometry);
return selectionGeometry;
}
return null;
}
protected Spatial getPhysicsSelection(Node toolNode, Spatial geom) {
PhysicsCollisionObject control = geom.getControl(RigidBodyControl.class);
if (control == null) {
control = geom.getControl(VehicleControl.class);
}
if (control == null) {
control = geom.getControl(GhostControl.class);
}
if (control == null) {
control = geom.getControl(CharacterControl.class);
}
if (control == null) {
return null;
}
Spatial selectionGeometry = DebugShapeFactory.getDebugShape(control.getCollisionShape());
if (selectionGeometry != null) {
selectionGeometry.setLocalTransform(geom.getWorldTransform());
toolNode.attachChild(selectionGeometry);
return selectionGeometry;
}
return null;
}
protected void detachSelectionShape() {
if (selectionShape != null) {
selectionShape.removeFromParent();
selectionShape = null;
}
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
}