Prechádzať zdrojové kódy

Merge remote-tracking branch 'upstream/master'

sebastien 7 rokov pred
rodič
commit
e567565ec7

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


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


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


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


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


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


+ 4 - 25
dist/preview release/viewer/babylon.viewer.d.ts

@@ -168,11 +168,11 @@ declare module BabylonViewer {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
+            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<Template> | Promise<string>;
+            hideOverlayScreen(): Promise<string> | Promise<Template>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -189,11 +189,11 @@ declare module BabylonViewer {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<Template> | Promise<string>;
+            showLoadingScreen(): Promise<string> | Promise<Template>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<Template> | Promise<string>;
+            hideLoadingScreen(): Promise<string> | Promise<Template>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -919,13 +919,6 @@ declare module BabylonViewer {
     }
 }
 declare module BabylonViewer {
-    /**
-      *
-      * @param name the name of the custom optimizer configuration
-      * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
-      */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
-    export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
     /**
@@ -1558,20 +1551,6 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 declare module BabylonViewer {
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-declare module BabylonViewer {
 }
 declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {

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


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


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

@@ -200,11 +200,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
+            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<Template> | Promise<string>;
+            hideOverlayScreen(): Promise<string> | Promise<Template>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -221,11 +221,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<Template> | Promise<string>;
+            showLoadingScreen(): Promise<string> | Promise<Template>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<Template> | Promise<string>;
+            hideLoadingScreen(): Promise<string> | Promise<Template>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -985,15 +985,7 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 
 declare module 'babylonjs-viewer/optimizer/custom' {
-    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
-    import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
-    /**
-      *
-      * @param name the name of the custom optimizer configuration
-      * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
-      */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
-    export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
+    
 }
 
 declare module 'babylonjs-viewer/initializer' {
@@ -1663,22 +1655,6 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 
-declare module 'babylonjs-viewer/optimizer/custom/extended' {
-    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';

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

@@ -15,6 +15,7 @@
   - Added ignoreChildren field to bounding box to save performance when using heavily nested meshes ([TrevorDev](https://github.com/TrevorDev))
   - Add uniform scaling drag support to scale gizmo ([TrevorDev](https://github.com/TrevorDev))
   - Support interacting with child elements ([TrevorDev](https://github.com/TrevorDev))
+  - BoundingBox gizmo support for including/excluding descendants when computing the bounding box ([TrevorDev](https://github.com/TrevorDev))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
   - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
   - Improved CPU particles rendering performance (up to x2 on low end devices)
@@ -32,8 +33,9 @@
   - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
   - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
   - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
-  - Added support for velocty gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#velocity-over-time)
-  - Added support for limit velocty gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time)
+  - Added support for velocity gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#velocity-over-time)
+  - Added support for limit velocity gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time)
+  - Added support for drag gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#drag-factor)
   - Added support for noise textures. [Doc](http://doc.babylonjs.com/babylon101/particles#noise-texture)
   - Added support for emit rate gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#emit-rate-over-time)
   - Start size gradient support for particles. [Doc](http://doc.babylonjs.com/babylon101/particles#start-size-over-time) ([TrevorDev](https://github.com/TrevorDev))
@@ -49,7 +51,7 @@
 ## Updates
 
 - Updated TypeScript version to new major 3.0.1 ([christopherstock](https://github.com/christopherstock))
-- All NPM packages have `latest`and `preview` streams [#3055](https://github.com/BabylonJS/Babylon.js/issues/3055) ([RaananW](https://github.com/RaananW))
+- All NPM packages have `latest` and `preview` streams [#3055](https://github.com/BabylonJS/Babylon.js/issues/3055) ([RaananW](https://github.com/RaananW))
 - Added New Tools Tab in the inspector (env texture and screenshot tools so far) ([sebavan](http://www.github.com/sebavan))
 - Moved to gulp 4, updated dependencies to latest ([RaananW](https://github.com/RaananW))
 
@@ -62,7 +64,7 @@
 ### Core Engine
 
 - Added `scene.pickSpriteWithRay` function ([Deltakosh](https://github.com/deltakosh))
-- Added support for muyltiple clip planes. [Demo](https://www.babylonjs-playground.com/#Y6W087) ([Deltakosh](https://github.com/deltakosh))
+- Added support for multiple clip planes. [Demo](https://www.babylonjs-playground.com/#Y6W087) ([Deltakosh](https://github.com/deltakosh))
 - Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
 - Added new `BoundingInfo.scale()` function to let users control the size of the bounding info ([Deltakosh](https://github.com/deltakosh))
 - Added new `Animatable.waitAsync` function to use Promises with animations. Demo [Here](https://www.babylonjs-playground.com/#HZBCXR) ([Deltakosh](https://github.com/deltakosh))

+ 5 - 11
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -18,6 +18,10 @@ module BABYLON {
          * If child meshes should be ignored when calculating the boudning box. This should be set to true to avoid perf hits with heavily nested meshes (Default: false)
          */
         public ignoreChildren = false;
+        /**
+         * Returns true if a descendant should be included when computing the bounding box. When null, all descendants are included. If ignoreChildren is set this will be ignored. (Default: null)
+         */
+        public includeChildPredicate: Nullable<(abstractMesh: AbstractMesh) => boolean> = null
 
         /**
          * The size of the rotation spheres attached to the bounding box (Default: 0.1)
@@ -332,15 +336,6 @@ module BABYLON {
                 })
         }
 
-        private _recurseComputeWorld(node: Node) {
-            node.computeWorldMatrix(true);
-            if(!this.ignoreChildren){
-                node.getDescendants().forEach((n) => {
-                    this._recurseComputeWorld(n);
-                });
-            }
-        }
-
         /**
          * Updates the bounding box information for the Gizmo
          */
@@ -364,7 +359,7 @@ module BABYLON {
                 this.attachedMesh.position.set(0, 0, 0);
 
                 // Update bounding dimensions/positions   
-                var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors(!this.ignoreChildren);
+                var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors(!this.ignoreChildren, this.includeChildPredicate);
                 boundingMinMax.max.subtractToRef(boundingMinMax.min, this._boundingDimensions);
 
                 // Update gizmo to match bounding box scaling and rotation
@@ -378,7 +373,6 @@ module BABYLON {
                 // restore position/rotation values
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
-                this._recurseComputeWorld(this.attachedMesh);
             }
             
             // Update rotation sphere locations

+ 106 - 0
src/Particles/EmitterTypes/babylon.cylinderParticleEmitter.ts

@@ -135,4 +135,110 @@ module BABYLON {
             this.directionRandomizer = serializationObject.directionRandomizer;
         }          
     }
+
+    /**
+     * Particle emitter emitting particles from the inside of a cylinder.
+     * It emits the particles randomly between two vectors.
+     */
+    export class CylinderDirectedParticleEmitter extends CylinderParticleEmitter {
+
+        /**
+         * Creates a new instance CylinderDirectedParticleEmitter
+         * @param radius the radius of the emission cylinder (1 by default)
+         * @param height the height of the emission cylinder (1 by default)
+         * @param radiusRange the range of the emission cylinder [0-1] 0 Surface only, 1 Entire Radius (1 by default) 
+         * @param direction1 the min limit of the emission direction (up vector by default)
+         * @param direction2 the max limit of the emission direction (up vector by default)
+         */
+        constructor(
+            radius = 1, 
+            height = 1,
+            radiusRange = 1,
+            /**
+             * The min limit of the emission direction.
+             */
+            public direction1 = new Vector3(0, 1, 0), 
+            /**
+             * The max limit of the emission direction.
+             */
+            public direction2 = new Vector3(0, 1, 0)) {
+            super(radius, height, radiusRange);
+        }
+
+        /**
+         * Called by the particle System when the direction is computed for the created particle.
+         * @param worldMatrix is the world matrix of the particle system
+         * @param directionToUpdate is the direction vector to update with the result
+         * @param particle is the particle we are computed the direction for
+         */
+        public startDirectionFunction(worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
+            var randX = Scalar.RandomRange(this.direction1.x, this.direction2.x);
+            var randY = Scalar.RandomRange(this.direction1.y, this.direction2.y);
+            var randZ = Scalar.RandomRange(this.direction1.z, this.direction2.z);
+            Vector3.TransformNormalFromFloatsToRef(randX, randY, randZ, worldMatrix, directionToUpdate);
+        }
+
+        /**
+         * Clones the current emitter and returns a copy of it
+         * @returns the new emitter
+         */
+        public clone(): CylinderDirectedParticleEmitter {
+            let newOne = new CylinderDirectedParticleEmitter(this.radius, this.height, this.radiusRange, this.direction1, this.direction2);
+
+            Tools.DeepCopy(this, newOne);
+
+            return newOne;
+        }     
+        
+        /**
+         * Called by the GPUParticleSystem to setup the update shader
+         * @param effect defines the update shader
+         */        
+        public applyToShader(effect: Effect): void {
+            effect.setFloat("radius", this.radius);
+            effect.setFloat("height", this.height);
+            effect.setFloat("radiusRange", this.radiusRange);
+            effect.setVector3("direction1", this.direction1);
+            effect.setVector3("direction2", this.direction2);
+        }       
+        
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns a string containng the defines string
+         */
+        public getEffectDefines(): string {
+            return "#define CYLINDEREMITTER\n#define DIRECTEDCYLINDEREMITTER"
+        }    
+        
+        /**
+         * Returns the string "CylinderDirectedParticleEmitter"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "CylinderDirectedParticleEmitter";
+        }       
+        
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        public serialize(): any {
+            var serializationObject = super.serialize();
+
+            serializationObject.direction1 = this.direction1.asArray();
+            serializationObject.direction2 = this.direction2.asArray();
+
+            return serializationObject;
+        }    
+        
+        /**
+         * Parse properties from a JSON object
+         * @param serializationObject defines the JSON object
+         */
+        public parse(serializationObject: any): void {
+            super.parse(serializationObject);
+            this.direction1.copyFrom(serializationObject.direction1);
+            this.direction2.copyFrom(serializationObject.direction2);
+        }           
+    }
 }

+ 11 - 0
src/Particles/babylon.IParticleSystem.ts

@@ -464,6 +464,17 @@ module BABYLON {
         createCylinderEmitter(radius: number, height: number, radiusRange: number, directionRandomizer: number): CylinderParticleEmitter;
 
         /**
+         * Creates a Directed Cylinder Emitter for the particle system (emits between direction1 and direction2)
+         * @param radius The radius of the cylinder to emit from
+         * @param height The height of the emission cylinder
+         * @param radiusRange the range of the emission cylinder [0-1] 0 Surface only, 1 Entire Radius (1 by default) 
+         * @param direction1 Particles are emitted between the direction1 and direction2 from within the cylinder
+         * @param direction2 Particles are emitted between the direction1 and direction2 from within the cylinder
+         * @returns the emitter
+         */
+        createDirectedCylinderEmitter(radius: number, height: number, radiusRange:number , direction1:Vector3, direction2: Vector3): SphereDirectedParticleEmitter;
+
+        /**
          * Creates a Cone Emitter for the particle system (emits from the cone to the particle position)
          * @param radius The radius of the cone to emit from
          * @param angle The base angle of the cone

+ 15 - 0
src/Particles/babylon.baseParticleSystem.ts

@@ -588,6 +588,21 @@ module BABYLON {
         }
 
         /**
+         * Creates a Directed Cylinder Emitter for the particle system (emits between direction1 and direction2)
+         * @param radius The radius of the cylinder to emit from
+         * @param height The height of the emission cylinder
+         * @param radiusRange the range of the emission cylinder [0-1] 0 Surface only, 1 Entire Radius (1 by default) 
+         * @param direction1 Particles are emitted between the direction1 and direction2 from within the cylinder
+         * @param direction2 Particles are emitted between the direction1 and direction2 from within the cylinder
+         * @returns the emitter
+         */
+        public createDirectedCylinderEmitter(radius = 1, height = 1, radiusRange = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)): CylinderDirectedParticleEmitter {
+            var particleEmitter = new CylinderDirectedParticleEmitter(radius, height, radiusRange, direction1, direction2)
+            this.particleEmitterType = particleEmitter;
+            return particleEmitter;
+        }
+
+        /**
          * Creates a Cone Emitter for the particle system (emits from the cone to the particle position)
          * @param radius The radius of the cone to emit from
          * @param angle The base angle of the cone

+ 30 - 3
src/Particles/babylon.gpuParticleSystem.ts

@@ -519,9 +519,9 @@
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex"],
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
-                                "angleRange", "radiusRange", "cellInfos", "noiseStrength"],
+                                "angleRange", "radiusRange", "cellInfos", "noiseStrength", "limitVelocityDamping"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "noiseSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "limitVelocityGradientSampler", "noiseSampler", "dragGradientSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -773,7 +773,15 @@
             
             if (this._velocityGradientsTexture) {
                 defines += "\n#define VELOCITYGRADIENTS";
-            }                    
+            }        
+
+            if (this._limitVelocityGradientsTexture) {
+                defines += "\n#define LIMITVELOCITYGRADIENTS";
+            }                
+            
+            if (this._dragGradientsTexture) {
+                defines += "\n#define DRAGGRADIENTS";
+            }              
             
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
@@ -919,6 +927,14 @@
         private _createVelocityGradientTexture() {
             this._createFactorGradientTexture(this._velocityGradients, "_velocityGradientsTexture");
         }          
+
+        private _createLimitVelocityGradientTexture() {
+            this._createFactorGradientTexture(this._limitVelocityGradients, "_limitVelocityGradientsTexture");
+        }          
+
+        private _createDragGradientTexture() {
+            this._createFactorGradientTexture(this._dragGradients, "_dragGradientsTexture");
+        }            
             
         private _createColorGradientTexture() {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
@@ -959,6 +975,8 @@
             this._createSizeGradientTexture();
             this._createAngularSpeedGradientTexture();
             this._createVelocityGradientTexture();
+            this._createLimitVelocityGradientTexture();
+            this._createDragGradientTexture();
 
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
@@ -1030,6 +1048,15 @@
                 this._updateEffect.setTexture("velocityGradientSampler", this._velocityGradientsTexture);      
             }
 
+            if (this._limitVelocityGradientsTexture) {      
+                this._updateEffect.setTexture("limitVelocityGradientSampler", this._limitVelocityGradientsTexture);  
+                this._updateEffect.setFloat("limitVelocityDamping", this.limitVelocityDamping);    
+            }            
+
+            if (this._dragGradientsTexture) {      
+                this._updateEffect.setTexture("dragGradientSampler", this._dragGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }

+ 42 - 6
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -52,7 +52,12 @@ uniform float radiusRange;
 uniform float radius;
 uniform float height;
 uniform float radiusRange;
-uniform float directionRandomizer;
+#ifdef DIRECTEDCYLINDEREMITTER
+  uniform vec3 direction1;
+  uniform vec3 direction2;
+#else
+  uniform float directionRandomizer;
+#endif
 #endif
 
 #ifdef CONEEMITTER
@@ -118,6 +123,15 @@ uniform sampler2D angularSpeedGradientSampler;
 uniform sampler2D velocityGradientSampler;
 #endif
 
+#ifdef LIMITVELOCITYGRADIENTS
+uniform sampler2D limitVelocityGradientSampler;
+uniform float limitVelocityDamping;
+#endif
+
+#ifdef DRAGGRADIENTS
+uniform sampler2D dragGradientSampler;
+#endif
+
 #ifdef NOISE
 uniform vec3 noiseStrength;
 uniform sampler2D noiseSampler;
@@ -238,10 +252,14 @@ void main() {
     float zPos = positionRadius * sin(angle);
     position = vec3(xPos, yPos, zPos);
 
-    // Direction
-    angle = angle + ((randoms3.x-0.5) * PI);
-    direction = vec3(cos(angle), randoms3.y-0.5, sin(angle));
-    direction = normalize(direction);
+    #ifdef DIRECTEDCYLINDEREMITTER
+      direction = direction1 + (direction2 - direction1) * randoms3;
+    #else
+      // Direction
+      angle = angle + ((randoms3.x-0.5) * PI);
+      direction = vec3(cos(angle), randoms3.y-0.5, sin(angle));
+      direction = normalize(direction);
+    #endif
 #elif defined(CONEEMITTER)
     vec3 randoms2 = getRandomVec3(seed.y);
 
@@ -300,6 +318,11 @@ void main() {
 #ifdef VELOCITYGRADIENTS
     directionScale *= texture(velocityGradientSampler, vec2(ageGradient, 0)).r;
 #endif
+
+#ifdef DRAGGRADIENTS
+    directionScale *= texture(dragGradientSampler, vec2(ageGradient, 0)).r;
+#endif
+
     outPosition = position + direction * directionScale;
     
     outLife = life;
@@ -318,7 +341,20 @@ void main() {
 #ifndef BILLBOARD    
     outInitialDirection = initialDirection;
 #endif
-    outDirection = direction + gravity * timeDelta;
+
+    vec3 updatedDirection = direction + gravity * timeDelta;
+
+#ifdef LIMITVELOCITYGRADIENTS
+    float limitVelocity = texture(limitVelocityGradientSampler, vec2(ageGradient, 0)).r;
+
+    float currentVelocity = length(updatedDirection);
+
+    if (currentVelocity > limitVelocity) {
+        updatedDirection = updatedDirection * limitVelocityDamping;
+    }
+#endif
+
+    outDirection = updatedDirection;
 
 #ifdef NOISE
     vec3 localPosition = outPosition - emitterWM[3].xyz;

+ 11 - 11
tests/validation/config.json

@@ -2,16 +2,6 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
-      "title": "Edges",
-      "playgroundId": "#TYAHX#113",
-      "referenceImage": "edges.png"
-    },
-    {
-      "title": "Outline",
-      "playgroundId": "#10WJ5S#6",
-      "referenceImage": "outline.png"
-    },
-    {
       "title": "Clip planes",
       "playgroundId": "#Y6W087#0",
       "referenceImage": "clipplanes.png"
@@ -545,6 +535,16 @@
       "playgroundId": "#3HPMAA#0",
       "renderCount": 50,
       "referenceImage": "depthRenderer.png"
+    },
+    {
+      "title": "Edges",
+      "playgroundId": "#TYAHX#113",
+      "referenceImage": "edges.png"
+    },
+    {
+      "title": "Outline",
+      "playgroundId": "#10WJ5S#6",
+      "referenceImage": "outline.png"
     }
   ]
-}
+}

+ 3 - 0
tests/validation/validation.js

@@ -48,6 +48,9 @@ function compare(renderData, referenceCanvas) {
 
     referenceContext.putImageData(referenceData, 0, 0);
 
+    if (differencesCount) {
+        console.log("Pixel difference: " + differencesCount + " pixels.")
+    }
     return (differencesCount * 100) / (width * height) > errorRatio;
 }