physicsAggregate.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import { PhysicsBody } from "./physicsBody.js";
  2. import { PhysicsShape } from "./physicsShape.js";
  3. import { Logger } from "../../Misc/logger.js";
  4. import { Quaternion, TmpVectors, Vector3 } from "../../Maths/math.vector.js";
  5. import { Scalar } from "../../Maths/math.scalar.js";
  6. import { PhysicsMotionType, PhysicsShapeType } from "./IPhysicsEnginePlugin.js";
  7. import { BoundingBox } from "../../Culling/boundingBox.js";
  8. /**
  9. * Helper class to create and interact with a PhysicsAggregate.
  10. * This is a transition object that works like Physics Plugin V1 Impostors.
  11. * This helper instanciate all mandatory physics objects to get a body/shape and material.
  12. * It's less efficient that handling body and shapes independently but for prototyping or
  13. * a small numbers of physics objects, it's good enough.
  14. */
  15. export class PhysicsAggregate {
  16. constructor(
  17. /**
  18. * The physics-enabled object used as the physics aggregate
  19. */
  20. transformNode,
  21. /**
  22. * The type of the physics aggregate
  23. */
  24. type, _options = { mass: 0 }, _scene) {
  25. this.transformNode = transformNode;
  26. this.type = type;
  27. this._options = _options;
  28. this._scene = _scene;
  29. this._disposeShapeWhenDisposed = true;
  30. //sanity check!
  31. if (!this.transformNode) {
  32. Logger.Error("No object was provided. A physics object is obligatory");
  33. return;
  34. }
  35. const m = transformNode;
  36. if (this.transformNode.parent && this._options.mass !== 0 && m.hasThinInstances) {
  37. Logger.Warn("A physics body has been created for an object which has a parent and thin instances. Babylon physics currently works in local space so unexpected issues may occur.");
  38. }
  39. // Legacy support for old syntax.
  40. if (!this._scene && transformNode.getScene) {
  41. this._scene = transformNode.getScene();
  42. }
  43. if (!this._scene) {
  44. return;
  45. }
  46. //default options params
  47. this._options.mass = _options.mass === void 0 ? 0 : _options.mass;
  48. this._options.friction = _options.friction === void 0 ? 0.2 : _options.friction;
  49. this._options.restitution = _options.restitution === void 0 ? 0.2 : _options.restitution;
  50. const motionType = this._options.mass === 0 ? PhysicsMotionType.STATIC : PhysicsMotionType.DYNAMIC;
  51. const startAsleep = this._options.startAsleep ?? false;
  52. this.body = new PhysicsBody(transformNode, motionType, startAsleep, this._scene);
  53. this._addSizeOptions();
  54. if (type.getClassName && type.getClassName() === "PhysicsShape") {
  55. this.shape = type;
  56. this._disposeShapeWhenDisposed = false;
  57. }
  58. else {
  59. this.shape = new PhysicsShape({ type: type, parameters: this._options }, this._scene);
  60. }
  61. if (this._options.isTriggerShape) {
  62. this.shape.isTrigger = true;
  63. }
  64. this.material = { friction: this._options.friction, restitution: this._options.restitution };
  65. this.body.shape = this.shape;
  66. this.shape.material = this.material;
  67. this.body.setMassProperties({ mass: this._options.mass });
  68. this._nodeDisposeObserver = this.transformNode.onDisposeObservable.add(() => {
  69. this.dispose();
  70. });
  71. }
  72. _getObjectBoundingBox() {
  73. if (this.transformNode.getRawBoundingInfo) {
  74. return this.transformNode.getRawBoundingInfo().boundingBox;
  75. }
  76. else {
  77. return new BoundingBox(new Vector3(-0.5, -0.5, -0.5), new Vector3(0.5, 0.5, 0.5));
  78. }
  79. }
  80. _hasVertices(node) {
  81. return node?.getTotalVertices() > 0;
  82. }
  83. _addSizeOptions() {
  84. this.transformNode.computeWorldMatrix(true);
  85. const bb = this._getObjectBoundingBox();
  86. const extents = TmpVectors.Vector3[0];
  87. extents.copyFrom(bb.extendSize);
  88. extents.scaleInPlace(2);
  89. extents.multiplyInPlace(this.transformNode.scaling);
  90. // In case we had any negative scaling, we need to take the absolute value of the extents.
  91. extents.x = Math.abs(extents.x);
  92. extents.y = Math.abs(extents.y);
  93. extents.z = Math.abs(extents.z);
  94. const min = TmpVectors.Vector3[1];
  95. min.copyFrom(bb.minimum);
  96. min.multiplyInPlace(this.transformNode.scaling);
  97. if (!this._options.center) {
  98. const center = new Vector3();
  99. center.copyFrom(bb.center);
  100. center.multiplyInPlace(this.transformNode.scaling);
  101. this._options.center = center;
  102. }
  103. switch (this.type) {
  104. case PhysicsShapeType.SPHERE:
  105. if (!this._options.radius && Scalar.WithinEpsilon(extents.x, extents.y, 0.0001) && Scalar.WithinEpsilon(extents.x, extents.z, 0.0001)) {
  106. this._options.radius = extents.x / 2;
  107. }
  108. else if (!this._options.radius) {
  109. Logger.Warn("Non uniform scaling is unsupported for sphere shapes. Setting the radius to the biggest bounding box extent.");
  110. this._options.radius = Math.max(extents.x, extents.y, extents.z) / 2;
  111. }
  112. break;
  113. case PhysicsShapeType.CAPSULE:
  114. {
  115. const capRadius = extents.x / 2;
  116. this._options.radius = this._options.radius ?? capRadius;
  117. this._options.pointA = this._options.pointA ?? new Vector3(0, min.y + capRadius, 0);
  118. this._options.pointB = this._options.pointB ?? new Vector3(0, min.y + extents.y - capRadius, 0);
  119. }
  120. break;
  121. case PhysicsShapeType.CYLINDER:
  122. {
  123. const capRadius = extents.x / 2;
  124. this._options.radius = this._options.radius ?? capRadius;
  125. this._options.pointA = this._options.pointA ?? new Vector3(0, min.y, 0);
  126. this._options.pointB = this._options.pointB ?? new Vector3(0, min.y + extents.y, 0);
  127. }
  128. break;
  129. case PhysicsShapeType.MESH:
  130. case PhysicsShapeType.CONVEX_HULL:
  131. if (!this._options.mesh && this._hasVertices(this.transformNode)) {
  132. this._options.mesh = this.transformNode;
  133. }
  134. else if (!this._options.mesh || !this._hasVertices(this._options.mesh)) {
  135. throw new Error("No valid mesh was provided for mesh or convex hull shape parameter. Please provide a mesh with valid geometry (number of vertices greater than 0).");
  136. }
  137. break;
  138. case PhysicsShapeType.BOX:
  139. this._options.extents = this._options.extents ?? new Vector3(extents.x, extents.y, extents.z);
  140. this._options.rotation = this._options.rotation ?? Quaternion.Identity();
  141. break;
  142. }
  143. }
  144. /**
  145. * Releases the body, shape and material
  146. */
  147. dispose() {
  148. if (this._nodeDisposeObserver) {
  149. this.body.transformNode.onDisposeObservable.remove(this._nodeDisposeObserver);
  150. this._nodeDisposeObserver = null;
  151. }
  152. this.body.dispose();
  153. if (this._disposeShapeWhenDisposed) {
  154. this.shape.dispose();
  155. }
  156. }
  157. }
  158. //# sourceMappingURL=physicsAggregate.js.map