boundingBox.ts 12 KB

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