babylon.oimoJSPlugin.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. var BABYLON;
  2. (function (BABYLON) {
  3. var OimoJSPlugin = (function () {
  4. function OimoJSPlugin() {
  5. this._registeredMeshes = [];
  6. /**
  7. * Update the body position according to the mesh position
  8. * @param mesh
  9. */
  10. this.updateBodyPosition = function (mesh) {
  11. for (var index = 0; index < this._registeredMeshes.length; index++) {
  12. var registeredMesh = this._registeredMeshes[index];
  13. if (registeredMesh.mesh === mesh || registeredMesh.mesh === mesh.parent) {
  14. var body = registeredMesh.body.body;
  15. mesh.computeWorldMatrix(true);
  16. var center = mesh.getBoundingInfo().boundingBox.center;
  17. body.setPosition(new OIMO.Vec3(center.x, center.y, center.z));
  18. body.setRotation(new OIMO.Vec3(mesh.rotation.x, mesh.rotation.y, mesh.rotation.z));
  19. body.sleeping = false;
  20. return;
  21. }
  22. // Case where the parent has been updated
  23. if (registeredMesh.mesh.parent === mesh) {
  24. mesh.computeWorldMatrix(true);
  25. registeredMesh.mesh.computeWorldMatrix(true);
  26. var absolutePosition = registeredMesh.mesh.getAbsolutePosition();
  27. var absoluteRotation = mesh.rotation;
  28. body = registeredMesh.body.body;
  29. body.setPosition(new OIMO.Vec3(absolutePosition.x, absolutePosition.y, absolutePosition.z));
  30. body.setRotation(new OIMO.Vec3(absoluteRotation.x, absoluteRotation.y, absoluteRotation.z));
  31. body.sleeping = false;
  32. return;
  33. }
  34. }
  35. };
  36. }
  37. OimoJSPlugin.prototype._checkWithEpsilon = function (value) {
  38. return value < BABYLON.PhysicsEngine.Epsilon ? BABYLON.PhysicsEngine.Epsilon : value;
  39. };
  40. OimoJSPlugin.prototype.initialize = function (iterations) {
  41. this._world = new OIMO.World();
  42. this._world.clear();
  43. };
  44. OimoJSPlugin.prototype.setGravity = function (gravity) {
  45. this._world.gravity = gravity;
  46. };
  47. OimoJSPlugin.prototype.registerMesh = function (mesh, impostor, options) {
  48. var body = null;
  49. this.unregisterMesh(mesh);
  50. mesh.computeWorldMatrix(true);
  51. var initialRotation = null;
  52. if (mesh.rotationQuaternion) {
  53. initialRotation = mesh.rotationQuaternion.clone();
  54. mesh.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 1);
  55. mesh.computeWorldMatrix(true);
  56. }
  57. var bbox = mesh.getBoundingInfo().boundingBox;
  58. // The delta between the mesh position and the mesh bounding box center
  59. var deltaPosition = mesh.position.subtract(bbox.center);
  60. // Transform delta position with the rotation
  61. if (initialRotation) {
  62. var m = new BABYLON.Matrix();
  63. initialRotation.toRotationMatrix(m);
  64. deltaPosition = BABYLON.Vector3.TransformCoordinates(deltaPosition, m);
  65. }
  66. // register mesh
  67. switch (impostor) {
  68. case BABYLON.PhysicsEngine.SphereImpostor:
  69. var radiusX = bbox.maximumWorld.x - bbox.minimumWorld.x;
  70. var radiusY = bbox.maximumWorld.y - bbox.minimumWorld.y;
  71. var radiusZ = bbox.maximumWorld.z - bbox.minimumWorld.z;
  72. var size = Math.max(this._checkWithEpsilon(radiusX), this._checkWithEpsilon(radiusY), this._checkWithEpsilon(radiusZ)) / 2;
  73. body = new OIMO.Body({
  74. type: 'sphere',
  75. size: [size],
  76. pos: [bbox.center.x, bbox.center.y, bbox.center.z],
  77. rot: [mesh.rotation.x / OIMO.TO_RAD, mesh.rotation.y / OIMO.TO_RAD, mesh.rotation.z / OIMO.TO_RAD],
  78. move: options.mass != 0,
  79. config: [options.mass, options.friction, options.restitution],
  80. world: this._world
  81. });
  82. break;
  83. case BABYLON.PhysicsEngine.PlaneImpostor:
  84. //Oimo "fakes" a cylinder as a box, so why don't we!
  85. case BABYLON.PhysicsEngine.CylinderImpostor:
  86. case BABYLON.PhysicsEngine.BoxImpostor:
  87. var min = bbox.minimumWorld;
  88. var max = bbox.maximumWorld;
  89. var box = max.subtract(min);
  90. var sizeX = this._checkWithEpsilon(box.x);
  91. var sizeY = this._checkWithEpsilon(box.y);
  92. var sizeZ = this._checkWithEpsilon(box.z);
  93. body = new OIMO.Body({
  94. type: 'box',
  95. size: [sizeX, sizeY, sizeZ],
  96. pos: [bbox.center.x, bbox.center.y, bbox.center.z],
  97. rot: [mesh.rotation.x / OIMO.TO_RAD, mesh.rotation.y / OIMO.TO_RAD, mesh.rotation.z / OIMO.TO_RAD],
  98. move: options.mass != 0,
  99. config: [options.mass, options.friction, options.restitution],
  100. world: this._world
  101. });
  102. break;
  103. }
  104. //If quaternion was set as the rotation of the object
  105. if (initialRotation) {
  106. //We have to access the rigid body's properties to set the quaternion.
  107. //The setQuaternion function of Oimo only sets the newOrientation that is only set after an impulse is given or a collision.
  108. body.body.orientation = new OIMO.Quat(initialRotation.w, initialRotation.x, initialRotation.y, initialRotation.z);
  109. //update the internal rotation matrix
  110. body.body.syncShapes();
  111. }
  112. this._registeredMeshes.push({
  113. mesh: mesh,
  114. body: body,
  115. delta: deltaPosition
  116. });
  117. return body;
  118. };
  119. OimoJSPlugin.prototype.registerMeshesAsCompound = function (parts, options) {
  120. var types = [], sizes = [], positions = [], rotations = [];
  121. var initialMesh = parts[0].mesh;
  122. for (var index = 0; index < parts.length; index++) {
  123. var part = parts[index];
  124. var bodyParameters = this._createBodyAsCompound(part, options, initialMesh);
  125. types.push(bodyParameters.type);
  126. sizes.push.apply(sizes, bodyParameters.size);
  127. positions.push.apply(positions, bodyParameters.pos);
  128. rotations.push.apply(rotations, bodyParameters.rot);
  129. }
  130. var body = new OIMO.Body({
  131. type: types,
  132. size: sizes,
  133. pos: positions,
  134. rot: rotations,
  135. move: options.mass != 0,
  136. config: [options.mass, options.friction, options.restitution],
  137. world: this._world
  138. });
  139. this._registeredMeshes.push({
  140. mesh: initialMesh,
  141. body: body
  142. });
  143. return body;
  144. };
  145. OimoJSPlugin.prototype._createBodyAsCompound = function (part, options, initialMesh) {
  146. var bodyParameters = null;
  147. var mesh = part.mesh;
  148. // We need the bounding box/sphere info to compute the physics body
  149. mesh.computeWorldMatrix();
  150. switch (part.impostor) {
  151. case BABYLON.PhysicsEngine.SphereImpostor:
  152. var bbox = mesh.getBoundingInfo().boundingBox;
  153. var radiusX = bbox.maximumWorld.x - bbox.minimumWorld.x;
  154. var radiusY = bbox.maximumWorld.y - bbox.minimumWorld.y;
  155. var radiusZ = bbox.maximumWorld.z - bbox.minimumWorld.z;
  156. var size = Math.max(this._checkWithEpsilon(radiusX), this._checkWithEpsilon(radiusY), this._checkWithEpsilon(radiusZ)) / 2;
  157. bodyParameters = {
  158. type: 'sphere',
  159. /* bug with oimo : sphere needs 3 sizes in this case */
  160. size: [size, -1, -1],
  161. pos: [mesh.position.x, mesh.position.y, mesh.position.z],
  162. rot: [mesh.rotation.x / OIMO.TO_RAD, mesh.rotation.y / OIMO.TO_RAD, mesh.rotation.z / OIMO.TO_RAD]
  163. };
  164. break;
  165. case BABYLON.PhysicsEngine.PlaneImpostor:
  166. case BABYLON.PhysicsEngine.BoxImpostor:
  167. bbox = mesh.getBoundingInfo().boundingBox;
  168. var min = bbox.minimumWorld;
  169. var max = bbox.maximumWorld;
  170. var box = max.subtract(min);
  171. var sizeX = this._checkWithEpsilon(box.x);
  172. var sizeY = this._checkWithEpsilon(box.y);
  173. var sizeZ = this._checkWithEpsilon(box.z);
  174. var relativePosition = mesh.position;
  175. bodyParameters = {
  176. type: 'box',
  177. size: [sizeX, sizeY, sizeZ],
  178. pos: [relativePosition.x, relativePosition.y, relativePosition.z],
  179. rot: [mesh.rotation.x / OIMO.TO_RAD, mesh.rotation.y / OIMO.TO_RAD, mesh.rotation.z / OIMO.TO_RAD]
  180. };
  181. break;
  182. }
  183. return bodyParameters;
  184. };
  185. OimoJSPlugin.prototype.unregisterMesh = function (mesh) {
  186. for (var index = 0; index < this._registeredMeshes.length; index++) {
  187. var registeredMesh = this._registeredMeshes[index];
  188. if (registeredMesh.mesh === mesh || registeredMesh.mesh === mesh.parent) {
  189. if (registeredMesh.body) {
  190. this._world.removeRigidBody(registeredMesh.body.body);
  191. this._unbindBody(registeredMesh.body);
  192. }
  193. this._registeredMeshes.splice(index, 1);
  194. return;
  195. }
  196. }
  197. };
  198. OimoJSPlugin.prototype._unbindBody = function (body) {
  199. for (var index = 0; index < this._registeredMeshes.length; index++) {
  200. var registeredMesh = this._registeredMeshes[index];
  201. if (registeredMesh.body === body) {
  202. registeredMesh.body = null;
  203. }
  204. }
  205. };
  206. OimoJSPlugin.prototype.applyImpulse = function (mesh, force, contactPoint) {
  207. for (var index = 0; index < this._registeredMeshes.length; index++) {
  208. var registeredMesh = this._registeredMeshes[index];
  209. if (registeredMesh.mesh === mesh || registeredMesh.mesh === mesh.parent) {
  210. // Get object mass to have a behaviour similar to cannon.js
  211. var mass = registeredMesh.body.body.massInfo.mass;
  212. // The force is scaled with the mass of object
  213. registeredMesh.body.body.applyImpulse(contactPoint.scale(OIMO.INV_SCALE), force.scale(OIMO.INV_SCALE * mass));
  214. return;
  215. }
  216. }
  217. };
  218. OimoJSPlugin.prototype.createLink = function (mesh1, mesh2, pivot1, pivot2, options) {
  219. var body1 = null, body2 = null;
  220. for (var index = 0; index < this._registeredMeshes.length; index++) {
  221. var registeredMesh = this._registeredMeshes[index];
  222. if (registeredMesh.mesh === mesh1) {
  223. body1 = registeredMesh.body.body;
  224. }
  225. else if (registeredMesh.mesh === mesh2) {
  226. body2 = registeredMesh.body.body;
  227. }
  228. }
  229. if (!body1 || !body2) {
  230. return false;
  231. }
  232. if (!options) {
  233. options = {};
  234. }
  235. new OIMO.Link({
  236. type: options.type,
  237. body1: body1,
  238. body2: body2,
  239. min: options.min,
  240. max: options.max,
  241. axe1: options.axe1,
  242. axe2: options.axe2,
  243. pos1: [pivot1.x, pivot1.y, pivot1.z],
  244. pos2: [pivot2.x, pivot2.y, pivot2.z],
  245. collision: options.collision,
  246. spring: options.spring,
  247. world: this._world
  248. });
  249. return true;
  250. };
  251. OimoJSPlugin.prototype.dispose = function () {
  252. this._world.clear();
  253. while (this._registeredMeshes.length) {
  254. this.unregisterMesh(this._registeredMeshes[0].mesh);
  255. }
  256. };
  257. OimoJSPlugin.prototype.isSupported = function () {
  258. return OIMO !== undefined;
  259. };
  260. OimoJSPlugin.prototype._getLastShape = function (body) {
  261. var lastShape = body.shapes;
  262. while (lastShape.next) {
  263. lastShape = lastShape.next;
  264. }
  265. return lastShape;
  266. };
  267. OimoJSPlugin.prototype.runOneStep = function (time) {
  268. this._world.step();
  269. // Update the position of all registered meshes
  270. var i = this._registeredMeshes.length;
  271. var m;
  272. while (i--) {
  273. var body = this._registeredMeshes[i].body.body;
  274. var mesh = this._registeredMeshes[i].mesh;
  275. var delta = this._registeredMeshes[i].delta;
  276. if (!body.sleeping) {
  277. if (body.shapes.next) {
  278. var parentShape = this._getLastShape(body);
  279. mesh.position.x = parentShape.position.x * OIMO.WORLD_SCALE;
  280. mesh.position.y = parentShape.position.y * OIMO.WORLD_SCALE;
  281. mesh.position.z = parentShape.position.z * OIMO.WORLD_SCALE;
  282. var mtx = BABYLON.Matrix.FromArray(body.getMatrix());
  283. if (!mesh.rotationQuaternion) {
  284. mesh.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 1);
  285. }
  286. mesh.rotationQuaternion.fromRotationMatrix(mtx);
  287. mesh.computeWorldMatrix();
  288. }
  289. else {
  290. m = body.getMatrix();
  291. mtx = BABYLON.Matrix.FromArray(m);
  292. // Body position
  293. var bodyX = mtx.m[12], bodyY = mtx.m[13], bodyZ = mtx.m[14];
  294. if (!delta) {
  295. mesh.position.x = bodyX;
  296. mesh.position.y = bodyY;
  297. mesh.position.z = bodyZ;
  298. }
  299. else {
  300. mesh.position.x = bodyX + delta.x;
  301. mesh.position.y = bodyY + delta.y;
  302. mesh.position.z = bodyZ + delta.z;
  303. }
  304. if (!mesh.rotationQuaternion) {
  305. mesh.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 1);
  306. }
  307. BABYLON.Quaternion.FromRotationMatrixToRef(mtx, mesh.rotationQuaternion);
  308. mesh.computeWorldMatrix();
  309. }
  310. }
  311. }
  312. };
  313. return OimoJSPlugin;
  314. })();
  315. BABYLON.OimoJSPlugin = OimoJSPlugin;
  316. })(BABYLON || (BABYLON = {}));