boundingSphere.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import { DeepImmutable } from "../types";
  2. import { ArrayTools } from "../Misc/arrayTools";
  3. import { Matrix, Vector3 } from "../Maths/math.vector";
  4. import { Plane } from '../Maths/math.plane';
  5. /**
  6. * Class used to store bounding sphere information
  7. */
  8. export class BoundingSphere {
  9. /**
  10. * Gets the center of the bounding sphere in local space
  11. */
  12. public readonly center = Vector3.Zero();
  13. /**
  14. * Radius of the bounding sphere in local space
  15. */
  16. public radius: number;
  17. /**
  18. * Gets the center of the bounding sphere in world space
  19. */
  20. public readonly centerWorld = Vector3.Zero();
  21. /**
  22. * Radius of the bounding sphere in world space
  23. */
  24. public radiusWorld: number;
  25. /**
  26. * Gets the minimum vector in local space
  27. */
  28. public readonly minimum = Vector3.Zero();
  29. /**
  30. * Gets the maximum vector in local space
  31. */
  32. public readonly maximum = Vector3.Zero();
  33. private _worldMatrix: DeepImmutable<Matrix>;
  34. private static readonly TmpVector3 = ArrayTools.BuildArray(3, Vector3.Zero);
  35. /**
  36. * Creates a new bounding sphere
  37. * @param min defines the minimum vector (in local space)
  38. * @param max defines the maximum vector (in local space)
  39. * @param worldMatrix defines the new world matrix
  40. */
  41. constructor(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
  42. this.reConstruct(min, max, worldMatrix);
  43. }
  44. /**
  45. * Recreates the entire bounding sphere from scratch as if we call the constructor in place
  46. * @param min defines the new minimum vector (in local space)
  47. * @param max defines the new maximum vector (in local space)
  48. * @param worldMatrix defines the new world matrix
  49. */
  50. public reConstruct(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
  51. this.minimum.copyFrom(min);
  52. this.maximum.copyFrom(max);
  53. var distance = Vector3.Distance(min, max);
  54. max.addToRef(min, this.center).scaleInPlace(0.5);
  55. this.radius = distance * 0.5;
  56. this._update(worldMatrix || Matrix.IdentityReadOnly);
  57. }
  58. /**
  59. * Scale the current bounding sphere by applying a scale factor
  60. * @param factor defines the scale factor to apply
  61. * @returns the current bounding box
  62. */
  63. public scale(factor: number): BoundingSphere {
  64. const newRadius = this.radius * factor;
  65. const tmpVectors = BoundingSphere.TmpVector3;
  66. const tempRadiusVector = tmpVectors[0].setAll(newRadius);
  67. const min = this.center.subtractToRef(tempRadiusVector, tmpVectors[1]);
  68. const max = this.center.addToRef(tempRadiusVector, tmpVectors[2]);
  69. this.reConstruct(min, max, this._worldMatrix);
  70. return this;
  71. }
  72. /**
  73. * Gets the world matrix of the bounding box
  74. * @returns a matrix
  75. */
  76. public getWorldMatrix(): DeepImmutable<Matrix> {
  77. return this._worldMatrix;
  78. }
  79. // Methods
  80. /** @hidden */
  81. public _update(worldMatrix: DeepImmutable<Matrix>): void {
  82. if (!worldMatrix.isIdentity()) {
  83. Vector3.TransformCoordinatesToRef(this.center, worldMatrix, this.centerWorld);
  84. const tempVector = BoundingSphere.TmpVector3[0];
  85. Vector3.TransformNormalFromFloatsToRef(1.0, 1.0, 1.0, worldMatrix, tempVector);
  86. this.radiusWorld = Math.max(Math.abs(tempVector.x), Math.abs(tempVector.y), Math.abs(tempVector.z)) * this.radius;
  87. }
  88. else {
  89. this.centerWorld.copyFrom(this.center);
  90. this.radiusWorld = this.radius;
  91. }
  92. }
  93. /**
  94. * Tests if the bounding sphere is intersecting the frustum planes
  95. * @param frustumPlanes defines the frustum planes to test
  96. * @returns true if there is an intersection
  97. */
  98. public isInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
  99. let center = this.centerWorld;
  100. let radius = this.radiusWorld;
  101. for (let i = 0; i < 6; i++) {
  102. if (frustumPlanes[i].dotCoordinate(center) <= -radius) {
  103. return false;
  104. }
  105. }
  106. return true;
  107. }
  108. /**
  109. * Tests if the bounding sphere center is in between the frustum planes.
  110. * Used for optimistic fast inclusion.
  111. * @param frustumPlanes defines the frustum planes to test
  112. * @returns true if the sphere center is in between the frustum planes
  113. */
  114. public isCenterInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
  115. let center = this.centerWorld;
  116. for (let i = 0; i < 6; i++) {
  117. if (frustumPlanes[i].dotCoordinate(center) < 0) {
  118. return false;
  119. }
  120. }
  121. return true;
  122. }
  123. /**
  124. * Tests if a point is inside the bounding sphere
  125. * @param point defines the point to test
  126. * @returns true if the point is inside the bounding sphere
  127. */
  128. public intersectsPoint(point: DeepImmutable<Vector3>): boolean {
  129. const squareDistance = Vector3.DistanceSquared(this.centerWorld, point);
  130. if (this.radiusWorld * this.radiusWorld < squareDistance) {
  131. return false;
  132. }
  133. return true;
  134. }
  135. // Statics
  136. /**
  137. * Checks if two sphere intersect
  138. * @param sphere0 sphere 0
  139. * @param sphere1 sphere 1
  140. * @returns true if the spheres intersect
  141. */
  142. public static Intersects(sphere0: DeepImmutable<BoundingSphere>, sphere1: DeepImmutable<BoundingSphere>): boolean {
  143. const squareDistance = Vector3.DistanceSquared(sphere0.centerWorld, sphere1.centerWorld);
  144. const radiusSum = sphere0.radiusWorld + sphere1.radiusWorld;
  145. if (radiusSum * radiusSum < squareDistance) {
  146. return false;
  147. }
  148. return true;
  149. }
  150. }