gizmoManager.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import { Observer, Observable } from "../Misc/observable";
  2. import { Nullable } from "../types";
  3. import { PointerInfo, PointerEventTypes } from "../Events/pointerEvents";
  4. import { Scene, IDisposable } from "../scene";
  5. import { Node } from "../node";
  6. import { AbstractMesh } from "../Meshes/abstractMesh";
  7. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
  8. import { Color3 } from '../Maths/math.color';
  9. import { SixDofDragBehavior } from "../Behaviors/Meshes/sixDofDragBehavior";
  10. import { Gizmo } from "./gizmo";
  11. import { RotationGizmo } from "./rotationGizmo";
  12. import { PositionGizmo } from "./positionGizmo";
  13. import { ScaleGizmo } from "./scaleGizmo";
  14. import { BoundingBoxGizmo } from "./boundingBoxGizmo";
  15. /**
  16. * Helps setup gizmo's in the scene to rotate/scale/position meshes
  17. */
  18. export class GizmoManager implements IDisposable {
  19. /**
  20. * Gizmo's created by the gizmo manager, gizmo will be null until gizmo has been enabled for the first time
  21. */
  22. public gizmos: { positionGizmo: Nullable<PositionGizmo>, rotationGizmo: Nullable<RotationGizmo>, scaleGizmo: Nullable<ScaleGizmo>, boundingBoxGizmo: Nullable<BoundingBoxGizmo> };
  23. /** When true, the gizmo will be detached from the current object when a pointer down occurs with an empty picked mesh */
  24. public clearGizmoOnEmptyPointerEvent = false;
  25. /** Fires an event when the manager is attached to a mesh */
  26. public onAttachedToMeshObservable = new Observable<Nullable<AbstractMesh>>();
  27. private _gizmosEnabled = { positionGizmo: false, rotationGizmo: false, scaleGizmo: false, boundingBoxGizmo: false };
  28. private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
  29. private _attachedMesh: Nullable<AbstractMesh> = null;
  30. private _boundingBoxColor = Color3.FromHexString("#0984e3");
  31. private _defaultUtilityLayer: UtilityLayerRenderer;
  32. private _defaultKeepDepthUtilityLayer: UtilityLayerRenderer;
  33. private _thickness: number = 1;
  34. /**
  35. * When bounding box gizmo is enabled, this can be used to track drag/end events
  36. */
  37. public boundingBoxDragBehavior = new SixDofDragBehavior();
  38. /**
  39. * Array of meshes which will have the gizmo attached when a pointer selected them. If null, all meshes are attachable. (Default: null)
  40. */
  41. public attachableMeshes: Nullable<Array<AbstractMesh>> = null;
  42. /**
  43. * If pointer events should perform attaching/detaching a gizmo, if false this can be done manually via attachToMesh. (Default: true)
  44. */
  45. public usePointerToAttachGizmos = true;
  46. /**
  47. * Utility layer that the bounding box gizmo belongs to
  48. */
  49. public get keepDepthUtilityLayer() {
  50. return this._defaultKeepDepthUtilityLayer;
  51. }
  52. /**
  53. * Utility layer that all gizmos besides bounding box belong to
  54. */
  55. public get utilityLayer() {
  56. return this._defaultUtilityLayer;
  57. }
  58. /**
  59. * Instatiates a gizmo manager
  60. * @param scene the scene to overlay the gizmos on top of
  61. * @param thickness display gizmo axis thickness
  62. */
  63. constructor(private scene: Scene, thickness: number = 1) {
  64. this._defaultKeepDepthUtilityLayer = new UtilityLayerRenderer(scene);
  65. this._defaultKeepDepthUtilityLayer.utilityLayerScene.autoClearDepthAndStencil = false;
  66. this._defaultUtilityLayer = new UtilityLayerRenderer(scene);
  67. this._thickness = thickness;
  68. this.gizmos = { positionGizmo: null, rotationGizmo: null, scaleGizmo: null, boundingBoxGizmo: null };
  69. // Instatiate/dispose gizmos based on pointer actions
  70. this._pointerObserver = scene.onPointerObservable.add((pointerInfo) => {
  71. if (!this.usePointerToAttachGizmos) {
  72. return;
  73. }
  74. if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
  75. if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh) {
  76. var node: Nullable<Node> = pointerInfo.pickInfo.pickedMesh;
  77. if (this.attachableMeshes == null) {
  78. // Attach to the most parent node
  79. while (node && node.parent != null) {
  80. node = node.parent;
  81. }
  82. } else {
  83. // Attach to the parent node that is an attachableMesh
  84. var found = false;
  85. this.attachableMeshes.forEach((mesh) => {
  86. if (node && (node == mesh || node.isDescendantOf(mesh))) {
  87. node = mesh;
  88. found = true;
  89. }
  90. });
  91. if (!found) {
  92. node = null;
  93. }
  94. }
  95. if (node instanceof AbstractMesh) {
  96. if (this._attachedMesh != node) {
  97. this.attachToMesh(node);
  98. }
  99. } else {
  100. if (this.clearGizmoOnEmptyPointerEvent) {
  101. this.attachToMesh(null);
  102. }
  103. }
  104. } else {
  105. if (this.clearGizmoOnEmptyPointerEvent) {
  106. this.attachToMesh(null);
  107. }
  108. }
  109. }
  110. });
  111. }
  112. /**
  113. * Attaches a set of gizmos to the specified mesh
  114. * @param mesh The mesh the gizmo's should be attached to
  115. */
  116. public attachToMesh(mesh: Nullable<AbstractMesh>) {
  117. if (this._attachedMesh) {
  118. this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
  119. }
  120. this._attachedMesh = mesh;
  121. for (var key in this.gizmos) {
  122. var gizmo = <Nullable<Gizmo>>((<any>this.gizmos)[key]);
  123. if (gizmo && (<any>this._gizmosEnabled)[key]) {
  124. gizmo.attachedMesh = mesh;
  125. }
  126. }
  127. if (this.boundingBoxGizmoEnabled && this._attachedMesh) {
  128. this._attachedMesh.addBehavior(this.boundingBoxDragBehavior);
  129. }
  130. this.onAttachedToMeshObservable.notifyObservers(mesh);
  131. }
  132. /**
  133. * If the position gizmo is enabled
  134. */
  135. public set positionGizmoEnabled(value: boolean) {
  136. if (value) {
  137. if (!this.gizmos.positionGizmo) {
  138. this.gizmos.positionGizmo = new PositionGizmo(this._defaultUtilityLayer, this._thickness);
  139. }
  140. this.gizmos.positionGizmo.attachedMesh = this._attachedMesh;
  141. } else if (this.gizmos.positionGizmo) {
  142. this.gizmos.positionGizmo.attachedMesh = null;
  143. }
  144. this._gizmosEnabled.positionGizmo = value;
  145. }
  146. public get positionGizmoEnabled(): boolean {
  147. return this._gizmosEnabled.positionGizmo;
  148. }
  149. /**
  150. * If the rotation gizmo is enabled
  151. */
  152. public set rotationGizmoEnabled(value: boolean) {
  153. if (value) {
  154. if (!this.gizmos.rotationGizmo) {
  155. this.gizmos.rotationGizmo = new RotationGizmo(this._defaultUtilityLayer, 32, false, this._thickness);
  156. }
  157. this.gizmos.rotationGizmo.attachedMesh = this._attachedMesh;
  158. } else if (this.gizmos.rotationGizmo) {
  159. this.gizmos.rotationGizmo.attachedMesh = null;
  160. }
  161. this._gizmosEnabled.rotationGizmo = value;
  162. }
  163. public get rotationGizmoEnabled(): boolean {
  164. return this._gizmosEnabled.rotationGizmo;
  165. }
  166. /**
  167. * If the scale gizmo is enabled
  168. */
  169. public set scaleGizmoEnabled(value: boolean) {
  170. if (value) {
  171. this.gizmos.scaleGizmo = this.gizmos.scaleGizmo || new ScaleGizmo(this._defaultUtilityLayer, this._thickness);
  172. this.gizmos.scaleGizmo.attachedMesh = this._attachedMesh;
  173. } else if (this.gizmos.scaleGizmo) {
  174. this.gizmos.scaleGizmo.attachedMesh = null;
  175. }
  176. this._gizmosEnabled.scaleGizmo = value;
  177. }
  178. public get scaleGizmoEnabled(): boolean {
  179. return this._gizmosEnabled.scaleGizmo;
  180. }
  181. /**
  182. * If the boundingBox gizmo is enabled
  183. */
  184. public set boundingBoxGizmoEnabled(value: boolean) {
  185. if (value) {
  186. this.gizmos.boundingBoxGizmo = this.gizmos.boundingBoxGizmo || new BoundingBoxGizmo(this._boundingBoxColor, this._defaultKeepDepthUtilityLayer);
  187. this.gizmos.boundingBoxGizmo.attachedMesh = this._attachedMesh;
  188. if (this._attachedMesh) {
  189. this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
  190. this._attachedMesh.addBehavior(this.boundingBoxDragBehavior);
  191. }
  192. } else if (this.gizmos.boundingBoxGizmo) {
  193. if (this._attachedMesh) {
  194. this._attachedMesh.removeBehavior(this.boundingBoxDragBehavior);
  195. }
  196. this.gizmos.boundingBoxGizmo.attachedMesh = null;
  197. }
  198. this._gizmosEnabled.boundingBoxGizmo = value;
  199. }
  200. public get boundingBoxGizmoEnabled(): boolean {
  201. return this._gizmosEnabled.boundingBoxGizmo;
  202. }
  203. /**
  204. * Disposes of the gizmo manager
  205. */
  206. public dispose() {
  207. this.scene.onPointerObservable.remove(this._pointerObserver);
  208. for (var key in this.gizmos) {
  209. var gizmo = <Nullable<Gizmo>>((<any>this.gizmos)[key]);
  210. if (gizmo) {
  211. gizmo.dispose();
  212. }
  213. }
  214. this._defaultKeepDepthUtilityLayer.dispose();
  215. this._defaultUtilityLayer.dispose();
  216. this.boundingBoxDragBehavior.detach();
  217. this.onAttachedToMeshObservable.clear();
  218. }
  219. }