octree.ts 6.7 KB

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