octreeSceneComponent.ts 9.2 KB

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