TileOrientedBoundingBox.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import BoxOutlineGeometry from "../Core/BoxOutlineGeometry.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Check from "../Core/Check.js";
  5. import ColorGeometryInstanceAttribute from "../Core/ColorGeometryInstanceAttribute.js";
  6. import GeometryInstance from "../Core/GeometryInstance.js";
  7. import Matrix3 from "../Core/Matrix3.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import CesiumMath from "../Core/Math.js";
  10. import OrientedBoundingBox from "../Core/OrientedBoundingBox.js";
  11. import PerInstanceColorAppearance from "./PerInstanceColorAppearance.js";
  12. import Primitive from "./Primitive.js";
  13. const scratchU = new Cartesian3();
  14. const scratchV = new Cartesian3();
  15. const scratchW = new Cartesian3();
  16. const scratchCartesian = new Cartesian3();
  17. function computeMissingVector(a, b, result) {
  18. result = Cartesian3.cross(a, b, result);
  19. const magnitude = Cartesian3.magnitude(result);
  20. return Cartesian3.multiplyByScalar(
  21. result,
  22. CesiumMath.EPSILON7 / magnitude,
  23. result
  24. );
  25. }
  26. function findOrthogonalVector(a, result) {
  27. const temp = Cartesian3.normalize(a, scratchCartesian);
  28. const b = Cartesian3.equalsEpsilon(
  29. temp,
  30. Cartesian3.UNIT_X,
  31. CesiumMath.EPSILON6
  32. )
  33. ? Cartesian3.UNIT_Y
  34. : Cartesian3.UNIT_X;
  35. return computeMissingVector(a, b, result);
  36. }
  37. function checkHalfAxes(halfAxes) {
  38. let u = Matrix3.getColumn(halfAxes, 0, scratchU);
  39. let v = Matrix3.getColumn(halfAxes, 1, scratchV);
  40. let w = Matrix3.getColumn(halfAxes, 2, scratchW);
  41. const uZero = Cartesian3.equals(u, Cartesian3.ZERO);
  42. const vZero = Cartesian3.equals(v, Cartesian3.ZERO);
  43. const wZero = Cartesian3.equals(w, Cartesian3.ZERO);
  44. if (!uZero && !vZero && !wZero) {
  45. return halfAxes;
  46. }
  47. if (uZero && vZero && wZero) {
  48. halfAxes[0] = CesiumMath.EPSILON7;
  49. halfAxes[4] = CesiumMath.EPSILON7;
  50. halfAxes[8] = CesiumMath.EPSILON7;
  51. return halfAxes;
  52. }
  53. if (uZero && !vZero && !wZero) {
  54. u = computeMissingVector(v, w, u);
  55. } else if (!uZero && vZero && !wZero) {
  56. v = computeMissingVector(u, w, v);
  57. } else if (!uZero && !vZero && wZero) {
  58. w = computeMissingVector(v, u, w);
  59. } else if (!uZero) {
  60. v = findOrthogonalVector(u, v);
  61. w = computeMissingVector(v, u, w);
  62. } else if (!vZero) {
  63. u = findOrthogonalVector(v, u);
  64. w = computeMissingVector(v, u, w);
  65. } else if (!wZero) {
  66. u = findOrthogonalVector(w, u);
  67. v = computeMissingVector(w, u, v);
  68. }
  69. Matrix3.setColumn(halfAxes, 0, u, halfAxes);
  70. Matrix3.setColumn(halfAxes, 1, v, halfAxes);
  71. Matrix3.setColumn(halfAxes, 2, w, halfAxes);
  72. return halfAxes;
  73. }
  74. /**
  75. * A tile bounding volume specified as an oriented bounding box.
  76. * @alias TileOrientedBoundingBox
  77. * @constructor
  78. *
  79. * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the box.
  80. * @param {Matrix3} [halfAxes=Matrix3.ZERO] The three orthogonal half-axes of the bounding box.
  81. * Equivalently, the transformation matrix, to rotate and scale a 2x2x2
  82. * cube centered at the origin.
  83. *
  84. * @private
  85. */
  86. function TileOrientedBoundingBox(center, halfAxes) {
  87. halfAxes = checkHalfAxes(halfAxes);
  88. this._orientedBoundingBox = new OrientedBoundingBox(center, halfAxes);
  89. this._boundingSphere = BoundingSphere.fromOrientedBoundingBox(
  90. this._orientedBoundingBox
  91. );
  92. }
  93. Object.defineProperties(TileOrientedBoundingBox.prototype, {
  94. /**
  95. * The underlying bounding volume.
  96. *
  97. * @memberof TileOrientedBoundingBox.prototype
  98. *
  99. * @type {object}
  100. * @readonly
  101. */
  102. boundingVolume: {
  103. get: function () {
  104. return this._orientedBoundingBox;
  105. },
  106. },
  107. /**
  108. * The underlying bounding sphere.
  109. *
  110. * @memberof TileOrientedBoundingBox.prototype
  111. *
  112. * @type {BoundingSphere}
  113. * @readonly
  114. */
  115. boundingSphere: {
  116. get: function () {
  117. return this._boundingSphere;
  118. },
  119. },
  120. });
  121. /**
  122. * Computes the distance between this bounding box and the camera attached to frameState.
  123. *
  124. * @param {FrameState} frameState The frameState to which the camera is attached.
  125. * @returns {number} The distance between the camera and the bounding box in meters. Returns 0 if the camera is inside the bounding volume.
  126. */
  127. TileOrientedBoundingBox.prototype.distanceToCamera = function (frameState) {
  128. //>>includeStart('debug', pragmas.debug);
  129. Check.defined("frameState", frameState);
  130. //>>includeEnd('debug');
  131. return Math.sqrt(
  132. this._orientedBoundingBox.distanceSquaredTo(frameState.camera.positionWC)
  133. );
  134. };
  135. /**
  136. * Determines which side of a plane this box is located.
  137. *
  138. * @param {Plane} plane The plane to test against.
  139. * @returns {Intersect} {@link Intersect.INSIDE} if the entire box is on the side of the plane
  140. * the normal is pointing, {@link Intersect.OUTSIDE} if the entire box is
  141. * on the opposite side, and {@link Intersect.INTERSECTING} if the box
  142. * intersects the plane.
  143. */
  144. TileOrientedBoundingBox.prototype.intersectPlane = function (plane) {
  145. //>>includeStart('debug', pragmas.debug);
  146. Check.defined("plane", plane);
  147. //>>includeEnd('debug');
  148. return this._orientedBoundingBox.intersectPlane(plane);
  149. };
  150. /**
  151. * Update the bounding box after the tile is transformed.
  152. *
  153. * @param {Cartesian3} center The center of the box.
  154. * @param {Matrix3} halfAxes The three orthogonal half-axes of the bounding box.
  155. * Equivalently, the transformation matrix, to rotate and scale a 2x2x2
  156. * cube centered at the origin.
  157. */
  158. TileOrientedBoundingBox.prototype.update = function (center, halfAxes) {
  159. Cartesian3.clone(center, this._orientedBoundingBox.center);
  160. halfAxes = checkHalfAxes(halfAxes);
  161. Matrix3.clone(halfAxes, this._orientedBoundingBox.halfAxes);
  162. BoundingSphere.fromOrientedBoundingBox(
  163. this._orientedBoundingBox,
  164. this._boundingSphere
  165. );
  166. };
  167. /**
  168. * Creates a debug primitive that shows the outline of the box.
  169. *
  170. * @param {Color} color The desired color of the primitive's mesh
  171. * @return {Primitive}
  172. */
  173. TileOrientedBoundingBox.prototype.createDebugVolume = function (color) {
  174. //>>includeStart('debug', pragmas.debug);
  175. Check.defined("color", color);
  176. //>>includeEnd('debug');
  177. const geometry = new BoxOutlineGeometry({
  178. // Make a 2x2x2 cube
  179. minimum: new Cartesian3(-1.0, -1.0, -1.0),
  180. maximum: new Cartesian3(1.0, 1.0, 1.0),
  181. });
  182. const modelMatrix = Matrix4.fromRotationTranslation(
  183. this.boundingVolume.halfAxes,
  184. this.boundingVolume.center
  185. );
  186. const instance = new GeometryInstance({
  187. geometry: geometry,
  188. id: "outline",
  189. modelMatrix: modelMatrix,
  190. attributes: {
  191. color: ColorGeometryInstanceAttribute.fromColor(color),
  192. },
  193. });
  194. return new Primitive({
  195. geometryInstances: instance,
  196. appearance: new PerInstanceColorAppearance({
  197. translucent: false,
  198. flat: true,
  199. }),
  200. asynchronous: false,
  201. });
  202. };
  203. export default TileOrientedBoundingBox;