David Catuhe 7 năm trước cách đây
mục cha
commit
a51a2aaf9f

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 15007 - 14923
Playground/babylon.d.txt


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 15030 - 14946
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 28 - 28
dist/preview release/babylon.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 207 - 93
dist/preview release/babylon.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 207 - 93
dist/preview release/babylon.no-module.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 28 - 28
dist/preview release/babylon.worker.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 207 - 93
dist/preview release/es6.js


+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -1720,7 +1720,7 @@ var BABYLON;
                         // Read data from WebGL
                         var canvas = engine.getRenderingCanvas();
                         if (canvas) {
-                            canvas.toBlob(function (blob) {
+                            BABYLON.Tools.ToBlob(canvas, function (blob) {
                                 if (blob) {
                                     var fileReader = new FileReader();
                                     fileReader.onload = function (event) {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.js

@@ -1870,7 +1870,7 @@ var BABYLON;
                         // Read data from WebGL
                         var canvas = engine.getRenderingCanvas();
                         if (canvas) {
-                            canvas.toBlob(function (blob) {
+                            BABYLON.Tools.ToBlob(canvas, function (blob) {
                                 if (blob) {
                                     var fileReader = new FileReader();
                                     fileReader.onload = function (event) {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 51 - 51
dist/preview release/viewer/babylon.viewer.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 207 - 93
dist/preview release/viewer/babylon.viewer.max.js


+ 22 - 1
src/Particles/babylon.IParticleSystem.ts

@@ -150,7 +150,28 @@ module BABYLON {
         /** 
          * Gets or sets a value indicating the time step multiplier to use in pre-warm mode (default is 1) 
          */
-        preWarmStepOffset: number;               
+        preWarmStepOffset: number;     
+        
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled) defines the speed of the sprite loop (default is 1 meaning the animation will play once during the entire particle lifetime)
+         */
+        spriteCellChangeSpeed: number;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled) defines the first sprite cell to display
+         */
+        startSpriteCellID: number;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled) defines the last sprite cell to display
+         */
+        endSpriteCellID: number;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell width to use
+         */
+        spriteCellWidth: number;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell height to use
+         */
+        spriteCellHeight: number;        
 
         /**
          * Gets the maximum number of particles active at the same time.

+ 79 - 8
src/Particles/babylon.gpuParticleSystem.ts

@@ -301,7 +301,37 @@
         /**
          * Gets or sets the maximal initial rotation in radians.         
          */
-        public maxInitialRotation = 0;            
+        public maxInitialRotation = 0;   
+        
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled) defines the speed of the sprite loop (default is 1 meaning the animation will play once during the entire particle lifetime)
+         */
+        public spriteCellChangeSpeed = 1;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled) defines the first sprite cell to display
+         */
+        public startSpriteCellID = 0;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled) defines the last sprite cell to display
+         */
+        public endSpriteCellID = 0;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell width to use
+         */
+        public spriteCellWidth = 0;
+        /**
+         * If using a spritesheet (isAnimationSheetEnabled), defines the sprite cell height to use
+         */
+        public spriteCellHeight = 0;
+                
+        private _isAnimationSheetEnabled: boolean;
+
+        /**
+         * Gets whether an animation sprite sheet is enabled or not on the particle system
+         */
+        public get isAnimationSheetEnabled(): boolean {
+            return this._isAnimationSheetEnabled;
+        }        
 
         /**
          * Is this system ready to be used/rendered
@@ -538,13 +568,14 @@
          * Instantiates a GPU particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * @param name The name of the particle system
-         * @param capacity The max number of particles alive at the same time
+         * @param options The options used to create the system
          * @param scene The scene the particle system belongs to
+         * @param isAnimationSheetEnabled Must be true if using a spritesheet to animate the particles texture
          */
         constructor(name: string, options: Partial<{
                         capacity: number,
                         randomTextureSize: number
-                    }>, scene: Scene) {
+                    }>, scene: Scene, isAnimationSheetEnabled: boolean = false) {
             this.id = name;
             this.name = name;
             this._scene = scene || Engine.LastCreatedScene;
@@ -568,14 +599,15 @@
             this._capacity = fullOptions.capacity;
             this._activeCount = fullOptions.capacity;
             this._currentActiveCount = 0;
+            this._isAnimationSheetEnabled = isAnimationSheetEnabled;
 
             this._scene.particleSystems.push(this);
 
             this._updateEffectOptions = {
-                attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "initialSize"],
+                attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "initialSize", "cellIndex"],
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
-                                "angleRange", "radiusRange"],
+                                "angleRange", "radiusRange", "cellInfos"],
                 uniformBuffersNames: [],
                 samplers:["randomSampler", "randomSampler2", "sizeGradientSampler"],
                 defines: "",
@@ -643,6 +675,12 @@
             }
 
             updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
+            offset += 2;
+
+            if (this._isAnimationSheetEnabled) {
+                updateVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1);
+                offset += 1;
+            }            
            
             let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
             this._engine.bindArrayBuffer(null);
@@ -674,6 +712,12 @@
                 offset += 3;
             }
             renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2, this._attributesStrideSize, true);
+            offset += 2;
+
+            if (this._isAnimationSheetEnabled) {
+                renderVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1, this._attributesStrideSize, true);
+                offset += 1;
+            }               
 
             renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
             renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
@@ -704,6 +748,10 @@
                 this._attributesStrideSize += 3;
             }
 
+            if (this._isAnimationSheetEnabled) {
+                this._attributesStrideSize += 1;
+            }            
+
             for (var particleIndex = 0; particleIndex < this._capacity; particleIndex++) {
                 // position
                 data.push(0.0);
@@ -754,6 +802,10 @@
                 // angle
                 data.push(0.0);  
                 data.push(0.0); 
+
+                if (this._isAnimationSheetEnabled) {
+                    data.push(0.0); 
+                }                
             }
 
             // Sprite data
@@ -797,7 +849,11 @@
             
             if (this._sizeGradientsTexture) {
                 defines += "\n#define SIZEGRADIENTS";
-            }                 
+            }     
+            
+            if (this.isAnimationSheetEnabled) {
+                defines += "\n#define ANIMATESHEET";
+            }             
 
             if (this._updateEffect && this._updateEffectOptions.defines === defines) {
                 return;
@@ -821,6 +877,10 @@
 
             this._updateEffectOptions.transformFeedbackVaryings.push("outAngle");
 
+            if (this.isAnimationSheetEnabled) {
+                this._updateEffectOptions.transformFeedbackVaryings.push("outCellIndex");
+            }               
+
             this._updateEffectOptions.defines = defines;
             this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());   
         }
@@ -840,13 +900,17 @@
                 defines += "\n#define COLORGRADIENTS";
             }   
 
+            if (this.isAnimationSheetEnabled) {
+                defines += "\n#define ANIMATESHEET";
+            }                 
+
             if (this._renderEffect && this._renderEffect.defines === defines) {
                 return;
             }
 
             this._renderEffect = new Effect("gpuRenderParticles", 
-                                            ["position", "age", "life", "size", "color", "offset", "uv", "initialDirection", "angle"], 
-                                            ["view", "projection", "colorDead", "invView", "vClipPlane"], 
+                                            ["position", "age", "life", "size", "color", "offset", "uv", "initialDirection", "angle", "cellIndex"], 
+                                            ["view", "projection", "colorDead", "invView", "vClipPlane", "sheetInfos"], 
                                             ["textureSampler", "colorGradientSampler"], this._scene.getEngine(), defines);
         }        
 
@@ -988,6 +1052,9 @@
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
+            if (this._isAnimationSheetEnabled) {
+                this._updateEffect.setFloat3("cellInfos", this.startSpriteCellID, this.endSpriteCellID, this.spriteCellChangeSpeed);
+            }            
 
             let emitterWM: Matrix;
             if ((<AbstractMesh>this.emitter).position) {
@@ -1024,6 +1091,10 @@
                     this._renderEffect.setDirectColor4("colorDead", this.colorDead);
                 }
 
+                if (this._isAnimationSheetEnabled && this.particleTexture) {
+                    let baseSize = this.particleTexture.getBaseSize();
+                    this._renderEffect.setFloat3("sheetInfos", this.spriteCellWidth / baseSize.width, this.spriteCellHeight / baseSize.height, baseSize.width / this.spriteCellWidth);
+                }
 
                 if (this._scene.clipPlane) {
                     var clipPlane = this._scene.clipPlane;

+ 5 - 42
src/Particles/babylon.particle.ts

@@ -60,8 +60,6 @@
          */
         public cellIndex: number = 0;  
 
-        private _currentFrameCounter = 0;
-
         /** @hidden */
         public _initialDirection: Nullable<Vector3>;
 
@@ -93,51 +91,16 @@
 
         private updateCellInfoFromSystem(): void {
             this.cellIndex = this.particleSystem.startSpriteCellID;
-
-            if (this.particleSystem.spriteCellChangeSpeed == 0) {
-                this.updateCellIndex = this._updateCellIndexWithSpeedCalculated;
-            }
-            else {
-                this.updateCellIndex = this._updateCellIndexWithCustomSpeed;
-            }
         }
 
         /**
-         * Defines how the sprite cell index is updated for the particle. This is 
-         * defined as a callback.
+         * Defines how the sprite cell index is updated for the particle
          */
-        public updateCellIndex: (scaledUpdateSpeed: number) => void;
-
-        private _updateCellIndexWithSpeedCalculated(scaledUpdateSpeed: number): void {
-            //   (ageOffset / scaledUpdateSpeed) / available cells
-            var numberOfScaledUpdatesPerCell = ((this.lifeTime - this.age) / scaledUpdateSpeed) / (this.particleSystem.endSpriteCellID + 1 - this.cellIndex);
-
-            this._currentFrameCounter += scaledUpdateSpeed;
-            if (this._currentFrameCounter >= numberOfScaledUpdatesPerCell * scaledUpdateSpeed) {
-                this._currentFrameCounter = 0;
-                this.cellIndex++;
-                if (this.cellIndex > this.particleSystem.endSpriteCellID) {
-                    this.cellIndex = this.particleSystem.endSpriteCellID;
-                }
-            }
-        }
+        public updateCellIndex(): void {
+            let dist = (this.particleSystem.endSpriteCellID - this.particleSystem.startSpriteCellID);
+            let ratio = Scalar.Clamp(((this.age * this.particleSystem.spriteCellChangeSpeed) / this.lifeTime) % this.lifeTime);
 
-        private _updateCellIndexWithCustomSpeed(): void {
-            if (this._currentFrameCounter >= this.particleSystem.spriteCellChangeSpeed) {
-                this.cellIndex++;
-                this._currentFrameCounter = 0;
-                if (this.cellIndex > this.particleSystem.endSpriteCellID) {
-                    if (this.particleSystem.spriteCellLoop) {
-                        this.cellIndex = this.particleSystem.startSpriteCellID;
-                    }
-                    else {
-                        this.cellIndex = this.particleSystem.endSpriteCellID;
-                    }
-                }
-            }
-            else {
-                this._currentFrameCounter++;
-            }
+            this.cellIndex = this.particleSystem.startSpriteCellID + (ratio * dist) | 0;
         }
 
         /**

+ 17 - 22
src/Particles/babylon.particleSystem.ts

@@ -313,19 +313,15 @@
         public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
 
         /**
-         * If using a spritesheet (isAnimationSheetEnabled), defines if the sprite animation should loop between startSpriteCellID and endSpriteCellID or not
+         * If using a spritesheet (isAnimationSheetEnabled) defines the speed of the sprite loop (default is 1 meaning the animation will play once during the entire particle lifetime)
          */
-        public spriteCellLoop = true;
+        public spriteCellChangeSpeed = 1;
         /**
-         * If using a spritesheet (isAnimationSheetEnabled) and spriteCellLoop defines the speed of the sprite loop
-         */
-        public spriteCellChangeSpeed = 0;
-        /**
-         * If using a spritesheet (isAnimationSheetEnabled) and spriteCellLoop defines the first sprite cell to display
+         * If using a spritesheet (isAnimationSheetEnabled) defines the first sprite cell to display
          */
         public startSpriteCellID = 0;
         /**
-         * If using a spritesheet (isAnimationSheetEnabled) and spriteCellLoop defines the last sprite cell to display
+         * If using a spritesheet (isAnimationSheetEnabled) defines the last sprite cell to display
          */
         public endSpriteCellID = 0;
         /**
@@ -524,7 +520,7 @@
                         }
 
                         if (this._isAnimationSheetEnabled) {
-                            particle.updateCellIndex(this._scaledUpdateSpeed);
+                            particle.updateCellIndex();
                         }
                     }
                 }
@@ -1375,13 +1371,6 @@
             serializationObject.customShader = this.customShader;
             serializationObject.preventAutoStart = this.preventAutoStart;
 
-            serializationObject.startSpriteCellID = this.startSpriteCellID;
-            serializationObject.endSpriteCellID = this.endSpriteCellID;
-            serializationObject.spriteCellLoop = this.spriteCellLoop;
-            serializationObject.spriteCellChangeSpeed = this.spriteCellChangeSpeed;
-            serializationObject.spriteCellWidth = this.spriteCellWidth;
-            serializationObject.spriteCellHeight = this.spriteCellHeight;
-
             serializationObject.isAnimationSheetEnabled = this._isAnimationSheetEnabled;
 
             return serializationObject;
@@ -1442,6 +1431,11 @@
             serializationObject.preWarmStepOffset = particleSystem.preWarmStepOffset;
             serializationObject.minInitialRotation = particleSystem.minInitialRotation;
             serializationObject.maxInitialRotation = particleSystem.maxInitialRotation;
+            serializationObject.startSpriteCellID = particleSystem.startSpriteCellID;
+            serializationObject.endSpriteCellID = particleSystem.endSpriteCellID;
+            serializationObject.spriteCellChangeSpeed = particleSystem.spriteCellChangeSpeed;
+            serializationObject.spriteCellWidth = particleSystem.spriteCellWidth;
+            serializationObject.spriteCellHeight = particleSystem.spriteCellHeight;            
 
             let colorGradients = particleSystem.getColorGradients();
             if (colorGradients) {
@@ -1588,6 +1582,13 @@
                 emitterType.parse(parsedParticleSystem);
             }
             particleSystem.particleEmitterType = emitterType;
+
+            // Animation sheet
+            particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
+            particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
+            particleSystem.spriteCellWidth = parsedParticleSystem.spriteCellWidth;
+            particleSystem.spriteCellHeight = parsedParticleSystem.spriteCellHeight;
+            particleSystem.spriteCellChangeSpeed = parsedParticleSystem.spriteCellChangeSpeed;
         }
 
         /**
@@ -1621,12 +1622,6 @@
             ParticleSystem._Parse(parsedParticleSystem, particleSystem, scene, rootUrl);
 
             particleSystem.textureMask = Color4.FromArray(parsedParticleSystem.textureMask);
-            particleSystem.startSpriteCellID = parsedParticleSystem.startSpriteCellID;
-            particleSystem.endSpriteCellID = parsedParticleSystem.endSpriteCellID;
-            particleSystem.spriteCellLoop = parsedParticleSystem.spriteCellLoop;
-            particleSystem.spriteCellChangeSpeed = parsedParticleSystem.spriteCellChangeSpeed;
-            particleSystem.spriteCellWidth = parsedParticleSystem.spriteCellWidth;
-            particleSystem.spriteCellHeight = parsedParticleSystem.spriteCellHeight;
 
             if (!particleSystem.preventAutoStart) {
                 particleSystem.start();

+ 18 - 1
src/Shaders/gpuRenderParticles.vertex.fx

@@ -13,6 +13,9 @@ in vec3 size;
 in vec3 initialDirection;
 #endif
 in vec2 angle;
+#ifdef ANIMATESHEET
+in float cellIndex;
+#endif
 in vec2 offset;
 in vec2 uv;
 
@@ -32,8 +35,22 @@ uniform vec4 colorDead;
 in vec4 color;
 #endif
 
+#ifdef ANIMATESHEET
+uniform vec3 sheetInfos;
+#endif
+
 void main() {
-  vUV = uv;
+
+	#ifdef ANIMATESHEET
+		float rowOffset = floor(cellIndex / sheetInfos.z);
+		float columnOffset = cellIndex - rowOffset * sheetInfos.z;
+
+		vec2 uvScale = sheetInfos.xy;
+		vec2 uvOffset = vec2(uv.x , 1.0 - uv.y);
+		vUV = (uvOffset + vec2(columnOffset, rowOffset)) * uvScale;
+	#else	
+   	vUV = uv;
+	#endif
   float ratio = age / life;
 #ifdef COLORGRADIENTS
 	vColor = texture(colorGradientSampler, vec2(ratio, 0));

+ 23 - 0
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -58,6 +58,9 @@ in vec3 direction;
 in vec3 initialDirection;
 #endif
 in vec2 angle;
+#ifdef ANIMATESHEET
+in float cellIndex;
+#endif
 
 // Output
 out vec3 outPosition;
@@ -76,12 +79,20 @@ out vec3 outDirection;
 out vec3 outInitialDirection;
 #endif
 out vec2 outAngle;
+#ifdef ANIMATESHEET
+out float outCellIndex;
+#endif
 
 #ifdef SIZEGRADIENTS
 uniform sampler2D sizeGradientSampler;
 in vec3 initialSize;
 #endif 
 
+#ifdef ANIMATESHEET
+uniform vec3 cellInfos;
+#endif
+
+
 vec3 getRandomVec3(float offset) {
   return texture(randomSampler2, vec2(float(gl_VertexID) * offset / currentCount, 0)).rgb;
 }
@@ -106,6 +117,9 @@ void main() {
 #endif      
       outDirection = direction;
       outAngle = angle;
+#ifdef ANIMATESHEET      
+      outCellIndex = cellIndex;
+#endif
       return;
     }
     vec3 position;
@@ -206,6 +220,9 @@ void main() {
 #ifndef BILLBOARD        
     outInitialDirection = initial;
 #endif
+#ifdef ANIMATESHEET      
+    outCellIndex = cellInfos.x;
+#endif
 
   } else {   
     outPosition = position + direction * timeDelta;
@@ -228,5 +245,11 @@ void main() {
 #endif
     outDirection = direction + gravity * timeDelta;
     outAngle = vec2(angle.x + angle.y * timeDelta, angle.y);
+#ifdef ANIMATESHEET      
+    float dist = cellInfos.y - cellInfos.x;
+    float ratio = clamp(mod(((outAge * cellInfos.z) / life), life), 0., 1.0);
+
+    outCellIndex = float(int(cellInfos.x + ratio * dist));
+#endif
   }
 }

+ 5 - 0
tests/nullEngine/app.js

@@ -162,6 +162,11 @@ var engine = new BABYLON.NullEngine();
 
 BABYLON.Tools.LogLevels = BABYLON.Tools.ErrorLogLevel & BABYLON.Tools.WarningLogLevel;
 const scene = new BABYLON.Scene(engine);
+BABYLON.ParticleHelper.CreateAsync("sun", scene)
+            .then((system) => {
+                console.log("ok");
+            });
+
 const camera = new BABYLON.ArcRotateCamera("camera", 0, 0, 10, BABYLON.Vector3.Zero(), scene);
 const mesh = BABYLON.MeshBuilder.CreateBox("box", {}, scene);
 mesh.position.set(0.5, 0.5, 0.5);

+ 0 - 59
tests/unit/babylon/src/Particles/babylon.particleHelper.tests.ts

@@ -1,59 +0,0 @@
-/**
- * Describes the test suite.
- */
-describe('Babylon Particle Helper', function () {
-  let subject: BABYLON.Engine;
-
-  /**
-   * Loads the dependencies.
-   */
-  before(function (done) {
-      this.timeout(180000);
-      (BABYLONDEVTOOLS).Loader
-          .useDist()
-          .load(function () {
-              // Force apply promise polyfill for consistent behavior between PhantomJS, IE11, and other browsers.
-              BABYLON.PromisePolyfill.Apply(true);
-              done();
-          });
-  });
-
-  /**
-   * Create a new engine subject before each test.
-   */
-  beforeEach(function () {
-      subject = new BABYLON.NullEngine({
-          renderHeight: 256,
-          renderWidth: 256,
-          textureSize: 256,
-          deterministicLockstep: false,
-          lockstepMaxSteps: 1
-      });
-  });
-
-  describe('#ParticleHelper.CreateAsync', () => {
-      let scene: BABYLON.Scene;
-
-      beforeEach(() => {
-        scene = new BABYLON.Scene(subject);
-      });
-
-      it('creates a sun particle system set', (done) => {
-        BABYLON.ParticleHelper.CreateAsync("fire", scene)
-            .then((system) => {
-                expect(system).to.be.instanceof(BABYLON.ParticleSystem);
-                done();
-            })
-            .catch((error) => { throw new Error("was not supposed to fail"); });
-      });
-
-      it('rejects the creation of the particle system', (done) => {
-        BABYLON.ParticleHelper.CreateAsync("test", scene)
-            .then(() => { throw new Error("was not supposed to succeed"); })
-            .catch((error) => {
-                expect(error).to.equals("An error occured while the creation of your particle system. Check if your type 'test' exists.");
-                done();
-            });
-    });
-  });
-});

+ 0 - 1
tests/unit/karma.conf.js

@@ -17,7 +17,6 @@ module.exports = function (config) {
             './tests/unit/babylon/serializers/babylon.glTFSerializer.tests.js',
             './tests/unit/babylon/src/babylon.node.tests.js',
             './tests/unit/babylon/src/Animations/babylon.animationGroup.tests.js',
-            './tests/unit/babylon/src/Helpers/babylon.particleHelper.tests.js',
             './tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.js',
             './tests/unit/babylon/src/PostProcess/babylon.postProcess.tests.js',
             './tests/unit/babylon/src/Material/babylon.material.tests.js',

BIN
tests/validation/ReferenceImages/particle helper.png


+ 6 - 0
tests/validation/config.json

@@ -2,6 +2,12 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
+      "title": "Particle helper",
+      "playgroundId": "#1VGT5D#2",
+      "renderCount": 50,
+      "referenceImage": "particle helper.png"
+    },       
+    {
       "title": "Chibi Rex",
       "playgroundId": "#QATUCH#4",
       "renderCount": 50,