ammoJSPlugin.ts 65 KB

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