axisScaleGizmo.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. import { ScaleGizmo } from "./scaleGizmo";
  17. /**
  18. * Single axis scale gizmo
  19. */
  20. export class AxisScaleGizmo extends Gizmo {
  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. private _isEnabled: boolean = true;
  40. private _parent: Nullable<ScaleGizmo> = null;
  41. private _arrow: AbstractMesh;
  42. private _coloredMaterial: StandardMaterial;
  43. private _hoverMaterial: StandardMaterial;
  44. /**
  45. * Creates an AxisScaleGizmo
  46. * @param gizmoLayer The utility layer the gizmo will be added to
  47. * @param dragAxis The axis which the gizmo will be able to scale on
  48. * @param color The color of the gizmo
  49. */
  50. constructor(dragAxis: Vector3, color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultUtilityLayer, parent: Nullable<ScaleGizmo> = null) {
  51. super(gizmoLayer);
  52. this._parent = parent;
  53. // Create Material
  54. this._coloredMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
  55. this._coloredMaterial.diffuseColor = color;
  56. this._coloredMaterial.specularColor = color.subtract(new Color3(0.1, 0.1, 0.1));
  57. this._hoverMaterial = new StandardMaterial("", gizmoLayer.utilityLayerScene);
  58. this._hoverMaterial.diffuseColor = color.add(new Color3(0.3, 0.3, 0.3));
  59. // Build mesh on root node
  60. this._arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
  61. var arrowMesh = BoxBuilder.CreateBox("yPosMesh", { size: 0.4 }, gizmoLayer.utilityLayerScene);
  62. var arrowTail = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0.005, height: 0.275, diameterBottom: 0.005, tessellation: 96 }, gizmoLayer.utilityLayerScene);
  63. arrowTail.material = this._coloredMaterial;
  64. this._arrow.addChild(arrowMesh);
  65. this._arrow.addChild(arrowTail);
  66. // Position arrow pointing in its drag axis
  67. arrowMesh.scaling.scaleInPlace(0.1);
  68. arrowMesh.material = this._coloredMaterial;
  69. arrowMesh.rotation.x = Math.PI / 2;
  70. arrowMesh.position.z += 0.3;
  71. arrowTail.position.z += 0.275 / 2;
  72. arrowTail.rotation.x = Math.PI / 2;
  73. this._arrow.lookAt(this._rootMesh.position.add(dragAxis));
  74. this._rootMesh.addChild(this._arrow);
  75. this._arrow.scaling.scaleInPlace(1 / 3);
  76. // Add drag behavior to handle events when the gizmo is dragged
  77. this.dragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
  78. this.dragBehavior.moveAttached = false;
  79. this._rootMesh.addBehavior(this.dragBehavior);
  80. var currentSnapDragDistance = 0;
  81. var tmpVector = new Vector3();
  82. var tmpSnapEvent = { snapDistance: 0 };
  83. this.dragBehavior.onDragObservable.add((event) => {
  84. if (this.attachedMesh) {
  85. // 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)
  86. var dragStrength = event.dragDistance * ((this.scaleRatio * 3) / this._rootMesh.scaling.length());
  87. // Snapping logic
  88. var snapped = false;
  89. var dragSteps = 0;
  90. if (this.uniformScaling) {
  91. this.attachedMesh.scaling.normalizeToRef(tmpVector);
  92. if (tmpVector.y < 0) {
  93. tmpVector.scaleInPlace(-1);
  94. }
  95. } else {
  96. tmpVector.copyFrom(dragAxis);
  97. }
  98. if (this.snapDistance == 0) {
  99. tmpVector.scaleToRef(dragStrength, tmpVector);
  100. } else {
  101. currentSnapDragDistance += dragStrength;
  102. if (Math.abs(currentSnapDragDistance) > this.snapDistance) {
  103. dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / this.snapDistance);
  104. if (currentSnapDragDistance < 0) {
  105. dragSteps *= -1;
  106. }
  107. currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
  108. tmpVector.scaleToRef(this.snapDistance * dragSteps, tmpVector);
  109. snapped = true;
  110. } else {
  111. tmpVector.scaleInPlace(0);
  112. }
  113. }
  114. this.attachedMesh.scaling.addInPlace(tmpVector);
  115. if (snapped) {
  116. tmpSnapEvent.snapDistance = this.snapDistance * dragSteps;
  117. this.onSnapObservable.notifyObservers(tmpSnapEvent);
  118. }
  119. }
  120. });
  121. this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
  122. if (this._customMeshSet) {
  123. return;
  124. }
  125. var isHovered = pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1);
  126. var material = isHovered ? this._hoverMaterial : this._coloredMaterial;
  127. this._rootMesh.getChildMeshes().forEach((m) => {
  128. m.material = material;
  129. if ((<LinesMesh>m).color) {
  130. (<LinesMesh>m).color = material.diffuseColor;
  131. }
  132. });
  133. });
  134. var light = gizmoLayer._getSharedGizmoLight();
  135. light.includedOnlyMeshes = light.includedOnlyMeshes.concat(this._rootMesh.getChildMeshes());
  136. }
  137. protected _attachedMeshChanged(value: Nullable<AbstractMesh>) {
  138. if (this.dragBehavior) {
  139. this.dragBehavior.enabled = value ? true : false;
  140. }
  141. }
  142. /**
  143. * If the gizmo is enabled
  144. */
  145. public set isEnabled(value: boolean) {
  146. this._isEnabled = value;
  147. if (!value) {
  148. this.attachedMesh = null;
  149. }
  150. else {
  151. if (this._parent) {
  152. this.attachedMesh = this._parent.attachedMesh;
  153. }
  154. }
  155. }
  156. public get isEnabled(): boolean {
  157. return this._isEnabled;
  158. }
  159. /**
  160. * Disposes of the gizmo
  161. */
  162. public dispose() {
  163. this.onSnapObservable.clear();
  164. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
  165. this.dragBehavior.detach();
  166. if (this._arrow) {
  167. this._arrow.dispose();
  168. }
  169. [this._coloredMaterial, this._hoverMaterial].forEach((matl) => {
  170. if (matl) {
  171. matl.dispose();
  172. }
  173. });
  174. super.dispose();
  175. }
  176. /**
  177. * Disposes and replaces the current meshes in the gizmo with the specified mesh
  178. * @param mesh The mesh to replace the default mesh of the gizmo
  179. * @param useGizmoMaterial If the gizmo's default material should be used (default: false)
  180. */
  181. public setCustomMesh(mesh: Mesh, useGizmoMaterial: boolean = false) {
  182. super.setCustomMesh(mesh);
  183. if (useGizmoMaterial) {
  184. this._rootMesh.getChildMeshes().forEach((m) => {
  185. m.material = this._coloredMaterial;
  186. if ((<LinesMesh>m).color) {
  187. (<LinesMesh>m).color = this._coloredMaterial.diffuseColor;
  188. }
  189. });
  190. this._customMeshSet = false;
  191. }
  192. }
  193. }