Просмотр исходного кода

Merge remote-tracking branch 'upstream/master'

jeff 9 лет назад
Родитель
Сommit
b8cbe1abf1
35 измененных файлов с 2723 добавлено и 1373 удалено
  1. 13 13
      dist/preview release/babylon.core.js
  2. 1164 1143
      dist/preview release/babylon.d.ts
  3. 18 18
      dist/preview release/babylon.js
  4. 204 62
      dist/preview release/babylon.max.js
  5. 21 21
      dist/preview release/babylon.noworker.js
  6. 4 0
      dist/preview release/what's new.md
  7. 8 0
      materialsLibrary/config.json
  8. 228 0
      materialsLibrary/dist/babylon.skyMaterial.js
  9. 1 0
      materialsLibrary/dist/babylon.skyMaterial.min.js
  10. 30 0
      materialsLibrary/dist/dts/babylon.skyMaterial.d.ts
  11. 287 0
      materialsLibrary/materials/sky/babylon.skyMaterial.ts
  12. 211 0
      materialsLibrary/materials/sky/sky.fragment.fx
  13. 59 0
      materialsLibrary/materials/sky/sky.vertex.fx
  14. 48 0
      materialsLibrary/test/add/addsky.js
  15. 10 1
      materialsLibrary/test/index.html
  16. 5 2
      src/Actions/babylon.action.js
  17. 9 5
      src/Actions/babylon.action.ts
  18. 34 22
      src/Actions/babylon.actionManager.js
  19. 28 20
      src/Actions/babylon.actionManager.ts
  20. 7 1
      src/Audio/babylon.sound.js
  21. 8 1
      src/Audio/babylon.sound.ts
  22. 1 0
      src/Debug/babylon.debugLayer.js
  23. 1 0
      src/Debug/babylon.debugLayer.ts
  24. 5 3
      src/Loading/Plugins/babylon.babylonFileLoader.js
  25. 4 3
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  26. 100 7
      src/Mesh/babylon.groundMesh.js
  27. 112 9
      src/Mesh/babylon.groundMesh.ts
  28. 31 19
      src/Mesh/babylon.meshBuilder.js
  29. 25 3
      src/Mesh/babylon.meshBuilder.ts
  30. 1 0
      src/Mesh/babylon.meshSimplification.js
  31. 1 1
      src/Mesh/babylon.meshSimplification.ts
  32. 5 5
      src/Particles/babylon.particleSystem.js
  33. 5 6
      src/Particles/babylon.particleSystem.ts
  34. 15 3
      src/babylon.scene.js
  35. 20 5
      src/babylon.scene.ts

Разница между файлами не показана из-за своего большого размера
+ 13 - 13
dist/preview release/babylon.core.js


Разница между файлами не показана из-за своего большого размера
+ 1164 - 1143
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 18 - 18
dist/preview release/babylon.js


+ 204 - 62
dist/preview release/babylon.max.js

@@ -12060,6 +12060,8 @@ var BABYLON;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
             this.cameraToUseForPointers = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
+            this._startingPointerPosition = new BABYLON.Vector2(0, 0);
+            this._startingPointerTime = 0;
             // Fog
             /**
             * is fog enabled on this scene.
@@ -12398,6 +12400,9 @@ var BABYLON;
                     return;
                 }
                 _this._updatePointerPosition(evt);
+                _this._startingPointerPosition.x = _this._pointerX;
+                _this._startingPointerPosition.y = _this._pointerY;
+                _this._startingPointerTime = new Date().getTime();
                 var predicate = null;
                 // Meshes
                 if (!_this.onPointerDown) {
@@ -12419,7 +12424,7 @@ var BABYLON;
                                 pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                 break;
                         }
-                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                     }
                 }
                 if (_this.onPointerDown) {
@@ -12441,7 +12446,7 @@ var BABYLON;
                                     pickResult.pickedSprite.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, BABYLON.ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, _this, evt));
                                     break;
                             }
-                            pickResult.pickedSprite.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, _this, evt));
+                            pickResult.pickedSprite.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, BABYLON.ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, _this, evt));
                         }
                     }
                 }
@@ -12454,7 +12459,7 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 if (!_this.onPointerUp) {
                     predicate = function (mesh) {
-                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnPickUpTrigger);
+                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && (mesh.actionManager.hasPickTriggers || mesh.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnLongPressTrigger));
                     };
                 }
                 // Meshes
@@ -12462,6 +12467,13 @@ var BABYLON;
                 if (pickResult.hit && pickResult.pickedMesh) {
                     if (pickResult.pickedMesh.actionManager) {
                         pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickUpTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                        if (Math.abs(_this._startingPointerPosition.x - _this._pointerX) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(_this._startingPointerPosition.y - _this._pointerY) < BABYLON.ActionManager.DragMovementThreshold) {
+                            pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                            if ((new Date().getTime() - _this._startingPointerTime) > BABYLON.ActionManager.LongPressDelay) {
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnLongPressTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                                ;
+                            }
+                        }
                     }
                 }
                 if (_this.onPointerUp) {
@@ -16546,33 +16558,33 @@ var BABYLON;
             var tessellation = options.tessellation || 64;
             var updatable = options.updatable;
             var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE;
+            var cap = options.cap || BABYLON.Mesh.NO_CAP;
             var pi2 = Math.PI * 2;
-            var shapeLathe = new Array();
-            // first rotatable point
+            var paths = new Array();
             var i = 0;
-            while (shape[i].x === 0) {
-                i++;
-            }
-            var pt = shape[i];
-            for (i = 0; i < shape.length; i++) {
-                shapeLathe.push(shape[i].subtract(pt));
-            }
-            // circle path
+            var p = 0;
             var step = pi2 / tessellation * arc;
             var rotated;
             var path = new Array();
             ;
             for (i = 0; i <= tessellation; i++) {
-                rotated = new BABYLON.Vector3(Math.cos(i * step) * radius, 0, Math.sin(i * step) * radius);
-                path.push(rotated);
-            }
-            if (closed) {
-                path.push(path[0]);
+                var path = [];
+                if (cap == BABYLON.Mesh.CAP_START || cap == BABYLON.Mesh.CAP_ALL) {
+                    path.push(new BABYLON.Vector3(0, shape[0].y, 0));
+                    path.push(new BABYLON.Vector3(shape[0].x, shape[0].y, shape[0].x));
+                }
+                for (p = 0; p < shape.length; p++) {
+                    rotated = new BABYLON.Vector3(Math.cos(i * step) * shape[p].x * radius, shape[p].y, Math.sin(i * step) * shape[p].x * radius);
+                    path.push(rotated);
+                }
+                if (cap == BABYLON.Mesh.CAP_END || cap == BABYLON.Mesh.CAP_ALL) {
+                    path.push(new BABYLON.Vector3(Math.cos(i * step) * shape[shape.length - 1].x * radius, shape[shape.length - 1].y, Math.sin(i * step) * shape[shape.length - 1].x * radius));
+                    path.push(new BABYLON.Vector3(0, shape[shape.length - 1].y, 0));
+                }
+                paths.push(path);
             }
-            // extrusion
-            var scaleFunction = function () { return 1; };
-            var rotateFunction = function () { return 0; };
-            var lathe = BABYLON.Mesh.ExtrudeShapeCustom(name, shapeLathe, path, scaleFunction, rotateFunction, closed, false, BABYLON.Mesh.NO_CAP, scene, updatable, sideOrientation);
+            // lathe ribbon
+            var lathe = MeshBuilder.CreateRibbon(name, { pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable }, scene);
             return lathe;
         };
         MeshBuilder.CreatePlane = function (name, options, scene) {
@@ -16591,6 +16603,12 @@ var BABYLON;
             var ground = new BABYLON.GroundMesh(name, scene);
             ground._setReady(false);
             ground._subdivisions = options.subdivisions || 1;
+            ground._width = options.width || 1;
+            ground._height = options.height || 1;
+            ground._maxX = ground._width / 2;
+            ground._maxZ = ground._height / 2;
+            ground._minX = -ground._maxX;
+            ground._minZ = -ground._maxZ;
             var vertexData = BABYLON.VertexData.CreateGround(options);
             vertexData.applyToMesh(ground, options.updatable);
             ground._setReady(true);
@@ -16612,6 +16630,12 @@ var BABYLON;
             var onReady = options.onReady;
             var ground = new BABYLON.GroundMesh(name, scene);
             ground._subdivisions = subdivisions;
+            ground._width = width;
+            ground._height = height;
+            ground._maxX = ground._width / 2;
+            ground._maxZ = ground._height / 2;
+            ground._minX = -ground._maxX;
+            ground._minZ = -ground._maxZ;
             ground._setReady(false);
             var onload = function (img) {
                 // Getting height map data
@@ -20630,12 +20654,14 @@ var BABYLON;
                     for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
                         var parsedSound = parsedData.sounds[index];
                         if (BABYLON.Engine.audioEngine.canUseWebAudio) {
-                            if (!loadedSounds[parsedSound.name]) {
+                            if (!parsedSound.url)
+                                parsedSound.url = parsedSound.name;
+                            if (!loadedSounds[parsedSound.url]) {
                                 loadedSound = BABYLON.Sound.Parse(parsedSound, scene, rootUrl);
-                                loadedSounds[loadedSound.name] = loadedSound;
+                                loadedSounds[parsedSound.url] = loadedSound;
                             }
                             else {
-                                BABYLON.Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.name]);
+                                BABYLON.Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
                             }
                         }
                         else {
@@ -21291,16 +21317,16 @@ var BABYLON;
             this._currentRenderId = this._scene.getRenderId();
             this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
             // determine the number of particles we need to create   
-            var emitCout;
+            var newParticles;
             if (this.manualEmitCount > -1) {
-                emitCout = this.manualEmitCount;
+                newParticles = this.manualEmitCount;
+                this._newPartsExcess = 0;
                 this.manualEmitCount = 0;
             }
             else {
-                emitCout = this.emitRate;
+                newParticles = ((this.emitRate * this._scaledUpdateSpeed) >> 0);
+                this._newPartsExcess += this.emitRate * this._scaledUpdateSpeed - newParticles;
             }
-            var newParticles = ((emitCout * this._scaledUpdateSpeed) >> 0);
-            this._newPartsExcess += emitCout * this._scaledUpdateSpeed - newParticles;
             if (this._newPartsExcess > 1.0) {
                 newParticles += this._newPartsExcess >> 0;
                 this._newPartsExcess -= this._newPartsExcess >> 0;
@@ -25630,6 +25656,11 @@ var BABYLON;
                 }
             }
             this._nextActiveAction.execute(evt);
+            this.skipToNextActiveAction();
+        };
+        Action.prototype.execute = function (evt) {
+        };
+        Action.prototype.skipToNextActiveAction = function () {
             if (this._nextActiveAction._child) {
                 if (!this._nextActiveAction._child._actionManager) {
                     this._nextActiveAction._child._actionManager = this._actionManager;
@@ -25640,8 +25671,6 @@ var BABYLON;
                 this._nextActiveAction = this;
             }
         };
-        Action.prototype.execute = function (evt) {
-        };
         Action.prototype.then = function (action) {
             this._child = action;
             action._actionManager = this._actionManager;
@@ -25756,6 +25785,27 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(ActionManager, "OnPickDownTrigger", {
+            get: function () {
+                return ActionManager._OnPickDownTrigger;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ActionManager, "OnPickUpTrigger", {
+            get: function () {
+                return ActionManager._OnPickUpTrigger;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ActionManager, "OnLongPressTrigger", {
+            get: function () {
+                return ActionManager._OnLongPressTrigger;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(ActionManager, "OnPointerOverTrigger", {
             get: function () {
                 return ActionManager._OnPointerOverTrigger;
@@ -25805,13 +25855,6 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(ActionManager, "OnPickUpTrigger", {
-            get: function () {
-                return ActionManager._OnPickUpTrigger;
-            },
-            enumerable: true,
-            configurable: true
-        });
         // Methods
         ActionManager.prototype.dispose = function () {
             var index = this._scene._actionManagers.indexOf(this);
@@ -25861,9 +25904,6 @@ var BABYLON;
                     if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPointerOutTrigger) {
                         return true;
                     }
-                    if (action.trigger === ActionManager._OnPickUpTrigger) {
-                        return true;
-                    }
                 }
                 return false;
             },
@@ -25878,10 +25918,7 @@ var BABYLON;
             get: function () {
                 for (var index = 0; index < this.actions.length; index++) {
                     var action = this.actions[index];
-                    if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnCenterPickTrigger) {
-                        return true;
-                    }
-                    if (action.trigger === ActionManager._OnPickUpTrigger) {
+                    if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPickUpTrigger) {
                         return true;
                     }
                 }
@@ -26099,14 +26136,18 @@ var BABYLON;
         ActionManager._OnLeftPickTrigger = 2;
         ActionManager._OnRightPickTrigger = 3;
         ActionManager._OnCenterPickTrigger = 4;
-        ActionManager._OnPointerOverTrigger = 5;
-        ActionManager._OnPointerOutTrigger = 6;
-        ActionManager._OnEveryFrameTrigger = 7;
-        ActionManager._OnIntersectionEnterTrigger = 8;
-        ActionManager._OnIntersectionExitTrigger = 9;
-        ActionManager._OnKeyDownTrigger = 10;
-        ActionManager._OnKeyUpTrigger = 11;
-        ActionManager._OnPickUpTrigger = 12;
+        ActionManager._OnPickDownTrigger = 5;
+        ActionManager._OnPickUpTrigger = 6;
+        ActionManager._OnLongPressTrigger = 7;
+        ActionManager._OnPointerOverTrigger = 8;
+        ActionManager._OnPointerOutTrigger = 9;
+        ActionManager._OnEveryFrameTrigger = 10;
+        ActionManager._OnIntersectionEnterTrigger = 11;
+        ActionManager._OnIntersectionExitTrigger = 12;
+        ActionManager._OnKeyDownTrigger = 13;
+        ActionManager._OnKeyUpTrigger = 14;
+        ActionManager.DragMovementThreshold = 10; // in pixels
+        ActionManager.LongPressDelay = 500; // in milliseconds
         return ActionManager;
     })();
     BABYLON.ActionManager = ActionManager;
@@ -27429,14 +27470,107 @@ var BABYLON;
             this.createOrUpdateSubmeshesOctree(octreeBlocksSize);
         };
         GroundMesh.prototype.getHeightAtCoordinates = function (x, z) {
-            var ray = new BABYLON.Ray(new BABYLON.Vector3(x, this.getBoundingInfo().boundingBox.maximumWorld.y + 1, z), new BABYLON.Vector3(0, -1, 0));
-            this.getWorldMatrix().invertToRef(this._worldInverse);
-            ray = BABYLON.Ray.Transform(ray, this._worldInverse);
-            var pickInfo = this.intersects(ray);
-            if (pickInfo.hit) {
-                return pickInfo.pickedPoint.y;
+            if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
+                return 0;
+            }
+            if (!this._heightQuads || this._heightQuads.length == 0) {
+                this._computeHeightQuads();
+            }
+            var facet = this._getFacetAt(x, z);
+            var y = -(facet.x * x + facet.z * z + facet.w) / facet.y;
+            return y;
+        };
+        GroundMesh.prototype.getNormalAtCoordinates = function (x, z) {
+            var normal = new BABYLON.Vector3(0, 1, 0);
+            this.getNormalAtCoordinatesToRef(x, z, normal);
+            return normal;
+        };
+        GroundMesh.prototype.getNormalAtCoordinatesToRef = function (x, z, ref) {
+            if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
+                return;
+            }
+            if (!this._heightQuads || this._heightQuads.length == 0) {
+                this._computeHeightQuads();
+            }
+            var facet = this._getFacetAt(x, z);
+            ref.x = facet.x;
+            ref.y = facet.y;
+            ref.z = facet.z;
+        };
+        GroundMesh.prototype._getFacetAt = function (x, z) {
+            // retrieve col and row from x, z coordinates
+            var col = Math.floor((x + this._maxX) * this._subdivisions / this._width);
+            var row = Math.floor(-(z + this._maxZ) * this._subdivisions / this._height + this._subdivisions);
+            var quad = this._heightQuads[row * this._subdivisions + col];
+            var facet;
+            if (z < quad.slope.x * x + quad.slope.y) {
+                facet = quad.facet1;
+            }
+            else {
+                facet = quad.facet2;
+            }
+            return facet;
+        };
+        GroundMesh.prototype._computeHeightQuads = function () {
+            this._heightQuads = new Array();
+            var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+            var v1 = BABYLON.Vector3.Zero();
+            var v2 = BABYLON.Vector3.Zero();
+            var v3 = BABYLON.Vector3.Zero();
+            var v4 = BABYLON.Vector3.Zero();
+            var v1v2 = BABYLON.Vector3.Zero();
+            var v1v3 = BABYLON.Vector3.Zero();
+            var v1v4 = BABYLON.Vector3.Zero();
+            var norm1 = BABYLON.Vector3.Zero();
+            var norm2 = BABYLON.Vector3.Zero();
+            var i = 0;
+            var j = 0;
+            var k = 0;
+            var cd = 0; // 2D slope coefficient : z = cd * x + h
+            var h = 0;
+            var d1 = 0; // facet plane equation : ax + by + cz + d = 0
+            var d2 = 0;
+            for (var row = 0; row < this._subdivisions; row++) {
+                for (var col = 0; col < this._subdivisions; col++) {
+                    i = col * 3;
+                    j = row * (this._subdivisions + 1) * 3;
+                    k = (row + 1) * (this._subdivisions + 1) * 3;
+                    v1.x = positions[j + i];
+                    v1.y = positions[j + i + 1];
+                    v1.z = positions[j + i + 2];
+                    v2.x = positions[j + i + 3];
+                    v2.y = positions[j + i + 4];
+                    v2.z = positions[j + i + 5];
+                    v3.x = positions[k + i];
+                    v3.y = positions[k + i + 1];
+                    v3.z = positions[k + i + 2];
+                    v4.x = positions[k + i + 3];
+                    v4.y = positions[k + i + 4];
+                    v4.z = positions[k + i + 5];
+                    // 2D slope V1V4
+                    cd = (v4.z - v1.z) / (v4.x - v1.x);
+                    h = v1.z - cd * v1.x; // v1 belongs to the slope
+                    var slope = new BABYLON.Vector2(cd, h);
+                    // facet equations :
+                    // we compute each facet normal vector
+                    // the equation of the facet plane is : norm.x * x + norm.y * y + norm.z * z + d = 0
+                    // we compute the value d by applying the equation to v1 which belongs to the plane
+                    // then we store the facet equation in a Vector4
+                    v2.subtractToRef(v1, v1v2);
+                    v3.subtractToRef(v1, v1v3);
+                    v4.subtractToRef(v1, v1v4);
+                    BABYLON.Vector3.CrossToRef(v1v4, v1v3, norm1);
+                    BABYLON.Vector3.CrossToRef(v1v2, v1v4, norm2);
+                    norm1.normalize();
+                    norm2.normalize();
+                    d1 = -(norm1.x * v1.x + norm1.y * v1.y + norm1.z * v1.z);
+                    d2 = -(norm2.x * v2.x + norm2.y * v2.y + norm2.z * v2.z);
+                    var facet1 = new BABYLON.Vector4(norm1.x, norm1.y, norm1.z, d1);
+                    var facet2 = new BABYLON.Vector4(norm2.x, norm2.y, norm2.z, d2);
+                    var quad = { slope: slope, facet1: facet1, facet2: facet2 };
+                    this._heightQuads.push(quad);
+                }
             }
-            return 0;
         };
         return GroundMesh;
     })(BABYLON.Mesh);
@@ -28114,6 +28248,7 @@ var BABYLON;
                 + "Render: <b>" + BABYLON.Tools.Format(scene.getRenderDuration()) + " ms</b><br>"
                 + "Frame: " + BABYLON.Tools.Format(scene.getLastFrameDuration()) + " ms<br>"
                 + "Potential FPS: " + BABYLON.Tools.Format(1000.0 / scene.getLastFrameDuration(), 0) + "<br><br>"
+                + "Resolution: " + engine.getRenderWidth() + "x" + engine.getRenderHeight() + "<br><br>"
                 + "</div>"
                 + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
                 + "<b>Extensions</b><br>"
@@ -30273,6 +30408,7 @@ var BABYLON;
             this._reconstructedMesh.material = this._mesh.material;
             this._reconstructedMesh.parent = this._mesh.parent;
             this._reconstructedMesh.isVisible = false;
+            this._reconstructedMesh.renderingGroupId = this._mesh.renderingGroupId;
         };
         QuadraticErrorSimplification.prototype.isFlipped = function (vertex1, vertex2, point, deletedArray, borderFactor, delTr) {
             for (var i = 0; i < vertex1.triangleCount; ++i) {
@@ -34769,7 +34905,13 @@ var BABYLON;
         };
         Sound.Parse = function (parsedSound, scene, rootUrl, sourceSound) {
             var soundName = parsedSound.name;
-            var soundUrl = rootUrl + soundName;
+            var soundUrl;
+            if (parsedSound.url) {
+                soundUrl = rootUrl + parsedSound.url;
+            }
+            else {
+                soundUrl = rootUrl + soundName;
+            }
             var options = {
                 autoplay: parsedSound.autoplay, loop: parsedSound.loop, volume: parsedSound.volume,
                 spatialSound: parsedSound.spatialSound, maxDistance: parsedSound.maxDistance,

Разница между файлами не показана из-за своего большого размера
+ 21 - 21
dist/preview release/babylon.noworker.js


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

@@ -40,7 +40,9 @@
     - _Arc_ feature in `CreateCylinder`, `CreateSphere`, `CreateTube`, `CreateDisc` and `CreateLathe` ([jerome](https://github.com/jbousquie))
     - _Slice_ feature in `MeshBuilder.CreateSphere()` ([jerome](https://github.com/jbousquie))
     - `closed` parameter in `MeshBuilder.CreateLathe()` ([jerome](https://github.com/jbousquie))
+    - `cap` parameter in `MeshBuilder.CreateLathe()` ([jerome](https://github.com/jbousquie))
     - `diameter`, `hasRings`, `enclose` parameters in `MeshBuilder.CreateCreateCylinder()` ([jerome](https://github.com/jbousquie))
+    - added `getNormalAtCoordinates()` and `getNormalAtCoordinatesToRef()` methods in `MeshBuilder.CreateLathe()` ([jerome](https://github.com/jbousquie))
     - `Material.dispose()` now removes disposed material from meshes ([deltakosh](https://github.com/deltakosh))
     - New `Material.getBindedMeshes()` function ([deltakosh](https://github.com/deltakosh))
     - OimoJS Plugin now uses Quaternions exclusively and calculates body rotations correctly. [PR](https://github.com/BabylonJS/Babylon.js/pull/761) ([RaananW](https://github.com/RaananW))
@@ -61,10 +63,12 @@
     - Fixed a possible but with the active camera while taking a screenshot. [PR](https://github.com/BabylonJS/Babylon.js/pull/701) ([RaananW](https://github.com/RaananW))
     - Fixed a bug with worker-collisions and instances. [PR](https://github.com/BabylonJS/Babylon.js/pull/705) ([RaananW](https://github.com/RaananW))
     - Fixed a bug with removed meshes and geometries from the worker-cache. [PR](https://github.com/BabylonJS/Babylon.js/pull/711) ([RaananW](https://github.com/RaananW))
+    - Fixed `getHeightAtCoordinates()` : brand new ultra fast algorithm, can be used for many objects at once in the render loop now ([jerome](https://github.com/jbousquie))
     - Fixed `closePath` and `closeArray` ribbon parameter now working back together ([jerome](https://github.com/jbousquie))
     - Fixed morphing on capped tubes  ([jerome](https://github.com/jbousquie))
     - Fixed morphing on extruded shapes  ([jerome](https://github.com/jbousquie))
     - Fixed tube and extruded shape cap light artifact  ([jerome](https://github.com/jbousquie))
+    - Fixed lathe light artifact with dedicated new geometry  ([jerome](https://github.com/jbousquie))
     - Fixed a bug calculating velocity during collision with gravity enabled. [PR](https://github.com/BabylonJS/Babylon.js/pull/738) ([RaananW](https://github.com/RaananW))
     - Fixed a bug in instance serialization. [PR](https://github.com/BabylonJS/Babylon.js/pull/726) ([RaananW](https://github.com/RaananW))
     - Fixed a memory leak with textures ([deltakosh](https://github.com/deltakosh)) 

+ 8 - 0
materialsLibrary/config.json

@@ -82,6 +82,14 @@
         "materials/triPlanar/triplanar.fragment.fx"
       ],
       "output": "babylon.triPlanarMaterial.js"
+    },
+    {
+      "file": "materials/sky/babylon.skyMaterial.ts",
+      "shaderFiles": [
+        "materials/sky/sky.vertex.fx",
+        "materials/sky/sky.fragment.fx"
+      ],
+      "output": "babylon.skyMaterial.js"
     }
   ],
   "build": {

Разница между файлами не показана из-за своего большого размера
+ 228 - 0
materialsLibrary/dist/babylon.skyMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
materialsLibrary/dist/babylon.skyMaterial.min.js


+ 30 - 0
materialsLibrary/dist/dts/babylon.skyMaterial.d.ts

@@ -0,0 +1,30 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+declare module BABYLON {
+    class SkyMaterial extends Material {
+        luminance: number;
+        turbidity: number;
+        rayleigh: number;
+        mieCoefficient: number;
+        mieDirectionalG: number;
+        distance: number;
+        inclination: number;
+        azimuth: number;
+        private _sunPosition;
+        private _renderId;
+        private _defines;
+        private _cachedDefines;
+        constructor(name: string, scene: Scene);
+        needAlphaBlending(): boolean;
+        needAlphaTesting(): boolean;
+        getAlphaTestTexture(): BaseTexture;
+        private _checkCache(scene, mesh?, useInstances?);
+        isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean;
+        bindOnlyWorldMatrix(world: Matrix): void;
+        bind(world: Matrix, mesh?: Mesh): void;
+        getAnimatables(): IAnimatable[];
+        dispose(forceDisposeEffect?: boolean): void;
+        clone(name: string): SkyMaterial;
+        serialize(): any;
+        static Parse(source: any, scene: Scene, rootUrl: string): SkyMaterial;
+    }
+}

+ 287 - 0
materialsLibrary/materials/sky/babylon.skyMaterial.ts

@@ -0,0 +1,287 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    class SkyMaterialDefines extends MaterialDefines {
+        public CLIPPLANE = false;
+        public POINTSIZE = false;
+        public FOG = false;
+        public VERTEXCOLOR = false;
+        public VERTEXALPHA = false;
+
+        constructor() {
+            super();
+            this._keys = Object.keys(this);
+        }
+    }
+
+    export class SkyMaterial extends Material {
+        // Public members
+        public luminance: number = 1.0;
+		public turbidity: number = 10.0;
+		public rayleigh: number = 2.0;
+		public mieCoefficient: number = 0.005;
+		public mieDirectionalG: number = 0.8;
+        
+        public distance: number = 500;
+        public inclination: number = 0.49;
+		public azimuth: number = 0.25;
+        
+        // Private members
+        private _sunPosition: Vector3 = Vector3.Zero();
+        
+        private _renderId: number;
+        
+        private _defines = new SkyMaterialDefines();
+        private _cachedDefines = new SkyMaterialDefines();
+
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+        }
+
+        public needAlphaBlending(): boolean {
+            return (this.alpha < 1.0);
+        }
+
+        public needAlphaTesting(): boolean {
+            return false;
+        }
+
+        public getAlphaTestTexture(): BaseTexture {
+            return null;
+        }
+
+        // Methods   
+        private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (!mesh) {
+                return true;
+            }
+            
+            if (mesh._materialDefines && mesh._materialDefines.isEqual(this._defines)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (this.checkReadyOnlyOnce) {
+                if (this._wasPreviouslyReady) {
+                    return true;
+                }
+            }
+
+            var scene = this.getScene();
+
+            if (!this.checkReadyOnEveryCall) {
+                if (this._renderId === scene.getRenderId()) {
+                    if (this._checkCache(scene, mesh, useInstances)) {
+                        return true;
+                    }
+                }
+            }
+
+            var engine = scene.getEngine();
+            this._defines.reset();
+
+            // Effect
+            if (scene.clipPlane) {
+                this._defines.CLIPPLANE = true;
+            }
+
+            // Point size
+            if (this.pointsCloud || scene.forcePointsCloud) {
+                this._defines.POINTSIZE = true;
+            }
+
+            // Fog
+            if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
+                this._defines.FOG = true;
+            }
+            
+            // Attribs
+            if (mesh) {
+                if (mesh.useVertexColors && mesh.isVerticesDataPresent(VertexBuffer.ColorKind)) {
+                    this._defines.VERTEXCOLOR = true;
+
+                    if (mesh.hasVertexAlpha) {
+                        this._defines.VERTEXALPHA = true;
+                    }
+                }
+            }
+
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+                
+                scene.resetCachedMaterial();
+                
+                // Fallbacks
+                var fallbacks = new EffectFallbacks();             
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+
+                //Attributes
+                var attribs = [VertexBuffer.PositionKind];
+
+                if (this._defines.VERTEXCOLOR) {
+                    attribs.push(VertexBuffer.ColorKind);
+                }
+
+                // Legacy browser patch
+                var shaderName = "sky";
+                
+                var join = this._defines.toString();
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs,
+                    ["world", "viewProjection",
+                        "vFogInfos", "vFogColor", "pointSize", "vClipPlane",
+                        "luminance", "turbidity", "rayleigh", "mieCoefficient", "mieDirectionalG", "sunPosition"
+                    ],
+                    [],
+                    join, fallbacks, this.onCompiled, this.onError);
+            }
+            
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            this._renderId = scene.getRenderId();
+            this._wasPreviouslyReady = true;
+
+            if (mesh) {
+                if (!mesh._materialDefines) {
+                    mesh._materialDefines = new SkyMaterialDefines();
+                }
+
+                this._defines.cloneTo(mesh._materialDefines);
+            }
+
+            return true;
+        }
+
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            this._effect.setMatrix("world", world);
+        }
+
+        public bind(world: Matrix, mesh?: Mesh): void {
+            var scene = this.getScene();
+
+            // Matrices        
+            this.bindOnlyWorldMatrix(world);
+            this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
+
+            if (scene.getCachedMaterial() !== this) {
+                // Clip plane
+                if (scene.clipPlane) {
+                    var clipPlane = scene.clipPlane;
+                    this._effect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
+                }
+
+                // Point size
+                if (this.pointsCloud) {
+                    this._effect.setFloat("pointSize", this.pointSize);
+                }               
+            }
+
+            // View
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setMatrix("view", scene.getViewMatrix());
+            }
+            
+            // Fog
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setFloat4("vFogInfos", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
+                this._effect.setColor3("vFogColor", scene.fogColor);
+            }
+            
+            // Sky
+            this._effect.setFloat("luminance", this.luminance);
+			this._effect.setFloat("turbidity", this.turbidity);
+			this._effect.setFloat("rayleigh", this.rayleigh);
+			this._effect.setFloat("mieCoefficient", this.mieCoefficient);
+			this._effect.setFloat("mieDirectionalG", this.mieDirectionalG);
+            
+            var theta = Math.PI * (this.inclination - 0.5);
+			var phi = 2 * Math.PI * (this.azimuth - 0.5);
+            
+            this._sunPosition.x = this.distance * Math.cos( phi );
+			this._sunPosition.y = this.distance * Math.sin( phi ) * Math.sin( theta );
+			this._sunPosition.z = this.distance * Math.sin( phi ) * Math.cos( theta );
+            
+			this._effect.setVector3("sunPosition", this._sunPosition);
+
+            super.bind(world, mesh);
+        }
+
+        public getAnimatables(): IAnimatable[] {
+            return [];
+        }
+
+        public dispose(forceDisposeEffect?: boolean): void {
+            super.dispose(forceDisposeEffect);
+        }
+
+        public clone(name: string): SkyMaterial {
+            var newMaterial = new SkyMaterial(name, this.getScene());
+
+            // Base material
+            this.copyTo(newMaterial);
+            
+            newMaterial.luminance = this.luminance;
+            newMaterial.turbidity = this.turbidity;
+            newMaterial.rayleigh = this.rayleigh;
+            newMaterial.mieCoefficient = this.mieCoefficient;
+            newMaterial.mieDirectionalG = this.mieDirectionalG;
+            newMaterial.distance = this.distance;
+            newMaterial.inclination = this.inclination;
+            newMaterial.azimuth = this.azimuth;
+            
+            return newMaterial;
+        }
+		
+		public serialize(): any {
+		
+            var serializationObject = super.serialize();
+            serializationObject.customType = "BABYLON.SkyMaterial";
+            
+            serializationObject.luminance = this.luminance;
+            serializationObject.turbidity = this.turbidity;
+            serializationObject.rayleigh = this.rayleigh;
+            serializationObject.mieCoefficient = this.mieCoefficient;
+            serializationObject.mieDirectionalG = this.mieDirectionalG;
+            serializationObject.distance = this.distance;
+            serializationObject.inclination = this.inclination;
+            serializationObject.azimuth = this.azimuth;
+
+            return serializationObject;
+        }
+
+        public static Parse(source: any, scene: Scene, rootUrl: string): SkyMaterial {
+            var material = new SkyMaterial(source.name, scene);
+
+            material.alpha = source.alpha;
+            material.id = source.id;
+            
+            Tags.AddTagsTo(material, source.tags);
+            material.backFaceCulling = source.backFaceCulling;
+            material.wireframe = source.wireframe;
+
+            if (source.checkReadyOnlyOnce) {
+                material.checkReadyOnlyOnce = source.checkReadyOnlyOnce;
+            }
+            
+            material.luminance = source.luminance;
+            material.turbidity = source.turbidity;
+            material.rayleigh = source.rayleigh;
+            material.mieCoefficient = source.mieCoefficient;
+            material.mieDirectionalG = source.mieDirectionalG;
+            material.distance = source.distance;
+            material.inclination = source.inclination;
+            material.azimuth = source.azimuth;
+
+            return material;
+        }
+    }
+} 
+

+ 211 - 0
materialsLibrary/materials/sky/sky.fragment.fx

@@ -0,0 +1,211 @@
+precision highp float;
+
+// Input
+varying vec3 vPositionW;
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+// Sky
+uniform float luminance;
+uniform float turbidity;
+uniform float rayleigh;
+uniform float mieCoefficient;
+uniform float mieDirectionalG;
+uniform vec3 sunPosition;
+
+// Fog
+#ifdef FOG
+#define FOGMODE_NONE    0.
+#define FOGMODE_EXP     1.
+#define FOGMODE_EXP2    2.
+#define FOGMODE_LINEAR  3.
+#define E 2.71828
+
+uniform vec4 vFogInfos;
+uniform vec3 vFogColor;
+varying float fFogDistance;
+
+float CalcFogFactor()
+{
+	float fogCoeff = 1.0;
+	float fogStart = vFogInfos.y;
+	float fogEnd = vFogInfos.z;
+	float fogDensity = vFogInfos.w;
+
+	if (FOGMODE_LINEAR == vFogInfos.x)
+	{
+		fogCoeff = (fogEnd - fFogDistance) / (fogEnd - fogStart);
+	}
+	else if (FOGMODE_EXP == vFogInfos.x)
+	{
+		fogCoeff = 1.0 / pow(E, fFogDistance * fogDensity);
+	}
+	else if (FOGMODE_EXP2 == vFogInfos.x)
+	{
+		fogCoeff = 1.0 / pow(E, fFogDistance * fFogDistance * fogDensity * fogDensity);
+	}
+
+	return clamp(fogCoeff, 0.0, 1.0);
+}
+#endif
+
+// Constants
+const float e = 2.71828182845904523536028747135266249775724709369995957;
+const float pi = 3.141592653589793238462643383279502884197169;
+const float n = 1.0003;
+const float N = 2.545E25;
+const float pn = 0.035;
+
+const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);
+
+const vec3 K = vec3(0.686, 0.678, 0.666);
+const float v = 4.0;
+
+const float rayleighZenithLength = 8.4E3;
+const float mieZenithLength = 1.25E3;
+const vec3 up = vec3(0.0, 1.0, 0.0);
+
+const float EE = 1000.0;
+const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;
+
+const float cutoffAngle = pi/1.95;
+const float steepness = 1.5;
+
+vec3 totalRayleigh(vec3 lambda)
+{
+	return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn));
+}
+
+vec3 simplifiedRayleigh()
+{
+	return 0.0005 / vec3(94, 40, 18);
+}
+
+float rayleighPhase(float cosTheta)
+{	 
+	return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));
+}
+
+vec3 totalMie(vec3 lambda, vec3 K, float T)
+{
+	float c = (0.2 * T ) * 10E-18;
+	return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;
+}
+
+float hgPhase(float cosTheta, float g)
+{
+	return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5));
+}
+
+float sunIntensity(float zenithAngleCos)
+{
+	return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));
+}
+
+float A = 0.15;
+float B = 0.50;
+float C = 0.10;
+float D = 0.20;
+float E = 0.02;
+float F = 0.30;
+float W = 1000.0;
+
+vec3 Uncharted2Tonemap(vec3 x)
+{
+	return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
+}
+
+void main(void) {
+	// Clip plane
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif
+
+	/**
+	*--------------------------------------------------------------------------------------------------
+	* Sky Color
+	*--------------------------------------------------------------------------------------------------
+	*/
+	const vec3 cameraPos = vec3(0.0, 0.0, 0.0);
+	float sunfade = 1.0 - clamp(1.0 - exp((sunPosition.y / 450000.0)), 0.0, 1.0);
+	float rayleighCoefficient = rayleigh - (1.0 * (1.0 - sunfade));
+	vec3 sunDirection = normalize(sunPosition);
+	float sunE = sunIntensity(dot(sunDirection, up));
+	vec3 betaR = simplifiedRayleigh() * rayleighCoefficient;
+	vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;
+	float zenithAngle = acos(max(0.0, dot(up, normalize(vPositionW - cameraPos))));
+	float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));
+	float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));
+	vec3 Fex = exp(-(betaR * sR + betaM * sM));
+	float cosTheta = dot(normalize(vPositionW - cameraPos), sunDirection);
+	float rPhase = rayleighPhase(cosTheta*0.5+0.5);
+	vec3 betaRTheta = betaR * rPhase;
+	float mPhase = hgPhase(cosTheta, mieDirectionalG);
+	vec3 betaMTheta = betaM * mPhase;
+	
+	vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));
+	Lin *= mix(vec3(1.0), pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex, vec3(1.0 / 2.0)), clamp(pow(1.0-dot(up, sunDirection), 5.0), 0.0, 1.0));
+
+	vec3 direction = normalize(vPositionW - cameraPos);
+	float theta = acos(direction.y);
+	float phi = atan(direction.z, direction.x);
+	vec2 uv = vec2(phi, theta) / vec2(2.0 * pi, pi) + vec2(0.5, 0.0);
+	vec3 L0 = vec3(0.1) * Fex;
+	
+	float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta);
+	L0 += (sunE * 19000.0 * Fex) * sundisk;
+	
+	vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));
+	vec3 texColor = (Lin+L0);   
+	texColor *= 0.04 ;
+	texColor += vec3(0.0,0.001,0.0025)*0.3;
+
+	float g_fMaxLuminance = 1.0;
+	float fLumScaled = 0.1 / luminance;     
+	float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); 
+
+	float ExposureBias = fLumCompressed;
+
+	vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);
+	vec3 skyColor = curr * whiteScale;
+
+	vec3 retColor = pow(skyColor,vec3(1.0/(1.2+(1.2*sunfade))));
+	
+	vec4 baseColor = vec4(retColor, 1.0);
+	/**
+	*--------------------------------------------------------------------------------------------------
+	* Sky Color
+	*--------------------------------------------------------------------------------------------------
+	*/
+	
+	// Alpha
+	float alpha = 1.0;
+
+#ifdef VERTEXCOLOR
+	baseColor.rgb *= vColor.rgb;
+#endif
+
+	// Lighting
+	vec3 diffuseBase = vec3(1.0, 1.0, 1.0);
+
+#ifdef VERTEXALPHA
+	alpha *= vColor.a;
+#endif
+
+	// Composition
+	vec4 color = vec4(baseColor.rgb, alpha);
+
+#ifdef FOG
+	float fog = CalcFogFactor();
+	color.rgb = fog * color.rgb + (1.0 - fog) * vFogColor;
+#endif
+
+	gl_FragColor = color;
+}

+ 59 - 0
materialsLibrary/materials/sky/sky.vertex.fx

@@ -0,0 +1,59 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+
+#ifdef VERTEXCOLOR
+attribute vec4 color;
+#endif
+
+// Uniforms
+uniform mat4 world;
+uniform mat4 viewProjection;
+
+#ifdef POINTSIZE
+uniform float pointSize;
+#endif
+
+// Output
+varying vec3 vPositionW;
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+#ifdef FOG
+varying float fFogDistance;
+#endif
+
+void main(void) {
+	gl_Position = viewProjection * world * vec4(position, 1.0);
+	
+	vec4 worldPos = world * vec4(position, 1.0);
+	vPositionW = vec3(worldPos);
+
+	// Clip plane
+#ifdef CLIPPLANE
+	fClipDistance = dot(worldPos, vClipPlane);
+#endif
+
+	// Fog
+#ifdef FOG
+	fFogDistance = (view * worldPos).z;
+#endif
+
+	// Vertex color
+#ifdef VERTEXCOLOR
+	vColor = color;
+#endif
+
+	// Point size
+#ifdef POINTSIZE
+	gl_PointSize = pointSize;
+#endif
+}

+ 48 - 0
materialsLibrary/test/add/addsky.js

@@ -0,0 +1,48 @@
+window.prepareSky = function() {
+	var sky = new BABYLON.SkyMaterial("sky", scene);
+	sky.backFaceCulling = false;
+	
+	registerRangeUI("sky", "azimuth", 0, 1, function(value) {
+		sky.azimuth = value;
+	}, function() {
+		return sky.azimuth;
+	});
+	
+	registerRangeUI("sky", "inclination", 0, 1, function(value) {
+		sky.inclination = value;
+	}, function() {
+		return sky.inclination;
+	});
+	
+	registerRangeUI("sky", "luminance", 0, 2, function(value) {
+		sky.luminance = value;
+	}, function() {
+		return sky.luminance;
+	});
+	
+	registerRangeUI("sky", "mieDirectionalG", 0, 1, function(value) {
+		sky.mieDirectionalG = value;
+	}, function() {
+		return sky.mieDirectionalG;
+	});
+	
+	registerRangeUI("sky", "mieCoefficient", 0, 0.1, function(value) {
+		sky.mieCoefficient = value;
+	}, function() {
+		return sky.mieCoefficient;
+	});
+	
+	registerRangeUI("sky", "rayleigh", 0, 4, function(value) {
+		sky.rayleigh = value;
+	}, function() {
+		return sky.rayleigh;
+	});
+	
+	registerRangeUI("sky", "turbidity", 0, 20, function(value) {
+		sky.turbidity = value;
+	}, function() {
+		return sky.turbidity;
+	});
+		
+	return sky;
+}

+ 10 - 1
materialsLibrary/test/index.html

@@ -14,6 +14,7 @@
 	<script src="../dist/babylon.furMaterial.js"></script>
 	<script src="../dist/babylon.triPlanarMaterial.js"></script>
 	<script src="../dist/babylon.gradientMaterial.js"></script>
+	<script src="../dist/babylon.skyMaterial.js"></script>
 
 	<style>
 		html, body {
@@ -57,6 +58,7 @@
 	<script src="add/addfire.js"></script>
 	<script src="add/addtriplanar.js"></script>
 	<script src="add/addgradient.js"></script>
+	<script src="add/addsky.js"></script>
 	
 	<script>
 		if (BABYLON.Engine.isSupported()) {
@@ -201,13 +203,16 @@
 				
 				var triPlanar = prepareTriPlanar();
 				
+				var sky = prepareSky();
+				
 				// Default to std
 				var currentMaterial = std;
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky']).onFinishChange(function () {
 					water.enableRenderTargets(false);
+					skybox.material = skyboxMaterial;
 					
 					switch (options.material) {
 						case "simple":
@@ -242,6 +247,10 @@
 						case "gradient":
 							currentMaterial = gradient;
 							break;
+						case "sky":
+							skybox.setEnabled(true);
+							skybox.material = sky;
+							break;
 						default:
 							currentMaterial = std;
 							break;

+ 5 - 2
src/Actions/babylon.action.js

@@ -39,6 +39,11 @@ var BABYLON;
                 }
             }
             this._nextActiveAction.execute(evt);
+            this.skipToNextActiveAction();
+        };
+        Action.prototype.execute = function (evt) {
+        };
+        Action.prototype.skipToNextActiveAction = function () {
             if (this._nextActiveAction._child) {
                 if (!this._nextActiveAction._child._actionManager) {
                     this._nextActiveAction._child._actionManager = this._actionManager;
@@ -49,8 +54,6 @@ var BABYLON;
                 this._nextActiveAction = this;
             }
         };
-        Action.prototype.execute = function (evt) {
-        };
         Action.prototype.then = function (action) {
             this._child = action;
             action._actionManager = this._actionManager;

+ 9 - 5
src/Actions/babylon.action.ts

@@ -53,6 +53,14 @@
 
             this._nextActiveAction.execute(evt);
 
+            this.skipToNextActiveAction();
+        }
+
+        public execute(evt: ActionEvent): void {
+
+        }
+
+        public skipToNextActiveAction(): void {
             if (this._nextActiveAction._child) {
 
                 if (!this._nextActiveAction._child._actionManager) {
@@ -65,10 +73,6 @@
             }
         }
 
-        public execute(evt: ActionEvent): void {
-
-        }
-
         public then(action: Action): Action {
             this._child = action;
 
@@ -86,4 +90,4 @@
             return this._actionManager._getEffectiveTarget(target, propertyPath);
         }
     }
-}
+}

+ 34 - 22
src/Actions/babylon.actionManager.js

@@ -95,6 +95,27 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(ActionManager, "OnPickDownTrigger", {
+            get: function () {
+                return ActionManager._OnPickDownTrigger;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ActionManager, "OnPickUpTrigger", {
+            get: function () {
+                return ActionManager._OnPickUpTrigger;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ActionManager, "OnLongPressTrigger", {
+            get: function () {
+                return ActionManager._OnLongPressTrigger;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(ActionManager, "OnPointerOverTrigger", {
             get: function () {
                 return ActionManager._OnPointerOverTrigger;
@@ -144,13 +165,6 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        Object.defineProperty(ActionManager, "OnPickUpTrigger", {
-            get: function () {
-                return ActionManager._OnPickUpTrigger;
-            },
-            enumerable: true,
-            configurable: true
-        });
         // Methods
         ActionManager.prototype.dispose = function () {
             var index = this._scene._actionManagers.indexOf(this);
@@ -200,9 +214,6 @@ var BABYLON;
                     if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPointerOutTrigger) {
                         return true;
                     }
-                    if (action.trigger === ActionManager._OnPickUpTrigger) {
-                        return true;
-                    }
                 }
                 return false;
             },
@@ -217,10 +228,7 @@ var BABYLON;
             get: function () {
                 for (var index = 0; index < this.actions.length; index++) {
                     var action = this.actions[index];
-                    if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnCenterPickTrigger) {
-                        return true;
-                    }
-                    if (action.trigger === ActionManager._OnPickUpTrigger) {
+                    if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPickUpTrigger) {
                         return true;
                     }
                 }
@@ -438,14 +446,18 @@ var BABYLON;
         ActionManager._OnLeftPickTrigger = 2;
         ActionManager._OnRightPickTrigger = 3;
         ActionManager._OnCenterPickTrigger = 4;
-        ActionManager._OnPointerOverTrigger = 5;
-        ActionManager._OnPointerOutTrigger = 6;
-        ActionManager._OnEveryFrameTrigger = 7;
-        ActionManager._OnIntersectionEnterTrigger = 8;
-        ActionManager._OnIntersectionExitTrigger = 9;
-        ActionManager._OnKeyDownTrigger = 10;
-        ActionManager._OnKeyUpTrigger = 11;
-        ActionManager._OnPickUpTrigger = 12;
+        ActionManager._OnPickDownTrigger = 5;
+        ActionManager._OnPickUpTrigger = 6;
+        ActionManager._OnLongPressTrigger = 7;
+        ActionManager._OnPointerOverTrigger = 8;
+        ActionManager._OnPointerOutTrigger = 9;
+        ActionManager._OnEveryFrameTrigger = 10;
+        ActionManager._OnIntersectionEnterTrigger = 11;
+        ActionManager._OnIntersectionExitTrigger = 12;
+        ActionManager._OnKeyDownTrigger = 13;
+        ActionManager._OnKeyUpTrigger = 14;
+        ActionManager.DragMovementThreshold = 10; // in pixels
+        ActionManager.LongPressDelay = 500; // in milliseconds
         return ActionManager;
     })();
     BABYLON.ActionManager = ActionManager;

+ 28 - 20
src/Actions/babylon.actionManager.ts

@@ -57,14 +57,16 @@
         private static _OnLeftPickTrigger = 2;
         private static _OnRightPickTrigger = 3;
         private static _OnCenterPickTrigger = 4;
-        private static _OnPointerOverTrigger = 5;
-        private static _OnPointerOutTrigger = 6;
-        private static _OnEveryFrameTrigger = 7;
-        private static _OnIntersectionEnterTrigger = 8;
-        private static _OnIntersectionExitTrigger = 9;
-        private static _OnKeyDownTrigger = 10;
-        private static _OnKeyUpTrigger = 11;
-        private static _OnPickUpTrigger = 12;
+        private static _OnPickDownTrigger = 5;
+        private static _OnPickUpTrigger = 6;
+        private static _OnLongPressTrigger = 7;
+        private static _OnPointerOverTrigger = 8;
+        private static _OnPointerOutTrigger = 9;
+        private static _OnEveryFrameTrigger = 10;
+        private static _OnIntersectionEnterTrigger = 11;
+        private static _OnIntersectionExitTrigger = 12;
+        private static _OnKeyDownTrigger = 13;
+        private static _OnKeyUpTrigger = 14;
 
         public static get NothingTrigger(): number {
             return ActionManager._NothingTrigger;
@@ -86,6 +88,18 @@
             return ActionManager._OnCenterPickTrigger;
         }
 
+        public static get OnPickDownTrigger(): number {
+            return ActionManager._OnPickDownTrigger;
+        }
+
+        public static get OnPickUpTrigger(): number {
+            return ActionManager._OnPickUpTrigger;
+        }
+
+        public static get OnLongPressTrigger(): number {
+            return ActionManager._OnLongPressTrigger;
+        }
+
         public static get OnPointerOverTrigger(): number {
             return ActionManager._OnPointerOverTrigger;
         }
@@ -113,9 +127,10 @@
         public static get OnKeyUpTrigger(): number {
             return ActionManager._OnKeyUpTrigger;
         }
-        public static get OnPickUpTrigger(): number {
-            return ActionManager._OnPickUpTrigger;
-        }
+
+        public static DragMovementThreshold = 10; // in pixels
+        public static LongPressDelay = 500; // in milliseconds
+        
         // Members
         public actions = new Array<Action>();
 
@@ -185,9 +200,6 @@
                 if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPointerOutTrigger) {
                     return true;
                 }
-                if (action.trigger === ActionManager._OnPickUpTrigger) {
-                    return true;
-                }
             }
 
             return false;
@@ -201,10 +213,7 @@
             for (var index = 0; index < this.actions.length; index++) {
                 var action = this.actions[index];
 
-                if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnCenterPickTrigger) {
-                    return true;
-                }
-                if (action.trigger === ActionManager._OnPickUpTrigger) {
+                if (action.trigger >= ActionManager._OnPickTrigger && action.trigger <= ActionManager._OnPickUpTrigger) {
                     return true;
                 }
             }
@@ -225,7 +234,6 @@
                 }
             }
 
-
             this.actions.push(action);
 
             action._actionManager = this;
@@ -458,4 +466,4 @@
         }
 
     }
-} 
+} 

+ 7 - 1
src/Audio/babylon.sound.js

@@ -484,7 +484,13 @@ var BABYLON;
         };
         Sound.Parse = function (parsedSound, scene, rootUrl, sourceSound) {
             var soundName = parsedSound.name;
-            var soundUrl = rootUrl + soundName;
+            var soundUrl;
+            if (parsedSound.url) {
+                soundUrl = rootUrl + parsedSound.url;
+            }
+            else {
+                soundUrl = rootUrl + soundName;
+            }
             var options = {
                 autoplay: parsedSound.autoplay, loop: parsedSound.loop, volume: parsedSound.volume,
                 spatialSound: parsedSound.spatialSound, maxDistance: parsedSound.maxDistance,

+ 8 - 1
src/Audio/babylon.sound.ts

@@ -526,7 +526,14 @@
 
         public static Parse(parsedSound: any, scene: Scene, rootUrl: string, sourceSound?: Sound): Sound {
             var soundName = parsedSound.name;
-            var soundUrl = rootUrl + soundName;
+            var soundUrl;
+            
+            if (parsedSound.url) {
+                soundUrl = rootUrl + parsedSound.url;
+            }
+            else {
+                soundUrl = rootUrl + soundName;
+            }
 
             var options = {
                 autoplay: parsedSound.autoplay, loop: parsedSound.loop, volume: parsedSound.volume,

+ 1 - 0
src/Debug/babylon.debugLayer.js

@@ -596,6 +596,7 @@ var BABYLON;
                 + "Render: <b>" + BABYLON.Tools.Format(scene.getRenderDuration()) + " ms</b><br>"
                 + "Frame: " + BABYLON.Tools.Format(scene.getLastFrameDuration()) + " ms<br>"
                 + "Potential FPS: " + BABYLON.Tools.Format(1000.0 / scene.getLastFrameDuration(), 0) + "<br><br>"
+                + "Resolution: " + engine.getRenderWidth() + "x" + engine.getRenderHeight() + "<br><br>"
                 + "</div>"
                 + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
                 + "<b>Extensions</b><br>"

+ 1 - 0
src/Debug/babylon.debugLayer.ts

@@ -741,6 +741,7 @@
             + "Render: <b>" + Tools.Format(scene.getRenderDuration()) + " ms</b><br>"
             + "Frame: " + Tools.Format(scene.getLastFrameDuration()) + " ms<br>"
             + "Potential FPS: " + Tools.Format(1000.0 / scene.getLastFrameDuration(), 0) + "<br><br>"
+            + "Resolution: " + engine.getRenderWidth() + "x" + engine.getRenderHeight() + "<br><br>"
             + "</div>"
             + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
             + "<b>Extensions</b><br>"

+ 5 - 3
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -327,12 +327,14 @@ var BABYLON;
                     for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
                         var parsedSound = parsedData.sounds[index];
                         if (BABYLON.Engine.audioEngine.canUseWebAudio) {
-                            if (!loadedSounds[parsedSound.name]) {
+                            if (!parsedSound.url)
+                                parsedSound.url = parsedSound.name;
+                            if (!loadedSounds[parsedSound.url]) {
                                 loadedSound = BABYLON.Sound.Parse(parsedSound, scene, rootUrl);
-                                loadedSounds[loadedSound.name] = loadedSound;
+                                loadedSounds[parsedSound.url] = loadedSound;
                             }
                             else {
-                                BABYLON.Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.name]);
+                                BABYLON.Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
                             }
                         }
                         else {

+ 4 - 3
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -358,12 +358,13 @@
                 for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
                     var parsedSound = parsedData.sounds[index];
                     if (Engine.audioEngine.canUseWebAudio) {
-                        if (!loadedSounds[parsedSound.name]) {
+                        if (!parsedSound.url) parsedSound.url = parsedSound.name;
+                        if (!loadedSounds[parsedSound.url]) {
                             loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
-                            loadedSounds[loadedSound.name] = loadedSound;
+                            loadedSounds[parsedSound.url] = loadedSound;
                         }
                         else {
-                            Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.name]);
+                            Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
                         }
                     } else {
                         var emptySound = new Sound(parsedSound.name, null, scene);

+ 100 - 7
src/Mesh/babylon.groundMesh.js

@@ -26,14 +26,107 @@ var BABYLON;
             this.createOrUpdateSubmeshesOctree(octreeBlocksSize);
         };
         GroundMesh.prototype.getHeightAtCoordinates = function (x, z) {
-            var ray = new BABYLON.Ray(new BABYLON.Vector3(x, this.getBoundingInfo().boundingBox.maximumWorld.y + 1, z), new BABYLON.Vector3(0, -1, 0));
-            this.getWorldMatrix().invertToRef(this._worldInverse);
-            ray = BABYLON.Ray.Transform(ray, this._worldInverse);
-            var pickInfo = this.intersects(ray);
-            if (pickInfo.hit) {
-                return pickInfo.pickedPoint.y;
+            if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
+                return 0;
+            }
+            if (!this._heightQuads || this._heightQuads.length == 0) {
+                this._computeHeightQuads();
+            }
+            var facet = this._getFacetAt(x, z);
+            var y = -(facet.x * x + facet.z * z + facet.w) / facet.y;
+            return y;
+        };
+        GroundMesh.prototype.getNormalAtCoordinates = function (x, z) {
+            var normal = new BABYLON.Vector3(0, 1, 0);
+            this.getNormalAtCoordinatesToRef(x, z, normal);
+            return normal;
+        };
+        GroundMesh.prototype.getNormalAtCoordinatesToRef = function (x, z, ref) {
+            if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
+                return;
+            }
+            if (!this._heightQuads || this._heightQuads.length == 0) {
+                this._computeHeightQuads();
+            }
+            var facet = this._getFacetAt(x, z);
+            ref.x = facet.x;
+            ref.y = facet.y;
+            ref.z = facet.z;
+        };
+        GroundMesh.prototype._getFacetAt = function (x, z) {
+            // retrieve col and row from x, z coordinates
+            var col = Math.floor((x + this._maxX) * this._subdivisions / this._width);
+            var row = Math.floor(-(z + this._maxZ) * this._subdivisions / this._height + this._subdivisions);
+            var quad = this._heightQuads[row * this._subdivisions + col];
+            var facet;
+            if (z < quad.slope.x * x + quad.slope.y) {
+                facet = quad.facet1;
+            }
+            else {
+                facet = quad.facet2;
+            }
+            return facet;
+        };
+        GroundMesh.prototype._computeHeightQuads = function () {
+            this._heightQuads = new Array();
+            var positions = this.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+            var v1 = BABYLON.Vector3.Zero();
+            var v2 = BABYLON.Vector3.Zero();
+            var v3 = BABYLON.Vector3.Zero();
+            var v4 = BABYLON.Vector3.Zero();
+            var v1v2 = BABYLON.Vector3.Zero();
+            var v1v3 = BABYLON.Vector3.Zero();
+            var v1v4 = BABYLON.Vector3.Zero();
+            var norm1 = BABYLON.Vector3.Zero();
+            var norm2 = BABYLON.Vector3.Zero();
+            var i = 0;
+            var j = 0;
+            var k = 0;
+            var cd = 0; // 2D slope coefficient : z = cd * x + h
+            var h = 0;
+            var d1 = 0; // facet plane equation : ax + by + cz + d = 0
+            var d2 = 0;
+            for (var row = 0; row < this._subdivisions; row++) {
+                for (var col = 0; col < this._subdivisions; col++) {
+                    i = col * 3;
+                    j = row * (this._subdivisions + 1) * 3;
+                    k = (row + 1) * (this._subdivisions + 1) * 3;
+                    v1.x = positions[j + i];
+                    v1.y = positions[j + i + 1];
+                    v1.z = positions[j + i + 2];
+                    v2.x = positions[j + i + 3];
+                    v2.y = positions[j + i + 4];
+                    v2.z = positions[j + i + 5];
+                    v3.x = positions[k + i];
+                    v3.y = positions[k + i + 1];
+                    v3.z = positions[k + i + 2];
+                    v4.x = positions[k + i + 3];
+                    v4.y = positions[k + i + 4];
+                    v4.z = positions[k + i + 5];
+                    // 2D slope V1V4
+                    cd = (v4.z - v1.z) / (v4.x - v1.x);
+                    h = v1.z - cd * v1.x; // v1 belongs to the slope
+                    var slope = new BABYLON.Vector2(cd, h);
+                    // facet equations :
+                    // we compute each facet normal vector
+                    // the equation of the facet plane is : norm.x * x + norm.y * y + norm.z * z + d = 0
+                    // we compute the value d by applying the equation to v1 which belongs to the plane
+                    // then we store the facet equation in a Vector4
+                    v2.subtractToRef(v1, v1v2);
+                    v3.subtractToRef(v1, v1v3);
+                    v4.subtractToRef(v1, v1v4);
+                    BABYLON.Vector3.CrossToRef(v1v4, v1v3, norm1);
+                    BABYLON.Vector3.CrossToRef(v1v2, v1v4, norm2);
+                    norm1.normalize();
+                    norm2.normalize();
+                    d1 = -(norm1.x * v1.x + norm1.y * v1.y + norm1.z * v1.z);
+                    d2 = -(norm2.x * v2.x + norm2.y * v2.y + norm2.z * v2.z);
+                    var facet1 = new BABYLON.Vector4(norm1.x, norm1.y, norm1.z, d1);
+                    var facet2 = new BABYLON.Vector4(norm2.x, norm2.y, norm2.z, d2);
+                    var quad = { slope: slope, facet1: facet1, facet2: facet2 };
+                    this._heightQuads.push(quad);
+                }
             }
-            return 0;
         };
         return GroundMesh;
     })(BABYLON.Mesh);

+ 112 - 9
src/Mesh/babylon.groundMesh.ts

@@ -3,7 +3,14 @@
         public generateOctree = false;
 
         private _worldInverse = new Matrix();
+        private _heightQuads: { slope: Vector2; facet1: Vector4; facet2: Vector4 }[];
         public _subdivisions: number;
+        public _width: number;
+        public _height: number;
+        public _minX: number;
+        public _maxX: number;
+        public _minZ: number;
+        public _maxZ: number;
 
         constructor(name: string, scene: Scene) {
             super(name, scene);
@@ -20,19 +27,115 @@
         }
 
         public getHeightAtCoordinates(x: number, z: number): number {
-            var ray = new Ray(new Vector3(x, this.getBoundingInfo().boundingBox.maximumWorld.y + 1, z), new Vector3(0, -1, 0));
-
-            this.getWorldMatrix().invertToRef(this._worldInverse);
+            if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
+                return 0;
+            }
+            if (!this._heightQuads || this._heightQuads.length == 0) {
+                this._computeHeightQuads();
+            }
+            var facet = this._getFacetAt(x, z);
+            var y = -(facet.x * x + facet.z * z + facet.w) / facet.y;
+            return y;
+        }
 
-            ray = Ray.Transform(ray, this._worldInverse);
+        public getNormalAtCoordinates(x: number, z: number): Vector3 {
+            var normal = new Vector3(0, 1, 0);
+            this.getNormalAtCoordinatesToRef(x, z, normal);
+            return normal;
+        }
 
-            var pickInfo = this.intersects(ray);
+        public getNormalAtCoordinatesToRef(x: number, z: number, ref: Vector3): void {
+            if (x < this._minX || x > this._maxX || z < this._minZ || z > this._maxZ) {
+                return;
+            }
+            if (!this._heightQuads || this._heightQuads.length == 0) {
+                this._computeHeightQuads();
+            }
+            var facet = this._getFacetAt(x, z);
+            ref.x = facet.x;
+            ref.y = facet.y;
+            ref.z = facet.z;
+        }
 
-            if (pickInfo.hit) {
-                return pickInfo.pickedPoint.y;
+        private _getFacetAt(x: number, z: number): Vector4 {
+            // retrieve col and row from x, z coordinates
+            var col = Math.floor((x + this._maxX) * this._subdivisions / this._width);
+            var row = Math.floor(-(z + this._maxZ) * this._subdivisions / this._height + this._subdivisions);
+            var quad = this._heightQuads[row * this._subdivisions + col];
+            var facet;
+            if (z < quad.slope.x * x + quad.slope.y) {
+                facet = quad.facet1;
+            } else {
+                facet = quad.facet2;
             }
+            return facet;
+        }
+
+        private _computeHeightQuads(): void {
+            this._heightQuads = new Array();
+            var positions = this.getVerticesData(VertexBuffer.PositionKind);
+            var v1 = Vector3.Zero();
+            var v2 = Vector3.Zero();
+            var v3 = Vector3.Zero();
+            var v4 = Vector3.Zero();
+            var v1v2 = Vector3.Zero();
+            var v1v3 = Vector3.Zero();
+            var v1v4 = Vector3.Zero();
+            var norm1 = Vector3.Zero();
+            var norm2 = Vector3.Zero();
+            var i = 0;
+            var j = 0;
+            var k = 0;
+            var cd = 0;     // 2D slope coefficient : z = cd * x + h
+            var h = 0;
+            var d1 = 0;     // facet plane equation : ax + by + cz + d = 0
+            var d2 = 0;
+
+            for (var row = 0; row < this._subdivisions; row++) {
+                for (var col = 0; col < this._subdivisions; col++) {
+                    i = col * 3;
+                    j = row * (this._subdivisions + 1) * 3;
+                    k = (row + 1) * (this._subdivisions + 1) * 3;
+                    v1.x = positions[j + i];
+                    v1.y = positions[j + i + 1];
+                    v1.z = positions[j + i + 2];
+                    v2.x = positions[j + i + 3];
+                    v2.y = positions[j + i + 4];
+                    v2.z = positions[j + i + 5];
+                    v3.x = positions[k + i];
+                    v3.y = positions[k + i + 1];
+                    v3.z = positions[k + i + 2];
+                    v4.x = positions[k + i + 3];
+                    v4.y = positions[k + i + 4];
+                    v4.z = positions[k + i + 5];
+
+                    // 2D slope V1V4
+                    cd = (v4.z - v1.z) / (v4.x - v1.x);
+                    h = v1.z - cd * v1.x;             // v1 belongs to the slope
+                    var slope = new Vector2(cd, h);
 
-            return 0;
+                    // facet equations :
+                    // we compute each facet normal vector
+                    // the equation of the facet plane is : norm.x * x + norm.y * y + norm.z * z + d = 0
+                    // we compute the value d by applying the equation to v1 which belongs to the plane
+                    // then we store the facet equation in a Vector4
+                    v2.subtractToRef(v1, v1v2);
+                    v3.subtractToRef(v1, v1v3);
+                    v4.subtractToRef(v1, v1v4);
+                    Vector3.CrossToRef(v1v4, v1v3, norm1);
+                    Vector3.CrossToRef(v1v2, v1v4, norm2);
+                    norm1.normalize();
+                    norm2.normalize();
+                    d1 = -(norm1.x * v1.x + norm1.y * v1.y + norm1.z * v1.z);
+                    d2 = -(norm2.x * v2.x + norm2.y * v2.y + norm2.z * v2.z);
+                    var facet1 = new BABYLON.Vector4(norm1.x, norm1.y, norm1.z, d1);
+                    var facet2 = new BABYLON.Vector4(norm2.x, norm2.y, norm2.z, d2);
+
+                    var quad = { slope: slope, facet1: facet1, facet2: facet2 };
+                    this._heightQuads.push(quad);
+                }
+            }
         }
     }
-} 
+}
+

+ 31 - 19
src/Mesh/babylon.meshBuilder.js

@@ -239,33 +239,33 @@ var BABYLON;
             var tessellation = options.tessellation || 64;
             var updatable = options.updatable;
             var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE;
+            var cap = options.cap || BABYLON.Mesh.NO_CAP;
             var pi2 = Math.PI * 2;
-            var shapeLathe = new Array();
-            // first rotatable point
+            var paths = new Array();
             var i = 0;
-            while (shape[i].x === 0) {
-                i++;
-            }
-            var pt = shape[i];
-            for (i = 0; i < shape.length; i++) {
-                shapeLathe.push(shape[i].subtract(pt));
-            }
-            // circle path
+            var p = 0;
             var step = pi2 / tessellation * arc;
             var rotated;
             var path = new Array();
             ;
             for (i = 0; i <= tessellation; i++) {
-                rotated = new BABYLON.Vector3(Math.cos(i * step) * radius, 0, Math.sin(i * step) * radius);
-                path.push(rotated);
-            }
-            if (closed) {
-                path.push(path[0]);
+                var path = [];
+                if (cap == BABYLON.Mesh.CAP_START || cap == BABYLON.Mesh.CAP_ALL) {
+                    path.push(new BABYLON.Vector3(0, shape[0].y, 0));
+                    path.push(new BABYLON.Vector3(shape[0].x, shape[0].y, shape[0].x));
+                }
+                for (p = 0; p < shape.length; p++) {
+                    rotated = new BABYLON.Vector3(Math.cos(i * step) * shape[p].x * radius, shape[p].y, Math.sin(i * step) * shape[p].x * radius);
+                    path.push(rotated);
+                }
+                if (cap == BABYLON.Mesh.CAP_END || cap == BABYLON.Mesh.CAP_ALL) {
+                    path.push(new BABYLON.Vector3(Math.cos(i * step) * shape[shape.length - 1].x * radius, shape[shape.length - 1].y, Math.sin(i * step) * shape[shape.length - 1].x * radius));
+                    path.push(new BABYLON.Vector3(0, shape[shape.length - 1].y, 0));
+                }
+                paths.push(path);
             }
-            // extrusion
-            var scaleFunction = function () { return 1; };
-            var rotateFunction = function () { return 0; };
-            var lathe = BABYLON.Mesh.ExtrudeShapeCustom(name, shapeLathe, path, scaleFunction, rotateFunction, closed, false, BABYLON.Mesh.NO_CAP, scene, updatable, sideOrientation);
+            // lathe ribbon
+            var lathe = MeshBuilder.CreateRibbon(name, { pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable }, scene);
             return lathe;
         };
         MeshBuilder.CreatePlane = function (name, options, scene) {
@@ -284,6 +284,12 @@ var BABYLON;
             var ground = new BABYLON.GroundMesh(name, scene);
             ground._setReady(false);
             ground._subdivisions = options.subdivisions || 1;
+            ground._width = options.width || 1;
+            ground._height = options.height || 1;
+            ground._maxX = ground._width / 2;
+            ground._maxZ = ground._height / 2;
+            ground._minX = -ground._maxX;
+            ground._minZ = -ground._maxZ;
             var vertexData = BABYLON.VertexData.CreateGround(options);
             vertexData.applyToMesh(ground, options.updatable);
             ground._setReady(true);
@@ -305,6 +311,12 @@ var BABYLON;
             var onReady = options.onReady;
             var ground = new BABYLON.GroundMesh(name, scene);
             ground._subdivisions = subdivisions;
+            ground._width = width;
+            ground._height = height;
+            ground._maxX = ground._width / 2;
+            ground._maxZ = ground._height / 2;
+            ground._minX = -ground._maxX;
+            ground._minZ = -ground._maxZ;
             ground._setReady(false);
             var onload = function (img) {
                 // Getting height map data

+ 25 - 3
src/Mesh/babylon.meshBuilder.ts

@@ -265,7 +265,7 @@
             return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, instance);
         }
 
-        public static CreateLathe(name: string, options: { shape: Vector3[], radius?: number, tessellation?: number, arc?: number, closed?: boolean, updatable?: boolean, sideOrientation?: number }, scene: Scene): Mesh {
+        public static CreateLathe(name: string, options: { shape: Vector3[], radius?: number, tessellation?: number, arc?: number, closed?: boolean, updatable?: boolean, sideOrientation?: number, cap?: number }, scene: Scene): Mesh {
             var arc: number = (options.arc <= 0 || options.arc > 1) ? 1.0 : options.arc || 1.0;
             var closed: boolean = (options.closed === undefined) ? true : options.closed;
             var shape = options.shape;
@@ -273,6 +273,7 @@
             var tessellation = options.tessellation || 64;
             var updatable = options.updatable;
             var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
+            var cap = options.cap || Mesh.NO_CAP;
             var pi2 = Math.PI * 2;
             var paths = new Array();
 
@@ -283,15 +284,23 @@
             var path = new Array<Vector3>();;
             for (i = 0; i <= tessellation; i++) {
                 var path: Vector3[] = [];
+                if (cap == Mesh.CAP_START || cap == Mesh.CAP_ALL) {
+                    path.push(new Vector3(0, shape[0].y, 0));
+                    path.push(new Vector3(shape[0].x, shape[0].y, shape[0].x));
+                }
                 for (p = 0; p < shape.length; p++) {
-                    rotated = new Vector3(Math.cos(i * step) * shape[p].x * radius, shape[p].y , Math.sin(i * step) * shape[p].x * radius);
+                    rotated = new Vector3(Math.cos(i * step) * shape[p].x * radius, shape[p].y, Math.sin(i * step) * shape[p].x * radius);
                     path.push(rotated);
                 }
+                if (cap == Mesh.CAP_END || cap == Mesh.CAP_ALL) {
+                    path.push(new Vector3(Math.cos(i * step) * shape[shape.length - 1].x * radius, shape[shape.length - 1].y, Math.sin(i * step) * shape[shape.length - 1].x * radius));
+                    path.push(new Vector3(0, shape[shape.length - 1].y, 0));
+                }
                 paths.push(path);
             }
 
             // lathe ribbon
-            var lathe = MeshBuilder.CreateRibbon(name, {pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable}, scene);
+            var lathe = MeshBuilder.CreateRibbon(name, { pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable }, scene);
             return lathe;
         }
 
@@ -318,6 +327,12 @@
             var ground = new GroundMesh(name, scene);
             ground._setReady(false);
             ground._subdivisions = options.subdivisions || 1;
+            ground._width = options.width || 1;
+            ground._height = options.height || 1;
+            ground._maxX = ground._width / 2;
+            ground._maxZ = ground._height / 2;
+            ground._minX = -ground._maxX;
+            ground._minZ = -ground._maxZ;
 
             var vertexData = VertexData.CreateGround(options);
 
@@ -349,6 +364,12 @@
 
             var ground = new GroundMesh(name, scene);
             ground._subdivisions = subdivisions;
+            ground._width = width;
+            ground._height = height;
+            ground._maxX = ground._width / 2;
+            ground._maxZ = ground._height / 2;
+            ground._minX = -ground._maxX;
+            ground._minZ = -ground._maxZ;
 
             ground._setReady(false);
 
@@ -791,3 +812,4 @@
         }
     }
 }
+

+ 1 - 0
src/Mesh/babylon.meshSimplification.js

@@ -475,6 +475,7 @@ var BABYLON;
             this._reconstructedMesh.material = this._mesh.material;
             this._reconstructedMesh.parent = this._mesh.parent;
             this._reconstructedMesh.isVisible = false;
+            this._reconstructedMesh.renderingGroupId = this._mesh.renderingGroupId;
         };
         QuadraticErrorSimplification.prototype.isFlipped = function (vertex1, vertex2, point, deletedArray, borderFactor, delTr) {
             for (var i = 0; i < vertex1.triangleCount; ++i) {

+ 1 - 1
src/Mesh/babylon.meshSimplification.ts

@@ -574,7 +574,7 @@
             this._reconstructedMesh.material = this._mesh.material;
             this._reconstructedMesh.parent = this._mesh.parent;
             this._reconstructedMesh.isVisible = false;
-            this._reconstructedMesh.renderingGroupId = this._mesh.renderingGroupId;
+            this._reconstructedMesh.renderingGroupId = this._mesh.renderingGroupId; 
         }
 
         private isFlipped(vertex1: DecimationVertex, vertex2: DecimationVertex, point: Vector3, deletedArray: Array<boolean>, borderFactor: number, delTr: Array<DecimationTriangle>): boolean {

+ 5 - 5
src/Particles/babylon.particleSystem.js

@@ -212,16 +212,16 @@ var BABYLON;
             this._currentRenderId = this._scene.getRenderId();
             this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
             // determine the number of particles we need to create   
-            var emitCout;
+            var newParticles;
             if (this.manualEmitCount > -1) {
-                emitCout = this.manualEmitCount;
+                newParticles = this.manualEmitCount;
+                this._newPartsExcess = 0;
                 this.manualEmitCount = 0;
             }
             else {
-                emitCout = this.emitRate;
+                newParticles = ((this.emitRate * this._scaledUpdateSpeed) >> 0);
+                this._newPartsExcess += this.emitRate * this._scaledUpdateSpeed - newParticles;
             }
-            var newParticles = ((emitCout * this._scaledUpdateSpeed) >> 0);
-            this._newPartsExcess += emitCout * this._scaledUpdateSpeed - newParticles;
             if (this._newPartsExcess > 1.0) {
                 newParticles += this._newPartsExcess >> 0;
                 this._newPartsExcess -= this._newPartsExcess >> 0;

+ 5 - 6
src/Particles/babylon.particleSystem.ts

@@ -299,18 +299,17 @@
             this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
 
             // determine the number of particles we need to create   
-            var emitCout;
+            var newParticles;
 
             if (this.manualEmitCount > -1) {
-                emitCout = this.manualEmitCount;
+                newParticles = this.manualEmitCount;
+                this._newPartsExcess = 0;
                 this.manualEmitCount = 0;
             } else {
-                emitCout = this.emitRate;
+                newParticles = ((this.emitRate * this._scaledUpdateSpeed) >> 0);
+                this._newPartsExcess += this.emitRate * this._scaledUpdateSpeed - newParticles;
             }
 
-            var newParticles = ((emitCout * this._scaledUpdateSpeed) >> 0);
-            this._newPartsExcess += emitCout * this._scaledUpdateSpeed - newParticles;
-
             if (this._newPartsExcess > 1.0) {
                 newParticles += this._newPartsExcess >> 0;
                 this._newPartsExcess -= this._newPartsExcess >> 0;

+ 15 - 3
src/babylon.scene.js

@@ -20,6 +20,8 @@ var BABYLON;
             this.animationsEnabled = true;
             this.constantlyUpdateMeshUnderPointer = false;
             this.cameraToUseForPointers = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
+            this._startingPointerPosition = new BABYLON.Vector2(0, 0);
+            this._startingPointerTime = 0;
             // Fog
             /**
             * is fog enabled on this scene.
@@ -358,6 +360,9 @@ var BABYLON;
                     return;
                 }
                 _this._updatePointerPosition(evt);
+                _this._startingPointerPosition.x = _this._pointerX;
+                _this._startingPointerPosition.y = _this._pointerY;
+                _this._startingPointerTime = new Date().getTime();
                 var predicate = null;
                 // Meshes
                 if (!_this.onPointerDown) {
@@ -379,7 +384,7 @@ var BABYLON;
                                 pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                 break;
                         }
-                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                        pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                     }
                 }
                 if (_this.onPointerDown) {
@@ -401,7 +406,7 @@ var BABYLON;
                                     pickResult.pickedSprite.actionManager.processTrigger(BABYLON.ActionManager.OnRightPickTrigger, BABYLON.ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, _this, evt));
                                     break;
                             }
-                            pickResult.pickedSprite.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, _this, evt));
+                            pickResult.pickedSprite.actionManager.processTrigger(BABYLON.ActionManager.OnPickDownTrigger, BABYLON.ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, _this, evt));
                         }
                     }
                 }
@@ -414,7 +419,7 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 if (!_this.onPointerUp) {
                     predicate = function (mesh) {
-                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnPickUpTrigger);
+                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && (mesh.actionManager.hasPickTriggers || mesh.actionManager.hasSpecificTrigger(BABYLON.ActionManager.OnLongPressTrigger));
                     };
                 }
                 // Meshes
@@ -422,6 +427,13 @@ var BABYLON;
                 if (pickResult.hit && pickResult.pickedMesh) {
                     if (pickResult.pickedMesh.actionManager) {
                         pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickUpTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                        if (Math.abs(_this._startingPointerPosition.x - _this._pointerX) < BABYLON.ActionManager.DragMovementThreshold && Math.abs(_this._startingPointerPosition.y - _this._pointerY) < BABYLON.ActionManager.DragMovementThreshold) {
+                            pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnPickTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                            if ((new Date().getTime() - _this._startingPointerTime) > BABYLON.ActionManager.LongPressDelay) {
+                                pickResult.pickedMesh.actionManager.processTrigger(BABYLON.ActionManager.OnLongPressTrigger, BABYLON.ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                                ;
+                            }
+                        }
                     }
                 }
                 if (_this.onPointerUp) {

+ 20 - 5
src/babylon.scene.ts

@@ -72,7 +72,8 @@
         private _pointerX: number;
         private _pointerY: number;
         private _meshUnderPointer: AbstractMesh;
-
+        private _startingPointerPosition = new Vector2(0, 0);
+        private _startingPointerTime = 0;
         // Mirror
         public _mirroredCameraPosition: Vector3;
 
@@ -509,6 +510,10 @@
 
                 this._updatePointerPosition(evt);
 
+                this._startingPointerPosition.x = this._pointerX;
+                this._startingPointerPosition.y = this._pointerY;
+                this._startingPointerTime = new Date().getTime();
+
                 var predicate = null;
 
                 // Meshes
@@ -532,7 +537,7 @@
                                 pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                 break;
                         }
-                        pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                        pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                     }
                 }
 
@@ -557,11 +562,12 @@
                                     pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
                                     break;
                             }
-                            pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
+                            pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
                         }
                     }
                 }
             };
+
             this._onPointerUp = (evt: PointerEvent) => {
                 if (!this.cameraToUseForPointers && !this.activeCamera) {
                     return;
@@ -573,7 +579,7 @@
 
                 if (!this.onPointerUp) {
                     predicate = (mesh: AbstractMesh): boolean => {
-                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasSpecificTrigger(ActionManager.OnPickUpTrigger);
+                        return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && (mesh.actionManager.hasPickTriggers || mesh.actionManager.hasSpecificTrigger(ActionManager.OnLongPressTrigger));
                     };
                 }
 
@@ -583,6 +589,15 @@
                 if (pickResult.hit && pickResult.pickedMesh) {
                     if (pickResult.pickedMesh.actionManager) {
                         pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+
+                        if (Math.abs(this._startingPointerPosition.x - this._pointerX) < ActionManager.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < ActionManager.DragMovementThreshold) {
+                            pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+
+                            if ((new Date().getTime() - this._startingPointerTime) > ActionManager.LongPressDelay) {
+                                pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));;
+                            }
+                        }
+
                     }
                 }
 
@@ -2516,4 +2531,4 @@
             return this._getByTags(this.materials, tagsQuery, forEach).concat(this._getByTags(this.multiMaterials, tagsQuery, forEach));
         }
     }
-}
+}