babylon.boundingBoxGizmo.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. module BABYLON {
  2. /**
  3. * Bounding box gizmo
  4. */
  5. export class BoundingBoxGizmo extends Gizmo {
  6. private _lineBoundingBox: AbstractMesh;
  7. private _rotateSpheresParent: AbstractMesh;
  8. private _scaleBoxesParent: AbstractMesh;
  9. private _boundingDimensions = new BABYLON.Vector3(1, 1, 1);
  10. private _renderObserver: Nullable<Observer<Scene>> = null;
  11. private _pointerObserver: Nullable<Observer<PointerInfo>> = null;
  12. private _scaleDragSpeed = 0.2;
  13. private _tmpQuaternion = new Quaternion();
  14. private _tmpVector = new Vector3(0, 0, 0);
  15. /**
  16. * The size of the rotation spheres attached to the bounding box (Default: 0.1)
  17. */
  18. public rotationSphereSize = 0.1;
  19. /**
  20. * The size of the scale boxes attached to the bounding box (Default: 0.1)
  21. */
  22. public scaleBoxSize = 0.1;
  23. /**
  24. * If set, the rotation spheres and scale boxes will increase in size based on the distance away from the camera to have a consistent screen size (Default: false)
  25. */
  26. public fixedDragMeshScreenSize = false;
  27. /**
  28. * The distance away from the object which the draggable meshes should appear world sized when fixedDragMeshScreenSize is set to true (default: 10)
  29. */
  30. public fixedDragMeshScreenSizeDistanceFactor = 10;
  31. /**
  32. * Fired when a rotation sphere or scale box is dragged
  33. */
  34. public onDragStartObservable = new Observable<{}>();
  35. /**
  36. * Fired when a rotation sphere or scale box drag is started
  37. */
  38. public onDragObservable = new Observable<{}>();
  39. /**
  40. * Fired when a rotation sphere or scale box drag is needed
  41. */
  42. public onDragEndObservable = new Observable<{}>();
  43. private _anchorMesh: AbstractMesh;
  44. private _existingMeshScale = new Vector3();
  45. /**
  46. * Creates an BoundingBoxGizmo
  47. * @param gizmoLayer The utility layer the gizmo will be added to
  48. * @param color The color of the gizmo
  49. */
  50. constructor(color: Color3 = Color3.Gray(), gizmoLayer: UtilityLayerRenderer = UtilityLayerRenderer.DefaultKeepDepthUtilityLayer) {
  51. super(gizmoLayer);
  52. // Do not update the gizmo's scale so it has a fixed size to the object its attached to
  53. this._updateScale = false;
  54. this._anchorMesh = new AbstractMesh("anchor", gizmoLayer.utilityLayerScene);
  55. // Create Materials
  56. var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
  57. coloredMaterial.disableLighting = true;
  58. coloredMaterial.emissiveColor = color;
  59. var hoverColoredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
  60. hoverColoredMaterial.disableLighting = true;
  61. hoverColoredMaterial.emissiveColor = color.clone().add(new Color3(0.2, 0.2, 0.2));
  62. // Build bounding box out of lines
  63. this._lineBoundingBox = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
  64. this._lineBoundingBox.rotationQuaternion = new BABYLON.Quaternion();
  65. var lines = []
  66. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(this._boundingDimensions.x, 0, 0)] }, gizmoLayer.utilityLayerScene));
  67. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
  68. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  69. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(this._boundingDimensions.x, 0, 0), new BABYLON.Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
  70. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(this._boundingDimensions.x, 0, 0), new BABYLON.Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  71. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, this._boundingDimensions.y, 0), new BABYLON.Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
  72. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, this._boundingDimensions.y, 0), new BABYLON.Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  73. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, 0, this._boundingDimensions.z), new BABYLON.Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  74. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(0, 0, this._boundingDimensions.z), new BABYLON.Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  75. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new BABYLON.Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  76. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new BABYLON.Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
  77. lines.push(BABYLON.MeshBuilder.CreateLines("lines", { points: [new BABYLON.Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new BABYLON.Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
  78. lines.forEach((l) => {
  79. l.color = color
  80. l.position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x / 2, -this._boundingDimensions.y / 2, -this._boundingDimensions.z / 2))
  81. l.isPickable = false;
  82. this._lineBoundingBox.addChild(l)
  83. })
  84. this._rootMesh.addChild(this._lineBoundingBox);
  85. // Create rotation spheres
  86. this._rotateSpheresParent = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
  87. this._rotateSpheresParent.rotationQuaternion = new Quaternion();
  88. for (let i = 0; i < 12; i++) {
  89. let sphere = BABYLON.MeshBuilder.CreateSphere("", { diameter: 1 }, gizmoLayer.utilityLayerScene);
  90. sphere.rotationQuaternion = new Quaternion();
  91. sphere.material = coloredMaterial;
  92. // Drag behavior
  93. var _dragBehavior = new PointerDragBehavior({});
  94. _dragBehavior.moveAttached = false;
  95. _dragBehavior.updateDragPlane = false;
  96. sphere.addBehavior(_dragBehavior);
  97. let startingTurnDirection = new Vector3(1, 0, 0);
  98. let totalTurnAmountOfDrag = 0;
  99. _dragBehavior.onDragStartObservable.add((event) => {
  100. startingTurnDirection.copyFrom(sphere.forward);
  101. totalTurnAmountOfDrag = 0;
  102. })
  103. _dragBehavior.onDragObservable.add((event) => {
  104. this.onDragObservable.notifyObservers({});
  105. if (this.attachedMesh) {
  106. var worldDragDirection = startingTurnDirection;
  107. // Project the world right on to the drag plane
  108. var toSub = event.dragPlaneNormal.scale(Vector3.Dot(event.dragPlaneNormal, worldDragDirection));
  109. var dragAxis = worldDragDirection.subtract(toSub).normalizeToNew();
  110. // project drag delta on to the resulting drag axis and rotate based on that
  111. var projectDist = -Vector3.Dot(dragAxis, event.delta);
  112. // Rotate based on axis
  113. if (!this.attachedMesh.rotationQuaternion) {
  114. this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y, this.attachedMesh.rotation.x, this.attachedMesh.rotation.z);
  115. }
  116. if (!this._anchorMesh.rotationQuaternion) {
  117. this._anchorMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._anchorMesh.rotation.y, this._anchorMesh.rotation.x, this._anchorMesh.rotation.z);
  118. }
  119. // Do not allow the object to turn more than a full circle
  120. totalTurnAmountOfDrag += projectDist;
  121. if (Math.abs(totalTurnAmountOfDrag) <= 2 * Math.PI) {
  122. if (i >= 8) {
  123. Quaternion.RotationYawPitchRollToRef(0, 0, projectDist, this._tmpQuaternion);
  124. } else if (i >= 4) {
  125. Quaternion.RotationYawPitchRollToRef(projectDist, 0, 0, this._tmpQuaternion);
  126. } else {
  127. Quaternion.RotationYawPitchRollToRef(0, projectDist, 0, this._tmpQuaternion);
  128. }
  129. // Rotate around center of bounding box
  130. this._anchorMesh.addChild(this.attachedMesh);
  131. this._anchorMesh.rotationQuaternion!.multiplyToRef(this._tmpQuaternion, this._anchorMesh.rotationQuaternion!);
  132. this._anchorMesh.removeChild(this.attachedMesh);
  133. }
  134. this.updateBoundingBox();
  135. }
  136. });
  137. // Selection/deselection
  138. _dragBehavior.onDragStartObservable.add(() => {
  139. this.onDragStartObservable.notifyObservers({});
  140. this._selectNode(sphere)
  141. })
  142. _dragBehavior.onDragEndObservable.add(() => {
  143. this.onDragEndObservable.notifyObservers({});
  144. this._selectNode(null)
  145. })
  146. this._rotateSpheresParent.addChild(sphere);
  147. }
  148. this._rootMesh.addChild(this._rotateSpheresParent);
  149. // Create scale cubes
  150. this._scaleBoxesParent = new BABYLON.AbstractMesh("", gizmoLayer.utilityLayerScene);
  151. this._scaleBoxesParent.rotationQuaternion = new Quaternion();
  152. for (var i = 0; i < 2; i++) {
  153. for (var j = 0; j < 2; j++) {
  154. for (var k = 0; k < 2; k++) {
  155. let box = BABYLON.MeshBuilder.CreateBox("", { size: 1 }, gizmoLayer.utilityLayerScene);
  156. box.material = coloredMaterial;
  157. // Dragging logic
  158. let dragAxis = new BABYLON.Vector3(i == 0 ? -1 : 1, j == 0 ? -1 : 1, k == 0 ? -1 : 1);
  159. var _dragBehavior = new PointerDragBehavior({ dragAxis: dragAxis });
  160. _dragBehavior.moveAttached = false;
  161. box.addBehavior(_dragBehavior);
  162. _dragBehavior.onDragObservable.add((event) => {
  163. this.onDragObservable.notifyObservers({});
  164. if (this.attachedMesh) {
  165. var deltaScale = new Vector3(event.dragDistance, event.dragDistance, event.dragDistance);
  166. deltaScale.scaleInPlace(this._scaleDragSpeed);
  167. this.updateBoundingBox();
  168. // Scale from the position of the opposite corner
  169. box.absolutePosition.subtractToRef(this._anchorMesh.position, this._tmpVector);
  170. this._anchorMesh.position.subtractInPlace(this._tmpVector);
  171. this._anchorMesh.addChild(this.attachedMesh);
  172. this._anchorMesh.scaling.addInPlace(deltaScale);
  173. if (this._anchorMesh.scaling.x < 0 || this._anchorMesh.scaling.y < 0 || this._anchorMesh.scaling.z < 0) {
  174. this._anchorMesh.scaling.subtractInPlace(deltaScale);
  175. }
  176. this._anchorMesh.removeChild(this.attachedMesh);
  177. }
  178. })
  179. // Selection/deselection
  180. _dragBehavior.onDragStartObservable.add(() => {
  181. this.onDragStartObservable.notifyObservers({});
  182. this._selectNode(box)
  183. })
  184. _dragBehavior.onDragEndObservable.add(() => {
  185. this.onDragEndObservable.notifyObservers({});
  186. this._selectNode(null)
  187. })
  188. this._scaleBoxesParent.addChild(box);
  189. }
  190. }
  191. }
  192. this._rootMesh.addChild(this._scaleBoxesParent);
  193. // Hover color change
  194. var pointerIds = new Array<AbstractMesh>();
  195. this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState) => {
  196. if (!pointerIds[(<PointerEvent>pointerInfo.event).pointerId]) {
  197. this._rotateSpheresParent.getChildMeshes().concat(this._scaleBoxesParent.getChildMeshes()).forEach((mesh) => {
  198. if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh == mesh) {
  199. pointerIds[(<PointerEvent>pointerInfo.event).pointerId] = mesh;
  200. mesh.material = hoverColoredMaterial;
  201. }
  202. });
  203. } else {
  204. if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh != pointerIds[(<PointerEvent>pointerInfo.event).pointerId]) {
  205. pointerIds[(<PointerEvent>pointerInfo.event).pointerId].material = coloredMaterial;
  206. delete pointerIds[(<PointerEvent>pointerInfo.event).pointerId];
  207. }
  208. }
  209. });
  210. // Update bounding box positions
  211. this._renderObserver = this.gizmoLayer.originalScene.onBeforeRenderObservable.add(() => {
  212. // Only update the bouding box if scaling has changed
  213. if (this.attachedMesh && !this._existingMeshScale.equals(this.attachedMesh.scaling)) {
  214. this.updateBoundingBox();
  215. }
  216. })
  217. this.updateBoundingBox();
  218. }
  219. protected _attachedMeshChanged(value: Nullable<AbstractMesh>) {
  220. if (value) {
  221. // Reset anchor mesh to match attached mesh's scale
  222. // This is needed to avoid invalid box/sphere position on first drag
  223. this._anchorMesh.addChild(value);
  224. this._anchorMesh.removeChild(value);
  225. this.updateBoundingBox();
  226. }
  227. }
  228. private _selectNode(selectedMesh: Nullable<Mesh>) {
  229. this._rotateSpheresParent.getChildMeshes()
  230. .concat(this._scaleBoxesParent.getChildMeshes()).forEach((m, i) => {
  231. m.isVisible = (!selectedMesh || m == selectedMesh);
  232. })
  233. }
  234. private _recurseComputeWorld(mesh: AbstractMesh) {
  235. mesh.computeWorldMatrix(true);
  236. mesh.getChildMeshes().forEach((m) => {
  237. this._recurseComputeWorld(m);
  238. });
  239. }
  240. /**
  241. * Updates the bounding box information for the Gizmo
  242. */
  243. public updateBoundingBox(){
  244. if(this.attachedMesh){
  245. // Rotate based on axis
  246. if (!this.attachedMesh.rotationQuaternion) {
  247. this.attachedMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.attachedMesh.rotation.y, this.attachedMesh.rotation.x, this.attachedMesh.rotation.z);
  248. }
  249. if (!this._anchorMesh.rotationQuaternion) {
  250. this._anchorMesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(this._anchorMesh.rotation.y, this._anchorMesh.rotation.x, this._anchorMesh.rotation.z);
  251. }
  252. this._anchorMesh.rotationQuaternion.copyFrom(this.attachedMesh.rotationQuaternion);
  253. // Store original position and reset mesh to origin before computing the bounding box
  254. this._tmpQuaternion.copyFrom(this.attachedMesh.rotationQuaternion);
  255. this._tmpVector.copyFrom(this.attachedMesh.position);
  256. this.attachedMesh.rotationQuaternion.set(0, 0, 0, 1);
  257. this.attachedMesh.position.set(0, 0, 0);
  258. // Update bounding dimensions/positions
  259. var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors();
  260. boundingMinMax.max.subtractToRef(boundingMinMax.min, this._boundingDimensions);
  261. // Update gizmo to match bounding box scaling and rotation
  262. this._lineBoundingBox.scaling.copyFrom(this._boundingDimensions);
  263. this._lineBoundingBox.position.set((boundingMinMax.max.x + boundingMinMax.min.x) / 2, (boundingMinMax.max.y + boundingMinMax.min.y) / 2, (boundingMinMax.max.z + boundingMinMax.min.z) / 2);
  264. this._rotateSpheresParent.position.copyFrom(this._lineBoundingBox.position);
  265. this._scaleBoxesParent.position.copyFrom(this._lineBoundingBox.position);
  266. this._lineBoundingBox.computeWorldMatrix();
  267. this._anchorMesh.position.copyFrom(this._lineBoundingBox.absolutePosition);
  268. // restore position/rotation values
  269. this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
  270. this.attachedMesh.position.copyFrom(this._tmpVector);
  271. this._recurseComputeWorld(this.attachedMesh);
  272. }
  273. // Update rotation sphere locations
  274. var rotateSpheres = this._rotateSpheresParent.getChildMeshes();
  275. for (var i = 0; i < 3; i++) {
  276. for (var j = 0; j < 2; j++) {
  277. for (var k = 0; k < 2; k++) {
  278. var index = ((i * 4) + (j * 2)) + k
  279. if (i == 0) {
  280. rotateSpheres[index].position.set(this._boundingDimensions.x / 2, this._boundingDimensions.y * j, this._boundingDimensions.z * k);
  281. rotateSpheres[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x / 2, -this._boundingDimensions.y / 2, -this._boundingDimensions.z / 2));
  282. rotateSpheres[index].lookAt(Vector3.Cross(Vector3.Right(), rotateSpheres[index].position.normalizeToNew()).normalizeToNew().add(rotateSpheres[index].position));
  283. }
  284. if (i == 1) {
  285. rotateSpheres[index].position.set(this._boundingDimensions.x * j, this._boundingDimensions.y / 2, this._boundingDimensions.z * k);
  286. rotateSpheres[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x / 2, -this._boundingDimensions.y / 2, -this._boundingDimensions.z / 2));
  287. rotateSpheres[index].lookAt(Vector3.Cross(Vector3.Up(), rotateSpheres[index].position.normalizeToNew()).normalizeToNew().add(rotateSpheres[index].position));
  288. }
  289. if (i == 2) {
  290. rotateSpheres[index].position.set(this._boundingDimensions.x * j, this._boundingDimensions.y * k, this._boundingDimensions.z / 2);
  291. rotateSpheres[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x / 2, -this._boundingDimensions.y / 2, -this._boundingDimensions.z / 2));
  292. rotateSpheres[index].lookAt(Vector3.Cross(Vector3.Forward(), rotateSpheres[index].position.normalizeToNew()).normalizeToNew().add(rotateSpheres[index].position));
  293. }
  294. if (this.fixedDragMeshScreenSize) {
  295. rotateSpheres[index].absolutePosition.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera!.position, this._tmpVector);
  296. var distanceFromCamera = this.rotationSphereSize * this._tmpVector.length() / this.fixedDragMeshScreenSizeDistanceFactor;
  297. rotateSpheres[index].scaling.set(distanceFromCamera, distanceFromCamera, distanceFromCamera);
  298. } else {
  299. rotateSpheres[index].scaling.set(this.rotationSphereSize, this.rotationSphereSize, this.rotationSphereSize);
  300. }
  301. }
  302. }
  303. }
  304. // Update scale box locations
  305. var scaleBoxes = this._scaleBoxesParent.getChildMeshes();
  306. for (var i = 0; i < 2; i++) {
  307. for (var j = 0; j < 2; j++) {
  308. for (var k = 0; k < 2; k++) {
  309. var index = ((i * 4) + (j * 2)) + k;
  310. if (scaleBoxes[index]) {
  311. scaleBoxes[index].position.set(this._boundingDimensions.x * i, this._boundingDimensions.y * j, this._boundingDimensions.z * k);
  312. scaleBoxes[index].position.addInPlace(new BABYLON.Vector3(-this._boundingDimensions.x / 2, -this._boundingDimensions.y / 2, -this._boundingDimensions.z / 2));
  313. if (this.fixedDragMeshScreenSize) {
  314. scaleBoxes[index].absolutePosition.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera!.position, this._tmpVector);
  315. var distanceFromCamera = this.scaleBoxSize * this._tmpVector.length() / this.fixedDragMeshScreenSizeDistanceFactor;
  316. scaleBoxes[index].scaling.set(distanceFromCamera, distanceFromCamera, distanceFromCamera);
  317. } else {
  318. scaleBoxes[index].scaling.set(this.scaleBoxSize, this.scaleBoxSize, this.scaleBoxSize);
  319. }
  320. }
  321. }
  322. }
  323. }
  324. if (this.attachedMesh) {
  325. this._existingMeshScale.copyFrom(this.attachedMesh.scaling);
  326. }
  327. }
  328. /**
  329. * Enables rotation on the specified axis and disables rotation on the others
  330. * @param axis The list of axis that should be enabled (eg. "xy" or "xyz")
  331. */
  332. public setEnabledRotationAxis(axis: string) {
  333. this._rotateSpheresParent.getChildMeshes().forEach((m, i) => {
  334. if (i < 4) {
  335. m.setEnabled(axis.indexOf("x") != -1);
  336. } else if (i < 8) {
  337. m.setEnabled(axis.indexOf("y") != -1);
  338. } else {
  339. m.setEnabled(axis.indexOf("z") != -1);
  340. }
  341. })
  342. }
  343. /**
  344. * Disposes of the gizmo
  345. */
  346. public dispose() {
  347. this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
  348. this.gizmoLayer.originalScene.onBeforeRenderObservable.remove(this._renderObserver);
  349. this._lineBoundingBox.dispose();
  350. this._rotateSpheresParent.dispose();
  351. this._scaleBoxesParent.dispose();
  352. super.dispose();
  353. }
  354. /**
  355. * Makes a mesh not pickable and wraps the mesh inside of a bounding box mesh that is pickable. (This is useful to avoid picking within complex geometry)
  356. * @param mesh the mesh to wrap in the bounding box mesh and make not pickable
  357. * @returns the bounding box mesh with the passed in mesh as a child
  358. */
  359. public static MakeNotPickableAndWrapInBoundingBox(mesh: Mesh): Mesh {
  360. var makeNotPickable = (root: AbstractMesh) => {
  361. root.isPickable = false;
  362. root.getChildMeshes().forEach((c) => {
  363. makeNotPickable(c);
  364. });
  365. }
  366. makeNotPickable(mesh);
  367. // Reset position to get boudning box from origin with no rotation
  368. if (!mesh.rotationQuaternion) {
  369. mesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(mesh.rotation.y, mesh.rotation.x, mesh.rotation.z);
  370. }
  371. var oldPos = mesh.position.clone()
  372. var oldRot = mesh.rotationQuaternion.clone();
  373. mesh.rotationQuaternion.set(0, 0, 0, 1);
  374. mesh.position.set(0, 0, 0)
  375. // Update bounding dimensions/positions
  376. var box = BABYLON.MeshBuilder.CreateBox("box", { size: 1 }, mesh.getScene());
  377. var boundingMinMax = mesh.getHierarchyBoundingVectors();
  378. boundingMinMax.max.subtractToRef(boundingMinMax.min, box.scaling);
  379. box.position.set((boundingMinMax.max.x + boundingMinMax.min.x) / 2, (boundingMinMax.max.y + boundingMinMax.min.y) / 2, (boundingMinMax.max.z + boundingMinMax.min.z) / 2);
  380. // Restore original positions
  381. mesh.addChild(box);
  382. mesh.rotationQuaternion.copyFrom(oldRot);
  383. mesh.position.copyFrom(oldPos);
  384. // Reverse parenting
  385. mesh.removeChild(box);
  386. box.addChild(mesh);
  387. box.visibility = 0;
  388. return box;
  389. }
  390. }
  391. }