2
0
Эх сурвалжийг харах

* select and move tools added to sdk
* ability to easily add other tools to scene composer
* changed camera controller to handle more types of mouse events
* much refactoring of sceneComposerToolController

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

bre..ns 14 жил өмнө
parent
commit
16d2c3b843
20 өөрчлөгдсөн 1416 нэмэгдсэн , 103 устгасан
  1. 1 1
      jme3-core/src/com/jme3/gde/core/scene/SceneCameraController.java
  2. 165 21
      jme3-core/src/com/jme3/gde/core/scene/controller/AbstractCameraController.java
  3. 15 14
      jme3-core/src/com/jme3/gde/core/scene/controller/SceneToolController.java
  4. 1 1
      jme3-core/src/com/jme3/gde/core/util/ToggleButtonGroup.java
  5. 8 0
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/Bundle.properties
  6. 106 37
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/ComposerCameraController.java
  7. 179 0
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerToolController.java
  8. 99 5
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerTopComponent.form
  9. 90 8
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerTopComponent.java
  10. 508 0
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java
  11. BIN
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_arrow_inout.png
  12. BIN
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_arrow_out.png
  13. BIN
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_arrow_rotate_clockwise.png
  14. BIN
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_select.png
  15. 160 0
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/MoveTool.java
  16. 81 0
      jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java
  17. 1 5
      jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainCameraController.java
  18. 1 5
      jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorTopComponent.java
  19. 0 5
      jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainToolController.java
  20. 1 1
      jme3-vehicle-creator/src/com/jme3/gde/vehiclecreator/VehicleCreatorCameraController.java

+ 1 - 1
jme3-core/src/com/jme3/gde/core/scene/SceneCameraController.java

@@ -46,7 +46,7 @@ public class SceneCameraController extends AbstractCameraController {
     }
 
     @Override
-    protected void checkClick(int button) {
+    protected void checkClick(int button, boolean pressed) {
     }
 
 }

+ 165 - 21
jme3-core/src/com/jme3/gde/core/scene/controller/AbstractCameraController.java

@@ -51,6 +51,7 @@ import com.jme3.math.Vector3f;
 import com.jme3.renderer.Camera;
 import com.jme3.renderer.RenderManager;
 import java.util.concurrent.Callable;
+import java.util.logging.Logger;
 
 /**
  *
@@ -70,8 +71,16 @@ public abstract class AbstractCameraController extends AbstractAppState implemen
     protected Object master;
     protected boolean moved = false;
     protected boolean movedR = false;
-    protected boolean checkClick = false;
+    protected boolean buttonDownL = false;
+    protected boolean buttonDownR = false;
+    protected boolean checkClickL = false;
     protected boolean checkClickR = false;
+    protected boolean checkReleaseL = false;
+    protected boolean checkReleaseR = false;
+    protected boolean checkDragged = false;
+    protected boolean checkDraggedR = false;
+    protected boolean checkReleaseLeft = false;
+    protected boolean checkReleaseRight = false;
 
     public AbstractCameraController(Camera cam, InputManager inputManager) {
         this.cam = cam;
@@ -151,15 +160,22 @@ public abstract class AbstractCameraController extends AbstractAppState implemen
         cam.setLocation(loc);
     }
 
-    public void onAction(String string, boolean bln, float f) {
+    /*public void onAction(String string, boolean bln, float f) {
         if ("MouseButtonLeft".equals(string)) {
             if (bln) {
                 leftMouse = true;
                 moved = false;
             } else {
+                if (leftMouse)
+                    checkReleaseLeft = true;
                 leftMouse = false;
                 if (!moved) {
-                    checkClick = true;
+                    if (checkClick == false)
+                        checkClick = true;
+                    checkDragged = false;
+                } else {
+                    checkDragged = true;
+                    checkClick = false;
                 }
             }
         }
@@ -168,49 +184,56 @@ public abstract class AbstractCameraController extends AbstractAppState implemen
                 rightMouse = true;
                 movedR = false;
             } else {
+                if (rightMouse)
+                    checkReleaseRight = true;
                 rightMouse = false;
                 if (!movedR) {
-                    checkClickR = true;
+                    if (checkClickR == false)
+                        checkClickR = true;
+                    checkDraggedR = false;
+                } else {
+                    checkDraggedR = true;
+                    checkClickR = false;
                 }
             }
         }
-    }
+    }*/
 
     public void onAnalog(String string, float f1, float f) {
         if ("MouseAxisX".equals(string)) {
             moved = true;
             movedR = true;
-            if (leftMouse) {
+            if (buttonDownL) {
                 rotateCamera(Vector3f.UNIT_Y, -f1 * 2.5f);
             }
-            if (rightMouse) {
+            if (buttonDownR) {
                 panCamera(f1 * 2.5f, 0);
             }
         } else if ("MouseAxisY".equals(string)) {
             moved = true;
             movedR = true;
-            if (leftMouse) {
+            if (buttonDownL) {
                 rotateCamera(cam.getLeft(), -f1 * 2.5f);
             }
-            if (rightMouse) {
+            if (buttonDownR) {
                 panCamera(0, -f1 * 2.5f);
             }
         } else if ("MouseAxisX-".equals(string)) {
             moved = true;
             movedR = true;
-            if (leftMouse) {
+            if (buttonDownL) {
                 rotateCamera(Vector3f.UNIT_Y, f1 * 2.5f);
             }
-            if (rightMouse) {
+            if (buttonDownR) {
                 panCamera(-f1 * 2.5f, 0);
             }
         } else if ("MouseAxisY-".equals(string)) {
             moved = true;
             movedR = true;
-            if (leftMouse) {
+            if (buttonDownL) {
                 rotateCamera(cam.getLeft(), f1 * 2.5f);
             }
-            if (rightMouse) {
+            if (buttonDownR) {
                 panCamera(0, f1 * 2.5f);
             }
         } else if ("MouseWheel".equals(string)) {
@@ -219,6 +242,37 @@ public abstract class AbstractCameraController extends AbstractAppState implemen
             zoomCamera(-.1f);
         }
     }
+    
+    public void onAction(String string, boolean pressed, float f) {
+        if ("MouseButtonLeft".equals(string)) {
+            if (pressed) {
+                if (!buttonDownL) { // mouse clicked
+                    checkClickL = true;
+                    checkReleaseL = false;
+                }
+            } else {
+                if (buttonDownL) { // mouse released
+                    checkReleaseL = true;
+                    checkClickL = false;
+                }
+            }
+            buttonDownL = pressed;
+        }
+        if ("MouseButtonRight".equals(string)) {
+            if (pressed) {
+                if (!buttonDownR) { // mouse clicked
+                    checkClickR = true;
+                    checkReleaseR = false;
+                }
+            } else {
+                if (buttonDownR) { // mouse released
+                    checkReleaseR = true;
+                    checkClickR = false;
+                }
+            }
+            buttonDownR = pressed;
+        }
+    }
 
     public void onJoyAxisEvent(JoyAxisEvent jae) {
     }
@@ -242,39 +296,129 @@ public abstract class AbstractCameraController extends AbstractAppState implemen
 
     /**APPSTATE**/
     private boolean appInit = false;
+    
+    @Override
     public void initialize(AppStateManager asm, Application aplctn) {
         appInit = true;
     }
 
+    @Override
     public boolean isInitialized() {
         return appInit;
     }
 
+    @Override
     public void stateAttached(AppStateManager asm) {
     }
 
+    @Override
     public void stateDetached(AppStateManager asm) {
     }
 
+    @Override
     public void update(float f) {
-        if (checkClick) {
-            checkClick(0);
-            checkClick = false;
-        }
-        if (checkClickR) {
-            checkClick(1);
-            checkClickR = false;
+        if (moved) {
+            // moved, check for drags
+            if (checkReleaseL || checkReleaseR) {
+                // drag released
+                if (checkReleaseL)
+                    checkDragged(0, false);
+                if (checkReleaseR)
+                    checkDragged(1, false);
+                checkReleaseL = false;
+                checkReleaseR = false;
+            } else {
+                if (buttonDownL)
+                    checkDragged(0, true);
+                else if (buttonDownR)
+                    checkDragged(1, true);
+                else
+                    checkMoved(); // no dragging, just moved
+            }
+            
+            moved = false;
+        } else {
+            // not moved, check for just clicks
+            if (checkClickL) {
+                checkClick(0, true);
+                checkClickL = false;
+            }
+            if (checkReleaseL) {
+                checkClick(0, false);
+                checkReleaseL = false;
+            }
+            if (checkClickR) {
+                checkClick(1, true);
+                checkClickR = false;
+            }
+            if (checkReleaseR) {
+                checkClick(1, false);
+                checkReleaseR = false;
+            }
         }
+        
+        /*if (checkDragged || checkDraggedR) {
+            if (checkDragged) {
+                checkDragged(0);
+                checkReleaseLeft = false;
+                checkDragged = false;
+                checkClick = false;
+                checkClickR = false;
+            }
+            if (checkDraggedR) {
+                checkDragged(1);
+                checkReleaseRight = false;
+                checkDraggedR = false;
+                checkClick = false;
+                checkClickR = false;
+            }
+        } else {
+            if (checkClick) {
+                checkClick(0, checkReleaseLeft);
+                checkReleaseLeft = false;
+                checkClick = false;
+                checkDragged = false;
+                checkDraggedR = false;
+            }
+            if (checkClickR) {
+                checkClick(1, checkReleaseRight);
+                checkReleaseRight = false;
+                checkClickR = false;
+                checkDragged = false;
+                checkDraggedR = false;
+            }
+        }*/
     }
 
-    protected abstract void checkClick(int button);
+    /**
+     * mouse clicked, not dragged
+     * @param pressed true if pressed, false if released
+     */
+    protected abstract void checkClick(int button, boolean pressed);
+    
+    /**
+     * Mouse dragged while button is depressed
+     */
+    protected void checkDragged(int button, boolean pressed) {
+        // override in sub classes
+    }
+    
+    /**
+     * The mouse moved, no dragging or buttons pressed
+     */
+    protected void checkMoved() {
+        // override in subclasses
+    }
 
+    @Override
     public void render(RenderManager rm) {
     }
 
+    @Override
     public void postRender() {
     }
 
+    @Override
     public void cleanup() {
     }
 

+ 15 - 14
jme3-core/src/com/jme3/gde/core/scene/controller/SceneToolController.java

@@ -61,33 +61,34 @@ public class SceneToolController implements AppState {
     }
 
     protected void initTools() {
-        Material redMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
-        redMat.getAdditionalRenderState().setWireframe(true);
-        redMat.setColor("Color", ColorRGBA.Red);
+        //Material redMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+        //redMat.getAdditionalRenderState().setWireframe(true);
+        //redMat.setColor("Color", ColorRGBA.Red);
         Material greenMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
         greenMat.getAdditionalRenderState().setWireframe(true);
         greenMat.setColor("Color", ColorRGBA.Green);
-        Material blueMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
-        blueMat.getAdditionalRenderState().setWireframe(true);
-        blueMat.setColor("Color", ColorRGBA.Blue);
+        //Material blueMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+        //blueMat.getAdditionalRenderState().setWireframe(true);
+        //blueMat.setColor("Color", ColorRGBA.Blue);
         Material grayMat = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
         grayMat.getAdditionalRenderState().setWireframe(true);
         grayMat.setColor("Color", ColorRGBA.Gray);
-
+        
         //cursor
         if (cursor == null) {
             cursor = new Node();
         }
         cursor.detachAllChildren();
-        Geometry cursorArrowX = new Geometry("cursorArrowX", new Arrow(Vector3f.UNIT_X));
-        Geometry cursorArrowY = new Geometry("cursorArrowY", new Arrow(Vector3f.UNIT_Y));
-        Geometry cursorArrowZ = new Geometry("cursorArrowZ", new Arrow(Vector3f.UNIT_Z));
-        cursorArrowX.setMaterial(redMat);
+        //Geometry cursorArrowX = new Geometry("cursorArrowX", new Arrow(Vector3f.UNIT_X));
+        Geometry cursorArrowY = new Geometry("cursorArrowY", new Arrow(new Vector3f(0,-1,0)));
+        cursorArrowY.setLocalTranslation(0, 1, 0);
+        //Geometry cursorArrowZ = new Geometry("cursorArrowZ", new Arrow(Vector3f.UNIT_Z));
+        //cursorArrowX.setMaterial(redMat);
         cursorArrowY.setMaterial(greenMat);
-        cursorArrowZ.setMaterial(blueMat);
-        cursor.attachChild(cursorArrowX);
+        //cursorArrowZ.setMaterial(blueMat);
+        //cursor.attachChild(cursorArrowX);
         cursor.attachChild(cursorArrowY);
-        cursor.attachChild(cursorArrowZ);
+        //cursor.attachChild(cursorArrowZ);
         toolsNode.attachChild(cursor);
 
         //grid

+ 1 - 1
jme3-terrain-editor/src/com/jme3/gde/terraineditor/ToggleButtonGroup.java → jme3-core/src/com/jme3/gde/core/util/ToggleButtonGroup.java

@@ -30,7 +30,7 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-package com.jme3.gde.terraineditor;
+package com.jme3.gde.core.util;
 
 import javax.swing.AbstractButton;
 import javax.swing.ButtonGroup;

+ 8 - 0
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/Bundle.properties

@@ -44,3 +44,11 @@ SceneComposerTopComponent.jTextField1.text=10
 SceneComposerTopComponent.jLabel4.text=kg
 SceneComposerTopComponent.jButton1.text=make character
 SceneComposerTopComponent.fixedCheckBox.text=radius/height
+SceneComposerTopComponent.moveButton.text=
+SceneComposerTopComponent.rotateButton.text=
+SceneComposerTopComponent.scaleButton.text=
+SceneComposerTopComponent.selectButton.text=
+SceneComposerTopComponent.selectButton.toolTipText=Select
+SceneComposerTopComponent.moveButton.toolTipText=Move
+SceneComposerTopComponent.rotateButton.toolTipText=Rotate (in-development)
+SceneComposerTopComponent.scaleButton.toolTipText=Scale (in-development)

+ 106 - 37
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/ComposerCameraController.java

@@ -36,6 +36,8 @@ import com.jme3.collision.CollisionResults;
 import com.jme3.gde.core.scene.SceneApplication;
 import com.jme3.gde.core.scene.controller.AbstractCameraController;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.input.KeyInput;
+import com.jme3.input.event.KeyInputEvent;
 import com.jme3.math.Ray;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
@@ -50,6 +52,8 @@ public class ComposerCameraController extends AbstractCameraController {
 
     private Node rootNode;
     private JmeNode jmeRootNode;
+    private SceneComposerToolController toolController;
+    private boolean forceCameraControls = false; // when user holds shift, this is true
 
     public ComposerCameraController(Camera cam, JmeNode rootNode) {
         super(cam, SceneApplication.getApplication().getInputManager());
@@ -57,49 +61,114 @@ public class ComposerCameraController extends AbstractCameraController {
         this.rootNode = rootNode.getLookup().lookup(Node.class);
     }
 
-    public void checkClick(int button) {
+    private boolean isEditButtonEnabled() {
+        return toolController.isEditToolEnabled();
+    }
+
+    public void setToolController(SceneComposerToolController toolController) {
+        this.toolController = toolController;
+    }
+    
+    public boolean isToolUsesCameraControls() {
+        return !toolController.isOverrideCameraControl();
+    }
+    
+    public Camera getCamera() {
+        return cam;
+    }
+    
+    @Override
+    public void onKeyEvent(KeyInputEvent kie) {
+        if (kie.isPressed()) {
+            if ( KeyInput.KEY_LSHIFT == kie.getKeyCode() ) {
+                forceCameraControls = true;
+            }
+        } else if (kie.isReleased()){
+            if ( KeyInput.KEY_LSHIFT == kie.getKeyCode() ) {
+                forceCameraControls = false;
+            }
+        }
+    }
+    
+    @Override
+    public void checkClick(int button, boolean pressed) {
         if (button == 0) {
-            CollisionResults results = new CollisionResults();
-            Ray ray = new Ray();
-            Vector3f pos = cam.getWorldCoordinates(new Vector2f(mouseX, mouseY), 0).clone();
-            Vector3f dir = cam.getWorldCoordinates(new Vector2f(mouseX, mouseY), 0.3f).clone();
-            dir.subtractLocal(pos).normalizeLocal();
-            ray.setOrigin(pos);
-            ray.setDirection(dir);
-            rootNode.collideWith(ray, results);
-            if (results == null) {
-                return;
+            if (isEditButtonEnabled() && !forceCameraControls) {
+                toolController.doEditToolActivatedPrimary(new Vector2f(mouseX,mouseY), pressed, cam);
             }
-            final CollisionResult result = results.getClosestCollision();
-            java.awt.EventQueue.invokeLater(new Runnable() {
-
-                public void run() {
-                    if (result != null && result.getGeometry() != null) {
-                        SceneApplication.getApplication().setCurrentFileNode(jmeRootNode.getChild(result.getGeometry()));
-                    } else {
-                        SceneApplication.getApplication().setCurrentFileNode(jmeRootNode);
-                    }
-                }
-            });
-            checkClick = false;
         }
         if (button == 1) {
-            CollisionResults results = new CollisionResults();
-            Ray ray = new Ray();
-            Vector3f pos = cam.getWorldCoordinates(new Vector2f(mouseX, mouseY), 0).clone();
-            Vector3f dir = cam.getWorldCoordinates(new Vector2f(mouseX, mouseY), 0.3f).clone();
-            dir.subtractLocal(pos).normalizeLocal();
-            ray.setOrigin(pos);
-            ray.setDirection(dir);
-            rootNode.collideWith(ray, results);
-            if (results == null) {
-                return;
+            if (isEditButtonEnabled() && !forceCameraControls) {
+                toolController.doEditToolActivatedSecondary(new Vector2f(mouseX,mouseY), pressed, cam);
+            }
+        }
+    }
+    
+    @Override
+    protected void checkDragged(int button, boolean pressed) {
+        if (button == 0)
+            toolController.doEditToolDraggedPrimary(new Vector2f(mouseX, mouseY), pressed, cam);
+        else if (button == 1)
+            toolController.doEditToolDraggedSecondary(new Vector2f(mouseX, mouseY), pressed, cam);
+    }
+    
+    @Override
+    protected void checkMoved() {
+        toolController.doEditToolMoved(new Vector2f(mouseX, mouseY), cam);
+    }
+    
+    @Override
+    public void onAnalog(String string, float f1, float f) {
+        if ("MouseAxisX".equals(string)) {
+            moved = true;
+            movedR = true;
+            if (isToolUsesCameraControls() || forceCameraControls) {
+                if (buttonDownL) {
+                    rotateCamera(Vector3f.UNIT_Y, -f1 * 2.5f);
+                }
+                if (buttonDownR) {
+                    panCamera(f1 * 2.5f, 0);
+                }
             }
-            CollisionResult result = results.getClosestCollision();
-            if (result != null) {
-                ((SceneComposerTopComponent) master).doMoveCursor(result.getContactPoint());//getGeometry().getWorldTranslation().add(result.getGeometry().getWorldRotation().mult(result.getContactPoint())));
+        } else if ("MouseAxisY".equals(string)) {
+            moved = true;
+            movedR = true;
+            if (isToolUsesCameraControls() || forceCameraControls) {
+                if (buttonDownL) {
+                    rotateCamera(cam.getLeft(), -f1 * 2.5f);
+                }
+                if (buttonDownR) {
+                    panCamera(0, -f1 * 2.5f);
+                }
+            }
+        } else if ("MouseAxisX-".equals(string)) {
+            moved = true;
+            movedR = true;
+            if (isToolUsesCameraControls() || forceCameraControls) {
+                if (buttonDownL) {
+                    rotateCamera(Vector3f.UNIT_Y, f1 * 2.5f);
+                }
+                if (buttonDownR) {
+                    panCamera(-f1 * 2.5f, 0);
+                }
+            }
+        } else if ("MouseAxisY-".equals(string)) {
+            moved = true;
+            movedR = true;
+            if (isToolUsesCameraControls() || forceCameraControls) {
+                if (buttonDownL) {
+                    rotateCamera(cam.getLeft(), f1 * 2.5f);
+                }
+                if (buttonDownR) {
+                    panCamera(0, f1 * 2.5f);
+                }
             }
-            checkClickR = false;
+        } else if ("MouseWheel".equals(string)) {
+            zoomCamera(.1f);
+        } else if ("MouseWheel-".equals(string)) {
+            zoomCamera(-.1f);
         }
     }
+    
+   
 }

+ 179 - 0
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerToolController.java

@@ -0,0 +1,179 @@
+/*
+ * 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.gde.core.scene.SceneApplication;
+import com.jme3.gde.core.scene.controller.SceneToolController;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.util.concurrent.Callable;
+
+/**
+ *
+ * @author Brent Owens
+ */
+public class SceneComposerToolController extends SceneToolController {
+
+    private JmeNode rootNode;
+    private SceneEditTool editTool;
+    private SceneEditorController editorController;
+    private ComposerCameraController cameraController;
+    private Camera overlayCam;
+    private ViewPort overlayView;
+    private Node onTopToolsNode;
+    
+    public SceneComposerToolController(Node toolsNode, AssetManager manager, JmeNode rootNode) {
+        super(toolsNode, manager);
+        this.rootNode = rootNode;
+    }
+
+    public SceneComposerToolController(AssetManager manager) {
+        super(manager);
+    }
+
+    public void setEditorController(SceneEditorController editorController) {
+        this.editorController = editorController;
+    }
+
+    public void setCameraController(ComposerCameraController cameraController) {
+        this.cameraController = cameraController;
+        
+        // a node in a viewport that will always render on top
+        onTopToolsNode = new Node("OverlayNode");
+        overlayView = SceneApplication.getApplication().getRenderManager().createMainView("Overlay", this.cameraController.getCamera());
+        overlayView.setClearFlags(false, true, false);
+        overlayView.attachScene( onTopToolsNode );
+    }
+    
+    @Override
+    public void cleanup() {
+        super.cleanup();
+        SceneApplication.getApplication().getRenderManager().removeMainView(overlayView);
+        cameraController = null;
+        editorController = null;
+        onTopToolsNode.detachAllChildren();
+    }
+    
+    @Override
+    public void update(float tpf) {
+        super.update(tpf);
+        if (onTopToolsNode != null) {
+            onTopToolsNode.updateLogicalState(tpf);
+            onTopToolsNode.updateGeometricState();
+        }
+        if (editTool != null)
+            editTool.updateToolsTransformation(selected);
+        
+    }
+    
+    @Override
+    public void render(RenderManager rm) {
+        super.render(rm);
+    }
+    
+    public boolean isEditToolEnabled() {
+        return editTool != null;
+    }
+    
+    /**
+     * If the current tool overrides camera zoom/pan controls
+     */
+    public boolean isOverrideCameraControl() {
+        if (editTool != null)
+            return editTool.isOverrideCameraControl();
+        else
+            return false;
+    }
+    
+    /**
+     * Scene composer edit tool activated. Pass in null to remove tools.
+     * 
+     * @param sceneEditButton pass in null to hide any existing tool markers
+     */
+    public void showEditTool(final SceneEditTool sceneEditTool) {
+        SceneApplication.getApplication().enqueue(new Callable<Object>() {
+            public Object call() throws Exception {
+                doEnableEditTool(sceneEditTool);
+                return null;
+            }
+        });
+    }
+    
+    private void doEnableEditTool(SceneEditTool sceneEditTool) {
+        if (editTool != null)
+            editTool.hideMarker();
+        editTool = sceneEditTool;
+        editTool.activate(manager, toolsNode, onTopToolsNode, selected, this);
+    }
+    
+    public void selectedSpatialTransformed() {
+        if (editTool != null) {
+            SceneApplication.getApplication().enqueue(new Callable<Object>() {
+                public Object call() throws Exception {
+                    editTool.updateToolsTransformation(selected);
+                    return null;
+                }
+            });
+        }
+    }
+    
+    public void setSelected(Spatial selected) {
+        this.selected = selected;
+    }
+    
+    public void setNeedsSave(boolean needsSave) {
+        editorController.setNeedsSave(needsSave);
+    }
+    
+    /**
+     * Primary button activated, send command to the tool
+     * for appropriate action.
+     */
+    public void doEditToolActivatedPrimary(Vector2f mouseLoc, boolean pressed, Camera camera) {
+        if (editTool != null){
+            editTool.setCamera(camera);
+            editTool.actionPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        }
+    }
+    
+    /**
+     * Secondary button activated, send command to the tool
+     * for appropriate action.
+     */
+    public void doEditToolActivatedSecondary(Vector2f mouseLoc, boolean pressed, Camera camera) {
+        if (editTool != null){
+            editTool.setCamera(camera);
+            editTool.actionSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        }
+    }
+    
+    public void doEditToolMoved(Vector2f mouseLoc, Camera camera) {
+        if (editTool != null){
+            editTool.setCamera(camera);
+            editTool.mouseMoved(mouseLoc);
+        }
+    }
+    
+    public void doEditToolDraggedPrimary(Vector2f mouseLoc, boolean pressed, Camera camera) {
+        if (editTool != null) {
+            editTool.setCamera(camera);
+            editTool.draggedPrimary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        }
+    }
+    
+    public void doEditToolDraggedSecondary(Vector2f mouseLoc, boolean pressed, Camera camera) {
+        if (editTool != null){
+            editTool.setCamera(camera);
+            editTool.draggedSecondary(mouseLoc, pressed, rootNode, editorController.getCurrentDataObject());
+        }
+    }
+}

+ 99 - 5
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerTopComponent.form

@@ -1,6 +1,13 @@
 <?xml version="1.1" encoding="UTF-8" ?>
 
 <Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <NonVisualComponents>
+    <Component class="javax.swing.ButtonGroup" name="spatialModButtonGroup">
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new ButtonGroup()"/>
+      </AuxValues>
+    </Component>
+  </NonVisualComponents>
   <Properties>
     <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
       <Color blue="cc" green="cc" red="cc" type="rgb"/>
@@ -28,7 +35,7 @@
               <EmptySpace max="-2" attributes="0"/>
               <Component id="sceneInfoPanel" max="32767" attributes="0"/>
           </Group>
-          <Component id="jToolBar1" alignment="0" pref="696" max="32767" attributes="0"/>
+          <Component id="jToolBar1" alignment="0" pref="708" max="32767" attributes="0"/>
       </Group>
     </DimensionLayout>
     <DimensionLayout dim="1">
@@ -73,7 +80,7 @@
                   <Component id="sceneInfoLabel1" min="-2" max="-2" attributes="0"/>
                   <EmptySpace max="-2" attributes="0"/>
                   <Component id="sceneInfoLabel2" min="-2" max="-2" attributes="0"/>
-                  <EmptySpace pref="93" max="32767" attributes="0"/>
+                  <EmptySpace pref="105" max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -117,7 +124,7 @@
         </DimensionLayout>
         <DimensionLayout dim="1">
           <Group type="103" groupAlignment="0" attributes="0">
-              <Component id="jScrollPane1" alignment="1" pref="133" max="32767" attributes="0"/>
+              <Component id="jScrollPane1" alignment="1" pref="139" max="32767" attributes="0"/>
           </Group>
         </DimensionLayout>
       </Layout>
@@ -151,6 +158,93 @@
 
       <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
       <SubComponents>
+        <Component class="javax.swing.JToggleButton" name="selectButton">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="spatialModButtonGroup"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/scenecomposer/icon_select.png"/>
+            </Property>
+            <Property name="selected" type="boolean" value="true"/>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.selectButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.selectButton.toolTipText" 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="selectButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JToggleButton" name="moveButton">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="spatialModButtonGroup"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/scenecomposer/icon_arrow_out.png"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.moveButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.moveButton.toolTipText" 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="moveButtonActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JToggleButton" name="rotateButton">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="spatialModButtonGroup"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/scenecomposer/icon_arrow_rotate_clockwise.png"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.rotateButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.rotateButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="enabled" type="boolean" value="false"/>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="horizontalTextPosition" type="int" value="0"/>
+            <Property name="verticalTextPosition" type="int" value="3"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JToggleButton" name="scaleButton">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="spatialModButtonGroup"/>
+            </Property>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/com/jme3/gde/scenecomposer/icon_arrow_inout.png"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.scaleButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/scenecomposer/Bundle.properties" key="SceneComposerTopComponent.scaleButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="enabled" type="boolean" value="false"/>
+            <Property name="focusable" type="boolean" value="false"/>
+            <Property name="horizontalTextPosition" type="int" value="0"/>
+            <Property name="verticalTextPosition" type="int" value="3"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JToolBar$Separator" name="jSeparator5">
+        </Component>
         <Component class="javax.swing.JButton" name="addObjectButton">
           <Properties>
             <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
@@ -332,12 +426,12 @@
           <Layout>
             <DimensionLayout dim="0">
               <Group type="103" groupAlignment="0" attributes="0">
-                  <EmptySpace min="0" pref="190" max="32767" attributes="0"/>
+                  <EmptySpace min="0" pref="211" max="32767" attributes="0"/>
               </Group>
             </DimensionLayout>
             <DimensionLayout dim="1">
               <Group type="103" groupAlignment="0" attributes="0">
-                  <EmptySpace min="0" pref="21" max="32767" attributes="0"/>
+                  <EmptySpace min="0" pref="23" max="32767" attributes="0"/>
               </Group>
             </DimensionLayout>
           </Layout>

+ 90 - 8
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneComposerTopComponent.java

@@ -12,15 +12,17 @@ import com.jme3.gde.core.scene.PreviewRequest;
 import com.jme3.gde.core.scene.SceneApplication;
 import com.jme3.gde.core.scene.SceneListener;
 import com.jme3.gde.core.scene.SceneRequest;
-import com.jme3.gde.core.scene.controller.SceneToolController;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.sceneexplorer.nodes.NodeUtility;
+import com.jme3.gde.scenecomposer.tools.MoveTool;
+import com.jme3.gde.scenecomposer.tools.SelectTool;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import java.util.Collection;
 import java.util.logging.Logger;
+import javax.swing.ButtonGroup;
 import javax.swing.border.TitledBorder;
 import org.openide.util.Lookup.Result;
 import org.openide.util.LookupEvent;
@@ -55,7 +57,7 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
     private static final String PREFERRED_ID = "SceneComposerTopComponent";
     private final Result<JmeSpatial> result;
     ComposerCameraController camController;
-    SceneToolController toolController;
+    SceneComposerToolController toolController;
     SceneEditorController editorController;
 //    private SaveCookie saveCookie = new SaveCookieImpl();
     private SceneRequest currentRequest;
@@ -77,6 +79,7 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
     private void initComponents() {
 
+        spatialModButtonGroup = new ButtonGroup();
         sceneInfoPanel = new javax.swing.JPanel();
         sceneInfoLabel1 = new javax.swing.JLabel();
         sceneInfoLabel2 = new javax.swing.JLabel();
@@ -84,6 +87,11 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
         jScrollPane1 = new javax.swing.JScrollPane();
         jList1 = new javax.swing.JList();
         jToolBar1 = new javax.swing.JToolBar();
+        selectButton = new javax.swing.JToggleButton();
+        moveButton = new javax.swing.JToggleButton();
+        rotateButton = new javax.swing.JToggleButton();
+        scaleButton = new javax.swing.JToggleButton();
+        jSeparator5 = new javax.swing.JToolBar.Separator();
         addObjectButton = new javax.swing.JButton();
         addCursorButton = new javax.swing.JButton();
         moveToCursorButton = new javax.swing.JButton();
@@ -134,7 +142,7 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
                 .addComponent(sceneInfoLabel1)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(sceneInfoLabel2)
-                .addContainerGap(93, Short.MAX_VALUE))
+                .addContainerGap(105, Short.MAX_VALUE))
         );
 
         palettePanel.setBackground(new java.awt.Color(204, 204, 204));
@@ -150,13 +158,63 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
         );
         palettePanelLayout.setVerticalGroup(
             palettePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 133, Short.MAX_VALUE)
+            .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 139, Short.MAX_VALUE)
         );
 
         jToolBar1.setBackground(new java.awt.Color(204, 204, 204));
         jToolBar1.setFloatable(false);
         jToolBar1.setRollover(true);
 
+        spatialModButtonGroup.add(selectButton);
+        selectButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/scenecomposer/icon_select.png"))); // NOI18N
+        selectButton.setSelected(true);
+        org.openide.awt.Mnemonics.setLocalizedText(selectButton, org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.selectButton.text")); // NOI18N
+        selectButton.setToolTipText(org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.selectButton.toolTipText")); // NOI18N
+        selectButton.setFocusable(false);
+        selectButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        selectButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        selectButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                selectButtonActionPerformed(evt);
+            }
+        });
+        jToolBar1.add(selectButton);
+
+        spatialModButtonGroup.add(moveButton);
+        moveButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/scenecomposer/icon_arrow_out.png"))); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(moveButton, org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.moveButton.text")); // NOI18N
+        moveButton.setToolTipText(org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.moveButton.toolTipText")); // NOI18N
+        moveButton.setFocusable(false);
+        moveButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        moveButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        moveButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                moveButtonActionPerformed(evt);
+            }
+        });
+        jToolBar1.add(moveButton);
+
+        spatialModButtonGroup.add(rotateButton);
+        rotateButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/scenecomposer/icon_arrow_rotate_clockwise.png"))); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(rotateButton, org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.rotateButton.text")); // NOI18N
+        rotateButton.setToolTipText(org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.rotateButton.toolTipText")); // NOI18N
+        rotateButton.setEnabled(false);
+        rotateButton.setFocusable(false);
+        rotateButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        rotateButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        jToolBar1.add(rotateButton);
+
+        spatialModButtonGroup.add(scaleButton);
+        scaleButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/scenecomposer/icon_arrow_inout.png"))); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(scaleButton, org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.scaleButton.text")); // NOI18N
+        scaleButton.setToolTipText(org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.scaleButton.toolTipText")); // NOI18N
+        scaleButton.setEnabled(false);
+        scaleButton.setFocusable(false);
+        scaleButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
+        scaleButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
+        jToolBar1.add(scaleButton);
+        jToolBar1.add(jSeparator5);
+
         addObjectButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/jme3/gde/scenecomposer/add.gif"))); // NOI18N
         org.openide.awt.Mnemonics.setLocalizedText(addObjectButton, org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.addObjectButton.text")); // NOI18N
         addObjectButton.setToolTipText(org.openide.util.NbBundle.getMessage(SceneComposerTopComponent.class, "SceneComposerTopComponent.addObjectButton.toolTipText")); // NOI18N
@@ -274,11 +332,11 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
         jPanel3.setLayout(jPanel3Layout);
         jPanel3Layout.setHorizontalGroup(
             jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGap(0, 190, Short.MAX_VALUE)
+            .addGap(0, 211, Short.MAX_VALUE)
         );
         jPanel3Layout.setVerticalGroup(
             jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGap(0, 21, Short.MAX_VALUE)
+            .addGap(0, 23, Short.MAX_VALUE)
         );
 
         jToolBar1.add(jPanel3);
@@ -357,7 +415,7 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
                 .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(sceneInfoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 696, Short.MAX_VALUE)
+            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 708, Short.MAX_VALUE)
         );
         layout.setVerticalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -400,6 +458,7 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
     private void moveToCursorButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveToCursorButtonActionPerformed
         if (editorController != null) {
             editorController.moveSelectedSpatial(toolController.getCursorLocation());
+            toolController.selectedSpatialTransformed();
         }
     }//GEN-LAST:event_moveToCursorButtonActionPerformed
 
@@ -442,6 +501,17 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
         }
 
     }//GEN-LAST:event_jButton1ActionPerformed
+
+    private void selectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectButtonActionPerformed
+        SelectTool tool = new SelectTool();
+        toolController.showEditTool(tool);
+    }//GEN-LAST:event_selectButtonActionPerformed
+
+    private void moveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveButtonActionPerformed
+        MoveTool tool = new MoveTool();
+        toolController.showEditTool(tool);
+    }//GEN-LAST:event_moveButtonActionPerformed
+
     // Variables declaration - do not modify//GEN-BEGIN:variables
     private javax.swing.JButton addCursorButton;
     private javax.swing.JButton addObjectButton;
@@ -464,19 +534,25 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
     private javax.swing.JToolBar.Separator jSeparator2;
     private javax.swing.JToolBar.Separator jSeparator3;
     private javax.swing.JToolBar.Separator jSeparator4;
+    private javax.swing.JToolBar.Separator jSeparator5;
     private javax.swing.JTextField jTextField1;
     private javax.swing.JToolBar jToolBar1;
     private javax.swing.JToolBar jToolBar2;
     private javax.swing.JToolBar jToolBar3;
+    private javax.swing.JToggleButton moveButton;
     private javax.swing.JButton moveToCursorButton;
     private javax.swing.JPanel palettePanel;
     private javax.swing.JSpinner radiusSpinner;
     private javax.swing.JButton resetCursorButton;
+    private javax.swing.JToggleButton rotateButton;
+    private javax.swing.JToggleButton scaleButton;
     private javax.swing.JLabel sceneInfoLabel1;
     private javax.swing.JLabel sceneInfoLabel2;
     private javax.swing.JPanel sceneInfoPanel;
+    private javax.swing.JToggleButton selectButton;
     private javax.swing.JToggleButton showGridToggleButton;
     private javax.swing.JToggleButton showSelectionToggleButton;
+    private javax.swing.ButtonGroup spatialModButtonGroup;
     // End of variables declaration//GEN-END:variables
 
     /**
@@ -811,10 +887,16 @@ public final class SceneComposerTopComponent extends TopComponent implements Sce
             if (toolController != null) {
                 toolController.cleanup();
             }
-            toolController = new SceneToolController(currentRequest.getToolNode(), currentRequest.getManager());
+            toolController = new SceneComposerToolController(currentRequest.getToolNode(), currentRequest.getManager(), request.getJmeNode());
             camController = new ComposerCameraController(SceneApplication.getApplication().getCamera(), request.getJmeNode());
+            toolController.setEditorController(editorController);
+            camController.setToolController(toolController);
             camController.setMaster(this);
             camController.enable();
+            
+            toolController.setCameraController(camController);
+            SelectTool tool = new SelectTool();
+            toolController.showEditTool(tool);
         }/* else {
         SceneApplication.getApplication().removeSceneListener(this);
         currentRequest = null;

+ 508 - 0
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java

@@ -0,0 +1,508 @@
+/*
+ * 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;
+    }
+
+   
+    
+}

BIN
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_arrow_inout.png


BIN
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_arrow_out.png


BIN
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_arrow_rotate_clockwise.png


BIN
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/icon_select.png


+ 160 - 0
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/MoveTool.java

@@ -0,0 +1,160 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools;
+
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneEditTool;
+import com.jme3.material.Material;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Quad;
+import org.openide.loaders.DataObject;
+
+/**
+ * Move an object.
+ * When created, it generates a quad that will lie along a plane
+ * that the user selects for moving on. When the mouse is over
+ * the axisMarker, it will highlight the plane that it is over: XY,XZ,YZ.
+ * When clicked and then dragged, the selected object will move along that 
+ * plane.
+ * @author Brent Owens
+ */
+public class MoveTool extends SceneEditTool {
+
+    private Vector3f pickedPlane;
+    private Vector3f startLoc;
+    private Vector3f lastLoc;
+    private boolean wasDragging = false;
+    private Vector3f offset;
+    private Node plane;
+    Material pinkMat;
+    private final Quaternion XY = new Quaternion().fromAngleAxis(0,   new Vector3f(1,0,0));
+    private final Quaternion YZ = new Quaternion().fromAngleAxis(-FastMath.PI/2,   new Vector3f(0,1,0));
+    private final Quaternion XZ = new Quaternion().fromAngleAxis(FastMath.PI/2,   new Vector3f(1,0,0));
+    
+    
+    public MoveTool() {
+        axisPickType = AxisMarkerPickType.planeOnly;
+        setOverrideCameraControl(true);
+        
+        float size = 1000;
+        Geometry g = new Geometry("plane", new Quad(size, size));
+        g.setLocalTranslation(-size/2, -size/2, 0);
+        plane = new Node();
+        plane.attachChild(g);
+    }
+
+    
+    
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (!pressed) {
+            setDefaultAxisMarkerColors();
+            pickedPlane = null; // mouse released, reset selection
+            offset = null;
+            if (wasDragging) {
+                actionPerformed(new MoveUndo(selectedSpatial, startLoc, lastLoc));
+                wasDragging = false;
+            }
+        }
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord) {
+        if (pickedPlane == null) {
+            highlightAxisMarker(camera, screenCoord, axisPickType);
+        }
+        else {
+            pickedPlane = null;
+            offset = null;
+        }
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (!pressed) {
+            setDefaultAxisMarkerColors();
+            pickedPlane = null; // mouse released, reset selection
+            offset = null;
+            if (wasDragging) {
+                actionPerformed(new MoveUndo(selectedSpatial, startLoc, lastLoc));
+                wasDragging = false;
+            }
+            return;
+        }
+        
+        if (selectedSpatial == null)
+            return;
+        if (pickedPlane == null) {
+            pickedPlane = pickAxisMarker(camera, screenCoord, axisPickType);
+            if (pickedPlane == null)
+                return;
+            startLoc = selectedSpatial.getLocalTranslation().clone();
+            
+            if (pickedPlane.equals(new Vector3f(1,1,0)))
+                plane.setLocalRotation(XY);
+            else if (pickedPlane.equals(new Vector3f(1,0,1)))
+                plane.setLocalRotation(XZ);
+            else if (pickedPlane.equals(new Vector3f(0,1,1)))
+                plane.setLocalRotation(YZ);
+            plane.setLocalTranslation(startLoc);
+        }
+        
+        Vector3f planeHit = pickWorldLocation(camera, screenCoord, plane);
+        if (planeHit == null)
+            return;
+
+        if (offset == null)
+            offset = planeHit.subtract(startLoc); // get the offset when we start so it doesn't jump
+
+        Vector3f newPos = planeHit.subtract(offset);
+        lastLoc = newPos;
+        selectedSpatial.setLocalTranslation(newPos);
+        updateToolsTransformation(selectedSpatial);
+        
+        wasDragging = true;
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        
+    }
+
+    private class MoveUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Vector3f before,after;
+        
+        MoveUndo(Spatial spatial, Vector3f before, Vector3f after) {
+            this.spatial = spatial;
+            this.before = before;
+            this.after = after;
+        }
+        
+        @Override
+        public void sceneUndo() {
+            spatial.setLocalTranslation(before);
+            toolController.selectedSpatialTransformed();
+        }
+
+        @Override
+        public void sceneRedo() {
+            spatial.setLocalTranslation(after);
+            toolController.selectedSpatialTransformed();
+        }
+        
+    }
+}

+ 81 - 0
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java

@@ -0,0 +1,81 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools;
+
+import com.jme3.gde.core.scene.SceneApplication;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.scenecomposer.SceneEditTool;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import java.util.concurrent.Callable;
+import org.openide.loaders.DataObject;
+
+/**
+ *
+ * @author Brent Owens
+ */
+public class SelectTool extends SceneEditTool {
+
+    protected Spatial selected;
+    private boolean wasDragging = false;
+    
+    
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) {
+        if (!pressed && !wasDragging) { 
+            // mouse released and wasn't dragging, select a new spatial
+            final Spatial result = pickWorldSpatial(getCamera(), screenCoord, rootNode);
+            
+            java.awt.EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    if (result != null) {
+                        SceneApplication.getApplication().setCurrentFileNode(rootNode.getChild(result));
+                    } else {
+                        SceneApplication.getApplication().setCurrentFileNode(rootNode);
+                    }
+                }
+            });
+
+            if (result != null) {
+                replaceSelectionShape(result);
+                updateToolsTransformation(selectedSpatial);
+            }
+            
+        }
+        
+        if (!pressed) {
+            wasDragging = false;
+        }
+    }
+
+    @Override
+    public void actionSecondary(final Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) {
+        if (!pressed && !wasDragging) {
+            final Vector3f result = pickWorldLocation(getCamera(), screenCoord, rootNode);
+            toolController.doSetCursorLocation(result);
+        }
+        if (!pressed) {
+            wasDragging = false;
+        }
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord) {
+        
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        wasDragging = pressed;
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        wasDragging = pressed;
+    }
+    
+}

+ 1 - 5
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainCameraController.java

@@ -32,13 +32,10 @@
 package com.jme3.gde.terraineditor;
 
 import com.jme3.app.Application;
-import com.jme3.app.state.AppStateManager;
 import com.jme3.collision.CollisionResult;
 import com.jme3.collision.CollisionResults;
 import com.jme3.gde.core.scene.SceneApplication;
 import com.jme3.gde.core.scene.controller.AbstractCameraController;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.input.InputManager;
 import com.jme3.input.KeyInput;
 import com.jme3.input.event.KeyInputEvent;
 import com.jme3.input.event.MouseMotionEvent;
@@ -46,7 +43,6 @@ import com.jme3.math.Ray;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.Camera;
-import java.util.logging.Logger;
 
 
 /**
@@ -177,7 +173,7 @@ public class TerrainCameraController extends AbstractCameraController {
     }
 
     @Override
-    protected void checkClick(int button) {
+    protected void checkClick(int button, boolean pressed) {
         if (button == 0) {
             if (isTerrainEditButtonEnabled() && !forceCameraControls) {
                 if (leftMouse)

+ 1 - 5
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainEditorTopComponent.java

@@ -42,9 +42,8 @@ import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeTerrainQuad;
 import com.jme3.gde.core.sceneexplorer.nodes.NodeUtility;
 import com.jme3.gde.core.properties.TexturePropertyEditor;
-import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
-import com.jme3.gde.core.undoredo.SceneUndoRedoManager;
 import com.jme3.gde.core.util.DataObjectSaveNode;
+import com.jme3.gde.core.util.ToggleButtonGroup;
 import com.jme3.gde.terraineditor.sky.SkyboxWizardAction;
 import com.jme3.gde.terraineditor.tools.EraseTerrainTool;
 import com.jme3.gde.terraineditor.tools.LevelTerrainTool;
@@ -82,8 +81,6 @@ import javax.swing.filechooser.FileSystemView;
 import javax.swing.table.DefaultTableModel;
 import javax.swing.table.TableCellEditor;
 import javax.swing.table.TableCellRenderer;
-import javax.swing.undo.CannotRedoException;
-import javax.swing.undo.CannotUndoException;
 import jme3tools.converters.ImageToAwt;
 import org.netbeans.api.progress.ProgressHandle;
 import org.netbeans.api.progress.ProgressHandleFactory;
@@ -104,7 +101,6 @@ import org.openide.explorer.ExplorerUtils;
 import org.openide.nodes.NodeListener;
 import org.openide.util.Exceptions;
 import org.openide.util.HelpCtx;
-import org.openide.util.Lookup;
 import org.openide.util.Lookup.Result;
 import org.openide.util.LookupEvent;
 import org.openide.util.LookupListener;

+ 0 - 5
jme3-terrain-editor/src/com/jme3/gde/terraineditor/TerrainToolController.java

@@ -38,13 +38,8 @@ import com.jme3.gde.core.scene.controller.SceneToolController;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.terraineditor.tools.TerrainTool;
-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.shape.Sphere;
 import java.util.concurrent.Callable;
 
 /**

+ 1 - 1
jme3-vehicle-creator/src/com/jme3/gde/vehiclecreator/VehicleCreatorCameraController.java

@@ -57,7 +57,7 @@ public class VehicleCreatorCameraController extends AbstractCameraController {
     }
 
     @Override
-    protected void checkClick(int button) {
+    protected void checkClick(int button, boolean pressed) {
     }
 
     /**