boundingBox.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import { ArrayTools } from "../Misc/arrayTools.js";
  2. import { Matrix, Vector3 } from "../Maths/math.vector.js";
  3. import { Epsilon } from "../Maths/math.constants.js";
  4. /**
  5. * Class used to store bounding box information
  6. */
  7. export class BoundingBox {
  8. /**
  9. * Creates a new bounding box
  10. * @param min defines the minimum vector (in local space)
  11. * @param max defines the maximum vector (in local space)
  12. * @param worldMatrix defines the new world matrix
  13. */
  14. constructor(min, max, worldMatrix) {
  15. /**
  16. * Gets the 8 vectors representing the bounding box in local space
  17. */
  18. this.vectors = ArrayTools.BuildArray(8, Vector3.Zero);
  19. /**
  20. * Gets the center of the bounding box in local space
  21. */
  22. this.center = Vector3.Zero();
  23. /**
  24. * Gets the center of the bounding box in world space
  25. */
  26. this.centerWorld = Vector3.Zero();
  27. /**
  28. * Gets the extend size in local space
  29. */
  30. this.extendSize = Vector3.Zero();
  31. /**
  32. * Gets the extend size in world space
  33. */
  34. this.extendSizeWorld = Vector3.Zero();
  35. /**
  36. * Gets the OBB (object bounding box) directions
  37. */
  38. this.directions = ArrayTools.BuildArray(3, Vector3.Zero);
  39. /**
  40. * Gets the 8 vectors representing the bounding box in world space
  41. */
  42. this.vectorsWorld = ArrayTools.BuildArray(8, Vector3.Zero);
  43. /**
  44. * Gets the minimum vector in world space
  45. */
  46. this.minimumWorld = Vector3.Zero();
  47. /**
  48. * Gets the maximum vector in world space
  49. */
  50. this.maximumWorld = Vector3.Zero();
  51. /**
  52. * Gets the minimum vector in local space
  53. */
  54. this.minimum = Vector3.Zero();
  55. /**
  56. * Gets the maximum vector in local space
  57. */
  58. this.maximum = Vector3.Zero();
  59. /** @internal */
  60. this._drawWrapperFront = null;
  61. /** @internal */
  62. this._drawWrapperBack = null;
  63. this.reConstruct(min, max, worldMatrix);
  64. }
  65. // Methods
  66. /**
  67. * Recreates the entire bounding box from scratch as if we call the constructor in place
  68. * @param min defines the new minimum vector (in local space)
  69. * @param max defines the new maximum vector (in local space)
  70. * @param worldMatrix defines the new world matrix
  71. */
  72. reConstruct(min, max, worldMatrix) {
  73. const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
  74. const vectors = this.vectors;
  75. this.minimum.copyFromFloats(minX, minY, minZ);
  76. this.maximum.copyFromFloats(maxX, maxY, maxZ);
  77. vectors[0].copyFromFloats(minX, minY, minZ);
  78. vectors[1].copyFromFloats(maxX, maxY, maxZ);
  79. vectors[2].copyFromFloats(maxX, minY, minZ);
  80. vectors[3].copyFromFloats(minX, maxY, minZ);
  81. vectors[4].copyFromFloats(minX, minY, maxZ);
  82. vectors[5].copyFromFloats(maxX, maxY, minZ);
  83. vectors[6].copyFromFloats(minX, maxY, maxZ);
  84. vectors[7].copyFromFloats(maxX, minY, maxZ);
  85. // OBB
  86. max.addToRef(min, this.center).scaleInPlace(0.5);
  87. max.subtractToRef(min, this.extendSize).scaleInPlace(0.5);
  88. this._worldMatrix = worldMatrix || Matrix.IdentityReadOnly;
  89. this._update(this._worldMatrix);
  90. }
  91. /**
  92. * Scale the current bounding box by applying a scale factor
  93. * @param factor defines the scale factor to apply
  94. * @returns the current bounding box
  95. */
  96. scale(factor) {
  97. const tmpVectors = BoundingBox._TmpVector3;
  98. const diff = this.maximum.subtractToRef(this.minimum, tmpVectors[0]);
  99. const len = diff.length();
  100. diff.normalizeFromLength(len);
  101. const distance = len * factor;
  102. const newRadius = diff.scaleInPlace(distance * 0.5);
  103. const min = this.center.subtractToRef(newRadius, tmpVectors[1]);
  104. const max = this.center.addToRef(newRadius, tmpVectors[2]);
  105. this.reConstruct(min, max, this._worldMatrix);
  106. return this;
  107. }
  108. /**
  109. * Gets the world matrix of the bounding box
  110. * @returns a matrix
  111. */
  112. getWorldMatrix() {
  113. return this._worldMatrix;
  114. }
  115. /**
  116. * @internal
  117. */
  118. _update(world) {
  119. const minWorld = this.minimumWorld;
  120. const maxWorld = this.maximumWorld;
  121. const directions = this.directions;
  122. const vectorsWorld = this.vectorsWorld;
  123. const vectors = this.vectors;
  124. if (!world.isIdentity()) {
  125. minWorld.setAll(Number.MAX_VALUE);
  126. maxWorld.setAll(-Number.MAX_VALUE);
  127. for (let index = 0; index < 8; ++index) {
  128. const v = vectorsWorld[index];
  129. Vector3.TransformCoordinatesToRef(vectors[index], world, v);
  130. minWorld.minimizeInPlace(v);
  131. maxWorld.maximizeInPlace(v);
  132. }
  133. // Extend
  134. maxWorld.subtractToRef(minWorld, this.extendSizeWorld).scaleInPlace(0.5);
  135. maxWorld.addToRef(minWorld, this.centerWorld).scaleInPlace(0.5);
  136. }
  137. else {
  138. minWorld.copyFrom(this.minimum);
  139. maxWorld.copyFrom(this.maximum);
  140. for (let index = 0; index < 8; ++index) {
  141. vectorsWorld[index].copyFrom(vectors[index]);
  142. }
  143. // Extend
  144. this.extendSizeWorld.copyFrom(this.extendSize);
  145. this.centerWorld.copyFrom(this.center);
  146. }
  147. Vector3.FromArrayToRef(world.m, 0, directions[0]);
  148. Vector3.FromArrayToRef(world.m, 4, directions[1]);
  149. Vector3.FromArrayToRef(world.m, 8, directions[2]);
  150. this._worldMatrix = world;
  151. }
  152. /**
  153. * Tests if the bounding box is intersecting the frustum planes
  154. * @param frustumPlanes defines the frustum planes to test
  155. * @returns true if there is an intersection
  156. */
  157. isInFrustum(frustumPlanes) {
  158. return BoundingBox.IsInFrustum(this.vectorsWorld, frustumPlanes);
  159. }
  160. /**
  161. * Tests if the bounding box is entirely inside the frustum planes
  162. * @param frustumPlanes defines the frustum planes to test
  163. * @returns true if there is an inclusion
  164. */
  165. isCompletelyInFrustum(frustumPlanes) {
  166. return BoundingBox.IsCompletelyInFrustum(this.vectorsWorld, frustumPlanes);
  167. }
  168. /**
  169. * Tests if a point is inside the bounding box
  170. * @param point defines the point to test
  171. * @returns true if the point is inside the bounding box
  172. */
  173. intersectsPoint(point) {
  174. const min = this.minimumWorld;
  175. const max = this.maximumWorld;
  176. const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
  177. const pointX = point.x, pointY = point.y, pointZ = point.z;
  178. const delta = -Epsilon;
  179. if (maxX - pointX < delta || delta > pointX - minX) {
  180. return false;
  181. }
  182. if (maxY - pointY < delta || delta > pointY - minY) {
  183. return false;
  184. }
  185. if (maxZ - pointZ < delta || delta > pointZ - minZ) {
  186. return false;
  187. }
  188. return true;
  189. }
  190. /**
  191. * Tests if the bounding box intersects with a bounding sphere
  192. * @param sphere defines the sphere to test
  193. * @returns true if there is an intersection
  194. */
  195. intersectsSphere(sphere) {
  196. return BoundingBox.IntersectsSphere(this.minimumWorld, this.maximumWorld, sphere.centerWorld, sphere.radiusWorld);
  197. }
  198. /**
  199. * Tests if the bounding box intersects with a box defined by a min and max vectors
  200. * @param min defines the min vector to use
  201. * @param max defines the max vector to use
  202. * @returns true if there is an intersection
  203. */
  204. intersectsMinMax(min, max) {
  205. const myMin = this.minimumWorld;
  206. const myMax = this.maximumWorld;
  207. const myMinX = myMin.x, myMinY = myMin.y, myMinZ = myMin.z, myMaxX = myMax.x, myMaxY = myMax.y, myMaxZ = myMax.z;
  208. const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
  209. if (myMaxX < minX || myMinX > maxX) {
  210. return false;
  211. }
  212. if (myMaxY < minY || myMinY > maxY) {
  213. return false;
  214. }
  215. if (myMaxZ < minZ || myMinZ > maxZ) {
  216. return false;
  217. }
  218. return true;
  219. }
  220. /**
  221. * Disposes the resources of the class
  222. */
  223. dispose() {
  224. this._drawWrapperFront?.dispose();
  225. this._drawWrapperBack?.dispose();
  226. }
  227. // Statics
  228. /**
  229. * Tests if two bounding boxes are intersections
  230. * @param box0 defines the first box to test
  231. * @param box1 defines the second box to test
  232. * @returns true if there is an intersection
  233. */
  234. static Intersects(box0, box1) {
  235. return box0.intersectsMinMax(box1.minimumWorld, box1.maximumWorld);
  236. }
  237. /**
  238. * Tests if a bounding box defines by a min/max vectors intersects a sphere
  239. * @param minPoint defines the minimum vector of the bounding box
  240. * @param maxPoint defines the maximum vector of the bounding box
  241. * @param sphereCenter defines the sphere center
  242. * @param sphereRadius defines the sphere radius
  243. * @returns true if there is an intersection
  244. */
  245. static IntersectsSphere(minPoint, maxPoint, sphereCenter, sphereRadius) {
  246. const vector = BoundingBox._TmpVector3[0];
  247. Vector3.ClampToRef(sphereCenter, minPoint, maxPoint, vector);
  248. const num = Vector3.DistanceSquared(sphereCenter, vector);
  249. return num <= sphereRadius * sphereRadius;
  250. }
  251. /**
  252. * Tests if a bounding box defined with 8 vectors is entirely inside frustum planes
  253. * @param boundingVectors defines an array of 8 vectors representing a bounding box
  254. * @param frustumPlanes defines the frustum planes to test
  255. * @returns true if there is an inclusion
  256. */
  257. static IsCompletelyInFrustum(boundingVectors, frustumPlanes) {
  258. for (let p = 0; p < 6; ++p) {
  259. const frustumPlane = frustumPlanes[p];
  260. for (let i = 0; i < 8; ++i) {
  261. if (frustumPlane.dotCoordinate(boundingVectors[i]) < 0) {
  262. return false;
  263. }
  264. }
  265. }
  266. return true;
  267. }
  268. /**
  269. * Tests if a bounding box defined with 8 vectors intersects frustum planes
  270. * @param boundingVectors defines an array of 8 vectors representing a bounding box
  271. * @param frustumPlanes defines the frustum planes to test
  272. * @returns true if there is an intersection
  273. */
  274. static IsInFrustum(boundingVectors, frustumPlanes) {
  275. for (let p = 0; p < 6; ++p) {
  276. let canReturnFalse = true;
  277. const frustumPlane = frustumPlanes[p];
  278. for (let i = 0; i < 8; ++i) {
  279. if (frustumPlane.dotCoordinate(boundingVectors[i]) >= 0) {
  280. canReturnFalse = false;
  281. break;
  282. }
  283. }
  284. if (canReturnFalse) {
  285. return false;
  286. }
  287. }
  288. return true;
  289. }
  290. }
  291. BoundingBox._TmpVector3 = ArrayTools.BuildArray(3, Vector3.Zero);
  292. //# sourceMappingURL=boundingBox.js.map