octreeSceneComponent.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import { ISmartArrayLike } from "Tools";
  2. import { Scene } from "scene";
  3. import { Vector3 } from "Math";
  4. import { AbstractMesh, SubMesh } from "Mesh";
  5. import { Collider } from "Collisions";
  6. import { Ray, Octree } from "Culling";
  7. import { SceneComponentConstants } from "sceneComponent";
  8. export interface Scene {
  9. /**
  10. * @hidden
  11. * Backing Filed
  12. */
  13. _selectionOctree: Octree<AbstractMesh>;
  14. /**
  15. * Gets the octree used to boost mesh selection (picking)
  16. * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
  17. */
  18. selectionOctree: Octree<AbstractMesh>;
  19. /**
  20. * Creates or updates the octree used to boost selection (picking)
  21. * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
  22. * @param maxCapacity defines the maximum capacity per leaf
  23. * @param maxDepth defines the maximum depth of the octree
  24. * @returns an octree of AbstractMesh
  25. */
  26. createOrUpdateSelectionOctree(maxCapacity?: number, maxDepth?: number): Octree<AbstractMesh>;
  27. }
  28. Scene.prototype.createOrUpdateSelectionOctree = function(maxCapacity = 64, maxDepth = 2): Octree<AbstractMesh> {
  29. let component = this._getComponent(SceneComponentConstants.NAME_OCTREE);
  30. if (!component) {
  31. component = new OctreeSceneComponent(this);
  32. this._addComponent(component);
  33. }
  34. if (!this._selectionOctree) {
  35. this._selectionOctree = new Octree<AbstractMesh>(Octree.CreationFuncForMeshes, maxCapacity, maxDepth);
  36. }
  37. var worldExtends = this.getWorldExtends();
  38. // Update octree
  39. this._selectionOctree.update(worldExtends.min, worldExtends.max, this.meshes);
  40. return this._selectionOctree;
  41. };
  42. Object.defineProperty(Scene.prototype, "selectionOctree", {
  43. get: function(this: Scene) {
  44. return this._selectionOctree;
  45. },
  46. enumerable: true,
  47. configurable: true
  48. });
  49. export interface AbstractMesh {
  50. /**
  51. * @hidden
  52. * Backing Field
  53. */
  54. _submeshesOctree: Octree<SubMesh>;
  55. /**
  56. * This function will create an octree to help to select the right submeshes for rendering, picking and collision computations.
  57. * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
  58. * @param maxCapacity defines the maximum size of each block (64 by default)
  59. * @param maxDepth defines the maximum depth to use (no more than 2 levels by default)
  60. * @returns the new octree
  61. * @see https://www.babylonjs-playground.com/#NA4OQ#12
  62. * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
  63. */
  64. createOrUpdateSubmeshesOctree(maxCapacity?: number, maxDepth?: number): Octree<SubMesh>;
  65. }
  66. /**
  67. * This function will create an octree to help to select the right submeshes for rendering, picking and collision computations.
  68. * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
  69. * @param maxCapacity defines the maximum size of each block (64 by default)
  70. * @param maxDepth defines the maximum depth to use (no more than 2 levels by default)
  71. * @returns the new octree
  72. * @see https://www.babylonjs-playground.com/#NA4OQ#12
  73. * @see http://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
  74. */
  75. AbstractMesh.prototype.createOrUpdateSubmeshesOctree = function(maxCapacity = 64, maxDepth = 2): Octree<SubMesh> {
  76. const scene = this.getScene();
  77. let component = scene._getComponent(SceneComponentConstants.NAME_OCTREE);
  78. if (!component) {
  79. component = new OctreeSceneComponent(scene);
  80. scene._addComponent(component);
  81. }
  82. if (!this._submeshesOctree) {
  83. this._submeshesOctree = new Octree<SubMesh>(Octree.CreationFuncForSubMeshes, maxCapacity, maxDepth);
  84. }
  85. this.computeWorldMatrix(true);
  86. let boundingInfo = this.getBoundingInfo();
  87. // Update octree
  88. var bbox = boundingInfo.boundingBox;
  89. this._submeshesOctree.update(bbox.minimumWorld, bbox.maximumWorld, this.subMeshes);
  90. return this._submeshesOctree;
  91. };
  92. /**
  93. * Defines the octree scene component responsible to manage any octrees
  94. * in a given scene.
  95. */
  96. export class OctreeSceneComponent {
  97. /**
  98. * The component name helpfull to identify the component in the list of scene components.
  99. */
  100. public readonly name = SceneComponentConstants.NAME_OCTREE;
  101. /**
  102. * The scene the component belongs to.
  103. */
  104. public scene: Scene;
  105. /**
  106. * Indicates if the meshes have been checked to make sure they are isEnabled()
  107. */
  108. public readonly checksIsEnabled = true;
  109. /**
  110. * Creates a new instance of the component for the given scene
  111. * @param scene Defines the scene to register the component in
  112. */
  113. constructor(scene: Scene) {
  114. this.scene = scene;
  115. this.scene.getActiveMeshCandidates = this.getActiveMeshCandidates.bind(this);
  116. this.scene.getActiveSubMeshCandidates = this.getActiveSubMeshCandidates.bind(this);
  117. this.scene.getCollidingSubMeshCandidates = this.getCollidingSubMeshCandidates.bind(this);
  118. this.scene.getIntersectingSubMeshCandidates = this.getIntersectingSubMeshCandidates.bind(this);
  119. }
  120. /**
  121. * Registers the component in a given scene
  122. */
  123. public register(): void {
  124. this.scene.onMeshRemovedObservable.add((mesh: AbstractMesh) => {
  125. const sceneOctree = this.scene.selectionOctree;
  126. if (sceneOctree !== undefined && sceneOctree !== null) {
  127. var index = sceneOctree.dynamicContent.indexOf(mesh);
  128. if (index !== -1) {
  129. sceneOctree.dynamicContent.splice(index, 1);
  130. }
  131. }
  132. });
  133. this.scene.onMeshImportedObservable.add((mesh: AbstractMesh) => {
  134. const sceneOctree = this.scene.selectionOctree;
  135. if (sceneOctree !== undefined && sceneOctree !== null) {
  136. sceneOctree.addMesh(mesh);
  137. }
  138. });
  139. }
  140. /**
  141. * Return the list of active meshes
  142. * @returns the list of active meshes
  143. */
  144. public getActiveMeshCandidates(): ISmartArrayLike<AbstractMesh> {
  145. if (this.scene._selectionOctree) {
  146. var selection = this.scene._selectionOctree.select(this.scene.frustumPlanes);
  147. return selection;
  148. }
  149. return this.scene._getDefaultMeshCandidates();
  150. }
  151. /**
  152. * Return the list of active sub meshes
  153. * @param mesh The mesh to get the candidates sub meshes from
  154. * @returns the list of active sub meshes
  155. */
  156. public getActiveSubMeshCandidates(mesh: AbstractMesh): ISmartArrayLike<SubMesh> {
  157. if (mesh._submeshesOctree && mesh.useOctreeForRenderingSelection) {
  158. var intersections = mesh._submeshesOctree.select(this.scene.frustumPlanes);
  159. return intersections;
  160. }
  161. return this.scene._getDefaultSubMeshCandidates(mesh);
  162. }
  163. private _tempRay = new BABYLON.Ray(Vector3.Zero(), new Vector3(1, 1, 1));
  164. /**
  165. * Return the list of sub meshes intersecting with a given local ray
  166. * @param mesh defines the mesh to find the submesh for
  167. * @param localRay defines the ray in local space
  168. * @returns the list of intersecting sub meshes
  169. */
  170. public getIntersectingSubMeshCandidates(mesh: AbstractMesh, localRay: Ray): ISmartArrayLike<SubMesh> {
  171. if (mesh._submeshesOctree && mesh.useOctreeForPicking) {
  172. Ray.TransformToRef(localRay, mesh.getWorldMatrix(), this._tempRay);
  173. var intersections = mesh._submeshesOctree.intersectsRay(this._tempRay);
  174. return intersections;
  175. }
  176. return this.scene._getDefaultSubMeshCandidates(mesh);
  177. }
  178. /**
  179. * Return the list of sub meshes colliding with a collider
  180. * @param mesh defines the mesh to find the submesh for
  181. * @param collider defines the collider to evaluate the collision against
  182. * @returns the list of colliding sub meshes
  183. */
  184. public getCollidingSubMeshCandidates(mesh: AbstractMesh, collider: Collider): ISmartArrayLike<SubMesh> {
  185. if (mesh._submeshesOctree && mesh.useOctreeForCollisions) {
  186. var radius = collider._velocityWorldLength + Math.max(collider._radius.x, collider._radius.y, collider._radius.z);
  187. var intersections = mesh._submeshesOctree.intersects(collider._basePointWorld, radius);
  188. return intersections;
  189. }
  190. return this.scene._getDefaultSubMeshCandidates(mesh);
  191. }
  192. /**
  193. * Rebuilds the elements related to this component in case of
  194. * context lost for instance.
  195. */
  196. public rebuild(): void {
  197. // Nothing to do here.
  198. }
  199. /**
  200. * Disposes the component and the associated ressources.
  201. */
  202. public dispose(): void {
  203. // Nothing to do here.
  204. }
  205. }