Browse Source

Merge pull request #313 from neph1/snapping

grid and scene snapping
Toni Helenius 3 years ago
parent
commit
f7f81f774a

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

@@ -5,6 +5,8 @@
 package com.jme3.gde.scenecomposer;
 
 import com.jme3.asset.AssetManager;
+import com.jme3.collision.CollisionResult;
+import com.jme3.collision.CollisionResults;
 import com.jme3.gde.core.scene.SceneApplication;
 import com.jme3.gde.core.scene.controller.SceneToolController;
 import com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode;
@@ -12,7 +14,11 @@ import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
 import com.jme3.gde.scenecomposer.gizmo.GizmoFactory;
 import com.jme3.gde.scenecomposer.tools.shortcuts.ShortcutManager;
 import com.jme3.input.event.KeyInputEvent;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Ray;
 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;
@@ -44,6 +50,8 @@ public class SceneComposerToolController extends SceneToolController {
     private boolean selectTerrain = false;
     private boolean selectGeometries = false;
     private TransformationType transformationType = TransformationType.local;
+    
+    private final float fifteenDegs = FastMath.HALF_PI / 6f;
 
     public enum TransformationType {
         local, global, camera
@@ -384,5 +392,95 @@ public class SceneComposerToolController extends SceneToolController {
     public JmeNode getRootNode() {
         return rootNode;
     }
+    
+    /**
+     * Update the selected spatial with translation from user input
+     * 
+     * @param translation absolute translation
+     * @param constraints axes affected
+     */
+    public void updateSelectedTranslation(final Vector3f translation, 
+            final Vector3f constraints) {
+        if (isSnapToScene()) {
+            translation.set(snapToScene(translation));
+        }
+        if (isSnapToGrid()) {
+            if (constraints.x != 0f) {
+                translation.setX((int) translation.x);
+            }
+            if (constraints.y != 0f) {
+                translation.setY((int) translation.y);
+            }
+            if (constraints.z != 0f) {
+                translation.setZ((int) translation.z);
+            }
+        }
+        selected.setLocalTranslation(translation);
+    }
+    
+    /**
+     * Update the selected spatial with rotation from user input
+     * 
+     * @param rotation absolute rotation
+     * @param constraints axes affected
+     */
+    public void updateSelectedRotation(final Quaternion rotation, 
+            final Vector3f constraints) {
+        if (isSnapToGrid()) {
+            final float[] angles = new float[3];
+            rotation.toAngles(angles);
+            
+            if (constraints.y != 0f) {
+                angles[1] = Math.round(angles[1] / FastMath.HALF_PI) 
+                        * fifteenDegs;
+            }
+            if (constraints.x != 0f) {
+                angles[0] = Math.round(angles[0] / FastMath.HALF_PI) 
+                        * fifteenDegs;
+            }
+            if (constraints.z != 0f) {
+                angles[2] = Math.round(angles[2] / FastMath.HALF_PI) 
+                        * fifteenDegs;
+            }
+            rotation.fromAngles(angles);
+        }
+        selected.setLocalRotation(rotation);
+    }
+    
+    /**
+     * Update the selected spatial with scale from user input
+     * 
+     * @param scale absolute scale
+     * @param constraints axes affected 
+     */
+    public void updateSelectedScale(final Vector3f scale, 
+            final Vector3f constraints) {
+        if (isSnapToGrid()) {
+            if (constraints.x != 0f) {
+                scale.setX((int) Math.max(scale.x, 1));
+            }
+            if (constraints.y != 0f) {
+                scale.setY((int) Math.max(scale.y, 1));
+            }
+            if (constraints.z != 0f) {
+                scale.setZ((int) Math.max(scale.z, 1));
+            }
+        }
+        selected.setLocalScale(scale);
+    }
+
+    private Vector3f snapToScene(final Vector3f position) {
+        final Ray ray = new Ray(position, Vector3f.UNIT_Y.negate());
+        final CollisionResults collisionResults = new CollisionResults();
+        final Node root = getRootNode().getLookup().lookup(Node.class);
+        root.collideWith(ray, collisionResults);
+        for (CollisionResult r : collisionResults) {
+            if (r.getGeometry() != selected) {
+                position.y = r.getContactPoint().y;
+                break;
+            }
+        }
+        return position;
+    }
 
 }

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

@@ -8,7 +8,6 @@ import com.jme3.asset.AssetManager;
 import com.jme3.bullet.control.CharacterControl;
 import com.jme3.bullet.control.RigidBodyControl;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
 import com.jme3.gde.scenecomposer.SceneComposerToolController;
 import com.jme3.gde.scenecomposer.SceneEditTool;
@@ -142,7 +141,7 @@ public class MoveTool extends SceneEditTool {
                 position = startPosition.add(diff);
             }
             lastPosition = position;
-            toolController.getSelectedSpatial().setLocalTranslation(position);
+            toolController.updateSelectedTranslation(position, pickedMarker);
             updateToolsTransformation();
         }
     }
@@ -210,4 +209,5 @@ public class MoveTool extends SceneEditTool {
             this.after.set(after);
         }
     }
+
 }

+ 162 - 163
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/RotateTool.java

@@ -1,163 +1,162 @@
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package com.jme3.gde.scenecomposer.tools;
-
-import com.jme3.asset.AssetManager;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
-import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
-import com.jme3.gde.scenecomposer.SceneComposerToolController;
-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.Node;
-import com.jme3.scene.Spatial;
-import org.openide.loaders.DataObject;
-import org.openide.util.Lookup;
-
-/**
- *
- * @author kbender
- */
-public class RotateTool extends SceneEditTool {
-
-    private Vector3f pickedMarker;
-    private Quaternion startRotate;
-    private Quaternion startWorldRotate;
-    private Quaternion lastRotate;
-    private boolean wasDragging = false;
-    private PickManager pickManager;
-
-    public RotateTool() {
-        axisPickType = SceneEditTool.AxisMarkerPickType.planeOnly;
-        setOverrideCameraControl(true);
-    }
-
-    @Override
-    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
-        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController);
-        pickManager = Lookup.getDefault().lookup(PickManager.class);
-        displayCircles();
-    }
-
-    @Override
-    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
-        if (!pressed) {
-            setDefaultAxisMarkerColors();
-            pickedMarker = null; // mouse released, reset selection
-            if (wasDragging) {
-                actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
-                wasDragging = false;
-            }
-            pickManager.reset();
-        } else {
-            if (toolController.getSelectedSpatial() == null) {
-                return;
-            }
-
-            if (pickedMarker == null) {
-                pickedMarker = pickAxisMarker(camera, screenCoord, axisPickType);
-                if (pickedMarker == null) {
-                    return;
-                }
-
-                if (pickedMarker.equals(QUAD_XY)) {
-                    pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
-                } else if (pickedMarker.equals(QUAD_XZ)) {
-                    pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
-                } else if (pickedMarker.equals(QUAD_YZ)) {
-                    pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
-                }
-                startRotate = toolController.getSelectedSpatial().getLocalRotation().clone();
-                startWorldRotate = toolController.getSelectedSpatial().getWorldRotation().clone();
-                wasDragging = true;
-            }
-        }
-    }
-
-    @Override
-    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
-        if (pressed) {
-            cancel();
-        }
-    }
-
-    @Override
-    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject currentDataObject) {
-        if (pickedMarker == null) {
-            highlightAxisMarker(camera, screenCoord, axisPickType);
-        } else {
-            pickedMarker = null;
-            pickManager.reset();
-        }
-    }
-
-    @Override
-    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
-        if (!pressed) {
-            setDefaultAxisMarkerColors();
-            pickedMarker = null; // mouse released, reset selection
-
-            if (wasDragging) {
-                actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
-                wasDragging = false;
-            }
-            pickManager.reset();
-        } else if (wasDragging) {
-            if (!pickManager.updatePick(camera, screenCoord)) {
-                return;
-            }
-
-            if (pickedMarker.equals(QUAD_XY) || pickedMarker.equals(QUAD_XZ) || pickedMarker.equals(QUAD_YZ)) {
-                Quaternion rotation = startRotate.mult(pickManager.getRotation(startWorldRotate.inverse()));
-                toolController.getSelectedSpatial().setLocalRotation(rotation);
-                lastRotate = rotation;
-            }
-            updateToolsTransformation();
-        }
-    }
-
-    @Override
-    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
-        if (pressed) {
-            cancel();
-        }
-    }
-
-    private void cancel() {
-        if (wasDragging) {
-            wasDragging = false;
-            toolController.getSelectedSpatial().setLocalRotation(startRotate);
-            setDefaultAxisMarkerColors();
-            pickedMarker = null; // mouse released, reset selection
-            pickManager.reset();
-        }
-    }
-
-    private class RotateUndo extends AbstractUndoableSceneEdit {
-
-        private Spatial spatial;
-        private Quaternion before, after;
-
-        RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
-            this.spatial = spatial;
-            this.before = before;
-            this.after = after;
-        }
-
-        @Override
-        public void sceneUndo() {
-            spatial.setLocalRotation(before);
-            toolController.selectedSpatialTransformed();
-        }
-
-        @Override
-        public void sceneRedo() {
-            spatial.setLocalRotation(after);
-            toolController.selectedSpatialTransformed();
-        }
-    }
-}
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.gde.scenecomposer.tools;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
+import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
+import com.jme3.gde.scenecomposer.SceneComposerToolController;
+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.Node;
+import com.jme3.scene.Spatial;
+import org.openide.loaders.DataObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author kbender
+ */
+public class RotateTool extends SceneEditTool {
+
+    private Vector3f pickedMarker;
+    private Quaternion startRotate;
+    private Quaternion startWorldRotate;
+    private Quaternion lastRotate;
+    private boolean wasDragging = false;
+    private PickManager pickManager;
+
+    public RotateTool() {
+        axisPickType = SceneEditTool.AxisMarkerPickType.planeOnly;
+        setOverrideCameraControl(true);
+    }
+
+    @Override
+    public void activate(AssetManager manager, Node toolNode, Node onTopToolNode, Spatial selectedSpatial, SceneComposerToolController toolController) {
+        super.activate(manager, toolNode, onTopToolNode, selectedSpatial, toolController);
+        pickManager = Lookup.getDefault().lookup(PickManager.class);
+        displayCircles();
+    }
+
+    @Override
+    public void actionPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (!pressed) {
+            setDefaultAxisMarkerColors();
+            pickedMarker = null; // mouse released, reset selection
+            if (wasDragging) {
+                actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
+                wasDragging = false;
+            }
+            pickManager.reset();
+        } else {
+            if (toolController.getSelectedSpatial() == null) {
+                return;
+            }
+
+            if (pickedMarker == null) {
+                pickedMarker = pickAxisMarker(camera, screenCoord, axisPickType);
+                if (pickedMarker == null) {
+                    return;
+                }
+
+                if (pickedMarker.equals(QUAD_XY)) {
+                    pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XY, getTransformType(), camera, screenCoord);
+                } else if (pickedMarker.equals(QUAD_XZ)) {
+                    pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_XZ, getTransformType(), camera, screenCoord);
+                } else if (pickedMarker.equals(QUAD_YZ)) {
+                    pickManager.initiatePick(toolController.getSelectedSpatial(), PickManager.PLANE_YZ, getTransformType(), camera, screenCoord);
+                }
+                startRotate = toolController.getSelectedSpatial().getLocalRotation().clone();
+                startWorldRotate = toolController.getSelectedSpatial().getWorldRotation().clone();
+                wasDragging = true;
+            }
+        }
+    }
+
+    @Override
+    public void actionSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject dataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    @Override
+    public void mouseMoved(Vector2f screenCoord, JmeNode rootNode, DataObject currentDataObject) {
+        if (pickedMarker == null) {
+            highlightAxisMarker(camera, screenCoord, axisPickType);
+        } else {
+            pickedMarker = null;
+            pickManager.reset();
+        }
+    }
+
+    @Override
+    public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (!pressed) {
+            setDefaultAxisMarkerColors();
+            pickedMarker = null; // mouse released, reset selection
+
+            if (wasDragging) {
+                actionPerformed(new RotateUndo(toolController.getSelectedSpatial(), startRotate, lastRotate));
+                wasDragging = false;
+            }
+            pickManager.reset();
+        } else if (wasDragging) {
+            if (!pickManager.updatePick(camera, screenCoord)) {
+                return;
+            }
+
+            if (pickedMarker.equals(QUAD_XY) || pickedMarker.equals(QUAD_XZ) || pickedMarker.equals(QUAD_YZ)) {
+                Quaternion rotation = startRotate.mult(pickManager.getRotation(startWorldRotate.inverse()));
+                toolController.updateSelectedRotation(rotation, pickedMarker);
+                lastRotate = rotation;
+            }
+            updateToolsTransformation();
+        }
+    }
+
+    @Override
+    public void draggedSecondary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        if (pressed) {
+            cancel();
+        }
+    }
+
+    private void cancel() {
+        if (wasDragging) {
+            wasDragging = false;
+            toolController.getSelectedSpatial().setLocalRotation(startRotate);
+            setDefaultAxisMarkerColors();
+            pickedMarker = null; // mouse released, reset selection
+            pickManager.reset();
+        }
+    }
+
+    private class RotateUndo extends AbstractUndoableSceneEdit {
+
+        private Spatial spatial;
+        private Quaternion before, after;
+
+        RotateUndo(Spatial spatial, Quaternion before, Quaternion after) {
+            this.spatial = spatial;
+            this.before = before;
+            this.after = after;
+        }
+
+        @Override
+        public void sceneUndo() {
+            spatial.setLocalRotation(before);
+            toolController.selectedSpatialTransformed();
+        }
+
+        @Override
+        public void sceneRedo() {
+            spatial.setLocalRotation(after);
+            toolController.selectedSpatialTransformed();
+        }
+    }
+}

+ 2 - 3
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/ScaleTool.java

@@ -6,7 +6,6 @@ package com.jme3.gde.scenecomposer.tools;
 
 import com.jme3.asset.AssetManager;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
 import com.jme3.gde.scenecomposer.SceneComposerToolController;
 import com.jme3.gde.scenecomposer.SceneEditTool;
@@ -122,15 +121,15 @@ public class ScaleTool extends SceneEditTool {
                 diff += 1f;
                 Vector3f scale = startScale.mult(diff);
                 lastScale = scale;
-                toolController.getSelectedSpatial().setLocalScale(scale);
+                toolController.updateSelectedScale(scale, pickedMarker);
             } else if (pickedMarker.equals(ARROW_X) || pickedMarker.equals(ARROW_Y) || pickedMarker.equals(ARROW_Z)) {
                 // Get the translation in the spatial Space
                 Quaternion worldToSpatial = toolController.getSelectedSpatial().getWorldRotation().inverse();
                 Vector3f diff = worldToSpatial.mult(pickManager.getTranslation(constraintAxis));
                 diff.multLocal(0.5f);
                 Vector3f scale = startScale.add(diff);
+                toolController.updateSelectedScale(scale, pickedMarker);
                 lastScale = scale;
-                toolController.getSelectedSpatial().setLocalScale(scale);
             }
             updateToolsTransformation();
         }

+ 1 - 2
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/MoveShortcut.java

@@ -9,7 +9,6 @@ import com.jme3.asset.AssetManager;
 import com.jme3.bullet.control.CharacterControl;
 import com.jme3.bullet.control.RigidBodyControl;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
 import com.jme3.gde.scenecomposer.SceneComposerToolController;
 import com.jme3.gde.scenecomposer.tools.PickManager;
@@ -167,7 +166,7 @@ public class MoveShortcut extends ShortcutTool {
                 position = startPosition.add(diff);
             }
             finalPosition = position;
-            toolController.getSelectedSpatial().setLocalTranslation(position);
+            toolController.updateSelectedTranslation(position, Vector3f.UNIT_XYZ);
             updateToolsTransformation();
         }
     }

+ 1 - 2
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/RotateShortcut.java

@@ -7,7 +7,6 @@ package com.jme3.gde.scenecomposer.tools.shortcuts;
 
 import com.jme3.asset.AssetManager;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
 import com.jme3.gde.scenecomposer.SceneComposerToolController;
 import com.jme3.gde.scenecomposer.tools.PickManager;
@@ -147,7 +146,7 @@ public class RotateShortcut extends ShortcutTool {
         if (pickManager.updatePick(camera, screenCoord)) {
 
             Quaternion rotation = startRotation.mult(pickManager.getRotation(startWorldRotate.inverse()));
-            toolController.getSelectedSpatial().setLocalRotation(rotation);
+            toolController.updateSelectedRotation(rotation, Vector3f.UNIT_XYZ);
             finalRotation = rotation;
             updateToolsTransformation();
         }

+ 1 - 2
jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/shortcuts/ScaleShortcut.java

@@ -7,7 +7,6 @@ package com.jme3.gde.scenecomposer.tools.shortcuts;
 
 import com.jme3.asset.AssetManager;
 import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
-import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.undoredo.AbstractUndoableSceneEdit;
 import com.jme3.gde.scenecomposer.SceneComposerToolController;
 import com.jme3.gde.scenecomposer.tools.PickManager;
@@ -157,7 +156,7 @@ public class ScaleShortcut extends ShortcutTool {
                 scale = startScale.add(diff);
             }
             finalScale = scale;
-            toolController.getSelectedSpatial().setLocalScale(scale);
+            toolController.updateSelectedScale(scale, Vector3f.UNIT_XYZ);
             updateToolsTransformation();
         }
     }