babylon.oimoJSPlugin.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. module BABYLON {
  2. declare var OIMO: any;
  3. /** @hidden */
  4. export class OimoJSPlugin implements IPhysicsEnginePlugin {
  5. public world: any;
  6. public name: string = "OimoJSPlugin";
  7. public BJSOIMO: any;
  8. constructor(iterations?: number) {
  9. this.BJSOIMO = OIMO;
  10. this.world = new this.BJSOIMO.World({
  11. iterations: iterations
  12. });
  13. this.world.clear();
  14. }
  15. public setGravity(gravity: Vector3) {
  16. this.world.gravity.copy(gravity);
  17. }
  18. public setTimeStep(timeStep: number) {
  19. this.world.timeStep = timeStep;
  20. }
  21. public getTimeStep(): number {
  22. return this.world.timeStep;
  23. }
  24. private _tmpImpostorsArray: Array<PhysicsImpostor> = [];
  25. public executeStep(delta: number, impostors: Array<PhysicsImpostor>) {
  26. impostors.forEach(function (impostor) {
  27. impostor.beforeStep();
  28. });
  29. this.world.step();
  30. impostors.forEach((impostor) => {
  31. impostor.afterStep();
  32. //update the ordered impostors array
  33. this._tmpImpostorsArray[impostor.uniqueId] = impostor;
  34. });
  35. //check for collisions
  36. var contact = this.world.contacts;
  37. while (contact !== null) {
  38. if (contact.touching && !contact.body1.sleeping && !contact.body2.sleeping) {
  39. contact = contact.next;
  40. continue;
  41. }
  42. //is this body colliding with any other? get the impostor
  43. var mainImpostor = this._tmpImpostorsArray[+contact.body1.name];
  44. var collidingImpostor = this._tmpImpostorsArray[+contact.body2.name];
  45. if (!mainImpostor || !collidingImpostor) {
  46. contact = contact.next;
  47. continue;
  48. }
  49. mainImpostor.onCollide({ body: collidingImpostor.physicsBody });
  50. collidingImpostor.onCollide({ body: mainImpostor.physicsBody });
  51. contact = contact.next;
  52. }
  53. }
  54. public applyImpulse(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
  55. var mass = impostor.physicsBody.mass;
  56. impostor.physicsBody.applyImpulse(contactPoint.scale(this.world.invScale), force.scale(this.world.invScale * mass));
  57. }
  58. public applyForce(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
  59. Tools.Warn("Oimo doesn't support applying force. Using impule instead.");
  60. this.applyImpulse(impostor, force, contactPoint);
  61. }
  62. public generatePhysicsBody(impostor: PhysicsImpostor) {
  63. //parent-child relationship. Does this impostor has a parent impostor?
  64. if (impostor.parent) {
  65. if (impostor.physicsBody) {
  66. this.removePhysicsBody(impostor);
  67. //TODO is that needed?
  68. impostor.forceUpdate();
  69. }
  70. return;
  71. }
  72. if (impostor.isBodyInitRequired()) {
  73. var bodyConfig: any = {
  74. name: impostor.uniqueId,
  75. //Oimo must have mass, also for static objects.
  76. config: [impostor.getParam("mass") || 1, impostor.getParam("friction"), impostor.getParam("restitution")],
  77. size: [],
  78. type: [],
  79. pos: [],
  80. posShape: [],
  81. rot: [],
  82. rotShape: [],
  83. move: impostor.getParam("mass") !== 0,
  84. density: impostor.getParam("mass"),
  85. friction: impostor.getParam("friction"),
  86. restitution: impostor.getParam("restitution"),
  87. //Supporting older versions of Oimo
  88. world: this.world
  89. };
  90. var impostors = [impostor];
  91. let addToArray = (parent: IPhysicsEnabledObject) => {
  92. if (!parent.getChildMeshes) return;
  93. parent.getChildMeshes().forEach(function (m) {
  94. if (m.physicsImpostor) {
  95. impostors.push(m.physicsImpostor);
  96. //m.physicsImpostor._init();
  97. }
  98. });
  99. }
  100. addToArray(impostor.object)
  101. let checkWithEpsilon = (value: number): number => {
  102. return Math.max(value, PhysicsEngine.Epsilon);
  103. }
  104. let globalQuaternion: Quaternion = new Quaternion();
  105. impostors.forEach((i) => {
  106. if (!i.object.rotationQuaternion) {
  107. return;
  108. }
  109. //get the correct bounding box
  110. var oldQuaternion = i.object.rotationQuaternion;
  111. globalQuaternion = oldQuaternion.clone();
  112. var rot = oldQuaternion.toEulerAngles();
  113. var extendSize = i.getObjectExtendSize();
  114. const radToDeg = 57.295779513082320876;
  115. if (i === impostor) {
  116. var center = impostor.getObjectCenter();
  117. impostor.object.getAbsolutePivotPoint().subtractToRef(center, this._tmpPositionVector);
  118. this._tmpPositionVector.divideInPlace(impostor.object.scaling);
  119. //Can also use Array.prototype.push.apply
  120. bodyConfig.pos.push(center.x);
  121. bodyConfig.pos.push(center.y);
  122. bodyConfig.pos.push(center.z);
  123. bodyConfig.posShape.push(0, 0, 0);
  124. //tmp solution
  125. bodyConfig.rot.push(0);
  126. bodyConfig.rot.push(0);
  127. bodyConfig.rot.push(0);
  128. bodyConfig.rotShape.push(0, 0, 0);
  129. } else {
  130. let localPosition = i.object.getAbsolutePosition().subtract(impostor.object.getAbsolutePosition());
  131. bodyConfig.posShape.push(localPosition.x);
  132. bodyConfig.posShape.push(localPosition.y);
  133. bodyConfig.posShape.push(localPosition.z);
  134. bodyConfig.pos.push(0, 0, 0);
  135. //tmp solution until https://github.com/lo-th/OIMO.js/pull/37 is merged
  136. bodyConfig.rot.push(0);
  137. bodyConfig.rot.push(0);
  138. bodyConfig.rot.push(0);
  139. bodyConfig.rotShape.push(rot.x * radToDeg);
  140. bodyConfig.rotShape.push(rot.y * radToDeg);
  141. bodyConfig.rotShape.push(rot.z * radToDeg);
  142. }
  143. // register mesh
  144. switch (i.type) {
  145. case PhysicsImpostor.ParticleImpostor:
  146. Tools.Warn("No Particle support in OIMO.js. using SphereImpostor instead");
  147. case PhysicsImpostor.SphereImpostor:
  148. var radiusX = extendSize.x;
  149. var radiusY = extendSize.y;
  150. var radiusZ = extendSize.z;
  151. var size = Math.max(
  152. checkWithEpsilon(radiusX),
  153. checkWithEpsilon(radiusY),
  154. checkWithEpsilon(radiusZ)) / 2;
  155. bodyConfig.type.push('sphere');
  156. //due to the way oimo works with compounds, add 3 times
  157. bodyConfig.size.push(size);
  158. bodyConfig.size.push(size);
  159. bodyConfig.size.push(size);
  160. break;
  161. case PhysicsImpostor.CylinderImpostor:
  162. var sizeX = checkWithEpsilon(extendSize.x) / 2;
  163. var sizeY = checkWithEpsilon(extendSize.y);
  164. bodyConfig.type.push('cylinder');
  165. bodyConfig.size.push(sizeX);
  166. bodyConfig.size.push(sizeY);
  167. //due to the way oimo works with compounds, add one more value.
  168. bodyConfig.size.push(sizeY);
  169. break;
  170. case PhysicsImpostor.PlaneImpostor:
  171. case PhysicsImpostor.BoxImpostor:
  172. default:
  173. var sizeX = checkWithEpsilon(extendSize.x);
  174. var sizeY = checkWithEpsilon(extendSize.y);
  175. var sizeZ = checkWithEpsilon(extendSize.z);
  176. bodyConfig.type.push('box');
  177. //if (i === impostor) {
  178. bodyConfig.size.push(sizeX);
  179. bodyConfig.size.push(sizeY);
  180. bodyConfig.size.push(sizeZ);
  181. //} else {
  182. // bodyConfig.size.push(0,0,0);
  183. //}
  184. break;
  185. }
  186. //actually not needed, but hey...
  187. i.object.rotationQuaternion = oldQuaternion;
  188. });
  189. impostor.physicsBody = this.world.add(bodyConfig);
  190. // set the quaternion, ignoring the previously defined (euler) rotation
  191. impostor.physicsBody.resetQuaternion(globalQuaternion);
  192. // update with delta 0, so the body will reveive the new rotation.
  193. impostor.physicsBody.updatePosition(0);
  194. } else {
  195. this._tmpPositionVector.copyFromFloats(0, 0, 0);
  196. }
  197. impostor.setDeltaPosition(this._tmpPositionVector);
  198. //this._tmpPositionVector.addInPlace(impostor.mesh.getBoundingInfo().boundingBox.center);
  199. //this.setPhysicsBodyTransformation(impostor, this._tmpPositionVector, impostor.mesh.rotationQuaternion);
  200. }
  201. private _tmpPositionVector: Vector3 = Vector3.Zero();
  202. public removePhysicsBody(impostor: PhysicsImpostor) {
  203. //impostor.physicsBody.dispose();
  204. //Same as : (older oimo versions)
  205. this.world.removeRigidBody(impostor.physicsBody);
  206. }
  207. public generateJoint(impostorJoint: PhysicsImpostorJoint) {
  208. var mainBody = impostorJoint.mainImpostor.physicsBody;
  209. var connectedBody = impostorJoint.connectedImpostor.physicsBody;
  210. if (!mainBody || !connectedBody) {
  211. return;
  212. }
  213. var jointData = impostorJoint.joint.jointData;
  214. var options = jointData.nativeParams || {};
  215. var type;
  216. var nativeJointData: any = {
  217. body1: mainBody,
  218. body2: connectedBody,
  219. axe1: options.axe1 || (jointData.mainAxis ? jointData.mainAxis.asArray() : null),
  220. axe2: options.axe2 || (jointData.connectedAxis ? jointData.connectedAxis.asArray() : null),
  221. pos1: options.pos1 || (jointData.mainPivot ? jointData.mainPivot.asArray() : null),
  222. pos2: options.pos2 || (jointData.connectedPivot ? jointData.connectedPivot.asArray() : null),
  223. min: options.min,
  224. max: options.max,
  225. collision: options.collision || jointData.collision,
  226. spring: options.spring,
  227. //supporting older version of Oimo
  228. world: this.world
  229. }
  230. switch (impostorJoint.joint.type) {
  231. case PhysicsJoint.BallAndSocketJoint:
  232. type = "jointBall";
  233. break;
  234. case PhysicsJoint.SpringJoint:
  235. Tools.Warn("OIMO.js doesn't support Spring Constraint. Simulating using DistanceJoint instead");
  236. var springData = <SpringJointData>jointData;
  237. nativeJointData.min = springData.length || nativeJointData.min;
  238. //Max should also be set, just make sure it is at least min
  239. nativeJointData.max = Math.max(nativeJointData.min, nativeJointData.max);
  240. case PhysicsJoint.DistanceJoint:
  241. type = "jointDistance";
  242. nativeJointData.max = (<DistanceJointData>jointData).maxDistance
  243. break;
  244. case PhysicsJoint.PrismaticJoint:
  245. type = "jointPrisme";
  246. break;
  247. case PhysicsJoint.SliderJoint:
  248. type = "jointSlide";
  249. break;
  250. case PhysicsJoint.WheelJoint:
  251. type = "jointWheel";
  252. break;
  253. case PhysicsJoint.HingeJoint:
  254. default:
  255. type = "jointHinge";
  256. break;
  257. }
  258. nativeJointData.type = type;
  259. impostorJoint.joint.physicsJoint = this.world.add(nativeJointData);
  260. }
  261. public removeJoint(impostorJoint: PhysicsImpostorJoint) {
  262. //Bug in Oimo prevents us from disposing a joint in the playground
  263. //joint.joint.physicsJoint.dispose();
  264. //So we will bruteforce it!
  265. try {
  266. this.world.removeJoint(impostorJoint.joint.physicsJoint);
  267. } catch (e) {
  268. Tools.Warn(e);
  269. }
  270. }
  271. public isSupported(): boolean {
  272. return this.BJSOIMO !== undefined;
  273. }
  274. public setTransformationFromPhysicsBody(impostor: PhysicsImpostor) {
  275. if (!impostor.physicsBody.sleeping) {
  276. //TODO check that
  277. /*if (impostor.physicsBody.shapes.next) {
  278. var parentShape = this._getLastShape(impostor.physicsBody);
  279. impostor.object.position.copyFrom(parentShape.position);
  280. console.log(parentShape.position);
  281. } else {*/
  282. impostor.object.position.copyFrom(impostor.physicsBody.getPosition());
  283. //}
  284. if (impostor.object.rotationQuaternion) {
  285. impostor.object.rotationQuaternion.copyFrom(impostor.physicsBody.getQuaternion());
  286. }
  287. }
  288. }
  289. public setPhysicsBodyTransformation(impostor: PhysicsImpostor, newPosition: Vector3, newRotation: Quaternion) {
  290. var body = impostor.physicsBody;
  291. body.position.copy(newPosition);
  292. body.orientation.copy(newRotation);
  293. body.syncShapes();
  294. body.awake();
  295. }
  296. /*private _getLastShape(body: any): any {
  297. var lastShape = body.shapes;
  298. while (lastShape.next) {
  299. lastShape = lastShape.next;
  300. }
  301. return lastShape;
  302. }*/
  303. public setLinearVelocity(impostor: PhysicsImpostor, velocity: Vector3) {
  304. impostor.physicsBody.linearVelocity.copy(velocity);
  305. }
  306. public setAngularVelocity(impostor: PhysicsImpostor, velocity: Vector3) {
  307. impostor.physicsBody.angularVelocity.copy(velocity);
  308. }
  309. public getLinearVelocity(impostor: PhysicsImpostor): Nullable<Vector3> {
  310. var v = impostor.physicsBody.linearVelocity;
  311. if (!v) {
  312. return null;
  313. }
  314. return new Vector3(v.x, v.y, v.z)
  315. }
  316. public getAngularVelocity(impostor: PhysicsImpostor): Nullable<Vector3> {
  317. var v = impostor.physicsBody.angularVelocity;
  318. if (!v) {
  319. return null;
  320. }
  321. return new Vector3(v.x, v.y, v.z)
  322. }
  323. public setBodyMass(impostor: PhysicsImpostor, mass: number) {
  324. var staticBody: boolean = mass === 0;
  325. //this will actually set the body's density and not its mass.
  326. //But this is how oimo treats the mass variable.
  327. impostor.physicsBody.shapes.density = staticBody ? 1 : mass;
  328. impostor.physicsBody.setupMass(staticBody ? 0x2 : 0x1);
  329. }
  330. public getBodyMass(impostor: PhysicsImpostor): number {
  331. return impostor.physicsBody.shapes.density;
  332. }
  333. public getBodyFriction(impostor: PhysicsImpostor): number {
  334. return impostor.physicsBody.shapes.friction;
  335. }
  336. public setBodyFriction(impostor: PhysicsImpostor, friction: number) {
  337. impostor.physicsBody.shapes.friction = friction;
  338. }
  339. public getBodyRestitution(impostor: PhysicsImpostor): number {
  340. return impostor.physicsBody.shapes.restitution;
  341. }
  342. public setBodyRestitution(impostor: PhysicsImpostor, restitution: number) {
  343. impostor.physicsBody.shapes.restitution = restitution;
  344. }
  345. public sleepBody(impostor: PhysicsImpostor) {
  346. impostor.physicsBody.sleep();
  347. }
  348. public wakeUpBody(impostor: PhysicsImpostor) {
  349. impostor.physicsBody.awake();
  350. }
  351. public updateDistanceJoint(joint: PhysicsJoint, maxDistance: number, minDistance?: number) {
  352. joint.physicsJoint.limitMotor.upperLimit = maxDistance;
  353. if (minDistance !== void 0) {
  354. joint.physicsJoint.limitMotor.lowerLimit = minDistance;
  355. }
  356. }
  357. public setMotor(joint: IMotorEnabledJoint, speed: number, maxForce?: number, motorIndex?: number) {
  358. //TODO separate rotational and transational motors.
  359. var motor = motorIndex ? joint.physicsJoint.rotationalLimitMotor2 : joint.physicsJoint.rotationalLimitMotor1 || joint.physicsJoint.rotationalLimitMotor || joint.physicsJoint.limitMotor;
  360. if (motor) {
  361. motor.setMotor(speed, maxForce);
  362. }
  363. }
  364. public setLimit(joint: IMotorEnabledJoint, upperLimit: number, lowerLimit?: number, motorIndex?: number) {
  365. //TODO separate rotational and transational motors.
  366. var motor = motorIndex ? joint.physicsJoint.rotationalLimitMotor2 : joint.physicsJoint.rotationalLimitMotor1 || joint.physicsJoint.rotationalLimitMotor || joint.physicsJoint.limitMotor;
  367. if (motor) {
  368. motor.setLimit(upperLimit, lowerLimit === void 0 ? -upperLimit : lowerLimit);
  369. }
  370. }
  371. public syncMeshWithImpostor(mesh: AbstractMesh, impostor: PhysicsImpostor) {
  372. var body = impostor.physicsBody;
  373. mesh.position.x = body.position.x;
  374. mesh.position.y = body.position.y;
  375. mesh.position.z = body.position.z;
  376. if (mesh.rotationQuaternion) {
  377. mesh.rotationQuaternion.x = body.orientation.x;
  378. mesh.rotationQuaternion.y = body.orientation.y;
  379. mesh.rotationQuaternion.z = body.orientation.z;
  380. mesh.rotationQuaternion.w = body.orientation.s;
  381. }
  382. }
  383. public getRadius(impostor: PhysicsImpostor): number {
  384. return impostor.physicsBody.shapes.radius;
  385. }
  386. public getBoxSizeToRef(impostor: PhysicsImpostor, result: Vector3): void {
  387. var shape = impostor.physicsBody.shapes;
  388. result.x = shape.halfWidth * 2;
  389. result.y = shape.halfHeight * 2;
  390. result.z = shape.halfDepth * 2;
  391. }
  392. public dispose() {
  393. this.world.clear();
  394. }
  395. }
  396. }