瀏覽代碼

Improved color gradients for particles

David Catuhe 7 年之前
父節點
當前提交
def009e303

文件差異過大導致無法顯示
+ 3614 - 3563
Playground/babylon.d.txt


文件差異過大導致無法顯示
+ 3615 - 3564
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 28 - 28
dist/preview release/babylon.js


+ 216 - 69
dist/preview release/babylon.max.js

@@ -15844,9 +15844,10 @@ var BABYLON;
          * @param createPolynomials if a polynomial sphere should be created for the cube texture
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @param fallback defines texture to use while falling back when (compressed) texture file not found.
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -15854,8 +15855,9 @@ var BABYLON;
             if (createPolynomials === void 0) { createPolynomials = false; }
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
+            if (fallback === void 0) { fallback = null; }
             var gl = this._gl;
-            var texture = new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
+            var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
             texture.url = rootUrl;
             texture.generateMipMaps = !noMipmap;
@@ -15870,8 +15872,7 @@ var BABYLON;
             var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse) {
-                extension = this._textureFormatInUse;
+            if (this._textureFormatInUse && !fallback) {
                 rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
                 isKTX = true;
             }
@@ -15880,6 +15881,11 @@ var BABYLON;
                 isEnv = (extension === ".env");
             }
             var onerror = function (request, exception) {
+                if (isKTX) {
+                    //remove the format appended to the rootUrl in the original createCubeTexture call.
+                    var exp = new RegExp("" + _this._textureFormatInUse + "$");
+                    _this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                }
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
@@ -55184,6 +55190,10 @@ var BABYLON;
              */
             this.cellIndex = 0;
             this._currentFrameCounter = 0;
+            /** @hidden */
+            this._currentColor1 = new BABYLON.Color4(0, 0, 0, 0);
+            /** @hidden */
+            this._currentColor2 = new BABYLON.Color4(0, 0, 0, 0);
             if (!this.particleSystem.isAnimationSheetEnabled) {
                 return;
             }
@@ -55255,6 +55265,11 @@ var BABYLON;
             other.angularSpeed = this.angularSpeed;
             other.particleSystem = this.particleSystem;
             other.cellIndex = this.cellIndex;
+            if (this._currentColorGradient) {
+                other._currentColorGradient = this._currentColorGradient;
+                other._currentColor1.copyFrom(this._currentColor1);
+                other._currentColor2.copyFrom(this._currentColor2);
+            }
         };
         return Particle;
     }());
@@ -55487,6 +55502,7 @@ var BABYLON;
                 if (_this._stockParticles.length !== 0) {
                     particle = _this._stockParticles.pop();
                     particle.age = 0;
+                    particle._currentColorGradient = null;
                     particle.cellIndex = _this.startSpriteCellID;
                 }
                 else {
@@ -55531,12 +55547,13 @@ var BABYLON;
                         var ratio = particle.age / particle.lifeTime;
                         // Color
                         if (_this._colorGradients && _this._colorGradients.length > 0) {
-                            var color1 = BABYLON.Tmp.Color4[0];
-                            var color2 = BABYLON.Tmp.Color4[1];
                             BABYLON.Tools.GetCurrentGradient(ratio, _this._colorGradients, function (currentGradient, nextGradient, scale) {
-                                currentGradient.getColorToRef(color1);
-                                nextGradient.getColorToRef(color2);
-                                BABYLON.Color4.LerpToRef(color1, color2, scale, particle.color);
+                                if (currentGradient !== particle._currentColorGradient) {
+                                    particle._currentColor1.copyFrom(particle._currentColor2);
+                                    nextGradient.getColorToRef(particle._currentColor2);
+                                    particle._currentColorGradient = currentGradient;
+                                }
+                                BABYLON.Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
                             });
                         }
                         else {
@@ -56055,7 +56072,15 @@ var BABYLON;
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 }
                 else {
-                    this._colorGradients[0].getColorToRef(particle.color);
+                    particle._currentColorGradient = this._colorGradients[0];
+                    particle._currentColorGradient.getColorToRef(particle.color);
+                    particle._currentColor1.copyFrom(particle.color);
+                    if (this._colorGradients.length > 1) {
+                        this._colorGradients[1].getColorToRef(particle._currentColor2);
+                    }
+                    else {
+                        particle._currentColor2.copyFrom(particle.color);
+                    }
                 }
             }
         };
@@ -63633,8 +63658,9 @@ var BABYLON;
          * @param generateDepthBuffer True to generate a depth buffer
          * @param generateStencilBuffer True to generate a stencil buffer
          * @param isMulti True if multiple textures need to be created (Draw Buffers)
+         * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
          */
-        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti) {
+        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti, format) {
             if (doNotChangeAspectRatio === void 0) { doNotChangeAspectRatio = true; }
             if (type === void 0) { type = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT; }
             if (isCube === void 0) { isCube = false; }
@@ -63642,6 +63668,7 @@ var BABYLON;
             if (generateDepthBuffer === void 0) { generateDepthBuffer = true; }
             if (generateStencilBuffer === void 0) { generateStencilBuffer = false; }
             if (isMulti === void 0) { isMulti = false; }
+            if (format === void 0) { format = BABYLON.Engine.TEXTUREFORMAT_RGBA; }
             var _this = _super.call(this, null, scene, !generateMipMaps) || this;
             _this.isCube = isCube;
             /**
@@ -63702,6 +63729,7 @@ var BABYLON;
             _this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 type: type,
+                format: format,
                 samplingMode: samplingMode,
                 generateDepthBuffer: generateDepthBuffer,
                 generateStencilBuffer: generateStencilBuffer
@@ -76989,6 +77017,23 @@ var BABYLON;
             }
         };
         /**
+         * Adds a camera to the pipeline
+         * @param camera the camera to be added
+         */
+        DefaultRenderingPipeline.prototype.addCamera = function (camera) {
+            this._originalCameras.push(camera);
+            this._buildPipeline();
+        };
+        /**
+         * Removes a camera from the pipeline
+         * @param camera the camera to remove
+         */
+        DefaultRenderingPipeline.prototype.removeCamera = function (camera) {
+            var index = this._originalCameras.indexOf(camera);
+            this._originalCameras.splice(index, 1);
+            this._buildPipeline();
+        };
+        /**
          * Dispose of the pipeline and stop all post processes
          */
         DefaultRenderingPipeline.prototype.dispose = function () {
@@ -87747,11 +87792,12 @@ var BABYLON;
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             for (var level = 0; level < mipmapCount; level++) {
                 var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
+                dataOffset += 4; //image data starts from next multiple of 4 offset. Each face refers to same imagesize field above.
                 for (var face = 0; face < this.numberOfFaces; face++) {
                     var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset + 4, imageSize);
+                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset, imageSize);
                     gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
-                    dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+                    dataOffset += imageSize; // add size of the image for the next face/mipmap
                     dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
                 }
                 width = Math.max(1.0, width * 0.5);
@@ -89326,23 +89372,26 @@ var BABYLON;
                     }
                 }
             });
+            this.attachedMesh = null;
         }
-        Gizmo.prototype._onInteractionsEnabledChanged = function (value) {
-        };
-        Object.defineProperty(Gizmo.prototype, "interactionsEnabled", {
-            get: function () {
-                return this._interactionsEnabled;
-            },
+        Object.defineProperty(Gizmo.prototype, "attachedMesh", {
             /**
-             * If interactions are enabled with this gizmo. (eg. dragging/rotation)
+             * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
+             * * When set, interactions will be enabled
              */
+            get: function () {
+                return this._attachedMesh;
+            },
             set: function (value) {
-                this._interactionsEnabled = value;
-                this._onInteractionsEnabledChanged(value);
+                this._attachedMesh = value;
+                this._rootMesh.setEnabled(value ? true : false);
+                this._attachedMeshChanged(value);
             },
             enumerable: true,
             configurable: true
         });
+        Gizmo.prototype._attachedMeshChanged = function (value) {
+        };
         /**
          * Disposes of the gizmo
          */
@@ -89376,6 +89425,15 @@ var BABYLON;
         function AxisDragGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89399,16 +89457,31 @@ var BABYLON;
             arrowTail.position.z += 0.15;
             arrow.lookAt(_this._rootMesh.position.subtract(dragAxis));
             _this._rootMesh.addChild(arrow);
+            var currentSnapDragDistance = 0;
+            var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             // Add drag behavior to handle events when the gizmo is dragged
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    _this.attachedMesh.position.addInPlace(event.delta);
+                    // Snapping logic
+                    if (_this.snapDistance == 0) {
+                        _this.attachedMesh.position.addInPlace(event.delta);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            event.delta.normalizeToRef(tmpVector);
+                            tmpVector.scaleInPlace(_this.snapDistance * dragSteps);
+                            _this.attachedMesh.position.addInPlace(tmpVector);
+                            tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                            _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                        }
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89425,13 +89498,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisDragGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisDragGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisDragGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89460,6 +89536,15 @@ var BABYLON;
         function AxisScaleGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89487,13 +89572,29 @@ var BABYLON;
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
+            var currentSnapDragDistance = 0;
             var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    // Snapping logic
+                    var snapped = false;
+                    var dragSteps = 0;
+                    if (_this.snapDistance == 0) {
+                        dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            dragAxis.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
+                            snapped = true;
+                        }
+                        else {
+                            tmpVector.scaleInPlace(0);
+                        }
+                    }
                     var invertCount = 0;
                     if (_this.attachedMesh.scaling["x"] < 0) {
                         invertCount++;
@@ -89510,6 +89611,10 @@ var BABYLON;
                     else {
                         _this.attachedMesh.scaling.subtractInPlace(tmpVector);
                     }
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89526,13 +89631,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisScaleGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisScaleGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89561,6 +89669,15 @@ var BABYLON;
         function PlaneRotationGizmo(gizmoLayer, planeNormal, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Rotation distance in radians that the gizmo will snap to (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89584,18 +89701,16 @@ var BABYLON;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
             _this._dragBehavior.onDragStartObservable.add(function (e) {
-                if (!_this.interactionsEnabled) {
-                    return;
+                if (_this.attachedMesh) {
+                    lastDragPosition = e.dragPlanePoint;
                 }
-                lastDragPosition = e.dragPlanePoint;
             });
             var rotationMatrix = new BABYLON.Matrix();
             var planeNormalTowardsCamera = new BABYLON.Vector3();
             var localPlaneNormalTowardsCamera = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
+            var currentSnapDragDistance = 0;
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh && lastDragPosition) {
                     if (!_this.attachedMesh.rotationQuaternion) {
                         _this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -89623,12 +89738,30 @@ var BABYLON;
                     var halfCircleSide = BABYLON.Vector3.Dot(localPlaneNormalTowardsCamera, cross) > 0.0;
                     if (halfCircleSide)
                         angle = -angle;
+                    // Snapping logic
+                    var snapped = false;
+                    if (_this.snapDistance != 0) {
+                        currentSnapDragDistance += angle;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            angle = _this.snapDistance * dragSteps;
+                            snapped = true;
+                        }
+                        else {
+                            angle = 0;
+                        }
+                    }
                     // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
                     var quaternionCoefficient = Math.sin(angle / 2);
                     var amountToRotate = new BABYLON.Quaternion(planeNormalTowardsCamera.x * quaternionCoefficient, planeNormalTowardsCamera.y * quaternionCoefficient, planeNormalTowardsCamera.z * quaternionCoefficient, Math.cos(angle / 2));
                     // Rotate selected mesh quaternion over fixed axis
                     _this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate, _this.attachedMesh.rotationQuaternion);
                     lastDragPosition = event.dragPlanePoint;
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = angle;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89645,13 +89778,16 @@ var BABYLON;
             });
             return _this;
         }
-        PlaneRotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        PlaneRotationGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         PlaneRotationGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89672,30 +89808,28 @@ var BABYLON;
     var PositionGizmo = /** @class */ (function (_super) {
         __extends(PositionGizmo, _super);
         /**
-         * Creates a PositionGizmo
-         * @param gizmoLayer The utility layer the gizmo will be added to
-         */
+             * Creates a PositionGizmo
+             * @param gizmoLayer The utility layer the gizmo will be added to
+             */
         function PositionGizmo(gizmoLayer) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._xDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(PositionGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        PositionGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(PositionGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89742,22 +89876,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(RotationGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        RotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(RotationGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89804,22 +89936,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(ScaleGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        ScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(ScaleGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -90086,6 +90216,23 @@ var BABYLON;
             }
         };
         /**
+         * Enables rotation on the specified axis and disables rotation on the others
+         * @param axis The list of axis that should be enabled (eg. "xy" or "xyz")
+         */
+        BoundingBoxGizmo.prototype.setEnabledRotationAxis = function (axis) {
+            this._rotateSpheresParent.getChildMeshes().forEach(function (m, i) {
+                if (i < 4) {
+                    m.setEnabled(axis.indexOf("x") != -1);
+                }
+                else if (i < 8) {
+                    m.setEnabled(axis.indexOf("y") != -1);
+                }
+                else {
+                    m.setEnabled(axis.indexOf("z") != -1);
+                }
+            });
+        };
+        /**
          * Disposes of the gizmo
          */
         BoundingBoxGizmo.prototype.dispose = function () {

+ 216 - 69
dist/preview release/babylon.no-module.max.js

@@ -15811,9 +15811,10 @@ var BABYLON;
          * @param createPolynomials if a polynomial sphere should be created for the cube texture
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @param fallback defines texture to use while falling back when (compressed) texture file not found.
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -15821,8 +15822,9 @@ var BABYLON;
             if (createPolynomials === void 0) { createPolynomials = false; }
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
+            if (fallback === void 0) { fallback = null; }
             var gl = this._gl;
-            var texture = new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
+            var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
             texture.url = rootUrl;
             texture.generateMipMaps = !noMipmap;
@@ -15837,8 +15839,7 @@ var BABYLON;
             var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse) {
-                extension = this._textureFormatInUse;
+            if (this._textureFormatInUse && !fallback) {
                 rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
                 isKTX = true;
             }
@@ -15847,6 +15848,11 @@ var BABYLON;
                 isEnv = (extension === ".env");
             }
             var onerror = function (request, exception) {
+                if (isKTX) {
+                    //remove the format appended to the rootUrl in the original createCubeTexture call.
+                    var exp = new RegExp("" + _this._textureFormatInUse + "$");
+                    _this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                }
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
@@ -55151,6 +55157,10 @@ var BABYLON;
              */
             this.cellIndex = 0;
             this._currentFrameCounter = 0;
+            /** @hidden */
+            this._currentColor1 = new BABYLON.Color4(0, 0, 0, 0);
+            /** @hidden */
+            this._currentColor2 = new BABYLON.Color4(0, 0, 0, 0);
             if (!this.particleSystem.isAnimationSheetEnabled) {
                 return;
             }
@@ -55222,6 +55232,11 @@ var BABYLON;
             other.angularSpeed = this.angularSpeed;
             other.particleSystem = this.particleSystem;
             other.cellIndex = this.cellIndex;
+            if (this._currentColorGradient) {
+                other._currentColorGradient = this._currentColorGradient;
+                other._currentColor1.copyFrom(this._currentColor1);
+                other._currentColor2.copyFrom(this._currentColor2);
+            }
         };
         return Particle;
     }());
@@ -55454,6 +55469,7 @@ var BABYLON;
                 if (_this._stockParticles.length !== 0) {
                     particle = _this._stockParticles.pop();
                     particle.age = 0;
+                    particle._currentColorGradient = null;
                     particle.cellIndex = _this.startSpriteCellID;
                 }
                 else {
@@ -55498,12 +55514,13 @@ var BABYLON;
                         var ratio = particle.age / particle.lifeTime;
                         // Color
                         if (_this._colorGradients && _this._colorGradients.length > 0) {
-                            var color1 = BABYLON.Tmp.Color4[0];
-                            var color2 = BABYLON.Tmp.Color4[1];
                             BABYLON.Tools.GetCurrentGradient(ratio, _this._colorGradients, function (currentGradient, nextGradient, scale) {
-                                currentGradient.getColorToRef(color1);
-                                nextGradient.getColorToRef(color2);
-                                BABYLON.Color4.LerpToRef(color1, color2, scale, particle.color);
+                                if (currentGradient !== particle._currentColorGradient) {
+                                    particle._currentColor1.copyFrom(particle._currentColor2);
+                                    nextGradient.getColorToRef(particle._currentColor2);
+                                    particle._currentColorGradient = currentGradient;
+                                }
+                                BABYLON.Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
                             });
                         }
                         else {
@@ -56022,7 +56039,15 @@ var BABYLON;
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 }
                 else {
-                    this._colorGradients[0].getColorToRef(particle.color);
+                    particle._currentColorGradient = this._colorGradients[0];
+                    particle._currentColorGradient.getColorToRef(particle.color);
+                    particle._currentColor1.copyFrom(particle.color);
+                    if (this._colorGradients.length > 1) {
+                        this._colorGradients[1].getColorToRef(particle._currentColor2);
+                    }
+                    else {
+                        particle._currentColor2.copyFrom(particle.color);
+                    }
                 }
             }
         };
@@ -63600,8 +63625,9 @@ var BABYLON;
          * @param generateDepthBuffer True to generate a depth buffer
          * @param generateStencilBuffer True to generate a stencil buffer
          * @param isMulti True if multiple textures need to be created (Draw Buffers)
+         * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
          */
-        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti) {
+        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti, format) {
             if (doNotChangeAspectRatio === void 0) { doNotChangeAspectRatio = true; }
             if (type === void 0) { type = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT; }
             if (isCube === void 0) { isCube = false; }
@@ -63609,6 +63635,7 @@ var BABYLON;
             if (generateDepthBuffer === void 0) { generateDepthBuffer = true; }
             if (generateStencilBuffer === void 0) { generateStencilBuffer = false; }
             if (isMulti === void 0) { isMulti = false; }
+            if (format === void 0) { format = BABYLON.Engine.TEXTUREFORMAT_RGBA; }
             var _this = _super.call(this, null, scene, !generateMipMaps) || this;
             _this.isCube = isCube;
             /**
@@ -63669,6 +63696,7 @@ var BABYLON;
             _this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 type: type,
+                format: format,
                 samplingMode: samplingMode,
                 generateDepthBuffer: generateDepthBuffer,
                 generateStencilBuffer: generateStencilBuffer
@@ -76956,6 +76984,23 @@ var BABYLON;
             }
         };
         /**
+         * Adds a camera to the pipeline
+         * @param camera the camera to be added
+         */
+        DefaultRenderingPipeline.prototype.addCamera = function (camera) {
+            this._originalCameras.push(camera);
+            this._buildPipeline();
+        };
+        /**
+         * Removes a camera from the pipeline
+         * @param camera the camera to remove
+         */
+        DefaultRenderingPipeline.prototype.removeCamera = function (camera) {
+            var index = this._originalCameras.indexOf(camera);
+            this._originalCameras.splice(index, 1);
+            this._buildPipeline();
+        };
+        /**
          * Dispose of the pipeline and stop all post processes
          */
         DefaultRenderingPipeline.prototype.dispose = function () {
@@ -87714,11 +87759,12 @@ var BABYLON;
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             for (var level = 0; level < mipmapCount; level++) {
                 var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
+                dataOffset += 4; //image data starts from next multiple of 4 offset. Each face refers to same imagesize field above.
                 for (var face = 0; face < this.numberOfFaces; face++) {
                     var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset + 4, imageSize);
+                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset, imageSize);
                     gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
-                    dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+                    dataOffset += imageSize; // add size of the image for the next face/mipmap
                     dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
                 }
                 width = Math.max(1.0, width * 0.5);
@@ -89293,23 +89339,26 @@ var BABYLON;
                     }
                 }
             });
+            this.attachedMesh = null;
         }
-        Gizmo.prototype._onInteractionsEnabledChanged = function (value) {
-        };
-        Object.defineProperty(Gizmo.prototype, "interactionsEnabled", {
-            get: function () {
-                return this._interactionsEnabled;
-            },
+        Object.defineProperty(Gizmo.prototype, "attachedMesh", {
             /**
-             * If interactions are enabled with this gizmo. (eg. dragging/rotation)
+             * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
+             * * When set, interactions will be enabled
              */
+            get: function () {
+                return this._attachedMesh;
+            },
             set: function (value) {
-                this._interactionsEnabled = value;
-                this._onInteractionsEnabledChanged(value);
+                this._attachedMesh = value;
+                this._rootMesh.setEnabled(value ? true : false);
+                this._attachedMeshChanged(value);
             },
             enumerable: true,
             configurable: true
         });
+        Gizmo.prototype._attachedMeshChanged = function (value) {
+        };
         /**
          * Disposes of the gizmo
          */
@@ -89343,6 +89392,15 @@ var BABYLON;
         function AxisDragGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89366,16 +89424,31 @@ var BABYLON;
             arrowTail.position.z += 0.15;
             arrow.lookAt(_this._rootMesh.position.subtract(dragAxis));
             _this._rootMesh.addChild(arrow);
+            var currentSnapDragDistance = 0;
+            var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             // Add drag behavior to handle events when the gizmo is dragged
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    _this.attachedMesh.position.addInPlace(event.delta);
+                    // Snapping logic
+                    if (_this.snapDistance == 0) {
+                        _this.attachedMesh.position.addInPlace(event.delta);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            event.delta.normalizeToRef(tmpVector);
+                            tmpVector.scaleInPlace(_this.snapDistance * dragSteps);
+                            _this.attachedMesh.position.addInPlace(tmpVector);
+                            tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                            _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                        }
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89392,13 +89465,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisDragGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisDragGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisDragGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89427,6 +89503,15 @@ var BABYLON;
         function AxisScaleGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89454,13 +89539,29 @@ var BABYLON;
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
+            var currentSnapDragDistance = 0;
             var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    // Snapping logic
+                    var snapped = false;
+                    var dragSteps = 0;
+                    if (_this.snapDistance == 0) {
+                        dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            dragAxis.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
+                            snapped = true;
+                        }
+                        else {
+                            tmpVector.scaleInPlace(0);
+                        }
+                    }
                     var invertCount = 0;
                     if (_this.attachedMesh.scaling["x"] < 0) {
                         invertCount++;
@@ -89477,6 +89578,10 @@ var BABYLON;
                     else {
                         _this.attachedMesh.scaling.subtractInPlace(tmpVector);
                     }
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89493,13 +89598,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisScaleGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisScaleGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89528,6 +89636,15 @@ var BABYLON;
         function PlaneRotationGizmo(gizmoLayer, planeNormal, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Rotation distance in radians that the gizmo will snap to (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89551,18 +89668,16 @@ var BABYLON;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
             _this._dragBehavior.onDragStartObservable.add(function (e) {
-                if (!_this.interactionsEnabled) {
-                    return;
+                if (_this.attachedMesh) {
+                    lastDragPosition = e.dragPlanePoint;
                 }
-                lastDragPosition = e.dragPlanePoint;
             });
             var rotationMatrix = new BABYLON.Matrix();
             var planeNormalTowardsCamera = new BABYLON.Vector3();
             var localPlaneNormalTowardsCamera = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
+            var currentSnapDragDistance = 0;
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh && lastDragPosition) {
                     if (!_this.attachedMesh.rotationQuaternion) {
                         _this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -89590,12 +89705,30 @@ var BABYLON;
                     var halfCircleSide = BABYLON.Vector3.Dot(localPlaneNormalTowardsCamera, cross) > 0.0;
                     if (halfCircleSide)
                         angle = -angle;
+                    // Snapping logic
+                    var snapped = false;
+                    if (_this.snapDistance != 0) {
+                        currentSnapDragDistance += angle;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            angle = _this.snapDistance * dragSteps;
+                            snapped = true;
+                        }
+                        else {
+                            angle = 0;
+                        }
+                    }
                     // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
                     var quaternionCoefficient = Math.sin(angle / 2);
                     var amountToRotate = new BABYLON.Quaternion(planeNormalTowardsCamera.x * quaternionCoefficient, planeNormalTowardsCamera.y * quaternionCoefficient, planeNormalTowardsCamera.z * quaternionCoefficient, Math.cos(angle / 2));
                     // Rotate selected mesh quaternion over fixed axis
                     _this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate, _this.attachedMesh.rotationQuaternion);
                     lastDragPosition = event.dragPlanePoint;
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = angle;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89612,13 +89745,16 @@ var BABYLON;
             });
             return _this;
         }
-        PlaneRotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        PlaneRotationGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         PlaneRotationGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89639,30 +89775,28 @@ var BABYLON;
     var PositionGizmo = /** @class */ (function (_super) {
         __extends(PositionGizmo, _super);
         /**
-         * Creates a PositionGizmo
-         * @param gizmoLayer The utility layer the gizmo will be added to
-         */
+             * Creates a PositionGizmo
+             * @param gizmoLayer The utility layer the gizmo will be added to
+             */
         function PositionGizmo(gizmoLayer) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._xDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(PositionGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        PositionGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(PositionGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89709,22 +89843,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(RotationGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        RotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(RotationGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89771,22 +89903,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(ScaleGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        ScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(ScaleGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -90053,6 +90183,23 @@ var BABYLON;
             }
         };
         /**
+         * Enables rotation on the specified axis and disables rotation on the others
+         * @param axis The list of axis that should be enabled (eg. "xy" or "xyz")
+         */
+        BoundingBoxGizmo.prototype.setEnabledRotationAxis = function (axis) {
+            this._rotateSpheresParent.getChildMeshes().forEach(function (m, i) {
+                if (i < 4) {
+                    m.setEnabled(axis.indexOf("x") != -1);
+                }
+                else if (i < 8) {
+                    m.setEnabled(axis.indexOf("y") != -1);
+                }
+                else {
+                    m.setEnabled(axis.indexOf("z") != -1);
+                }
+            });
+        };
+        /**
          * Disposes of the gizmo
          */
         BoundingBoxGizmo.prototype.dispose = function () {

文件差異過大導致無法顯示
+ 28 - 28
dist/preview release/babylon.worker.js


+ 216 - 69
dist/preview release/es6.js

@@ -15811,9 +15811,10 @@ var BABYLON;
          * @param createPolynomials if a polynomial sphere should be created for the cube texture
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @param fallback defines texture to use while falling back when (compressed) texture file not found.
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -15821,8 +15822,9 @@ var BABYLON;
             if (createPolynomials === void 0) { createPolynomials = false; }
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
+            if (fallback === void 0) { fallback = null; }
             var gl = this._gl;
-            var texture = new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
+            var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
             texture.url = rootUrl;
             texture.generateMipMaps = !noMipmap;
@@ -15837,8 +15839,7 @@ var BABYLON;
             var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse) {
-                extension = this._textureFormatInUse;
+            if (this._textureFormatInUse && !fallback) {
                 rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
                 isKTX = true;
             }
@@ -15847,6 +15848,11 @@ var BABYLON;
                 isEnv = (extension === ".env");
             }
             var onerror = function (request, exception) {
+                if (isKTX) {
+                    //remove the format appended to the rootUrl in the original createCubeTexture call.
+                    var exp = new RegExp("" + _this._textureFormatInUse + "$");
+                    _this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                }
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
@@ -55151,6 +55157,10 @@ var BABYLON;
              */
             this.cellIndex = 0;
             this._currentFrameCounter = 0;
+            /** @hidden */
+            this._currentColor1 = new BABYLON.Color4(0, 0, 0, 0);
+            /** @hidden */
+            this._currentColor2 = new BABYLON.Color4(0, 0, 0, 0);
             if (!this.particleSystem.isAnimationSheetEnabled) {
                 return;
             }
@@ -55222,6 +55232,11 @@ var BABYLON;
             other.angularSpeed = this.angularSpeed;
             other.particleSystem = this.particleSystem;
             other.cellIndex = this.cellIndex;
+            if (this._currentColorGradient) {
+                other._currentColorGradient = this._currentColorGradient;
+                other._currentColor1.copyFrom(this._currentColor1);
+                other._currentColor2.copyFrom(this._currentColor2);
+            }
         };
         return Particle;
     }());
@@ -55454,6 +55469,7 @@ var BABYLON;
                 if (_this._stockParticles.length !== 0) {
                     particle = _this._stockParticles.pop();
                     particle.age = 0;
+                    particle._currentColorGradient = null;
                     particle.cellIndex = _this.startSpriteCellID;
                 }
                 else {
@@ -55498,12 +55514,13 @@ var BABYLON;
                         var ratio = particle.age / particle.lifeTime;
                         // Color
                         if (_this._colorGradients && _this._colorGradients.length > 0) {
-                            var color1 = BABYLON.Tmp.Color4[0];
-                            var color2 = BABYLON.Tmp.Color4[1];
                             BABYLON.Tools.GetCurrentGradient(ratio, _this._colorGradients, function (currentGradient, nextGradient, scale) {
-                                currentGradient.getColorToRef(color1);
-                                nextGradient.getColorToRef(color2);
-                                BABYLON.Color4.LerpToRef(color1, color2, scale, particle.color);
+                                if (currentGradient !== particle._currentColorGradient) {
+                                    particle._currentColor1.copyFrom(particle._currentColor2);
+                                    nextGradient.getColorToRef(particle._currentColor2);
+                                    particle._currentColorGradient = currentGradient;
+                                }
+                                BABYLON.Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
                             });
                         }
                         else {
@@ -56022,7 +56039,15 @@ var BABYLON;
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 }
                 else {
-                    this._colorGradients[0].getColorToRef(particle.color);
+                    particle._currentColorGradient = this._colorGradients[0];
+                    particle._currentColorGradient.getColorToRef(particle.color);
+                    particle._currentColor1.copyFrom(particle.color);
+                    if (this._colorGradients.length > 1) {
+                        this._colorGradients[1].getColorToRef(particle._currentColor2);
+                    }
+                    else {
+                        particle._currentColor2.copyFrom(particle.color);
+                    }
                 }
             }
         };
@@ -63600,8 +63625,9 @@ var BABYLON;
          * @param generateDepthBuffer True to generate a depth buffer
          * @param generateStencilBuffer True to generate a stencil buffer
          * @param isMulti True if multiple textures need to be created (Draw Buffers)
+         * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
          */
-        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti) {
+        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti, format) {
             if (doNotChangeAspectRatio === void 0) { doNotChangeAspectRatio = true; }
             if (type === void 0) { type = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT; }
             if (isCube === void 0) { isCube = false; }
@@ -63609,6 +63635,7 @@ var BABYLON;
             if (generateDepthBuffer === void 0) { generateDepthBuffer = true; }
             if (generateStencilBuffer === void 0) { generateStencilBuffer = false; }
             if (isMulti === void 0) { isMulti = false; }
+            if (format === void 0) { format = BABYLON.Engine.TEXTUREFORMAT_RGBA; }
             var _this = _super.call(this, null, scene, !generateMipMaps) || this;
             _this.isCube = isCube;
             /**
@@ -63669,6 +63696,7 @@ var BABYLON;
             _this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 type: type,
+                format: format,
                 samplingMode: samplingMode,
                 generateDepthBuffer: generateDepthBuffer,
                 generateStencilBuffer: generateStencilBuffer
@@ -76956,6 +76984,23 @@ var BABYLON;
             }
         };
         /**
+         * Adds a camera to the pipeline
+         * @param camera the camera to be added
+         */
+        DefaultRenderingPipeline.prototype.addCamera = function (camera) {
+            this._originalCameras.push(camera);
+            this._buildPipeline();
+        };
+        /**
+         * Removes a camera from the pipeline
+         * @param camera the camera to remove
+         */
+        DefaultRenderingPipeline.prototype.removeCamera = function (camera) {
+            var index = this._originalCameras.indexOf(camera);
+            this._originalCameras.splice(index, 1);
+            this._buildPipeline();
+        };
+        /**
          * Dispose of the pipeline and stop all post processes
          */
         DefaultRenderingPipeline.prototype.dispose = function () {
@@ -87714,11 +87759,12 @@ var BABYLON;
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             for (var level = 0; level < mipmapCount; level++) {
                 var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
+                dataOffset += 4; //image data starts from next multiple of 4 offset. Each face refers to same imagesize field above.
                 for (var face = 0; face < this.numberOfFaces; face++) {
                     var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset + 4, imageSize);
+                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset, imageSize);
                     gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
-                    dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+                    dataOffset += imageSize; // add size of the image for the next face/mipmap
                     dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
                 }
                 width = Math.max(1.0, width * 0.5);
@@ -89293,23 +89339,26 @@ var BABYLON;
                     }
                 }
             });
+            this.attachedMesh = null;
         }
-        Gizmo.prototype._onInteractionsEnabledChanged = function (value) {
-        };
-        Object.defineProperty(Gizmo.prototype, "interactionsEnabled", {
-            get: function () {
-                return this._interactionsEnabled;
-            },
+        Object.defineProperty(Gizmo.prototype, "attachedMesh", {
             /**
-             * If interactions are enabled with this gizmo. (eg. dragging/rotation)
+             * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
+             * * When set, interactions will be enabled
              */
+            get: function () {
+                return this._attachedMesh;
+            },
             set: function (value) {
-                this._interactionsEnabled = value;
-                this._onInteractionsEnabledChanged(value);
+                this._attachedMesh = value;
+                this._rootMesh.setEnabled(value ? true : false);
+                this._attachedMeshChanged(value);
             },
             enumerable: true,
             configurable: true
         });
+        Gizmo.prototype._attachedMeshChanged = function (value) {
+        };
         /**
          * Disposes of the gizmo
          */
@@ -89343,6 +89392,15 @@ var BABYLON;
         function AxisDragGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89366,16 +89424,31 @@ var BABYLON;
             arrowTail.position.z += 0.15;
             arrow.lookAt(_this._rootMesh.position.subtract(dragAxis));
             _this._rootMesh.addChild(arrow);
+            var currentSnapDragDistance = 0;
+            var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             // Add drag behavior to handle events when the gizmo is dragged
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    _this.attachedMesh.position.addInPlace(event.delta);
+                    // Snapping logic
+                    if (_this.snapDistance == 0) {
+                        _this.attachedMesh.position.addInPlace(event.delta);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            event.delta.normalizeToRef(tmpVector);
+                            tmpVector.scaleInPlace(_this.snapDistance * dragSteps);
+                            _this.attachedMesh.position.addInPlace(tmpVector);
+                            tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                            _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                        }
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89392,13 +89465,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisDragGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisDragGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisDragGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89427,6 +89503,15 @@ var BABYLON;
         function AxisScaleGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89454,13 +89539,29 @@ var BABYLON;
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
+            var currentSnapDragDistance = 0;
             var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    // Snapping logic
+                    var snapped = false;
+                    var dragSteps = 0;
+                    if (_this.snapDistance == 0) {
+                        dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            dragAxis.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
+                            snapped = true;
+                        }
+                        else {
+                            tmpVector.scaleInPlace(0);
+                        }
+                    }
                     var invertCount = 0;
                     if (_this.attachedMesh.scaling["x"] < 0) {
                         invertCount++;
@@ -89477,6 +89578,10 @@ var BABYLON;
                     else {
                         _this.attachedMesh.scaling.subtractInPlace(tmpVector);
                     }
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89493,13 +89598,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisScaleGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisScaleGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89528,6 +89636,15 @@ var BABYLON;
         function PlaneRotationGizmo(gizmoLayer, planeNormal, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Rotation distance in radians that the gizmo will snap to (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89551,18 +89668,16 @@ var BABYLON;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
             _this._dragBehavior.onDragStartObservable.add(function (e) {
-                if (!_this.interactionsEnabled) {
-                    return;
+                if (_this.attachedMesh) {
+                    lastDragPosition = e.dragPlanePoint;
                 }
-                lastDragPosition = e.dragPlanePoint;
             });
             var rotationMatrix = new BABYLON.Matrix();
             var planeNormalTowardsCamera = new BABYLON.Vector3();
             var localPlaneNormalTowardsCamera = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
+            var currentSnapDragDistance = 0;
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh && lastDragPosition) {
                     if (!_this.attachedMesh.rotationQuaternion) {
                         _this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -89590,12 +89705,30 @@ var BABYLON;
                     var halfCircleSide = BABYLON.Vector3.Dot(localPlaneNormalTowardsCamera, cross) > 0.0;
                     if (halfCircleSide)
                         angle = -angle;
+                    // Snapping logic
+                    var snapped = false;
+                    if (_this.snapDistance != 0) {
+                        currentSnapDragDistance += angle;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            angle = _this.snapDistance * dragSteps;
+                            snapped = true;
+                        }
+                        else {
+                            angle = 0;
+                        }
+                    }
                     // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
                     var quaternionCoefficient = Math.sin(angle / 2);
                     var amountToRotate = new BABYLON.Quaternion(planeNormalTowardsCamera.x * quaternionCoefficient, planeNormalTowardsCamera.y * quaternionCoefficient, planeNormalTowardsCamera.z * quaternionCoefficient, Math.cos(angle / 2));
                     // Rotate selected mesh quaternion over fixed axis
                     _this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate, _this.attachedMesh.rotationQuaternion);
                     lastDragPosition = event.dragPlanePoint;
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = angle;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89612,13 +89745,16 @@ var BABYLON;
             });
             return _this;
         }
-        PlaneRotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        PlaneRotationGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         PlaneRotationGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89639,30 +89775,28 @@ var BABYLON;
     var PositionGizmo = /** @class */ (function (_super) {
         __extends(PositionGizmo, _super);
         /**
-         * Creates a PositionGizmo
-         * @param gizmoLayer The utility layer the gizmo will be added to
-         */
+             * Creates a PositionGizmo
+             * @param gizmoLayer The utility layer the gizmo will be added to
+             */
         function PositionGizmo(gizmoLayer) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._xDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(PositionGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        PositionGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(PositionGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89709,22 +89843,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(RotationGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        RotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(RotationGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89771,22 +89903,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(ScaleGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        ScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(ScaleGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -90053,6 +90183,23 @@ var BABYLON;
             }
         };
         /**
+         * Enables rotation on the specified axis and disables rotation on the others
+         * @param axis The list of axis that should be enabled (eg. "xy" or "xyz")
+         */
+        BoundingBoxGizmo.prototype.setEnabledRotationAxis = function (axis) {
+            this._rotateSpheresParent.getChildMeshes().forEach(function (m, i) {
+                if (i < 4) {
+                    m.setEnabled(axis.indexOf("x") != -1);
+                }
+                else if (i < 8) {
+                    m.setEnabled(axis.indexOf("y") != -1);
+                }
+                else {
+                    m.setEnabled(axis.indexOf("z") != -1);
+                }
+            });
+        };
+        /**
          * Disposes of the gizmo
          */
         BoundingBoxGizmo.prototype.dispose = function () {

文件差異過大導致無法顯示
+ 39 - 39
dist/preview release/viewer/babylon.viewer.js


+ 216 - 69
dist/preview release/viewer/babylon.viewer.max.js

@@ -15932,9 +15932,10 @@ var BABYLON;
          * @param createPolynomials if a polynomial sphere should be created for the cube texture
          * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
          * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @param fallback defines texture to use while falling back when (compressed) texture file not found.
          * @returns the cube texture as an InternalTexture
          */
-        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset) {
+        Engine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback) {
             var _this = this;
             if (onLoad === void 0) { onLoad = null; }
             if (onError === void 0) { onError = null; }
@@ -15942,8 +15943,9 @@ var BABYLON;
             if (createPolynomials === void 0) { createPolynomials = false; }
             if (lodScale === void 0) { lodScale = 0; }
             if (lodOffset === void 0) { lodOffset = 0; }
+            if (fallback === void 0) { fallback = null; }
             var gl = this._gl;
-            var texture = new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
+            var texture = fallback ? fallback : new BABYLON.InternalTexture(this, BABYLON.InternalTexture.DATASOURCE_CUBE);
             texture.isCube = true;
             texture.url = rootUrl;
             texture.generateMipMaps = !noMipmap;
@@ -15958,8 +15960,7 @@ var BABYLON;
             var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse) {
-                extension = this._textureFormatInUse;
+            if (this._textureFormatInUse && !fallback) {
                 rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
                 isKTX = true;
             }
@@ -15968,6 +15969,11 @@ var BABYLON;
                 isEnv = (extension === ".env");
             }
             var onerror = function (request, exception) {
+                if (isKTX) {
+                    //remove the format appended to the rootUrl in the original createCubeTexture call.
+                    var exp = new RegExp("" + _this._textureFormatInUse + "$");
+                    _this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                }
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
@@ -55272,6 +55278,10 @@ var BABYLON;
              */
             this.cellIndex = 0;
             this._currentFrameCounter = 0;
+            /** @hidden */
+            this._currentColor1 = new BABYLON.Color4(0, 0, 0, 0);
+            /** @hidden */
+            this._currentColor2 = new BABYLON.Color4(0, 0, 0, 0);
             if (!this.particleSystem.isAnimationSheetEnabled) {
                 return;
             }
@@ -55343,6 +55353,11 @@ var BABYLON;
             other.angularSpeed = this.angularSpeed;
             other.particleSystem = this.particleSystem;
             other.cellIndex = this.cellIndex;
+            if (this._currentColorGradient) {
+                other._currentColorGradient = this._currentColorGradient;
+                other._currentColor1.copyFrom(this._currentColor1);
+                other._currentColor2.copyFrom(this._currentColor2);
+            }
         };
         return Particle;
     }());
@@ -55575,6 +55590,7 @@ var BABYLON;
                 if (_this._stockParticles.length !== 0) {
                     particle = _this._stockParticles.pop();
                     particle.age = 0;
+                    particle._currentColorGradient = null;
                     particle.cellIndex = _this.startSpriteCellID;
                 }
                 else {
@@ -55619,12 +55635,13 @@ var BABYLON;
                         var ratio = particle.age / particle.lifeTime;
                         // Color
                         if (_this._colorGradients && _this._colorGradients.length > 0) {
-                            var color1 = BABYLON.Tmp.Color4[0];
-                            var color2 = BABYLON.Tmp.Color4[1];
                             BABYLON.Tools.GetCurrentGradient(ratio, _this._colorGradients, function (currentGradient, nextGradient, scale) {
-                                currentGradient.getColorToRef(color1);
-                                nextGradient.getColorToRef(color2);
-                                BABYLON.Color4.LerpToRef(color1, color2, scale, particle.color);
+                                if (currentGradient !== particle._currentColorGradient) {
+                                    particle._currentColor1.copyFrom(particle._currentColor2);
+                                    nextGradient.getColorToRef(particle._currentColor2);
+                                    particle._currentColorGradient = currentGradient;
+                                }
+                                BABYLON.Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
                             });
                         }
                         else {
@@ -56143,7 +56160,15 @@ var BABYLON;
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 }
                 else {
-                    this._colorGradients[0].getColorToRef(particle.color);
+                    particle._currentColorGradient = this._colorGradients[0];
+                    particle._currentColorGradient.getColorToRef(particle.color);
+                    particle._currentColor1.copyFrom(particle.color);
+                    if (this._colorGradients.length > 1) {
+                        this._colorGradients[1].getColorToRef(particle._currentColor2);
+                    }
+                    else {
+                        particle._currentColor2.copyFrom(particle.color);
+                    }
                 }
             }
         };
@@ -63721,8 +63746,9 @@ var BABYLON;
          * @param generateDepthBuffer True to generate a depth buffer
          * @param generateStencilBuffer True to generate a stencil buffer
          * @param isMulti True if multiple textures need to be created (Draw Buffers)
+         * @param format The internal format of the buffer in the RTT (RED, RG, RGB, RGBA, ALPHA...)
          */
-        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti) {
+        function RenderTargetTexture(name, size, scene, generateMipMaps, doNotChangeAspectRatio, type, isCube, samplingMode, generateDepthBuffer, generateStencilBuffer, isMulti, format) {
             if (doNotChangeAspectRatio === void 0) { doNotChangeAspectRatio = true; }
             if (type === void 0) { type = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT; }
             if (isCube === void 0) { isCube = false; }
@@ -63730,6 +63756,7 @@ var BABYLON;
             if (generateDepthBuffer === void 0) { generateDepthBuffer = true; }
             if (generateStencilBuffer === void 0) { generateStencilBuffer = false; }
             if (isMulti === void 0) { isMulti = false; }
+            if (format === void 0) { format = BABYLON.Engine.TEXTUREFORMAT_RGBA; }
             var _this = _super.call(this, null, scene, !generateMipMaps) || this;
             _this.isCube = isCube;
             /**
@@ -63790,6 +63817,7 @@ var BABYLON;
             _this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 type: type,
+                format: format,
                 samplingMode: samplingMode,
                 generateDepthBuffer: generateDepthBuffer,
                 generateStencilBuffer: generateStencilBuffer
@@ -77077,6 +77105,23 @@ var BABYLON;
             }
         };
         /**
+         * Adds a camera to the pipeline
+         * @param camera the camera to be added
+         */
+        DefaultRenderingPipeline.prototype.addCamera = function (camera) {
+            this._originalCameras.push(camera);
+            this._buildPipeline();
+        };
+        /**
+         * Removes a camera from the pipeline
+         * @param camera the camera to remove
+         */
+        DefaultRenderingPipeline.prototype.removeCamera = function (camera) {
+            var index = this._originalCameras.indexOf(camera);
+            this._originalCameras.splice(index, 1);
+            this._buildPipeline();
+        };
+        /**
          * Dispose of the pipeline and stop all post processes
          */
         DefaultRenderingPipeline.prototype.dispose = function () {
@@ -87835,11 +87880,12 @@ var BABYLON;
             var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
             for (var level = 0; level < mipmapCount; level++) {
                 var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
+                dataOffset += 4; //image data starts from next multiple of 4 offset. Each face refers to same imagesize field above.
                 for (var face = 0; face < this.numberOfFaces; face++) {
                     var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset + 4, imageSize);
+                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset, imageSize);
                     gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
-                    dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+                    dataOffset += imageSize; // add size of the image for the next face/mipmap
                     dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
                 }
                 width = Math.max(1.0, width * 0.5);
@@ -89414,23 +89460,26 @@ var BABYLON;
                     }
                 }
             });
+            this.attachedMesh = null;
         }
-        Gizmo.prototype._onInteractionsEnabledChanged = function (value) {
-        };
-        Object.defineProperty(Gizmo.prototype, "interactionsEnabled", {
-            get: function () {
-                return this._interactionsEnabled;
-            },
+        Object.defineProperty(Gizmo.prototype, "attachedMesh", {
             /**
-             * If interactions are enabled with this gizmo. (eg. dragging/rotation)
+             * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
+             * * When set, interactions will be enabled
              */
+            get: function () {
+                return this._attachedMesh;
+            },
             set: function (value) {
-                this._interactionsEnabled = value;
-                this._onInteractionsEnabledChanged(value);
+                this._attachedMesh = value;
+                this._rootMesh.setEnabled(value ? true : false);
+                this._attachedMeshChanged(value);
             },
             enumerable: true,
             configurable: true
         });
+        Gizmo.prototype._attachedMeshChanged = function (value) {
+        };
         /**
          * Disposes of the gizmo
          */
@@ -89464,6 +89513,15 @@ var BABYLON;
         function AxisDragGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89487,16 +89545,31 @@ var BABYLON;
             arrowTail.position.z += 0.15;
             arrow.lookAt(_this._rootMesh.position.subtract(dragAxis));
             _this._rootMesh.addChild(arrow);
+            var currentSnapDragDistance = 0;
+            var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             // Add drag behavior to handle events when the gizmo is dragged
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    _this.attachedMesh.position.addInPlace(event.delta);
+                    // Snapping logic
+                    if (_this.snapDistance == 0) {
+                        _this.attachedMesh.position.addInPlace(event.delta);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(Math.abs(currentSnapDragDistance) / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            event.delta.normalizeToRef(tmpVector);
+                            tmpVector.scaleInPlace(_this.snapDistance * dragSteps);
+                            _this.attachedMesh.position.addInPlace(tmpVector);
+                            tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                            _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                        }
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89513,13 +89586,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisDragGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisDragGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisDragGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89548,6 +89624,15 @@ var BABYLON;
         function AxisScaleGizmo(gizmoLayer, dragAxis, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89575,13 +89660,29 @@ var BABYLON;
             _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
+            var currentSnapDragDistance = 0;
             var tmpVector = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh) {
-                    dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    // Snapping logic
+                    var snapped = false;
+                    var dragSteps = 0;
+                    if (_this.snapDistance == 0) {
+                        dragAxis.scaleToRef(event.dragDistance, tmpVector);
+                    }
+                    else {
+                        currentSnapDragDistance += event.dragDistance;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            dragAxis.scaleToRef(_this.snapDistance * dragSteps, tmpVector);
+                            snapped = true;
+                        }
+                        else {
+                            tmpVector.scaleInPlace(0);
+                        }
+                    }
                     var invertCount = 0;
                     if (_this.attachedMesh.scaling["x"] < 0) {
                         invertCount++;
@@ -89598,6 +89699,10 @@ var BABYLON;
                     else {
                         _this.attachedMesh.scaling.subtractInPlace(tmpVector);
                     }
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = _this.snapDistance * dragSteps;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89614,13 +89719,16 @@ var BABYLON;
             });
             return _this;
         }
-        AxisScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        AxisScaleGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         AxisScaleGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89649,6 +89757,15 @@ var BABYLON;
         function PlaneRotationGizmo(gizmoLayer, planeNormal, color) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._pointerObserver = null;
+            /**
+             * Rotation distance in radians that the gizmo will snap to (Default: 0)
+             */
+            _this.snapDistance = 0;
+            /**
+             * Event that fires each time the gizmo snaps to a new location.
+             * * snapDistance is the the change in distance
+             */
+            _this.onSnapObservable = new BABYLON.Observable();
             // Create Material
             var coloredMaterial = new BABYLON.StandardMaterial("", gizmoLayer.utilityLayerScene);
             coloredMaterial.disableLighting = true;
@@ -89672,18 +89789,16 @@ var BABYLON;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
             _this._dragBehavior.onDragStartObservable.add(function (e) {
-                if (!_this.interactionsEnabled) {
-                    return;
+                if (_this.attachedMesh) {
+                    lastDragPosition = e.dragPlanePoint;
                 }
-                lastDragPosition = e.dragPlanePoint;
             });
             var rotationMatrix = new BABYLON.Matrix();
             var planeNormalTowardsCamera = new BABYLON.Vector3();
             var localPlaneNormalTowardsCamera = new BABYLON.Vector3();
+            var tmpSnapEvent = { snapDistance: 0 };
+            var currentSnapDragDistance = 0;
             _this._dragBehavior.onDragObservable.add(function (event) {
-                if (!_this.interactionsEnabled) {
-                    return;
-                }
                 if (_this.attachedMesh && lastDragPosition) {
                     if (!_this.attachedMesh.rotationQuaternion) {
                         _this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -89711,12 +89826,30 @@ var BABYLON;
                     var halfCircleSide = BABYLON.Vector3.Dot(localPlaneNormalTowardsCamera, cross) > 0.0;
                     if (halfCircleSide)
                         angle = -angle;
+                    // Snapping logic
+                    var snapped = false;
+                    if (_this.snapDistance != 0) {
+                        currentSnapDragDistance += angle;
+                        if (Math.abs(currentSnapDragDistance) > _this.snapDistance) {
+                            var dragSteps = Math.floor(currentSnapDragDistance / _this.snapDistance);
+                            currentSnapDragDistance = currentSnapDragDistance % _this.snapDistance;
+                            angle = _this.snapDistance * dragSteps;
+                            snapped = true;
+                        }
+                        else {
+                            angle = 0;
+                        }
+                    }
                     // Convert angle and axis to quaternion (http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm)
                     var quaternionCoefficient = Math.sin(angle / 2);
                     var amountToRotate = new BABYLON.Quaternion(planeNormalTowardsCamera.x * quaternionCoefficient, planeNormalTowardsCamera.y * quaternionCoefficient, planeNormalTowardsCamera.z * quaternionCoefficient, Math.cos(angle / 2));
                     // Rotate selected mesh quaternion over fixed axis
                     _this.attachedMesh.rotationQuaternion.multiplyToRef(amountToRotate, _this.attachedMesh.rotationQuaternion);
                     lastDragPosition = event.dragPlanePoint;
+                    if (snapped) {
+                        tmpSnapEvent.snapDistance = angle;
+                        _this.onSnapObservable.notifyObservers(tmpSnapEvent);
+                    }
                 }
             });
             _this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add(function (pointerInfo, eventState) {
@@ -89733,13 +89866,16 @@ var BABYLON;
             });
             return _this;
         }
-        PlaneRotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._dragBehavior.enabled = value;
+        PlaneRotationGizmo.prototype._attachedMeshChanged = function (value) {
+            if (this._dragBehavior) {
+                this._dragBehavior.enabled = value ? true : false;
+            }
         };
         /**
          * Disposes of the gizmo
          */
         PlaneRotationGizmo.prototype.dispose = function () {
+            this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this._dragBehavior.detach();
             _super.prototype.dispose.call(this);
@@ -89760,30 +89896,28 @@ var BABYLON;
     var PositionGizmo = /** @class */ (function (_super) {
         __extends(PositionGizmo, _super);
         /**
-         * Creates a PositionGizmo
-         * @param gizmoLayer The utility layer the gizmo will be added to
-         */
+             * Creates a PositionGizmo
+             * @param gizmoLayer The utility layer the gizmo will be added to
+             */
         function PositionGizmo(gizmoLayer) {
             var _this = _super.call(this, gizmoLayer) || this;
             _this._xDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisDragGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(PositionGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        PositionGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(PositionGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89830,22 +89964,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.PlaneRotationGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(RotationGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        RotationGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(RotationGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -89892,22 +90024,20 @@ var BABYLON;
             _this._xDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(1, 0, 0), BABYLON.Color3.Green().scale(0.5));
             _this._yDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 1, 0), BABYLON.Color3.Red().scale(0.5));
             _this._zDrag = new BABYLON.AxisScaleGizmo(gizmoLayer, new BABYLON.Vector3(0, 0, 1), BABYLON.Color3.Blue().scale(0.5));
+            _this.attachedMesh = null;
             return _this;
         }
         Object.defineProperty(ScaleGizmo.prototype, "attachedMesh", {
             set: function (mesh) {
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+                if (this._xDrag) {
+                    this._xDrag.attachedMesh = mesh;
+                    this._yDrag.attachedMesh = mesh;
+                    this._zDrag.attachedMesh = mesh;
+                }
             },
             enumerable: true,
             configurable: true
         });
-        ScaleGizmo.prototype._onInteractionsEnabledChanged = function (value) {
-            this._xDrag.interactionsEnabled = value;
-            this._yDrag.interactionsEnabled = value;
-            this._zDrag.interactionsEnabled = value;
-        };
         Object.defineProperty(ScaleGizmo.prototype, "updateGizmoRotationToMatchAttachedMesh", {
             get: function () {
                 return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
@@ -90174,6 +90304,23 @@ var BABYLON;
             }
         };
         /**
+         * Enables rotation on the specified axis and disables rotation on the others
+         * @param axis The list of axis that should be enabled (eg. "xy" or "xyz")
+         */
+        BoundingBoxGizmo.prototype.setEnabledRotationAxis = function (axis) {
+            this._rotateSpheresParent.getChildMeshes().forEach(function (m, i) {
+                if (i < 4) {
+                    m.setEnabled(axis.indexOf("x") != -1);
+                }
+                else if (i < 8) {
+                    m.setEnabled(axis.indexOf("y") != -1);
+                }
+                else {
+                    m.setEnabled(axis.indexOf("z") != -1);
+                }
+            });
+        };
+        /**
          * Disposes of the gizmo
          */
         BoundingBoxGizmo.prototype.dispose = function () {

+ 12 - 0
src/Particles/babylon.particle.ts

@@ -68,6 +68,13 @@
         /** @hidden */
         public _initialSize: number;
 
+        /** @hidden */
+        public _currentColorGradient: Nullable<ColorGradient>;
+        /** @hidden */
+        public _currentColor1 = new Color4(0, 0, 0, 0);
+        /** @hidden */
+        public _currentColor2 = new Color4(0, 0, 0, 0);
+
         /**
          * Creates a new instance Particle
          * @param particleSystem the particle system the particle belongs to
@@ -159,6 +166,11 @@
             other.angularSpeed = this.angularSpeed;
             other.particleSystem = this.particleSystem;
             other.cellIndex = this.cellIndex;
+            if (this._currentColorGradient) {
+                other._currentColorGradient = this._currentColorGradient;
+                other._currentColor1.copyFrom(this._currentColor1);
+                other._currentColor2.copyFrom(this._currentColor2);
+            }
         }
     }
 } 

+ 16 - 7
src/Particles/babylon.particleSystem.ts

@@ -491,13 +491,13 @@
 
                         // Color
                         if (this._colorGradients && this._colorGradients.length > 0) {
-                            var color1 = Tmp.Color4[0];
-                            var color2 = Tmp.Color4[1];
-
                             Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
-                                (<ColorGradient>currentGradient).getColorToRef(color1);
-                                (<ColorGradient>nextGradient).getColorToRef(color2);
-                                Color4.LerpToRef(color1, color2, scale, particle.color);
+                                if (currentGradient !== particle._currentColorGradient) {
+                                    particle._currentColor1.copyFrom(particle._currentColor2);
+                                    (<ColorGradient>nextGradient).getColorToRef(particle._currentColor2);    
+                                    particle._currentColorGradient = (<ColorGradient>currentGradient);
+                                }
+                                Color4.LerpToRef(particle._currentColor1, particle._currentColor2, scale, particle.color);
                             });
                         }
                         else {
@@ -864,6 +864,7 @@
             if (this._stockParticles.length !== 0) {
                 particle = <Particle>this._stockParticles.pop();
                 particle.age = 0;
+                particle._currentColorGradient = null;
                 particle.cellIndex = this.startSpriteCellID;
             } else {
                 particle = new Particle(this);
@@ -969,7 +970,15 @@
                     this.colorDead.subtractToRef(particle.color, this._colorDiff);
                     this._colorDiff.scaleToRef(1.0 / particle.lifeTime, particle.colorStep);
                 } else {
-                    this._colorGradients[0].getColorToRef(particle.color);
+                    particle._currentColorGradient = this._colorGradients[0];
+                    particle._currentColorGradient.getColorToRef(particle.color);
+                    particle._currentColor1.copyFrom(particle.color);
+
+                    if (this._colorGradients.length > 1) {
+                        this._colorGradients[1].getColorToRef(particle._currentColor2);
+                    } else {
+                        particle._currentColor2.copyFrom(particle.color);
+                    }
                 }
             }
         }