babylon.octree.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. module BABYLON {
  2. /**
  3. * Contains an array of blocks representing the octree
  4. */
  5. export interface IOctreeContainer<T> {
  6. /**
  7. * Blocks within the octree
  8. */
  9. blocks: Array<OctreeBlock<T>>;
  10. }
  11. /**
  12. * Octrees are a really powerful data structure that can quickly select entities based on space coordinates.
  13. * @see https://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
  14. */
  15. export class Octree<T> {
  16. /**
  17. * Blocks within the octree containing objects
  18. */
  19. public blocks: Array<OctreeBlock<T>>;
  20. /**
  21. * Content stored in the octree
  22. */
  23. public dynamicContent = new Array<T>();
  24. private _maxBlockCapacity: number;
  25. private _selectionContent: SmartArrayNoDuplicate<T>;
  26. private _creationFunc: (entry: T, block: OctreeBlock<T>) => void;
  27. /**
  28. * Creates a octree
  29. * @see https://doc.babylonjs.com/how_to/optimizing_your_scene_with_octrees
  30. * @param creationFunc function to be used to instatiate the octree
  31. * @param maxBlockCapacity defines the maximum number of meshes you want on your octree's leaves (default: 64)
  32. * @param maxDepth defines the maximum depth (sub-levels) for your octree. Default value is 2, which means 8 8 8 = 512 blocks :) (This parameter takes precedence over capacity.)
  33. */
  34. constructor(creationFunc: (
  35. entry: T,
  36. block: OctreeBlock<T>) => void,
  37. maxBlockCapacity?: number,
  38. /** Defines the maximum depth (sub-levels) for your octree. Default value is 2, which means 8 8 8 = 512 blocks :) (This parameter takes precedence over capacity.) */
  39. public maxDepth = 2
  40. ) {
  41. this._maxBlockCapacity = maxBlockCapacity || 64;
  42. this._selectionContent = new SmartArrayNoDuplicate<T>(1024);
  43. this._creationFunc = creationFunc;
  44. }
  45. // Methods
  46. /**
  47. * Updates the octree by adding blocks for the passed in meshes within the min and max world parameters
  48. * @param worldMin worldMin for the octree blocks var blockSize = new Vector3((worldMax.x - worldMin.x) / 2, (worldMax.y - worldMin.y) / 2, (worldMax.z - worldMin.z) / 2);
  49. * @param worldMax worldMax for the octree blocks var blockSize = new Vector3((worldMax.x - worldMin.x) / 2, (worldMax.y - worldMin.y) / 2, (worldMax.z - worldMin.z) / 2);
  50. * @param entries meshes to be added to the octree blocks
  51. */
  52. public update(worldMin: Vector3, worldMax: Vector3, entries: T[]): void {
  53. Octree._CreateBlocks(worldMin, worldMax, entries, this._maxBlockCapacity, 0, this.maxDepth, this, this._creationFunc);
  54. }
  55. /**
  56. * Adds a mesh to the octree
  57. * @param entry Mesh to add to the octree
  58. */
  59. public addMesh(entry: T): void {
  60. for (var index = 0; index < this.blocks.length; index++) {
  61. var block = this.blocks[index];
  62. block.addEntry(entry);
  63. }
  64. }
  65. /**
  66. * Selects an array of meshes within the frustum
  67. * @param frustumPlanes The frustum planes to use which will select all meshes within it
  68. * @param allowDuplicate If duplicate objects are allowed in the resulting object array
  69. * @returns array of meshes within the frustum
  70. */
  71. public select(frustumPlanes: Plane[], allowDuplicate?: boolean): SmartArray<T> {
  72. this._selectionContent.reset();
  73. for (var index = 0; index < this.blocks.length; index++) {
  74. var block = this.blocks[index];
  75. block.select(frustumPlanes, this._selectionContent, allowDuplicate);
  76. }
  77. if (allowDuplicate) {
  78. this._selectionContent.concat(this.dynamicContent);
  79. } else {
  80. this._selectionContent.concatWithNoDuplicate(this.dynamicContent);
  81. }
  82. return this._selectionContent;
  83. }
  84. /**
  85. * Test if the octree intersect with the given bounding sphere and if yes, then add its content to the selection array
  86. * @param sphereCenter defines the bounding sphere center
  87. * @param sphereRadius defines the bounding sphere radius
  88. * @param allowDuplicate defines if the selection array can contains duplicated entries
  89. * @returns an array of objects that intersect the sphere
  90. */
  91. public intersects(sphereCenter: Vector3, sphereRadius: number, allowDuplicate?: boolean): SmartArray<T> {
  92. this._selectionContent.reset();
  93. for (var index = 0; index < this.blocks.length; index++) {
  94. var block = this.blocks[index];
  95. block.intersects(sphereCenter, sphereRadius, this._selectionContent, allowDuplicate);
  96. }
  97. if (allowDuplicate) {
  98. this._selectionContent.concat(this.dynamicContent);
  99. } else {
  100. this._selectionContent.concatWithNoDuplicate(this.dynamicContent);
  101. }
  102. return this._selectionContent;
  103. }
  104. /**
  105. * Test if the octree intersect with the given ray and if yes, then add its content to resulting array
  106. * @param ray defines the ray to test with
  107. * @returns array of intersected objects
  108. */
  109. public intersectsRay(ray: Ray): SmartArray<T> {
  110. this._selectionContent.reset();
  111. for (var index = 0; index < this.blocks.length; index++) {
  112. var block = this.blocks[index];
  113. block.intersectsRay(ray, this._selectionContent);
  114. }
  115. this._selectionContent.concatWithNoDuplicate(this.dynamicContent);
  116. return this._selectionContent;
  117. }
  118. /**
  119. * @hidden
  120. */
  121. public static _CreateBlocks<T>(worldMin: Vector3, worldMax: Vector3, entries: T[], maxBlockCapacity: number, currentDepth: number, maxDepth: number, target: IOctreeContainer<T>, creationFunc: (entry: T, block: OctreeBlock<T>) => void): void {
  122. target.blocks = new Array<OctreeBlock<T>>();
  123. var blockSize = new Vector3((worldMax.x - worldMin.x) / 2, (worldMax.y - worldMin.y) / 2, (worldMax.z - worldMin.z) / 2);
  124. // Segmenting space
  125. for (var x = 0; x < 2; x++) {
  126. for (var y = 0; y < 2; y++) {
  127. for (var z = 0; z < 2; z++) {
  128. var localMin = worldMin.add(blockSize.multiplyByFloats(x, y, z));
  129. var localMax = worldMin.add(blockSize.multiplyByFloats(x + 1, y + 1, z + 1));
  130. var block = new OctreeBlock<T>(localMin, localMax, maxBlockCapacity, currentDepth + 1, maxDepth, creationFunc);
  131. block.addEntries(entries);
  132. target.blocks.push(block);
  133. }
  134. }
  135. }
  136. }
  137. /**
  138. * Adds a mesh into the octree block if it intersects the block
  139. */
  140. public static CreationFuncForMeshes = (entry: AbstractMesh, block: OctreeBlock<AbstractMesh>): void => {
  141. let boundingInfo = entry.getBoundingInfo();
  142. if (!entry.isBlocked && boundingInfo.boundingBox.intersectsMinMax(block.minPoint, block.maxPoint)) {
  143. block.entries.push(entry);
  144. }
  145. }
  146. /**
  147. * Adds a submesh into the octree block if it intersects the block
  148. */
  149. public static CreationFuncForSubMeshes = (entry: SubMesh, block: OctreeBlock<SubMesh>): void => {
  150. let boundingInfo = entry.getBoundingInfo();
  151. if (boundingInfo.boundingBox.intersectsMinMax(block.minPoint, block.maxPoint)) {
  152. block.entries.push(entry);
  153. }
  154. }
  155. }
  156. }