axisDragGizmo.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { Observer, Observable } from "../Misc/observable";
  2. import { Nullable } from "../types";
  3. import { PointerInfo } from "../Events/pointerEvents";
  4. import { Vector3, Matrix } from "../Maths/math";
  5. import { TransformNode } from "../Meshes/transformNode";
  6. import { AbstractMesh } from "../Meshes/abstractMesh";
  7. import { Mesh } from "../Meshes/mesh";
  8. import { LinesMesh } from "../Meshes/linesMesh";
  9. import { CylinderBuilder } from "../Meshes/Builders/cylinderBuilder";
  10. import { PointerDragBehavior } from "../Behaviors/Meshes/pointerDragBehavior";
  11. import { _TimeToken } from "../Instrumentation/timeToken";
  12. import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
  13. import { Gizmo } from "./gizmo";
  14. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
  15. import { StandardMaterial } from "../Materials/standardMaterial";
  16. import { Scene } from "../scene";
  17. import { PositionGizmo } from "./positionGizmo";
  18. import { Color3 } from '../Maths/math.color';
  19. /**
  20. * Single axis drag gizmo
  21. */
  22. export class AxisDragGizmo extends Gizmo {
  23. /**
  24. * Drag behavior responsible for the gizmos dragging interactions
  25. */
  26. public dragBehavior: PointerDragBehavior;
  27. private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
  28. /**
  29. * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
  30. */
  31. public snapDistance = 0;
  32. /**
  33. * Event that fires each time the gizmo snaps to a new location.
  34. * * snapDistance is the the change in distance
  35. */
  36. public onSnapObservable = new Observable<{ snapDistance: number }>();
  37. private _isEnabled: boolean = true;
  38. private _parent: Nullable<PositionGizmo> = null;
  39. private _arrow: TransformNode;
  40. private _coloredMaterial: StandardMaterial;
  41. private _hoverMaterial: StandardMaterial;
  42. /** @hidden */
  43. public static _CreateArrow(scene: Scene, material: StandardMaterial): TransformNode {
  44. var arrow = new TransformNode("arrow", scene);
  45. var cylinder = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0, height: 0.075, diameterBottom: 0.0375, tessellation: 96 }, scene);
  46. var line = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0.005, height: 0.275, diameterBottom: 0.005, tessellation: 96 }, scene);
  47. line.material = material;
  48. cylinder.parent = arrow;
  49. line.parent = arrow;
  50. // Position arrow pointing in its drag axis
  51. cylinder.material = material;
  52. cylinder.rotation.x = Math.PI / 2;
  53. cylinder.position.z += 0.3;
  54. line.position.z += 0.275 / 2;
  55. line.rotation.x = Math.PI / 2;
  56. return arrow;
  57. }
  58. /** @hidden */
  59. public static _CreateArrowInstance(scene: Scene, arrow: TransformNode): TransformNode {
  60. const instance = new TransformNode("arrow", scene);
  61. for (const mesh of arrow.getChildMeshes()) {
  62. const childInstance = (mesh as Mesh).createInstance(mesh.name);
  63. childInstance.parent = instance;
  64. }
  65. return instance;
  66. }
  67. /**
  68. * Creates an AxisDragGizmo
  69. * @param gizmoLayer The utility layer the gizmo will be added to
  70. * @param dragAxis The axis which the gizmo will be able to drag on
  71. * @param color The color of the gizmo
  72. */
  73. constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, parent: Nullable<PositionGizmo> = null) {
  74. super(gizmoLayer);
  75. this._parent = parent;
  76. // Create Material
  77. this._coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
  78. this._coloredMaterial.diffuseColor = color;
  79. this._coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
  80. this._hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
  81. this._hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
  82. // Build mesh on root node
  83. this._arrow = AxisDragGizmo._CreateArrow(gizmoLayer.utilityLayerScene, this._coloredMaterial);
  84. this._arrow.lookAt(this._rootMesh.position.add(dragAxis));
  85. this._arrow.scaling.scaleInPlace(1 / 3);
  86. this._arrow.parent = this._rootMesh;
  87. var currentSnapDragDistance = 0;
  88. var tmpVector = new Vector3();
  89. var tmpSnapEvent = { snapDistance: 0 };
  90. // Add drag behavior to handle events when the gizmo is dragged
  91. this.dragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
  92. this.dragBehavior.moveAttached = false;
  93. this._rootMesh.addBehavior(this.dragBehavior);
  94. var localDelta = new Vector3();
  95. var tmpMatrix = new Matrix();
  96. this.dragBehavior.onDragObservable.add((event) => {
  97. if (this.attachedMesh) {
  98. // Convert delta to local translation if it has a parent
  99. if (this.attachedMesh.parent) {
  100. this.attachedMesh.parent.computeWorldMatrix().invertToRef(tmpMatrix);
  101. tmpMatrix.setTranslationFromFloats(0, 0, 0);
  102. Vector3.TransformCoordinatesToRef(event.delta, tmpMatrix, localDelta);
  103. } else {
  104. localDelta.copyFrom(event.delta);
  105. }
  106. // Snapping logic
  107. if (this.snapDistance == 0) {
  108. this.attachedMesh.position.addInPlace(localDelta);
  109. } else {
  110. currentSnapDragDistance += event.dragDistance;
  111. if (Math.abs(currentSnapDragDistance) > this.snapDistance) {
  112. var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / this.snapDistance);
  113. currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
  114. localDelta.normalizeToRef(tmpVector);
  115. tmpVector.scaleInPlace(this.snapDistance * dragSteps);
  116. this.attachedMesh.position.addInPlace(tmpVector);
  117. tmpSnapEvent.snapDistance = this.snapDistance * dragSteps;
  118. this.onSnapObservable.notifyObservers(tmpSnapEvent);
  119. }
  120. }
  121. }
  122. });
  123. this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
  124. if (this._customMeshSet) {
  125. return;
  126. }
  127. var isHovered = pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1);
  128. var material = isHovered ? this._hoverMaterial : this._coloredMaterial;
  129. this._rootMesh.getChildMeshes().forEach((m) => {
  130. m.material = material;
  131. if ((<LinesMesh>m).color) {
  132. (<LinesMesh>m).color = material.diffuseColor;
  133. }
  134. });
  135. });
  136. var light = gizmoLayer._getSharedGizmoLight();
  137. light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._rootMesh.getChildMeshes(false));
  138. }
  139. protected _attachedMeshChanged(value: Nullable<AbstractMesh>) {
  140. if (this.dragBehavior) {
  141. this.dragBehavior.enabled = value ? true : false;
  142. }
  143. }
  144. /**
  145. * If the gizmo is enabled
  146. */
  147. public set isEnabled(value: boolean) {
  148. this._isEnabled = value;
  149. if (!value) {
  150. this.attachedMesh = null;
  151. }
  152. else {
  153. if (this._parent) {
  154. this.attachedMesh = this._parent.attachedMesh;
  155. }
  156. }
  157. }
  158. public get isEnabled(): boolean {
  159. return this._isEnabled;
  160. }
  161. /**
  162. * Disposes of the gizmo
  163. */
  164. public dispose() {
  165. this.onSnapObservable.clear();
  166. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
  167. this.dragBehavior.detach();
  168. if (this._arrow) {
  169. this._arrow.dispose();
  170. }
  171. [this._coloredMaterial, this._hoverMaterial].forEach((matl) => {
  172. if (matl) {
  173. matl.dispose();
  174. }
  175. });
  176. super.dispose();
  177. }
  178. }