瀏覽代碼

Oimo plugin

new oimo plugin supports the new architecture
Raanan Weber 9 年之前
父節點
當前提交
fdd09f0ab7

+ 3 - 9
src/Physics/Plugins/babylon.cannonJSPlugin.js

@@ -626,6 +626,9 @@ var BABYLON;
         CannonJSPlugin.prototype.generateJoint = function (impostorJoint) {
             var mainBody = impostorJoint.mainImpostor.physicsBody;
             var connectedBody = impostorJoint.connectedImpostor.physicsBody;
+            if (!mainBody || !connectedBody) {
+                return;
+            }
             var constraint;
             var jointData = impostorJoint.joint.jointData;
             var constraintData = {
@@ -820,15 +823,6 @@ var BABYLON;
         CannonJSPlugin.prototype.isSupported = function () {
             return window.CANNON !== undefined;
         };
-        //TODO 
-        CannonJSPlugin.prototype.supports = function (feature) {
-            switch (feature) {
-                case BABYLON.PhysicsFeature.PIVOT_IN_JOINT:
-                    return false;
-                case BABYLON.PhysicsFeature.TRIMESH:
-                    return true;
-            }
-        };
         CannonJSPlugin.prototype.dispose = function () {
             //nothing to do, actually.
         };

+ 3 - 1
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -638,7 +638,9 @@
         public generateJoint(impostorJoint: PhysicsImpostorJoint) {
             var mainBody = impostorJoint.mainImpostor.physicsBody;
             var connectedBody = impostorJoint.connectedImpostor.physicsBody;
-
+            if (!mainBody || !connectedBody) {
+                return;
+            }
             var constraint;
             var jointData = impostorJoint.joint.jointData;
             var constraintData = {

+ 195 - 289
src/Physics/Plugins/babylon.oimoJSPlugin.js

@@ -1,279 +1,225 @@
 var BABYLON;
 (function (BABYLON) {
     var OimoJSPlugin = (function () {
-        function OimoJSPlugin() {
-            this._registeredMeshes = [];
-            this.name = "oimo";
-            /**
-             * Update the body position according to the mesh position
-             * @param mesh
-             */
-            this.updateBodyPosition = function (mesh) {
-                for (var index = 0; index < this._registeredMeshes.length; index++) {
-                    var registeredMesh = this._registeredMeshes[index];
-                    var body = registeredMesh.body.body;
-                    var updated = false;
-                    var newPosition;
-                    if (registeredMesh.mesh === mesh || registeredMesh.mesh === mesh.parent) {
-                        mesh.computeWorldMatrix(true);
-                        newPosition = mesh.getBoundingInfo().boundingBox.center;
-                        updated = true;
-                    }
-                    else if (registeredMesh.mesh.parent === mesh) {
-                        mesh.computeWorldMatrix(true);
-                        registeredMesh.mesh.computeWorldMatrix(true);
-                        newPosition = registeredMesh.mesh.getAbsolutePosition();
-                        updated = true;
-                    }
-                    if (updated) {
-                        body.setPosition(new OIMO.Vec3(newPosition.x, newPosition.y, newPosition.z));
-                        body.setQuaternion(mesh.rotationQuaternion);
-                        body.sleeping = false;
-                        //force Oimo to update the body's position
-                        body.updatePosition(1);
-                    }
-                }
-            };
+        function OimoJSPlugin(iterations) {
+            this.name = "OimoJSPlugin";
+            this._tmpImpostorsArray = [];
+            this._tmpPositionVector = BABYLON.Vector3.Zero();
+            this.world = new OIMO.World(null, null, iterations);
+            this.world.clear();
         }
-        OimoJSPlugin.prototype._checkWithEpsilon = function (value) {
-            return value < BABYLON.PhysicsEngine.Epsilon ? BABYLON.PhysicsEngine.Epsilon : value;
-        };
-        OimoJSPlugin.prototype.initialize = function (iterations) {
-            this._world = new OIMO.World(null, null, iterations);
-            this._world.clear();
-        };
         OimoJSPlugin.prototype.setGravity = function (gravity) {
-            this._gravity = this._world.gravity = gravity;
-        };
-        OimoJSPlugin.prototype.getGravity = function () {
-            return this._gravity;
-        };
-        OimoJSPlugin.prototype.registerMesh = function (mesh, impostor, options) {
-            this.unregisterMesh(mesh);
-            if (!mesh.rotationQuaternion) {
-                mesh.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(mesh.rotation.y, mesh.rotation.x, mesh.rotation.z);
-            }
-            mesh.computeWorldMatrix(true);
-            var bbox = mesh.getBoundingInfo().boundingBox;
-            // The delta between the mesh position and the mesh bounding box center
-            var deltaPosition = mesh.position.subtract(bbox.center);
-            //calculate rotation to fit Oimo's needs (Euler...)
-            var rot = new OIMO.Euler().setFromQuaternion({ x: mesh.rotationQuaternion.x, y: mesh.rotationQuaternion.y, z: mesh.rotationQuaternion.z, s: mesh.rotationQuaternion.w });
-            //get the correct bounding box
-            var oldQuaternion = mesh.rotationQuaternion;
-            mesh.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 1);
-            mesh.computeWorldMatrix(true);
-            var bodyConfig = {
-                name: mesh.uniqueId,
-                pos: [bbox.center.x, bbox.center.y, bbox.center.z],
-                rot: [rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD],
-                move: options.mass != 0,
-                config: [options.mass, options.friction, options.restitution],
-                world: this._world
-            };
-            // register mesh
-            switch (impostor) {
-                case BABYLON.PhysicsEngine.SphereImpostor:
-                    var radiusX = bbox.maximumWorld.x - bbox.minimumWorld.x;
-                    var radiusY = bbox.maximumWorld.y - bbox.minimumWorld.y;
-                    var radiusZ = bbox.maximumWorld.z - bbox.minimumWorld.z;
-                    var size = Math.max(this._checkWithEpsilon(radiusX), this._checkWithEpsilon(radiusY), this._checkWithEpsilon(radiusZ)) / 2;
-                    bodyConfig.type = 'sphere';
-                    bodyConfig.size = [size];
-                    break;
-                case BABYLON.PhysicsEngine.PlaneImpostor:
-                //Oimo "fakes" a cylinder as a box, so why don't we!
-                case BABYLON.PhysicsEngine.CylinderImpostor:
-                case BABYLON.PhysicsEngine.BoxImpostor:
-                    var min = bbox.minimumWorld;
-                    var max = bbox.maximumWorld;
-                    var box = max.subtract(min);
-                    var sizeX = this._checkWithEpsilon(box.x);
-                    var sizeY = this._checkWithEpsilon(box.y);
-                    var sizeZ = this._checkWithEpsilon(box.z);
-                    bodyConfig.type = 'box';
-                    bodyConfig.size = [sizeX, sizeY, sizeZ];
-                    break;
-            }
-            var body = new OIMO.Body(bodyConfig);
-            //We have to access the rigid body's properties to set the quaternion. 
-            //The setQuaternion function of Oimo only sets the newOrientation that is only set after an impulse is given or a collision.
-            //body.body.orientation = new OIMO.Quat(mesh.rotationQuaternion.w, mesh.rotationQuaternion.x, mesh.rotationQuaternion.y, mesh.rotationQuaternion.z);
-            //TEST
-            //body.body.resetQuaternion(new OIMO.Quat(mesh.rotationQuaternion.w, mesh.rotationQuaternion.x, mesh.rotationQuaternion.y, mesh.rotationQuaternion.z));
-            //update the internal rotation matrix
-            //body.body.syncShapes();
-            this._registeredMeshes.push({
-                mesh: mesh,
-                body: body,
-                delta: deltaPosition
-            });
-            //for the sake of consistency.
-            mesh.rotationQuaternion = oldQuaternion;
-            return body;
+            this.world.gravity.copy(gravity);
         };
-        OimoJSPlugin.prototype.registerMeshesAsCompound = function (parts, options) {
-            var types = [], sizes = [], positions = [], rotations = [];
-            var initialMesh = parts[0].mesh;
-            for (var index = 0; index < parts.length; index++) {
-                var part = parts[index];
-                var bodyParameters = this._createBodyAsCompound(part, options, initialMesh);
-                types.push(bodyParameters.type);
-                sizes.push.apply(sizes, bodyParameters.size);
-                positions.push.apply(positions, bodyParameters.pos);
-                rotations.push.apply(rotations, bodyParameters.rot);
-            }
-            var body = new OIMO.Body({
-                name: initialMesh.uniqueId,
-                type: types,
-                size: sizes,
-                pos: positions,
-                rot: rotations,
-                move: options.mass != 0,
-                config: [options.mass, options.friction, options.restitution],
-                world: this._world
+        OimoJSPlugin.prototype.executeStep = function (delta, impostors) {
+            impostors.forEach(function (impostor) {
+                impostor.beforeStep();
             });
-            //Reset the body's rotation to be of the initial mesh's.
-            var rot = new OIMO.Euler().setFromQuaternion({ x: initialMesh.rotationQuaternion.x, y: initialMesh.rotationQuaternion.y, z: initialMesh.rotationQuaternion.z, s: initialMesh.rotationQuaternion.w });
-            body.resetRotation(rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD);
-            this._registeredMeshes.push({
-                mesh: initialMesh,
-                body: body
+            this.world.step();
+            impostors.forEach(function (impostor) {
+                impostor.afterStep();
+                //update the ordered impostors array
+                this._tmpImpostorsArray[impostor.mesh.uniqueId] = impostor;
             });
-            return body;
-        };
-        OimoJSPlugin.prototype._createBodyAsCompound = function (part, options, initialMesh) {
-            var mesh = part.mesh;
-            if (!mesh.rotationQuaternion) {
-                mesh.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(mesh.rotation.y, mesh.rotation.x, mesh.rotation.z);
-            }
-            // We need the bounding box/sphere info to compute the physics body
-            mesh.computeWorldMatrix(true);
-            var rot = new OIMO.Euler().setFromQuaternion({ x: mesh.rotationQuaternion.x, y: mesh.rotationQuaternion.y, z: mesh.rotationQuaternion.z, s: mesh.rotationQuaternion.w });
-            var bodyParameters = {
-                name: mesh.uniqueId,
-                pos: [mesh.position.x, mesh.position.y, mesh.position.z],
-                //A bug in Oimo (Body class) prevents us from using rot directly.
-                rot: [0, 0, 0],
-                //For future reference, if the bug will ever be fixed.
-                realRot: [rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD]
-            };
-            var oldQuaternion = mesh.rotationQuaternion;
-            mesh.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 1);
-            mesh.computeWorldMatrix(true);
-            switch (part.impostor) {
-                case BABYLON.PhysicsEngine.SphereImpostor:
-                    var bbox = mesh.getBoundingInfo().boundingBox;
-                    var radiusX = bbox.maximumWorld.x - bbox.minimumWorld.x;
-                    var radiusY = bbox.maximumWorld.y - bbox.minimumWorld.y;
-                    var radiusZ = bbox.maximumWorld.z - bbox.minimumWorld.z;
-                    var size = Math.max(this._checkWithEpsilon(radiusX), this._checkWithEpsilon(radiusY), this._checkWithEpsilon(radiusZ)) / 2;
-                    bodyParameters.type = 'sphere';
-                    bodyParameters.size = [size, size, size];
-                    break;
-                case BABYLON.PhysicsEngine.PlaneImpostor:
-                case BABYLON.PhysicsEngine.CylinderImpostor:
-                case BABYLON.PhysicsEngine.BoxImpostor:
-                    bbox = mesh.getBoundingInfo().boundingBox;
-                    var min = bbox.minimumWorld;
-                    var max = bbox.maximumWorld;
-                    var box = max.subtract(min);
-                    var sizeX = this._checkWithEpsilon(box.x);
-                    var sizeY = this._checkWithEpsilon(box.y);
-                    var sizeZ = this._checkWithEpsilon(box.z);
-                    bodyParameters.type = 'box';
-                    bodyParameters.size = [sizeX, sizeY, sizeZ];
-                    break;
-            }
-            mesh.rotationQuaternion = oldQuaternion;
-            return bodyParameters;
-        };
-        OimoJSPlugin.prototype.unregisterMesh = function (mesh) {
-            for (var index = 0; index < this._registeredMeshes.length; index++) {
-                var registeredMesh = this._registeredMeshes[index];
-                if (registeredMesh.mesh === mesh || registeredMesh.mesh === mesh.parent) {
-                    if (registeredMesh.body) {
-                        this._world.removeRigidBody(registeredMesh.body.body);
-                        this._unbindBody(registeredMesh.body);
-                    }
-                    this._registeredMeshes.splice(index, 1);
-                    return;
+            //check for collisions
+            var contact = this.world.contacts;
+            while (contact !== null) {
+                if (contact.touching && !contact.body1.sleeping && !contact.body2.sleeping) {
+                    continue;
                 }
+                //is this body colliding with any other? get the impostor
+                var mainImpostor = this._tmpImpostorsArray[+contact.body1.name];
+                var collidingImpostor = this._tmpImpostorsArray[+contact.body2.name];
+                if (!mainImpostor || !collidingImpostor)
+                    continue;
+                mainImpostor.onCollide({ body: collidingImpostor.physicsBody });
+                collidingImpostor.onCollide({ body: mainImpostor.physicsBody });
+                contact = contact.next;
             }
         };
-        OimoJSPlugin.prototype._unbindBody = function (body) {
-            for (var index = 0; index < this._registeredMeshes.length; index++) {
-                var registeredMesh = this._registeredMeshes[index];
-                if (registeredMesh.body === body) {
-                    registeredMesh.body = null;
-                }
-            }
+        OimoJSPlugin.prototype.applyImpulse = function (impostor, force, contactPoint) {
+            impostor.physicsBody.body.applyImpulse(contactPoint.scale(OIMO.INV_SCALE), force.scale(OIMO.INV_SCALE));
         };
-        OimoJSPlugin.prototype.applyImpulse = function (mesh, force, contactPoint) {
-            for (var index = 0; index < this._registeredMeshes.length; index++) {
-                var registeredMesh = this._registeredMeshes[index];
-                if (registeredMesh.mesh === mesh || registeredMesh.mesh === mesh.parent) {
-                    // Get object mass to have a behaviour similar to cannon.js
-                    var mass = registeredMesh.body.body.massInfo.mass;
-                    // The force is scaled with the mass of object
-                    registeredMesh.body.body.applyImpulse(contactPoint.scale(OIMO.INV_SCALE), force.scale(OIMO.INV_SCALE * mass));
-                    return;
+        OimoJSPlugin.prototype.applyForce = function (impostor, force, contactPoint) {
+            BABYLON.Tools.Warn("Oimo doesn't support applying force. Using impule instead.");
+            this.applyImpulse(impostor, force, contactPoint);
+        };
+        OimoJSPlugin.prototype.generatePhysicsBody = function (impostor) {
+            //parent-child relationship. Does this impostor has a parent impostor?
+            if (impostor.parent) {
+                if (impostor.physicsBody) {
+                    this.removePhysicsBody(impostor);
+                    //TODO is that needed?
+                    impostor.forceUpdate();
                 }
+                return;
             }
-        };
-        OimoJSPlugin.prototype.createLink = function (mesh1, mesh2, pivot1, pivot2, options) {
-            var body1 = null, body2 = null;
-            for (var index = 0; index < this._registeredMeshes.length; index++) {
-                var registeredMesh = this._registeredMeshes[index];
-                if (registeredMesh.mesh === mesh1) {
-                    body1 = registeredMesh.body.body;
+            impostor.mesh.computeWorldMatrix(true);
+            if (impostor.isBodyInitRequired()) {
+                if (!impostor.mesh.rotationQuaternion) {
+                    impostor.mesh.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(impostor.mesh.rotation.y, impostor.mesh.rotation.x, impostor.mesh.rotation.z);
                 }
-                else if (registeredMesh.mesh === mesh2) {
-                    body2 = registeredMesh.body.body;
+                var deltaPosition = impostor.mesh.position.subtract(impostor.mesh.getBoundingInfo().boundingBox.center);
+                //calculate rotation to fit Oimo's needs (Euler...)
+                var bodyConfig = {
+                    name: impostor.mesh.uniqueId,
+                    //pos: [bbox.center.x, bbox.center.y, bbox.center.z],
+                    //rot: [rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD],
+                    config: [impostor.getParam("mass"), impostor.getParam("friction"), impostor.getParam("restitution")],
+                    size: [],
+                    type: [],
+                    pos: [],
+                    rot: []
+                };
+                var impostors = [impostor];
+                function addToArray(parent) {
+                    parent.getChildMeshes().forEach(function (m) {
+                        if (m.physicImpostor) {
+                            impostors.push(m.physicImpostor);
+                        }
+                    });
                 }
+                addToArray(impostor.mesh);
+                impostors.forEach(function (i) {
+                    //get the correct bounding box
+                    var oldQuaternion = i.mesh.rotationQuaternion;
+                    i.mesh.rotationQuaternion = new BABYLON.Quaternion(0, 0, 0, 1);
+                    i.mesh.computeWorldMatrix(true);
+                    var bbox = i.mesh.getBoundingInfo().boundingBox;
+                    var rot = new OIMO.Euler().setFromQuaternion({ x: impostor.mesh.rotationQuaternion.x, y: impostor.mesh.rotationQuaternion.y, z: impostor.mesh.rotationQuaternion.z, s: impostor.mesh.rotationQuaternion.w });
+                    if (i === impostor) {
+                        //Can also use Array.prototype.push.apply
+                        bodyConfig.pos.push(bbox.center.x);
+                        bodyConfig.pos.push(bbox.center.y);
+                        bodyConfig.pos.push(bbox.center.z);
+                    }
+                    else {
+                        bodyConfig.pos.push(i.mesh.position.x);
+                        bodyConfig.pos.push(i.mesh.position.y);
+                        bodyConfig.pos.push(i.mesh.position.z);
+                    }
+                    bodyConfig.rot.push(rot.x / OIMO.TO_RAD);
+                    bodyConfig.rot.push(rot.y / OIMO.TO_RAD);
+                    bodyConfig.rot.push(rot.z / OIMO.TO_RAD);
+                    // register mesh
+                    switch (i.type) {
+                        case BABYLON.PhysicsEngine.SphereImpostor:
+                            var radiusX = bbox.maximumWorld.x - bbox.minimumWorld.x;
+                            var radiusY = bbox.maximumWorld.y - bbox.minimumWorld.y;
+                            var radiusZ = bbox.maximumWorld.z - bbox.minimumWorld.z;
+                            var size = Math.max(this._checkWithEpsilon(radiusX), this._checkWithEpsilon(radiusY), this._checkWithEpsilon(radiusZ)) / 2;
+                            bodyConfig.type.push('sphere');
+                            //due to the way oimo works with compounds, add 3 times
+                            bodyConfig.size.push(size);
+                            bodyConfig.size.push(size);
+                            bodyConfig.size.push(size);
+                            break;
+                        case BABYLON.PhysicsEngine.PlaneImpostor:
+                        //TODO Oimo now supports cylinder!
+                        case BABYLON.PhysicsEngine.CylinderImpostor:
+                        case BABYLON.PhysicsEngine.BoxImpostor:
+                            var min = bbox.minimumWorld;
+                            var max = bbox.maximumWorld;
+                            var box = max.subtract(min);
+                            var sizeX = this._checkWithEpsilon(box.x);
+                            var sizeY = this._checkWithEpsilon(box.y);
+                            var sizeZ = this._checkWithEpsilon(box.z);
+                            bodyConfig.type.push('box');
+                            bodyConfig.size.push(sizeX);
+                            bodyConfig.size.push(sizeY);
+                            bodyConfig.size.push(sizeZ);
+                            break;
+                    }
+                    //actually not needed, but hey...
+                    i.mesh.rotationQuaternion = oldQuaternion;
+                });
+                impostor.physicsBody = this.world.add(bodyConfig);
+                impostor.setDeltaPosition(deltaPosition);
             }
-            if (!body1 || !body2) {
-                return false;
+            else {
+                this._tmpPositionVector.copyFromFloats(0, 0, 0);
             }
-            if (!options) {
-                options = {};
+            this._tmpPositionVector.addInPlace(impostor.mesh.getBoundingInfo().boundingBox.center);
+            this.setPhysicsBodyTransformation(impostor, this._tmpPositionVector, impostor.mesh.rotationQuaternion);
+        };
+        OimoJSPlugin.prototype._checkWithEpsilon = function (value) {
+            return value < BABYLON.PhysicsEngine.Epsilon ? BABYLON.PhysicsEngine.Epsilon : value;
+        };
+        OimoJSPlugin.prototype.removePhysicsBody = function (impostor) {
+            this.world.removeRigidBody(impostor.physicsBody);
+        };
+        OimoJSPlugin.prototype.generateJoint = function (impostorJoint) {
+            var mainBody = impostorJoint.mainImpostor.physicsBody;
+            var connectedBody = impostorJoint.connectedImpostor.physicsBody;
+            if (!mainBody || !connectedBody) {
+                return;
             }
-            new OIMO.Link({
-                type: options.type,
-                body1: body1,
-                body2: body2,
+            var jointData = impostorJoint.joint.jointData;
+            var options = jointData.nativeParams || {};
+            var type;
+            switch (impostorJoint.joint.type) {
+                case BABYLON.PhysicsJoint.BallAndSocketJoint:
+                    type = "jointBall";
+                    break;
+                case BABYLON.PhysicsJoint.DistanceJoint:
+                    type = "jointDistance";
+                    break;
+                case BABYLON.PhysicsJoint.PrismaticJoint:
+                    type = "jointPrisme";
+                    break;
+                case BABYLON.PhysicsJoint.SliderJoint:
+                    type = "jointSlide";
+                    break;
+                case BABYLON.PhysicsJoint.WheelJoint:
+                    type = "jointWheel";
+                    break;
+                case BABYLON.PhysicsJoint.HingeJoint:
+                default:
+                    type = "jointHinge";
+                    break;
+            }
+            impostorJoint.joint.physicsJoint = this.world.add({
+                type: type,
+                body1: mainBody.body,
+                body2: connectedBody.body,
                 min: options.min,
                 max: options.max,
-                axe1: options.axe1,
-                axe2: options.axe2,
-                pos1: [pivot1.x, pivot1.y, pivot1.z],
-                pos2: [pivot2.x, pivot2.y, pivot2.z],
+                axe1: jointData.mainAxis ? jointData.mainAxis.asArray() : null,
+                axe2: jointData.connectedAxis ? jointData.connectedAxis.asArray() : null,
+                pos1: jointData.mainPivot ? jointData.mainPivot.asArray() : null,
+                pos2: jointData.connectedPivot ? jointData.connectedPivot.asArray() : null,
                 collision: options.collision,
-                spring: options.spring,
-                world: this._world
+                spring: options.spring
             });
-            return true;
         };
-        OimoJSPlugin.prototype.dispose = function () {
-            this._world.clear();
-            while (this._registeredMeshes.length) {
-                this.unregisterMesh(this._registeredMeshes[0].mesh);
-            }
+        OimoJSPlugin.prototype.removeJoint = function (joint) {
+            //TODO
         };
         OimoJSPlugin.prototype.isSupported = function () {
             return OIMO !== undefined;
         };
-        OimoJSPlugin.prototype.getWorldObject = function () {
-            return this._world;
-        };
-        OimoJSPlugin.prototype.getPhysicsBodyOfMesh = function (mesh) {
-            for (var index = 0; index < this._registeredMeshes.length; index++) {
-                var registeredMesh = this._registeredMeshes[index];
-                if (registeredMesh.mesh === mesh) {
-                    return registeredMesh.body;
+        OimoJSPlugin.prototype.setTransformationFromPhysicsBody = function (impostor) {
+            if (!impostor.physicsBody.sleeping) {
+                //TODO check that
+                if (impostor.physicsBody.shapes.next) {
+                    var parentShape = this._getLastShape(impostor.physicsBody);
+                    impostor.mesh.position.x = parentShape.position.x * OIMO.WORLD_SCALE;
+                    impostor.mesh.position.y = parentShape.position.y * OIMO.WORLD_SCALE;
+                    impostor.mesh.position.z = parentShape.position.z * OIMO.WORLD_SCALE;
                 }
+                else {
+                    impostor.mesh.position.copyFrom(impostor.physicsBody.getPosition());
+                }
+                impostor.mesh.rotationQuaternion.copyFrom(impostor.physicsBody.getQuaternion());
             }
-            return null;
+        };
+        OimoJSPlugin.prototype.setPhysicsBodyTransformation = function (impostor, newPosition, newRotation) {
+            var body = impostor.physicsBody;
+            body.setPosition(newPosition);
+            body.setQuaternion(newRotation);
+            body.sleeping = false;
+            //force Oimo to update the body's position
+            body.updatePosition(1);
         };
         OimoJSPlugin.prototype._getLastShape = function (body) {
             var lastShape = body.shapes;
@@ -282,50 +228,10 @@ var BABYLON;
             }
             return lastShape;
         };
-        OimoJSPlugin.prototype.runOneStep = function (time) {
-            this._world.step();
-            // Update the position of all registered meshes
-            var i = this._registeredMeshes.length;
-            var m;
-            while (i--) {
-                var body = this._registeredMeshes[i].body.body;
-                var mesh = this._registeredMeshes[i].mesh;
-                if (!this._registeredMeshes[i].delta) {
-                    this._registeredMeshes[i].delta = BABYLON.Vector3.Zero();
-                }
-                if (!body.sleeping) {
-                    //TODO check that
-                    if (body.shapes.next) {
-                        var parentShape = this._getLastShape(body);
-                        mesh.position.x = parentShape.position.x * OIMO.WORLD_SCALE;
-                        mesh.position.y = parentShape.position.y * OIMO.WORLD_SCALE;
-                        mesh.position.z = parentShape.position.z * OIMO.WORLD_SCALE;
-                    }
-                    else {
-                        mesh.position.copyFrom(body.getPosition()).addInPlace(this._registeredMeshes[i].delta);
-                    }
-                    mesh.rotationQuaternion.copyFrom(body.getQuaternion());
-                    mesh.computeWorldMatrix();
-                }
-                //check if the collide callback is set. 
-                if (mesh.onPhysicsCollide) {
-                    var meshUniqueName = mesh.uniqueId;
-                    var contact = this._world.contacts;
-                    while (contact !== null) {
-                        //is this body colliding with any other?
-                        if ((contact.body1.name == mesh.uniqueId || contact.body2.name == mesh.uniqueId) && contact.touching && !contact.body1.sleeping && !contact.body2.sleeping) {
-                            var otherUniqueId = contact.body1.name == mesh.uniqueId ? contact.body2.name : contact.body1.name;
-                            //get the mesh and execute the callback
-                            var otherMesh = mesh.getScene().getMeshByUniqueID(otherUniqueId);
-                            if (otherMesh)
-                                mesh.onPhysicsCollide(otherMesh, contact);
-                        }
-                        contact = contact.next;
-                    }
-                }
-            }
+        OimoJSPlugin.prototype.dispose = function () {
+            this.world.clear();
         };
         return OimoJSPlugin;
-    })();
+    }());
     BABYLON.OimoJSPlugin = OimoJSPlugin;
 })(BABYLON || (BABYLON = {}));

+ 292 - 11
src/Physics/Plugins/babylon.oimoJSPlugin.ts

@@ -2,6 +2,288 @@ module BABYLON {
     declare var OIMO;
 
     export class OimoJSPlugin {
+
+        public world: any;
+        public name: string = "OimoJSPlugin";
+
+        constructor(iterations?: number) {
+            this.world = new OIMO.World(null, null, iterations);
+            this.world.clear();
+        }
+
+        public setGravity(gravity: Vector3) {
+            this.world.gravity.copy(gravity);
+        }
+
+        private _tmpImpostorsArray: Array<PhysicsImpostor> = [];
+
+        public executeStep(delta: number, impostors: Array<PhysicsImpostor>) {
+
+            impostors.forEach(function(impostor) {
+                impostor.beforeStep();
+            });
+
+            this.world.step();
+
+            impostors.forEach(function(impostor) {
+                impostor.afterStep();
+                //update the ordered impostors array
+                this._tmpImpostorsArray[impostor.mesh.uniqueId] = impostor;
+            });
+            
+            //check for collisions
+            var contact = this.world.contacts;
+
+            while (contact !== null) {
+                if (contact.touching && !contact.body1.sleeping && !contact.body2.sleeping) {
+                    continue;
+                }
+                //is this body colliding with any other? get the impostor
+                var mainImpostor = this._tmpImpostorsArray[+contact.body1.name];
+                var collidingImpostor = this._tmpImpostorsArray[+contact.body2.name];
+
+                if (!mainImpostor || !collidingImpostor) continue;
+                mainImpostor.onCollide({ body: collidingImpostor.physicsBody });
+                collidingImpostor.onCollide({ body: mainImpostor.physicsBody });
+                contact = contact.next;
+            }
+
+        }
+
+        public applyImpulse(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
+            impostor.physicsBody.body.applyImpulse(contactPoint.scale(OIMO.INV_SCALE), force.scale(OIMO.INV_SCALE));
+
+        }
+        public applyForce(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
+            Tools.Warn("Oimo doesn't support applying force. Using impule instead.");
+            this.applyImpulse(impostor, force, contactPoint);
+        }
+        public generatePhysicsBody(impostor: PhysicsImpostor) {
+            //parent-child relationship. Does this impostor has a parent impostor?
+            if (impostor.parent) {
+                if (impostor.physicsBody) {
+                    this.removePhysicsBody(impostor);
+                    //TODO is that needed?
+                    impostor.forceUpdate();
+                }
+                return;
+            }
+
+            impostor.mesh.computeWorldMatrix(true);
+
+            if (impostor.isBodyInitRequired()) {
+                if (!impostor.mesh.rotationQuaternion) {
+                    impostor.mesh.rotationQuaternion = Quaternion.RotationYawPitchRoll(impostor.mesh.rotation.y, impostor.mesh.rotation.x, impostor.mesh.rotation.z);
+                }
+
+                var deltaPosition = impostor.mesh.position.subtract(impostor.mesh.getBoundingInfo().boundingBox.center);
+            
+                //calculate rotation to fit Oimo's needs (Euler...)
+                
+                
+                var bodyConfig: any = {
+                    name: impostor.mesh.uniqueId,
+                    //pos: [bbox.center.x, bbox.center.y, bbox.center.z],
+                    //rot: [rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD],
+                    config: [impostor.getParam("mass"), impostor.getParam("friction"), impostor.getParam("restitution")],
+                    size: [],
+                    type: [],
+                    pos: [],
+                    rot: []
+                };
+
+                var impostors = [impostor];
+                function addToArray(parent: AbstractMesh) {
+                    parent.getChildMeshes().forEach(function(m) {
+                        if (m.physicImpostor) {
+                            impostors.push(m.physicImpostor);
+                        }
+                    });
+                }
+                addToArray(impostor.mesh)
+
+                impostors.forEach(function(i) {
+                    
+                    //get the correct bounding box
+                    var oldQuaternion = i.mesh.rotationQuaternion;
+                    i.mesh.rotationQuaternion = new Quaternion(0, 0, 0, 1);
+                    i.mesh.computeWorldMatrix(true);
+
+
+                    var bbox = i.mesh.getBoundingInfo().boundingBox;
+                    var rot = new OIMO.Euler().setFromQuaternion({ x: impostor.mesh.rotationQuaternion.x, y: impostor.mesh.rotationQuaternion.y, z: impostor.mesh.rotationQuaternion.z, s: impostor.mesh.rotationQuaternion.w });
+                    
+                    if (i === impostor) {
+                        //Can also use Array.prototype.push.apply
+                        bodyConfig.pos.push(bbox.center.x);
+                        bodyConfig.pos.push(bbox.center.y);
+                        bodyConfig.pos.push(bbox.center.z);
+                    } else {
+                        bodyConfig.pos.push(i.mesh.position.x);
+                        bodyConfig.pos.push(i.mesh.position.y);
+                        bodyConfig.pos.push(i.mesh.position.z);
+                    }
+
+                    bodyConfig.rot.push(rot.x / OIMO.TO_RAD);
+                    bodyConfig.rot.push(rot.y / OIMO.TO_RAD);
+                    bodyConfig.rot.push(rot.z / OIMO.TO_RAD);
+                    
+                    // register mesh
+                    switch (i.type) {
+                        case PhysicsEngine.SphereImpostor:
+                            var radiusX = bbox.maximumWorld.x - bbox.minimumWorld.x;
+                            var radiusY = bbox.maximumWorld.y - bbox.minimumWorld.y;
+                            var radiusZ = bbox.maximumWorld.z - bbox.minimumWorld.z;
+
+                            var size = Math.max(
+                                this._checkWithEpsilon(radiusX),
+                                this._checkWithEpsilon(radiusY),
+                                this._checkWithEpsilon(radiusZ)) / 2;
+
+                            bodyConfig.type.push('sphere');
+                            //due to the way oimo works with compounds, add 3 times
+                            bodyConfig.size.push(size);
+                            bodyConfig.size.push(size);
+                            bodyConfig.size.push(size);
+                            break;
+
+                        case PhysicsEngine.PlaneImpostor:
+                        //TODO Oimo now supports cylinder!
+                        case PhysicsEngine.CylinderImpostor:
+                        case PhysicsEngine.BoxImpostor:
+
+                            var min = bbox.minimumWorld;
+                            var max = bbox.maximumWorld;
+                            var box = max.subtract(min);
+                            var sizeX = this._checkWithEpsilon(box.x);
+                            var sizeY = this._checkWithEpsilon(box.y);
+                            var sizeZ = this._checkWithEpsilon(box.z);
+
+                            bodyConfig.type.push('box');
+                            bodyConfig.size.push(sizeX);
+                            bodyConfig.size.push(sizeY);
+                            bodyConfig.size.push(sizeZ);
+                            break;
+                    }
+                    
+                    //actually not needed, but hey...
+                    i.mesh.rotationQuaternion = oldQuaternion;
+                });
+
+                impostor.physicsBody = this.world.add(bodyConfig);
+                
+                impostor.setDeltaPosition(deltaPosition);
+
+            } else {
+                this._tmpPositionVector.copyFromFloats(0, 0, 0);
+            }
+            this._tmpPositionVector.addInPlace(impostor.mesh.getBoundingInfo().boundingBox.center);
+            this.setPhysicsBodyTransformation(impostor, this._tmpPositionVector, impostor.mesh.rotationQuaternion);
+        }
+
+        private _checkWithEpsilon(value: number): number {
+            return value < PhysicsEngine.Epsilon ? PhysicsEngine.Epsilon : value;
+        }
+
+        private _tmpPositionVector: Vector3 = Vector3.Zero();
+
+        public removePhysicsBody(impostor: PhysicsImpostor) {
+            this.world.removeRigidBody(impostor.physicsBody);
+        }
+
+        public generateJoint(impostorJoint: PhysicsImpostorJoint) {
+            var mainBody = impostorJoint.mainImpostor.physicsBody;
+            var connectedBody = impostorJoint.connectedImpostor.physicsBody;
+
+            if (!mainBody || !connectedBody) {
+                return;
+            }
+            var jointData = impostorJoint.joint.jointData;
+            var options = jointData.nativeParams || {};
+            var type;
+            switch (impostorJoint.joint.type) {
+                case PhysicsJoint.BallAndSocketJoint:
+                    type = "jointBall";
+                    break;
+                case PhysicsJoint.DistanceJoint:
+                    type = "jointDistance";
+                    break;
+                case PhysicsJoint.PrismaticJoint:
+                    type = "jointPrisme";
+                    break;
+                case PhysicsJoint.SliderJoint:
+                    type = "jointSlide";
+                    break;
+                case PhysicsJoint.WheelJoint:
+                    type = "jointWheel";
+                    break;
+                case PhysicsJoint.HingeJoint:
+                default:
+                    type = "jointHinge";
+                    break;
+            }
+            impostorJoint.joint.physicsJoint = this.world.add({
+                type: type,
+                body1: mainBody.body,
+                body2: connectedBody.body,
+                min: options.min,
+                max: options.max,
+                axe1: jointData.mainAxis ? jointData.mainAxis.asArray() : null,
+                axe2: jointData.connectedAxis ? jointData.connectedAxis.asArray() : null,
+                pos1: jointData.mainPivot ? jointData.mainPivot.asArray() : null,
+                pos2: jointData.connectedPivot ? jointData.connectedPivot.asArray() : null,
+                collision: options.collision,
+                spring: options.spring
+            });
+        }
+
+        public removeJoint(joint: PhysicsImpostorJoint) {
+            //TODO
+        }
+
+        public isSupported(): boolean {
+            return OIMO !== undefined;
+        }
+
+        public setTransformationFromPhysicsBody(impostor: PhysicsImpostor) {
+            if (!impostor.physicsBody.sleeping) {
+                //TODO check that
+                if (impostor.physicsBody.shapes.next) {
+                    var parentShape = this._getLastShape(impostor.physicsBody);
+                    impostor.mesh.position.x = parentShape.position.x * OIMO.WORLD_SCALE;
+                    impostor.mesh.position.y = parentShape.position.y * OIMO.WORLD_SCALE;
+                    impostor.mesh.position.z = parentShape.position.z * OIMO.WORLD_SCALE;
+                } else {
+                    impostor.mesh.position.copyFrom(impostor.physicsBody.getPosition());
+
+                }
+                impostor.mesh.rotationQuaternion.copyFrom(impostor.physicsBody.getQuaternion());
+            }
+        }
+
+        public setPhysicsBodyTransformation(impostor: PhysicsImpostor, newPosition: Vector3, newRotation: Quaternion) {
+            var body = impostor.physicsBody;
+            body.setPosition(newPosition);
+            body.setQuaternion(newRotation);
+            body.sleeping = false;
+            //force Oimo to update the body's position
+            body.updatePosition(1);
+        }
+
+        private _getLastShape(body: any): any {
+            var lastShape = body.shapes;
+            while (lastShape.next) {
+                lastShape = lastShape.next;
+            }
+            return lastShape;
+        }
+
+        public dispose() {
+            this.world.clear();
+        }
+    }
+
+    /*export class OldOimoJSPlugin {
         private _world;
         private _registeredMeshes = [];
 
@@ -50,11 +332,14 @@ module BABYLON {
 
             var bodyConfig: any = {
                 name: mesh.uniqueId,
-                pos: [bbox.center.x, bbox.center.y, bbox.center.z],
-                rot: [rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD],
+                //pos: [bbox.center.x, bbox.center.y, bbox.center.z],
+                //rot: [rot.x / OIMO.TO_RAD, rot.y / OIMO.TO_RAD, rot.z / OIMO.TO_RAD],
                 move: options.mass != 0,
                 config: [options.mass, options.friction, options.restitution],
-                world: this._world
+                type: [],
+                shape: [],
+                pos: [],
+                rot: []
             };
 
             // register mesh
@@ -240,11 +525,7 @@ module BABYLON {
             }
         }
 
-        /**
-         * Update the body position according to the mesh position
-         * @param mesh
-         */
-        public updateBodyPosition = function (mesh: AbstractMesh): void {
+        public updateBodyPosition = function(mesh: AbstractMesh): void {
 
             for (var index = 0; index < this._registeredMeshes.length; index++) {
                 var registeredMesh = this._registeredMeshes[index];
@@ -397,17 +678,17 @@ module BABYLON {
                     var contact = this._world.contacts;
                     while (contact !== null) {
                         //is this body colliding with any other?
-                        if ((contact.body1.name == mesh.uniqueId || contact.body2.name == mesh.uniqueId) && contact.touching && /* !contact.sleeping*/ !contact.body1.sleeping && !contact.body2.sleeping) {
+                        if ((contact.body1.name == mesh.uniqueId || contact.body2.name == mesh.uniqueId) && contact.touching && !contact.body1.sleeping && !contact.body2.sleeping) {
                             var otherUniqueId = contact.body1.name == mesh.uniqueId ? contact.body2.name : contact.body1.name;
                             //get the mesh and execute the callback
                             var otherMesh = mesh.getScene().getMeshByUniqueID(otherUniqueId);
                             if (otherMesh)
-                                mesh.onPhysicsCollide(otherMesh, contact); 
+                                mesh.onPhysicsCollide(otherMesh, contact);
                         }
                         contact = contact.next;
                     }
                 }
             }
         }
-    }
+    }*/
 }

+ 1 - 1
src/Physics/babylon.physicsImpostor.js

@@ -32,7 +32,7 @@ var BABYLON;
                 _this.mesh.position.addInPlace(_this._deltaPosition);
                 _this.mesh.rotationQuaternion.multiplyInPlace(_this._deltaRotation);
             };
-            //event object due to cannon's architecture.
+            //event and body object due to cannon's event-based architecture.
             this.onCollide = function (e) {
                 var otherImpostor = _this._physicsEngine.getImpostorWithPhysicsBody(e.body);
                 if (otherImpostor) {

+ 1 - 1
src/Physics/babylon.physicsImpostor.ts

@@ -177,7 +177,7 @@ module BABYLON {
             this.mesh.rotationQuaternion.multiplyInPlace(this._deltaRotation);
         }
         
-        //event object due to cannon's architecture.
+        //event and body object due to cannon's event-based architecture.
         public onCollide = (e: { body: any }) => {
             var otherImpostor = this._physicsEngine.getImpostorWithPhysicsBody(e.body);
             if (otherImpostor) {