瀏覽代碼

Gizmo Mesh Upgrade - Scale Gizmo Update

Dave Gould 4 年之前
父節點
當前提交
3220e3ca17
共有 2 個文件被更改,包括 224 次插入51 次删除
  1. 86 36
      src/Gizmos/axisScaleGizmo.ts
  2. 138 15
      src/Gizmos/scaleGizmo.ts

+ 86 - 36
src/Gizmos/axisScaleGizmo.ts

@@ -45,9 +45,12 @@ export class AxisScaleGizmo extends Gizmo {
     private _isEnabled: boolean = true;
     private _parent: Nullable<ScaleGizmo> = null;
 
-    private _arrow: AbstractMesh;
+    private _gizmoMesh: AbstractMesh;
     private _coloredMaterial: StandardMaterial;
     private _hoverMaterial: StandardMaterial;
+    private _disableMaterial: StandardMaterial;
+
+    private _eventListeners: any[] = [];
 
     /**
      * Creates an AxisScaleGizmo
@@ -65,26 +68,40 @@ export class AxisScaleGizmo extends Gizmo {
         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));
+        this._hoverMaterial.diffuseColor = Color3.Yellow();
 
-        // Build mesh on root node
-        this._arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
-        var arrowMesh = BoxBuilder.CreateBox("yPosMesh", { size: 0.4 * (1 + (thickness - 1) / 4) }, gizmoLayer.utilityLayerScene);
-        var arrowTail = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0.005 * thickness, height: 0.275, diameterBottom: 0.005 * thickness, tessellation: 96 }, gizmoLayer.utilityLayerScene);
-        arrowTail.material = this._coloredMaterial;
-        this._arrow.addChild(arrowMesh);
-        this._arrow.addChild(arrowTail);
+        this._disableMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
+        this._disableMaterial.diffuseColor = Color3.Gray();
+        this._disableMaterial.alpha = 0.4;
 
-        // Position arrow pointing in its drag axis
-        arrowMesh.scaling.scaleInPlace(0.1);
-        arrowMesh.material = this._coloredMaterial;
-        arrowMesh.rotation.x = Math.PI / 2;
-        arrowMesh.position.z += 0.3;
-        arrowTail.position.z += 0.275 / 2;
-        arrowTail.rotation.x = Math.PI / 2;
-        this._arrow.lookAt(this._rootMesh.position.add(dragAxis));
-        this._rootMesh.addChild(this._arrow);
-        this._arrow.scaling.scaleInPlace(1 / 3);
+        // Build mesh + Collider
+        this._gizmoMesh = new AbstractMesh("axis", gizmoLayer.utilityLayerScene);
+        const { arrowMesh, arrowTail } = this._createGizmoMesh(this._gizmoMesh, thickness);
+        this._createGizmoMesh(this._gizmoMesh, thickness * 4, true);
+
+        this._gizmoMesh.lookAt(this._rootMesh.position.add(dragAxis));
+        this._rootMesh.addChild(this._gizmoMesh);
+        this._gizmoMesh.scaling.scaleInPlace(1 / 3);
+
+        // Closure of inital prop values for resetting
+        const nodePosition = arrowMesh.position.clone();
+        const linePosition = arrowTail.position.clone();
+        const lineScale = arrowTail.scaling.clone();
+
+        const increaseGizmoMesh = (dragDistance: number) => {
+            const scalar = 0.15; // This will increase the rate of gizmo size on drag
+            const originalScale = arrowTail.scaling.y;
+            const newScale = originalScale + dragDistance * scalar;
+            arrowMesh.position.z += ((newScale - originalScale) / 4);
+            arrowTail.scaling.y = newScale;
+            arrowTail.position.z = arrowMesh.position.z / 2;
+        };
+
+        const resetGizmoMesh = () => {
+            arrowMesh.position = new Vector3(nodePosition.x, nodePosition.y, nodePosition.z);
+            arrowTail.position = new Vector3(linePosition.x, linePosition.y, linePosition.z);
+            arrowTail.scaling = new Vector3(lineScale.x, lineScale.y, lineScale.z);
+        };
 
         // Add drag behavior to handle events when the gizmo is dragged
         this.dragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
@@ -139,25 +156,55 @@ export class AxisScaleGizmo extends Gizmo {
                 this._matrixChanged();
             }
         });
+        // On Drag Listener: to move gizmo mesh with user action
+        this.dragBehavior.onDragObservable.add(e => increaseGizmoMesh(e.dragDistance));
+        this.dragBehavior.onDragEndObservable.add(resetGizmoMesh);
 
-        this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
-            if (this._customMeshSet) {
-                return;
-            }
-            this._isHovered = !!(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1));
-            var material = this._isHovered ? this._hoverMaterial : this._coloredMaterial;
-            this._rootMesh.getChildMeshes().forEach((m) => {
-                m.material = material;
-                if ((<LinesMesh>m).color) {
-                    (<LinesMesh>m).color = material.diffuseColor;
-                }
-            });
-        });
+        // Listeners for Universal Scalar
+        document.addEventListener('universalGizmoDrag', e => increaseGizmoMesh((e as any).detail));
+        document.addEventListener('universalGizmoEnd', resetGizmoMesh);
+        this._eventListeners.push({listener: 'universalGizmoDrag', fn: increaseGizmoMesh });
+        this._eventListeners.push({listener: 'universalGizmoEnd', fn: resetGizmoMesh });
+
+        const cache: any = {
+            material: this._coloredMaterial,
+            hoverMaterial: this._hoverMaterial,
+            disableMaterial: this._disableMaterial,
+            active: false
+        };
+        this._parent?.addToAxisCache((this._gizmoMesh as Mesh), cache);
 
         var light = gizmoLayer._getSharedGizmoLight();
         light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._rootMesh.getChildMeshes());
     }
 
+    private _createGizmoMesh(parentMesh: AbstractMesh, thickness: number, isCollider = false) {
+        var arrowMesh = BoxBuilder.CreateBox("yPosMesh", { size: 0.4 * (1 + (thickness - 1) / 4) }, this.gizmoLayer.utilityLayerScene);
+        var arrowTail = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0.005 * thickness, height: 0.275, diameterBottom: 0.005 * thickness, tessellation: 96 }, this.gizmoLayer.utilityLayerScene);
+
+        // Position arrow pointing in its drag axis
+        arrowMesh.scaling.scaleInPlace(0.1);
+        arrowMesh.material = this._coloredMaterial;
+        arrowMesh.rotation.x = Math.PI / 2;
+        arrowMesh.position.z += 0.3;
+
+        arrowTail.material = this._coloredMaterial;
+        arrowTail.position.z += 0.275 / 2;
+        arrowTail.rotation.x = Math.PI / 2;
+
+        if (isCollider) {
+            arrowMesh.name = 'ignore';
+            arrowMesh.visibility = 0;
+            arrowTail.name = 'ignore';
+            arrowTail.visibility = 0;
+        }
+
+        parentMesh.addChild(arrowMesh);
+        parentMesh.addChild(arrowTail);
+
+        return { arrowMesh, arrowTail };
+    }
+
     protected _attachedNodeChanged(value: Nullable<Node>) {
         if (this.dragBehavior) {
             this.dragBehavior.enabled = value ? true : false;
@@ -165,8 +212,8 @@ export class AxisScaleGizmo extends Gizmo {
     }
 
     /**
- * If the gizmo is enabled
- */
+     * If the gizmo is enabled
+     */
     public set isEnabled(value: boolean) {
         this._isEnabled = value;
         if (!value) {
@@ -191,14 +238,17 @@ export class AxisScaleGizmo extends Gizmo {
         this.onSnapObservable.clear();
         this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
         this.dragBehavior.detach();
-        if (this._arrow) {
-            this._arrow.dispose();
+        if (this._gizmoMesh) {
+            this._gizmoMesh.dispose();
         }
         [this._coloredMaterial, this._hoverMaterial].forEach((matl) => {
             if (matl) {
                 matl.dispose();
             }
         });
+        this._eventListeners.forEach(e => {
+            document.addEventListener(e.listener, e.fn, false);
+        });
         super.dispose();
     }
 

+ 138 - 15
src/Gizmos/scaleGizmo.ts

@@ -1,5 +1,5 @@
 import { Logger } from "../Misc/logger";
-import { Observable } from "../Misc/observable";
+import { Observable, Observer } from "../Misc/observable";
 import { Nullable } from "../types";
 import { Vector3 } from "../Maths/math.vector";
 import { Color3 } from '../Maths/math.color';
@@ -10,6 +10,9 @@ import { AxisScaleGizmo } from "./axisScaleGizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 import { Mesh } from "../Meshes/mesh";
 import { Node } from "../node";
+import { PointerEventTypes, PointerInfo } from "../Events/pointerEvents";
+import { LinesMesh } from "../Meshes/linesMesh";
+import { StandardMaterial } from "../Materials/standardMaterial";
 /**
  * Gizmo that enables scaling a mesh along 3 axis
  */
@@ -38,6 +41,12 @@ export class ScaleGizmo extends Gizmo {
     private _uniformScalingMesh: Mesh;
     private _octahedron: Mesh;
     private _sensitivity: number = 1;
+    private _observables: Nullable<Observer<PointerInfo>>[] = [];
+
+    /** Gizmo state variables used for UI behavior */
+    private dragging = false;
+    /** Node Caching for quick lookup */
+    private gizmoAxisCache: Map<Mesh, any> = new Map();
 
     /** Fires an event when any of it's sub gizmos are dragged */
     public onDragStartObservable = new Observable();
@@ -97,20 +106,7 @@ export class ScaleGizmo extends Gizmo {
         this.xGizmo = new AxisScaleGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer, this, thickness);
         this.yGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer, this, thickness);
         this.zGizmo = new AxisScaleGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer, this, thickness);
-
-        // Create uniform scale gizmo
-        this.uniformScaleGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Yellow().scale(0.5), gizmoLayer, this);
-        this.uniformScaleGizmo.updateGizmoRotationToMatchAttachedMesh = false;
-        this.uniformScaleGizmo.uniformScaling = 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(this._octahedron);
+        this.uniformScaleGizmo = this.createUniformScaleMesh();
 
         // Relay drag events
         [this.xGizmo, this.yGizmo, this.zGizmo, this.uniformScaleGizmo].forEach((gizmo) => {
@@ -124,6 +120,54 @@ export class ScaleGizmo extends Gizmo {
 
         this.attachedMesh = null;
         this.attachedNode = null;
+        this.subscribeToPointerObserver();
+    }
+
+    createUniformScaleMesh() {
+        const coloredMaterial = new StandardMaterial("", this.gizmoLayer.utilityLayerScene);
+        coloredMaterial.diffuseColor = Color3.Gray();
+
+        const hoverMaterial = new StandardMaterial("", this.gizmoLayer.utilityLayerScene);
+        hoverMaterial.diffuseColor = Color3.Yellow();
+
+        const disableMaterial = new StandardMaterial("", this.gizmoLayer.utilityLayerScene);
+        disableMaterial.diffuseColor = Color3.Gray();
+        disableMaterial.alpha = 0.4;
+
+        const uniformScaleGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Gray().scale(0.5), this.gizmoLayer, this);
+        uniformScaleGizmo.updateGizmoRotationToMatchAttachedMesh = false;
+        uniformScaleGizmo.uniformScaling = true;
+        this._uniformScalingMesh = PolyhedronBuilder.CreatePolyhedron("uniform", { type: 1 }, uniformScaleGizmo.gizmoLayer.utilityLayerScene);
+        this._uniformScalingMesh.scaling.scaleInPlace(0.02);
+        this._uniformScalingMesh.visibility = 0;
+        this._octahedron = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, uniformScaleGizmo.gizmoLayer.utilityLayerScene);
+        this._octahedron.scaling.scaleInPlace(0.007);
+        this._uniformScalingMesh.addChild(this._octahedron);
+        uniformScaleGizmo.setCustomMesh(this._uniformScalingMesh, true);
+        var light = this.gizmoLayer._getSharedGizmoLight();
+        light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._octahedron);
+
+        // Drag Event Listeners
+        uniformScaleGizmo.dragBehavior.onDragObservable.add(e => {
+            document.dispatchEvent(new CustomEvent('universalGizmoDrag', {
+                detail: e.delta.y
+            }));
+        });
+
+        uniformScaleGizmo.dragBehavior.onDragEndObservable.add(e => {
+            document.dispatchEvent(new CustomEvent('universalGizmoEnd'));
+        });
+
+        const cache = {
+            material: coloredMaterial,
+            hoverMaterial,
+            disableMaterial,
+            active: false
+        };
+
+        this.addToAxisCache(uniformScaleGizmo._rootMesh, cache);
+
+        return uniformScaleGizmo;
     }
 
     public set updateGizmoRotationToMatchAttachedMesh(value: boolean) {
@@ -189,6 +233,82 @@ export class ScaleGizmo extends Gizmo {
     }
 
     /**
+     * Builds Gizmo Axis Cache to enable features such as hover state preservation and graying out other axis during manipulation
+     * @param mesh Axis gizmo mesh
+      @param cache display gizmo axis thickness
+     */
+    public addToAxisCache(mesh: Mesh, cache: any) {
+        this.gizmoAxisCache.set(mesh, cache);
+    }
+
+    public subscribeToPointerObserver(): void {
+        // Assumption, if user sets custom mesh, it will be disposed and pointerInfo will never hold reference to that mesh.
+        // Will be equivilent to if (this._customMeshSet) return;
+        const pointerObserver = this.gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
+            if (pointerInfo.pickInfo) {
+                // On Hover Logic
+                console.log(pointerInfo.pickInfo.pickedMesh?.id);
+                if (pointerInfo.type === PointerEventTypes.POINTERMOVE) {
+                    if (this.dragging) { return; }
+                    this.gizmoAxisCache.forEach((statusMap, parentMesh) => {
+                        const isHovered = pointerInfo.pickInfo && (parentMesh.getChildMeshes().indexOf((pointerInfo.pickInfo.pickedMesh as Mesh)) != -1);
+                        const material = isHovered || statusMap.active ? statusMap.hoverMaterial : statusMap.material;
+                        parentMesh.getChildMeshes().forEach((m) => {
+                            if (m.name !== 'ignore') {
+                                m.material = material;
+                                if ((m as LinesMesh).color) {
+                                    (m as LinesMesh).color = material.diffuseColor;
+                                }
+                            }
+                        });
+                    });
+                }
+
+                // On Mouse Down
+                if (pointerInfo.type === PointerEventTypes.POINTERDOWN) {
+                    // If user Clicked Gizmo
+                    if (this.gizmoAxisCache.has(pointerInfo.pickInfo.pickedMesh?.parent as Mesh)) {
+                        this.dragging = true;
+                        const statusMap = this.gizmoAxisCache.get(pointerInfo.pickInfo.pickedMesh?.parent as Mesh);
+                        statusMap!.active = true;
+                        console.log(this.gizmoAxisCache);
+                        this.gizmoAxisCache.forEach((statusMap, parentMesh) => {
+                            const isHovered = pointerInfo.pickInfo && (parentMesh.getChildMeshes().indexOf((pointerInfo.pickInfo.pickedMesh as Mesh)) != -1);
+                            const material = isHovered || statusMap.active ? statusMap.hoverMaterial : statusMap.disableMaterial;
+                            parentMesh.getChildMeshes().forEach((m) => {
+                                if (m.name !== 'ignore') {
+                                    m.material = material;
+                                    if ((m as LinesMesh).color) {
+                                        (m as LinesMesh).color = material.diffuseColor;
+                                    }
+                                }
+                            });
+                        });
+                    }
+                }
+
+                // On Mouse Up
+                if (pointerInfo.type === PointerEventTypes.POINTERUP) {
+                    this.gizmoAxisCache.forEach((statusMap, parentMesh) => {
+                        statusMap.active = false;
+                        this.dragging = false;
+                        parentMesh.getChildMeshes().forEach((m) => {
+                            if (m.name !== 'ignore') {
+                                m.material = statusMap.material;
+                                if ((m as LinesMesh).color) {
+                                    (m as LinesMesh).color = statusMap.material.diffuseColor;
+                                }
+                            }
+                        });
+                    });
+                }
+            }
+        });
+
+        this._observables = [pointerObserver];
+    }
+
+    /**
      * Disposes of the gizmo
      */
     public dispose() {
@@ -197,6 +317,9 @@ export class ScaleGizmo extends Gizmo {
                 gizmo.dispose();
             }
         });
+        this._observables.forEach(obs => {
+            this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(obs);
+        });
         this.onDragStartObservable.clear();
         this.onDragEndObservable.clear();