sebavan 6 rokov pred
rodič
commit
26444baf14

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 26144 - 25902
Playground/babylon.d.txt


+ 1 - 0
Playground/debug.html

@@ -35,6 +35,7 @@
         <script src="js/libs/fileSaver.js"></script>
 
         <!-- Babylon.js -->
+        <script src="https://preview.babylonjs.com/ammo.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/gltf_validator.js"></script>

+ 1 - 0
Playground/frame.html

@@ -28,6 +28,7 @@
 
         <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
         <!-- Babylon.js -->
+        <script src="https://preview.babylonjs.com/ammo.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/earcut.min.js"></script>

+ 1 - 0
Playground/full.html

@@ -26,6 +26,7 @@
 
         <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
         <!-- Babylon.js -->
+        <script src="https://preview.babylonjs.com/ammo.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/gltf_validator.js"></script>

+ 2 - 0
Playground/index-local.html

@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <html>
 
+
 <head>
     <title>Babylon.js Playground</title>
     <meta charset='utf-8' />
@@ -14,6 +15,7 @@
     <script src="js/libs/jszip.min.js"></script>
     <script src="js/libs/fileSaver.js"></script>
     <!-- Dependencies -->
+    <script src="../dist/preview%20release/ammo.js"></script>
     <script src="../dist/preview%20release/cannon.js"></script>
     <script src="../dist/preview%20release/Oimo.js"></script>
     <script src="../dist/preview%20release/gltf_validator.js"></script>

+ 1 - 0
Playground/index.html

@@ -34,6 +34,7 @@
         <script src="js/libs/jszip.min.js"></script>
         <script src="js/libs/fileSaver.js"></script>
         <!-- Dependencies -->
+        <script src="https://preview.babylonjs.com/ammo.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/gltf_validator.js"></script>

+ 1 - 0
Playground/indexStable.html

@@ -34,6 +34,7 @@
     <script src="js/libs/jszip.min.js"></script>
     <script src="js/libs/fileSaver.js"></script>
     <!-- Physics -->
+    <script src="https://cdn.babylonjs.com/ammo.js"></script>
     <script src="https://cdn.babylonjs.com/cannon.js"></script>
     <script src="https://cdn.babylonjs.com/Oimo.js"></script>
     <script src="https://cdn.babylonjs.com/gltf_validator.js"></script>

+ 1 - 0
Playground/ts.html

@@ -34,6 +34,7 @@
         <script src="js/libs/jszip.min.js"></script>
         <script src="js/libs/fileSaver.js"></script>
         <!-- Dependencies -->
+        <script src="https://preview.babylonjs.com/ammo.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/earcut.min.js"></script>

+ 1 - 0
Playground/zipContent/index.html

@@ -8,6 +8,7 @@
         <!-- Babylon.js -->
         <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
         <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
+        <script src="https://preview.babylonjs.com/ammo.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/gltf_validator.js"></script>

+ 2 - 1
Viewer/tests/validation/validate.html

@@ -3,7 +3,8 @@
 <head>
 	<title>BabylonJS - Build validation page</title>
 	<link href="index.css" rel="stylesheet" />
-    <script src="https://preview.babylonjs.com/cannon.js"></script>
+	<script src="https://preview.babylonjs.com/ammo.js"></script>
+	<script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/gltf_validator.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 666 - 0
dist/preview release/ammo.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 16272 - 16027
dist/preview release/babylon.d.ts


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/babylon.js


+ 645 - 5
dist/preview release/babylon.max.js

@@ -15725,7 +15725,7 @@ var BABYLON;
          * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
          * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
          * @param noMipmap defines a boolean indicating that no mipmaps shall be generated.  Ignored for compressed textures.  They must be in the file
-         * @param invertY when true, image is flipped when loaded.  You probably want true. Ignored for compressed textures.  Must be flipped in the file
+         * @param invertY when true, image is flipped when loaded.  You probably want true. Certain compressed textures may invert this if their default is inverted (eg. ktx)
          * @param scene needed for loading to the correct scene
          * @param samplingMode mode with should be used sample / access the texture (Default: BABYLON.Texture.TRILINEAR_SAMPLINGMODE)
          * @param onLoad optional callback to be called upon successful completion
@@ -15796,7 +15796,7 @@ var BABYLON;
                         customFallback = true;
                         excludeLoaders.push(loader);
                         BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
-                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
+                        _this.createTexture(urlArg, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                         return;
                     }
                 }
@@ -15805,7 +15805,7 @@ var BABYLON;
                         texture.onLoadedObservable.remove(onLoadObserver);
                     }
                     if (BABYLON.Tools.UseFallbackTexture) {
-                        _this.createTexture(BABYLON.Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        _this.createTexture(BABYLON.Tools.fallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture);
                         return;
                     }
                 }
@@ -15821,7 +15821,7 @@ var BABYLON;
                             onInternalError("TextureLoader failed to load data");
                         }
                         else {
-                            _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
+                            _this._prepareWebGLTexture(texture, scene, width, height, texture.invertY, !loadMipmap, isCompressed, function () {
                                 done();
                                 return false;
                             }, samplingMode);
@@ -15844,7 +15844,7 @@ var BABYLON;
                         // in case of a webgl context lost
                         texture._buffer = img;
                     }
-                    _this._prepareWebGLTexture(texture, scene, img.width, img.height, invertY, noMipmap, false, function (potWidth, potHeight, continuationCallback) {
+                    _this._prepareWebGLTexture(texture, scene, img.width, img.height, texture.invertY, noMipmap, false, function (potWidth, potHeight, continuationCallback) {
                         var gl = _this._gl;
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         var internalFormat = format ? _this._getInternalFormat(format) : ((extension === ".jpg") ? gl.RGB : gl.RGBA);
@@ -31398,6 +31398,8 @@ var BABYLON;
             this.next = null;
             // Private
             /** @hidden */
+            this._invertVScale = false;
+            /** @hidden */
             this._initialSlot = -1;
             /** @hidden */
             this._designatedSlot = -1;
@@ -32480,6 +32482,9 @@ var BABYLON;
             }
             scene.getEngine().onBeforeTextureInitObservable.notifyObservers(_this);
             var load = function () {
+                if (_this._texture && _this._texture._invertVScale) {
+                    _this.vScale *= -1;
+                }
                 if (_this.onLoadObservable.hasObservers()) {
                     _this.onLoadObservable.notifyObservers(_this);
                 }
@@ -97727,6 +97732,7 @@ var BABYLON;
             this._bodyUpdateRequired = false;
             this._onBeforePhysicsStepCallbacks = new Array();
             this._onAfterPhysicsStepCallbacks = new Array();
+            /** @hidden */
             this._onPhysicsCollideCallbacks = [];
             this._deltaPosition = BABYLON.Vector3.Zero();
             this._isDisposed = false;
@@ -99884,6 +99890,632 @@ var BABYLON;
 
 var BABYLON;
 (function (BABYLON) {
+    /**
+     * AmmoJS Physics plugin
+     * @see https://doc.babylonjs.com/how_to/using_the_physics_engine
+     * @see https://github.com/kripken/ammo.js/
+     */
+    var AmmoJSPlugin = /** @class */ (function () {
+        /**
+         * Initializes the ammoJS plugin
+         * @param _useDeltaForWorldStep if the time between frames should be used when calculating physics steps (Default: true)
+         */
+        function AmmoJSPlugin(_useDeltaForWorldStep) {
+            if (_useDeltaForWorldStep === void 0) { _useDeltaForWorldStep = true; }
+            var _this = this;
+            this._useDeltaForWorldStep = _useDeltaForWorldStep;
+            /**
+             * Name of the plugin
+             */
+            this.name = "AmmoJSPlugin";
+            this._timeStep = 1 / 60;
+            this._maxSteps = 5;
+            this._tmpQuaternion = new BABYLON.Quaternion();
+            this._tmpContactCallbackResult = false;
+            if (typeof Ammo === "function") {
+                Ammo();
+            }
+            this.bjsAMMO = Ammo;
+            if (!this.isSupported()) {
+                BABYLON.Tools.Error("AmmoJS is not available. Please make sure you included the js file.");
+                return;
+            }
+            // Initialize the physics world
+            this._collisionConfiguration = new this.bjsAMMO.btDefaultCollisionConfiguration();
+            this._dispatcher = new this.bjsAMMO.btCollisionDispatcher(this._collisionConfiguration);
+            this._overlappingPairCache = new this.bjsAMMO.btDbvtBroadphase();
+            this._solver = new this.bjsAMMO.btSequentialImpulseConstraintSolver();
+            this.world = new this.bjsAMMO.btDiscreteDynamicsWorld(this._dispatcher, this._overlappingPairCache, this._solver, this._collisionConfiguration);
+            this._tmpAmmoConcreteContactResultCallback = new this.bjsAMMO.ConcreteContactResultCallback();
+            this._tmpAmmoConcreteContactResultCallback.addSingleResult = function () { _this._tmpContactCallbackResult = true; };
+            // Create temp ammo variables
+            this._tmpAmmoTransform = new this.bjsAMMO.btTransform();
+            this._tmpAmmoTransform.setIdentity();
+            this._tmpAmmoQuaternion = new this.bjsAMMO.btQuaternion(0, 0, 0, 1);
+            this._tmpAmmoVectorA = new this.bjsAMMO.btVector3(0, 0, 0);
+            this._tmpAmmoVectorB = new this.bjsAMMO.btVector3(0, 0, 0);
+            this._tmpAmmoVectorC = new this.bjsAMMO.btVector3(0, 0, 0);
+        }
+        /**
+         * Sets the gravity of the physics world (m/(s^2))
+         * @param gravity Gravity to set
+         */
+        AmmoJSPlugin.prototype.setGravity = function (gravity) {
+            this._tmpAmmoVectorA.setValue(gravity.x, gravity.y, gravity.z);
+            this.world.setGravity(this._tmpAmmoVectorA);
+        };
+        /**
+         * Amount of time to step forward on each frame (only used if useDeltaForWorldStep is false in the constructor)
+         * @param timeStep timestep to use in seconds
+         */
+        AmmoJSPlugin.prototype.setTimeStep = function (timeStep) {
+            this._timeStep = timeStep;
+        };
+        /**
+         * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
+         * @returns the current timestep in seconds
+         */
+        AmmoJSPlugin.prototype.getTimeStep = function () {
+            return this._timeStep;
+        };
+        // Ammo's contactTest and contactPairTest take a callback that runs synchronously, wrap them so that they are easier to consume
+        AmmoJSPlugin.prototype._isImpostorInContact = function (impostor) {
+            this._tmpContactCallbackResult = false;
+            this.world.contactTest(impostor.physicsBody, this._tmpAmmoConcreteContactResultCallback);
+            return this._tmpContactCallbackResult;
+        };
+        // Ammo's collision events have some weird quirks
+        // contactPairTest fires too many events as it fires events even when objects are close together but contactTest does not
+        // so only fire event if both contactTest and contactPairTest have a hit
+        AmmoJSPlugin.prototype._isImpostorPairInContact = function (impostorA, impostorB) {
+            this._tmpContactCallbackResult = false;
+            this.world.contactPairTest(impostorA.physicsBody, impostorB.physicsBody, this._tmpAmmoConcreteContactResultCallback);
+            return this._tmpContactCallbackResult;
+        };
+        // Ammo's behavior when maxSteps > 0 does not behave as described in docs
+        // @see http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
+        //
+        // When maxSteps is 0 do the entire simulation in one step
+        // When maxSteps is > 0, run up to maxStep times, if on the last step the (remaining step - fixedTimeStep) is < fixedTimeStep, the remainder will be used for the step. (eg. if remainder is 1.001 and fixedTimeStep is 1 the last step will be 1.001, if instead it did 2 steps (1, 0.001) issues occuered when having a tiny step in ammo)
+        // Note: To get deterministic physics, timeStep would always need to be divisible by fixedTimeStep
+        AmmoJSPlugin.prototype._stepSimulation = function (timeStep, maxSteps, fixedTimeStep) {
+            if (timeStep === void 0) { timeStep = 1 / 60; }
+            if (maxSteps === void 0) { maxSteps = 10; }
+            if (fixedTimeStep === void 0) { fixedTimeStep = 1 / 60; }
+            if (maxSteps == 0) {
+                this.world.stepSimulation(timeStep, 0);
+            }
+            else {
+                while (maxSteps > 0 && timeStep > 0) {
+                    if (timeStep - fixedTimeStep < fixedTimeStep) {
+                        this.world.stepSimulation(timeStep, 0);
+                        timeStep = 0;
+                    }
+                    else {
+                        timeStep -= fixedTimeStep;
+                        this.world.stepSimulation(fixedTimeStep, 0);
+                    }
+                    maxSteps--;
+                }
+            }
+        };
+        /**
+         * Moves the physics simulation forward delta seconds and updates the given physics imposters
+         * Prior to the step the imposters physics location is set to the position of the babylon meshes
+         * After the step the babylon meshes are set to the position of the physics imposters
+         * @param delta amount of time to step forward
+         * @param impostors array of imposters to update before/after the step
+         */
+        AmmoJSPlugin.prototype.executeStep = function (delta, impostors) {
+            for (var _i = 0, impostors_1 = impostors; _i < impostors_1.length; _i++) {
+                var impostor = impostors_1[_i];
+                // Update physics world objects to match babylon world
+                impostor.beforeStep();
+            }
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+            for (var _a = 0, impostors_2 = impostors; _a < impostors_2.length; _a++) {
+                var mainImpostor = impostors_2[_a];
+                // After physics update make babylon world objects match physics world objects
+                mainImpostor.afterStep();
+                // Handle collision event
+                if (mainImpostor._onPhysicsCollideCallbacks.length > 0) {
+                    if (this._isImpostorInContact(mainImpostor)) {
+                        for (var _b = 0, _c = mainImpostor._onPhysicsCollideCallbacks; _b < _c.length; _b++) {
+                            var collideCallback = _c[_b];
+                            for (var _d = 0, _e = collideCallback.otherImpostors; _d < _e.length; _d++) {
+                                var otherImpostor = _e[_d];
+                                if (mainImpostor.physicsBody.isActive() || otherImpostor.physicsBody.isActive()) {
+                                    if (this._isImpostorPairInContact(mainImpostor, otherImpostor)) {
+                                        mainImpostor.onCollide({ body: otherImpostor.physicsBody });
+                                        otherImpostor.onCollide({ body: mainImpostor.physicsBody });
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        };
+        /**
+         * Applies an implulse on the imposter
+         * @param impostor imposter to apply impulse
+         * @param force amount of force to be applied to the imposter
+         * @param contactPoint the location to apply the impulse on the imposter
+         */
+        AmmoJSPlugin.prototype.applyImpulse = function (impostor, force, contactPoint) {
+            var worldPoint = this._tmpAmmoVectorA;
+            var impulse = this._tmpAmmoVectorB;
+            worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
+            impulse.setValue(force.x, force.y, force.z);
+            impostor.physicsBody.applyImpulse(impulse, worldPoint);
+        };
+        /**
+         * Applies a force on the imposter
+         * @param impostor imposter to apply force
+         * @param force amount of force to be applied to the imposter
+         * @param contactPoint the location to apply the force on the imposter
+         */
+        AmmoJSPlugin.prototype.applyForce = function (impostor, force, contactPoint) {
+            var worldPoint = this._tmpAmmoVectorA;
+            var impulse = this._tmpAmmoVectorB;
+            worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
+            impulse.setValue(force.x, force.y, force.z);
+            impostor.physicsBody.applyForce(impulse, worldPoint);
+        };
+        /**
+         * Creates a physics body using the plugin
+         * @param impostor the imposter to create the physics body on
+         */
+        AmmoJSPlugin.prototype.generatePhysicsBody = function (impostor) {
+            impostor._pluginData = { toDispose: [] };
+            //parent-child relationship
+            if (impostor.parent) {
+                if (impostor.physicsBody) {
+                    this.removePhysicsBody(impostor);
+                    impostor.forceUpdate();
+                }
+                return;
+            }
+            if (impostor.isBodyInitRequired()) {
+                var colShape = this._createShape(impostor);
+                var mass = impostor.getParam("mass");
+                impostor._pluginData.mass = mass;
+                var localInertia = new Ammo.btVector3(0, 0, 0);
+                var startTransform = new Ammo.btTransform();
+                startTransform.setIdentity();
+                if (mass !== 0) {
+                    colShape.calculateLocalInertia(mass, localInertia);
+                }
+                this._tmpAmmoVectorA.setValue(impostor.object.position.x, impostor.object.position.y, impostor.object.position.z);
+                this._tmpAmmoQuaternion.setValue(impostor.object.rotationQuaternion.x, impostor.object.rotationQuaternion.y, impostor.object.rotationQuaternion.z, impostor.object.rotationQuaternion.w);
+                startTransform.setOrigin(this._tmpAmmoVectorA);
+                startTransform.setRotation(this._tmpAmmoQuaternion);
+                var myMotionState = new Ammo.btDefaultMotionState(startTransform);
+                var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
+                var body = new Ammo.btRigidBody(rbInfo);
+                // Make objects kinematic if it's mass is 0
+                if (mass === 0) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
+                    body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
+                }
+                body.setRestitution(impostor.getParam("restitution"));
+                this.world.addRigidBody(body);
+                impostor.physicsBody = body;
+                impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
+            }
+        };
+        /**
+         * Removes the physics body from the imposter and disposes of the body's memory
+         * @param impostor imposter to remove the physics body from
+         */
+        AmmoJSPlugin.prototype.removePhysicsBody = function (impostor) {
+            var _this = this;
+            if (this.world) {
+                this.world.removeRigidBody(impostor.physicsBody);
+                impostor._pluginData.toDispose.forEach(function (d) {
+                    _this.bjsAMMO.destroy(d);
+                });
+            }
+        };
+        /**
+         * Generates a joint
+         * @param impostorJoint the imposter joint to create the joint with
+         */
+        AmmoJSPlugin.prototype.generateJoint = function (impostorJoint) {
+            var mainBody = impostorJoint.mainImpostor.physicsBody;
+            var connectedBody = impostorJoint.connectedImpostor.physicsBody;
+            if (!mainBody || !connectedBody) {
+                return;
+            }
+            var jointData = impostorJoint.joint.jointData;
+            if (!jointData.mainPivot) {
+                jointData.mainPivot = new BABYLON.Vector3(0, 0, 0);
+            }
+            if (!jointData.connectedPivot) {
+                jointData.connectedPivot = new BABYLON.Vector3(0, 0, 0);
+            }
+            var joint;
+            switch (impostorJoint.joint.type) {
+                case BABYLON.PhysicsJoint.BallAndSocketJoint:
+                    joint = new Ammo.btPoint2PointConstraint(mainBody, connectedBody, new Ammo.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new Ammo.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
+                    break;
+                default:
+                    BABYLON.Tools.Warn("JointType not currently supported by the Ammo plugin, falling back to PhysicsJoint.BallAndSocketJoint");
+                    joint = new Ammo.btPoint2PointConstraint(mainBody, connectedBody, new Ammo.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new Ammo.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
+                    break;
+            }
+            this.world.addConstraint(joint, true);
+            impostorJoint.joint.physicsJoint = joint;
+        };
+        /**
+         * Removes a joint
+         * @param impostorJoint the imposter joint to remove the joint from
+         */
+        AmmoJSPlugin.prototype.removeJoint = function (impostorJoint) {
+            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+        };
+        // adds all verticies (including child verticies) to the triangle mesh
+        AmmoJSPlugin.prototype._addMeshVerts = function (btTriangleMesh, topLevelObject, object) {
+            var _this = this;
+            var triangleCount = 0;
+            if (object && object.getIndices && object.getWorldMatrix && object.getChildMeshes) {
+                var indices = object.getIndices();
+                if (!indices) {
+                    indices = [];
+                }
+                var vertexPositions = object.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+                if (!vertexPositions) {
+                    vertexPositions = [];
+                }
+                object.computeWorldMatrix(false);
+                var faceCount = indices.length / 3;
+                for (var i = 0; i < faceCount; i++) {
+                    var triPoints = [];
+                    for (var point = 0; point < 3; point++) {
+                        var v = new BABYLON.Vector3(vertexPositions[(indices[(i * 3) + point] * 3) + 0], vertexPositions[(indices[(i * 3) + point] * 3) + 1], vertexPositions[(indices[(i * 3) + point] * 3) + 2]);
+                        v = BABYLON.Vector3.TransformCoordinates(v, object.getWorldMatrix());
+                        v.subtractInPlace(topLevelObject.position);
+                        var vec;
+                        if (point == 0) {
+                            vec = this._tmpAmmoVectorA;
+                        }
+                        else if (point == 1) {
+                            vec = this._tmpAmmoVectorB;
+                        }
+                        else {
+                            vec = this._tmpAmmoVectorC;
+                        }
+                        vec.setValue(v.x, v.y, v.z);
+                        triPoints.push(vec);
+                    }
+                    btTriangleMesh.addTriangle(triPoints[0], triPoints[1], triPoints[2]);
+                    triangleCount++;
+                }
+                object.getChildMeshes().forEach(function (m) {
+                    triangleCount += _this._addMeshVerts(btTriangleMesh, topLevelObject, m);
+                });
+            }
+            return triangleCount;
+        };
+        AmmoJSPlugin.prototype._createShape = function (impostor, ignoreChildren) {
+            var _this = this;
+            if (ignoreChildren === void 0) { ignoreChildren = false; }
+            var object = impostor.object;
+            var returnValue;
+            var extendSize = impostor.getObjectExtendSize();
+            if (!ignoreChildren) {
+                var meshChildren = impostor.object.getChildMeshes ? impostor.object.getChildMeshes(true) : [];
+                if (meshChildren.length > 0) {
+                    returnValue = new Ammo.btCompoundShape();
+                    // Add shape of all children to the compound shape
+                    meshChildren.forEach(function (childMesh) {
+                        var childImpostor = childMesh.getPhysicsImpostor();
+                        if (childImpostor) {
+                            var shape = _this._createShape(childImpostor);
+                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+                            _this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion.x, childMesh.rotationQuaternion.y, childMesh.rotationQuaternion.z, childMesh.rotationQuaternion.w);
+                            _this._tmpAmmoTransform.setRotation(_this._tmpAmmoQuaternion);
+                            returnValue.addChildShape(_this._tmpAmmoTransform, shape);
+                            childImpostor.dispose();
+                        }
+                    });
+                    // Add parents shape as a child if present
+                    var shape = this._createShape(impostor, true);
+                    if (shape) {
+                        this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
+                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
+                        this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
+                        this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
+                        returnValue.addChildShape(this._tmpAmmoTransform, shape);
+                    }
+                    return returnValue;
+                }
+            }
+            switch (impostor.type) {
+                case BABYLON.PhysicsImpostor.SphereImpostor:
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
+                case BABYLON.PhysicsImpostor.CylinderImpostor:
+                    this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
+                    returnValue = new Ammo.btCylinderShape(this._tmpAmmoVectorA);
+                    break;
+                case BABYLON.PhysicsImpostor.PlaneImpostor:
+                case BABYLON.PhysicsImpostor.BoxImpostor:
+                    this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
+                    returnValue = new Ammo.btBoxShape(this._tmpAmmoVectorA);
+                    break;
+                case BABYLON.PhysicsImpostor.MeshImpostor:
+                    var tetraMesh = new Ammo.btTriangleMesh();
+                    impostor._pluginData.toDispose.concat([tetraMesh]);
+                    var triangeCount = this._addMeshVerts(tetraMesh, object, object);
+                    if (triangeCount == 0) {
+                        returnValue = new Ammo.btCompoundShape();
+                    }
+                    else {
+                        returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
+                    }
+                    break;
+            }
+            return returnValue;
+        };
+        /**
+         * Sets the physics body position/rotation from the babylon mesh's position/rotation
+         * @param impostor imposter containing the physics body and babylon object
+         */
+        AmmoJSPlugin.prototype.setTransformationFromPhysicsBody = function (impostor) {
+            impostor.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform);
+            impostor.object.position.set(this._tmpAmmoTransform.getOrigin().x(), this._tmpAmmoTransform.getOrigin().y(), this._tmpAmmoTransform.getOrigin().z());
+            if (!impostor.object.rotationQuaternion) {
+                if (impostor.object.rotation) {
+                    this._tmpQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
+                    this._tmpQuaternion.toEulerAnglesToRef(impostor.object.rotation);
+                }
+            }
+            else {
+                impostor.object.rotationQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
+            }
+        };
+        /**
+         * Sets the babylon object's position/rotation from the physics body's position/rotation
+         * @param impostor imposter containing the physics body and babylon object
+         * @param newPosition new position
+         * @param newRotation new rotation
+         */
+        AmmoJSPlugin.prototype.setPhysicsBodyTransformation = function (impostor, newPosition, newRotation) {
+            var trans = impostor.physicsBody.getWorldTransform();
+            // If rotation/position has changed update and activate riged body
+            if (trans.getOrigin().x() != newPosition.x ||
+                trans.getOrigin().y() != newPosition.y ||
+                trans.getOrigin().z() != newPosition.z ||
+                trans.getRotation().x() != newRotation.x ||
+                trans.getRotation().y() != newRotation.y ||
+                trans.getRotation().z() != newRotation.z ||
+                trans.getRotation().w() != newRotation.w) {
+                this._tmpAmmoVectorA.setValue(newPosition.x, newPosition.y, newPosition.z);
+                trans.setOrigin(this._tmpAmmoVectorA);
+                this._tmpAmmoQuaternion.setValue(newRotation.x, newRotation.y, newRotation.z, newRotation.w);
+                trans.setRotation(this._tmpAmmoQuaternion);
+                impostor.physicsBody.setWorldTransform(trans);
+                if (impostor.mass == 0) {
+                    // Kinematic objects must be updated using motion state
+                    var motionState = impostor.physicsBody.getMotionState();
+                    if (motionState) {
+                        motionState.setWorldTransform(trans);
+                    }
+                }
+                else {
+                    impostor.physicsBody.activate();
+                }
+            }
+        };
+        /**
+         * If this plugin is supported
+         * @returns true if its supported
+         */
+        AmmoJSPlugin.prototype.isSupported = function () {
+            return this.bjsAMMO !== undefined;
+        };
+        /**
+         * Sets the linear velocity of the physics body
+         * @param impostor imposter to set the velocity on
+         * @param velocity velocity to set
+         */
+        AmmoJSPlugin.prototype.setLinearVelocity = function (impostor, velocity) {
+            this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
+            impostor.physicsBody.setLinearVelocity(this._tmpAmmoVectorA);
+        };
+        /**
+         * Sets the angular velocity of the physics body
+         * @param impostor imposter to set the velocity on
+         * @param velocity velocity to set
+         */
+        AmmoJSPlugin.prototype.setAngularVelocity = function (impostor, velocity) {
+            this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
+            impostor.physicsBody.setAngularVelocity(this._tmpAmmoVectorA);
+        };
+        /**
+         * gets the linear velocity
+         * @param impostor imposter to get linear velocity from
+         * @returns linear velocity
+         */
+        AmmoJSPlugin.prototype.getLinearVelocity = function (impostor) {
+            var v = impostor.physicsBody.getLinearVelocity();
+            if (!v) {
+                return null;
+            }
+            return new BABYLON.Vector3(v.x(), v.y(), v.z());
+        };
+        /**
+         * gets the angular velocity
+         * @param impostor imposter to get angular velocity from
+         * @returns angular velocity
+         */
+        AmmoJSPlugin.prototype.getAngularVelocity = function (impostor) {
+            var v = impostor.physicsBody.getAngularVelocity();
+            if (!v) {
+                return null;
+            }
+            return new BABYLON.Vector3(v.x(), v.y(), v.z());
+        };
+        /**
+         * Sets the mass of physics body
+         * @param impostor imposter to set the mass on
+         * @param mass mass to set
+         */
+        AmmoJSPlugin.prototype.setBodyMass = function (impostor, mass) {
+            impostor.physicsBody.setMassProps(mass);
+            impostor._pluginData.mass = mass;
+        };
+        /**
+         * Gets the mass of the physics body
+         * @param impostor imposter to get the mass from
+         * @returns mass
+         */
+        AmmoJSPlugin.prototype.getBodyMass = function (impostor) {
+            return impostor._pluginData.mass;
+        };
+        /**
+         * Gets friction of the impostor
+         * @param impostor impostor to get friction from
+         * @returns friction value
+         */
+        AmmoJSPlugin.prototype.getBodyFriction = function (impostor) {
+            return impostor.physicsBody.getFriction();
+        };
+        /**
+         * Sets friction of the impostor
+         * @param impostor impostor to set friction on
+         * @param friction friction value
+         */
+        AmmoJSPlugin.prototype.setBodyFriction = function (impostor, friction) {
+            impostor.physicsBody.setFriction(friction);
+        };
+        /**
+         * Gets restitution of the impostor
+         * @param impostor impostor to get restitution from
+         * @returns restitution value
+         */
+        AmmoJSPlugin.prototype.getBodyRestitution = function (impostor) {
+            return impostor.physicsBody.getRestitution();
+        };
+        /**
+         * Sets resitution of the impostor
+         * @param impostor impostor to set resitution on
+         * @param restitution resitution value
+         */
+        AmmoJSPlugin.prototype.setBodyRestitution = function (impostor, restitution) {
+            impostor.physicsBody.setRestitution(restitution);
+        };
+        /**
+         * Sleeps the physics body and stops it from being active
+         * @param impostor impostor to sleep
+         */
+        AmmoJSPlugin.prototype.sleepBody = function (impostor) {
+            BABYLON.Tools.Warn("sleepBody is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Activates the physics body
+         * @param impostor impostor to activate
+         */
+        AmmoJSPlugin.prototype.wakeUpBody = function (impostor) {
+            impostor.physicsBody.activate();
+        };
+        /**
+         * Updates the distance parameters of the joint
+         * @param joint joint to update
+         * @param maxDistance maximum distance of the joint
+         * @param minDistance minimum distance of the joint
+         */
+        AmmoJSPlugin.prototype.updateDistanceJoint = function (joint, maxDistance, minDistance) {
+            BABYLON.Tools.Warn("updateDistanceJoint is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Sets a motor on the joint
+         * @param joint joint to set motor on
+         * @param speed speed of the motor
+         * @param maxForce maximum force of the motor
+         * @param motorIndex index of the motor
+         */
+        AmmoJSPlugin.prototype.setMotor = function (joint, speed, maxForce, motorIndex) {
+            BABYLON.Tools.Warn("setMotor is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Sets the motors limit
+         * @param joint joint to set limit on
+         * @param upperLimit upper limit
+         * @param lowerLimit lower limit
+         */
+        AmmoJSPlugin.prototype.setLimit = function (joint, upperLimit, lowerLimit) {
+            BABYLON.Tools.Warn("setLimit is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Syncs the position and rotation of a mesh with the impostor
+         * @param mesh mesh to sync
+         * @param impostor impostor to update the mesh with
+         */
+        AmmoJSPlugin.prototype.syncMeshWithImpostor = function (mesh, impostor) {
+            var body = impostor.physicsBody;
+            body.getMotionState().getWorldTransform(this._tmpAmmoTransform);
+            mesh.position.x = this._tmpAmmoTransform.getOrigin().x();
+            mesh.position.y = this._tmpAmmoTransform.getOrigin().y();
+            mesh.position.z = this._tmpAmmoTransform.getOrigin().z();
+            if (mesh.rotationQuaternion) {
+                mesh.rotationQuaternion.x = this._tmpAmmoTransform.getRotation().x();
+                mesh.rotationQuaternion.y = this._tmpAmmoTransform.getRotation().y();
+                mesh.rotationQuaternion.z = this._tmpAmmoTransform.getRotation().z();
+                mesh.rotationQuaternion.w = this._tmpAmmoTransform.getRotation().w();
+            }
+        };
+        /**
+         * Gets the radius of the impostor
+         * @param impostor impostor to get radius from
+         * @returns the radius
+         */
+        AmmoJSPlugin.prototype.getRadius = function (impostor) {
+            var exntend = impostor.getObjectExtendSize();
+            return exntend.x / 2;
+        };
+        /**
+         * Gets the box size of the impostor
+         * @param impostor impostor to get box size from
+         * @param result the resulting box size
+         */
+        AmmoJSPlugin.prototype.getBoxSizeToRef = function (impostor, result) {
+            var exntend = impostor.getObjectExtendSize();
+            result.x = exntend.x;
+            result.y = exntend.y;
+            result.z = exntend.z;
+        };
+        /**
+         * Disposes of the impostor
+         */
+        AmmoJSPlugin.prototype.dispose = function () {
+            // Dispose of world
+            Ammo.destroy(this.world);
+            Ammo.destroy(this._solver);
+            Ammo.destroy(this._overlappingPairCache);
+            Ammo.destroy(this._dispatcher);
+            Ammo.destroy(this._collisionConfiguration);
+            // Dispose of tmp variables
+            Ammo.destroy(this._tmpAmmoVectorA);
+            Ammo.destroy(this._tmpAmmoVectorB);
+            Ammo.destroy(this._tmpAmmoVectorC);
+            Ammo.destroy(this._tmpAmmoTransform);
+            Ammo.destroy(this._tmpAmmoQuaternion);
+            Ammo.destroy(this._tmpAmmoConcreteContactResultCallback);
+            this.world = null;
+        };
+        AmmoJSPlugin.KINEMATIC_FLAG = 2;
+        AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG = 4;
+        return AmmoJSPlugin;
+    }());
+    BABYLON.AmmoJSPlugin = AmmoJSPlugin;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.ammoJSPlugin.js.map
+
+var BABYLON;
+(function (BABYLON) {
     /** @hidden */
     var OimoJSPlugin = /** @class */ (function () {
         function OimoJSPlugin(iterations) {
@@ -101635,6 +102267,10 @@ var BABYLON;
          */
         KTXTextureLoader.prototype.transformUrl = function (rootUrl, textureFormatInUse) {
             var lastDot = rootUrl.lastIndexOf('.');
+            if (lastDot != -1 && rootUrl.substring(lastDot + 1) == "ktx") {
+                // Already transformed
+                return rootUrl;
+            }
             return (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + textureFormatInUse;
         };
         /**
@@ -101660,6 +102296,8 @@ var BABYLON;
             if (Array.isArray(data)) {
                 return;
             }
+            // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
+            texture._invertVScale = !texture.invertY;
             var engine = texture.getEngine();
             var ktx = new BABYLON.KhronosTextureContainer(data, 6);
             var loadMipmap = ktx.numberOfMipmapLevels > 1 && texture.generateMipMaps;
@@ -101677,6 +102315,8 @@ var BABYLON;
          * @param callback defines the method to call once ready to upload
          */
         KTXTextureLoader.prototype.loadData = function (data, texture, callback) {
+            // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
+            texture._invertVScale = !texture.invertY;
             var ktx = new BABYLON.KhronosTextureContainer(data, 1);
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, function () {
                 ktx.uploadLevels(texture, texture.generateMipMaps);

+ 645 - 5
dist/preview release/babylon.no-module.max.js

@@ -15692,7 +15692,7 @@ var BABYLON;
          * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
          * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
          * @param noMipmap defines a boolean indicating that no mipmaps shall be generated.  Ignored for compressed textures.  They must be in the file
-         * @param invertY when true, image is flipped when loaded.  You probably want true. Ignored for compressed textures.  Must be flipped in the file
+         * @param invertY when true, image is flipped when loaded.  You probably want true. Certain compressed textures may invert this if their default is inverted (eg. ktx)
          * @param scene needed for loading to the correct scene
          * @param samplingMode mode with should be used sample / access the texture (Default: BABYLON.Texture.TRILINEAR_SAMPLINGMODE)
          * @param onLoad optional callback to be called upon successful completion
@@ -15763,7 +15763,7 @@ var BABYLON;
                         customFallback = true;
                         excludeLoaders.push(loader);
                         BABYLON.Tools.Warn(loader.constructor.name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
-                        _this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
+                        _this.createTexture(urlArg, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                         return;
                     }
                 }
@@ -15772,7 +15772,7 @@ var BABYLON;
                         texture.onLoadedObservable.remove(onLoadObserver);
                     }
                     if (BABYLON.Tools.UseFallbackTexture) {
-                        _this.createTexture(BABYLON.Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        _this.createTexture(BABYLON.Tools.fallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture);
                         return;
                     }
                 }
@@ -15788,7 +15788,7 @@ var BABYLON;
                             onInternalError("TextureLoader failed to load data");
                         }
                         else {
-                            _this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, function () {
+                            _this._prepareWebGLTexture(texture, scene, width, height, texture.invertY, !loadMipmap, isCompressed, function () {
                                 done();
                                 return false;
                             }, samplingMode);
@@ -15811,7 +15811,7 @@ var BABYLON;
                         // in case of a webgl context lost
                         texture._buffer = img;
                     }
-                    _this._prepareWebGLTexture(texture, scene, img.width, img.height, invertY, noMipmap, false, function (potWidth, potHeight, continuationCallback) {
+                    _this._prepareWebGLTexture(texture, scene, img.width, img.height, texture.invertY, noMipmap, false, function (potWidth, potHeight, continuationCallback) {
                         var gl = _this._gl;
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         var internalFormat = format ? _this._getInternalFormat(format) : ((extension === ".jpg") ? gl.RGB : gl.RGBA);
@@ -31365,6 +31365,8 @@ var BABYLON;
             this.next = null;
             // Private
             /** @hidden */
+            this._invertVScale = false;
+            /** @hidden */
             this._initialSlot = -1;
             /** @hidden */
             this._designatedSlot = -1;
@@ -32447,6 +32449,9 @@ var BABYLON;
             }
             scene.getEngine().onBeforeTextureInitObservable.notifyObservers(_this);
             var load = function () {
+                if (_this._texture && _this._texture._invertVScale) {
+                    _this.vScale *= -1;
+                }
                 if (_this.onLoadObservable.hasObservers()) {
                     _this.onLoadObservable.notifyObservers(_this);
                 }
@@ -97694,6 +97699,7 @@ var BABYLON;
             this._bodyUpdateRequired = false;
             this._onBeforePhysicsStepCallbacks = new Array();
             this._onAfterPhysicsStepCallbacks = new Array();
+            /** @hidden */
             this._onPhysicsCollideCallbacks = [];
             this._deltaPosition = BABYLON.Vector3.Zero();
             this._isDisposed = false;
@@ -99851,6 +99857,632 @@ var BABYLON;
 
 var BABYLON;
 (function (BABYLON) {
+    /**
+     * AmmoJS Physics plugin
+     * @see https://doc.babylonjs.com/how_to/using_the_physics_engine
+     * @see https://github.com/kripken/ammo.js/
+     */
+    var AmmoJSPlugin = /** @class */ (function () {
+        /**
+         * Initializes the ammoJS plugin
+         * @param _useDeltaForWorldStep if the time between frames should be used when calculating physics steps (Default: true)
+         */
+        function AmmoJSPlugin(_useDeltaForWorldStep) {
+            if (_useDeltaForWorldStep === void 0) { _useDeltaForWorldStep = true; }
+            var _this = this;
+            this._useDeltaForWorldStep = _useDeltaForWorldStep;
+            /**
+             * Name of the plugin
+             */
+            this.name = "AmmoJSPlugin";
+            this._timeStep = 1 / 60;
+            this._maxSteps = 5;
+            this._tmpQuaternion = new BABYLON.Quaternion();
+            this._tmpContactCallbackResult = false;
+            if (typeof Ammo === "function") {
+                Ammo();
+            }
+            this.bjsAMMO = Ammo;
+            if (!this.isSupported()) {
+                BABYLON.Tools.Error("AmmoJS is not available. Please make sure you included the js file.");
+                return;
+            }
+            // Initialize the physics world
+            this._collisionConfiguration = new this.bjsAMMO.btDefaultCollisionConfiguration();
+            this._dispatcher = new this.bjsAMMO.btCollisionDispatcher(this._collisionConfiguration);
+            this._overlappingPairCache = new this.bjsAMMO.btDbvtBroadphase();
+            this._solver = new this.bjsAMMO.btSequentialImpulseConstraintSolver();
+            this.world = new this.bjsAMMO.btDiscreteDynamicsWorld(this._dispatcher, this._overlappingPairCache, this._solver, this._collisionConfiguration);
+            this._tmpAmmoConcreteContactResultCallback = new this.bjsAMMO.ConcreteContactResultCallback();
+            this._tmpAmmoConcreteContactResultCallback.addSingleResult = function () { _this._tmpContactCallbackResult = true; };
+            // Create temp ammo variables
+            this._tmpAmmoTransform = new this.bjsAMMO.btTransform();
+            this._tmpAmmoTransform.setIdentity();
+            this._tmpAmmoQuaternion = new this.bjsAMMO.btQuaternion(0, 0, 0, 1);
+            this._tmpAmmoVectorA = new this.bjsAMMO.btVector3(0, 0, 0);
+            this._tmpAmmoVectorB = new this.bjsAMMO.btVector3(0, 0, 0);
+            this._tmpAmmoVectorC = new this.bjsAMMO.btVector3(0, 0, 0);
+        }
+        /**
+         * Sets the gravity of the physics world (m/(s^2))
+         * @param gravity Gravity to set
+         */
+        AmmoJSPlugin.prototype.setGravity = function (gravity) {
+            this._tmpAmmoVectorA.setValue(gravity.x, gravity.y, gravity.z);
+            this.world.setGravity(this._tmpAmmoVectorA);
+        };
+        /**
+         * Amount of time to step forward on each frame (only used if useDeltaForWorldStep is false in the constructor)
+         * @param timeStep timestep to use in seconds
+         */
+        AmmoJSPlugin.prototype.setTimeStep = function (timeStep) {
+            this._timeStep = timeStep;
+        };
+        /**
+         * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
+         * @returns the current timestep in seconds
+         */
+        AmmoJSPlugin.prototype.getTimeStep = function () {
+            return this._timeStep;
+        };
+        // Ammo's contactTest and contactPairTest take a callback that runs synchronously, wrap them so that they are easier to consume
+        AmmoJSPlugin.prototype._isImpostorInContact = function (impostor) {
+            this._tmpContactCallbackResult = false;
+            this.world.contactTest(impostor.physicsBody, this._tmpAmmoConcreteContactResultCallback);
+            return this._tmpContactCallbackResult;
+        };
+        // Ammo's collision events have some weird quirks
+        // contactPairTest fires too many events as it fires events even when objects are close together but contactTest does not
+        // so only fire event if both contactTest and contactPairTest have a hit
+        AmmoJSPlugin.prototype._isImpostorPairInContact = function (impostorA, impostorB) {
+            this._tmpContactCallbackResult = false;
+            this.world.contactPairTest(impostorA.physicsBody, impostorB.physicsBody, this._tmpAmmoConcreteContactResultCallback);
+            return this._tmpContactCallbackResult;
+        };
+        // Ammo's behavior when maxSteps > 0 does not behave as described in docs
+        // @see http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
+        //
+        // When maxSteps is 0 do the entire simulation in one step
+        // When maxSteps is > 0, run up to maxStep times, if on the last step the (remaining step - fixedTimeStep) is < fixedTimeStep, the remainder will be used for the step. (eg. if remainder is 1.001 and fixedTimeStep is 1 the last step will be 1.001, if instead it did 2 steps (1, 0.001) issues occuered when having a tiny step in ammo)
+        // Note: To get deterministic physics, timeStep would always need to be divisible by fixedTimeStep
+        AmmoJSPlugin.prototype._stepSimulation = function (timeStep, maxSteps, fixedTimeStep) {
+            if (timeStep === void 0) { timeStep = 1 / 60; }
+            if (maxSteps === void 0) { maxSteps = 10; }
+            if (fixedTimeStep === void 0) { fixedTimeStep = 1 / 60; }
+            if (maxSteps == 0) {
+                this.world.stepSimulation(timeStep, 0);
+            }
+            else {
+                while (maxSteps > 0 && timeStep > 0) {
+                    if (timeStep - fixedTimeStep < fixedTimeStep) {
+                        this.world.stepSimulation(timeStep, 0);
+                        timeStep = 0;
+                    }
+                    else {
+                        timeStep -= fixedTimeStep;
+                        this.world.stepSimulation(fixedTimeStep, 0);
+                    }
+                    maxSteps--;
+                }
+            }
+        };
+        /**
+         * Moves the physics simulation forward delta seconds and updates the given physics imposters
+         * Prior to the step the imposters physics location is set to the position of the babylon meshes
+         * After the step the babylon meshes are set to the position of the physics imposters
+         * @param delta amount of time to step forward
+         * @param impostors array of imposters to update before/after the step
+         */
+        AmmoJSPlugin.prototype.executeStep = function (delta, impostors) {
+            for (var _i = 0, impostors_1 = impostors; _i < impostors_1.length; _i++) {
+                var impostor = impostors_1[_i];
+                // Update physics world objects to match babylon world
+                impostor.beforeStep();
+            }
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+            for (var _a = 0, impostors_2 = impostors; _a < impostors_2.length; _a++) {
+                var mainImpostor = impostors_2[_a];
+                // After physics update make babylon world objects match physics world objects
+                mainImpostor.afterStep();
+                // Handle collision event
+                if (mainImpostor._onPhysicsCollideCallbacks.length > 0) {
+                    if (this._isImpostorInContact(mainImpostor)) {
+                        for (var _b = 0, _c = mainImpostor._onPhysicsCollideCallbacks; _b < _c.length; _b++) {
+                            var collideCallback = _c[_b];
+                            for (var _d = 0, _e = collideCallback.otherImpostors; _d < _e.length; _d++) {
+                                var otherImpostor = _e[_d];
+                                if (mainImpostor.physicsBody.isActive() || otherImpostor.physicsBody.isActive()) {
+                                    if (this._isImpostorPairInContact(mainImpostor, otherImpostor)) {
+                                        mainImpostor.onCollide({ body: otherImpostor.physicsBody });
+                                        otherImpostor.onCollide({ body: mainImpostor.physicsBody });
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        };
+        /**
+         * Applies an implulse on the imposter
+         * @param impostor imposter to apply impulse
+         * @param force amount of force to be applied to the imposter
+         * @param contactPoint the location to apply the impulse on the imposter
+         */
+        AmmoJSPlugin.prototype.applyImpulse = function (impostor, force, contactPoint) {
+            var worldPoint = this._tmpAmmoVectorA;
+            var impulse = this._tmpAmmoVectorB;
+            worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
+            impulse.setValue(force.x, force.y, force.z);
+            impostor.physicsBody.applyImpulse(impulse, worldPoint);
+        };
+        /**
+         * Applies a force on the imposter
+         * @param impostor imposter to apply force
+         * @param force amount of force to be applied to the imposter
+         * @param contactPoint the location to apply the force on the imposter
+         */
+        AmmoJSPlugin.prototype.applyForce = function (impostor, force, contactPoint) {
+            var worldPoint = this._tmpAmmoVectorA;
+            var impulse = this._tmpAmmoVectorB;
+            worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
+            impulse.setValue(force.x, force.y, force.z);
+            impostor.physicsBody.applyForce(impulse, worldPoint);
+        };
+        /**
+         * Creates a physics body using the plugin
+         * @param impostor the imposter to create the physics body on
+         */
+        AmmoJSPlugin.prototype.generatePhysicsBody = function (impostor) {
+            impostor._pluginData = { toDispose: [] };
+            //parent-child relationship
+            if (impostor.parent) {
+                if (impostor.physicsBody) {
+                    this.removePhysicsBody(impostor);
+                    impostor.forceUpdate();
+                }
+                return;
+            }
+            if (impostor.isBodyInitRequired()) {
+                var colShape = this._createShape(impostor);
+                var mass = impostor.getParam("mass");
+                impostor._pluginData.mass = mass;
+                var localInertia = new Ammo.btVector3(0, 0, 0);
+                var startTransform = new Ammo.btTransform();
+                startTransform.setIdentity();
+                if (mass !== 0) {
+                    colShape.calculateLocalInertia(mass, localInertia);
+                }
+                this._tmpAmmoVectorA.setValue(impostor.object.position.x, impostor.object.position.y, impostor.object.position.z);
+                this._tmpAmmoQuaternion.setValue(impostor.object.rotationQuaternion.x, impostor.object.rotationQuaternion.y, impostor.object.rotationQuaternion.z, impostor.object.rotationQuaternion.w);
+                startTransform.setOrigin(this._tmpAmmoVectorA);
+                startTransform.setRotation(this._tmpAmmoQuaternion);
+                var myMotionState = new Ammo.btDefaultMotionState(startTransform);
+                var rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
+                var body = new Ammo.btRigidBody(rbInfo);
+                // Make objects kinematic if it's mass is 0
+                if (mass === 0) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
+                    body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
+                }
+                body.setRestitution(impostor.getParam("restitution"));
+                this.world.addRigidBody(body);
+                impostor.physicsBody = body;
+                impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
+            }
+        };
+        /**
+         * Removes the physics body from the imposter and disposes of the body's memory
+         * @param impostor imposter to remove the physics body from
+         */
+        AmmoJSPlugin.prototype.removePhysicsBody = function (impostor) {
+            var _this = this;
+            if (this.world) {
+                this.world.removeRigidBody(impostor.physicsBody);
+                impostor._pluginData.toDispose.forEach(function (d) {
+                    _this.bjsAMMO.destroy(d);
+                });
+            }
+        };
+        /**
+         * Generates a joint
+         * @param impostorJoint the imposter joint to create the joint with
+         */
+        AmmoJSPlugin.prototype.generateJoint = function (impostorJoint) {
+            var mainBody = impostorJoint.mainImpostor.physicsBody;
+            var connectedBody = impostorJoint.connectedImpostor.physicsBody;
+            if (!mainBody || !connectedBody) {
+                return;
+            }
+            var jointData = impostorJoint.joint.jointData;
+            if (!jointData.mainPivot) {
+                jointData.mainPivot = new BABYLON.Vector3(0, 0, 0);
+            }
+            if (!jointData.connectedPivot) {
+                jointData.connectedPivot = new BABYLON.Vector3(0, 0, 0);
+            }
+            var joint;
+            switch (impostorJoint.joint.type) {
+                case BABYLON.PhysicsJoint.BallAndSocketJoint:
+                    joint = new Ammo.btPoint2PointConstraint(mainBody, connectedBody, new Ammo.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new Ammo.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
+                    break;
+                default:
+                    BABYLON.Tools.Warn("JointType not currently supported by the Ammo plugin, falling back to PhysicsJoint.BallAndSocketJoint");
+                    joint = new Ammo.btPoint2PointConstraint(mainBody, connectedBody, new Ammo.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new Ammo.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
+                    break;
+            }
+            this.world.addConstraint(joint, true);
+            impostorJoint.joint.physicsJoint = joint;
+        };
+        /**
+         * Removes a joint
+         * @param impostorJoint the imposter joint to remove the joint from
+         */
+        AmmoJSPlugin.prototype.removeJoint = function (impostorJoint) {
+            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+        };
+        // adds all verticies (including child verticies) to the triangle mesh
+        AmmoJSPlugin.prototype._addMeshVerts = function (btTriangleMesh, topLevelObject, object) {
+            var _this = this;
+            var triangleCount = 0;
+            if (object && object.getIndices && object.getWorldMatrix && object.getChildMeshes) {
+                var indices = object.getIndices();
+                if (!indices) {
+                    indices = [];
+                }
+                var vertexPositions = object.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+                if (!vertexPositions) {
+                    vertexPositions = [];
+                }
+                object.computeWorldMatrix(false);
+                var faceCount = indices.length / 3;
+                for (var i = 0; i < faceCount; i++) {
+                    var triPoints = [];
+                    for (var point = 0; point < 3; point++) {
+                        var v = new BABYLON.Vector3(vertexPositions[(indices[(i * 3) + point] * 3) + 0], vertexPositions[(indices[(i * 3) + point] * 3) + 1], vertexPositions[(indices[(i * 3) + point] * 3) + 2]);
+                        v = BABYLON.Vector3.TransformCoordinates(v, object.getWorldMatrix());
+                        v.subtractInPlace(topLevelObject.position);
+                        var vec;
+                        if (point == 0) {
+                            vec = this._tmpAmmoVectorA;
+                        }
+                        else if (point == 1) {
+                            vec = this._tmpAmmoVectorB;
+                        }
+                        else {
+                            vec = this._tmpAmmoVectorC;
+                        }
+                        vec.setValue(v.x, v.y, v.z);
+                        triPoints.push(vec);
+                    }
+                    btTriangleMesh.addTriangle(triPoints[0], triPoints[1], triPoints[2]);
+                    triangleCount++;
+                }
+                object.getChildMeshes().forEach(function (m) {
+                    triangleCount += _this._addMeshVerts(btTriangleMesh, topLevelObject, m);
+                });
+            }
+            return triangleCount;
+        };
+        AmmoJSPlugin.prototype._createShape = function (impostor, ignoreChildren) {
+            var _this = this;
+            if (ignoreChildren === void 0) { ignoreChildren = false; }
+            var object = impostor.object;
+            var returnValue;
+            var extendSize = impostor.getObjectExtendSize();
+            if (!ignoreChildren) {
+                var meshChildren = impostor.object.getChildMeshes ? impostor.object.getChildMeshes(true) : [];
+                if (meshChildren.length > 0) {
+                    returnValue = new Ammo.btCompoundShape();
+                    // Add shape of all children to the compound shape
+                    meshChildren.forEach(function (childMesh) {
+                        var childImpostor = childMesh.getPhysicsImpostor();
+                        if (childImpostor) {
+                            var shape = _this._createShape(childImpostor);
+                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+                            _this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion.x, childMesh.rotationQuaternion.y, childMesh.rotationQuaternion.z, childMesh.rotationQuaternion.w);
+                            _this._tmpAmmoTransform.setRotation(_this._tmpAmmoQuaternion);
+                            returnValue.addChildShape(_this._tmpAmmoTransform, shape);
+                            childImpostor.dispose();
+                        }
+                    });
+                    // Add parents shape as a child if present
+                    var shape = this._createShape(impostor, true);
+                    if (shape) {
+                        this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
+                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
+                        this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
+                        this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
+                        returnValue.addChildShape(this._tmpAmmoTransform, shape);
+                    }
+                    return returnValue;
+                }
+            }
+            switch (impostor.type) {
+                case BABYLON.PhysicsImpostor.SphereImpostor:
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
+                case BABYLON.PhysicsImpostor.CylinderImpostor:
+                    this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
+                    returnValue = new Ammo.btCylinderShape(this._tmpAmmoVectorA);
+                    break;
+                case BABYLON.PhysicsImpostor.PlaneImpostor:
+                case BABYLON.PhysicsImpostor.BoxImpostor:
+                    this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
+                    returnValue = new Ammo.btBoxShape(this._tmpAmmoVectorA);
+                    break;
+                case BABYLON.PhysicsImpostor.MeshImpostor:
+                    var tetraMesh = new Ammo.btTriangleMesh();
+                    impostor._pluginData.toDispose.concat([tetraMesh]);
+                    var triangeCount = this._addMeshVerts(tetraMesh, object, object);
+                    if (triangeCount == 0) {
+                        returnValue = new Ammo.btCompoundShape();
+                    }
+                    else {
+                        returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
+                    }
+                    break;
+            }
+            return returnValue;
+        };
+        /**
+         * Sets the physics body position/rotation from the babylon mesh's position/rotation
+         * @param impostor imposter containing the physics body and babylon object
+         */
+        AmmoJSPlugin.prototype.setTransformationFromPhysicsBody = function (impostor) {
+            impostor.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform);
+            impostor.object.position.set(this._tmpAmmoTransform.getOrigin().x(), this._tmpAmmoTransform.getOrigin().y(), this._tmpAmmoTransform.getOrigin().z());
+            if (!impostor.object.rotationQuaternion) {
+                if (impostor.object.rotation) {
+                    this._tmpQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
+                    this._tmpQuaternion.toEulerAnglesToRef(impostor.object.rotation);
+                }
+            }
+            else {
+                impostor.object.rotationQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
+            }
+        };
+        /**
+         * Sets the babylon object's position/rotation from the physics body's position/rotation
+         * @param impostor imposter containing the physics body and babylon object
+         * @param newPosition new position
+         * @param newRotation new rotation
+         */
+        AmmoJSPlugin.prototype.setPhysicsBodyTransformation = function (impostor, newPosition, newRotation) {
+            var trans = impostor.physicsBody.getWorldTransform();
+            // If rotation/position has changed update and activate riged body
+            if (trans.getOrigin().x() != newPosition.x ||
+                trans.getOrigin().y() != newPosition.y ||
+                trans.getOrigin().z() != newPosition.z ||
+                trans.getRotation().x() != newRotation.x ||
+                trans.getRotation().y() != newRotation.y ||
+                trans.getRotation().z() != newRotation.z ||
+                trans.getRotation().w() != newRotation.w) {
+                this._tmpAmmoVectorA.setValue(newPosition.x, newPosition.y, newPosition.z);
+                trans.setOrigin(this._tmpAmmoVectorA);
+                this._tmpAmmoQuaternion.setValue(newRotation.x, newRotation.y, newRotation.z, newRotation.w);
+                trans.setRotation(this._tmpAmmoQuaternion);
+                impostor.physicsBody.setWorldTransform(trans);
+                if (impostor.mass == 0) {
+                    // Kinematic objects must be updated using motion state
+                    var motionState = impostor.physicsBody.getMotionState();
+                    if (motionState) {
+                        motionState.setWorldTransform(trans);
+                    }
+                }
+                else {
+                    impostor.physicsBody.activate();
+                }
+            }
+        };
+        /**
+         * If this plugin is supported
+         * @returns true if its supported
+         */
+        AmmoJSPlugin.prototype.isSupported = function () {
+            return this.bjsAMMO !== undefined;
+        };
+        /**
+         * Sets the linear velocity of the physics body
+         * @param impostor imposter to set the velocity on
+         * @param velocity velocity to set
+         */
+        AmmoJSPlugin.prototype.setLinearVelocity = function (impostor, velocity) {
+            this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
+            impostor.physicsBody.setLinearVelocity(this._tmpAmmoVectorA);
+        };
+        /**
+         * Sets the angular velocity of the physics body
+         * @param impostor imposter to set the velocity on
+         * @param velocity velocity to set
+         */
+        AmmoJSPlugin.prototype.setAngularVelocity = function (impostor, velocity) {
+            this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
+            impostor.physicsBody.setAngularVelocity(this._tmpAmmoVectorA);
+        };
+        /**
+         * gets the linear velocity
+         * @param impostor imposter to get linear velocity from
+         * @returns linear velocity
+         */
+        AmmoJSPlugin.prototype.getLinearVelocity = function (impostor) {
+            var v = impostor.physicsBody.getLinearVelocity();
+            if (!v) {
+                return null;
+            }
+            return new BABYLON.Vector3(v.x(), v.y(), v.z());
+        };
+        /**
+         * gets the angular velocity
+         * @param impostor imposter to get angular velocity from
+         * @returns angular velocity
+         */
+        AmmoJSPlugin.prototype.getAngularVelocity = function (impostor) {
+            var v = impostor.physicsBody.getAngularVelocity();
+            if (!v) {
+                return null;
+            }
+            return new BABYLON.Vector3(v.x(), v.y(), v.z());
+        };
+        /**
+         * Sets the mass of physics body
+         * @param impostor imposter to set the mass on
+         * @param mass mass to set
+         */
+        AmmoJSPlugin.prototype.setBodyMass = function (impostor, mass) {
+            impostor.physicsBody.setMassProps(mass);
+            impostor._pluginData.mass = mass;
+        };
+        /**
+         * Gets the mass of the physics body
+         * @param impostor imposter to get the mass from
+         * @returns mass
+         */
+        AmmoJSPlugin.prototype.getBodyMass = function (impostor) {
+            return impostor._pluginData.mass;
+        };
+        /**
+         * Gets friction of the impostor
+         * @param impostor impostor to get friction from
+         * @returns friction value
+         */
+        AmmoJSPlugin.prototype.getBodyFriction = function (impostor) {
+            return impostor.physicsBody.getFriction();
+        };
+        /**
+         * Sets friction of the impostor
+         * @param impostor impostor to set friction on
+         * @param friction friction value
+         */
+        AmmoJSPlugin.prototype.setBodyFriction = function (impostor, friction) {
+            impostor.physicsBody.setFriction(friction);
+        };
+        /**
+         * Gets restitution of the impostor
+         * @param impostor impostor to get restitution from
+         * @returns restitution value
+         */
+        AmmoJSPlugin.prototype.getBodyRestitution = function (impostor) {
+            return impostor.physicsBody.getRestitution();
+        };
+        /**
+         * Sets resitution of the impostor
+         * @param impostor impostor to set resitution on
+         * @param restitution resitution value
+         */
+        AmmoJSPlugin.prototype.setBodyRestitution = function (impostor, restitution) {
+            impostor.physicsBody.setRestitution(restitution);
+        };
+        /**
+         * Sleeps the physics body and stops it from being active
+         * @param impostor impostor to sleep
+         */
+        AmmoJSPlugin.prototype.sleepBody = function (impostor) {
+            BABYLON.Tools.Warn("sleepBody is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Activates the physics body
+         * @param impostor impostor to activate
+         */
+        AmmoJSPlugin.prototype.wakeUpBody = function (impostor) {
+            impostor.physicsBody.activate();
+        };
+        /**
+         * Updates the distance parameters of the joint
+         * @param joint joint to update
+         * @param maxDistance maximum distance of the joint
+         * @param minDistance minimum distance of the joint
+         */
+        AmmoJSPlugin.prototype.updateDistanceJoint = function (joint, maxDistance, minDistance) {
+            BABYLON.Tools.Warn("updateDistanceJoint is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Sets a motor on the joint
+         * @param joint joint to set motor on
+         * @param speed speed of the motor
+         * @param maxForce maximum force of the motor
+         * @param motorIndex index of the motor
+         */
+        AmmoJSPlugin.prototype.setMotor = function (joint, speed, maxForce, motorIndex) {
+            BABYLON.Tools.Warn("setMotor is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Sets the motors limit
+         * @param joint joint to set limit on
+         * @param upperLimit upper limit
+         * @param lowerLimit lower limit
+         */
+        AmmoJSPlugin.prototype.setLimit = function (joint, upperLimit, lowerLimit) {
+            BABYLON.Tools.Warn("setLimit is not currently supported by the Ammo physics plugin");
+        };
+        /**
+         * Syncs the position and rotation of a mesh with the impostor
+         * @param mesh mesh to sync
+         * @param impostor impostor to update the mesh with
+         */
+        AmmoJSPlugin.prototype.syncMeshWithImpostor = function (mesh, impostor) {
+            var body = impostor.physicsBody;
+            body.getMotionState().getWorldTransform(this._tmpAmmoTransform);
+            mesh.position.x = this._tmpAmmoTransform.getOrigin().x();
+            mesh.position.y = this._tmpAmmoTransform.getOrigin().y();
+            mesh.position.z = this._tmpAmmoTransform.getOrigin().z();
+            if (mesh.rotationQuaternion) {
+                mesh.rotationQuaternion.x = this._tmpAmmoTransform.getRotation().x();
+                mesh.rotationQuaternion.y = this._tmpAmmoTransform.getRotation().y();
+                mesh.rotationQuaternion.z = this._tmpAmmoTransform.getRotation().z();
+                mesh.rotationQuaternion.w = this._tmpAmmoTransform.getRotation().w();
+            }
+        };
+        /**
+         * Gets the radius of the impostor
+         * @param impostor impostor to get radius from
+         * @returns the radius
+         */
+        AmmoJSPlugin.prototype.getRadius = function (impostor) {
+            var exntend = impostor.getObjectExtendSize();
+            return exntend.x / 2;
+        };
+        /**
+         * Gets the box size of the impostor
+         * @param impostor impostor to get box size from
+         * @param result the resulting box size
+         */
+        AmmoJSPlugin.prototype.getBoxSizeToRef = function (impostor, result) {
+            var exntend = impostor.getObjectExtendSize();
+            result.x = exntend.x;
+            result.y = exntend.y;
+            result.z = exntend.z;
+        };
+        /**
+         * Disposes of the impostor
+         */
+        AmmoJSPlugin.prototype.dispose = function () {
+            // Dispose of world
+            Ammo.destroy(this.world);
+            Ammo.destroy(this._solver);
+            Ammo.destroy(this._overlappingPairCache);
+            Ammo.destroy(this._dispatcher);
+            Ammo.destroy(this._collisionConfiguration);
+            // Dispose of tmp variables
+            Ammo.destroy(this._tmpAmmoVectorA);
+            Ammo.destroy(this._tmpAmmoVectorB);
+            Ammo.destroy(this._tmpAmmoVectorC);
+            Ammo.destroy(this._tmpAmmoTransform);
+            Ammo.destroy(this._tmpAmmoQuaternion);
+            Ammo.destroy(this._tmpAmmoConcreteContactResultCallback);
+            this.world = null;
+        };
+        AmmoJSPlugin.KINEMATIC_FLAG = 2;
+        AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG = 4;
+        return AmmoJSPlugin;
+    }());
+    BABYLON.AmmoJSPlugin = AmmoJSPlugin;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.ammoJSPlugin.js.map
+
+var BABYLON;
+(function (BABYLON) {
     /** @hidden */
     var OimoJSPlugin = /** @class */ (function () {
         function OimoJSPlugin(iterations) {
@@ -101602,6 +102234,10 @@ var BABYLON;
          */
         KTXTextureLoader.prototype.transformUrl = function (rootUrl, textureFormatInUse) {
             var lastDot = rootUrl.lastIndexOf('.');
+            if (lastDot != -1 && rootUrl.substring(lastDot + 1) == "ktx") {
+                // Already transformed
+                return rootUrl;
+            }
             return (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + textureFormatInUse;
         };
         /**
@@ -101627,6 +102263,8 @@ var BABYLON;
             if (Array.isArray(data)) {
                 return;
             }
+            // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
+            texture._invertVScale = !texture.invertY;
             var engine = texture.getEngine();
             var ktx = new BABYLON.KhronosTextureContainer(data, 6);
             var loadMipmap = ktx.numberOfMipmapLevels > 1 && texture.generateMipMaps;
@@ -101644,6 +102282,8 @@ var BABYLON;
          * @param callback defines the method to call once ready to upload
          */
         KTXTextureLoader.prototype.loadData = function (data, texture, callback) {
+            // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
+            texture._invertVScale = !texture.invertY;
             var ktx = new BABYLON.KhronosTextureContainer(data, 1);
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, function () {
                 ktx.uploadLevels(texture, texture.generateMipMaps);

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/babylon.worker.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 647 - 7
dist/preview release/es6.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 2 - 0
dist/preview release/what's new.md

@@ -71,7 +71,9 @@
 - Added support for overriding the mesh used for the world matrix for a mesh with a skeleton ([bghgary](https://github.com/bghgary))
 - Added support for linking a bone to a transform node ([bghgary](https://github.com/bghgary))
 - Factored out `setDirection` function from `lookAt` for transform node ([bghgary](https://github.com/bghgary))
+- Added support for AmmoJS physics plugin ([TrevorDev](https://github.com/TrevorDev))
 - Add support for setting renderingGroupId and creating instances to `AxesViewer` ([bghgary](https://github.com/bghgary))
+- Invert vScale of compressed ktx textures as they are inverted in the file and UNPACK_FLIP_Y_WEBGL is not supported by ktx ([TrevorDev](https://github.com/TrevorDev))
 
 ### glTF Loader
 

+ 1 - 0
localDev/index.html

@@ -8,6 +8,7 @@
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <script src="../dist/preview%20release/cannon.js"></script>
     <script src="../dist/preview%20release/Oimo.js"></script>
+    <script src="../dist/preview%20release/ammo.js"></script>
     <script src="../dist/preview%20release/gltf_validator.js"></script>
     <script src="../Tools/DevLoader/BabylonLoader.js"></script>
     <script src="src/webgl-debug.js"></script>

+ 1 - 0
sandbox/index-local.html

@@ -5,6 +5,7 @@
     <title>BabylonJS - Sandbox</title>
     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
     <link href="index.css" rel="stylesheet" />
+    <script src="../dist/preview%20release/ammo.js"></script>
     <script src="../dist/preview%20release/cannon.js"></script>
     <script src="../dist/preview%20release/Oimo.js"></script>
     <script src="../dist/preview%20release/gltf_validator.js"></script>

+ 1 - 0
sandbox/index.html

@@ -31,6 +31,7 @@
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://playground.babylonjs.com/js/libs/split.js"></script>
 
+    <script src="https://preview.babylonjs.com/ammo.js"></script>
     <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/gltf_validator.js"></script>

+ 5 - 5
src/Engines/engine.ts

@@ -4267,7 +4267,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
          * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
          * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
          * @param noMipmap defines a boolean indicating that no mipmaps shall be generated.  Ignored for compressed textures.  They must be in the file
-         * @param invertY when true, image is flipped when loaded.  You probably want true. Ignored for compressed textures.  Must be flipped in the file
+         * @param invertY when true, image is flipped when loaded.  You probably want true. Certain compressed textures may invert this if their default is inverted (eg. ktx)
          * @param scene needed for loading to the correct scene
          * @param samplingMode mode with should be used sample / access the texture (Default: Texture.TRILINEAR_SAMPLINGMODE)
          * @param onLoad optional callback to be called upon successful completion
@@ -4339,7 +4339,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
                         customFallback = true;
                         excludeLoaders.push(loader);
                         Logger.Warn((loader.constructor as any).name + " failed when trying to load " + texture.url + ", falling back to the next supported loader");
-                        this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
+                        this.createTexture(urlArg, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture, undefined, undefined, excludeLoaders);
                         return;
                     }
                 }
@@ -4349,7 +4349,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
                         texture.onLoadedObservable.remove(onLoadObserver);
                     }
                     if (Tools.UseFallbackTexture) {
-                        this.createTexture(Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                        this.createTexture(Tools.fallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture);
                         return;
                     }
                 }
@@ -4366,7 +4366,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
                         if (loadFailed) {
                             onInternalError("TextureLoader failed to load data");
                         } else {
-                            this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
+                            this._prepareWebGLTexture(texture, scene, width, height, texture.invertY, !loadMipmap, isCompressed, () => {
                                 done();
                                 return false;
                             }, samplingMode);
@@ -4389,7 +4389,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
                         texture._buffer = img;
                     }
 
-                    this._prepareWebGLTexture(texture, scene, img.width, img.height, invertY, noMipmap, false, (potWidth, potHeight, continuationCallback) => {
+                    this._prepareWebGLTexture(texture, scene, img.width, img.height, texture.invertY, noMipmap, false, (potWidth, potHeight, continuationCallback) => {
                         let gl = this._gl;
                         var isPot = (img.width === potWidth && img.height === potHeight);
                         let internalFormat = format ? this._getInternalFormat(format) : ((extension === ".jpg") ? gl.RGB : gl.RGBA);

+ 8 - 0
src/Materials/Textures/Loaders/ktxTextureLoader.ts

@@ -39,6 +39,10 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "States";
          */
         public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
             var lastDot = rootUrl.lastIndexOf('.');
+            if (lastDot != -1 && rootUrl.substring(lastDot + 1) == "ktx") {
+                // Already transformed
+                return rootUrl;
+            }
             return (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + textureFormatInUse;
         }
 
@@ -67,6 +71,8 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "States";
                 return;
             }
 
+            // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
+            texture._invertVScale = !texture.invertY;
             var engine = texture.getEngine();
             var ktx = new KhronosTextureContainer(data, 6);
 
@@ -91,6 +97,8 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "States";
          */
         public loadData(data: ArrayBuffer, texture: InternalTexture,
             callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed: boolean) => void): void {
+            // Need to invert vScale as invertY via UNPACK_FLIP_Y_WEBGL is not supported by compressed texture
+            texture._invertVScale = !texture.invertY;
             var ktx = new KhronosTextureContainer(data, 1);
 
             callback(ktx.pixelWidth, ktx.pixelHeight, false, true, () => {

+ 2 - 0
src/Materials/Textures/internalTexture.ts

@@ -153,6 +153,8 @@ declare type BaseTexture = import("Materials/Textures/baseTexture").BaseTexture;
 
         // Private
         /** @hidden */
+        public _invertVScale = false;
+        /** @hidden */
         public _initialSlot = -1;
         /** @hidden */
         public _designatedSlot = -1;

+ 3 - 0
src/Materials/Textures/texture.ts

@@ -278,6 +278,9 @@ declare type RenderTargetTexture = import ("Materials/Textures/renderTargetTextu
             scene.getEngine().onBeforeTextureInitObservable.notifyObservers(this);
 
             let load = () => {
+                if (this._texture && this._texture._invertVScale) {
+                    this.vScale *= -1;
+                }
                 if (this.onLoadObservable.hasObservers()) {
                     this.onLoadObservable.notifyObservers(this);
                 }

+ 694 - 0
src/Physics/Plugins/babylon.ammoJSPlugin.ts

@@ -0,0 +1,694 @@
+module BABYLON {
+    declare var Ammo: any;
+
+    /**
+     * AmmoJS Physics plugin
+     * @see https://doc.babylonjs.com/how_to/using_the_physics_engine
+     * @see https://github.com/kripken/ammo.js/
+     */
+    export class AmmoJSPlugin implements IPhysicsEnginePlugin {
+        /**
+         * Reference to the Ammo library
+         */
+        public bjsAMMO: any;
+        /**
+         * Created ammoJS world which physics bodies are added to
+         */
+        public world: any;
+        /**
+         * Name of the plugin
+         */
+        public name: string = "AmmoJSPlugin";
+
+        private _timeStep: number = 1 / 60;
+        private _maxSteps = 5;
+        private _tmpQuaternion = new BABYLON.Quaternion();
+        private _tmpAmmoTransform: any;
+        private _tmpAmmoQuaternion: any;
+        private _tmpAmmoConcreteContactResultCallback: any;
+        private _collisionConfiguration: any;
+        private _dispatcher: any;
+        private _overlappingPairCache: any;
+        private _solver: any;
+        private _tmpAmmoVectorA: any;
+        private _tmpAmmoVectorB: any;
+        private _tmpAmmoVectorC: any;
+        private _tmpContactCallbackResult = false;
+
+        private static readonly KINEMATIC_FLAG = 2;
+        private static readonly DISABLE_DEACTIVATION_FLAG = 4;
+
+        /**
+         * Initializes the ammoJS plugin
+         * @param _useDeltaForWorldStep if the time between frames should be used when calculating physics steps (Default: true)
+         */
+        public constructor(private _useDeltaForWorldStep: boolean = true) {
+            if (typeof Ammo === "function") {
+                Ammo();
+            }
+            this.bjsAMMO = Ammo;
+            if (!this.isSupported()) {
+                Tools.Error("AmmoJS is not available. Please make sure you included the js file.");
+                return;
+            }
+
+            // Initialize the physics world
+            this._collisionConfiguration  = new this.bjsAMMO.btDefaultCollisionConfiguration();
+            this._dispatcher              = new this.bjsAMMO.btCollisionDispatcher(this._collisionConfiguration);
+            this._overlappingPairCache    = new this.bjsAMMO.btDbvtBroadphase();
+            this._solver                  = new this.bjsAMMO.btSequentialImpulseConstraintSolver();
+            this.world           = new this.bjsAMMO.btDiscreteDynamicsWorld(this._dispatcher, this._overlappingPairCache, this._solver, this._collisionConfiguration);
+            this._tmpAmmoConcreteContactResultCallback = new this.bjsAMMO.ConcreteContactResultCallback();
+            this._tmpAmmoConcreteContactResultCallback.addSingleResult = () => { this._tmpContactCallbackResult = true; };
+
+            // Create temp ammo variables
+            this._tmpAmmoTransform = new this.bjsAMMO.btTransform();
+            this._tmpAmmoTransform.setIdentity();
+            this._tmpAmmoQuaternion = new this.bjsAMMO.btQuaternion(0, 0, 0, 1);
+            this._tmpAmmoVectorA = new this.bjsAMMO.btVector3(0, 0, 0);
+            this._tmpAmmoVectorB = new this.bjsAMMO.btVector3(0, 0, 0);
+            this._tmpAmmoVectorC = new this.bjsAMMO.btVector3(0, 0, 0);
+        }
+
+        /**
+         * Sets the gravity of the physics world (m/(s^2))
+         * @param gravity Gravity to set
+         */
+        public setGravity(gravity: Vector3): void {
+            this._tmpAmmoVectorA.setValue(gravity.x, gravity.y, gravity.z);
+            this.world.setGravity(this._tmpAmmoVectorA);
+        }
+
+        /**
+         * Amount of time to step forward on each frame (only used if useDeltaForWorldStep is false in the constructor)
+         * @param timeStep timestep to use in seconds
+         */
+        public setTimeStep(timeStep: number) {
+            this._timeStep = timeStep;
+        }
+
+        /**
+         * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
+         * @returns the current timestep in seconds
+         */
+        public getTimeStep(): number {
+            return this._timeStep;
+        }
+
+        // Ammo's contactTest and contactPairTest take a callback that runs synchronously, wrap them so that they are easier to consume
+        private _isImpostorInContact(impostor: PhysicsImpostor) {
+            this._tmpContactCallbackResult = false;
+            this.world.contactTest(impostor.physicsBody, this._tmpAmmoConcreteContactResultCallback);
+            return this._tmpContactCallbackResult;
+        }
+        // Ammo's collision events have some weird quirks
+        // contactPairTest fires too many events as it fires events even when objects are close together but contactTest does not
+        // so only fire event if both contactTest and contactPairTest have a hit
+        private _isImpostorPairInContact(impostorA: PhysicsImpostor, impostorB: PhysicsImpostor) {
+            this._tmpContactCallbackResult = false;
+            this.world.contactPairTest(impostorA.physicsBody, impostorB.physicsBody, this._tmpAmmoConcreteContactResultCallback);
+            return this._tmpContactCallbackResult;
+        }
+
+        // Ammo's behavior when maxSteps > 0 does not behave as described in docs
+        // @see http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
+        //
+        // When maxSteps is 0 do the entire simulation in one step
+        // When maxSteps is > 0, run up to maxStep times, if on the last step the (remaining step - fixedTimeStep) is < fixedTimeStep, the remainder will be used for the step. (eg. if remainder is 1.001 and fixedTimeStep is 1 the last step will be 1.001, if instead it did 2 steps (1, 0.001) issues occuered when having a tiny step in ammo)
+        // Note: To get deterministic physics, timeStep would always need to be divisible by fixedTimeStep
+        private _stepSimulation(timeStep: number = 1 / 60, maxSteps: number = 10, fixedTimeStep: number = 1 / 60) {
+            if (maxSteps == 0) {
+                this.world.stepSimulation(timeStep, 0);
+            }else {
+                while (maxSteps > 0 && timeStep > 0) {
+                    if (timeStep - fixedTimeStep <  fixedTimeStep) {
+                        this.world.stepSimulation(timeStep, 0);
+                        timeStep = 0;
+                    }else {
+                        timeStep -= fixedTimeStep;
+                        this.world.stepSimulation(fixedTimeStep, 0);
+                    }
+                    maxSteps--;
+                }
+            }
+        }
+
+        /**
+         * Moves the physics simulation forward delta seconds and updates the given physics imposters
+         * Prior to the step the imposters physics location is set to the position of the babylon meshes
+         * After the step the babylon meshes are set to the position of the physics imposters
+         * @param delta amount of time to step forward
+         * @param impostors array of imposters to update before/after the step
+         */
+        public executeStep(delta: number, impostors: Array<PhysicsImpostor>): void {
+            for (var impostor of impostors) {
+                // Update physics world objects to match babylon world
+                impostor.beforeStep();
+            }
+
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+
+            for (var mainImpostor of impostors) {
+                // After physics update make babylon world objects match physics world objects
+                mainImpostor.afterStep();
+
+                // Handle collision event
+                if (mainImpostor._onPhysicsCollideCallbacks.length > 0) {
+                    if (this._isImpostorInContact(mainImpostor)) {
+                        for (var collideCallback of mainImpostor._onPhysicsCollideCallbacks) {
+                            for (var otherImpostor of  collideCallback.otherImpostors) {
+                                if (mainImpostor.physicsBody.isActive() || otherImpostor.physicsBody.isActive()) {
+                                    if (this._isImpostorPairInContact(mainImpostor, otherImpostor)) {
+                                        mainImpostor.onCollide({ body: otherImpostor.physicsBody });
+                                        otherImpostor.onCollide({ body: mainImpostor.physicsBody });
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Applies an implulse on the imposter
+         * @param impostor imposter to apply impulse
+         * @param force amount of force to be applied to the imposter
+         * @param contactPoint the location to apply the impulse on the imposter
+         */
+        public applyImpulse(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
+            var worldPoint = this._tmpAmmoVectorA;
+            var impulse = this._tmpAmmoVectorB;
+            worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
+            impulse.setValue(force.x, force.y, force.z);
+
+            impostor.physicsBody.applyImpulse(impulse, worldPoint);
+        }
+
+        /**
+         * Applies a force on the imposter
+         * @param impostor imposter to apply force
+         * @param force amount of force to be applied to the imposter
+         * @param contactPoint the location to apply the force on the imposter
+         */
+        public applyForce(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
+            var worldPoint = this._tmpAmmoVectorA;
+            var impulse = this._tmpAmmoVectorB;
+            worldPoint.setValue(contactPoint.x, contactPoint.y, contactPoint.z);
+            impulse.setValue(force.x, force.y, force.z);
+
+            impostor.physicsBody.applyForce(impulse, worldPoint);
+        }
+
+        /**
+         * Creates a physics body using the plugin
+         * @param impostor the imposter to create the physics body on
+         */
+        public generatePhysicsBody(impostor: PhysicsImpostor) {
+            impostor._pluginData = {toDispose: []};
+
+            //parent-child relationship
+            if (impostor.parent) {
+                if (impostor.physicsBody) {
+                    this.removePhysicsBody(impostor);
+                    impostor.forceUpdate();
+                }
+                return;
+            }
+
+            if (impostor.isBodyInitRequired()) {
+                var colShape = this._createShape(impostor);
+                var mass          = impostor.getParam("mass");
+                impostor._pluginData.mass = mass;
+                var localInertia  = new Ammo.btVector3(0, 0, 0);
+                var startTransform  = new Ammo.btTransform();
+                startTransform.setIdentity();
+                if (mass !== 0) {
+                    colShape.calculateLocalInertia(mass, localInertia);
+                }
+                this._tmpAmmoVectorA.setValue(impostor.object.position.x, impostor.object.position.y, impostor.object.position.z);
+                this._tmpAmmoQuaternion.setValue(impostor.object.rotationQuaternion!.x, impostor.object.rotationQuaternion!.y, impostor.object.rotationQuaternion!.z, impostor.object.rotationQuaternion!.w);
+                startTransform.setOrigin(this._tmpAmmoVectorA);
+                startTransform.setRotation(this._tmpAmmoQuaternion);
+                var myMotionState = new Ammo.btDefaultMotionState(startTransform);
+                var rbInfo        = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
+                var body          = new Ammo.btRigidBody(rbInfo);
+
+                // Make objects kinematic if it's mass is 0
+                if (mass === 0) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
+                    body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
+                }
+
+                body.setRestitution(impostor.getParam("restitution"));
+                this.world.addRigidBody(body);
+                impostor.physicsBody = body;
+
+                impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
+            }
+        }
+
+        /**
+         * Removes the physics body from the imposter and disposes of the body's memory
+         * @param impostor imposter to remove the physics body from
+         */
+        public removePhysicsBody(impostor: PhysicsImpostor) {
+            if (this.world) {
+                this.world.removeRigidBody(impostor.physicsBody);
+
+                impostor._pluginData.toDispose.forEach((d: any) => {
+                    this.bjsAMMO.destroy(d);
+                });
+            }
+        }
+
+        /**
+         * Generates a joint
+         * @param impostorJoint the imposter joint to create the joint with
+         */
+        public generateJoint(impostorJoint: PhysicsImpostorJoint) {
+            var mainBody = impostorJoint.mainImpostor.physicsBody;
+            var connectedBody = impostorJoint.connectedImpostor.physicsBody;
+            if (!mainBody || !connectedBody) {
+                return;
+            }
+
+            var jointData = impostorJoint.joint.jointData;
+            if (!jointData.mainPivot) {
+                jointData.mainPivot = new Vector3(0, 0, 0);
+            }
+            if (!jointData.connectedPivot) {
+                jointData.connectedPivot = new Vector3(0, 0, 0);
+            }
+
+            var joint: any;
+            switch (impostorJoint.joint.type) {
+                case PhysicsJoint.BallAndSocketJoint:
+                    joint = new Ammo.btPoint2PointConstraint(mainBody, connectedBody, new Ammo.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new Ammo.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
+                    break;
+                default:
+                    Tools.Warn("JointType not currently supported by the Ammo plugin, falling back to PhysicsJoint.BallAndSocketJoint");
+                    joint = new Ammo.btPoint2PointConstraint(mainBody, connectedBody, new Ammo.btVector3(jointData.mainPivot.x, jointData.mainPivot.y, jointData.mainPivot.z), new Ammo.btVector3(jointData.connectedPivot.x, jointData.connectedPivot.y, jointData.connectedPivot.z));
+                    break;
+            }
+            this.world.addConstraint(joint, true);
+            impostorJoint.joint.physicsJoint = joint;
+        }
+
+        /**
+         * Removes a joint
+         * @param impostorJoint the imposter joint to remove the joint from
+         */
+        public removeJoint(impostorJoint: PhysicsImpostorJoint) {
+            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+        }
+
+        // adds all verticies (including child verticies) to the triangle mesh
+        private _addMeshVerts(btTriangleMesh: any, topLevelObject: IPhysicsEnabledObject, object: IPhysicsEnabledObject) {
+            var triangleCount = 0;
+            if (object && object.getIndices && object.getWorldMatrix && object.getChildMeshes) {
+                var indices = object.getIndices();
+                if (!indices) {
+                    indices = [];
+                }
+                var vertexPositions = object.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+                if (!vertexPositions) {
+                    vertexPositions = [];
+                }
+                object.computeWorldMatrix(false);
+                var faceCount = indices.length / 3;
+                for (var i = 0; i < faceCount; i++) {
+                    var triPoints = [];
+                    for (var point = 0; point < 3; point++) {
+                        var v = new BABYLON.Vector3(vertexPositions[(indices[(i * 3) + point] * 3) + 0], vertexPositions[(indices[(i * 3) + point] * 3) + 1], vertexPositions[(indices[(i * 3) + point] * 3) + 2]);
+                        v = Vector3.TransformCoordinates(v, object.getWorldMatrix());
+                        v.subtractInPlace(topLevelObject.position);
+                        var vec: any;
+                        if (point == 0) {
+                            vec = this._tmpAmmoVectorA;
+                        }else if (point == 1) {
+                            vec = this._tmpAmmoVectorB;
+                        }else {
+                            vec = this._tmpAmmoVectorC;
+                        }
+                        vec.setValue(v.x, v.y, v.z);
+
+                        triPoints.push(vec);
+                    }
+                    btTriangleMesh.addTriangle(triPoints[0], triPoints[1], triPoints[2]);
+                    triangleCount++;
+                }
+
+                object.getChildMeshes().forEach((m) => {
+                    triangleCount += this._addMeshVerts(btTriangleMesh, topLevelObject, m);
+                });
+            }
+            return triangleCount;
+        }
+
+        private _createShape(impostor: PhysicsImpostor, ignoreChildren= false) {
+            var object = impostor.object;
+
+            var returnValue: any;
+            var extendSize = impostor.getObjectExtendSize();
+
+            if (!ignoreChildren) {
+                var meshChildren = impostor.object.getChildMeshes ? impostor.object.getChildMeshes(true) : [];
+                if (meshChildren.length > 0) {
+                    returnValue = new Ammo.btCompoundShape();
+
+                    // Add shape of all children to the compound shape
+                    meshChildren.forEach((childMesh) => {
+                        var childImpostor = childMesh.getPhysicsImpostor();
+                        if (childImpostor) {
+                            var shape = this._createShape(childImpostor);
+                            this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+                            this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion!.x, childMesh.rotationQuaternion!.y, childMesh.rotationQuaternion!.z, childMesh.rotationQuaternion!.w);
+                            this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
+                            returnValue.addChildShape(this._tmpAmmoTransform, shape);
+                            childImpostor.dispose();
+                        }
+                    });
+
+                    // Add parents shape as a child if present
+                    var shape = this._createShape(impostor, true);
+                    if (shape) {
+                        this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
+                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
+                        this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
+                        this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
+
+                        returnValue.addChildShape(this._tmpAmmoTransform, shape);
+                    }
+
+                    return returnValue;
+                }
+            }
+
+            switch (impostor.type) {
+                case PhysicsImpostor.SphereImpostor:
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
+                case PhysicsImpostor.CylinderImpostor:
+                this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
+                    returnValue = new Ammo.btCylinderShape(this._tmpAmmoVectorA);
+                    break;
+                case PhysicsImpostor.PlaneImpostor:
+                case PhysicsImpostor.BoxImpostor:
+                    this._tmpAmmoVectorA.setValue(extendSize.x / 2, extendSize.y / 2, extendSize.z / 2);
+                    returnValue = new Ammo.btBoxShape(this._tmpAmmoVectorA);
+                    break;
+                case PhysicsImpostor.MeshImpostor:
+                    var tetraMesh = new Ammo.btTriangleMesh();
+                    impostor._pluginData.toDispose.concat([tetraMesh]);
+                    var triangeCount = this._addMeshVerts(tetraMesh, object, object);
+                    if (triangeCount == 0) {
+                        returnValue = new Ammo.btCompoundShape();
+                    }else {
+                        returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
+                    }
+                    break;
+            }
+
+            return returnValue;
+        }
+
+        /**
+         * Sets the physics body position/rotation from the babylon mesh's position/rotation
+         * @param impostor imposter containing the physics body and babylon object
+         */
+        public setTransformationFromPhysicsBody(impostor: PhysicsImpostor) {
+            impostor.physicsBody.getMotionState().getWorldTransform(this._tmpAmmoTransform);
+            impostor.object.position.set(this._tmpAmmoTransform.getOrigin().x(), this._tmpAmmoTransform.getOrigin().y(), this._tmpAmmoTransform.getOrigin().z());
+
+            if (!impostor.object.rotationQuaternion) {
+                if (impostor.object.rotation) {
+                    this._tmpQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
+                    this._tmpQuaternion.toEulerAnglesToRef(impostor.object.rotation);
+                }
+            }else {
+                impostor.object.rotationQuaternion.set(this._tmpAmmoTransform.getRotation().x(), this._tmpAmmoTransform.getRotation().y(), this._tmpAmmoTransform.getRotation().z(), this._tmpAmmoTransform.getRotation().w());
+            }
+        }
+
+        /**
+         * Sets the babylon object's position/rotation from the physics body's position/rotation
+         * @param impostor imposter containing the physics body and babylon object
+         * @param newPosition new position
+         * @param newRotation new rotation
+         */
+        public setPhysicsBodyTransformation(impostor: PhysicsImpostor, newPosition: Vector3, newRotation: Quaternion) {
+            var trans = impostor.physicsBody.getWorldTransform();
+
+            // If rotation/position has changed update and activate riged body
+            if (
+                trans.getOrigin().x() != newPosition.x ||
+                trans.getOrigin().y() != newPosition.y ||
+                trans.getOrigin().z() != newPosition.z ||
+                trans.getRotation().x() != newRotation.x ||
+                trans.getRotation().y() != newRotation.y ||
+                trans.getRotation().z() != newRotation.z ||
+                trans.getRotation().w() != newRotation.w
+            ) {
+                this._tmpAmmoVectorA.setValue(newPosition.x, newPosition.y, newPosition.z);
+                trans.setOrigin(this._tmpAmmoVectorA);
+
+                this._tmpAmmoQuaternion.setValue(newRotation.x, newRotation.y, newRotation.z, newRotation.w);
+                trans.setRotation(this._tmpAmmoQuaternion);
+                impostor.physicsBody.setWorldTransform(trans);
+
+                if (impostor.mass == 0) {
+                    // Kinematic objects must be updated using motion state
+                    var motionState = impostor.physicsBody.getMotionState();
+                    if (motionState) {
+                        motionState.setWorldTransform(trans);
+                    }
+                }else {
+                    impostor.physicsBody.activate();
+                }
+            }
+        }
+
+        /**
+         * If this plugin is supported
+         * @returns true if its supported
+         */
+        public isSupported(): boolean {
+            return this.bjsAMMO !== undefined;
+        }
+
+        /**
+         * Sets the linear velocity of the physics body
+         * @param impostor imposter to set the velocity on
+         * @param velocity velocity to set
+         */
+        public setLinearVelocity(impostor: PhysicsImpostor, velocity: Vector3) {
+            this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
+            impostor.physicsBody.setLinearVelocity(this._tmpAmmoVectorA);
+        }
+
+        /**
+         * Sets the angular velocity of the physics body
+         * @param impostor imposter to set the velocity on
+         * @param velocity velocity to set
+         */
+        public setAngularVelocity(impostor: PhysicsImpostor, velocity: Vector3) {
+            this._tmpAmmoVectorA.setValue(velocity.x, velocity.y, velocity.z);
+            impostor.physicsBody.setAngularVelocity(this._tmpAmmoVectorA);
+        }
+
+        /**
+         * gets the linear velocity
+         * @param impostor imposter to get linear velocity from
+         * @returns linear velocity
+         */
+        public getLinearVelocity(impostor: PhysicsImpostor): Nullable<Vector3> {
+            var v = impostor.physicsBody.getLinearVelocity();
+            if (!v) {
+                return null;
+            }
+            return new Vector3(v.x(), v.y(), v.z());
+        }
+
+        /**
+         * gets the angular velocity
+         * @param impostor imposter to get angular velocity from
+         * @returns angular velocity
+         */
+        public getAngularVelocity(impostor: PhysicsImpostor): Nullable<Vector3> {
+            var v = impostor.physicsBody.getAngularVelocity();
+            if (!v) {
+                return null;
+            }
+            return new Vector3(v.x(), v.y(), v.z());
+        }
+
+        /**
+         * Sets the mass of physics body
+         * @param impostor imposter to set the mass on
+         * @param mass mass to set
+         */
+        public setBodyMass(impostor: PhysicsImpostor, mass: number) {
+            impostor.physicsBody.setMassProps(mass);
+            impostor._pluginData.mass = mass;
+        }
+
+        /**
+         * Gets the mass of the physics body
+         * @param impostor imposter to get the mass from
+         * @returns mass
+         */
+        public getBodyMass(impostor: PhysicsImpostor): number {
+            return impostor._pluginData.mass;
+        }
+
+        /**
+         * Gets friction of the impostor
+         * @param impostor impostor to get friction from
+         * @returns friction value
+         */
+        public getBodyFriction(impostor: PhysicsImpostor): number {
+            return impostor.physicsBody.getFriction();
+        }
+
+        /**
+         * Sets friction of the impostor
+         * @param impostor impostor to set friction on
+         * @param friction friction value
+         */
+        public setBodyFriction(impostor: PhysicsImpostor, friction: number) {
+            impostor.physicsBody.setFriction(friction);
+        }
+
+        /**
+         * Gets restitution of the impostor
+         * @param impostor impostor to get restitution from
+         * @returns restitution value
+         */
+        public getBodyRestitution(impostor: PhysicsImpostor): number {
+            return impostor.physicsBody.getRestitution();
+        }
+
+        /**
+         * Sets resitution of the impostor
+         * @param impostor impostor to set resitution on
+         * @param restitution resitution value
+         */
+        public setBodyRestitution(impostor: PhysicsImpostor, restitution: number) {
+            impostor.physicsBody.setRestitution(restitution);
+        }
+
+        /**
+         * Sleeps the physics body and stops it from being active
+         * @param impostor impostor to sleep
+         */
+        public sleepBody(impostor: PhysicsImpostor) {
+            Tools.Warn("sleepBody is not currently supported by the Ammo physics plugin");
+        }
+
+        /**
+         * Activates the physics body
+         * @param impostor impostor to activate
+         */
+        public wakeUpBody(impostor: PhysicsImpostor) {
+            impostor.physicsBody.activate();
+        }
+
+        /**
+         * Updates the distance parameters of the joint
+         * @param joint joint to update
+         * @param maxDistance maximum distance of the joint
+         * @param minDistance minimum distance of the joint
+         */
+        public updateDistanceJoint(joint: PhysicsJoint, maxDistance: number, minDistance?: number) {
+            Tools.Warn("updateDistanceJoint is not currently supported by the Ammo physics plugin");
+        }
+
+        /**
+         * Sets a motor on the joint
+         * @param joint joint to set motor on
+         * @param speed speed of the motor
+         * @param maxForce maximum force of the motor
+         * @param motorIndex index of the motor
+         */
+        public setMotor(joint: IMotorEnabledJoint, speed?: number, maxForce?: number, motorIndex?: number) {
+            Tools.Warn("setMotor is not currently supported by the Ammo physics plugin");
+        }
+
+        /**
+         * Sets the motors limit
+         * @param joint joint to set limit on
+         * @param upperLimit upper limit
+         * @param lowerLimit lower limit
+         */
+        public setLimit(joint: IMotorEnabledJoint, upperLimit: number, lowerLimit?: number) {
+            Tools.Warn("setLimit is not currently supported by the Ammo physics plugin");
+        }
+
+        /**
+         * Syncs the position and rotation of a mesh with the impostor
+         * @param mesh mesh to sync
+         * @param impostor impostor to update the mesh with
+         */
+        public syncMeshWithImpostor(mesh: AbstractMesh, impostor: PhysicsImpostor) {
+            var body = impostor.physicsBody;
+
+            body.getMotionState().getWorldTransform(this._tmpAmmoTransform);
+
+            mesh.position.x = this._tmpAmmoTransform.getOrigin().x();
+            mesh.position.y = this._tmpAmmoTransform.getOrigin().y();
+            mesh.position.z = this._tmpAmmoTransform.getOrigin().z();
+
+            if (mesh.rotationQuaternion) {
+                mesh.rotationQuaternion.x = this._tmpAmmoTransform.getRotation().x();
+                mesh.rotationQuaternion.y = this._tmpAmmoTransform.getRotation().y();
+                mesh.rotationQuaternion.z = this._tmpAmmoTransform.getRotation().z();
+                mesh.rotationQuaternion.w = this._tmpAmmoTransform.getRotation().w();
+            }
+        }
+
+        /**
+         * Gets the radius of the impostor
+         * @param impostor impostor to get radius from
+         * @returns the radius
+         */
+        public getRadius(impostor: PhysicsImpostor): number {
+            var exntend = impostor.getObjectExtendSize();
+            return exntend.x / 2;
+        }
+
+        /**
+         * Gets the box size of the impostor
+         * @param impostor impostor to get box size from
+         * @param result the resulting box size
+         */
+        public getBoxSizeToRef(impostor: PhysicsImpostor, result: Vector3): void {
+            var exntend = impostor.getObjectExtendSize();
+            result.x = exntend.x;
+            result.y = exntend.y;
+            result.z = exntend.z;
+        }
+
+        /**
+         * Disposes of the impostor
+         */
+        public dispose() {
+            // Dispose of world
+            Ammo.destroy(this.world);
+            Ammo.destroy(this._solver);
+            Ammo.destroy(this._overlappingPairCache);
+            Ammo.destroy(this._dispatcher);
+            Ammo.destroy(this._collisionConfiguration);
+
+            // Dispose of tmp variables
+            Ammo.destroy(this._tmpAmmoVectorA);
+            Ammo.destroy(this._tmpAmmoVectorB);
+            Ammo.destroy(this._tmpAmmoVectorC);
+            Ammo.destroy(this._tmpAmmoTransform);
+            Ammo.destroy(this._tmpAmmoQuaternion);
+            Ammo.destroy(this._tmpAmmoConcreteContactResultCallback);
+
+            this.world = null;
+        }
+    }
+}

+ 5 - 2
src/Physics/physicsImpostor.ts

@@ -158,6 +158,9 @@ import { PhysicsJoint, PhysicsJointData } from "./physicsJoint";
          */
         public static IDENTITY_QUATERNION = Quaternion.Identity();
 
+        /** @hidden */
+        public _pluginData: any;
+
         private _physicsEngine: Nullable<IPhysicsEngine>;
         //The native cannon/oimo/energy physics body object.
         private _physicsBody: any;
@@ -165,7 +168,8 @@ import { PhysicsJoint, PhysicsJointData } from "./physicsJoint";
 
         private _onBeforePhysicsStepCallbacks = new Array<(impostor: PhysicsImpostor) => void>();
         private _onAfterPhysicsStepCallbacks = new Array<(impostor: PhysicsImpostor) => void>();
-        private _onPhysicsCollideCallbacks: Array<{ callback: (collider: PhysicsImpostor, collidedAgainst: PhysicsImpostor) => void, otherImpostors: Array<PhysicsImpostor> }> = [];
+        /** @hidden */
+        public _onPhysicsCollideCallbacks: Array<{ callback: (collider: PhysicsImpostor, collidedAgainst: PhysicsImpostor) => void, otherImpostors: Array<PhysicsImpostor> }> = [];
 
         private _deltaPosition: Vector3 = Vector3.Zero();
         private _deltaRotation: Quaternion;
@@ -419,7 +423,6 @@ import { PhysicsJoint, PhysicsJointData } from "./physicsJoint";
                 this.object.rotationQuaternion = q;
                 //calculate the world matrix with the new rotation
                 this.object.computeWorldMatrix && this.object.computeWorldMatrix(true);
-
                 return size;
             } else {
                 return PhysicsImpostor.DEFAULT_OBJECT_SIZE;

+ 1 - 0
tests/validation/validate.html

@@ -6,6 +6,7 @@
 	<link href="index.css" rel="stylesheet" />
 
 	<script src="https://preview.babylonjs.com/draco_decoder_gltf.js"></script>
+	<script src="https://preview.babylonjs.com/ammo.js"></script>
 	<script src="https://preview.babylonjs.com/cannon.js"></script>
 	<script src="https://preview.babylonjs.com/Oimo.js"></script>
 	<script src="https://preview.babylonjs.com/gltf_validator.js"></script>