positionGizmo.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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 { AxisDragGizmo } from "./axisDragGizmo.js";
  7. import { PlaneDragGizmo } from "./planeDragGizmo.js";
  8. import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer.js";
  9. /**
  10. * Gizmo that enables dragging a mesh along 3 axis
  11. */
  12. export class PositionGizmo extends Gizmo {
  13. get attachedMesh() {
  14. return this._meshAttached;
  15. }
  16. set attachedMesh(mesh) {
  17. this._meshAttached = mesh;
  18. this._nodeAttached = mesh;
  19. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].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.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  35. if (gizmo.isEnabled) {
  36. gizmo.attachedNode = node;
  37. }
  38. else {
  39. gizmo.attachedNode = null;
  40. }
  41. });
  42. }
  43. /**
  44. * True when the mouse pointer is hovering a gizmo mesh
  45. */
  46. get isHovered() {
  47. return this.xGizmo.isHovered || this.yGizmo.isHovered || this.zGizmo.isHovered || this.xPlaneGizmo.isHovered || this.yPlaneGizmo.isHovered || this.zPlaneGizmo.isHovered;
  48. }
  49. get isDragging() {
  50. return (this.xGizmo.dragBehavior.dragging ||
  51. this.yGizmo.dragBehavior.dragging ||
  52. this.zGizmo.dragBehavior.dragging ||
  53. this.xPlaneGizmo.dragBehavior.dragging ||
  54. this.yPlaneGizmo.dragBehavior.dragging ||
  55. this.zPlaneGizmo.dragBehavior.dragging);
  56. }
  57. get additionalTransformNode() {
  58. return this._additionalTransformNode;
  59. }
  60. set additionalTransformNode(transformNode) {
  61. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  62. gizmo.additionalTransformNode = transformNode;
  63. });
  64. }
  65. /**
  66. * Creates a PositionGizmo
  67. * @param gizmoLayer The utility layer the gizmo will be added to
  68. * @param thickness display gizmo axis thickness
  69. * @param gizmoManager
  70. * @param options More options
  71. */
  72. constructor(gizmoLayer = UtilityLayerRenderer.DefaultUtilityLayer, thickness = 1, gizmoManager, options) {
  73. super(gizmoLayer);
  74. /**
  75. * protected variables
  76. */
  77. this._meshAttached = null;
  78. this._nodeAttached = null;
  79. this._observables = [];
  80. /** Node Caching for quick lookup */
  81. this._gizmoAxisCache = new Map();
  82. /** Fires an event when any of it's sub gizmos are dragged */
  83. this.onDragStartObservable = new Observable();
  84. /** Fires an event when any of it's sub gizmos are being dragged */
  85. this.onDragObservable = new Observable();
  86. /** Fires an event when any of it's sub gizmos are released from dragging */
  87. this.onDragEndObservable = new Observable();
  88. /**
  89. * If set to true, planar drag is enabled
  90. */
  91. this._planarGizmoEnabled = false;
  92. this.xGizmo = new AxisDragGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), gizmoLayer, this, thickness);
  93. this.yGizmo = new AxisDragGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), gizmoLayer, this, thickness);
  94. this.zGizmo = new AxisDragGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), gizmoLayer, this, thickness);
  95. this.xPlaneGizmo = new PlaneDragGizmo(new Vector3(1, 0, 0), Color3.Red().scale(0.5), this.gizmoLayer, this);
  96. this.yPlaneGizmo = new PlaneDragGizmo(new Vector3(0, 1, 0), Color3.Green().scale(0.5), this.gizmoLayer, this);
  97. this.zPlaneGizmo = new PlaneDragGizmo(new Vector3(0, 0, 1), Color3.Blue().scale(0.5), this.gizmoLayer, this);
  98. this.additionalTransformNode = options?.additionalTransformNode;
  99. // Relay drag events
  100. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  101. gizmo.dragBehavior.onDragStartObservable.add(() => {
  102. this.onDragStartObservable.notifyObservers({});
  103. });
  104. gizmo.dragBehavior.onDragObservable.add(() => {
  105. this.onDragObservable.notifyObservers({});
  106. });
  107. gizmo.dragBehavior.onDragEndObservable.add(() => {
  108. this.onDragEndObservable.notifyObservers({});
  109. });
  110. });
  111. this.attachedMesh = null;
  112. if (gizmoManager) {
  113. gizmoManager.addToAxisCache(this._gizmoAxisCache);
  114. }
  115. else {
  116. // Only subscribe to pointer event if gizmoManager isnt
  117. Gizmo.GizmoAxisPointerObserver(gizmoLayer, this._gizmoAxisCache);
  118. }
  119. }
  120. /**
  121. * If the planar drag gizmo is enabled
  122. * setting this will enable/disable XY, XZ and YZ planes regardless of individual gizmo settings.
  123. */
  124. set planarGizmoEnabled(value) {
  125. this._planarGizmoEnabled = value;
  126. [this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  127. if (gizmo) {
  128. gizmo.isEnabled = value;
  129. if (value) {
  130. if (gizmo.attachedMesh) {
  131. gizmo.attachedMesh = this.attachedMesh;
  132. }
  133. else {
  134. gizmo.attachedNode = this.attachedNode;
  135. }
  136. }
  137. }
  138. }, this);
  139. }
  140. get planarGizmoEnabled() {
  141. return this._planarGizmoEnabled;
  142. }
  143. /**
  144. * posture that the gizmo will be display
  145. * When set null, default value will be used (Quaternion(0, 0, 0, 1))
  146. */
  147. get customRotationQuaternion() {
  148. return this._customRotationQuaternion;
  149. }
  150. set customRotationQuaternion(customRotationQuaternion) {
  151. this._customRotationQuaternion = customRotationQuaternion;
  152. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  153. if (gizmo) {
  154. gizmo.customRotationQuaternion = customRotationQuaternion;
  155. }
  156. });
  157. }
  158. /**
  159. * If set the gizmo's rotation will be updated to match the attached mesh each frame (Default: true)
  160. * NOTE: This is only possible for meshes with uniform scaling, as otherwise it's not possible to decompose the rotation
  161. */
  162. set updateGizmoRotationToMatchAttachedMesh(value) {
  163. this._updateGizmoRotationToMatchAttachedMesh = value;
  164. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  165. if (gizmo) {
  166. gizmo.updateGizmoRotationToMatchAttachedMesh = value;
  167. }
  168. });
  169. }
  170. get updateGizmoRotationToMatchAttachedMesh() {
  171. return this._updateGizmoRotationToMatchAttachedMesh;
  172. }
  173. set updateGizmoPositionToMatchAttachedMesh(value) {
  174. this._updateGizmoPositionToMatchAttachedMesh = value;
  175. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  176. if (gizmo) {
  177. gizmo.updateGizmoPositionToMatchAttachedMesh = value;
  178. }
  179. });
  180. }
  181. get updateGizmoPositionToMatchAttachedMesh() {
  182. return this._updateGizmoPositionToMatchAttachedMesh;
  183. }
  184. set anchorPoint(value) {
  185. this._anchorPoint = value;
  186. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  187. gizmo.anchorPoint = value;
  188. });
  189. }
  190. get anchorPoint() {
  191. return this._anchorPoint;
  192. }
  193. /**
  194. * Set the coordinate system to use. By default it's local.
  195. * But it's possible for a user to tweak so its local for translation and world for rotation.
  196. * In that case, setting the coordinate system will change `updateGizmoRotationToMatchAttachedMesh` and `updateGizmoPositionToMatchAttachedMesh`
  197. */
  198. set coordinatesMode(coordinatesMode) {
  199. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  200. gizmo.coordinatesMode = coordinatesMode;
  201. });
  202. }
  203. set updateScale(value) {
  204. if (this.xGizmo) {
  205. this.xGizmo.updateScale = value;
  206. this.yGizmo.updateScale = value;
  207. this.zGizmo.updateScale = value;
  208. }
  209. }
  210. get updateScale() {
  211. return this.xGizmo.updateScale;
  212. }
  213. /**
  214. * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
  215. */
  216. set snapDistance(value) {
  217. this._snapDistance = value;
  218. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  219. if (gizmo) {
  220. gizmo.snapDistance = value;
  221. }
  222. });
  223. }
  224. get snapDistance() {
  225. return this._snapDistance;
  226. }
  227. /**
  228. * Ratio for the scale of the gizmo (Default: 1)
  229. */
  230. set scaleRatio(value) {
  231. this._scaleRatio = value;
  232. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  233. if (gizmo) {
  234. gizmo.scaleRatio = value;
  235. }
  236. });
  237. }
  238. get scaleRatio() {
  239. return this._scaleRatio;
  240. }
  241. /**
  242. * Builds Gizmo Axis Cache to enable features such as hover state preservation and graying out other axis during manipulation
  243. * @param mesh Axis gizmo mesh
  244. * @param cache Gizmo axis definition used for reactive gizmo UI
  245. */
  246. addToAxisCache(mesh, cache) {
  247. this._gizmoAxisCache.set(mesh, cache);
  248. }
  249. /**
  250. * Force release the drag action by code
  251. */
  252. releaseDrag() {
  253. this.xGizmo.dragBehavior.releaseDrag();
  254. this.yGizmo.dragBehavior.releaseDrag();
  255. this.zGizmo.dragBehavior.releaseDrag();
  256. this.xPlaneGizmo.dragBehavior.releaseDrag();
  257. this.yPlaneGizmo.dragBehavior.releaseDrag();
  258. this.zPlaneGizmo.dragBehavior.releaseDrag();
  259. }
  260. /**
  261. * Disposes of the gizmo
  262. */
  263. dispose() {
  264. [this.xGizmo, this.yGizmo, this.zGizmo, this.xPlaneGizmo, this.yPlaneGizmo, this.zPlaneGizmo].forEach((gizmo) => {
  265. if (gizmo) {
  266. gizmo.dispose();
  267. }
  268. });
  269. this._observables.forEach((obs) => {
  270. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(obs);
  271. });
  272. this.onDragStartObservable.clear();
  273. this.onDragObservable.clear();
  274. this.onDragEndObservable.clear();
  275. }
  276. /**
  277. * CustomMeshes are not supported by this gizmo
  278. */
  279. setCustomMesh() {
  280. 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,gizmo.xPlaneGizmo, gizmo.yPlaneGizmo, gizmo.zPlaneGizmo)");
  281. }
  282. }
  283. //# sourceMappingURL=positionGizmo.js.map