boundingBox.ts 13 KB

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