Browse Source

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into FollowCamera_InputManager

duncan law 6 years ago
parent
commit
f2e7380973
74 changed files with 12453 additions and 10792 deletions
  1. 5274 5253
      Playground/babylon.d.txt
  2. 1 1
      Playground/index-local.html
  3. 2 2
      Playground/index.html
  4. 2 2
      Playground/ts.html
  5. 5258 5237
      dist/preview release/babylon.d.ts
  6. 1 1
      dist/preview release/babylon.js
  7. 85 15
      dist/preview release/babylon.max.js
  8. 85 15
      dist/preview release/babylon.no-module.max.js
  9. 1 1
      dist/preview release/babylon.worker.js
  10. 85 15
      dist/preview release/es6.js
  11. 1 1
      dist/preview release/glTF2Interface/package.json
  12. 1 1
      dist/preview release/gui/babylon.gui.js
  13. 1 1
      dist/preview release/gui/babylon.gui.min.js
  14. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  15. 2 2
      dist/preview release/gui/package.json
  16. 8 8
      dist/preview release/inspector/babylon.inspector.bundle.js
  17. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  18. 1 1
      dist/preview release/inspector/babylon.inspector.d.ts
  19. 2 2
      dist/preview release/inspector/babylon.inspector.module.d.ts
  20. 5 5
      dist/preview release/inspector/package.json
  21. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  22. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  23. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  24. 16 4
      dist/preview release/loaders/babylon.objFileLoader.d.ts
  25. 85 32
      dist/preview release/loaders/babylon.objFileLoader.js
  26. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  27. 16 4
      dist/preview release/loaders/babylonjs.loaders.d.ts
  28. 85 32
      dist/preview release/loaders/babylonjs.loaders.js
  29. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  30. 16 4
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  31. 3 3
      dist/preview release/loaders/package.json
  32. 2 2
      dist/preview release/materialsLibrary/package.json
  33. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  34. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  35. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  36. 2 2
      dist/preview release/postProcessesLibrary/package.json
  37. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  38. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  39. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  40. 3 3
      dist/preview release/serializers/package.json
  41. 2 2
      dist/preview release/viewer/babylon.viewer.d.ts
  42. 3 3
      dist/preview release/viewer/babylon.viewer.js
  43. 6 6
      dist/preview release/viewer/babylon.viewer.max.js
  44. 2 2
      dist/preview release/viewer/babylon.viewer.module.d.ts
  45. 8 2
      dist/preview release/what's new.md
  46. 1087 0
      gui/src/2D/controls/colorpicker.ts
  47. 1 1
      inspector/index.html
  48. 116 50
      loaders/src/OBJ/babylon.objFileLoader.ts
  49. 5 5
      materialsLibrary/index.html
  50. 1 1
      materialsLibrary/test/addCell.js
  51. 11 11
      materialsLibrary/test/addMix.js
  52. 2 2
      materialsLibrary/test/addbackground.js
  53. 3 3
      materialsLibrary/test/addfire.js
  54. 2 2
      materialsLibrary/test/addlava.js
  55. 3 3
      materialsLibrary/test/addpbr.js
  56. 2 2
      materialsLibrary/test/addpbrmetallicroughness.js
  57. 2 2
      materialsLibrary/test/addpbrspecularglossiness.js
  58. 7 7
      materialsLibrary/test/addterrain.js
  59. 4 4
      materialsLibrary/test/addtriplanar.js
  60. 1 1
      materialsLibrary/test/addwater.js
  61. 1 1
      package.json
  62. 1 1
      postProcessLibrary/index.html
  63. 4 4
      proceduralTexturesLibrary/index.html
  64. 3 0
      readme.md
  65. 8 4
      src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts
  66. 1 1
      src/Cameras/babylon.arcRotateCamera.ts
  67. 1 1
      src/Engine/babylon.engine.ts
  68. 4 1
      src/Gizmos/babylon.axisScaleGizmo.ts
  69. 30 1
      src/Gizmos/babylon.boundingBoxGizmo.ts
  70. 4 1
      src/Gizmos/babylon.planeRotationGizmo.ts
  71. 25 0
      src/Math/babylon.math.ts
  72. 8 0
      src/babylon.scene.ts
  73. 5 5
      tests/nullEngine/app.js
  74. 33 0
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

File diff suppressed because it is too large
+ 5274 - 5253
Playground/babylon.d.txt


+ 1 - 1
Playground/index-local.html

@@ -23,7 +23,7 @@
     <!-- Monaco -->
 
     <!-- Babylon.js -->
-    <script src="../tools/DevLoader/BabylonLoader.js"></script>
+    <script src="../Tools/DevLoader/BabylonLoader.js"></script>
 
     <link href="css/index.css" rel="stylesheet" />
 </head>

+ 2 - 2
Playground/index.html

@@ -56,8 +56,8 @@
     <!-- Extensions -->
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js" async></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js" async></script>
-    <!-- <script async src="https://www.babylontoolkit.com/playground/scripts/babylon.navmesh.js"></script>
-    <script async src="https://www.babylontoolkit.com/playground/scripts/babylon.manager.js"></script> -->
+    <!-- <script async src="https://www.babylontoolkit.com/Playground/scripts/babylon.navmesh.js"></script>
+    <script async src="https://www.babylontoolkit.com/Playground/scripts/babylon.manager.js"></script> -->
 
     <link href="css/index.css" rel="stylesheet" />
 </head>

+ 2 - 2
Playground/ts.html

@@ -54,8 +54,8 @@
         <!-- Extensions -->
         <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js" async></script>
         <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js" async></script>
-        <script src="https://www.babylontoolkit.com/playground/scripts/babylon.navmesh.js"></script>
-        <script src="https://www.babylontoolkit.com/playground/scripts/babylon.manager.js"></script>
+        <script src="https://www.babylontoolkit.com/Playground/scripts/babylon.navmesh.js"></script>
+        <script src="https://www.babylontoolkit.com/Playground/scripts/babylon.manager.js"></script>
                                
         <link href="css/index.css" rel="stylesheet" />
         <link href="css/color_ts.css" rel="stylesheet" />

File diff suppressed because it is too large
+ 5258 - 5237
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.js


+ 85 - 15
dist/preview release/babylon.max.js

@@ -5414,6 +5414,27 @@ var BABYLON;
             }
             return this;
         };
+        /**
+         * Due to float precision, scale of a mesh could be uniform but float values are off by a small fraction
+         * Check if is non uniform within a certain amount of decimal places to account for this
+         * @param epsilon the amount the values can differ
+         * @returns if the the vector is non uniform to a certain number of decimal places
+         */
+        Vector3.prototype.isNonUniformWithinEpsilon = function (epsilon) {
+            var absX = Math.abs(this.x);
+            var absY = Math.abs(this.y);
+            if (!BABYLON.Scalar.WithinEpsilon(absX, absY, epsilon)) {
+                return true;
+            }
+            var absZ = Math.abs(this.z);
+            if (!BABYLON.Scalar.WithinEpsilon(absX, absZ, epsilon)) {
+                return true;
+            }
+            if (!BABYLON.Scalar.WithinEpsilon(absY, absZ, epsilon)) {
+                return true;
+            }
+            return false;
+        };
         Object.defineProperty(Vector3.prototype, "isNonUniform", {
             /**
              * Gets a boolean indicating that the vector is non uniform meaning x, y or z are not all the same
@@ -12988,7 +13009,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.16";
+                return "4.0.0-alpha.17";
             },
             enumerable: true,
             configurable: true
@@ -20304,7 +20325,7 @@ var BABYLON;
         });
         Object.defineProperty(TransformNode.prototype, "rotationQuaternion", {
             /**
-             * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (null by default).
+             * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (undefined by default, but can be null).
              * If set, only the rotationQuaternion is then used to compute the node rotation (ie. node.rotation will be ignored)
              */
             get: function () {
@@ -28122,6 +28143,13 @@ var BABYLON;
                 group.stop();
             }
         };
+        /**
+         * Resets the last animation time frame.
+         * Useful to override when animations start running when loading a scene for the first time.
+         */
+        Scene.prototype.resetLastAnimationTimeFrame = function () {
+            this._animationTimeLast = BABYLON.Tools.Now;
+        };
         Scene.prototype._animate = function () {
             if (!this.animationsEnabled || this._activeAnimatables.length === 0) {
                 return;
@@ -52036,7 +52064,7 @@ var BABYLON;
              * Maximum allowed angle on the latitudinal axis.
              * This can help limiting how the Camera is able to move in the scene.
              */
-            _this.upperBetaLimit = Math.PI;
+            _this.upperBetaLimit = Math.PI - 0.01;
             /**
              * Minimum allowed distance of the camera to the target (The camera can not get closer).
              * This can help limiting how the Camera is able to move in the scene.
@@ -99718,7 +99746,7 @@ var BABYLON;
                 case BABYLON.PhysicsJoint.PointToPointJoint:
                 case BABYLON.PhysicsJoint.BallAndSocketJoint:
                 default:
-                    constraint = new this.BJSCANNON.PointToPointConstraint(mainBody, constraintData.pivotA, connectedBody, constraintData.pivotA, constraintData.maxForce);
+                    constraint = new this.BJSCANNON.PointToPointConstraint(mainBody, constraintData.pivotA, connectedBody, constraintData.pivotB, constraintData.maxForce);
                     break;
             }
             //set the collideConnected flag after the creation, since DistanceJoint ignores it.
@@ -100344,9 +100372,10 @@ var BABYLON;
                 if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
                     body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
                 }
-                body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
+                this.setBodyRestitution(impostor, impostor.getParam("restitution"));
+                this.setBodyFriction(impostor, impostor.getParam("friction"));
                 impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
             }
         };
@@ -100409,7 +100438,7 @@ var BABYLON;
                     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);
+            this.world.addConstraint(joint, !impostorJoint.joint.jointData.collision);
             impostorJoint.joint.physicsJoint = joint;
         };
         /**
@@ -100666,7 +100695,7 @@ var BABYLON;
          * @returns friction value
          */
         AmmoJSPlugin.prototype.getBodyFriction = function (impostor) {
-            return impostor.physicsBody.getFriction();
+            return impostor._pluginData.friction;
         };
         /**
          * Sets friction of the impostor
@@ -100675,6 +100704,7 @@ var BABYLON;
          */
         AmmoJSPlugin.prototype.setBodyFriction = function (impostor, friction) {
             impostor.physicsBody.setFriction(friction);
+            impostor._pluginData.friction = friction;
         };
         /**
          * Gets restitution of the impostor
@@ -100682,7 +100712,7 @@ var BABYLON;
          * @returns restitution value
          */
         AmmoJSPlugin.prototype.getBodyRestitution = function (impostor) {
-            return impostor.physicsBody.getRestitution();
+            return impostor._pluginData.restitution;
         };
         /**
          * Sets resitution of the impostor
@@ -100691,6 +100721,7 @@ var BABYLON;
          */
         AmmoJSPlugin.prototype.setBodyRestitution = function (impostor, restitution) {
             impostor.physicsBody.setRestitution(restitution);
+            impostor._pluginData.restitution = restitution;
         };
         /**
          * Sleeps the physics body and stops it from being active
@@ -104099,11 +104130,14 @@ var BABYLON;
                         tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
                         // Slowly move mesh to avoid jitter
                         var oldParent = pickedMesh.parent;
-                        pickedMesh.setParent(null);
-                        BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
-                        pickedMesh.setParent(oldParent);
-                        BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                        // Only rotate the mesh if it's parent has uniform scaling
+                        if (!oldParent || (oldParent.scaling && !oldParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                            pickedMesh.setParent(null);
+                            BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
+                            pickedMesh.setParent(oldParent);
+                        }
                     }
+                    BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
                 }
             });
         };
@@ -104761,7 +104795,10 @@ var BABYLON;
                     else {
                         currentSnapDragDistance += event.dragDistance;
                         if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
-                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
                             tmpVector.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
                             snapped = true;
@@ -104942,7 +104979,10 @@ var BABYLON;
                     if (_this.snapDistance != 0) {
                         currentSnapDragDistance += angle;
                         if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
-                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
                             angle = _this.snapDistance * dragSteps;
                             snapped = true;
@@ -105511,6 +105551,11 @@ var BABYLON;
                 _dragBehavior.onDragObservable.add(function (event) {
                     _this.onRotationSphereDragObservable.notifyObservers({});
                     if (_this.attachedMesh) {
+                        var originalParent = _this.attachedMesh.parent;
+                        if (originalParent && (originalParent.scaling && originalParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                            BABYLON.Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                            return;
+                        }
                         BoundingBoxGizmo._RemoveAndStorePivotPoint(_this.attachedMesh);
                         var worldDragDirection = startingTurnDirection;
                         // Project the world right on to the drag plane
@@ -105543,6 +105588,7 @@ var BABYLON;
                             _this._anchorMesh.addChild(_this.attachedMesh);
                             _this._anchorMesh.rotationQuaternion.multiplyToRef(_this._tmpQuaternion, _this._anchorMesh.rotationQuaternion);
                             _this._anchorMesh.removeChild(_this.attachedMesh);
+                            _this.attachedMesh.setParent(originalParent);
                         }
                         _this.updateBoundingBox();
                         BoundingBoxGizmo._RestorePivotPoint(_this.attachedMesh);
@@ -105582,6 +105628,11 @@ var BABYLON;
                         _dragBehavior.onDragObservable.add(function (event) {
                             _this.onScaleBoxDragObservable.notifyObservers({});
                             if (_this.attachedMesh) {
+                                var originalParent = _this.attachedMesh.parent;
+                                if (originalParent && (originalParent.scaling && originalParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                                    BABYLON.Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                                    return;
+                                }
                                 BoundingBoxGizmo._RemoveAndStorePivotPoint(_this.attachedMesh);
                                 var relativeDragDistance = (event.dragDistance / _this._boundingDimensions.length()) * _this._anchorMesh.scaling.length();
                                 var deltaScale = new BABYLON.Vector3(relativeDragDistance, relativeDragDistance, relativeDragDistance);
@@ -105608,6 +105659,7 @@ var BABYLON;
                                     _this._anchorMesh.scaling.subtractInPlace(deltaScale);
                                 }
                                 _this._anchorMesh.removeChild(_this.attachedMesh);
+                                _this.attachedMesh.setParent(originalParent);
                                 BoundingBoxGizmo._RestorePivotPoint(_this.attachedMesh);
                             }
                             _this._updateDummy();
@@ -105701,10 +105753,15 @@ var BABYLON;
                 // Reset anchor mesh to match attached mesh's scale
                 // This is needed to avoid invalid box/sphere position on first drag
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(value);
+                var originalParent = value.parent;
                 this._anchorMesh.addChild(value);
                 this._anchorMesh.removeChild(value);
+                value.setParent(originalParent);
                 BoundingBoxGizmo._RestorePivotPoint(value);
                 this.updateBoundingBox();
+                value.getChildMeshes(false).forEach(function (m) {
+                    m.markAsDirty("scaling");
+                });
                 this.gizmoLayer.utilityLayerScene.onAfterRenderObservable.addOnce(function () {
                     _this._updateDummy();
                 });
@@ -105722,6 +105779,8 @@ var BABYLON;
         BoundingBoxGizmo.prototype.updateBoundingBox = function () {
             if (this.attachedMesh) {
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
+                var originalParent = this.attachedMesh.parent;
+                this.attachedMesh.setParent(null);
                 this._update();
                 // Rotate based on axis
                 if (!this.attachedMesh.rotationQuaternion) {
@@ -105749,6 +105808,7 @@ var BABYLON;
                 // restore position/rotation values
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
+                this.attachedMesh.setParent(originalParent);
             }
             this._updateRotationSpheres();
             this._updateScaleBoxes();
@@ -105829,6 +105889,15 @@ var BABYLON;
                 }
             });
         };
+        /**
+         * Enables/disables scaling
+         * @param enable if scaling should be enabled
+         */
+        BoundingBoxGizmo.prototype.setEnabledScaling = function (enable) {
+            this._scaleBoxesParent.getChildMeshes().forEach(function (m, i) {
+                m.setEnabled(enable);
+            });
+        };
         BoundingBoxGizmo.prototype._updateDummy = function () {
             if (this._dragMesh) {
                 this._dragMesh.position.copyFrom(this._lineBoundingBox.getAbsolutePosition());
@@ -115919,10 +115988,11 @@ var BABYLON;
          */
         MeshAssetTask.prototype.runTask = function (scene, onSuccess, onError) {
             var _this = this;
-            BABYLON.SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene, function (meshes, particleSystems, skeletons) {
+            BABYLON.SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene, function (meshes, particleSystems, skeletons, animationGroups) {
                 _this.loadedMeshes = meshes;
                 _this.loadedParticleSystems = particleSystems;
                 _this.loadedSkeletons = skeletons;
+                _this.loadedAnimationGroups = animationGroups;
                 onSuccess();
             }, null, function (scene, message, exception) {
                 onError(message, exception);

+ 85 - 15
dist/preview release/babylon.no-module.max.js

@@ -5381,6 +5381,27 @@ var BABYLON;
             }
             return this;
         };
+        /**
+         * Due to float precision, scale of a mesh could be uniform but float values are off by a small fraction
+         * Check if is non uniform within a certain amount of decimal places to account for this
+         * @param epsilon the amount the values can differ
+         * @returns if the the vector is non uniform to a certain number of decimal places
+         */
+        Vector3.prototype.isNonUniformWithinEpsilon = function (epsilon) {
+            var absX = Math.abs(this.x);
+            var absY = Math.abs(this.y);
+            if (!BABYLON.Scalar.WithinEpsilon(absX, absY, epsilon)) {
+                return true;
+            }
+            var absZ = Math.abs(this.z);
+            if (!BABYLON.Scalar.WithinEpsilon(absX, absZ, epsilon)) {
+                return true;
+            }
+            if (!BABYLON.Scalar.WithinEpsilon(absY, absZ, epsilon)) {
+                return true;
+            }
+            return false;
+        };
         Object.defineProperty(Vector3.prototype, "isNonUniform", {
             /**
              * Gets a boolean indicating that the vector is non uniform meaning x, y or z are not all the same
@@ -12955,7 +12976,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.16";
+                return "4.0.0-alpha.17";
             },
             enumerable: true,
             configurable: true
@@ -20271,7 +20292,7 @@ var BABYLON;
         });
         Object.defineProperty(TransformNode.prototype, "rotationQuaternion", {
             /**
-             * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (null by default).
+             * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (undefined by default, but can be null).
              * If set, only the rotationQuaternion is then used to compute the node rotation (ie. node.rotation will be ignored)
              */
             get: function () {
@@ -28089,6 +28110,13 @@ var BABYLON;
                 group.stop();
             }
         };
+        /**
+         * Resets the last animation time frame.
+         * Useful to override when animations start running when loading a scene for the first time.
+         */
+        Scene.prototype.resetLastAnimationTimeFrame = function () {
+            this._animationTimeLast = BABYLON.Tools.Now;
+        };
         Scene.prototype._animate = function () {
             if (!this.animationsEnabled || this._activeAnimatables.length === 0) {
                 return;
@@ -52003,7 +52031,7 @@ var BABYLON;
              * Maximum allowed angle on the latitudinal axis.
              * This can help limiting how the Camera is able to move in the scene.
              */
-            _this.upperBetaLimit = Math.PI;
+            _this.upperBetaLimit = Math.PI - 0.01;
             /**
              * Minimum allowed distance of the camera to the target (The camera can not get closer).
              * This can help limiting how the Camera is able to move in the scene.
@@ -99685,7 +99713,7 @@ var BABYLON;
                 case BABYLON.PhysicsJoint.PointToPointJoint:
                 case BABYLON.PhysicsJoint.BallAndSocketJoint:
                 default:
-                    constraint = new this.BJSCANNON.PointToPointConstraint(mainBody, constraintData.pivotA, connectedBody, constraintData.pivotA, constraintData.maxForce);
+                    constraint = new this.BJSCANNON.PointToPointConstraint(mainBody, constraintData.pivotA, connectedBody, constraintData.pivotB, constraintData.maxForce);
                     break;
             }
             //set the collideConnected flag after the creation, since DistanceJoint ignores it.
@@ -100311,9 +100339,10 @@ var BABYLON;
                 if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
                     body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
                 }
-                body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
+                this.setBodyRestitution(impostor, impostor.getParam("restitution"));
+                this.setBodyFriction(impostor, impostor.getParam("friction"));
                 impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
             }
         };
@@ -100376,7 +100405,7 @@ var BABYLON;
                     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);
+            this.world.addConstraint(joint, !impostorJoint.joint.jointData.collision);
             impostorJoint.joint.physicsJoint = joint;
         };
         /**
@@ -100633,7 +100662,7 @@ var BABYLON;
          * @returns friction value
          */
         AmmoJSPlugin.prototype.getBodyFriction = function (impostor) {
-            return impostor.physicsBody.getFriction();
+            return impostor._pluginData.friction;
         };
         /**
          * Sets friction of the impostor
@@ -100642,6 +100671,7 @@ var BABYLON;
          */
         AmmoJSPlugin.prototype.setBodyFriction = function (impostor, friction) {
             impostor.physicsBody.setFriction(friction);
+            impostor._pluginData.friction = friction;
         };
         /**
          * Gets restitution of the impostor
@@ -100649,7 +100679,7 @@ var BABYLON;
          * @returns restitution value
          */
         AmmoJSPlugin.prototype.getBodyRestitution = function (impostor) {
-            return impostor.physicsBody.getRestitution();
+            return impostor._pluginData.restitution;
         };
         /**
          * Sets resitution of the impostor
@@ -100658,6 +100688,7 @@ var BABYLON;
          */
         AmmoJSPlugin.prototype.setBodyRestitution = function (impostor, restitution) {
             impostor.physicsBody.setRestitution(restitution);
+            impostor._pluginData.restitution = restitution;
         };
         /**
          * Sleeps the physics body and stops it from being active
@@ -104066,11 +104097,14 @@ var BABYLON;
                         tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
                         // Slowly move mesh to avoid jitter
                         var oldParent = pickedMesh.parent;
-                        pickedMesh.setParent(null);
-                        BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
-                        pickedMesh.setParent(oldParent);
-                        BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                        // Only rotate the mesh if it's parent has uniform scaling
+                        if (!oldParent || (oldParent.scaling && !oldParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                            pickedMesh.setParent(null);
+                            BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
+                            pickedMesh.setParent(oldParent);
+                        }
                     }
+                    BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
                 }
             });
         };
@@ -104728,7 +104762,10 @@ var BABYLON;
                     else {
                         currentSnapDragDistance += event.dragDistance;
                         if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
-                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
                             tmpVector.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
                             snapped = true;
@@ -104909,7 +104946,10 @@ var BABYLON;
                     if (_this.snapDistance != 0) {
                         currentSnapDragDistance += angle;
                         if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
-                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
                             angle = _this.snapDistance * dragSteps;
                             snapped = true;
@@ -105478,6 +105518,11 @@ var BABYLON;
                 _dragBehavior.onDragObservable.add(function (event) {
                     _this.onRotationSphereDragObservable.notifyObservers({});
                     if (_this.attachedMesh) {
+                        var originalParent = _this.attachedMesh.parent;
+                        if (originalParent && (originalParent.scaling && originalParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                            BABYLON.Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                            return;
+                        }
                         BoundingBoxGizmo._RemoveAndStorePivotPoint(_this.attachedMesh);
                         var worldDragDirection = startingTurnDirection;
                         // Project the world right on to the drag plane
@@ -105510,6 +105555,7 @@ var BABYLON;
                             _this._anchorMesh.addChild(_this.attachedMesh);
                             _this._anchorMesh.rotationQuaternion.multiplyToRef(_this._tmpQuaternion, _this._anchorMesh.rotationQuaternion);
                             _this._anchorMesh.removeChild(_this.attachedMesh);
+                            _this.attachedMesh.setParent(originalParent);
                         }
                         _this.updateBoundingBox();
                         BoundingBoxGizmo._RestorePivotPoint(_this.attachedMesh);
@@ -105549,6 +105595,11 @@ var BABYLON;
                         _dragBehavior.onDragObservable.add(function (event) {
                             _this.onScaleBoxDragObservable.notifyObservers({});
                             if (_this.attachedMesh) {
+                                var originalParent = _this.attachedMesh.parent;
+                                if (originalParent && (originalParent.scaling && originalParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                                    BABYLON.Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                                    return;
+                                }
                                 BoundingBoxGizmo._RemoveAndStorePivotPoint(_this.attachedMesh);
                                 var relativeDragDistance = (event.dragDistance / _this._boundingDimensions.length()) * _this._anchorMesh.scaling.length();
                                 var deltaScale = new BABYLON.Vector3(relativeDragDistance, relativeDragDistance, relativeDragDistance);
@@ -105575,6 +105626,7 @@ var BABYLON;
                                     _this._anchorMesh.scaling.subtractInPlace(deltaScale);
                                 }
                                 _this._anchorMesh.removeChild(_this.attachedMesh);
+                                _this.attachedMesh.setParent(originalParent);
                                 BoundingBoxGizmo._RestorePivotPoint(_this.attachedMesh);
                             }
                             _this._updateDummy();
@@ -105668,10 +105720,15 @@ var BABYLON;
                 // Reset anchor mesh to match attached mesh's scale
                 // This is needed to avoid invalid box/sphere position on first drag
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(value);
+                var originalParent = value.parent;
                 this._anchorMesh.addChild(value);
                 this._anchorMesh.removeChild(value);
+                value.setParent(originalParent);
                 BoundingBoxGizmo._RestorePivotPoint(value);
                 this.updateBoundingBox();
+                value.getChildMeshes(false).forEach(function (m) {
+                    m.markAsDirty("scaling");
+                });
                 this.gizmoLayer.utilityLayerScene.onAfterRenderObservable.addOnce(function () {
                     _this._updateDummy();
                 });
@@ -105689,6 +105746,8 @@ var BABYLON;
         BoundingBoxGizmo.prototype.updateBoundingBox = function () {
             if (this.attachedMesh) {
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
+                var originalParent = this.attachedMesh.parent;
+                this.attachedMesh.setParent(null);
                 this._update();
                 // Rotate based on axis
                 if (!this.attachedMesh.rotationQuaternion) {
@@ -105716,6 +105775,7 @@ var BABYLON;
                 // restore position/rotation values
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
+                this.attachedMesh.setParent(originalParent);
             }
             this._updateRotationSpheres();
             this._updateScaleBoxes();
@@ -105796,6 +105856,15 @@ var BABYLON;
                 }
             });
         };
+        /**
+         * Enables/disables scaling
+         * @param enable if scaling should be enabled
+         */
+        BoundingBoxGizmo.prototype.setEnabledScaling = function (enable) {
+            this._scaleBoxesParent.getChildMeshes().forEach(function (m, i) {
+                m.setEnabled(enable);
+            });
+        };
         BoundingBoxGizmo.prototype._updateDummy = function () {
             if (this._dragMesh) {
                 this._dragMesh.position.copyFrom(this._lineBoundingBox.getAbsolutePosition());
@@ -115886,10 +115955,11 @@ var BABYLON;
          */
         MeshAssetTask.prototype.runTask = function (scene, onSuccess, onError) {
             var _this = this;
-            BABYLON.SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene, function (meshes, particleSystems, skeletons) {
+            BABYLON.SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene, function (meshes, particleSystems, skeletons, animationGroups) {
                 _this.loadedMeshes = meshes;
                 _this.loadedParticleSystems = particleSystems;
                 _this.loadedSkeletons = skeletons;
+                _this.loadedAnimationGroups = animationGroups;
                 onSuccess();
             }, null, function (scene, message, exception) {
                 onError(message, exception);

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.worker.js


+ 85 - 15
dist/preview release/es6.js

@@ -5381,6 +5381,27 @@ var BABYLON;
             }
             return this;
         };
+        /**
+         * Due to float precision, scale of a mesh could be uniform but float values are off by a small fraction
+         * Check if is non uniform within a certain amount of decimal places to account for this
+         * @param epsilon the amount the values can differ
+         * @returns if the the vector is non uniform to a certain number of decimal places
+         */
+        Vector3.prototype.isNonUniformWithinEpsilon = function (epsilon) {
+            var absX = Math.abs(this.x);
+            var absY = Math.abs(this.y);
+            if (!BABYLON.Scalar.WithinEpsilon(absX, absY, epsilon)) {
+                return true;
+            }
+            var absZ = Math.abs(this.z);
+            if (!BABYLON.Scalar.WithinEpsilon(absX, absZ, epsilon)) {
+                return true;
+            }
+            if (!BABYLON.Scalar.WithinEpsilon(absY, absZ, epsilon)) {
+                return true;
+            }
+            return false;
+        };
         Object.defineProperty(Vector3.prototype, "isNonUniform", {
             /**
              * Gets a boolean indicating that the vector is non uniform meaning x, y or z are not all the same
@@ -12955,7 +12976,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.16";
+                return "4.0.0-alpha.17";
             },
             enumerable: true,
             configurable: true
@@ -20271,7 +20292,7 @@ var BABYLON;
         });
         Object.defineProperty(TransformNode.prototype, "rotationQuaternion", {
             /**
-             * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (null by default).
+             * Gets or sets the rotation Quaternion property : this a Quaternion object defining the node rotation by using a unit quaternion (undefined by default, but can be null).
              * If set, only the rotationQuaternion is then used to compute the node rotation (ie. node.rotation will be ignored)
              */
             get: function () {
@@ -28089,6 +28110,13 @@ var BABYLON;
                 group.stop();
             }
         };
+        /**
+         * Resets the last animation time frame.
+         * Useful to override when animations start running when loading a scene for the first time.
+         */
+        Scene.prototype.resetLastAnimationTimeFrame = function () {
+            this._animationTimeLast = BABYLON.Tools.Now;
+        };
         Scene.prototype._animate = function () {
             if (!this.animationsEnabled || this._activeAnimatables.length === 0) {
                 return;
@@ -52003,7 +52031,7 @@ var BABYLON;
              * Maximum allowed angle on the latitudinal axis.
              * This can help limiting how the Camera is able to move in the scene.
              */
-            _this.upperBetaLimit = Math.PI;
+            _this.upperBetaLimit = Math.PI - 0.01;
             /**
              * Minimum allowed distance of the camera to the target (The camera can not get closer).
              * This can help limiting how the Camera is able to move in the scene.
@@ -99685,7 +99713,7 @@ var BABYLON;
                 case BABYLON.PhysicsJoint.PointToPointJoint:
                 case BABYLON.PhysicsJoint.BallAndSocketJoint:
                 default:
-                    constraint = new this.BJSCANNON.PointToPointConstraint(mainBody, constraintData.pivotA, connectedBody, constraintData.pivotA, constraintData.maxForce);
+                    constraint = new this.BJSCANNON.PointToPointConstraint(mainBody, constraintData.pivotA, connectedBody, constraintData.pivotB, constraintData.maxForce);
                     break;
             }
             //set the collideConnected flag after the creation, since DistanceJoint ignores it.
@@ -100311,9 +100339,10 @@ var BABYLON;
                 if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
                     body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
                 }
-                body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
+                this.setBodyRestitution(impostor, impostor.getParam("restitution"));
+                this.setBodyFriction(impostor, impostor.getParam("friction"));
                 impostor._pluginData.toDispose.concat([body, rbInfo, myMotionState, startTransform, localInertia, colShape]);
             }
         };
@@ -100376,7 +100405,7 @@ var BABYLON;
                     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);
+            this.world.addConstraint(joint, !impostorJoint.joint.jointData.collision);
             impostorJoint.joint.physicsJoint = joint;
         };
         /**
@@ -100633,7 +100662,7 @@ var BABYLON;
          * @returns friction value
          */
         AmmoJSPlugin.prototype.getBodyFriction = function (impostor) {
-            return impostor.physicsBody.getFriction();
+            return impostor._pluginData.friction;
         };
         /**
          * Sets friction of the impostor
@@ -100642,6 +100671,7 @@ var BABYLON;
          */
         AmmoJSPlugin.prototype.setBodyFriction = function (impostor, friction) {
             impostor.physicsBody.setFriction(friction);
+            impostor._pluginData.friction = friction;
         };
         /**
          * Gets restitution of the impostor
@@ -100649,7 +100679,7 @@ var BABYLON;
          * @returns restitution value
          */
         AmmoJSPlugin.prototype.getBodyRestitution = function (impostor) {
-            return impostor.physicsBody.getRestitution();
+            return impostor._pluginData.restitution;
         };
         /**
          * Sets resitution of the impostor
@@ -100658,6 +100688,7 @@ var BABYLON;
          */
         AmmoJSPlugin.prototype.setBodyRestitution = function (impostor, restitution) {
             impostor.physicsBody.setRestitution(restitution);
+            impostor._pluginData.restitution = restitution;
         };
         /**
          * Sleeps the physics body and stops it from being active
@@ -104066,11 +104097,14 @@ var BABYLON;
                         tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
                         // Slowly move mesh to avoid jitter
                         var oldParent = pickedMesh.parent;
-                        pickedMesh.setParent(null);
-                        BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
-                        pickedMesh.setParent(oldParent);
-                        BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                        // Only rotate the mesh if it's parent has uniform scaling
+                        if (!oldParent || (oldParent.scaling && !oldParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                            pickedMesh.setParent(null);
+                            BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
+                            pickedMesh.setParent(oldParent);
+                        }
                     }
+                    BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
                 }
             });
         };
@@ -104728,7 +104762,10 @@ var BABYLON;
                     else {
                         currentSnapDragDistance += event.dragDistance;
                         if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
-                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
                             tmpVector.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
                             snapped = true;
@@ -104909,7 +104946,10 @@ var BABYLON;
                     if (_this.snapDistance != 0) {
                         currentSnapDragDistance += angle;
                         if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
-                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
                             angle = _this.snapDistance * dragSteps;
                             snapped = true;
@@ -105478,6 +105518,11 @@ var BABYLON;
                 _dragBehavior.onDragObservable.add(function (event) {
                     _this.onRotationSphereDragObservable.notifyObservers({});
                     if (_this.attachedMesh) {
+                        var originalParent = _this.attachedMesh.parent;
+                        if (originalParent && (originalParent.scaling && originalParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                            BABYLON.Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                            return;
+                        }
                         BoundingBoxGizmo._RemoveAndStorePivotPoint(_this.attachedMesh);
                         var worldDragDirection = startingTurnDirection;
                         // Project the world right on to the drag plane
@@ -105510,6 +105555,7 @@ var BABYLON;
                             _this._anchorMesh.addChild(_this.attachedMesh);
                             _this._anchorMesh.rotationQuaternion.multiplyToRef(_this._tmpQuaternion, _this._anchorMesh.rotationQuaternion);
                             _this._anchorMesh.removeChild(_this.attachedMesh);
+                            _this.attachedMesh.setParent(originalParent);
                         }
                         _this.updateBoundingBox();
                         BoundingBoxGizmo._RestorePivotPoint(_this.attachedMesh);
@@ -105549,6 +105595,11 @@ var BABYLON;
                         _dragBehavior.onDragObservable.add(function (event) {
                             _this.onScaleBoxDragObservable.notifyObservers({});
                             if (_this.attachedMesh) {
+                                var originalParent = _this.attachedMesh.parent;
+                                if (originalParent && (originalParent.scaling && originalParent.scaling.isNonUniformWithinEpsilon(0.001))) {
+                                    BABYLON.Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                                    return;
+                                }
                                 BoundingBoxGizmo._RemoveAndStorePivotPoint(_this.attachedMesh);
                                 var relativeDragDistance = (event.dragDistance / _this._boundingDimensions.length()) * _this._anchorMesh.scaling.length();
                                 var deltaScale = new BABYLON.Vector3(relativeDragDistance, relativeDragDistance, relativeDragDistance);
@@ -105575,6 +105626,7 @@ var BABYLON;
                                     _this._anchorMesh.scaling.subtractInPlace(deltaScale);
                                 }
                                 _this._anchorMesh.removeChild(_this.attachedMesh);
+                                _this.attachedMesh.setParent(originalParent);
                                 BoundingBoxGizmo._RestorePivotPoint(_this.attachedMesh);
                             }
                             _this._updateDummy();
@@ -105668,10 +105720,15 @@ var BABYLON;
                 // Reset anchor mesh to match attached mesh's scale
                 // This is needed to avoid invalid box/sphere position on first drag
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(value);
+                var originalParent = value.parent;
                 this._anchorMesh.addChild(value);
                 this._anchorMesh.removeChild(value);
+                value.setParent(originalParent);
                 BoundingBoxGizmo._RestorePivotPoint(value);
                 this.updateBoundingBox();
+                value.getChildMeshes(false).forEach(function (m) {
+                    m.markAsDirty("scaling");
+                });
                 this.gizmoLayer.utilityLayerScene.onAfterRenderObservable.addOnce(function () {
                     _this._updateDummy();
                 });
@@ -105689,6 +105746,8 @@ var BABYLON;
         BoundingBoxGizmo.prototype.updateBoundingBox = function () {
             if (this.attachedMesh) {
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
+                var originalParent = this.attachedMesh.parent;
+                this.attachedMesh.setParent(null);
                 this._update();
                 // Rotate based on axis
                 if (!this.attachedMesh.rotationQuaternion) {
@@ -105716,6 +105775,7 @@ var BABYLON;
                 // restore position/rotation values
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
+                this.attachedMesh.setParent(originalParent);
             }
             this._updateRotationSpheres();
             this._updateScaleBoxes();
@@ -105796,6 +105856,15 @@ var BABYLON;
                 }
             });
         };
+        /**
+         * Enables/disables scaling
+         * @param enable if scaling should be enabled
+         */
+        BoundingBoxGizmo.prototype.setEnabledScaling = function (enable) {
+            this._scaleBoxesParent.getChildMeshes().forEach(function (m, i) {
+                m.setEnabled(enable);
+            });
+        };
         BoundingBoxGizmo.prototype._updateDummy = function () {
             if (this._dragMesh) {
                 this._dragMesh.position.copyFrom(this._lineBoundingBox.getAbsolutePosition());
@@ -115886,10 +115955,11 @@ var BABYLON;
          */
         MeshAssetTask.prototype.runTask = function (scene, onSuccess, onError) {
             var _this = this;
-            BABYLON.SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene, function (meshes, particleSystems, skeletons) {
+            BABYLON.SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene, function (meshes, particleSystems, skeletons, animationGroups) {
                 _this.loadedMeshes = meshes;
                 _this.loadedParticleSystems = particleSystems;
                 _this.loadedSkeletons = skeletons;
+                _this.loadedAnimationGroups = animationGroups;
                 onSuccess();
             }, null, function (scene, message, exception) {
                 onError(message, exception);

+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


+ 2 - 2
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.16"
+        "babylonjs": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

File diff suppressed because it is too large
+ 8 - 8
dist/preview release/inspector/babylon.inspector.bundle.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js.map


+ 1 - 1
dist/preview release/inspector/babylon.inspector.d.ts

@@ -1,6 +1,6 @@
 /*Babylon.js Inspector*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../tools/gulp/babylonjs
 declare module INSPECTOR {
 }
 declare module INSPECTOR {

+ 2 - 2
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -1,6 +1,6 @@
 /*Babylon.js Inspector*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../tools/gulp/babylonjs
 
 declare module 'babylonjs-inspector' {
     export * from "babylonjs-inspector/inspector";
@@ -31,7 +31,7 @@ declare module 'babylonjs-inspector/components/propertyChangedEvent' {
 
 /*Babylon.js Inspector*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../tools/gulp/babylonjs
 declare module INSPECTOR {
 }
 declare module INSPECTOR {

+ 5 - 5
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.16",
-        "babylonjs-gui": "4.0.0-alpha.16",
-        "babylonjs-loaders": "4.0.0-alpha.16",
-        "babylonjs-serializers": "4.0.0-alpha.16"
+        "babylonjs": "4.0.0-alpha.17",
+        "babylonjs-gui": "4.0.0-alpha.17",
+        "babylonjs-loaders": "4.0.0-alpha.17",
+        "babylonjs-serializers": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 16 - 4
dist/preview release/loaders/babylon.objFileLoader.d.ts

@@ -30,7 +30,18 @@ declare module BABYLON {
     }
     class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
+        /**
+         * Invert model on y-axis (does a model scaling inversion)
+         */
         static INVERT_Y: boolean;
+        /**
+         * Include in meshes the vertex colors available in some OBJ files.  This is not part of OBJ standard.
+         */
+        static IMPORT_VERTEX_COLORS: boolean;
+        /**
+         * Compute the normals for the model, even if normals are present in the file
+         */
+        static COMPUTE_NORMALS: boolean;
         name: string;
         extensions: string;
         obj: RegExp;
@@ -59,10 +70,10 @@ declare module BABYLON {
          */
         private _loadMTL;
         /**
-         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * Imports one or more meshes from the loaded OBJ data and adds them to the scene
          * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
          * @param scene the scene the meshes should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -75,9 +86,10 @@ declare module BABYLON {
             animationGroups: AnimationGroup[];
         }>;
         /**
-         * Imports all objects from the loaded glTF data and adds them to the scene
+         * Imports all objects from the loaded OBJ data and adds them to the scene
+         *
          * @param scene the scene the objects should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load

+ 85 - 32
dist/preview release/loaders/babylon.objFileLoader.js

@@ -218,7 +218,7 @@ var BABYLON;
             this.mtllib = /^mtllib /;
             this.usemtl = /^usemtl /;
             this.smooth = /^s /;
-            this.vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
+            this.vertexPattern = /v( +[\d|\.|\+|\-|e|E]+){3,7}/;
             // vn float float float
             this.normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
             // vt float float
@@ -252,10 +252,10 @@ var BABYLON;
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
         };
         /**
-         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * Imports one or more meshes from the loaded OBJ data and adds them to the scene
          * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
          * @param scene the scene the meshes should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -273,9 +273,10 @@ var BABYLON;
             });
         };
         /**
-         * Imports all objects from the loaded glTF data and adds them to the scene
+         * Imports all objects from the loaded OBJ data and adds them to the scene
+         *
          * @param scene the scene the objects should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -321,16 +322,19 @@ var BABYLON;
             var positions = []; //values for the positions of vertices
             var normals = []; //Values for the normals
             var uvs = []; //Values for the textures
+            var colors = [];
             var meshesFromObj = []; //[mesh] Contains all the obj meshes
             var handledMesh; //The current mesh of meshes array
             var indicesForBabylon = []; //The list of indices for VertexData
             var wrappedPositionForBabylon = []; //The list of position in vectors
+            var wrappedColorsForBabylon = []; // Array with all color values to match with the indices
             var wrappedUvsForBabylon = []; //Array with all value of uvs to match with the indices
             var wrappedNormalsForBabylon = []; //Array with all value of normals to match with the indices
             var tuplePosNorm = []; //Create a tuple with indice of Position, Normal, UV  [pos, norm, uvs]
             var curPositionInIndices = 0;
             var hasMeshes = false; //Meshes are defined in the file
             var unwrappedPositionsForBabylon = []; //Value of positionForBabylon w/o Vector3() [x,y,z]
+            var unwrappedColorsForBabylon = []; // Value of colorForBabylon w/o Color4() [r,g,b,a]
             var unwrappedNormalsForBabylon = []; //Value of normalsForBabylon w/o Vector3()  [x,y,z]
             var unwrappedUVForBabylon = []; //Value of uvsForBabylon w/o Vector3()      [x,y,z]
             var triangles = []; //Indices from new triangles coming from polygons
@@ -340,11 +344,12 @@ var BABYLON;
             var objMeshName = ""; //The name of the current obj mesh
             var increment = 1; //Id for meshes created by the multimaterial
             var isFirstMaterial = true;
+            var grayColor = new BABYLON.Color4(0.5, 0.5, 0.5, 1);
             /**
              * Search for obj in the given array.
              * This function is called to check if a couple of data already exists in an array.
              *
-             * If found, returns the index of the founded tuple index. Returns -1 if not found
+             * If found, returns the index of the found tuple index. Returns -1 if not found
              * @param arr Array<{ normals: Array<number>, idx: Array<number> }>
              * @param obj Array<number>
              * @returns {boolean}
@@ -379,7 +384,7 @@ var BABYLON;
              * @param textureVectorFromOBJ Vector3 The value of uvs
              * @param normalsVectorFromOBJ Vector3 The value of normals at index objNormale
              */
-            var setData = function (indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positionVectorFromOBJ, textureVectorFromOBJ, normalsVectorFromOBJ) {
+            var setData = function (indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positionVectorFromOBJ, textureVectorFromOBJ, normalsVectorFromOBJ, positionColorsFromOBJ) {
                 //Check if this tuple already exists in the list of tuples
                 var _index;
                 if (OBJFileLoader.OPTIMIZE_WITH_UV) {
@@ -410,6 +415,11 @@ var BABYLON;
                     //Push the normals for Babylon
                     //Each element is a BABYLON.Vector3(x,y,z)
                     wrappedNormalsForBabylon.push(normalsVectorFromOBJ);
+                    if (positionColorsFromOBJ !== undefined) {
+                        //Push the colors for Babylon
+                        //Each element is a BABYLON.Color4(r,g,b,a)
+                        wrappedColorsForBabylon.push(positionColorsFromOBJ);
+                    }
                     //Add the tuple in the comparison list
                     tuplePosNorm[indicePositionFromObj].normals.push(indiceNormalFromObj);
                     tuplePosNorm[indicePositionFromObj].idx.push(curPositionInIndices++);
@@ -420,12 +430,12 @@ var BABYLON;
                 else {
                     //The tuple already exists
                     //Add the index of the already existing tuple
-                    //At this index we can get the value of position, normal and uvs of vertex
+                    //At this index we can get the value of position, normal, color and uvs of vertex
                     indicesForBabylon.push(_index);
                 }
             };
             /**
-             * Transform BABYLON.Vector() object onto 3 digits in an array
+             * Transform BABYLON.Vector() and BABYLON.Color() objects into numbers in an array
              */
             var unwrapData = function () {
                 //Every array has the same length
@@ -434,11 +444,16 @@ var BABYLON;
                     unwrappedPositionsForBabylon.push(wrappedPositionForBabylon[l].x, wrappedPositionForBabylon[l].y, wrappedPositionForBabylon[l].z);
                     unwrappedNormalsForBabylon.push(wrappedNormalsForBabylon[l].x, wrappedNormalsForBabylon[l].y, wrappedNormalsForBabylon[l].z);
                     unwrappedUVForBabylon.push(wrappedUvsForBabylon[l].x, wrappedUvsForBabylon[l].y); //z is an optional value not supported by BABYLON
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        //Push the r, g, b, a values of each element in the unwrapped array
+                        unwrappedColorsForBabylon.push(wrappedColorsForBabylon[l].r, wrappedColorsForBabylon[l].g, wrappedColorsForBabylon[l].b, wrappedColorsForBabylon[l].a);
+                    }
                 }
                 // Reset arrays for the next new meshes
                 wrappedPositionForBabylon = [];
                 wrappedNormalsForBabylon = [];
                 wrappedUvsForBabylon = [];
+                wrappedColorsForBabylon = [];
                 tuplePosNorm = [];
                 curPositionInIndices = 0;
             };
@@ -489,8 +504,8 @@ var BABYLON;
                     var indicePositionFromObj = parseInt(triangles[k]) - 1;
                     setData(indicePositionFromObj, 0, 0, //In the pattern 1, normals and uvs are not defined
                     positions[indicePositionFromObj], //Get the vectors data
-                    BABYLON.Vector2.Zero(), BABYLON.Vector3.Up() //Create default vectors
-                    );
+                    BABYLON.Vector2.Zero(), BABYLON.Vector3.Up(), //Create default vectors
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -514,8 +529,8 @@ var BABYLON;
                     var indiceUvsFromObj = parseInt(point[1]) - 1;
                     setData(indicePositionFromObj, indiceUvsFromObj, 0, //Default value for normals
                     positions[indicePositionFromObj], //Get the values for each element
-                    uvs[indiceUvsFromObj], BABYLON.Vector3.Up() //Default value for normals
-                    );
+                    uvs[indiceUvsFromObj], BABYLON.Vector3.Up(), //Default value for normals
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -539,8 +554,8 @@ var BABYLON;
                     var indiceUvsFromObj = parseInt(point[1]) - 1;
                     // Set normal indice
                     var indiceNormalFromObj = parseInt(point[2]) - 1;
-                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
-                    );
+                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -562,7 +577,7 @@ var BABYLON;
                     var indiceNormalFromObj = parseInt(point[1]) - 1;
                     setData(indicePositionFromObj, 1, //Default value for uv
                     indiceNormalFromObj, positions[indicePositionFromObj], //Get each vector of data
-                    BABYLON.Vector2.Zero(), normals[indiceNormalFromObj]);
+                    BABYLON.Vector2.Zero(), normals[indiceNormalFromObj], OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -586,8 +601,8 @@ var BABYLON;
                     var indiceUvsFromObj = uvs.length + parseInt(point[1]);
                     // Set normal indice
                     var indiceNormalFromObj = normals.length + parseInt(point[2]);
-                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
-                    );
+                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -608,9 +623,13 @@ var BABYLON;
                     handledMesh.positions = unwrappedPositionsForBabylon.slice();
                     handledMesh.normals = unwrappedNormalsForBabylon.slice();
                     handledMesh.uvs = unwrappedUVForBabylon.slice();
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        handledMesh.colors = unwrappedColorsForBabylon.slice();
+                    }
                     //Reset the array for the next mesh
                     indicesForBabylon = [];
                     unwrappedPositionsForBabylon = [];
+                    unwrappedColorsForBabylon = [];
                     unwrappedNormalsForBabylon = [];
                     unwrappedUVForBabylon = [];
                 }
@@ -627,12 +646,23 @@ var BABYLON;
                     continue;
                     //Get information about one position possible for the vertices
                 }
-                else if ((result = this.vertexPattern.exec(line)) !== null) {
+                else if (this.vertexPattern.test(line)) {
+                    result = line.split(' ');
+                    //Value of result with line: "v 1.0 2.0 3.0"
+                    // ["v", "1.0", "2.0", "3.0"]
                     //Create a Vector3 with the position x, y, z
-                    //Value of result:
-                    // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
                     //Add the Vector in the list of positions
                     positions.push(new BABYLON.Vector3(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])));
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        if (result.length >= 7) {
+                            // TODO: if these numbers are > 1 we can use Color4.FromInts(r,g,b,a)
+                            colors.push(new BABYLON.Color4(parseFloat(result[4]), parseFloat(result[5]), parseFloat(result[6]), (result.length === 7 || result[7] === undefined) ? 1 : parseFloat(result[7])));
+                        }
+                        else {
+                            // TODO: maybe push NULL and if all are NULL to skip (and remove grayColor var).
+                            colors.push(grayColor);
+                        }
+                    }
                 }
                 else if ((result = this.normalPattern.exec(line)) !== null) {
                     //Create a Vector3 with the normals x, y, z
@@ -690,13 +720,12 @@ var BABYLON;
                 else if (this.group.test(line) || this.obj.test(line)) {
                     //Create a new mesh corresponding to the name of the group.
                     //Definition of the mesh
-                    var objMesh = 
-                    //Set the name of the current obj mesh
-                    {
+                    var objMesh = {
                         name: line.substring(2).trim(),
                         indices: undefined,
                         positions: undefined,
                         normals: undefined,
+                        colors: undefined,
                         uvs: undefined,
                         materialName: ""
                     };
@@ -717,13 +746,12 @@ var BABYLON;
                         //Set the data for the previous mesh
                         addPreviousObjMesh();
                         //Create a new mesh
-                        var objMesh = 
-                        //Set the name of the current obj mesh
-                        {
+                        var objMesh = {
                             name: objMeshName + "_mm" + increment.toString(),
                             indices: undefined,
                             positions: undefined,
                             normals: undefined,
+                            colors: undefined,
                             uvs: undefined,
                             materialName: materialNameFromObj
                         };
@@ -758,7 +786,7 @@ var BABYLON;
             if (hasMeshes) {
                 //Set the data for the last mesh
                 handledMesh = meshesFromObj[meshesFromObj.length - 1];
-                //Reverse indices for displaying faces in the good sens
+                //Reverse indices for displaying faces in the good sense
                 indicesForBabylon.reverse();
                 //Get the good array
                 unwrapData();
@@ -767,8 +795,11 @@ var BABYLON;
                 handledMesh.positions = unwrappedPositionsForBabylon;
                 handledMesh.normals = unwrappedNormalsForBabylon;
                 handledMesh.uvs = unwrappedUVForBabylon;
+                if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                    handledMesh.colors = unwrappedColorsForBabylon;
+                }
             }
-            //If any o or g keyword found, create a mesj with a random id
+            //If any o or g keyword found, create a mesh with a random id
             if (!hasMeshes) {
                 // reverse tab of indices
                 indicesForBabylon.reverse();
@@ -779,6 +810,7 @@ var BABYLON;
                     name: BABYLON.Geometry.RandomId(),
                     indices: indicesForBabylon,
                     positions: unwrappedPositionsForBabylon,
+                    colors: unwrappedColorsForBabylon,
                     normals: unwrappedNormalsForBabylon,
                     uvs: unwrappedUVForBabylon,
                     materialName: materialNameFromObj
@@ -811,11 +843,21 @@ var BABYLON;
                 //This is indispensable for the importMesh function
                 materialToUse.push(meshesFromObj[j].materialName);
                 var vertexData = new BABYLON.VertexData(); //The container for the values
-                //Set the data for the babylonMesh
-                vertexData.positions = handledMesh.positions;
-                vertexData.normals = handledMesh.normals;
+                //Set the vertex data for the babylonMesh
                 vertexData.uvs = handledMesh.uvs;
                 vertexData.indices = handledMesh.indices;
+                vertexData.positions = handledMesh.positions;
+                if (OBJFileLoader.COMPUTE_NORMALS === true) {
+                    var normals_1 = new Array();
+                    BABYLON.VertexData.ComputeNormals(handledMesh.positions, handledMesh.indices, normals_1);
+                    vertexData.normals = normals_1;
+                }
+                else {
+                    vertexData.normals = handledMesh.normals;
+                }
+                if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                    vertexData.colors = handledMesh.colors;
+                }
                 //Set the data from the VertexBuffer to the current BABYLON.Mesh
                 vertexData.applyToMesh(babylonMesh);
                 if (OBJFileLoader.INVERT_Y) {
@@ -873,7 +915,18 @@ var BABYLON;
             });
         };
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
+        /**
+         * Invert model on y-axis (does a model scaling inversion)
+         */
         OBJFileLoader.INVERT_Y = false;
+        /**
+         * Include in meshes the vertex colors available in some OBJ files.  This is not part of OBJ standard.
+         */
+        OBJFileLoader.IMPORT_VERTEX_COLORS = false;
+        /**
+         * Compute the normals for the model, even if normals are present in the file
+         */
+        OBJFileLoader.COMPUTE_NORMALS = false;
         return OBJFileLoader;
     }());
     BABYLON.OBJFileLoader = OBJFileLoader;

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 16 - 4
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -48,7 +48,18 @@ declare module BABYLON {
     }
     class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
+        /**
+         * Invert model on y-axis (does a model scaling inversion)
+         */
         static INVERT_Y: boolean;
+        /**
+         * Include in meshes the vertex colors available in some OBJ files.  This is not part of OBJ standard.
+         */
+        static IMPORT_VERTEX_COLORS: boolean;
+        /**
+         * Compute the normals for the model, even if normals are present in the file
+         */
+        static COMPUTE_NORMALS: boolean;
         name: string;
         extensions: string;
         obj: RegExp;
@@ -77,10 +88,10 @@ declare module BABYLON {
          */
         private _loadMTL;
         /**
-         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * Imports one or more meshes from the loaded OBJ data and adds them to the scene
          * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
          * @param scene the scene the meshes should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -93,9 +104,10 @@ declare module BABYLON {
             animationGroups: AnimationGroup[];
         }>;
         /**
-         * Imports all objects from the loaded glTF data and adds them to the scene
+         * Imports all objects from the loaded OBJ data and adds them to the scene
+         *
          * @param scene the scene the objects should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load

+ 85 - 32
dist/preview release/loaders/babylonjs.loaders.js

@@ -417,7 +417,7 @@ var BABYLON;
             this.mtllib = /^mtllib /;
             this.usemtl = /^usemtl /;
             this.smooth = /^s /;
-            this.vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
+            this.vertexPattern = /v( +[\d|\.|\+|\-|e|E]+){3,7}/;
             // vn float float float
             this.normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
             // vt float float
@@ -451,10 +451,10 @@ var BABYLON;
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
         };
         /**
-         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * Imports one or more meshes from the loaded OBJ data and adds them to the scene
          * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
          * @param scene the scene the meshes should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -472,9 +472,10 @@ var BABYLON;
             });
         };
         /**
-         * Imports all objects from the loaded glTF data and adds them to the scene
+         * Imports all objects from the loaded OBJ data and adds them to the scene
+         *
          * @param scene the scene the objects should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -520,16 +521,19 @@ var BABYLON;
             var positions = []; //values for the positions of vertices
             var normals = []; //Values for the normals
             var uvs = []; //Values for the textures
+            var colors = [];
             var meshesFromObj = []; //[mesh] Contains all the obj meshes
             var handledMesh; //The current mesh of meshes array
             var indicesForBabylon = []; //The list of indices for VertexData
             var wrappedPositionForBabylon = []; //The list of position in vectors
+            var wrappedColorsForBabylon = []; // Array with all color values to match with the indices
             var wrappedUvsForBabylon = []; //Array with all value of uvs to match with the indices
             var wrappedNormalsForBabylon = []; //Array with all value of normals to match with the indices
             var tuplePosNorm = []; //Create a tuple with indice of Position, Normal, UV  [pos, norm, uvs]
             var curPositionInIndices = 0;
             var hasMeshes = false; //Meshes are defined in the file
             var unwrappedPositionsForBabylon = []; //Value of positionForBabylon w/o Vector3() [x,y,z]
+            var unwrappedColorsForBabylon = []; // Value of colorForBabylon w/o Color4() [r,g,b,a]
             var unwrappedNormalsForBabylon = []; //Value of normalsForBabylon w/o Vector3()  [x,y,z]
             var unwrappedUVForBabylon = []; //Value of uvsForBabylon w/o Vector3()      [x,y,z]
             var triangles = []; //Indices from new triangles coming from polygons
@@ -539,11 +543,12 @@ var BABYLON;
             var objMeshName = ""; //The name of the current obj mesh
             var increment = 1; //Id for meshes created by the multimaterial
             var isFirstMaterial = true;
+            var grayColor = new BABYLON.Color4(0.5, 0.5, 0.5, 1);
             /**
              * Search for obj in the given array.
              * This function is called to check if a couple of data already exists in an array.
              *
-             * If found, returns the index of the founded tuple index. Returns -1 if not found
+             * If found, returns the index of the found tuple index. Returns -1 if not found
              * @param arr Array<{ normals: Array<number>, idx: Array<number> }>
              * @param obj Array<number>
              * @returns {boolean}
@@ -578,7 +583,7 @@ var BABYLON;
              * @param textureVectorFromOBJ Vector3 The value of uvs
              * @param normalsVectorFromOBJ Vector3 The value of normals at index objNormale
              */
-            var setData = function (indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positionVectorFromOBJ, textureVectorFromOBJ, normalsVectorFromOBJ) {
+            var setData = function (indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positionVectorFromOBJ, textureVectorFromOBJ, normalsVectorFromOBJ, positionColorsFromOBJ) {
                 //Check if this tuple already exists in the list of tuples
                 var _index;
                 if (OBJFileLoader.OPTIMIZE_WITH_UV) {
@@ -609,6 +614,11 @@ var BABYLON;
                     //Push the normals for Babylon
                     //Each element is a BABYLON.Vector3(x,y,z)
                     wrappedNormalsForBabylon.push(normalsVectorFromOBJ);
+                    if (positionColorsFromOBJ !== undefined) {
+                        //Push the colors for Babylon
+                        //Each element is a BABYLON.Color4(r,g,b,a)
+                        wrappedColorsForBabylon.push(positionColorsFromOBJ);
+                    }
                     //Add the tuple in the comparison list
                     tuplePosNorm[indicePositionFromObj].normals.push(indiceNormalFromObj);
                     tuplePosNorm[indicePositionFromObj].idx.push(curPositionInIndices++);
@@ -619,12 +629,12 @@ var BABYLON;
                 else {
                     //The tuple already exists
                     //Add the index of the already existing tuple
-                    //At this index we can get the value of position, normal and uvs of vertex
+                    //At this index we can get the value of position, normal, color and uvs of vertex
                     indicesForBabylon.push(_index);
                 }
             };
             /**
-             * Transform BABYLON.Vector() object onto 3 digits in an array
+             * Transform BABYLON.Vector() and BABYLON.Color() objects into numbers in an array
              */
             var unwrapData = function () {
                 //Every array has the same length
@@ -633,11 +643,16 @@ var BABYLON;
                     unwrappedPositionsForBabylon.push(wrappedPositionForBabylon[l].x, wrappedPositionForBabylon[l].y, wrappedPositionForBabylon[l].z);
                     unwrappedNormalsForBabylon.push(wrappedNormalsForBabylon[l].x, wrappedNormalsForBabylon[l].y, wrappedNormalsForBabylon[l].z);
                     unwrappedUVForBabylon.push(wrappedUvsForBabylon[l].x, wrappedUvsForBabylon[l].y); //z is an optional value not supported by BABYLON
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        //Push the r, g, b, a values of each element in the unwrapped array
+                        unwrappedColorsForBabylon.push(wrappedColorsForBabylon[l].r, wrappedColorsForBabylon[l].g, wrappedColorsForBabylon[l].b, wrappedColorsForBabylon[l].a);
+                    }
                 }
                 // Reset arrays for the next new meshes
                 wrappedPositionForBabylon = [];
                 wrappedNormalsForBabylon = [];
                 wrappedUvsForBabylon = [];
+                wrappedColorsForBabylon = [];
                 tuplePosNorm = [];
                 curPositionInIndices = 0;
             };
@@ -688,8 +703,8 @@ var BABYLON;
                     var indicePositionFromObj = parseInt(triangles[k]) - 1;
                     setData(indicePositionFromObj, 0, 0, //In the pattern 1, normals and uvs are not defined
                     positions[indicePositionFromObj], //Get the vectors data
-                    BABYLON.Vector2.Zero(), BABYLON.Vector3.Up() //Create default vectors
-                    );
+                    BABYLON.Vector2.Zero(), BABYLON.Vector3.Up(), //Create default vectors
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -713,8 +728,8 @@ var BABYLON;
                     var indiceUvsFromObj = parseInt(point[1]) - 1;
                     setData(indicePositionFromObj, indiceUvsFromObj, 0, //Default value for normals
                     positions[indicePositionFromObj], //Get the values for each element
-                    uvs[indiceUvsFromObj], BABYLON.Vector3.Up() //Default value for normals
-                    );
+                    uvs[indiceUvsFromObj], BABYLON.Vector3.Up(), //Default value for normals
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -738,8 +753,8 @@ var BABYLON;
                     var indiceUvsFromObj = parseInt(point[1]) - 1;
                     // Set normal indice
                     var indiceNormalFromObj = parseInt(point[2]) - 1;
-                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
-                    );
+                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -761,7 +776,7 @@ var BABYLON;
                     var indiceNormalFromObj = parseInt(point[1]) - 1;
                     setData(indicePositionFromObj, 1, //Default value for uv
                     indiceNormalFromObj, positions[indicePositionFromObj], //Get each vector of data
-                    BABYLON.Vector2.Zero(), normals[indiceNormalFromObj]);
+                    BABYLON.Vector2.Zero(), normals[indiceNormalFromObj], OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -785,8 +800,8 @@ var BABYLON;
                     var indiceUvsFromObj = uvs.length + parseInt(point[1]);
                     // Set normal indice
                     var indiceNormalFromObj = normals.length + parseInt(point[2]);
-                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
-                    );
+                    setData(indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj, positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
+                    OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined);
                 }
                 //Reset variable for the next line
                 triangles = [];
@@ -807,9 +822,13 @@ var BABYLON;
                     handledMesh.positions = unwrappedPositionsForBabylon.slice();
                     handledMesh.normals = unwrappedNormalsForBabylon.slice();
                     handledMesh.uvs = unwrappedUVForBabylon.slice();
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        handledMesh.colors = unwrappedColorsForBabylon.slice();
+                    }
                     //Reset the array for the next mesh
                     indicesForBabylon = [];
                     unwrappedPositionsForBabylon = [];
+                    unwrappedColorsForBabylon = [];
                     unwrappedNormalsForBabylon = [];
                     unwrappedUVForBabylon = [];
                 }
@@ -826,12 +845,23 @@ var BABYLON;
                     continue;
                     //Get information about one position possible for the vertices
                 }
-                else if ((result = this.vertexPattern.exec(line)) !== null) {
+                else if (this.vertexPattern.test(line)) {
+                    result = line.split(' ');
+                    //Value of result with line: "v 1.0 2.0 3.0"
+                    // ["v", "1.0", "2.0", "3.0"]
                     //Create a Vector3 with the position x, y, z
-                    //Value of result:
-                    // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
                     //Add the Vector in the list of positions
                     positions.push(new BABYLON.Vector3(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])));
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        if (result.length >= 7) {
+                            // TODO: if these numbers are > 1 we can use Color4.FromInts(r,g,b,a)
+                            colors.push(new BABYLON.Color4(parseFloat(result[4]), parseFloat(result[5]), parseFloat(result[6]), (result.length === 7 || result[7] === undefined) ? 1 : parseFloat(result[7])));
+                        }
+                        else {
+                            // TODO: maybe push NULL and if all are NULL to skip (and remove grayColor var).
+                            colors.push(grayColor);
+                        }
+                    }
                 }
                 else if ((result = this.normalPattern.exec(line)) !== null) {
                     //Create a Vector3 with the normals x, y, z
@@ -889,13 +919,12 @@ var BABYLON;
                 else if (this.group.test(line) || this.obj.test(line)) {
                     //Create a new mesh corresponding to the name of the group.
                     //Definition of the mesh
-                    var objMesh = 
-                    //Set the name of the current obj mesh
-                    {
+                    var objMesh = {
                         name: line.substring(2).trim(),
                         indices: undefined,
                         positions: undefined,
                         normals: undefined,
+                        colors: undefined,
                         uvs: undefined,
                         materialName: ""
                     };
@@ -916,13 +945,12 @@ var BABYLON;
                         //Set the data for the previous mesh
                         addPreviousObjMesh();
                         //Create a new mesh
-                        var objMesh = 
-                        //Set the name of the current obj mesh
-                        {
+                        var objMesh = {
                             name: objMeshName + "_mm" + increment.toString(),
                             indices: undefined,
                             positions: undefined,
                             normals: undefined,
+                            colors: undefined,
                             uvs: undefined,
                             materialName: materialNameFromObj
                         };
@@ -957,7 +985,7 @@ var BABYLON;
             if (hasMeshes) {
                 //Set the data for the last mesh
                 handledMesh = meshesFromObj[meshesFromObj.length - 1];
-                //Reverse indices for displaying faces in the good sens
+                //Reverse indices for displaying faces in the good sense
                 indicesForBabylon.reverse();
                 //Get the good array
                 unwrapData();
@@ -966,8 +994,11 @@ var BABYLON;
                 handledMesh.positions = unwrappedPositionsForBabylon;
                 handledMesh.normals = unwrappedNormalsForBabylon;
                 handledMesh.uvs = unwrappedUVForBabylon;
+                if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                    handledMesh.colors = unwrappedColorsForBabylon;
+                }
             }
-            //If any o or g keyword found, create a mesj with a random id
+            //If any o or g keyword found, create a mesh with a random id
             if (!hasMeshes) {
                 // reverse tab of indices
                 indicesForBabylon.reverse();
@@ -978,6 +1009,7 @@ var BABYLON;
                     name: BABYLON.Geometry.RandomId(),
                     indices: indicesForBabylon,
                     positions: unwrappedPositionsForBabylon,
+                    colors: unwrappedColorsForBabylon,
                     normals: unwrappedNormalsForBabylon,
                     uvs: unwrappedUVForBabylon,
                     materialName: materialNameFromObj
@@ -1010,11 +1042,21 @@ var BABYLON;
                 //This is indispensable for the importMesh function
                 materialToUse.push(meshesFromObj[j].materialName);
                 var vertexData = new BABYLON.VertexData(); //The container for the values
-                //Set the data for the babylonMesh
-                vertexData.positions = handledMesh.positions;
-                vertexData.normals = handledMesh.normals;
+                //Set the vertex data for the babylonMesh
                 vertexData.uvs = handledMesh.uvs;
                 vertexData.indices = handledMesh.indices;
+                vertexData.positions = handledMesh.positions;
+                if (OBJFileLoader.COMPUTE_NORMALS === true) {
+                    var normals_1 = new Array();
+                    BABYLON.VertexData.ComputeNormals(handledMesh.positions, handledMesh.indices, normals_1);
+                    vertexData.normals = normals_1;
+                }
+                else {
+                    vertexData.normals = handledMesh.normals;
+                }
+                if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                    vertexData.colors = handledMesh.colors;
+                }
                 //Set the data from the VertexBuffer to the current BABYLON.Mesh
                 vertexData.applyToMesh(babylonMesh);
                 if (OBJFileLoader.INVERT_Y) {
@@ -1072,7 +1114,18 @@ var BABYLON;
             });
         };
         OBJFileLoader.OPTIMIZE_WITH_UV = false;
+        /**
+         * Invert model on y-axis (does a model scaling inversion)
+         */
         OBJFileLoader.INVERT_Y = false;
+        /**
+         * Include in meshes the vertex colors available in some OBJ files.  This is not part of OBJ standard.
+         */
+        OBJFileLoader.IMPORT_VERTEX_COLORS = false;
+        /**
+         * Compute the normals for the model, even if normals are present in the file
+         */
+        OBJFileLoader.COMPUTE_NORMALS = false;
         return OBJFileLoader;
     }());
     BABYLON.OBJFileLoader = OBJFileLoader;

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.min.js


+ 16 - 4
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -55,7 +55,18 @@ declare module BABYLON {
     }
     class OBJFileLoader implements ISceneLoaderPluginAsync {
         static OPTIMIZE_WITH_UV: boolean;
+        /**
+         * Invert model on y-axis (does a model scaling inversion)
+         */
         static INVERT_Y: boolean;
+        /**
+         * Include in meshes the vertex colors available in some OBJ files.  This is not part of OBJ standard.
+         */
+        static IMPORT_VERTEX_COLORS: boolean;
+        /**
+         * Compute the normals for the model, even if normals are present in the file
+         */
+        static COMPUTE_NORMALS: boolean;
         name: string;
         extensions: string;
         obj: RegExp;
@@ -84,10 +95,10 @@ declare module BABYLON {
          */
         private _loadMTL;
         /**
-         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * Imports one or more meshes from the loaded OBJ data and adds them to the scene
          * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
          * @param scene the scene the meshes should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -100,9 +111,10 @@ declare module BABYLON {
             animationGroups: AnimationGroup[];
         }>;
         /**
-         * Imports all objects from the loaded glTF data and adds them to the scene
+         * Imports all objects from the loaded OBJ data and adds them to the scene
+         *
          * @param scene the scene the objects should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load

+ 3 - 3
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.16",
-        "babylonjs": "4.0.0-alpha.16"
+        "babylonjs-gltf2interface": "4.0.0-alpha.17",
+        "babylonjs": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.16"
+        "babylonjs": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js


+ 2 - 2
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.16"
+        "babylonjs": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.16"
+        "babylonjs": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 3 - 3
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.16",
-        "babylonjs-gltf2interface": "4.0.0-alpha.16"
+        "babylonjs": "4.0.0-alpha.17",
+        "babylonjs-gltf2interface": "4.0.0-alpha.17"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/viewer/babylon.viewer.d.ts

@@ -4,8 +4,8 @@
 declare module "babylonjs-loaders"{ export=BABYLON;}
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
-//   ../../../../../Tools/Gulp/babylonjs
-//   ../../../../../Tools/Gulp/babylonjs-loaders
+//   ../../../../../tools/gulp/babylonjs
+//   ../../../../../tools/gulp/babylonjs-loaders
 declare module BabylonViewer {
     /**
         * BabylonJS Viewer

File diff suppressed because it is too large
+ 3 - 3
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 6 - 6
dist/preview release/viewer/babylon.viewer.max.js


+ 2 - 2
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -5,8 +5,8 @@ declare module "babylonjs-loaders"{ export=BABYLON;}
 
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
-//   ../../../../../Tools/Gulp/babylonjs
-//   ../../../../../Tools/Gulp/babylonjs-loaders
+//   ../../../../../tools/gulp/babylonjs
+//   ../../../../../tools/gulp/babylonjs-loaders
 
 declare module 'babylonjs-viewer' {
     import { mapperManager } from 'babylonjs-viewer/configuration/mappers';

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

@@ -88,6 +88,9 @@
 - Enable dragging in boundingBoxGizmo without needing a parent ([TrevorDev](https://github.com/TrevorDev))
 - Added per mesh culling strategy ([jerome](https://github.com/jbousquie))
 
+### OBJ Loader
+- Add color vertex support (not part of standard) ([brianzinn](https://github.com/brianzinn))
+
 ### glTF Loader
 
 - Added support for mesh instancing for improved performance when multiple nodes point to the same mesh ([bghgary](https://github.com/bghgary))
@@ -131,8 +134,11 @@
 - Update physics position using absolutePosition instead of pivotPosition ([TrevorDev](https://github.com/TrevorDev))
 - Disable camera arrow key controls when the Command key is selected on Mac OS ([kcoley](https://github.com/kcoley))
 - Viewer should not set receiveShadows on an instanced mesh ([TrevorDev](https://github.com/TrevorDev))
-- Updated comment in TransformNode.rotationQuaternion to include undefined as one of the potential return values ([nathankmiller](https://github.com/nathankmiller)
-- CannonJS ignores connectedPivot joint parameter ([TrevorDev](https://github.com/TrevorDev)))
+- Rotation/Scaling snapping not working in the negative direction ([TrevorDev](https://github.com/TrevorDev))
+- Updated comment in TransformNode.rotationQuaternion to include undefined as one of the potential return values ([nathankmiller](https://github.com/nathankmiller))
+- CannonJS ignores connectedPivot joint parameter ([TrevorDev](https://github.com/TrevorDev))
+- Fix case sensitive paths ([mrdunk](https://github.com))
+- Attaching a BoundingBoxGizmo on a child should not remove its parent ([TrevorDev](https://github.com/TrevorDev)))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))

File diff suppressed because it is too large
+ 1087 - 0
gui/src/2D/controls/colorpicker.ts


+ 1 - 1
inspector/index.html

@@ -7,7 +7,7 @@
     <meta name="viewport" content="width=device-width, user-scalable=no">
 
     <!--Babylon-->
-    <script src="../tools/DevLoader/BabylonLoader.js"></script>
+    <script src="../Tools/DevLoader/BabylonLoader.js"></script>
 
     <style>
         html,

+ 116 - 50
loaders/src/OBJ/babylon.objFileLoader.ts

@@ -201,10 +201,31 @@ module BABYLON {
         }
     }
 
+    type MeshObject = {
+        name: string;
+        indices?: Array<number>;
+        positions?: Array<number>;
+        normals?: Array<number>;
+        colors?: Array<number>;
+        uvs?: Array<number>;
+        materialName: string;
+    };
+
     export class OBJFileLoader implements ISceneLoaderPluginAsync {
 
         public static OPTIMIZE_WITH_UV = false;
+        /**
+         * Invert model on y-axis (does a model scaling inversion)
+         */
         public static INVERT_Y = false;
+        /**
+         * Include in meshes the vertex colors available in some OBJ files.  This is not part of OBJ standard.
+         */
+        public static IMPORT_VERTEX_COLORS = false;
+        /**
+         * Compute the normals for the model, even if normals are present in the file
+         */
+        public static COMPUTE_NORMALS = false;
         public name = "obj";
         public extensions = ".obj";
         public obj = /^o/;
@@ -212,7 +233,7 @@ module BABYLON {
         public mtllib = /^mtllib /;
         public usemtl = /^usemtl /;
         public smooth = /^s /;
-        public vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
+        public vertexPattern = /v( +[\d|\.|\+|\-|e|E]+){3,7}/;
         // vn float float float
         public normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/;
         // vt float float
@@ -253,10 +274,10 @@ module BABYLON {
         }
 
         /**
-         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * Imports one or more meshes from the loaded OBJ data and adds them to the scene
          * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
          * @param scene the scene the meshes should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -275,9 +296,10 @@ module BABYLON {
         }
 
         /**
-         * Imports all objects from the loaded glTF data and adds them to the scene
+         * Imports all objects from the loaded OBJ data and adds them to the scene
+         *
          * @param scene the scene the objects should be added to
-         * @param data the glTF data to load
+         * @param data the OBJ data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param fileName Defines the name of the file to load
@@ -321,20 +343,22 @@ module BABYLON {
          * @private
          */
         private _parseSolid(meshesNames: any, scene: BABYLON.Scene, data: string, rootUrl: string): Promise<Array<AbstractMesh>> {
-
             var positions: Array<BABYLON.Vector3> = [];      //values for the positions of vertices
             var normals: Array<BABYLON.Vector3> = [];      //Values for the normals
             var uvs: Array<BABYLON.Vector2> = [];      //Values for the textures
-            var meshesFromObj: Array<any> = [];      //[mesh] Contains all the obj meshes
-            var handledMesh: any;      //The current mesh of meshes array
+            var colors: Array<BABYLON.Color4> = [];
+            var meshesFromObj: Array<MeshObject> = [];      //[mesh] Contains all the obj meshes
+            var handledMesh: MeshObject;      //The current mesh of meshes array
             var indicesForBabylon: Array<number> = [];      //The list of indices for VertexData
             var wrappedPositionForBabylon: Array<BABYLON.Vector3> = [];      //The list of position in vectors
+            var wrappedColorsForBabylon: Array<BABYLON.Color4> = []; // Array with all color values to match with the indices
             var wrappedUvsForBabylon: Array<BABYLON.Vector2> = [];      //Array with all value of uvs to match with the indices
             var wrappedNormalsForBabylon: Array<BABYLON.Vector3> = [];      //Array with all value of normals to match with the indices
             var tuplePosNorm: Array<{ normals: Array<number>; idx: Array<number>; uv: Array<number> }> = [];      //Create a tuple with indice of Position, Normal, UV  [pos, norm, uvs]
             var curPositionInIndices = 0;
             var hasMeshes: Boolean = false;   //Meshes are defined in the file
-            var unwrappedPositionsForBabylon: Array<number> = [];      //Value of positionForBabylon w/o Vector3() [x,y,z]
+            var unwrappedPositionsForBabylon: Array<number> = [];    //Value of positionForBabylon w/o Vector3() [x,y,z]
+            var unwrappedColorsForBabylon: Array<number> = [];       // Value of colorForBabylon w/o Color4() [r,g,b,a]
             var unwrappedNormalsForBabylon: Array<number> = [];      //Value of normalsForBabylon w/o Vector3()  [x,y,z]
             var unwrappedUVForBabylon: Array<number> = [];      //Value of uvsForBabylon w/o Vector3()      [x,y,z]
             var triangles: Array<string> = [];      //Indices from new triangles coming from polygons
@@ -345,11 +369,13 @@ module BABYLON {
             var increment: number = 1;      //Id for meshes created by the multimaterial
             var isFirstMaterial: boolean = true;
 
+            var grayColor = new BABYLON.Color4(0.5, 0.5, 0.5, 1);
+
             /**
              * Search for obj in the given array.
              * This function is called to check if a couple of data already exists in an array.
              *
-             * If found, returns the index of the founded tuple index. Returns -1 if not found
+             * If found, returns the index of the found tuple index. Returns -1 if not found
              * @param arr Array<{ normals: Array<number>, idx: Array<number> }>
              * @param obj Array<number>
              * @returns {boolean}
@@ -383,7 +409,7 @@ module BABYLON {
              * @param textureVectorFromOBJ Vector3 The value of uvs
              * @param normalsVectorFromOBJ Vector3 The value of normals at index objNormale
              */
-            var setData = (indicePositionFromObj: number, indiceUvsFromObj: number, indiceNormalFromObj: number, positionVectorFromOBJ: BABYLON.Vector3, textureVectorFromOBJ: BABYLON.Vector2, normalsVectorFromOBJ: BABYLON.Vector3) => {
+            var setData = (indicePositionFromObj: number, indiceUvsFromObj: number, indiceNormalFromObj: number, positionVectorFromOBJ: BABYLON.Vector3, textureVectorFromOBJ: BABYLON.Vector2, normalsVectorFromOBJ: BABYLON.Vector3, positionColorsFromOBJ?: BABYLON.Color4) => {
                 //Check if this tuple already exists in the list of tuples
                 var _index: number;
                 if (OBJFileLoader.OPTIMIZE_WITH_UV) {
@@ -421,6 +447,13 @@ module BABYLON {
                     //Push the normals for Babylon
                     //Each element is a BABYLON.Vector3(x,y,z)
                     wrappedNormalsForBabylon.push(normalsVectorFromOBJ);
+
+                    if (positionColorsFromOBJ !== undefined) {
+                        //Push the colors for Babylon
+                        //Each element is a BABYLON.Color4(r,g,b,a)
+                        wrappedColorsForBabylon.push(positionColorsFromOBJ);
+                    }
+
                     //Add the tuple in the comparison list
                     tuplePosNorm[indicePositionFromObj].normals.push(indiceNormalFromObj);
                     tuplePosNorm[indicePositionFromObj].idx.push(curPositionInIndices++);
@@ -428,13 +461,13 @@ module BABYLON {
                 } else {
                     //The tuple already exists
                     //Add the index of the already existing tuple
-                    //At this index we can get the value of position, normal and uvs of vertex
+                    //At this index we can get the value of position, normal, color and uvs of vertex
                     indicesForBabylon.push(_index);
                 }
             };
 
             /**
-             * Transform BABYLON.Vector() object onto 3 digits in an array
+             * Transform BABYLON.Vector() and BABYLON.Color() objects into numbers in an array
              */
             var unwrapData = () => {
                 //Every array has the same length
@@ -443,11 +476,16 @@ module BABYLON {
                     unwrappedPositionsForBabylon.push(wrappedPositionForBabylon[l].x, wrappedPositionForBabylon[l].y, wrappedPositionForBabylon[l].z);
                     unwrappedNormalsForBabylon.push(wrappedNormalsForBabylon[l].x, wrappedNormalsForBabylon[l].y, wrappedNormalsForBabylon[l].z);
                     unwrappedUVForBabylon.push(wrappedUvsForBabylon[l].x, wrappedUvsForBabylon[l].y); //z is an optional value not supported by BABYLON
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        //Push the r, g, b, a values of each element in the unwrapped array
+                        unwrappedColorsForBabylon.push(wrappedColorsForBabylon[l].r, wrappedColorsForBabylon[l].g, wrappedColorsForBabylon[l].b, wrappedColorsForBabylon[l].a);
+                    }
                 }
                 // Reset arrays for the next new meshes
                 wrappedPositionForBabylon = [];
                 wrappedNormalsForBabylon = [];
                 wrappedUvsForBabylon = [];
+                wrappedColorsForBabylon = [];
                 tuplePosNorm = [];
                 curPositionInIndices = 0;
             };
@@ -504,7 +542,8 @@ module BABYLON {
                         indicePositionFromObj,
                         0, 0,                                           //In the pattern 1, normals and uvs are not defined
                         positions[indicePositionFromObj],               //Get the vectors data
-                        BABYLON.Vector2.Zero(), BABYLON.Vector3.Up()    //Create default vectors
+                        BABYLON.Vector2.Zero(), BABYLON.Vector3.Up(),    //Create default vectors
+                        OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
                     );
                 }
                 //Reset variable for the next line
@@ -535,7 +574,8 @@ module BABYLON {
                         0,                                  //Default value for normals
                         positions[indicePositionFromObj],   //Get the values for each element
                         uvs[indiceUvsFromObj],
-                        BABYLON.Vector3.Up()                //Default value for normals
+                        BABYLON.Vector3.Up(),                //Default value for normals
+                        OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
                     );
                 }
 
@@ -566,7 +606,8 @@ module BABYLON {
 
                     setData(
                         indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj,
-                        positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
+                        positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
+                        OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
                     );
 
                 }
@@ -597,7 +638,8 @@ module BABYLON {
                         indiceNormalFromObj,
                         positions[indicePositionFromObj], //Get each vector of data
                         BABYLON.Vector2.Zero(),
-                        normals[indiceNormalFromObj]
+                        normals[indiceNormalFromObj],
+                        OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
                     );
                 }
                 //Reset variable for the next line
@@ -627,7 +669,8 @@ module BABYLON {
 
                     setData(
                         indicePositionFromObj, indiceUvsFromObj, indiceNormalFromObj,
-                        positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj] //Set the vector for each component
+                        positions[indicePositionFromObj], uvs[indiceUvsFromObj], normals[indiceNormalFromObj], //Set the vector for each component
+                        OBJFileLoader.IMPORT_VERTEX_COLORS === true ? colors[indicePositionFromObj] : undefined
                     );
 
                 }
@@ -655,9 +698,14 @@ module BABYLON {
                     handledMesh.normals = unwrappedNormalsForBabylon.slice();
                     handledMesh.uvs = unwrappedUVForBabylon.slice();
 
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        handledMesh.colors = unwrappedColorsForBabylon.slice();
+                    }
+
                     //Reset the array for the next mesh
                     indicesForBabylon = [];
                     unwrappedPositionsForBabylon = [];
+                    unwrappedColorsForBabylon = [];
                     unwrappedNormalsForBabylon = [];
                     unwrappedUVForBabylon = [];
                 }
@@ -676,10 +724,12 @@ module BABYLON {
                     continue;
 
                     //Get information about one position possible for the vertices
-                } else if ((result = this.vertexPattern.exec(line)) !== null) {
+                } else if (this.vertexPattern.test(line)) {
+                    result = line.split(' ');
+                    //Value of result with line: "v 1.0 2.0 3.0"
+                    // ["v", "1.0", "2.0", "3.0"]
+
                     //Create a Vector3 with the position x, y, z
-                    //Value of result:
-                    // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
                     //Add the Vector in the list of positions
                     positions.push(new BABYLON.Vector3(
                         parseFloat(result[1]),
@@ -687,6 +737,21 @@ module BABYLON {
                         parseFloat(result[3])
                     ));
 
+                    if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                        if (result.length >= 7) {
+                            // TODO: if these numbers are > 1 we can use Color4.FromInts(r,g,b,a)
+                            colors.push(new BABYLON.Color4(
+                                parseFloat(result[4]),
+                                parseFloat(result[5]),
+                                parseFloat(result[6]),
+                                (result.length === 7 || result[7] === undefined) ? 1 : parseFloat(result[7])
+                            ));
+                        } else {
+                            // TODO: maybe push NULL and if all are NULL to skip (and remove grayColor var).
+                            colors.push(grayColor);
+                        }
+                    }
+
                 } else if ((result = this.normalPattern.exec(line)) !== null) {
                     //Create a Vector3 with the normals x, y, z
                     //Value of result
@@ -765,20 +830,12 @@ module BABYLON {
                 } else if (this.group.test(line) || this.obj.test(line)) {
                     //Create a new mesh corresponding to the name of the group.
                     //Definition of the mesh
-                    var objMesh: {
-                        name: string;
-                        indices?: Array<number>;
-                        positions?: Array<number>;
-                        normals?: Array<number>;
-                        uvs?: Array<number>;
-                        materialName: string;
-                    } =
-                    //Set the name of the current obj mesh
-                    {
-                        name: line.substring(2).trim(),
+                    var objMesh: MeshObject = {
+                        name: line.substring(2).trim(), //Set the name of the current obj mesh
                         indices: undefined,
                         positions: undefined,
                         normals: undefined,
+                        colors: undefined,
                         uvs: undefined,
                         materialName: ""
                     };
@@ -802,20 +859,12 @@ module BABYLON {
                         //Set the data for the previous mesh
                         addPreviousObjMesh();
                         //Create a new mesh
-                        var objMesh: {
-                            name: string;
-                            indices?: Array<number>;
-                            positions?: Array<number>;
-                            normals?: Array<number>;
-                            uvs?: Array<number>;
-                            materialName: string;
-                        } =
-                        //Set the name of the current obj mesh
-                        {
-                            name: objMeshName + "_mm" + increment.toString(),
+                        var objMesh: MeshObject = {
+                            name: objMeshName + "_mm" + increment.toString(), //Set the name of the current obj mesh
                             indices: undefined,
                             positions: undefined,
                             normals: undefined,
+                            colors: undefined,
                             uvs: undefined,
                             materialName: materialNameFromObj
                         };
@@ -851,7 +900,7 @@ module BABYLON {
                 //Set the data for the last mesh
                 handledMesh = meshesFromObj[meshesFromObj.length - 1];
 
-                //Reverse indices for displaying faces in the good sens
+                //Reverse indices for displaying faces in the good sense
                 indicesForBabylon.reverse();
                 //Get the good array
                 unwrapData();
@@ -860,9 +909,13 @@ module BABYLON {
                 handledMesh.positions = unwrappedPositionsForBabylon;
                 handledMesh.normals = unwrappedNormalsForBabylon;
                 handledMesh.uvs = unwrappedUVForBabylon;
+
+                if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                    handledMesh.colors = unwrappedColorsForBabylon;
+                }
             }
 
-            //If any o or g keyword found, create a mesj with a random id
+            //If any o or g keyword found, create a mesh with a random id
             if (!hasMeshes) {
                 // reverse tab of indices
                 indicesForBabylon.reverse();
@@ -873,6 +926,7 @@ module BABYLON {
                     name: BABYLON.Geometry.RandomId(),
                     indices: indicesForBabylon,
                     positions: unwrappedPositionsForBabylon,
+                    colors: unwrappedColorsForBabylon,
                     normals: unwrappedNormalsForBabylon,
                     uvs: unwrappedUVForBabylon,
                     materialName: materialNameFromObj
@@ -910,11 +964,23 @@ module BABYLON {
                 materialToUse.push(meshesFromObj[j].materialName);
 
                 var vertexData: VertexData = new BABYLON.VertexData(); //The container for the values
-                //Set the data for the babylonMesh
-                vertexData.positions = handledMesh.positions;
-                vertexData.normals = handledMesh.normals;
-                vertexData.uvs = handledMesh.uvs;
-                vertexData.indices = handledMesh.indices;
+                //Set the vertex data for the babylonMesh
+                vertexData.uvs = handledMesh.uvs as FloatArray;
+                vertexData.indices = handledMesh.indices as FloatArray;
+                vertexData.positions = handledMesh.positions as FloatArray;
+
+                if (OBJFileLoader.COMPUTE_NORMALS === true) {
+                    let normals: Array<number> = new Array<number>();
+                    BABYLON.VertexData.ComputeNormals(handledMesh.positions, handledMesh.indices, normals);
+                    vertexData.normals = normals;
+                } else {
+                    vertexData.normals = handledMesh.normals as FloatArray;
+                }
+
+                if (OBJFileLoader.IMPORT_VERTEX_COLORS === true) {
+                    vertexData.colors = handledMesh.colors as FloatArray;
+                }
+
                 //Set the data from the VertexBuffer to the current BABYLON.Mesh
                 vertexData.applyToMesh(babylonMesh);
                 if (OBJFileLoader.INVERT_Y) {

+ 5 - 5
materialsLibrary/index.html

@@ -94,14 +94,14 @@
 			var knot = BABYLON.Mesh.CreateTorusKnot("knot", 10, 3, 128, 64, 2, 3, scene);
 			knot.setEnabled(false);
 			
-			var heightMap = BABYLON.Mesh.CreateGroundFromHeightMap("heightMap", "/playground/textures/heightMap.png", 100, 100, 100, 0, 10, scene, false);
+			var heightMap = BABYLON.Mesh.CreateGroundFromHeightMap("heightMap", "/Playground/textures/heightMap.png", 100, 100, 100, 0, 10, scene, false);
 			heightMap.setEnabled(false);
 			
 			// Skybox
 			var skybox = BABYLON.Mesh.CreateBox("skyBox", 1000.0, scene);
 			var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
 			skyboxMaterial.backFaceCulling = false;
-			skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("/playground/textures/TropicalSunnyDay", scene);
+			skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("/Playground/textures/TropicalSunnyDay", scene);
 			skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
 			skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
 			skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
@@ -113,7 +113,7 @@
 
 			// Rabbit
 			var rabbit;
-			BABYLON.SceneLoader.ImportMesh("Rabbit", "/playground/scenes/", "Rabbit.babylon", scene, function (newMeshes, particleSystems, skeletons) {
+			BABYLON.SceneLoader.ImportMesh("Rabbit", "/Playground/scenes/", "Rabbit.babylon", scene, function (newMeshes, particleSystems, skeletons) {
 				rabbit = newMeshes[1];
 				rabbit.setEnabled(false);
 				rabbit.scaling = new BABYLON.Vector3(0.3, 0.3, 0.3);
@@ -172,14 +172,14 @@
 				
 				// Create shaders
 				var std = new BABYLON.StandardMaterial("std", scene);
-				std.diffuseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+				std.diffuseTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 				std.diffuseTexture.uScale = 5;
 				std.diffuseTexture.vScale = 5;
 
                 var lava = prepareLava();
 
 				var simple = new BABYLON.SimpleMaterial("simple", scene);
-				simple.diffuseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+				simple.diffuseTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 				simple.diffuseTexture.uScale = 5;
 				simple.diffuseTexture.vScale = 5;
 

+ 1 - 1
materialsLibrary/test/addCell.js

@@ -1,6 +1,6 @@
 window.prepareCell = function() {
     var cell = new BABYLON.CellMaterial("cell", scene);
-	cell.diffuseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+	cell.diffuseTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 	cell.diffuseTexture.uScale = cell.diffuseTexture.vScale = 3;
 	cell.computeHighLevel = true;
 

+ 11 - 11
materialsLibrary/test/addMix.js

@@ -2,23 +2,23 @@ window.prepareMix = function() {
     var mix = new BABYLON.MixMaterial("mix", scene);
     mix.specularColor = new BABYLON.Color3(0.5, 0.5, 0.5);
     mix.specularPower = 64;
-    mix.mixTexture1 = new BABYLON.Texture("/playground/textures/mixMap.png", scene);
-    mix.mixTexture2 = new BABYLON.Texture("/playground/textures/mixMap_2.png", scene);
+    mix.mixTexture1 = new BABYLON.Texture("/Playground/textures/mixMap.png", scene);
+    mix.mixTexture2 = new BABYLON.Texture("/Playground/textures/mixMap_2.png", scene);
 
-    mix.diffuseTexture1 = new BABYLON.Texture("/playground/textures/floor.png", scene);
-    mix.diffuseTexture2 = new BABYLON.Texture("/playground/textures/rock.png", scene);
-    mix.diffuseTexture3 = new BABYLON.Texture("/playground/textures/grass.png", scene);
-    mix.diffuseTexture4 = new BABYLON.Texture("/playground/textures/floor.png", scene);
+    mix.diffuseTexture1 = new BABYLON.Texture("/Playground/textures/floor.png", scene);
+    mix.diffuseTexture2 = new BABYLON.Texture("/Playground/textures/rock.png", scene);
+    mix.diffuseTexture3 = new BABYLON.Texture("/Playground/textures/grass.png", scene);
+    mix.diffuseTexture4 = new BABYLON.Texture("/Playground/textures/floor.png", scene);
 
     mix.diffuseTexture1.uScale = mix.diffuseTexture1.vScale = 10;
     mix.diffuseTexture2.uScale = mix.diffuseTexture2.vScale = 10;
     mix.diffuseTexture3.uScale = mix.diffuseTexture3.vScale = 10;
     mix.diffuseTexture4.uScale = mix.diffuseTexture4.vScale = 10;
 
-    mix.diffuseTexture5 = new BABYLON.Texture("/playground/textures/leopard_fur.jpg", scene);
-    mix.diffuseTexture6 = new BABYLON.Texture("/playground/textures/fur.jpg", scene);
-    mix.diffuseTexture7 = new BABYLON.Texture("/playground/textures/sand.jpg", scene);
-    mix.diffuseTexture8 = new BABYLON.Texture("/playground/textures/crate.png", scene);
+    mix.diffuseTexture5 = new BABYLON.Texture("/Playground/textures/leopard_fur.JPG", scene);
+    mix.diffuseTexture6 = new BABYLON.Texture("/Playground/textures/fur.jpg", scene);
+    mix.diffuseTexture7 = new BABYLON.Texture("/Playground/textures/sand.jpg", scene);
+    mix.diffuseTexture8 = new BABYLON.Texture("/Playground/textures/crate.png", scene);
 
     mix.diffuseTexture5.uScale = mix.diffuseTexture5.vScale = 10;
     mix.diffuseTexture6.uScale = mix.diffuseTexture6.vScale = 10;
@@ -26,4 +26,4 @@ window.prepareMix = function() {
     mix.diffuseTexture8.uScale = mix.diffuseTexture8.vScale = 10;
     
     return mix;
-};
+};

+ 2 - 2
materialsLibrary/test/addbackground.js

@@ -1,12 +1,12 @@
 window.prepareBackgroundMaterial = function() {
 	var backSky = new BABYLON.BackgroundMaterial("backSky", scene);
-	var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("/playground/textures/environment.dds", scene);
+	var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("/Playground/textures/environment.dds", scene);
 	hdrTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE
 	backSky.reflectionTexture = hdrTexture;
 	backSky.backFaceCulling = false;
 	
 	var back = new BABYLON.BackgroundMaterial("back", scene);
-	back.diffuseTexture = new BABYLON.Texture("/playground/textures/WhiteTransarentRamp.png", scene);
+	back.diffuseTexture = new BABYLON.Texture("/Playground/textures/WhiteTransarentRamp.png", scene);
 	back.diffuseTexture.hasAlpha = true;
 
     // Skybox

+ 3 - 3
materialsLibrary/test/addfire.js

@@ -1,8 +1,8 @@
 window.prepareFire = function() {
     var fire = new BABYLON.FireMaterial("fire", scene);
-    fire.diffuseTexture = new BABYLON.Texture("/playground/textures/fire/diffuse.png", scene);
-    fire.distortionTexture = new BABYLON.Texture("/playground/textures/fire/distortion.png", scene);
-    fire.opacityTexture = new BABYLON.Texture("/playground/textures/fire/opacity.png", scene);
+    fire.diffuseTexture = new BABYLON.Texture("/Playground/textures/fire/diffuse.png", scene);
+    fire.distortionTexture = new BABYLON.Texture("/Playground/textures/fire/distortion.png", scene);
+    fire.opacityTexture = new BABYLON.Texture("/Playground/textures/fire/opacity.png", scene);
     
     registerRangeUI("fire", "speed", 0, 20, function(value) {
 		fire.speed = value;

+ 2 - 2
materialsLibrary/test/addlava.js

@@ -1,9 +1,9 @@
 window.prepareLava = function() {
     var lava = new BABYLON.LavaMaterial("lava", scene);
-    lava.diffuseTexture = new BABYLON.Texture("/playground/textures/lava/lavatile.jpg", scene);
+    lava.diffuseTexture = new BABYLON.Texture("/Playground/textures/lava/lavatile.jpg", scene);
     lava.diffuseTexture.uScale = 0.5;
     lava.diffuseTexture.vScale = 0.5;
-    lava.noiseTexture = new BABYLON.Texture("/playground/textures/lava/cloud.png", scene);
+    lava.noiseTexture = new BABYLON.Texture("/Playground/textures/lava/cloud.png", scene);
     lava.fogColor = BABYLON.Color3.Black();
     lava.speed = 2.5;
 

+ 3 - 3
materialsLibrary/test/addpbr.js

@@ -1,11 +1,11 @@
 window.preparePBR = function() {
 	var pbr = new BABYLON.PBRMaterial("pbr", scene);
 
-	pbr.albedoTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+	pbr.albedoTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 	pbr.albedoTexture.uScale = 5;
 	pbr.albedoTexture.vScale = 5;
     
-    var hdrTexture = new BABYLON.HDRCubeTexture("/playground/textures/environment.hdr", scene, 512);
+    var hdrTexture = new BABYLON.HDRCubeTexture("/Playground/textures/environment.hdr", scene, 512);
 
    
     // Uncomment for PMREM Generation
@@ -15,7 +15,7 @@ window.preparePBR = function() {
     pbr.linkRefractionWithTransparency = true;
     pbr.indexOfRefraction = 0.52;
 
-	var colorGrading = new BABYLON.Texture("/playground/textures/colorGrade.png", scene, true);
+	var colorGrading = new BABYLON.Texture("/Playground/textures/colorGrade.png", scene, true);
 	pbr.cameraColorGradingTexture = colorGrading;
 	pbr.colorGradingEnabled = true;
 

+ 2 - 2
materialsLibrary/test/addpbrmetallicroughness.js

@@ -1,14 +1,14 @@
 window.preparePBRMetallicRoughness = function() {
 	var pbr = new BABYLON.PBRMetallicRoughnessMaterial("pbrmetallicroughness", scene);
 
-	pbr.baseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+	pbr.baseTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 	pbr.baseTexture.uScale = 5;
 	pbr.baseTexture.vScale = 5;
 	pbr.metallic = 0.5;
 	pbr.roughness = 0.1;
 
     // Uncomment for PMREM Generation
-    var hdrTexture = new BABYLON.HDRCubeTexture("/playground/textures/environment.hdr", scene, 512);
+    var hdrTexture = new BABYLON.HDRCubeTexture("/Playground/textures/environment.hdr", scene, 512);
     // var hdrTexture = new BABYLON.HDRCubeTexture("textures/hdr/environment.hdr", scene, 128, false, true, false, true);
 
     // Skybox

+ 2 - 2
materialsLibrary/test/addpbrspecularglossiness.js

@@ -1,14 +1,14 @@
 window.preparePBRSpecularGlossiness = function() {
 	var pbr = new BABYLON.PBRSpecularGlossinessMaterial("pbrspecularglossiness", scene);
 
-	pbr.diffuseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+	pbr.diffuseTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 	pbr.diffuseTexture.uScale = 5;
 	pbr.diffuseTexture.vScale = 5;
 	pbr.specularColor = new BABYLON.Color3(0.3, 0.3, 0.3);
 	pbr.glossiness = 0.9;
 
     // Uncomment for PMREM Generation
-    var hdrTexture = new BABYLON.HDRCubeTexture("/playground/textures/environment.hdr", scene, 512);
+    var hdrTexture = new BABYLON.HDRCubeTexture("/Playground/textures/environment.hdr", scene, 512);
     // var hdrTexture = new BABYLON.HDRCubeTexture("textures/hdr/environment.hdr", scene, 128, false, true, false, true);
 
     // Skybox

+ 7 - 7
materialsLibrary/test/addterrain.js

@@ -2,14 +2,14 @@ window.prepareTerrain = function() {
     var terrain = new BABYLON.TerrainMaterial("terrain", scene);
     terrain.specularColor = new BABYLON.Color3(0.5, 0.5, 0.5);
     terrain.specularPower = 64;
-    terrain.mixTexture = new BABYLON.Texture("/playground/textures/mixMap.png", scene);
-    terrain.diffuseTexture1 = new BABYLON.Texture("/playground/textures/floor.png", scene);
-    terrain.diffuseTexture2 = new BABYLON.Texture("/playground/textures/rock.png", scene);
-    terrain.diffuseTexture3 = new BABYLON.Texture("/playground/textures/grass.png", scene);
+    terrain.mixTexture = new BABYLON.Texture("/Playground/textures/mixMap.png", scene);
+    terrain.diffuseTexture1 = new BABYLON.Texture("/Playground/textures/floor.png", scene);
+    terrain.diffuseTexture2 = new BABYLON.Texture("/Playground/textures/rock.png", scene);
+    terrain.diffuseTexture3 = new BABYLON.Texture("/Playground/textures/grass.png", scene);
     
-    terrain.bumpTexture1 = new BABYLON.Texture("/playground/textures/floor_bump.PNG", scene);
-    terrain.bumpTexture2 = new BABYLON.Texture("/playground/textures/rockn.png", scene);
-    terrain.bumpTexture3 = new BABYLON.Texture("/playground/textures/grassn.png", scene);
+    terrain.bumpTexture1 = new BABYLON.Texture("/Playground/textures/floor_bump.PNG", scene);
+    terrain.bumpTexture2 = new BABYLON.Texture("/Playground/textures/rockn.png", scene);
+    terrain.bumpTexture3 = new BABYLON.Texture("/Playground/textures/grassn.png", scene);
     
     terrain.diffuseTexture1.uScale = terrain.diffuseTexture1.vScale = 10;
     terrain.diffuseTexture2.uScale = terrain.diffuseTexture2.vScale = 10;

+ 4 - 4
materialsLibrary/test/addtriplanar.js

@@ -1,10 +1,10 @@
 window.prepareTriPlanar = function() {
     var triPlanar = new BABYLON.TriPlanarMaterial("triplanar", scene);
-    triPlanar.diffuseTextureX = new BABYLON.Texture("/playground/textures/rock.png", scene);
-    triPlanar.diffuseTextureY = new BABYLON.Texture("/playground/textures/grass.png", scene);
+    triPlanar.diffuseTextureX = new BABYLON.Texture("/Playground/textures/rock.png", scene);
+    triPlanar.diffuseTextureY = new BABYLON.Texture("/Playground/textures/grass.png", scene);
     triPlanar.diffuseTextureZ = triPlanar.diffuseTextureX;
-    triPlanar.normalTextureX = new BABYLON.Texture("/playground/textures/rockn.png", scene);
-    triPlanar.normalTextureY = new BABYLON.Texture("/playground/textures/grassn.png", scene);
+    triPlanar.normalTextureX = new BABYLON.Texture("/Playground/textures/rockn.png", scene);
+    triPlanar.normalTextureY = new BABYLON.Texture("/Playground/textures/grassn.png", scene);
     triPlanar.normalTextureZ = triPlanar.normalTextureX;
     triPlanar.specularPower = 64;
     triPlanar.tileSize = 1.5;

+ 1 - 1
materialsLibrary/test/addwater.js

@@ -2,7 +2,7 @@ window.prepareWater = function() {
 	var water = new BABYLON.WaterMaterial("water", scene);
 	water.backFaceCulling = false;
 	water.enableRenderTargets(false);
-	water.bumpTexture = new BABYLON.Texture("/playground/textures/waterbump.png", scene);
+	water.bumpTexture = new BABYLON.Texture("/Playground/textures/waterbump.png", scene);
 	water.windForce = 45;
 	water.waveHeight = 1.3;
 	water.windDirection = new BABYLON.Vector2(1, 1);

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.0.0-alpha.16",
+    "version": "4.0.0-alpha.17",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
postProcessLibrary/index.html

@@ -4,7 +4,7 @@
 <head>
 	<title>Shaders Library</title>
 	<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
-	<script src="../tools/DevLoader/BabylonLoader.js"></script>
+	<script src="../Tools/DevLoader/BabylonLoader.js"></script>
 
 	<style>
 		html,

+ 4 - 4
proceduralTexturesLibrary/index.html

@@ -4,7 +4,7 @@
 <head>
 	<title>Procedural textures Library</title>
 	<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
-	<script src="../tools/DevLoader/BabylonLoader.js"></script>
+	<script src="../Tools/DevLoader/BabylonLoader.js"></script>
 
 	<style>
 		html,
@@ -90,7 +90,7 @@
 			var skybox = BABYLON.Mesh.CreateBox("skyBox", 1000.0, scene);
 			var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
 			skyboxMaterial.backFaceCulling = false;
-			skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("/playground/textures/TropicalSunnyDay", scene);
+			skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("/Playground/textures/TropicalSunnyDay", scene);
 			skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
 			skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
 			skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
@@ -102,7 +102,7 @@
 
 			// Rabbit
 			var rabbit;
-			BABYLON.SceneLoader.ImportMesh("Rabbit", "/playground/scenes/", "Rabbit.babylon", scene, function (newMeshes, particleSystems, skeletons) {
+			BABYLON.SceneLoader.ImportMesh("Rabbit", "/Playground/scenes/", "Rabbit.babylon", scene, function (newMeshes, particleSystems, skeletons) {
 				rabbit = newMeshes[1];
 				rabbit.setEnabled(false);
 				rabbit.scaling = new BABYLON.Vector3(0.3, 0.3, 0.3);
@@ -161,7 +161,7 @@
 				var std = new BABYLON.StandardMaterial("std", scene);
 				std.specularColor = new BABYLON.Color3(0, 0, 0);
 				
-				var diffuseTexture = new BABYLON.Texture("/playground/textures/amiga.jpg", scene);
+				var diffuseTexture = new BABYLON.Texture("/Playground/textures/amiga.jpg", scene);
 				diffuseTexture.uScale = 5;
 				diffuseTexture.vScale = 5;
 

+ 3 - 0
readme.md

@@ -112,6 +112,9 @@ If you want to contribute, please read our [contribution guidelines](https://git
 - [Documentation](https://doc.babylonjs.com)
 - [Examples](https://doc.babylonjs.com/examples)
 
+## Contributing
+Please see the [Contributing Guidelines](./contributing.md)
+
 ## Useful links
 
 - Official web site: [www.babylonjs.com](https://www.babylonjs.com/)

+ 8 - 4
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -208,11 +208,15 @@ module BABYLON {
                         tmpQuaternion.multiplyToRef(this._startingOrientation, tmpQuaternion);
                         // Slowly move mesh to avoid jitter
                         var oldParent = pickedMesh.parent;
-                        pickedMesh.setParent(null);
-                        Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
-                        pickedMesh.setParent(oldParent);
-                        BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+
+                        // Only rotate the mesh if it's parent has uniform scaling
+                        if (!oldParent || ((oldParent as Mesh).scaling && !(oldParent as Mesh).scaling.isNonUniformWithinEpsilon(0.001))) {
+                            pickedMesh.setParent(null);
+                            Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
+                            pickedMesh.setParent(oldParent);
+                        }
                     }
+                    BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
                 }
             });
         }

+ 1 - 1
src/Cameras/babylon.arcRotateCamera.ts

@@ -91,7 +91,7 @@ module BABYLON {
          * This can help limiting how the Camera is able to move in the scene.
          */
         @serialize()
-        public upperBetaLimit = Math.PI;
+        public upperBetaLimit = Math.PI - 0.01;
 
         /**
          * Minimum allowed distance of the camera to the target (The camera can not get closer).

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -486,7 +486,7 @@ module BABYLON {
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "4.0.0-alpha.16";
+            return "4.0.0-alpha.17";
         }
 
         /**

+ 4 - 1
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -86,7 +86,10 @@ module BABYLON {
                     } else {
                         currentSnapDragDistance += event.dragDistance;
                         if (Math.abs(currentSnapDragDistance) > this.snapDistance) {
-                            dragSteps = Math.floor(currentSnapDragDistance / this.snapDistance);
+                            dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
                             tmpVector.scaleToRef(this.snapDistance * dragSteps, tmpVector);
                             snapped = true;

+ 30 - 1
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -171,6 +171,11 @@ module BABYLON {
                 _dragBehavior.onDragObservable.add((event) => {
                     this.onRotationSphereDragObservable.notifyObservers({});
                     if (this.attachedMesh) {
+                        var originalParent = this.attachedMesh.parent;
+                        if (originalParent && ((originalParent as Mesh).scaling && (originalParent as Mesh).scaling.isNonUniformWithinEpsilon(0.001))) {
+                            Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                            return;
+                        }
                         BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
 
                         var worldDragDirection = startingTurnDirection;
@@ -208,6 +213,7 @@ module BABYLON {
                             this._anchorMesh.addChild(this.attachedMesh);
                             this._anchorMesh.rotationQuaternion!.multiplyToRef(this._tmpQuaternion, this._anchorMesh.rotationQuaternion!);
                             this._anchorMesh.removeChild(this.attachedMesh);
+                            this.attachedMesh.setParent(originalParent);
                         }
                         this.updateBoundingBox();
 
@@ -248,6 +254,11 @@ module BABYLON {
                         _dragBehavior.onDragObservable.add((event) => {
                             this.onScaleBoxDragObservable.notifyObservers({});
                             if (this.attachedMesh) {
+                                var originalParent = this.attachedMesh.parent;
+                                if (originalParent && ((originalParent as Mesh).scaling && (originalParent as Mesh).scaling.isNonUniformWithinEpsilon(0.001))) {
+                                    Tools.Warn("BoundingBoxGizmo controls are not supported on child meshes with non-uniform parent scaling");
+                                    return;
+                                }
                                 BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
                                 var relativeDragDistance = (event.dragDistance / this._boundingDimensions.length()) * this._anchorMesh.scaling.length();
                                 var deltaScale = new Vector3(relativeDragDistance, relativeDragDistance, relativeDragDistance);
@@ -274,7 +285,7 @@ module BABYLON {
                                     this._anchorMesh.scaling.subtractInPlace(deltaScale);
                                 }
                                 this._anchorMesh.removeChild(this.attachedMesh);
-
+                                this.attachedMesh.setParent(originalParent);
                                 BoundingBoxGizmo._RestorePivotPoint(this.attachedMesh);
                             }
                             this._updateDummy();
@@ -339,10 +350,15 @@ module BABYLON {
                 // Reset anchor mesh to match attached mesh's scale
                 // This is needed to avoid invalid box/sphere position on first drag
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(value);
+                var originalParent = value.parent;
                 this._anchorMesh.addChild(value);
                 this._anchorMesh.removeChild(value);
+                value.setParent(originalParent);
                 BoundingBoxGizmo._RestorePivotPoint(value);
                 this.updateBoundingBox();
+                value.getChildMeshes(false).forEach((m) => {
+                    m.markAsDirty("scaling");
+                });
 
                 this.gizmoLayer.utilityLayerScene.onAfterRenderObservable.addOnce(() => {
                     this._updateDummy();
@@ -363,6 +379,8 @@ module BABYLON {
         public updateBoundingBox() {
             if (this.attachedMesh) {
                 BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
+                var originalParent = this.attachedMesh.parent;
+                this.attachedMesh.setParent(null);
                 this._update();
                 // Rotate based on axis
                 if (!this.attachedMesh.rotationQuaternion) {
@@ -394,6 +412,7 @@ module BABYLON {
                 // restore position/rotation values
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
+                this.attachedMesh.setParent(originalParent);
             }
 
             this._updateRotationSpheres();
@@ -476,6 +495,16 @@ module BABYLON {
             });
         }
 
+        /**
+         * Enables/disables scaling
+         * @param enable if scaling should be enabled
+         */
+        public setEnabledScaling(enable: boolean) {
+            this._scaleBoxesParent.getChildMeshes().forEach((m, i) => {
+                m.setEnabled(enable);
+            });
+        }
+
         private _updateDummy() {
             if (this._dragMesh) {
                 this._dragMesh.position.copyFrom(this._lineBoundingBox.getAbsolutePosition());

+ 4 - 1
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -117,7 +117,10 @@ module BABYLON {
                     if (this.snapDistance != 0) {
                         currentSnapDragDistance += angle;
                         if (Math.abs(currentSnapDragDistance) > this.snapDistance) {
-                            var dragSteps = Math.floor(currentSnapDragDistance / this.snapDistance);
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / this.snapDistance);
+                            if (currentSnapDragDistance < 0) {
+                                dragSteps *= -1;
+                            }
                             currentSnapDragDistance = currentSnapDragDistance % this.snapDistance;
                             angle = this.snapDistance * dragSteps;
                             snapped = true;

+ 25 - 0
src/Math/babylon.math.ts

@@ -1926,6 +1926,31 @@ module BABYLON {
         }
 
         /**
+         * Due to float precision, scale of a mesh could be uniform but float values are off by a small fraction
+         * Check if is non uniform within a certain amount of decimal places to account for this
+         * @param epsilon the amount the values can differ
+         * @returns if the the vector is non uniform to a certain number of decimal places
+         */
+        public isNonUniformWithinEpsilon(epsilon: number) {
+            let absX = Math.abs(this.x);
+            let absY = Math.abs(this.y);
+            if (!Scalar.WithinEpsilon(absX, absY, epsilon)) {
+                return true;
+            }
+
+            let absZ = Math.abs(this.z);
+            if (!Scalar.WithinEpsilon(absX, absZ, epsilon)) {
+                return true;
+            }
+
+            if (!Scalar.WithinEpsilon(absY, absZ, epsilon)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        /**
          * Gets a boolean indicating that the vector is non uniform meaning x, y or z are not all the same
          */
         public get isNonUniform(): boolean {

+ 8 - 0
src/babylon.scene.ts

@@ -2653,6 +2653,14 @@ module BABYLON {
             }
         }
 
+        /**
+         * Resets the last animation time frame.
+         * Useful to override when animations start running when loading a scene for the first time.
+         */
+        public resetLastAnimationTimeFrame(): void {
+            this._animationTimeLast = BABYLON.Tools.Now;
+        }
+
         private _animate(): void {
             if (!this.animationsEnabled || this._activeAnimatables.length === 0) {
                 return;

+ 5 - 5
tests/nullEngine/app.js

@@ -266,27 +266,27 @@ var engine = new BABYLON.NullEngine();
 //             left:{
 //                 size:{width:b,depth:y,height:z},
 //                 pos:{x:-(x-b)/2,y:0,z:0},
-//                 mat:{url:"/playground/textures/crate.png",grain:90}
+//                 mat:{url:"/Playground/textures/crate.png",grain:90}
 //             },
 //             right:{
 //                 size:{width:b,depth:y,height:z},
 //                 pos:{x:(x-b)/2,y:0,z:0},
-//                 mat:{url:"/playground/textures/crate.png",grain:90}
+//                 mat:{url:"/Playground/textures/crate.png",grain:90}
 //             },
 //             top:{
 //                 size:{width:x-(b*2),depth:y,height:b},
 //                 pos:{x:0,y:(z-b-1)/2,z:0},
-//                 mat:{url:"/playground/textures/albedo.png",grain:0}
+//                 mat:{url:"/Playground/textures/albedo.png",grain:0}
 //             },
 //             bottom:{
 //                 size:{width:x-(b*2),depth:y,height:b},
 //                 pos:{x:0,y:-(z-b-1)/2,z:0},
-//                 mat:{url:"/playground/textures/albedo.png",grain:0}
+//                 mat:{url:"/Playground/textures/albedo.png",grain:0}
 //             },
 //             back:{
 //                 size:{width:x-(b*2),depth:b,height:z-(b*2)-1},
 //                 pos:{x:0,y:0,z:(y-b)/2-20},
-//                 mat:{url:"/playground/textures/albedo.png",grain:0}
+//                 mat:{url:"/Playground/textures/albedo.png",grain:0}
 //             },
 //             shelf:{
 //                 size:{width:x-(b*2)-1,depth:y-b-30,height:b},

+ 33 - 0
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -519,6 +519,39 @@ describe('Babylon Scene Loader', function() {
         // TODO: test KHR_lights
     });
 
+    /**
+     * Integration tests for loading OBJ assets.
+     */
+    describe('#OBJ', () => {
+        it('should load a tetrahedron (without colors)', () => {
+            var fileContents = `               
+                g tetrahedron
+                
+                v 1.00 1.00 1.00 0.666 0 0
+                v 2.00 1.00 1.00 0.666 0 0
+                v 1.00 2.00 1.00 0.666 0 0
+                v 1.00 1.00 2.00 0.666 0 0
+                
+                f 1 3 2
+                f 1 4 3
+                f 1 2 4
+                f 2 3 4
+            `;
+
+            var scene = new BABYLON.Scene(subject);
+            return BABYLON.SceneLoader.LoadAssetContainerAsync('', 'data:' + fileContents, scene, ()=> {}, ".obj").then(container => {
+                expect(container.meshes.length).to.eq(1);
+                let tetrahedron = container.meshes[0];
+
+                var positions : BABYLON.FloatArray = tetrahedron.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+                var colors : BABYLON.FloatArray = tetrahedron.getVerticesData(BABYLON.VertexBuffer.ColorKind);
+
+                expect(positions).to.deep.equal([1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 2]);
+                assert.isNull(colors, 'expecting colors vertex buffer to be null')
+            })
+        })
+    })
+
     describe('#AssetContainer', () => {
         it('should be loaded from BoomBox GLTF', () => {
             var scene = new BABYLON.Scene(subject);