axisScaleGizmo.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { Observer, Observable } from "../Misc/observable";
  2. import { Nullable } from "../types";
  3. import { PointerInfo } from "../Events/pointerEvents";
  4. import { Vector3, Color3 } from "../Maths/math";
  5. import { AbstractMesh } from "../Meshes/abstractMesh";
  6. import { Mesh } from "../Meshes/mesh";
  7. import { LinesMesh } from "../Meshes/linesMesh";
  8. import { BoxBuilder } from "../Meshes/Builders/boxBuilder";
  9. import { CylinderBuilder } from "../Meshes/Builders/cylinderBuilder";
  10. import { StandardMaterial } from "../Materials/standardMaterial";
  11. import { PointerDragBehavior } from "../Behaviors/Meshes/pointerDragBehavior";
  12. import { _TimeToken } from "../Instrumentation/timeToken";
  13. import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
  14. import { Gizmo } from "./gizmo";
  15. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
  16. /**
  17. * Single axis scale gizmo
  18. */
  19. export class AxisScaleGizmo extends Gizmo {
  20. private _coloredMaterial: StandardMaterial;
  21. /**
  22. * Drag behavior responsible for the gizmos dragging interactions
  23. */
  24. public dragBehavior: PointerDragBehavior;
  25. private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
  26. /**
  27. * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
  28. */
  29. public snapDistance = 0;
  30. /**
  31. * Event that fires each time the gizmo snaps to a new location.
  32. * * snapDistance is the the change in distance
  33. */
  34. public onSnapObservable = new Observable<{ snapDistance: number }>();
  35. /**
  36. * If the scaling operation should be done on all axis (default: false)
  37. */
  38. public uniformScaling = false;
  39. /**
  40. * Creates an AxisScaleGizmo
  41. * @param gizmoLayer The utility layer the gizmo will be added to
  42. * @param dragAxis The axis which the gizmo will be able to scale on
  43. * @param color The color of the gizmo
  44. */
  45. constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer) {
  46. super(gizmoLayer);
  47. // Create Material
  48. this._coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
  49. this._coloredMaterial.diffuseColor = color;
  50. this._coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
  51. var hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
  52. hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
  53. // Build mesh on root node
  54. var arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
  55. var arrowMesh = BoxBuilder.CreateBox("yPosMesh", { size: 0.4 }, gizmoLayer.utilityLayerScene);
  56. var arrowTail = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0.005, height: 0.275, diameterBottom: 0.005, tessellation: 96 }, gizmoLayer.utilityLayerScene);
  57. arrowTail.material = this._coloredMaterial;
  58. arrow.addChild(arrowMesh);
  59. arrow.addChild(arrowTail);
  60. // Position arrow pointing in its drag axis
  61. arrowMesh.scaling.scaleInPlace(0.1);
  62. arrowMesh.material = this._coloredMaterial;
  63. arrowMesh.rotation.x = Math.PI / 2;
  64. arrowMesh.position.z += 0.3;
  65. arrowTail.position.z += 0.275 / 2;
  66. arrowTail.rotation.x = Math.PI / 2;
  67. arrow.lookAt(this._rootMesh.position.add(dragAxis));
  68. this._rootMesh.addChild(arrow);
  69. arrow.scaling.scaleInPlace(1 / 3);
  70. // Add drag behavior to handle events when the gizmo is dragged
  71. this.dragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
  72. this.dragBehavior.moveAttached = false;
  73. this._rootMesh.addBehavior(this.dragBehavior);
  74. var currentSnapDragDistance = 0;
  75. var tmpVector = new Vector3();
  76. var tmpSnapEvent = { snapDistance: 0 };
  77. this.dragBehavior.onDragObservable.add((event) => {
  78. if (this.attachedMesh) {
  79. // Drag strength is modified by the scale of the gizmo (eg. for small objects like boombox the strength will be increased to match the behavior of larger objects)
  80. var dragStrength = event.dragDistance * ((this.scaleRatio * 3) / this._rootMesh.scaling.length());
  81. // Snapping logic
  82. var snapped = false;
  83. var dragSteps = 0;
  84. if (this.uniformScaling) {
  85. this.attachedMesh.scaling.normalizeToRef(tmpVector);
  86. if (tmpVector.y < 0) {
  87. tmpVector.scaleInPlace(-1);
  88. }
  89. } else {
  90. tmpVector.copyFrom(dragAxis);
  91. }
  92. if (this.snapDistance == 0) {
  93. tmpVector.scaleToRef(dragStrength, tmpVector);
  94. } else {
  95. currentSnapDragDistance += dragStrength;
  96. if (Math.abs(currentSnapDragDistance) > this.snapDistance) {
  97. dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / this.snapDistance);
  98. if (currentSnapDragDistance < 0) {
  99. dragSteps *= -1;
  100. }
  101. currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
  102. tmpVector.scaleToRef(this.snapDistance * dragSteps, tmpVector);
  103. snapped = true;
  104. } else {
  105. tmpVector.scaleInPlace(0);
  106. }
  107. }
  108. this.attachedMesh.scaling.addInPlace(tmpVector);
  109. if (snapped) {
  110. tmpSnapEvent.snapDistance = this.snapDistance * dragSteps;
  111. this.onSnapObservable.notifyObservers(tmpSnapEvent);
  112. }
  113. }
  114. });
  115. this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
  116. if (this._customMeshSet) {
  117. return;
  118. }
  119. var isHovered = pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1);
  120. var material = isHovered ? hoverMaterial : this._coloredMaterial;
  121. this._rootMesh.getChildMeshes().forEach((m) => {
  122. m.material = material;
  123. if ((<LinesMesh>m).color) {
  124. (<LinesMesh>m).color = material.diffuseColor;
  125. }
  126. });
  127. });
  128. var light = gizmoLayer._getSharedGizmoLight();
  129. light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._rootMesh.getChildMeshes());
  130. }
  131. protected _attachedMeshChanged(value: Nullable<AbstractMesh>) {
  132. if (this.dragBehavior) {
  133. this.dragBehavior.enabled = value ? true : false;
  134. }
  135. }
  136. /**
  137. * Disposes of the gizmo
  138. */
  139. public dispose() {
  140. this.onSnapObservable.clear();
  141. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
  142. this.dragBehavior.detach();
  143. super.dispose();
  144. }
  145. /**
  146. * Disposes and replaces the current meshes in the gizmo with the specified mesh
  147. * @param mesh The mesh to replace the default mesh of the gizmo
  148. * @param useGizmoMaterial If the gizmo's default material should be used (default: false)
  149. */
  150. public setCustomMesh(mesh: Mesh, useGizmoMaterial: boolean = false) {
  151. super.setCustomMesh(mesh);
  152. if (useGizmoMaterial) {
  153. this._rootMesh.getChildMeshes().forEach((m) => {
  154. m.material = this._coloredMaterial;
  155. if ((<LinesMesh>m).color) {
  156. (<LinesMesh>m).color = this._coloredMaterial.diffuseColor;
  157. }
  158. });
  159. this._customMeshSet = false;
  160. }
  161. }
  162. }