浏览代码

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 6 年之前
父节点
当前提交
1c8f110c73

+ 3 - 0
dist/preview release/what's new.md

@@ -6,6 +6,9 @@
 
 ## Updates
 - Support Vive Focus 3Dof controller ([TrevorDev](https://github.com/TrevorDev))
+- Planar positioning support for GizmoManager ([Balupg](https://github.com/balupg))
+- Individual gizmos can now be enabled/disabled ([Balupg](https://github.com/balupg))
+
 - Unify preparation of instance attributes. Added `MaterialHelper.PushAttributesForInstances` ([MarkusBillharz](https://github.com/MarkusBillharz))
  
 ### Inspector

+ 48 - 12
src/Gizmos/axisDragGizmo.ts

@@ -14,6 +14,7 @@ import { Gizmo } from "./gizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 import { StandardMaterial } from "../Materials/standardMaterial";
 import { Scene } from "../scene";
+import { PositionGizmo } from "./positionGizmo";
 /**
  * Single axis drag gizmo
  */
@@ -33,6 +34,13 @@ export class AxisDragGizmo extends Gizmo {
      */
     public onSnapObservable = new Observable<{ snapDistance: number }>();
 
+    private _isEnabled: boolean = true;
+    private _parent: Nullable<PositionGizmo> = null;
+
+    private _arrow: TransformNode;
+    private _coloredMaterial: StandardMaterial;
+    private _hoverMaterial: StandardMaterial;
+
     /** @hidden */
     public static _CreateArrow(scene: Scene, material: StandardMaterial): TransformNode {
         var arrow = new TransformNode("arrow", scene);
@@ -67,23 +75,23 @@ export class AxisDragGizmo extends Gizmo {
      * @param dragAxis The axis which the gizmo will be able to drag on
      * @param color The color of the gizmo
      */
-    constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer) {
+    constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, parent: Nullable<PositionGizmo> = null) {
         super(gizmoLayer);
-
+        this._parent = parent;
         // Create Material
-        var coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
-        coloredMaterial.diffuseColor = color;
-        coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
+        this._coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
+        this._coloredMaterial.diffuseColor = color;
+        this._coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
 
-        var hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
-        hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
+        this._hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
+        this._hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
 
         // Build mesh on root node
-        var arrow = AxisDragGizmo._CreateArrow(gizmoLayer.utilityLayerScene, coloredMaterial);
+        this._arrow = AxisDragGizmo._CreateArrow(gizmoLayer.utilityLayerScene, this._coloredMaterial);
 
-        arrow.lookAt(this._rootMesh.position.add(dragAxis));
-        arrow.scaling.scaleInPlace(1 / 3);
-        arrow.parent = this._rootMesh;
+        this._arrow.lookAt(this._rootMesh.position.add(dragAxis));
+        this._arrow.scaling.scaleInPlace(1 / 3);
+        this._arrow.parent = this._rootMesh;
 
         var currentSnapDragDistance = 0;
         var tmpVector = new Vector3();
@@ -128,7 +136,7 @@ export class AxisDragGizmo extends Gizmo {
                 return;
             }
             var isHovered = pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1);
-            var material = isHovered ? hoverMaterial : coloredMaterial;
+            var material = isHovered ? this._hoverMaterial : this._coloredMaterial;
             this._rootMesh.getChildMeshes().forEach((m) => {
                 m.material = material;
                 if ((<LinesMesh>m).color) {
@@ -145,6 +153,25 @@ export class AxisDragGizmo extends Gizmo {
             this.dragBehavior.enabled = value ? true : false;
         }
     }
+
+    /**
+     * If the gizmo is enabled
+     */
+    public set isEnabled(value: boolean) {
+        this._isEnabled = value;
+        if (!value) {
+            this.attachedMesh = null;
+        }
+        else {
+            if (this._parent) {
+                this.attachedMesh = this._parent.attachedMesh;
+            }
+        }
+    }
+    public get isEnabled(): boolean {
+        return this._isEnabled;
+    }
+
     /**
      * Disposes of the gizmo
      */
@@ -152,6 +179,15 @@ export class AxisDragGizmo extends Gizmo {
         this.onSnapObservable.clear();
         this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
         this.dragBehavior.detach();
+        if (this._arrow)
+        {
+            this._arrow.dispose();
+        }
+        [this._coloredMaterial, this._hoverMaterial].forEach((matl) => {
+            if (matl) {
+                matl.dispose();
+            }
+        });
         super.dispose();
     }
 }

+ 47 - 12
src/Gizmos/axisScaleGizmo.ts

@@ -13,11 +13,12 @@ import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
 import { Gizmo } from "./gizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
+import { ScaleGizmo } from "./scaleGizmo";
+
 /**
  * Single axis scale gizmo
  */
 export class AxisScaleGizmo extends Gizmo {
-    private _coloredMaterial: StandardMaterial;
     /**
      * Drag behavior responsible for the gizmos dragging interactions
      */
@@ -36,30 +37,38 @@ export class AxisScaleGizmo extends Gizmo {
      * If the scaling operation should be done on all axis (default: false)
      */
     public uniformScaling = false;
+
+    private _isEnabled: boolean = true;
+    private _parent: Nullable<ScaleGizmo> = null;
+
+    private _arrow: AbstractMesh;
+    private _coloredMaterial: StandardMaterial;
+    private _hoverMaterial: StandardMaterial;
+
     /**
      * Creates an AxisScaleGizmo
      * @param gizmoLayer The utility layer the gizmo will be added to
      * @param dragAxis The axis which the gizmo will be able to scale on
      * @param color The color of the gizmo
      */
-    constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer) {
+    constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, parent: Nullable<ScaleGizmo> = null) {
         super(gizmoLayer);
-
+        this._parent = parent;
         // Create Material
         this._coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
         this._coloredMaterial.diffuseColor = color;
         this._coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
 
-        var hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
-        hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
+        this._hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
+        this._hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
 
         // Build mesh on root node
-        var arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
+        this._arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
         var arrowMesh = BoxBuilder.CreateBox("yPosMesh", { size: 0.4 }, gizmoLayer.utilityLayerScene);
         var arrowTail = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0.005, height: 0.275, diameterBottom: 0.005, tessellation: 96 }, gizmoLayer.utilityLayerScene);
         arrowTail.material = this._coloredMaterial;
-        arrow.addChild(arrowMesh);
-        arrow.addChild(arrowTail);
+        this._arrow.addChild(arrowMesh);
+        this._arrow.addChild(arrowTail);
 
         // Position arrow pointing in its drag axis
         arrowMesh.scaling.scaleInPlace(0.1);
@@ -68,9 +77,9 @@ export class AxisScaleGizmo extends Gizmo {
         arrowMesh.position.z += 0.3;
         arrowTail.position.z += 0.275 / 2;
         arrowTail.rotation.x = Math.PI / 2;
-        arrow.lookAt(this._rootMesh.position.add(dragAxis));
-        this._rootMesh.addChild(arrow);
-        arrow.scaling.scaleInPlace(1 / 3);
+        this._arrow.lookAt(this._rootMesh.position.add(dragAxis));
+        this._rootMesh.addChild(this._arrow);
+        this._arrow.scaling.scaleInPlace(1 / 3);
 
         // Add drag behavior to handle events when the gizmo is dragged
         this.dragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
@@ -127,7 +136,7 @@ export class AxisScaleGizmo extends Gizmo {
                 return;
             }
             var isHovered = pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1);
-            var material = isHovered ? hoverMaterial : this._coloredMaterial;
+            var material = isHovered ? this._hoverMaterial : this._coloredMaterial;
             this._rootMesh.getChildMeshes().forEach((m) => {
                 m.material = material;
                 if ((<LinesMesh>m).color) {
@@ -147,12 +156,38 @@ export class AxisScaleGizmo extends Gizmo {
     }
 
     /**
+ * If the gizmo is enabled
+ */
+    public set isEnabled(value: boolean) {
+        this._isEnabled = value;
+        if (!value) {
+            this.attachedMesh = null;
+        }
+        else {
+            if (this._parent) {
+                this.attachedMesh = this._parent.attachedMesh;
+            }
+        }
+    }
+    public get isEnabled(): boolean {
+        return this._isEnabled;
+    }
+
+    /**
      * Disposes of the gizmo
      */
     public dispose() {
         this.onSnapObservable.clear();
         this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
         this.dragBehavior.detach();
+        if (this._arrow) {
+            this._arrow.dispose();
+        }
+        [this._coloredMaterial, this._hoverMaterial].forEach((matl) => {
+            if (matl) {
+                matl.dispose();
+            }
+        });
         super.dispose();
     }
 

+ 183 - 0
src/Gizmos/planeDragGizmo.ts

@@ -0,0 +1,183 @@
+import { Observer, Observable } from "../Misc/observable";
+import { Nullable } from "../types";
+import { PointerInfo } from "../Events/pointerEvents";
+import { Vector3, Color3, Matrix } from "../Maths/math";
+import { TransformNode } from "../Meshes/transformNode";
+import { AbstractMesh } from "../Meshes/abstractMesh";
+import { Mesh } from "../Meshes/mesh";
+import { PlaneBuilder } from "../Meshes/Builders/planeBuilder";
+import { PointerDragBehavior } from "../Behaviors/Meshes/pointerDragBehavior";
+import { _TimeToken } from "../Instrumentation/timeToken";
+import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
+import { Gizmo } from "./gizmo";
+import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
+import { StandardMaterial } from "../Materials/standardMaterial";
+import { Scene } from "../scene";
+import { PositionGizmo } from "./positionGizmo";
+/**
+ * Single plane drag gizmo
+ */
+export class PlaneDragGizmo extends Gizmo {
+    /**
+     * Drag behavior responsible for the gizmos dragging interactions
+     */
+    public dragBehavior: PointerDragBehavior;
+    private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
+    /**
+     * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+     */
+    public snapDistance = 0;
+    /**
+     * Event that fires each time the gizmo snaps to a new location.
+     * * snapDistance is the the change in distance
+     */
+    public onSnapObservable = new Observable<{ snapDistance: number }>();
+
+    private _plane: TransformNode;
+    private _coloredMaterial: StandardMaterial;
+    private _hoverMaterial: StandardMaterial;
+
+    private _isEnabled: boolean = false;
+    private _parent: Nullable<PositionGizmo> = null;
+
+    /** @hidden */
+    public static _CreatePlane(scene: Scene, material: StandardMaterial): TransformNode {
+        var plane = new TransformNode("plane", scene);
+
+        //make sure plane is double sided
+        var dragPlane = PlaneBuilder.CreatePlane("dragPlane", { width: .1375, height: .1375, sideOrientation: 2 }, scene);
+        dragPlane.material = material;
+        dragPlane.parent = plane;
+
+        // Position plane pointing normal to dragPlane normal
+        dragPlane.material = material;
+        return plane;
+    }
+
+    /** @hidden */
+    public static _CreateArrowInstance(scene: Scene, arrow: TransformNode): TransformNode {
+        const instance = new TransformNode("arrow", scene);
+        for (const mesh of arrow.getChildMeshes()) {
+            const childInstance = (mesh as Mesh).createInstance(mesh.name);
+            childInstance.parent = instance;
+        }
+        return instance;
+    }
+
+    /**
+     * Creates a PlaneDragGizmo
+     * @param gizmoLayer The utility layer the gizmo will be added to
+     * @param dragPlaneNormal The axis normal to which the gizmo will be able to drag on
+     * @param color The color of the gizmo
+     */
+    constructor(dragPlaneNormal: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, parent: Nullable<PositionGizmo> = null) {
+        super(gizmoLayer);
+        this._parent = parent;
+        // Create Material
+        this._coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
+        this._coloredMaterial.diffuseColor = color;
+        this._coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
+
+        this._hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
+        this._hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
+
+        // Build plane mesh on root node
+        this._plane = PlaneDragGizmo._CreatePlane(gizmoLayer.utilityLayerScene, this._coloredMaterial);
+
+        this._plane.lookAt(this._rootMesh.position.add(dragPlaneNormal));
+        this._plane.scaling.scaleInPlace(1 / 3);
+        this._plane.parent = this._rootMesh;
+
+        var currentSnapDragDistance = 0;
+        var tmpVector = new Vector3();
+        var tmpSnapEvent = { snapDistance: 0 };
+        // Add dragPlaneNormal drag behavior to handle events when the gizmo is dragged
+        this.dragBehavior = new PointerDragBehavior({ dragPlaneNormal: dragPlaneNormal });
+        this.dragBehavior.moveAttached = false;
+        this._rootMesh.addBehavior(this.dragBehavior);
+
+        var localDelta = new Vector3();
+        var tmpMatrix = new Matrix();
+        this.dragBehavior.onDragObservable.add((event) => {
+            if (this.attachedMesh) {
+                // Convert delta to local translation if it has a parent
+                if (this.attachedMesh.parent) {
+                    this.attachedMesh.parent.computeWorldMatrix().invertToRef(tmpMatrix);
+                    tmpMatrix.setTranslationFromFloats(0, 0, 0);
+                    Vector3.TransformCoordinatesToRef(event.delta, tmpMatrix, localDelta);
+                } else {
+                    localDelta.copyFrom(event.delta);
+                }
+                // Snapping logic
+                if (this.snapDistance == 0) {
+                    this.attachedMesh.position.addInPlace(localDelta);
+                } else {
+                    currentSnapDragDistance += event.dragDistance;
+                    if (Math.abs(currentSnapDragDistance) > this.snapDistance) {
+                        var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / this.snapDistance);
+                        currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
+                        localDelta.normalizeToRef(tmpVector);
+                        tmpVector.scaleInPlace(this.snapDistance * dragSteps);
+                        this.attachedMesh.position.addInPlace(tmpVector);
+                        tmpSnapEvent.snapDistance = this.snapDistance * dragSteps;
+                        this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
+                }
+            }
+        });
+
+        this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
+            if (this._customMeshSet) {
+                return;
+            }
+            var isHovered = pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1);
+            var material = isHovered ? this._hoverMaterial : this._coloredMaterial;
+            this._rootMesh.getChildMeshes().forEach((m) => {
+                m.material = material;
+            });
+        });
+
+        var light = gizmoLayer._getSharedGizmoLight();
+        light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._rootMesh.getChildMeshes(false));
+    }
+    protected _attachedMeshChanged(value: Nullable<AbstractMesh>) {
+        if (this.dragBehavior) {
+            this.dragBehavior.enabled = value ? true : false;
+        }
+    }
+
+    /**
+     * If the gizmo is enabled
+     */
+    public set isEnabled(value: boolean) {
+        this._isEnabled = value;
+        if (!value) {
+            this.attachedMesh = null;
+        }
+        else {
+            if (this._parent) {
+                this.attachedMesh = this._parent.attachedMesh;
+            }
+        }
+    }
+    public get isEnabled(): boolean {
+        return this._isEnabled;
+    }
+    /**
+     * Disposes of the gizmo
+     */
+    public dispose() {
+        this.onSnapObservable.clear();
+        this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
+        this.dragBehavior.detach();
+        super.dispose();
+        if (this._plane) {
+            this._plane.dispose();
+        }
+        [this._coloredMaterial, this._hoverMaterial].forEach((matl) => {
+            if (matl) {
+                matl.dispose();
+            }
+        });
+    }
+}

+ 23 - 3
src/Gizmos/planeRotationGizmo.ts

@@ -13,6 +13,7 @@ import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 import { StandardMaterial } from "../Materials/standardMaterial";
 
 import "../Meshes/Builders/linesBuilder";
+import { RotationGizmo } from "./rotationGizmo";
 
 /**
  * Single plane rotation gizmo
@@ -34,6 +35,9 @@ export class PlaneRotationGizmo extends Gizmo {
      */
     public onSnapObservable = new Observable<{ snapDistance: number }>();
 
+    private _isEnabled: boolean = true;
+    private _parent: Nullable<RotationGizmo> = null;
+
     /**
      * Creates a PlaneRotationGizmo
      * @param gizmoLayer The utility layer the gizmo will be added to
@@ -41,9 +45,9 @@ export class PlaneRotationGizmo extends Gizmo {
      * @param color The color of the gizmo
      * @param tessellation Amount of tessellation to be used when creating rotation circles
      */
-    constructor(planeNormal: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, tessellation = 32) {
+    constructor(planeNormal: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, tessellation = 32, parent: Nullable<RotationGizmo> = null) {
         super(gizmoLayer);
-
+        this._parent = parent;
         // Create Material
         var coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
         coloredMaterial.diffuseColor = color;
@@ -207,7 +211,23 @@ export class PlaneRotationGizmo extends Gizmo {
             this.dragBehavior.enabled = value ? true : false;
         }
     }
-
+    /**
+         * If the gizmo is enabled
+         */
+    public set isEnabled(value: boolean) {
+        this._isEnabled = value;
+        if (!value) {
+            this.attachedMesh = null;
+        }
+        else {
+            if (this._parent) {
+                this.attachedMesh = this._parent.attachedMesh;
+            }
+        }
+    }
+    public get isEnabled(): boolean {
+        return this._isEnabled;
+    }
     /**
      * Disposes of the gizmo
      */

+ 90 - 32
src/Gizmos/positionGizmo.ts

@@ -8,6 +8,7 @@ import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
 import { Gizmo } from "./gizmo";
 import { AxisDragGizmo } from "./axisDragGizmo";
+import { PlaneDragGizmo } from "./planeDragGizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 /**
  * Gizmo that enables dragging a mesh along 3 axis
@@ -25,21 +26,51 @@ export class PositionGizmo extends Gizmo {
      * Internal gizmo used for interactions on the z axis
      */
     public zGizmo: AxisDragGizmo;
+    /**
+     * Internal gizmo used for interactions on the yz plane
+     */
+    public xPlaneGizmo: PlaneDragGizmo;
+    /**
+     * Internal gizmo used for interactions on the xz plane
+     */
+    public yPlaneGizmo: PlaneDragGizmo;
+    /**
+     * Internal gizmo used for interactions on the xy plane
+     */
+    public zPlaneGizmo: PlaneDragGizmo;
+
+    /**
+     * private variables
+     */
+    private _meshAttached: Nullable<AbstractMesh> = null;
+    private _updateGizmoRotationToMatchAttachedMesh: boolean;
+    private _snapDistance: number;
+    private _scaleRatio: number;
 
     /** Fires an event when any of it's sub gizmos are dragged */
     public onDragStartObservable = new Observable();
     /** Fires an event when any of it's sub gizmos are released from dragging */
     public onDragEndObservable = new Observable();
 
+    /**
+     * If set to true, planar drag is enabled
+     */
+    private _planarGizmoEnabled = false;
+
     public get attachedMesh() {
-        return this.xGizmo.attachedMesh;
+        return this._meshAttached;
     }
     public set attachedMesh(mesh: Nullable<AbstractMesh>) {
-        if (this.xGizmo) {
-            this.xGizmo.attachedMesh = mesh;
-            this.yGizmo.attachedMesh = mesh;
-            this.zGizmo.attachedMesh = mesh;
-        }
+        this._meshAttached = mesh;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
+            if (gizmo.isEnabled) {
+                gizmo.attachedMesh = mesh;
+            }
+            else {
+                gizmo.attachedMesh = null;
+            }
+        });
+
     }
     /**
      * Creates a PositionGizmo
@@ -47,12 +78,15 @@ export class PositionGizmo extends Gizmo {
      */
     constructor(gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer) {
         super(gizmoLayer);
-        this.xGizmo = new AxisDragGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer);
-        this.yGizmo = new AxisDragGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer);
-        this.zGizmo = new AxisDragGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer);
+        this.xGizmo = new AxisDragGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer, this);
+        this.yGizmo = new AxisDragGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer, this);
+        this.zGizmo = new AxisDragGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer, this);
 
+        this.xPlaneGizmo = new PlaneDragGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), this.gizmoLayer, this);
+        this.yPlaneGizmo = new PlaneDragGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), this.gizmoLayer, this);
+        this.zPlaneGizmo = new PlaneDragGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), this.gizmoLayer, this);
         // Relay drag events
-        [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
             gizmo.dragBehavior.onDragStartObservable.add(() => {
                 this.onDragStartObservable.notifyObservers({});
             });
@@ -64,52 +98,76 @@ export class PositionGizmo extends Gizmo {
         this.attachedMesh = null;
     }
 
+    /**
+     * If the planar drag gizmo is enabled
+     * setting this will enable/disable XY, XZ and YZ planes regardless of individual gizmo settings.
+     */
+    public set planarGizmoEnabled(value: boolean) {
+        this._planarGizmoEnabled = value;
+        [this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.isEnabled = value;
+                if (value) {
+                    gizmo.attachedMesh = this.attachedMesh;
+                }
+            }
+        }, this);
+    }
+    public get planarGizmoEnabled(): boolean {
+        return this._planarGizmoEnabled;
+    }
+
     public set updateGizmoRotationToMatchAttachedMesh(value: boolean) {
-        if (this.xGizmo) {
-            this.xGizmo.updateGizmoRotationToMatchAttachedMesh = value;
-            this.yGizmo.updateGizmoRotationToMatchAttachedMesh = value;
-            this.zGizmo.updateGizmoRotationToMatchAttachedMesh = value;
-        }
+        this._updateGizmoRotationToMatchAttachedMesh = value;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.updateGizmoRotationToMatchAttachedMesh = value;
+            }
+        });
     }
     public get updateGizmoRotationToMatchAttachedMesh() {
-        return this.xGizmo.updateGizmoRotationToMatchAttachedMesh;
+        return this._updateGizmoRotationToMatchAttachedMesh;
     }
 
     /**
      * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
      */
     public set snapDistance(value: number) {
-        if (this.xGizmo) {
-            this.xGizmo.snapDistance = value;
-            this.yGizmo.snapDistance = value;
-            this.zGizmo.snapDistance = value;
-        }
+        this._snapDistance = value;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.snapDistance = value;
+            }
+        });
     }
     public get snapDistance() {
-        return this.xGizmo.snapDistance;
+        return this._snapDistance;
     }
 
     /**
      * Ratio for the scale of the gizmo (Default: 1)
      */
     public set scaleRatio(value: number) {
-        if (this.xGizmo) {
-            this.xGizmo.scaleRatio = value;
-            this.yGizmo.scaleRatio = value;
-            this.zGizmo.scaleRatio = value;
-        }
+        this._scaleRatio = value;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.scaleRatio = value;
+            }
+        });
     }
     public get scaleRatio() {
-        return this.xGizmo.scaleRatio;
+        return this._scaleRatio;
     }
 
     /**
      * Disposes of the gizmo
      */
     public dispose() {
-        this.xGizmo.dispose();
-        this.yGizmo.dispose();
-        this.zGizmo.dispose();
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.dispose();
+            }
+        });
         this.onDragStartObservable.clear();
         this.onDragEndObservable.clear();
     }
@@ -119,6 +177,6 @@ export class PositionGizmo extends Gizmo {
      * @param mesh The mesh to replace the default mesh of the gizmo
      */
     public setCustomMesh(mesh: Mesh) {
-        Logger.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo)");
+        Logger.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo,gizmo.xPlaneGizmo, gizmo.yPlaneGizmo, gizmo.zPlaneGizmo)");
     }
 }

+ 16 - 9
src/Gizmos/rotationGizmo.ts

@@ -31,15 +31,22 @@ export class RotationGizmo extends Gizmo {
     /** Fires an event when any of it's sub gizmos are released from dragging */
     public onDragEndObservable = new Observable();
 
+    private _meshAttached: Nullable<AbstractMesh>;
+
     public get attachedMesh() {
-        return this.xGizmo.attachedMesh;
+        return this._meshAttached;
     }
     public set attachedMesh(mesh: Nullable<AbstractMesh>) {
-        if (this.xGizmo) {
-            this.xGizmo.attachedMesh = mesh;
-            this.yGizmo.attachedMesh = mesh;
-            this.zGizmo.attachedMesh = mesh;
-        }
+        this._meshAttached = mesh;
+
+        [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
+            if (gizmo.isEnabled) {
+                gizmo.attachedMesh = mesh;
+            }
+            else {
+                gizmo.attachedMesh = null;
+            }
+        });
     }
     /**
      * Creates a RotationGizmo
@@ -48,9 +55,9 @@ export class RotationGizmo extends Gizmo {
      */
     constructor(gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, tessellation = 32) {
         super(gizmoLayer);
-        this.xGizmo = new PlaneRotationGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer, tessellation);
-        this.yGizmo = new PlaneRotationGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer, tessellation);
-        this.zGizmo = new PlaneRotationGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer, tessellation);
+        this.xGizmo = new PlaneRotationGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer, tessellation, this);
+        this.yGizmo = new PlaneRotationGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer, tessellation, this);
+        this.zGizmo = new PlaneRotationGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer, tessellation, this);
 
         // Relay drag events
         [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {

+ 63 - 42
src/Gizmos/scaleGizmo.ts

@@ -7,6 +7,7 @@ import { PolyhedronBuilder } from "../Meshes/Builders/polyhedronBuilder";
 import { Gizmo } from "./gizmo";
 import { AxisScaleGizmo } from "./axisScaleGizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
+import { Mesh } from "../Meshes/mesh";
 /**
  * Gizmo that enables scaling a mesh along 3 axis
  */
@@ -29,21 +30,31 @@ export class ScaleGizmo extends Gizmo {
      */
     public uniformScaleGizmo: AxisScaleGizmo;
 
+    private _meshAttached: Nullable<AbstractMesh> = null;
+    private _updateGizmoRotationToMatchAttachedMesh: boolean;
+    private _snapDistance: number;
+    private _scaleRatio: number;
+    private _uniformScalingMesh: Mesh;
+    private _octahedron: Mesh;
+
     /** Fires an event when any of it's sub gizmos are dragged */
     public onDragStartObservable = new Observable();
     /** Fires an event when any of it's sub gizmos are released from dragging */
     public onDragEndObservable = new Observable();
 
     public get attachedMesh() {
-        return this.xGizmo.attachedMesh;
+        return this._meshAttached;
     }
     public set attachedMesh(mesh: Nullable<AbstractMesh>) {
-        if (this.xGizmo) {
-            this.xGizmo.attachedMesh = mesh;
-            this.yGizmo.attachedMesh = mesh;
-            this.zGizmo.attachedMesh = mesh;
-            this.uniformScaleGizmo.attachedMesh = mesh;
-        }
+        this._meshAttached = mesh;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
+            if (gizmo.isEnabled) {
+                gizmo.attachedMesh = mesh;
+            }
+            else {
+                gizmo.attachedMesh = null;
+            }
+        });
     }
     /**
      * Creates a ScaleGizmo
@@ -51,23 +62,23 @@ export class ScaleGizmo extends Gizmo {
      */
     constructor(gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer) {
         super(gizmoLayer);
-        this.xGizmo = new AxisScaleGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer);
-        this.yGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer);
-        this.zGizmo = new AxisScaleGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer);
+        this.xGizmo = new AxisScaleGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer, this);
+        this.yGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer, this);
+        this.zGizmo = new AxisScaleGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer, this);
 
         // Create uniform scale gizmo
-        this.uniformScaleGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Yellow().scale(0.5), gizmoLayer);
+        this.uniformScaleGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Yellow().scale(0.5), gizmoLayer, this);
         this.uniformScaleGizmo.updateGizmoRotationToMatchAttachedMesh = false;
         this.uniformScaleGizmo.uniformScaling = true;
-        var uniformScalingMesh = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
-        uniformScalingMesh.scaling.scaleInPlace(0.02);
-        uniformScalingMesh.visibility = 0;
-        var octahedron = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
-        octahedron.scaling.scaleInPlace(0.007);
-        uniformScalingMesh.addChild(octahedron);
-        this.uniformScaleGizmo.setCustomMesh(uniformScalingMesh, true);
+        this._uniformScalingMesh = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
+        this._uniformScalingMesh.scaling.scaleInPlace(0.02);
+        this._uniformScalingMesh.visibility = 0;
+        this._octahedron = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
+        this._octahedron.scaling.scaleInPlace(0.007);
+        this._uniformScalingMesh.addChild(this._octahedron);
+        this.uniformScaleGizmo.setCustomMesh(this._uniformScalingMesh, true);
         var light = gizmoLayer._getSharedGizmoLight();
-        light.includedOnlyMeshes = light.includedOnlyMeshes.concat(octahedron);
+        light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._octahedron);
 
         // Relay drag events
         [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
@@ -86,55 +97,65 @@ export class ScaleGizmo extends Gizmo {
         if (!value) {
             Logger.Warn("Setting updateGizmoRotationToMatchAttachedMesh = false on scaling gizmo is not supported.");
         }
-        if (this.xGizmo) {
-            this.xGizmo.updateGizmoRotationToMatchAttachedMesh = value;
-            this.yGizmo.updateGizmoRotationToMatchAttachedMesh = value;
-            this.zGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+        else {
+            this._updateGizmoRotationToMatchAttachedMesh = value;
+            [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
+                if (gizmo) {
+                    gizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                }
+            });
         }
     }
     public get updateGizmoRotationToMatchAttachedMesh() {
-        return this.xGizmo.updateGizmoRotationToMatchAttachedMesh;
+        return this._updateGizmoRotationToMatchAttachedMesh;
     }
 
     /**
      * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
      */
     public set snapDistance(value: number) {
-        if (this.xGizmo) {
-            this.xGizmo.snapDistance = value;
-            this.yGizmo.snapDistance = value;
-            this.zGizmo.snapDistance = value;
-            this.uniformScaleGizmo.snapDistance = value;
-        }
+        this._snapDistance = value;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.snapDistance = value;
+            }
+        });
     }
     public get snapDistance() {
-        return this.xGizmo.snapDistance;
+        return this._snapDistance;
     }
 
     /**
      * Ratio for the scale of the gizmo (Default: 1)
      */
     public set scaleRatio(value: number) {
-        if (this.xGizmo) {
-            this.xGizmo.scaleRatio = value;
-            this.yGizmo.scaleRatio = value;
-            this.zGizmo.scaleRatio = value;
-            this.uniformScaleGizmo.scaleRatio = value;
-        }
+        this._scaleRatio = value;
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.scaleRatio = value;
+            }
+        });
     }
     public get scaleRatio() {
-        return this.xGizmo.scaleRatio;
+        return this._scaleRatio;
     }
 
     /**
      * Disposes of the gizmo
      */
     public dispose() {
-        this.xGizmo.dispose();
-        this.yGizmo.dispose();
-        this.zGizmo.dispose();
-        this.uniformScaleGizmo.dispose();
+        [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
+            if (gizmo) {
+                gizmo.dispose();
+            }
+        });
         this.onDragStartObservable.clear();
         this.onDragEndObservable.clear();
+
+        [this._uniformScalingMesh, this._octahedron].forEach((msh) => {
+            if (msh) {
+                msh.dispose();
+            }
+        });
     }
 }