rotationGizmo.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import { Logger } from "../Misc/logger.js";
  2. import { Observable } from "../Misc/observable.js";
  3. import { Vector3 } from "../Maths/math.vector.js";
  4. import { Color3 } from "../Maths/math.color.js";
  5. import { Gizmo } from "./gizmo.js";
  6. import { PlaneRotationGizmo } from "./planeRotationGizmo.js";
  7. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer.js";
  8. /**
  9. * Gizmo that enables rotating a mesh along 3 axis
  10. */
  11. export class RotationGizmo extends Gizmo {
  12. get attachedMesh() {
  13. return this._meshAttached;
  14. }
  15. set attachedMesh(mesh) {
  16. this._meshAttached = mesh;
  17. this._nodeAttached = mesh;
  18. this._checkBillboardTransform();
  19. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  20. if (gizmo.isEnabled) {
  21. gizmo.attachedMesh = mesh;
  22. }
  23. else {
  24. gizmo.attachedMesh = null;
  25. }
  26. });
  27. }
  28. get attachedNode() {
  29. return this._nodeAttached;
  30. }
  31. set attachedNode(node) {
  32. this._meshAttached = null;
  33. this._nodeAttached = node;
  34. this._checkBillboardTransform();
  35. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  36. if (gizmo.isEnabled) {
  37. gizmo.attachedNode = node;
  38. }
  39. else {
  40. gizmo.attachedNode = null;
  41. }
  42. });
  43. }
  44. _checkBillboardTransform() {
  45. if (this._nodeAttached && this._nodeAttached.billboardMode) {
  46. Logger.Log("Rotation Gizmo will not work with transforms in billboard mode.");
  47. }
  48. }
  49. /**
  50. * Sensitivity factor for dragging (Default: 1)
  51. */
  52. set sensitivity(value) {
  53. this._sensitivity = value;
  54. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  55. if (gizmo) {
  56. gizmo.sensitivity = value;
  57. }
  58. });
  59. }
  60. get sensitivity() {
  61. return this._sensitivity;
  62. }
  63. /**
  64. * True when the mouse pointer is hovering a gizmo mesh
  65. */
  66. get isHovered() {
  67. return this.xGizmo.isHovered || this.yGizmo.isHovered || this.zGizmo.isHovered;
  68. }
  69. /**
  70. * True when the mouse pointer is dragging a gizmo mesh
  71. */
  72. get isDragging() {
  73. return this.xGizmo.dragBehavior.dragging || this.yGizmo.dragBehavior.dragging || this.zGizmo.dragBehavior.dragging;
  74. }
  75. get additionalTransformNode() {
  76. return this._additionalTransformNode;
  77. }
  78. set additionalTransformNode(transformNode) {
  79. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  80. gizmo.additionalTransformNode = transformNode;
  81. });
  82. }
  83. /**
  84. * Creates a RotationGizmo
  85. * @param gizmoLayer The utility layer the gizmo will be added to
  86. * @param tessellation Amount of tessellation to be used when creating rotation circles
  87. * @param useEulerRotation Use and update Euler angle instead of quaternion
  88. * @param thickness display gizmo axis thickness
  89. * @param gizmoManager Gizmo manager
  90. * @param options More options
  91. */
  92. constructor(gizmoLayer = UtilityLayerRenderer.DefaultUtilityLayer, tessellation = 32, useEulerRotation = false, thickness = 1, gizmoManager, options) {
  93. super(gizmoLayer);
  94. /** Fires an event when any of it's sub gizmos are dragged */
  95. this.onDragStartObservable = new Observable();
  96. /** Fires an event when any of it's sub gizmos are being dragged */
  97. this.onDragObservable = new Observable();
  98. /** Fires an event when any of it's sub gizmos are released from dragging */
  99. this.onDragEndObservable = new Observable();
  100. this._observables = [];
  101. this._sensitivity = 1;
  102. /** Node Caching for quick lookup */
  103. this._gizmoAxisCache = new Map();
  104. const xColor = options && options.xOptions && options.xOptions.color ? options.xOptions.color : Color3.Red().scale(0.5);
  105. const yColor = options && options.yOptions && options.yOptions.color ? options.yOptions.color : Color3.Green().scale(0.5);
  106. const zColor = options && options.zOptions && options.zOptions.color ? options.zOptions.color : Color3.Blue().scale(0.5);
  107. this.xGizmo = new PlaneRotationGizmo(new Vector3(1, 0, 0), xColor, gizmoLayer, tessellation, this, useEulerRotation, thickness);
  108. this.yGizmo = new PlaneRotationGizmo(new Vector3(0, 1, 0), yColor, gizmoLayer, tessellation, this, useEulerRotation, thickness);
  109. this.zGizmo = new PlaneRotationGizmo(new Vector3(0, 0, 1), zColor, gizmoLayer, tessellation, this, useEulerRotation, thickness);
  110. this.additionalTransformNode = options?.additionalTransformNode;
  111. // Relay drag events and set update scale
  112. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  113. //must set updateScale on each gizmo, as setting it on root RotationGizmo doesnt prevent individual gizmos from updating
  114. //currently updateScale is a property with no getter/setter, so no good way to override behavior at runtime, so we will at least set it on startup
  115. if (options && options.updateScale != undefined) {
  116. gizmo.updateScale = options.updateScale;
  117. }
  118. gizmo.dragBehavior.onDragStartObservable.add(() => {
  119. this.onDragStartObservable.notifyObservers({});
  120. });
  121. gizmo.dragBehavior.onDragObservable.add(() => {
  122. this.onDragObservable.notifyObservers({});
  123. });
  124. gizmo.dragBehavior.onDragEndObservable.add(() => {
  125. this.onDragEndObservable.notifyObservers({});
  126. });
  127. });
  128. this.attachedMesh = null;
  129. this.attachedNode = null;
  130. if (gizmoManager) {
  131. gizmoManager.addToAxisCache(this._gizmoAxisCache);
  132. }
  133. else {
  134. // Only subscribe to pointer event if gizmoManager isnt
  135. Gizmo.GizmoAxisPointerObserver(gizmoLayer, this._gizmoAxisCache);
  136. }
  137. }
  138. /**
  139. * If set the gizmo's rotation will be updated to match the attached mesh each frame (Default: true)
  140. * NOTE: This is only possible for meshes with uniform scaling, as otherwise it's not possible to decompose the rotation
  141. */
  142. set updateGizmoRotationToMatchAttachedMesh(value) {
  143. if (this.xGizmo) {
  144. this.xGizmo.updateGizmoRotationToMatchAttachedMesh = value;
  145. this.yGizmo.updateGizmoRotationToMatchAttachedMesh = value;
  146. this.zGizmo.updateGizmoRotationToMatchAttachedMesh = value;
  147. }
  148. }
  149. get updateGizmoRotationToMatchAttachedMesh() {
  150. return this.xGizmo.updateGizmoRotationToMatchAttachedMesh;
  151. }
  152. set updateGizmoPositionToMatchAttachedMesh(value) {
  153. if (this.xGizmo) {
  154. this.xGizmo.updateGizmoPositionToMatchAttachedMesh = value;
  155. this.yGizmo.updateGizmoPositionToMatchAttachedMesh = value;
  156. this.zGizmo.updateGizmoPositionToMatchAttachedMesh = value;
  157. }
  158. }
  159. get updateGizmoPositionToMatchAttachedMesh() {
  160. return this.xGizmo.updateGizmoPositionToMatchAttachedMesh;
  161. }
  162. set anchorPoint(value) {
  163. this._anchorPoint = value;
  164. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  165. gizmo.anchorPoint = value;
  166. });
  167. }
  168. get anchorPoint() {
  169. return this._anchorPoint;
  170. }
  171. /**
  172. * Set the coordinate system to use. By default it's local.
  173. * But it's possible for a user to tweak so its local for translation and world for rotation.
  174. * In that case, setting the coordinate system will change `updateGizmoRotationToMatchAttachedMesh` and `updateGizmoPositionToMatchAttachedMesh`
  175. */
  176. set coordinatesMode(coordinatesMode) {
  177. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  178. gizmo.coordinatesMode = coordinatesMode;
  179. });
  180. }
  181. set updateScale(value) {
  182. if (this.xGizmo) {
  183. this.xGizmo.updateScale = value;
  184. this.yGizmo.updateScale = value;
  185. this.zGizmo.updateScale = value;
  186. }
  187. }
  188. get updateScale() {
  189. return this.xGizmo.updateScale;
  190. }
  191. /**
  192. * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
  193. */
  194. set snapDistance(value) {
  195. if (this.xGizmo) {
  196. this.xGizmo.snapDistance = value;
  197. this.yGizmo.snapDistance = value;
  198. this.zGizmo.snapDistance = value;
  199. }
  200. }
  201. get snapDistance() {
  202. return this.xGizmo.snapDistance;
  203. }
  204. /**
  205. * Ratio for the scale of the gizmo (Default: 1)
  206. */
  207. set scaleRatio(value) {
  208. if (this.xGizmo) {
  209. this.xGizmo.scaleRatio = value;
  210. this.yGizmo.scaleRatio = value;
  211. this.zGizmo.scaleRatio = value;
  212. }
  213. }
  214. get scaleRatio() {
  215. return this.xGizmo.scaleRatio;
  216. }
  217. /**
  218. * posture that the gizmo will be display
  219. * When set null, default value will be used (Quaternion(0, 0, 0, 1))
  220. */
  221. get customRotationQuaternion() {
  222. return this._customRotationQuaternion;
  223. }
  224. set customRotationQuaternion(customRotationQuaternion) {
  225. this._customRotationQuaternion = customRotationQuaternion;
  226. [this.xGizmo, this.yGizmo, this.zGizmo].forEach((gizmo) => {
  227. if (gizmo) {
  228. gizmo.customRotationQuaternion = customRotationQuaternion;
  229. }
  230. });
  231. }
  232. /**
  233. * Builds Gizmo Axis Cache to enable features such as hover state preservation and graying out other axis during manipulation
  234. * @param mesh Axis gizmo mesh
  235. * @param cache Gizmo axis definition used for reactive gizmo UI
  236. */
  237. addToAxisCache(mesh, cache) {
  238. this._gizmoAxisCache.set(mesh, cache);
  239. }
  240. /**
  241. * Force release the drag action by code
  242. */
  243. releaseDrag() {
  244. this.xGizmo.dragBehavior.releaseDrag();
  245. this.yGizmo.dragBehavior.releaseDrag();
  246. this.zGizmo.dragBehavior.releaseDrag();
  247. }
  248. /**
  249. * Disposes of the gizmo
  250. */
  251. dispose() {
  252. this.xGizmo.dispose();
  253. this.yGizmo.dispose();
  254. this.zGizmo.dispose();
  255. this.onDragStartObservable.clear();
  256. this.onDragObservable.clear();
  257. this.onDragEndObservable.clear();
  258. this._observables.forEach((obs) => {
  259. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(obs);
  260. });
  261. }
  262. /**
  263. * CustomMeshes are not supported by this gizmo
  264. */
  265. setCustomMesh() {
  266. Logger.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo)");
  267. }
  268. }
  269. //# sourceMappingURL=rotationGizmo.js.map