babylon.oimoJSPlugin.ts 19 KB

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