ammoJSPlugin.ts 66 KB

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