ammoJSPlugin.ts 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530
  1. import { Quaternion, Vector3, Matrix } from "../../Maths/math.vector";
  2. import { IPhysicsEnginePlugin, PhysicsImpostorJoint } from "../../Physics/IPhysicsEngine";
  3. import { Logger } from "../../Misc/logger";
  4. import { PhysicsImpostor, IPhysicsEnabledObject } from "../../Physics/physicsImpostor";
  5. import { PhysicsJoint, IMotorEnabledJoint, DistanceJointData } from "../../Physics/physicsJoint";
  6. import { VertexBuffer } from "../../Meshes/buffer";
  7. import { VertexData } from "../../Meshes/mesh.vertexData";
  8. import { Nullable } from "../../types";
  9. import { AbstractMesh } from "../../Meshes/abstractMesh";
  10. import { Mesh } from "../../Meshes/mesh";
  11. import { ShapeBuilder } from "../../Meshes/Builders/shapeBuilder";
  12. import { LinesBuilder } from "../../Meshes/Builders/linesBuilder";
  13. import { LinesMesh } from '../../Meshes/linesMesh';
  14. import { PhysicsRaycastResult } from "../physicsRaycastResult";
  15. import { Scalar } from "../../Maths/math.scalar";
  16. import { Epsilon } from '../../Maths/math.constants';
  17. declare var Ammo: any;
  18. /**
  19. * AmmoJS Physics plugin
  20. * @see https://doc.babylonjs.com/how_to/using_the_physics_engine
  21. * @see https://github.com/kripken/ammo.js/
  22. */
  23. export class AmmoJSPlugin implements IPhysicsEnginePlugin {
  24. /**
  25. * Reference to the Ammo library
  26. */
  27. public bjsAMMO: any = {};
  28. /**
  29. * Created ammoJS world which physics bodies are added to
  30. */
  31. public world: any;
  32. /**
  33. * Name of the plugin
  34. */
  35. public name: string = "AmmoJSPlugin";
  36. private _timeStep: number = 1 / 60;
  37. private _fixedTimeStep: number = 1 / 60;
  38. private _maxSteps = 5;
  39. private _tmpQuaternion = new Quaternion();
  40. private _tmpAmmoTransform: any;
  41. private _tmpAmmoQuaternion: any;
  42. private _tmpAmmoConcreteContactResultCallback: any;
  43. private _collisionConfiguration: any;
  44. private _dispatcher: any;
  45. private _overlappingPairCache: any;
  46. private _solver: any;
  47. private _softBodySolver: any;
  48. private _tmpAmmoVectorA: any;
  49. private _tmpAmmoVectorB: any;
  50. private _tmpAmmoVectorC: any;
  51. private _tmpAmmoVectorD: any;
  52. private _tmpContactCallbackResult = false;
  53. private _tmpAmmoVectorRCA: any;
  54. private _tmpAmmoVectorRCB: any;
  55. private _raycastResult: PhysicsRaycastResult;
  56. private _tmpContactPoint = new Vector3();
  57. private static readonly DISABLE_COLLISION_FLAG = 4;
  58. private static readonly KINEMATIC_FLAG = 2;
  59. private static readonly DISABLE_DEACTIVATION_FLAG = 4;
  60. /**
  61. * Initializes the ammoJS plugin
  62. * @param _useDeltaForWorldStep if the time between frames should be used when calculating physics steps (Default: true)
  63. * @param ammoInjection can be used to inject your own ammo reference
  64. * @param overlappingPairCache can be used to specify your own overlapping pair cache
  65. */
  66. public constructor(private _useDeltaForWorldStep: boolean = true, ammoInjection: any = Ammo, overlappingPairCache: any = null) {
  67. if (typeof ammoInjection === "function") {
  68. Logger.Error("AmmoJS is not ready. Please make sure you await Ammo() before using the plugin.");
  69. return;
  70. } else {
  71. this.bjsAMMO = ammoInjection;
  72. }
  73. if (!this.isSupported()) {
  74. Logger.Error("AmmoJS is not available. Please make sure you included the js file.");
  75. return;
  76. }
  77. // Initialize the physics world
  78. this._collisionConfiguration = new this.bjsAMMO.btSoftBodyRigidBodyCollisionConfiguration();
  79. this._dispatcher = new this.bjsAMMO.btCollisionDispatcher(this._collisionConfiguration);
  80. this._overlappingPairCache = overlappingPairCache || new this.bjsAMMO.btDbvtBroadphase();
  81. this._solver = new this.bjsAMMO.btSequentialImpulseConstraintSolver();
  82. this._softBodySolver = new this.bjsAMMO.btDefaultSoftBodySolver();
  83. this.world = new this.bjsAMMO.btSoftRigidDynamicsWorld(this._dispatcher, this._overlappingPairCache, this._solver, this._collisionConfiguration, this._softBodySolver);
  84. this._tmpAmmoConcreteContactResultCallback = new this.bjsAMMO.ConcreteContactResultCallback();
  85. this._tmpAmmoConcreteContactResultCallback.addSingleResult = (contactPoint: any, colObj0Wrap: any, partId0: any, index0: any) => {
  86. contactPoint = this.bjsAMMO.wrapPointer(contactPoint, Ammo.btManifoldPoint);
  87. const worldPoint = contactPoint.getPositionWorldOnA();
  88. this._tmpContactPoint.x = worldPoint.x();
  89. this._tmpContactPoint.y = worldPoint.y();
  90. this._tmpContactPoint.z = worldPoint.z();
  91. this._tmpContactCallbackResult = true;
  92. };
  93. this._raycastResult = new PhysicsRaycastResult();
  94. // Create temp ammo variables
  95. this._tmpAmmoTransform = new this.bjsAMMO.btTransform();
  96. this._tmpAmmoTransform.setIdentity();
  97. this._tmpAmmoQuaternion = new this.bjsAMMO.btQuaternion(0, 0, 0, 1);
  98. this._tmpAmmoVectorA = new this.bjsAMMO.btVector3(0, 0, 0);
  99. this._tmpAmmoVectorB = new this.bjsAMMO.btVector3(0, 0, 0);
  100. this._tmpAmmoVectorC = new this.bjsAMMO.btVector3(0, 0, 0);
  101. this._tmpAmmoVectorD = new this.bjsAMMO.btVector3(0, 0, 0);
  102. }
  103. /**
  104. * Sets the gravity of the physics world (m/(s^2))
  105. * @param gravity Gravity to set
  106. */
  107. public setGravity(gravity: Vector3): void {
  108. this._tmpAmmoVectorA.setValue(gravity.x, gravity.y, gravity.z);
  109. this.world.setGravity(this._tmpAmmoVectorA);
  110. this.world.getWorldInfo().set_m_gravity(this._tmpAmmoVectorA);
  111. }
  112. /**
  113. * Amount of time to step forward on each frame (only used if useDeltaForWorldStep is false in the constructor)
  114. * @param timeStep timestep to use in seconds
  115. */
  116. public setTimeStep(timeStep: number) {
  117. this._timeStep = timeStep;
  118. }
  119. /**
  120. * Increment to step forward in the physics engine (If timeStep is set to 1/60 and fixedTimeStep is set to 1/120 the physics engine should run 2 steps per frame) (Default: 1/60)
  121. * @param fixedTimeStep fixedTimeStep to use in seconds
  122. */
  123. public setFixedTimeStep(fixedTimeStep: number) {
  124. this._fixedTimeStep = fixedTimeStep;
  125. }
  126. /**
  127. * Sets the maximum number of steps by the physics engine per frame (Default: 5)
  128. * @param maxSteps the maximum number of steps by the physics engine per frame
  129. */
  130. public setMaxSteps(maxSteps: number) {
  131. this._maxSteps = maxSteps;
  132. }
  133. /**
  134. * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
  135. * @returns the current timestep in seconds
  136. */
  137. public getTimeStep(): number {
  138. return this._timeStep;
  139. }
  140. /**
  141. * The create custom shape handler function to be called when using BABYLON.PhysicsImposter.CustomImpostor
  142. */
  143. public onCreateCustomShape: (impostor: PhysicsImpostor) => any;
  144. // Ammo's contactTest and contactPairTest take a callback that runs synchronously, wrap them so that they are easier to consume
  145. private _isImpostorInContact(impostor: PhysicsImpostor) {
  146. this._tmpContactCallbackResult = false;
  147. this.world.contactTest(impostor.physicsBody, this._tmpAmmoConcreteContactResultCallback);
  148. return this._tmpContactCallbackResult;
  149. }
  150. // Ammo's collision events have some weird quirks
  151. // contactPairTest fires too many events as it fires events even when objects are close together but contactTest does not
  152. // so only fire event if both contactTest and contactPairTest have a hit
  153. private _isImpostorPairInContact(impostorA: PhysicsImpostor, impostorB: PhysicsImpostor) {
  154. this._tmpContactCallbackResult = false;
  155. this.world.contactPairTest(impostorA.physicsBody, impostorB.physicsBody, this._tmpAmmoConcreteContactResultCallback);
  156. return this._tmpContactCallbackResult;
  157. }
  158. // Ammo's behavior when maxSteps > 0 does not behave as described in docs
  159. // @see http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
  160. //
  161. // When maxSteps is 0 do the entire simulation in one step
  162. // When maxSteps is > 0, run up to maxStep times, if on the last step the (remaining step - fixedTimeStep) is < fixedTimeStep, the remainder will be used for the step. (eg. if remainder is 1.001 and fixedTimeStep is 1 the last step will be 1.001, if instead it did 2 steps (1, 0.001) issues occuered when having a tiny step in ammo)
  163. // Note: To get deterministic physics, timeStep would always need to be divisible by fixedTimeStep
  164. private _stepSimulation(timeStep: number = 1 / 60, maxSteps: number = 10, fixedTimeStep: number = 1 / 60) {
  165. if (maxSteps == 0) {
  166. this.world.stepSimulation(timeStep, 0);
  167. } else {
  168. while (maxSteps > 0 && timeStep > 0) {
  169. if (timeStep - fixedTimeStep < fixedTimeStep) {
  170. this.world.stepSimulation(timeStep, 0);
  171. timeStep = 0;
  172. } else {
  173. timeStep -= fixedTimeStep;
  174. this.world.stepSimulation(fixedTimeStep, 0);
  175. }
  176. maxSteps--;
  177. }
  178. }
  179. }
  180. /**
  181. * Moves the physics simulation forward delta seconds and updates the given physics imposters
  182. * Prior to the step the imposters physics location is set to the position of the babylon meshes
  183. * After the step the babylon meshes are set to the position of the physics imposters
  184. * @param delta amount of time to step forward
  185. * @param impostors array of imposters to update before/after the step
  186. */
  187. public executeStep(delta: number, impostors: Array<PhysicsImpostor>): void {
  188. for (var impostor of impostors) {
  189. // Update physics world objects to match babylon world
  190. if (!impostor.soft) {
  191. impostor.beforeStep();
  192. }
  193. }
  194. this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps, this._fixedTimeStep);
  195. for (var mainImpostor of impostors) {
  196. // After physics update make babylon world objects match physics world objects
  197. if (mainImpostor.soft) {
  198. this._afterSoftStep(mainImpostor);
  199. }
  200. else {
  201. mainImpostor.afterStep();
  202. }
  203. // Handle collision event
  204. if (mainImpostor._onPhysicsCollideCallbacks.length > 0) {
  205. if (this._isImpostorInContact(mainImpostor)) {
  206. for (var collideCallback of mainImpostor._onPhysicsCollideCallbacks) {
  207. for (var otherImpostor of collideCallback.otherImpostors) {
  208. if (mainImpostor.physicsBody.isActive() || otherImpostor.physicsBody.isActive()) {
  209. if (this._isImpostorPairInContact(mainImpostor, otherImpostor)) {
  210. mainImpostor.onCollide({ body: otherImpostor.physicsBody, point: this._tmpContactPoint });
  211. otherImpostor.onCollide({ body: mainImpostor.physicsBody, point: this._tmpContactPoint });
  212. }
  213. }
  214. }
  215. }
  216. }
  217. }
  218. }
  219. }
  220. /**
  221. * Update babylon mesh to match physics world object
  222. * @param impostor imposter to match
  223. */
  224. private _afterSoftStep(impostor: PhysicsImpostor): void {
  225. if (impostor.type === PhysicsImpostor.RopeImpostor) {
  226. this._ropeStep(impostor);
  227. }
  228. else {
  229. this._softbodyOrClothStep(impostor);
  230. }
  231. }
  232. /**
  233. * Update babylon mesh vertices vertices to match physics world softbody or cloth
  234. * @param impostor imposter to match
  235. */
  236. private _ropeStep(impostor: PhysicsImpostor): void {
  237. var bodyVertices = impostor.physicsBody.get_m_nodes();
  238. var nbVertices = bodyVertices.size();
  239. var node: any;
  240. var nodePositions: any;
  241. var x, y, z: number;
  242. var path: Array<Vector3> = new Array();
  243. for (var n = 0; n < nbVertices; n++) {
  244. node = bodyVertices.at(n);
  245. nodePositions = node.get_m_x();
  246. x = nodePositions.x();
  247. y = nodePositions.y();
  248. z = nodePositions.z();
  249. path.push(new Vector3(x, y, z));
  250. }
  251. var object = impostor.object;
  252. var shape = impostor.getParam("shape");
  253. if (impostor._isFromLine) {
  254. impostor.object = LinesBuilder.CreateLines("lines", { points: path, instance: <LinesMesh>object });
  255. }
  256. else {
  257. impostor.object = ShapeBuilder.ExtrudeShape("ext", { shape: shape, path: path, instance: <Mesh>object });
  258. }
  259. }
  260. /**
  261. * Update babylon mesh vertices vertices to match physics world softbody or cloth
  262. * @param impostor imposter to match
  263. */
  264. private _softbodyOrClothStep(impostor: PhysicsImpostor): void {
  265. var normalDirection = (impostor.type === PhysicsImpostor.ClothImpostor) ? 1 : -1;
  266. var object = impostor.object;
  267. var vertexPositions = object.getVerticesData(VertexBuffer.PositionKind);
  268. if (!vertexPositions) {
  269. vertexPositions = [];
  270. }
  271. var vertexNormals = object.getVerticesData(VertexBuffer.NormalKind);
  272. if (!vertexNormals) {
  273. vertexNormals = [];
  274. }
  275. var nbVertices = vertexPositions.length / 3;
  276. var bodyVertices = impostor.physicsBody.get_m_nodes();
  277. var node: any;
  278. var nodePositions: any;
  279. var nodeNormals: any;
  280. var x, y, z: number;
  281. var nx, ny, nz: number;
  282. for (var n = 0; n < nbVertices; n++) {
  283. node = bodyVertices.at(n);
  284. nodePositions = node.get_m_x();
  285. x = nodePositions.x();
  286. y = nodePositions.y();
  287. z = nodePositions.z() * normalDirection;
  288. var nodeNormals = node.get_m_n();
  289. nx = nodeNormals.x();
  290. ny = nodeNormals.y();
  291. nz = nodeNormals.z() * normalDirection;
  292. vertexPositions[3 * n] = x;
  293. vertexPositions[3 * n + 1] = y;
  294. vertexPositions[3 * n + 2] = z;
  295. vertexNormals[3 * n] = nx;
  296. vertexNormals[3 * n + 1] = ny;
  297. vertexNormals[3 * n + 2] = nz;
  298. }
  299. var vertex_data = new VertexData();
  300. vertex_data.positions = vertexPositions;
  301. vertex_data.normals = vertexNormals;
  302. vertex_data.uvs = object.getVerticesData(VertexBuffer.UVKind);
  303. vertex_data.colors = object.getVerticesData(VertexBuffer.ColorKind);
  304. if (object && object.getIndices) {
  305. vertex_data.indices = object.getIndices();
  306. }
  307. vertex_data.applyToMesh(<Mesh>object);
  308. }
  309. private _tmpMatrix = new Matrix();
  310. /**
  311. * Applies an impulse on the imposter
  312. * @param impostor imposter to apply impulse to
  313. * @param force amount of force to be applied to the imposter
  314. * @param contactPoint the location to apply the impulse on the imposter
  315. */
  316. public applyImpulse(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
  317. if (!impostor.soft) {
  318. impostor.physicsBody.activate();
  319. var worldPoint = this._tmpAmmoVectorA;
  320. var impulse = this._tmpAmmoVectorB;
  321. // Convert contactPoint relative to center of mass
  322. if (impostor.object && impostor.object.getWorldMatrix) {
  323. contactPoint.subtractInPlace(impostor.object.getWorldMatrix().getTranslation());
  324. }
  325. worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
  326. impulse.setValue(force.x, force.y, force.z);
  327. impostor.physicsBody.applyImpulse(impulse, worldPoint);
  328. }
  329. else {
  330. Logger.Warn("Cannot be applied to a soft body");
  331. }
  332. }
  333. /**
  334. * Applies a force on the imposter
  335. * @param impostor imposter to apply force
  336. * @param force amount of force to be applied to the imposter
  337. * @param contactPoint the location to apply the force on the imposter
  338. */
  339. public applyForce(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
  340. if (!impostor.soft) {
  341. impostor.physicsBody.activate();
  342. var worldPoint = this._tmpAmmoVectorA;
  343. var impulse = this._tmpAmmoVectorB;
  344. worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
  345. // Convert contactPoint relative to center of mass
  346. if (impostor.object && impostor.object.getWorldMatrix) {
  347. var localTranslation = impostor.object.getWorldMatrix().getTranslation();
  348. worldPoint.x -= localTranslation.x;
  349. worldPoint.y -= localTranslation.y;
  350. worldPoint.z -= localTranslation.z;
  351. }
  352. impulse.setValue(force.x, force.y, force.z);
  353. impostor.physicsBody.applyForce(impulse, worldPoint);
  354. }
  355. else {
  356. Logger.Warn("Cannot be applied to a soft body");
  357. }
  358. }
  359. /**
  360. * Creates a physics body using the plugin
  361. * @param impostor the imposter to create the physics body on
  362. */
  363. public generatePhysicsBody(impostor: PhysicsImpostor) {
  364. // Note: this method will not be called on child imposotrs for compound impostors
  365. impostor._pluginData.toDispose = [];
  366. //parent-child relationship
  367. if (impostor.parent) {
  368. if (impostor.physicsBody) {
  369. this.removePhysicsBody(impostor);
  370. impostor.forceUpdate();
  371. }
  372. return;
  373. }
  374. if (impostor.isBodyInitRequired()) {
  375. var colShape = this._createShape(impostor);
  376. var mass = impostor.getParam("mass");
  377. impostor._pluginData.mass = mass;
  378. if (impostor.soft) {
  379. colShape.get_m_cfg().set_collisions(0x11);
  380. colShape.get_m_cfg().set_kDP(impostor.getParam("damping"));
  381. this.bjsAMMO.castObject(colShape, this.bjsAMMO.btCollisionObject).getCollisionShape().setMargin(impostor.getParam("margin"));
  382. colShape.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
  383. this.world.addSoftBody(colShape, 1, -1);
  384. impostor.physicsBody = colShape;
  385. impostor._pluginData.toDispose.push(colShape);
  386. this.setBodyPressure(impostor, 0);
  387. if (impostor.type === PhysicsImpostor.SoftbodyImpostor) {
  388. this.setBodyPressure(impostor, impostor.getParam("pressure"));
  389. }
  390. this.setBodyStiffness(impostor, impostor.getParam("stiffness"));
  391. this.setBodyVelocityIterations(impostor, impostor.getParam("velocityIterations"));
  392. this.setBodyPositionIterations(impostor, impostor.getParam("positionIterations"));
  393. }
  394. else {
  395. var localInertia = new this.bjsAMMO.btVector3(0, 0, 0);
  396. var startTransform = new this.bjsAMMO.btTransform();
  397. startTransform.setIdentity();
  398. if (mass !== 0) {
  399. colShape.calculateLocalInertia(mass, localInertia);
  400. }
  401. this._tmpAmmoVectorA.setValue(impostor.object.position.x, impostor.object.position.y, impostor.object.position.z);
  402. this._tmpAmmoQuaternion.setValue(impostor.object.rotationQuaternion!.x, impostor.object.rotationQuaternion!.y, impostor.object.rotationQuaternion!.z, impostor.object.rotationQuaternion!.w);
  403. startTransform.setOrigin(this._tmpAmmoVectorA);
  404. startTransform.setRotation(this._tmpAmmoQuaternion);
  405. var myMotionState = new this.bjsAMMO.btDefaultMotionState(startTransform);
  406. var rbInfo = new this.bjsAMMO.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
  407. var body = new this.bjsAMMO.btRigidBody(rbInfo);
  408. // Make objects kinematic if it's mass is 0
  409. if (mass === 0) {
  410. body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
  411. body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
  412. }
  413. // Disable collision if NoImpostor, but keep collision if shape is btCompoundShape
  414. if (impostor.type == PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
  415. body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
  416. }
  417. let group = impostor.getParam("group");
  418. let mask = impostor.getParam("mask");
  419. if (group && mask) {
  420. this.world.addRigidBody(body, group, mask);
  421. } else {
  422. this.world.addRigidBody(body);
  423. }
  424. impostor.physicsBody = body;
  425. impostor._pluginData.toDispose = impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
  426. }
  427. this.setBodyRestitution(impostor, impostor.getParam("restitution"));
  428. this.setBodyFriction(impostor, impostor.getParam("friction"));
  429. }
  430. }
  431. /**
  432. * Removes the physics body from the imposter and disposes of the body's memory
  433. * @param impostor imposter to remove the physics body from
  434. */
  435. public removePhysicsBody(impostor: PhysicsImpostor) {
  436. if (this.world) {
  437. if (impostor.soft) {
  438. this.world.removeSoftBody(impostor.physicsBody);
  439. } else {
  440. this.world.removeRigidBody(impostor.physicsBody);
  441. }
  442. if (impostor._pluginData) {
  443. impostor._pluginData.toDispose.forEach((d: any) => {
  444. this.bjsAMMO.destroy(d);
  445. });
  446. impostor._pluginData.toDispose = [];
  447. }
  448. }
  449. }
  450. /**
  451. * Generates a joint
  452. * @param impostorJoint the imposter joint to create the joint with
  453. */
  454. public generateJoint(impostorJoint: PhysicsImpostorJoint) {
  455. var mainBody = impostorJoint.mainImpostor.physicsBody;
  456. var connectedBody = impostorJoint.connectedImpostor.physicsBody;
  457. if (!mainBody || !connectedBody) {
  458. return;
  459. }
  460. var jointData = impostorJoint.joint.jointData;
  461. if (!jointData.mainPivot) {
  462. jointData.mainPivot = new Vector3(0, 0, 0);
  463. }
  464. if (!jointData.connectedPivot) {
  465. jointData.connectedPivot = new Vector3(0, 0, 0);
  466. }
  467. var joint: any;
  468. switch (impostorJoint.joint.type) {
  469. case PhysicsJoint.DistanceJoint:
  470. var distance = (<DistanceJointData>jointData).maxDistance;
  471. if (distance) {
  472. jointData.mainPivot = new Vector3(0, -distance / 2, 0);
  473. jointData.connectedPivot = new Vector3(0, distance / 2, 0);
  474. }
  475. joint = new this.bjsAMMO.btPoint2PointConstraint(mainBody, connectedBody, new this.bjsAMMO.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new this.bjsAMMO.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
  476. break;
  477. case PhysicsJoint.HingeJoint:
  478. if (!jointData.mainAxis) {
  479. jointData.mainAxis = new Vector3(0, 0, 0);
  480. }
  481. if (!jointData.connectedAxis) {
  482. jointData.connectedAxis = new Vector3(0, 0, 0);
  483. }
  484. var mainAxis = new this.bjsAMMO.btVector3(jointData.mainAxis.x, jointData.mainAxis.y, jointData.mainAxis.z);
  485. var connectedAxis = new this.bjsAMMO.btVector3(jointData.connectedAxis.x, jointData.connectedAxis.y, jointData.connectedAxis.z);
  486. joint = new this.bjsAMMO.btHingeConstraint(mainBody, connectedBody, new this.bjsAMMO.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new this.bjsAMMO.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z), mainAxis, connectedAxis);
  487. break;
  488. case PhysicsJoint.BallAndSocketJoint:
  489. joint = new this.bjsAMMO.btPoint2PointConstraint(mainBody, connectedBody, new this.bjsAMMO.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new this.bjsAMMO.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
  490. break;
  491. default:
  492. Logger.Warn("JointType not currently supported by the Ammo plugin, falling back to PhysicsJoint.BallAndSocketJoint");
  493. joint = new this.bjsAMMO.btPoint2PointConstraint(mainBody, connectedBody, new this.bjsAMMO.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new this.bjsAMMO.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
  494. break;
  495. }
  496. this.world.addConstraint(joint, !impostorJoint.joint.jointData.collision);
  497. impostorJoint.joint.physicsJoint = joint;
  498. }
  499. /**
  500. * Removes a joint
  501. * @param impostorJoint the imposter joint to remove the joint from
  502. */
  503. public removeJoint(impostorJoint: PhysicsImpostorJoint) {
  504. if (this.world) {
  505. this.world.removeConstraint(impostorJoint.joint.physicsJoint);
  506. }
  507. }
  508. // adds all verticies (including child verticies) to the triangle mesh
  509. private _addMeshVerts(btTriangleMesh: any, topLevelObject: IPhysicsEnabledObject, object: IPhysicsEnabledObject) {
  510. var triangleCount = 0;
  511. if (object && object.getIndices && object.getWorldMatrix && object.getChildMeshes) {
  512. var indices = object.getIndices();
  513. if (!indices) {
  514. indices = [];
  515. }
  516. var vertexPositions = object.getVerticesData(VertexBuffer.PositionKind);
  517. if (!vertexPositions) {
  518. vertexPositions = [];
  519. }
  520. object.computeWorldMatrix(false);
  521. var faceCount = indices.length / 3;
  522. for (var i = 0; i < faceCount; i++) {
  523. var triPoints = [];
  524. for (var point = 0; point < 3; point++) {
  525. var v = new Vector3(vertexPositions[(indices[(i * 3) + point] * 3) + 0], vertexPositions[(indices[(i * 3) + point] * 3) + 1], vertexPositions[(indices[(i * 3) + point] * 3) + 2]);
  526. // Adjust for initial scaling
  527. Matrix.ScalingToRef(object.scaling.x, object.scaling.y, object.scaling.z, this._tmpMatrix);
  528. v = Vector3.TransformCoordinates(v, this._tmpMatrix);
  529. var vec: any;
  530. if (point == 0) {
  531. vec = this._tmpAmmoVectorA;
  532. } else if (point == 1) {
  533. vec = this._tmpAmmoVectorB;
  534. } else {
  535. vec = this._tmpAmmoVectorC;
  536. }
  537. vec.setValue(v.x, v.y, v.z);
  538. triPoints.push(vec);
  539. }
  540. btTriangleMesh.addTriangle(triPoints[0], triPoints[1], triPoints[2]);
  541. triangleCount++;
  542. }
  543. object.getChildMeshes().forEach((m) => {
  544. triangleCount += this._addMeshVerts(btTriangleMesh, topLevelObject, m);
  545. });
  546. }
  547. return triangleCount;
  548. }
  549. /**
  550. * Initialise the soft body vertices to match its object's (mesh) vertices
  551. * Softbody vertices (nodes) are in world space and to match this
  552. * The object's position and rotation is set to zero and so its vertices are also then set in world space
  553. * @param impostor to create the softbody for
  554. */
  555. private _softVertexData(impostor: PhysicsImpostor): VertexData {
  556. var object = impostor.object;
  557. if (object && object.getIndices && object.getWorldMatrix && object.getChildMeshes) {
  558. var indices = object.getIndices();
  559. if (!indices) {
  560. indices = [];
  561. }
  562. var vertexPositions = object.getVerticesData(VertexBuffer.PositionKind);
  563. if (!vertexPositions) {
  564. vertexPositions = [];
  565. }
  566. var vertexNormals = object.getVerticesData(VertexBuffer.NormalKind);
  567. if (!vertexNormals) {
  568. vertexNormals = [];
  569. }
  570. object.computeWorldMatrix(false);
  571. var newPoints = [];
  572. var newNorms = [];
  573. for (var i = 0; i < vertexPositions.length; i += 3) {
  574. var v = new Vector3(vertexPositions[i], vertexPositions[i + 1], vertexPositions[i + 2]);
  575. var n = new Vector3(vertexNormals[i], vertexNormals[i + 1], vertexNormals[i + 2]);
  576. v = Vector3.TransformCoordinates(v, object.getWorldMatrix());
  577. n = Vector3.TransformNormal(n, object.getWorldMatrix());
  578. newPoints.push(v.x, v.y, v.z);
  579. newNorms.push(n.x, n.y, n.z);
  580. }
  581. var vertex_data = new VertexData();
  582. vertex_data.positions = newPoints;
  583. vertex_data.normals = newNorms;
  584. vertex_data.uvs = object.getVerticesData(VertexBuffer.UVKind);
  585. vertex_data.colors = object.getVerticesData(VertexBuffer.ColorKind);
  586. if (object && object.getIndices) {
  587. vertex_data.indices = object.getIndices();
  588. }
  589. vertex_data.applyToMesh(<Mesh>object);
  590. object.position = Vector3.Zero();
  591. object.rotationQuaternion = null;
  592. object.rotation = Vector3.Zero();
  593. object.computeWorldMatrix(true);
  594. return vertex_data;
  595. }
  596. return VertexData.ExtractFromMesh(<Mesh>object);
  597. }
  598. /**
  599. * Create an impostor's soft body
  600. * @param impostor to create the softbody for
  601. */
  602. private _createSoftbody(impostor: PhysicsImpostor) {
  603. var object = impostor.object;
  604. if (object && object.getIndices) {
  605. var indices = object.getIndices();
  606. if (!indices) {
  607. indices = [];
  608. }
  609. var vertex_data = this._softVertexData(impostor);
  610. var vertexPositions = vertex_data.positions;
  611. var vertexNormals = vertex_data.normals;
  612. if (vertexPositions === null || vertexNormals === null) {
  613. return new this.bjsAMMO.btCompoundShape();
  614. }
  615. else {
  616. var triPoints = [];
  617. var triNorms = [];
  618. for (var i = 0; i < vertexPositions.length; i += 3) {
  619. var v = new Vector3(vertexPositions[i], vertexPositions[i + 1], vertexPositions[i + 2]);
  620. var n = new Vector3(vertexNormals[i], vertexNormals[i + 1], vertexNormals[i + 2]);
  621. triPoints.push(v.x, v.y, -v.z);
  622. triNorms.push(n.x, n.y, -n.z);
  623. }
  624. var softBody = new this.bjsAMMO.btSoftBodyHelpers().CreateFromTriMesh(
  625. this.world.getWorldInfo(),
  626. triPoints,
  627. object.getIndices(),
  628. indices.length / 3,
  629. true
  630. );
  631. var nbVertices = vertexPositions.length / 3;
  632. var bodyVertices = softBody.get_m_nodes();
  633. var node: any;
  634. var nodeNormals: any;
  635. for (var i = 0; i < nbVertices; i++) {
  636. node = bodyVertices.at(i);
  637. var nodeNormals = node.get_m_n();
  638. nodeNormals.setX(triNorms[3 * i]);
  639. nodeNormals.setY(triNorms[3 * i + 1]);
  640. nodeNormals.setZ(triNorms[3 * i + 2]);
  641. }
  642. return softBody;
  643. }
  644. }
  645. }
  646. /**
  647. * Create cloth for an impostor
  648. * @param impostor to create the softbody for
  649. */
  650. private _createCloth(impostor: PhysicsImpostor) {
  651. var object = impostor.object;
  652. if (object && object.getIndices) {
  653. var indices = object.getIndices();
  654. if (!indices) {
  655. indices = [];
  656. }
  657. var vertex_data = this._softVertexData(impostor);
  658. var vertexPositions = vertex_data.positions;
  659. var vertexNormals = vertex_data.normals;
  660. if (vertexPositions === null || vertexNormals === null) {
  661. return new this.bjsAMMO.btCompoundShape();
  662. }
  663. else {
  664. var len = vertexPositions.length;
  665. var segments = Math.sqrt(len / 3);
  666. impostor.segments = segments;
  667. var segs = segments - 1;
  668. this._tmpAmmoVectorA.setValue(vertexPositions[0], vertexPositions[1], vertexPositions[2]);
  669. this._tmpAmmoVectorB.setValue(vertexPositions[3 * segs], vertexPositions[3 * segs + 1], vertexPositions[3 * segs + 2]);
  670. this._tmpAmmoVectorD.setValue(vertexPositions[len - 3], vertexPositions[len - 2], vertexPositions[len - 1]);
  671. this._tmpAmmoVectorC.setValue(vertexPositions[len - 3 - 3 * segs], vertexPositions[len - 2 - 3 * segs], vertexPositions[len - 1 - 3 * segs]);
  672. var clothBody = new this.bjsAMMO.btSoftBodyHelpers().CreatePatch(
  673. this.world.getWorldInfo(),
  674. this._tmpAmmoVectorA,
  675. this._tmpAmmoVectorB,
  676. this._tmpAmmoVectorC,
  677. this._tmpAmmoVectorD,
  678. segments,
  679. segments,
  680. impostor.getParam("fixedPoints"),
  681. true
  682. );
  683. return clothBody;
  684. }
  685. }
  686. }
  687. /**
  688. * Create rope for an impostor
  689. * @param impostor to create the softbody for
  690. */
  691. private _createRope(impostor: PhysicsImpostor) {
  692. var len: number;
  693. var segments: number;
  694. var vertex_data = this._softVertexData(impostor);
  695. var vertexPositions = vertex_data.positions;
  696. var vertexNormals = vertex_data.normals;
  697. if (vertexPositions === null || vertexNormals === null) {
  698. return new this.bjsAMMO.btCompoundShape();
  699. }
  700. //force the mesh to be updatable
  701. vertex_data.applyToMesh(<Mesh>impostor.object, true);
  702. impostor._isFromLine = true;
  703. // If in lines mesh all normals will be zero
  704. var vertexSquared: Array<number> = <Array<number>>vertexNormals.map((x: number) => x * x);
  705. var reducer = (accumulator: number, currentValue: number): number => accumulator + currentValue;
  706. var reduced: number = vertexSquared.reduce(reducer);
  707. if (reduced === 0) { // line mesh
  708. len = vertexPositions.length;
  709. segments = len / 3 - 1;
  710. this._tmpAmmoVectorA.setValue(vertexPositions[0], vertexPositions[1], vertexPositions[2]);
  711. this._tmpAmmoVectorB.setValue(vertexPositions[len - 3], vertexPositions[len - 2], vertexPositions[len - 1]);
  712. }
  713. else { //extruded mesh
  714. impostor._isFromLine = false;
  715. var pathVectors = impostor.getParam("path");
  716. var shape = impostor.getParam("shape");
  717. if (shape === null) {
  718. Logger.Warn("No shape available for extruded mesh");
  719. return new this.bjsAMMO.btCompoundShape();
  720. }
  721. if ((vertexPositions!.length % (3 * pathVectors.length)) !== 0) {
  722. Logger.Warn("Path does not match extrusion");
  723. return new this.bjsAMMO.btCompoundShape();
  724. }
  725. len = pathVectors.length;
  726. segments = len - 1;
  727. this._tmpAmmoVectorA.setValue(pathVectors[0].x, pathVectors[0].y, pathVectors[0].z);
  728. this._tmpAmmoVectorB.setValue(pathVectors[len - 1].x, pathVectors[len - 1].y, pathVectors[len - 1].z);
  729. }
  730. impostor.segments = segments;
  731. var fixedPoints = impostor.getParam("fixedPoints");
  732. fixedPoints = (fixedPoints > 3) ? 3 : fixedPoints;
  733. var ropeBody = new this.bjsAMMO.btSoftBodyHelpers().CreateRope(
  734. this.world.getWorldInfo(),
  735. this._tmpAmmoVectorA,
  736. this._tmpAmmoVectorB,
  737. segments - 1,
  738. fixedPoints
  739. );
  740. ropeBody.get_m_cfg().set_collisions(0x11);
  741. return ropeBody;
  742. }
  743. /**
  744. * Create a custom physics impostor shape using the plugin's onCreateCustomShape handler
  745. * @param impostor to create the custom physics shape for
  746. */
  747. private _createCustom(impostor: PhysicsImpostor): any {
  748. let returnValue: any = null;
  749. if (this.onCreateCustomShape) {
  750. returnValue = this.onCreateCustomShape(impostor);
  751. }
  752. if (returnValue == null) {
  753. returnValue = new this.bjsAMMO.btCompoundShape();
  754. }
  755. return returnValue;
  756. }
  757. // adds all verticies (including child verticies) to the convex hull shape
  758. private _addHullVerts(btConvexHullShape: any, topLevelObject: IPhysicsEnabledObject, object: IPhysicsEnabledObject) {
  759. var triangleCount = 0;
  760. if (object && object.getIndices && object.getWorldMatrix && object.getChildMeshes) {
  761. var indices = object.getIndices();
  762. if (!indices) {
  763. indices = [];
  764. }
  765. var vertexPositions = object.getVerticesData(VertexBuffer.PositionKind);
  766. if (!vertexPositions) {
  767. vertexPositions = [];
  768. }
  769. object.computeWorldMatrix(false);
  770. var faceCount = indices.length / 3;
  771. for (var i = 0; i < faceCount; i++) {
  772. var triPoints = [];
  773. for (var point = 0; point < 3; point++) {
  774. var v = new Vector3(vertexPositions[(indices[(i * 3) + point] * 3) + 0], vertexPositions[(indices[(i * 3) + point] * 3) + 1], vertexPositions[(indices[(i * 3) + point] * 3) + 2]);
  775. // Adjust for initial scaling
  776. Matrix.ScalingToRef(object.scaling.x, object.scaling.y, object.scaling.z, this._tmpMatrix);
  777. v = Vector3.TransformCoordinates(v, this._tmpMatrix);
  778. var vec: any;
  779. if (point == 0) {
  780. vec = this._tmpAmmoVectorA;
  781. } else if (point == 1) {
  782. vec = this._tmpAmmoVectorB;
  783. } else {
  784. vec = this._tmpAmmoVectorC;
  785. }
  786. vec.setValue(v.x, v.y, v.z);
  787. triPoints.push(vec);
  788. }
  789. btConvexHullShape.addPoint(triPoints[0], true);
  790. btConvexHullShape.addPoint(triPoints[1], true);
  791. btConvexHullShape.addPoint(triPoints[2], true);
  792. triangleCount++;
  793. }
  794. object.getChildMeshes().forEach((m) => {
  795. triangleCount += this._addHullVerts(btConvexHullShape, topLevelObject, m);
  796. });
  797. }
  798. return triangleCount;
  799. }
  800. private _createShape(impostor: PhysicsImpostor, ignoreChildren = false) {
  801. var object = impostor.object;
  802. var returnValue: any;
  803. var extendSize = impostor.getObjectExtendSize();
  804. if (!ignoreChildren) {
  805. var meshChildren = impostor.object.getChildMeshes ? impostor.object.getChildMeshes(true) : [];
  806. returnValue = new this.bjsAMMO.btCompoundShape();
  807. // Add shape of all children to the compound shape
  808. var childrenAdded = 0;
  809. meshChildren.forEach((childMesh) => {
  810. var childImpostor = childMesh.getPhysicsImpostor();
  811. if (childImpostor) {
  812. if (childImpostor.type == PhysicsImpostor.MeshImpostor) {
  813. throw "A child MeshImpostor is not supported. Only primitive impostors are supported as children (eg. box or sphere)";
  814. }
  815. var shape = this._createShape(childImpostor);
  816. // Position needs to be scaled based on parent's scaling
  817. var parentMat = childMesh.parent!.getWorldMatrix().clone();
  818. var s = new Vector3();
  819. parentMat.decompose(s);
  820. this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x * s.x, childMesh.position.y * s.y, childMesh.position.z * s.z);
  821. this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion!.x, childMesh.rotationQuaternion!.y, childMesh.rotationQuaternion!.z, childMesh.rotationQuaternion!.w);
  822. this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
  823. returnValue.addChildShape(this._tmpAmmoTransform, shape);
  824. childImpostor.dispose();
  825. childrenAdded++;
  826. }
  827. });
  828. if (childrenAdded > 0) {
  829. // Add parents shape as a child if present
  830. if (impostor.type != PhysicsImpostor.NoImpostor) {
  831. var shape = this._createShape(impostor, true);
  832. if (shape) {
  833. this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
  834. this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
  835. this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
  836. returnValue.addChildShape(this._tmpAmmoTransform, shape);
  837. }
  838. }
  839. return returnValue;
  840. } else {
  841. // If no children with impostors create the actual shape below instead
  842. this.bjsAMMO.destroy(returnValue);
  843. returnValue = null;
  844. }
  845. }
  846. switch (impostor.type) {
  847. case PhysicsImpostor.SphereImpostor:
  848. // Is there a better way to compare floats number? With an epsylon or with a Math function
  849. if (Scalar.WithinEpsilon(extendSize.x, extendSize.y, 0.0001) && Scalar.WithinEpsilon(extendSize.x, extendSize.z, 0.0001)) {
  850. returnValue = new this.bjsAMMO.btSphereShape(extendSize.x / 2);
  851. } else {
  852. // create a btMultiSphereShape because it's not possible to set a local scaling on a btSphereShape
  853. var positions = [new this.bjsAMMO.btVector3(0, 0, 0)];
  854. var radii = [1];
  855. returnValue = new this.bjsAMMO.btMultiSphereShape(positions, radii, 1);
  856. returnValue.setLocalScaling(new this.bjsAMMO.btVector3(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2));
  857. }
  858. break;
  859. case PhysicsImpostor.CapsuleImpostor:
  860. returnValue = new this.bjsAMMO.btCapsuleShape(extendSize.x / 2, extendSize.y / 2);
  861. break;
  862. case PhysicsImpostor.CylinderImpostor:
  863. this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
  864. returnValue = new this.bjsAMMO.btCylinderShape(this._tmpAmmoVectorA);
  865. break;
  866. case PhysicsImpostor.PlaneImpostor:
  867. case PhysicsImpostor.BoxImpostor:
  868. this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
  869. returnValue = new this.bjsAMMO.btBoxShape(this._tmpAmmoVectorA);
  870. break;
  871. case PhysicsImpostor.MeshImpostor:
  872. if (impostor.getParam("mass") == 0) {
  873. // Only create btBvhTriangleMeshShape impostor is static
  874. // See https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=7283
  875. var tetraMesh = new this.bjsAMMO.btTriangleMesh();
  876. impostor._pluginData.toDispose.push(tetraMesh);
  877. var triangeCount = this._addMeshVerts(tetraMesh, object, object);
  878. if (triangeCount == 0) {
  879. returnValue = new this.bjsAMMO.btCompoundShape();
  880. } else {
  881. returnValue = new this.bjsAMMO.btBvhTriangleMeshShape(tetraMesh);
  882. }
  883. break;
  884. }
  885. // Otherwise create convexHullImpostor
  886. case PhysicsImpostor.ConvexHullImpostor:
  887. var convexMesh = new this.bjsAMMO.btConvexHullShape();
  888. var triangeCount = this._addHullVerts(convexMesh, object, object);
  889. if (triangeCount == 0) {
  890. // Cleanup Unused Convex Hull Shape
  891. impostor._pluginData.toDispose.push(convexMesh);
  892. returnValue = new this.bjsAMMO.btCompoundShape();
  893. } else {
  894. returnValue = convexMesh;
  895. }
  896. break;
  897. case PhysicsImpostor.NoImpostor:
  898. // Fill with sphere but collision is disabled on the rigid body in generatePhysicsBody, using an empty shape caused unexpected movement with joints
  899. returnValue = new this.bjsAMMO.btSphereShape(extendSize.x / 2);
  900. break;
  901. case PhysicsImpostor.CustomImpostor:
  902. // Only usable when the plugin's onCreateCustomShape is set
  903. returnValue = this._createCustom(impostor);
  904. break;
  905. case PhysicsImpostor.SoftbodyImpostor:
  906. // Only usable with a mesh that has sufficient and shared vertices
  907. returnValue = this._createSoftbody(impostor);
  908. break;
  909. case PhysicsImpostor.ClothImpostor:
  910. // Only usable with a ground mesh that has sufficient and shared vertices
  911. returnValue = this._createCloth(impostor);
  912. break;
  913. case PhysicsImpostor.RopeImpostor:
  914. // Only usable with a line mesh or an extruded mesh that is updatable
  915. returnValue = this._createRope(impostor);
  916. break;
  917. default:
  918. Logger.Warn("The impostor type is not currently supported by the ammo plugin.");
  919. break;
  920. }
  921. return returnValue;
  922. }
  923. /**
  924. * Sets the physics body position/rotation from the babylon mesh's position/rotation
  925. * @param impostor imposter containing the physics body and babylon object
  926. */
  927. public setTransformationFromPhysicsBody(impostor: PhysicsImpostor) {
  928. impostor.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform);
  929. impostor.object.position.set(this._tmpAmmoTransform.getOrigin().x(), this._tmpAmmoTransform.getOrigin().y(), this._tmpAmmoTransform.getOrigin().z());
  930. if (!impostor.object.rotationQuaternion) {
  931. if (impostor.object.rotation) {
  932. this._tmpQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
  933. this._tmpQuaternion.toEulerAnglesToRef(impostor.object.rotation);
  934. }
  935. } else {
  936. impostor.object.rotationQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
  937. }
  938. }
  939. /**
  940. * Sets the babylon object's position/rotation from the physics body's position/rotation
  941. * @param impostor imposter containing the physics body and babylon object
  942. * @param newPosition new position
  943. * @param newRotation new rotation
  944. */
  945. public setPhysicsBodyTransformation(impostor: PhysicsImpostor, newPosition: Vector3, newRotation: Quaternion) {
  946. var trans = impostor.physicsBody.getWorldTransform();
  947. // If rotation/position has changed update and activate rigged body
  948. if (
  949. Math.abs(trans.getOrigin().x() - newPosition.x) > Epsilon ||
  950. Math.abs(trans.getOrigin().y() - newPosition.y) > Epsilon ||
  951. Math.abs(trans.getOrigin().z() - newPosition.z) > Epsilon ||
  952. Math.abs(trans.getRotation().x() - newRotation.x) > Epsilon ||
  953. Math.abs(trans.getRotation().y() - newRotation.y) > Epsilon ||
  954. Math.abs(trans.getRotation().z() - newRotation.z) > Epsilon ||
  955. Math.abs(trans.getRotation().w() - newRotation.w) > Epsilon
  956. ) {
  957. this._tmpAmmoVectorA.setValue(newPosition.x, newPosition.y, newPosition.z);
  958. trans.setOrigin(this._tmpAmmoVectorA);
  959. this._tmpAmmoQuaternion.setValue(newRotation.x, newRotation.y, newRotation.z, newRotation.w);
  960. trans.setRotation(this._tmpAmmoQuaternion);
  961. impostor.physicsBody.setWorldTransform(trans);
  962. if (impostor.mass == 0) {
  963. // Kinematic objects must be updated using motion state
  964. var motionState = impostor.physicsBody.getMotionState();
  965. if (motionState) {
  966. motionState.setWorldTransform(trans);
  967. }
  968. } else {
  969. impostor.physicsBody.activate();
  970. }
  971. }
  972. }
  973. /**
  974. * If this plugin is supported
  975. * @returns true if its supported
  976. */
  977. public isSupported(): boolean {
  978. return this.bjsAMMO !== undefined;
  979. }
  980. /**
  981. * Sets the linear velocity of the physics body
  982. * @param impostor imposter to set the velocity on
  983. * @param velocity velocity to set
  984. */
  985. public setLinearVelocity(impostor: PhysicsImpostor, velocity: Vector3) {
  986. this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
  987. if (impostor.soft) {
  988. impostor.physicsBody.linearVelocity(this._tmpAmmoVectorA);
  989. }
  990. else {
  991. impostor.physicsBody.setLinearVelocity(this._tmpAmmoVectorA);
  992. }
  993. }
  994. /**
  995. * Sets the angular velocity of the physics body
  996. * @param impostor imposter to set the velocity on
  997. * @param velocity velocity to set
  998. */
  999. public setAngularVelocity(impostor: PhysicsImpostor, velocity: Vector3) {
  1000. this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
  1001. if (impostor.soft) {
  1002. impostor.physicsBody.angularVelocity(this._tmpAmmoVectorA);
  1003. }
  1004. else {
  1005. impostor.physicsBody.setAngularVelocity(this._tmpAmmoVectorA);
  1006. }
  1007. }
  1008. /**
  1009. * gets the linear velocity
  1010. * @param impostor imposter to get linear velocity from
  1011. * @returns linear velocity
  1012. */
  1013. public getLinearVelocity(impostor: PhysicsImpostor): Nullable<Vector3> {
  1014. if (impostor.soft) {
  1015. var v = impostor.physicsBody.linearVelocity();
  1016. }
  1017. else {
  1018. var v = impostor.physicsBody.getLinearVelocity();
  1019. }
  1020. if (!v) {
  1021. return null;
  1022. }
  1023. var result = new Vector3(v.x(), v.y(), v.z());
  1024. this.bjsAMMO.destroy(v);
  1025. return result;
  1026. }
  1027. /**
  1028. * gets the angular velocity
  1029. * @param impostor imposter to get angular velocity from
  1030. * @returns angular velocity
  1031. */
  1032. public getAngularVelocity(impostor: PhysicsImpostor): Nullable<Vector3> {
  1033. if (impostor.soft) {
  1034. var v = impostor.physicsBody.angularVelocity();
  1035. }
  1036. else {
  1037. var v = impostor.physicsBody.getAngularVelocity();
  1038. }
  1039. if (!v) {
  1040. return null;
  1041. }
  1042. var result = new Vector3(v.x(), v.y(), v.z());
  1043. this.bjsAMMO.destroy(v);
  1044. return result;
  1045. }
  1046. /**
  1047. * Sets the mass of physics body
  1048. * @param impostor imposter to set the mass on
  1049. * @param mass mass to set
  1050. */
  1051. public setBodyMass(impostor: PhysicsImpostor, mass: number) {
  1052. if (impostor.soft) {
  1053. impostor.physicsBody.setTotalMass(mass, false);
  1054. }
  1055. else {
  1056. impostor.physicsBody.setMassProps(mass);
  1057. }
  1058. impostor._pluginData.mass = mass;
  1059. }
  1060. /**
  1061. * Gets the mass of the physics body
  1062. * @param impostor imposter to get the mass from
  1063. * @returns mass
  1064. */
  1065. public getBodyMass(impostor: PhysicsImpostor): number {
  1066. return impostor._pluginData.mass || 0;
  1067. }
  1068. /**
  1069. * Gets friction of the impostor
  1070. * @param impostor impostor to get friction from
  1071. * @returns friction value
  1072. */
  1073. public getBodyFriction(impostor: PhysicsImpostor): number {
  1074. return impostor._pluginData.friction || 0;
  1075. }
  1076. /**
  1077. * Sets friction of the impostor
  1078. * @param impostor impostor to set friction on
  1079. * @param friction friction value
  1080. */
  1081. public setBodyFriction(impostor: PhysicsImpostor, friction: number) {
  1082. if (impostor.soft) {
  1083. impostor.physicsBody.get_m_cfg().set_kDF(friction);
  1084. }
  1085. else {
  1086. impostor.physicsBody.setFriction(friction);
  1087. }
  1088. impostor._pluginData.friction = friction;
  1089. }
  1090. /**
  1091. * Gets restitution of the impostor
  1092. * @param impostor impostor to get restitution from
  1093. * @returns restitution value
  1094. */
  1095. public getBodyRestitution(impostor: PhysicsImpostor): number {
  1096. return impostor._pluginData.restitution || 0;
  1097. }
  1098. /**
  1099. * Sets resitution of the impostor
  1100. * @param impostor impostor to set resitution on
  1101. * @param restitution resitution value
  1102. */
  1103. public setBodyRestitution(impostor: PhysicsImpostor, restitution: number) {
  1104. impostor.physicsBody.setRestitution(restitution);
  1105. impostor._pluginData.restitution = restitution;
  1106. }
  1107. /**
  1108. * Gets pressure inside the impostor
  1109. * @param impostor impostor to get pressure from
  1110. * @returns pressure value
  1111. */
  1112. public getBodyPressure(impostor: PhysicsImpostor): number {
  1113. if (!impostor.soft) {
  1114. Logger.Warn("Pressure is not a property of a rigid body");
  1115. return 0;
  1116. }
  1117. return impostor._pluginData.pressure || 0;
  1118. }
  1119. /**
  1120. * Sets pressure inside a soft body impostor
  1121. * Cloth and rope must remain 0 pressure
  1122. * @param impostor impostor to set pressure on
  1123. * @param pressure pressure value
  1124. */
  1125. public setBodyPressure(impostor: PhysicsImpostor, pressure: number) {
  1126. if (impostor.soft) {
  1127. if (impostor.type === PhysicsImpostor.SoftbodyImpostor) {
  1128. impostor.physicsBody.get_m_cfg().set_kPR(pressure);
  1129. impostor._pluginData.pressure = pressure;
  1130. }
  1131. else {
  1132. impostor.physicsBody.get_m_cfg().set_kPR(0);
  1133. impostor._pluginData.pressure = 0;
  1134. }
  1135. }
  1136. else {
  1137. Logger.Warn("Pressure can only be applied to a softbody");
  1138. }
  1139. }
  1140. /**
  1141. * Gets stiffness of the impostor
  1142. * @param impostor impostor to get stiffness from
  1143. * @returns pressure value
  1144. */
  1145. public getBodyStiffness(impostor: PhysicsImpostor): number {
  1146. if (!impostor.soft) {
  1147. Logger.Warn("Stiffness is not a property of a rigid body");
  1148. return 0;
  1149. }
  1150. return impostor._pluginData.stiffness || 0;
  1151. }
  1152. /**
  1153. * Sets stiffness of the impostor
  1154. * @param impostor impostor to set stiffness on
  1155. * @param stiffness stiffness value from 0 to 1
  1156. */
  1157. public setBodyStiffness(impostor: PhysicsImpostor, stiffness: number) {
  1158. if (impostor.soft) {
  1159. stiffness = stiffness < 0 ? 0 : stiffness;
  1160. stiffness = stiffness > 1 ? 1 : stiffness;
  1161. impostor.physicsBody.get_m_materials().at(0).set_m_kLST(stiffness);
  1162. impostor._pluginData.stiffness = stiffness;
  1163. }
  1164. else {
  1165. Logger.Warn("Stiffness cannot be applied to a rigid body");
  1166. }
  1167. }
  1168. /**
  1169. * Gets velocityIterations of the impostor
  1170. * @param impostor impostor to get velocity iterations from
  1171. * @returns velocityIterations value
  1172. */
  1173. public getBodyVelocityIterations(impostor: PhysicsImpostor): number {
  1174. if (!impostor.soft) {
  1175. Logger.Warn("Velocity iterations is not a property of a rigid body");
  1176. return 0;
  1177. }
  1178. return impostor._pluginData.velocityIterations || 0;
  1179. }
  1180. /**
  1181. * Sets velocityIterations of the impostor
  1182. * @param impostor impostor to set velocity iterations on
  1183. * @param velocityIterations velocityIterations value
  1184. */
  1185. public setBodyVelocityIterations(impostor: PhysicsImpostor, velocityIterations: number) {
  1186. if (impostor.soft) {
  1187. velocityIterations = velocityIterations < 0 ? 0 : velocityIterations;
  1188. impostor.physicsBody.get_m_cfg().set_viterations(velocityIterations);
  1189. impostor._pluginData.velocityIterations = velocityIterations;
  1190. }
  1191. else {
  1192. Logger.Warn("Velocity iterations cannot be applied to a rigid body");
  1193. }
  1194. }
  1195. /**
  1196. * Gets positionIterations of the impostor
  1197. * @param impostor impostor to get position iterations from
  1198. * @returns positionIterations value
  1199. */
  1200. public getBodyPositionIterations(impostor: PhysicsImpostor): number {
  1201. if (!impostor.soft) {
  1202. Logger.Warn("Position iterations is not a property of a rigid body");
  1203. return 0;
  1204. }
  1205. return impostor._pluginData.positionIterations || 0;
  1206. }
  1207. /**
  1208. * Sets positionIterations of the impostor
  1209. * @param impostor impostor to set position on
  1210. * @param positionIterations positionIterations value
  1211. */
  1212. public setBodyPositionIterations(impostor: PhysicsImpostor, positionIterations: number) {
  1213. if (impostor.soft) {
  1214. positionIterations = positionIterations < 0 ? 0 : positionIterations;
  1215. impostor.physicsBody.get_m_cfg().set_piterations(positionIterations);
  1216. impostor._pluginData.positionIterations = positionIterations;
  1217. }
  1218. else {
  1219. Logger.Warn("Position iterations cannot be applied to a rigid body");
  1220. }
  1221. }
  1222. /**
  1223. * Append an anchor to a cloth object
  1224. * @param impostor is the cloth impostor to add anchor to
  1225. * @param otherImpostor is the rigid impostor to anchor to
  1226. * @param width ratio across width from 0 to 1
  1227. * @param height ratio up height from 0 to 1
  1228. * @param influence the elasticity between cloth impostor and anchor from 0, very stretchy to 1, little strech
  1229. * @param noCollisionBetweenLinkedBodies when true collisions between soft impostor and anchor are ignored; default false
  1230. */
  1231. public appendAnchor(impostor: PhysicsImpostor, otherImpostor: PhysicsImpostor, width: number, height: number, influence: number = 1, noCollisionBetweenLinkedBodies: boolean = false) {
  1232. var segs = impostor.segments;
  1233. var nbAcross = Math.round((segs - 1) * width);
  1234. var nbUp = Math.round((segs - 1) * height);
  1235. var nbDown = segs - 1 - nbUp;
  1236. var node = nbAcross + segs * nbDown;
  1237. impostor.physicsBody.appendAnchor(node, otherImpostor.physicsBody, noCollisionBetweenLinkedBodies, influence);
  1238. }
  1239. /**
  1240. * Append an hook to a rope object
  1241. * @param impostor is the rope impostor to add hook to
  1242. * @param otherImpostor is the rigid impostor to hook to
  1243. * @param length ratio along the rope from 0 to 1
  1244. * @param influence the elasticity between soft impostor and anchor from 0, very stretchy to 1, little strech
  1245. * @param noCollisionBetweenLinkedBodies when true collisions between soft impostor and anchor are ignored; default false
  1246. */
  1247. public appendHook(impostor: PhysicsImpostor, otherImpostor: PhysicsImpostor, length: number, influence: number = 1, noCollisionBetweenLinkedBodies: boolean = false) {
  1248. var node = Math.round(impostor.segments * length);
  1249. impostor.physicsBody.appendAnchor(node, otherImpostor.physicsBody, noCollisionBetweenLinkedBodies, influence);
  1250. }
  1251. /**
  1252. * Sleeps the physics body and stops it from being active
  1253. * @param impostor impostor to sleep
  1254. */
  1255. public sleepBody(impostor: PhysicsImpostor) {
  1256. Logger.Warn("sleepBody is not currently supported by the Ammo physics plugin");
  1257. }
  1258. /**
  1259. * Activates the physics body
  1260. * @param impostor impostor to activate
  1261. */
  1262. public wakeUpBody(impostor: PhysicsImpostor) {
  1263. impostor.physicsBody.activate();
  1264. }
  1265. /**
  1266. * Updates the distance parameters of the joint
  1267. * @param joint joint to update
  1268. * @param maxDistance maximum distance of the joint
  1269. * @param minDistance minimum distance of the joint
  1270. */
  1271. public updateDistanceJoint(joint: PhysicsJoint, maxDistance: number, minDistance?: number) {
  1272. Logger.Warn("updateDistanceJoint is not currently supported by the Ammo physics plugin");
  1273. }
  1274. /**
  1275. * Sets a motor on the joint
  1276. * @param joint joint to set motor on
  1277. * @param speed speed of the motor
  1278. * @param maxForce maximum force of the motor
  1279. * @param motorIndex index of the motor
  1280. */
  1281. public setMotor(joint: IMotorEnabledJoint, speed?: number, maxForce?: number, motorIndex?: number) {
  1282. joint.physicsJoint.enableAngularMotor(true, speed, maxForce);
  1283. }
  1284. /**
  1285. * Sets the motors limit
  1286. * @param joint joint to set limit on
  1287. * @param upperLimit upper limit
  1288. * @param lowerLimit lower limit
  1289. */
  1290. public setLimit(joint: IMotorEnabledJoint, upperLimit: number, lowerLimit?: number) {
  1291. Logger.Warn("setLimit is not currently supported by the Ammo physics plugin");
  1292. }
  1293. /**
  1294. * Syncs the position and rotation of a mesh with the impostor
  1295. * @param mesh mesh to sync
  1296. * @param impostor impostor to update the mesh with
  1297. */
  1298. public syncMeshWithImpostor(mesh: AbstractMesh, impostor: PhysicsImpostor) {
  1299. var body = impostor.physicsBody;
  1300. body.getMotionState().getWorldTransform(this._tmpAmmoTransform);
  1301. mesh.position.x = this._tmpAmmoTransform.getOrigin().x();
  1302. mesh.position.y = this._tmpAmmoTransform.getOrigin().y();
  1303. mesh.position.z = this._tmpAmmoTransform.getOrigin().z();
  1304. if (mesh.rotationQuaternion) {
  1305. mesh.rotationQuaternion.x = this._tmpAmmoTransform.getRotation().x();
  1306. mesh.rotationQuaternion.y = this._tmpAmmoTransform.getRotation().y();
  1307. mesh.rotationQuaternion.z = this._tmpAmmoTransform.getRotation().z();
  1308. mesh.rotationQuaternion.w = this._tmpAmmoTransform.getRotation().w();
  1309. }
  1310. }
  1311. /**
  1312. * Gets the radius of the impostor
  1313. * @param impostor impostor to get radius from
  1314. * @returns the radius
  1315. */
  1316. public getRadius(impostor: PhysicsImpostor): number {
  1317. var exntend = impostor.getObjectExtendSize();
  1318. return exntend.x / 2;
  1319. }
  1320. /**
  1321. * Gets the box size of the impostor
  1322. * @param impostor impostor to get box size from
  1323. * @param result the resulting box size
  1324. */
  1325. public getBoxSizeToRef(impostor: PhysicsImpostor, result: Vector3): void {
  1326. var exntend = impostor.getObjectExtendSize();
  1327. result.x = exntend.x;
  1328. result.y = exntend.y;
  1329. result.z = exntend.z;
  1330. }
  1331. /**
  1332. * Disposes of the impostor
  1333. */
  1334. public dispose() {
  1335. // Dispose of world
  1336. this.bjsAMMO.destroy(this.world);
  1337. this.bjsAMMO.destroy(this._solver);
  1338. this.bjsAMMO.destroy(this._overlappingPairCache);
  1339. this.bjsAMMO.destroy(this._dispatcher);
  1340. this.bjsAMMO.destroy(this._collisionConfiguration);
  1341. // Dispose of tmp variables
  1342. this.bjsAMMO.destroy(this._tmpAmmoVectorA);
  1343. this.bjsAMMO.destroy(this._tmpAmmoVectorB);
  1344. this.bjsAMMO.destroy(this._tmpAmmoVectorC);
  1345. this.bjsAMMO.destroy(this._tmpAmmoTransform);
  1346. this.bjsAMMO.destroy(this._tmpAmmoQuaternion);
  1347. this.bjsAMMO.destroy(this._tmpAmmoConcreteContactResultCallback);
  1348. this.world = null;
  1349. }
  1350. /**
  1351. * Does a raycast in the physics world
  1352. * @param from when should the ray start?
  1353. * @param to when should the ray end?
  1354. * @returns PhysicsRaycastResult
  1355. */
  1356. public raycast(from: Vector3, to: Vector3): PhysicsRaycastResult {
  1357. this._tmpAmmoVectorRCA = new this.bjsAMMO.btVector3(from.x, from.y, from.z);
  1358. this._tmpAmmoVectorRCB = new this.bjsAMMO.btVector3(to.x, to.y, to.z);
  1359. var rayCallback = new this.bjsAMMO.ClosestRayResultCallback(this._tmpAmmoVectorRCA, this._tmpAmmoVectorRCB);
  1360. this.world.rayTest(this._tmpAmmoVectorRCA, this._tmpAmmoVectorRCB, rayCallback);
  1361. this._raycastResult.reset(from, to);
  1362. if (rayCallback.hasHit()) {
  1363. // TODO: do we want/need the body? If so, set all the data
  1364. /*
  1365. var rigidBody = this.bjsAMMO.btRigidBody.prototype.upcast(
  1366. rayCallback.get_m_collisionObject()
  1367. );
  1368. var body = {};
  1369. */
  1370. this._raycastResult.setHitData(
  1371. {
  1372. x: rayCallback.get_m_hitNormalWorld().x(),
  1373. y: rayCallback.get_m_hitNormalWorld().y(),
  1374. z: rayCallback.get_m_hitNormalWorld().z(),
  1375. },
  1376. {
  1377. x: rayCallback.get_m_hitPointWorld().x(),
  1378. y: rayCallback.get_m_hitPointWorld().y(),
  1379. z: rayCallback.get_m_hitPointWorld().z(),
  1380. }
  1381. );
  1382. this._raycastResult.calculateHitDistance();
  1383. }
  1384. this.bjsAMMO.destroy(rayCallback);
  1385. this.bjsAMMO.destroy(this._tmpAmmoVectorRCA);
  1386. this.bjsAMMO.destroy(this._tmpAmmoVectorRCB);
  1387. return this._raycastResult;
  1388. }
  1389. }