ammoJSPlugin.ts 62 KB

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