gizmoManager.ts 9.6 KB

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