Sfoglia il codice sorgente

decouple Math and engine and tools

David Catuhe 6 anni fa
parent
commit
d5ac6a269c

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

@@ -99,3 +99,4 @@
 
 ## Breaking changes
 - Setting mesh.scaling to a new vector will no longer automatically call forceUpdate (this should be done manually when needed) ([TrevorDev](https://github.com/TrevorDev))
+- `Tools.ExtractMinAndMaxIndexed` and `Tools.ExtractMinAndMax` are now ambiant functions (available on `BABYLON.extractMinAndMaxIndexed` and `BABYLON.extractMinAndMax`) ([Deltakosh](https://github.com/deltakosh/))

+ 1 - 1
gui/src/2D/adtInstrumentation.ts

@@ -1,7 +1,7 @@
 
 import { Nullable } from "babylonjs/types";
 import { Observer } from "babylonjs/Misc/observable";
-import { PerfCounter } from "babylonjs/Misc/tools";
+import { PerfCounter } from "babylonjs/Misc/perfCounter";
 import { IDisposable } from "babylonjs/scene";
 
 import { AdvancedDynamicTexture } from "./advancedDynamicTexture";

+ 2 - 3
src/Behaviors/Cameras/framingBehavior.ts

@@ -4,7 +4,6 @@ import { ArcRotateCamera } from "../../Cameras/arcRotateCamera";
 import { ExponentialEase, EasingFunction } from "../../Animations/easing";
 import { Nullable } from "../../types";
 import { PointerInfoPre, PointerEventTypes } from "../../Events/pointerEvents";
-import { Tools } from "../../Misc/tools";
 import { PrecisionDate } from "../../Misc/precisionDate";
 import { Observer } from "../../Misc/observable";
 import { AbstractMesh } from "../../Meshes/abstractMesh";
@@ -293,8 +292,8 @@ export class FramingBehavior implements Behavior<ArcRotateCamera> {
 
         for (let i = 0; i < meshes.length; i++) {
             let boundingInfo = meshes[i].getHierarchyBoundingVectors(true);
-            Tools.CheckExtends(boundingInfo.min, min, max);
-            Tools.CheckExtends(boundingInfo.max, min, max);
+            Vector3.CheckExtends(boundingInfo.min, min, max);
+            Vector3.CheckExtends(boundingInfo.max, min, max);
         }
 
         this.zoomOnBoundingInfo(min, max, focusOnOriginXZ, onAnimationEnd);

+ 46 - 58
src/Engines/engine.ts

@@ -2,12 +2,9 @@ import { Observer, Observable } from "../Misc/observable";
 import { PerformanceMonitor } from "../Misc/performanceMonitor";
 import { StringDictionary } from "../Misc/stringDictionary";
 import { PromisePolyfill } from "../Misc/promise";
-import { Tools, ICustomAnimationFrameRequester, PerfCounter, IFileRequest } from "../Misc/tools";
-import { Nullable, FloatArray, DataArray, IndicesArray } from "../types";
-import { Camera } from "../Cameras/camera";
+import { Tools, ICustomAnimationFrameRequester, IFileRequest } from "../Misc/tools";
+import { Nullable, FloatArray, DataArray, IndicesArray, float } from "../types";
 import { Scene } from "../scene";
-import { Matrix, Color3, Color4, Viewport, Vector4 } from "../Maths/math";
-import { Scalar } from "../Maths/math.scalar";
 import { VertexBuffer } from "../Meshes/buffer";
 import { UniformBuffer } from "../Materials/uniformBuffer";
 import { Effect, EffectCreationOptions, EffectFallbacks } from "../Materials/effect";
@@ -32,6 +29,7 @@ import { DataBuffer } from '../Meshes/dataBuffer';
 import { WebGLDataBuffer } from '../Meshes/WebGL/webGLDataBuffer';
 import { IShaderProcessor } from './Processors/iShaderProcessor';
 import { WebGL2ShaderProcessor } from './WebGL/webGL2ShaderProcessors';
+import { PerfCounter } from '../Misc/perfCounter';
 
 declare type Material = import("../Materials/material").Material;
 declare type PostProcess = import("../PostProcesses/postProcess").PostProcess;
@@ -40,6 +38,36 @@ declare type VideoTexture = import("../Materials/Textures/videoTexture").VideoTe
 declare type RenderTargetTexture = import("../Materials/Textures/renderTargetTexture").RenderTargetTexture;
 
 /**
+ * @hidden
+ */
+export interface IColor4Like {
+    r: float;
+    g: float;
+    b: float;
+    a: float;
+}
+
+/**
+ * @hidden
+ */
+export interface IViewportLike {
+    x: float;
+    y: float;
+    width: float;
+    height: float;
+}
+
+/**
+ * Defines the interface used by objects containing a viewport (like a camera)
+ */
+interface IViewportOwnerLike {
+    /**
+     * Gets or sets the viewport
+     */
+    viewport: IViewportLike;
+}
+
+/**
  * Keeps track of all the buffer info used in engine.
  */
 class BufferPointer {
@@ -847,7 +875,7 @@ export class Engine {
     private _compiledEffects: { [key: string]: Effect } = {};
     private _vertexAttribArraysEnabled: boolean[] = [];
     /** @hidden */
-    protected _cachedViewport: Nullable<Viewport>;
+    protected _cachedViewport: Nullable<IViewportLike>;
     private _cachedVertexArrayObject: Nullable<WebGLVertexArrayObject>;
     /** @hidden */
     protected _cachedVertexBuffers: any;
@@ -915,7 +943,7 @@ export class Engine {
     /**
      * Gets the current viewport
      */
-    public get currentViewport(): Nullable<Viewport> {
+    public get currentViewport(): Nullable<IViewportLike> {
         return this._cachedViewport;
     }
 
@@ -1638,12 +1666,12 @@ export class Engine {
 
     /**
      * Gets current aspect ratio
-     * @param camera defines the camera to use to get the aspect ratio
+     * @param viewportOwner defines the camera to use to get the aspect ratio
      * @param useScreen defines if screen size must be used (or the current render target if any)
      * @returns a number defining the aspect ratio
      */
-    public getAspectRatio(camera: Camera, useScreen = false): number {
-        var viewport = camera.viewport;
+    public getAspectRatio(viewportOwner: IViewportOwnerLike, useScreen = false): number {
+        var viewport = viewportOwner.viewport;
         return (this.getRenderWidth(useScreen) * viewport.width) / (this.getRenderHeight(useScreen) * viewport.height);
     }
 
@@ -2097,7 +2125,7 @@ export class Engine {
      * @param depth defines if the depth buffer must be cleared
      * @param stencil defines if the stencil buffer must be cleared
      */
-    public clear(color: Nullable<Color4>, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
+    public clear(color: Nullable<IColor4Like>, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
         this.applyStates();
 
         var mode = 0;
@@ -2124,7 +2152,7 @@ export class Engine {
      * @param height defines the height of the clear rectangle
      * @param clearColor defines the clear color
      */
-    public scissorClear(x: number, y: number, width: number, height: number, clearColor: Color4): void {
+    public scissorClear(x: number, y: number, width: number, height: number, clearColor: IColor4Like): void {
         this.enableScissor(x, y, width, height);
         this.clear(clearColor, true, true, true);
         this.disableScissor();
@@ -2154,7 +2182,7 @@ export class Engine {
         gl.disable(gl.SCISSOR_TEST);
     }
 
-    private _viewportCached = new Vector4(0, 0, 0, 0);
+    private _viewportCached = { x: 0, y: 0, z: 0, w: 0 };
 
     /** @hidden */
     public _viewport(x: number, y: number, width: number, height: number): void {
@@ -2177,7 +2205,7 @@ export class Engine {
      * @param requiredWidth defines the width required for rendering. If not provided the rendering canvas' width is used
      * @param requiredHeight defines the height required for rendering. If not provided the rendering canvas' height is used
      */
-    public setViewport(viewport: Viewport, requiredWidth?: number, requiredHeight?: number): void {
+    public setViewport(viewport: IViewportLike, requiredWidth?: number, requiredHeight?: number): void {
         var width = requiredWidth || this.getRenderWidth();
         var height = requiredHeight || this.getRenderHeight();
         var x = viewport.x || 0;
@@ -2196,7 +2224,7 @@ export class Engine {
      * @param height defines the height of the viewport (in screen space)
      * @return the current viewport Object (if any) that is being replaced by this call. You can restore this viewport later on to go back to the original state
      */
-    public setDirectViewport(x: number, y: number, width: number, height: number): Nullable<Viewport> {
+    public setDirectViewport(x: number, y: number, width: number, height: number): Nullable<IViewportLike> {
         let currentViewport = this._cachedViewport;
         this._cachedViewport = null;
 
@@ -3652,19 +3680,6 @@ export class Engine {
     }
 
     /**
-     * Set the value of an uniform to a matrix
-     * @param uniform defines the webGL uniform location where to store the value
-     * @param matrix defines the matrix to store
-     */
-    public setMatrix(uniform: Nullable<WebGLUniformLocation>, matrix: Matrix): void {
-        if (!uniform) {
-            return;
-        }
-
-        this._gl.uniformMatrix4fv(uniform, false, matrix.toArray() as Float32Array);
-    }
-
-    /**
      * Set the value of an uniform to a matrix (3x3)
      * @param uniform defines the webGL uniform location where to store the value
      * @param matrix defines the Float32Array representing the 3x3 matrix to store
@@ -3775,38 +3790,11 @@ export class Engine {
     }
 
     /**
-     * Set the value of an uniform to a Color3
-     * @param uniform defines the webGL uniform location where to store the value
-     * @param color3 defines the color to store
-     */
-    public setColor3(uniform: Nullable<WebGLUniformLocation>, color3: Color3): void {
-        if (!uniform) {
-            return;
-        }
-
-        this._gl.uniform3f(uniform, color3.r, color3.g, color3.b);
-    }
-
-    /**
-     * Set the value of an uniform to a Color3 and an alpha value
-     * @param uniform defines the webGL uniform location where to store the value
-     * @param color3 defines the color to store
-     * @param alpha defines the alpha component to store
-     */
-    public setColor4(uniform: Nullable<WebGLUniformLocation>, color3: Color3, alpha: number): void {
-        if (!uniform) {
-            return;
-        }
-
-        this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
-    }
-
-    /**
      * Sets a Color4 on a uniform variable
      * @param uniform defines the uniform location
      * @param color4 defines the value to be set
      */
-    public setDirectColor4(uniform: Nullable<WebGLUniformLocation>, color4: Color4): void {
+    public setDirectColor4(uniform: Nullable<WebGLUniformLocation>, color4: IColor4Like): void {
         if (!uniform) {
             return;
         }
@@ -5018,8 +5006,8 @@ export class Engine {
             target = gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex;
         }
 
-        const lodMaxWidth = Math.round(Scalar.Log2(texture.width));
-        const lodMaxHeight = Math.round(Scalar.Log2(texture.height));
+        const lodMaxWidth = Math.round(Math.log(texture.width) * Math.LOG2E);
+        const lodMaxHeight = Math.round(Math.log(texture.height) * Math.LOG2E);
         const width = useTextureWidthAndHeight ? texture.width : Math.pow(2, Math.max(lodMaxWidth - lod, 0));
         const height = useTextureWidthAndHeight ? texture.height : Math.pow(2, Math.max(lodMaxHeight - lod, 0));
 

+ 3 - 13
src/Engines/nullEngine.ts

@@ -1,8 +1,7 @@
 import { Logger } from "../Misc/logger";
 import { Nullable, FloatArray, IndicesArray } from "../types";
 import { Scene } from "../scene";
-import { Matrix, Color3, Color4, Viewport } from "../Maths/math";
-import { Engine, EngineCapabilities } from "../Engines/engine";
+import { Engine, EngineCapabilities, IViewportLike, IColor4Like } from "../Engines/engine";
 import { RenderTargetCreationOptions } from "../Materials/Textures/renderTargetCreationOptions";
 import { VertexBuffer } from "../Meshes/buffer";
 import { InternalTexture } from "../Materials/Textures/internalTexture";
@@ -158,7 +157,7 @@ export class NullEngine extends Engine {
         return buffer;
     }
 
-    public clear(color: Color4, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
+    public clear(color: IColor4Like, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
     }
 
     public getRenderWidth(useScreen = false): number {
@@ -177,7 +176,7 @@ export class NullEngine extends Engine {
         return this._options.renderHeight;
     }
 
-    public setViewport(viewport: Viewport, requiredWidth?: number, requiredHeight?: number): void {
+    public setViewport(viewport: IViewportLike, requiredWidth?: number, requiredHeight?: number): void {
         this._cachedViewport = viewport;
     }
 
@@ -252,9 +251,6 @@ export class NullEngine extends Engine {
     public setMatrices(uniform: WebGLUniformLocation, matrices: Float32Array): void {
     }
 
-    public setMatrix(uniform: WebGLUniformLocation, matrix: Matrix): void {
-    }
-
     public setMatrix3x3(uniform: WebGLUniformLocation, matrix: Float32Array): void {
     }
 
@@ -276,12 +272,6 @@ export class NullEngine extends Engine {
     public setFloat4(uniform: WebGLUniformLocation, x: number, y: number, z: number, w: number): void {
     }
 
-    public setColor3(uniform: WebGLUniformLocation, color3: Color3): void {
-    }
-
-    public setColor4(uniform: WebGLUniformLocation, color3: Color3, alpha: number): void {
-    }
-
     public setAlphaMode(mode: number, noDepthWriteChange: boolean = false): void {
         if (this._alphaMode === mode) {
             return;

+ 1 - 1
src/Instrumentation/engineInstrumentation.ts

@@ -1,5 +1,5 @@
 import { Observer } from "../Misc/observable";
-import { PerfCounter } from "../Misc/tools";
+import { PerfCounter } from "../Misc/perfCounter";
 import { Nullable } from "../types";
 import { IDisposable } from "../scene";
 import { Engine } from "../Engines/engine";

+ 2 - 1
src/Instrumentation/sceneInstrumentation.ts

@@ -1,10 +1,11 @@
-import { Tools, PerfCounter } from "../Misc/tools";
+import { Tools } from "../Misc/tools";
 import { Observer } from "../Misc/observable";
 import { Nullable } from "../types";
 import { Camera } from "../Cameras/camera";
 import { Scene, IDisposable } from "../scene";
 import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
+import { PerfCounter } from '../Misc/perfCounter';
 /**
  * This class can be used to get instrumentation data from a Babylon engine
  * @see http://doc.babylonjs.com/how_to/optimizing_your_scene#sceneinstrumentation

+ 3 - 3
src/Materials/effect.ts

@@ -1194,7 +1194,7 @@ export class Effect implements IDisposable {
      */
     public setMatrix(uniformName: string, matrix: Matrix): Effect {
         if (this._cacheMatrix(uniformName, matrix)) {
-            this._engine.setMatrix(this._uniforms[uniformName], matrix);
+            this._engine.setMatrices(this._uniforms[uniformName], matrix.toArray() as Float32Array);
         }
         return this;
     }
@@ -1356,7 +1356,7 @@ export class Effect implements IDisposable {
     public setColor3(uniformName: string, color3: Color3): Effect {
 
         if (this._cacheFloat3(uniformName, color3.r, color3.g, color3.b)) {
-            this._engine.setColor3(this._uniforms[uniformName], color3);
+            this._engine.setFloat3(this._uniforms[uniformName], color3.r, color3.g, color3.b);
         }
         return this;
     }
@@ -1370,7 +1370,7 @@ export class Effect implements IDisposable {
      */
     public setColor4(uniformName: string, color3: Color3, alpha: number): Effect {
         if (this._cacheFloat4(uniformName, color3.r, color3.g, color3.b, alpha)) {
-            this._engine.setColor4(this._uniforms[uniformName], color3, alpha);
+            this._engine.setFloat4(this._uniforms[uniformName], color3.r, color3.g, color3.b, alpha);
         }
         return this;
     }

+ 79 - 0
src/Maths/math.functions.ts

@@ -0,0 +1,79 @@
+import { FloatArray, Nullable, IndicesArray } from '../types';
+import { Vector2, Vector3 } from './math';
+
+/**
+ * Extracts minimum and maximum values from a list of indexed positions
+ * @param positions defines the positions to use
+ * @param indices defines the indices to the positions
+ * @param indexStart defines the start index
+ * @param indexCount defines the end index
+ * @param bias defines bias value to add to the result
+ * @return minimum and maximum values
+ */
+export function extractMinAndMaxIndexed(positions: FloatArray, indices: IndicesArray, indexStart: number, indexCount: number, bias: Nullable<Vector2> = null): { minimum: Vector3; maximum: Vector3 } {
+    var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+    var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+
+    for (var index = indexStart; index < indexStart + indexCount; index++) {
+        const offset = indices[index] * 3;
+        const x = positions[offset];
+        const y = positions[offset + 1];
+        const z = positions[offset + 2];
+        minimum.minimizeInPlaceFromFloats(x, y, z);
+        maximum.maximizeInPlaceFromFloats(x, y, z);
+    }
+
+    if (bias) {
+        minimum.x -= minimum.x * bias.x + bias.y;
+        minimum.y -= minimum.y * bias.x + bias.y;
+        minimum.z -= minimum.z * bias.x + bias.y;
+        maximum.x += maximum.x * bias.x + bias.y;
+        maximum.y += maximum.y * bias.x + bias.y;
+        maximum.z += maximum.z * bias.x + bias.y;
+    }
+
+    return {
+        minimum: minimum,
+        maximum: maximum
+    };
+}
+
+/**
+ * Extracts minimum and maximum values from a list of positions
+ * @param positions defines the positions to use
+ * @param start defines the start index in the positions array
+ * @param count defines the number of positions to handle
+ * @param bias defines bias value to add to the result
+ * @param stride defines the stride size to use (distance between two positions in the positions array)
+ * @return minimum and maximum values
+ */
+export function extractMinAndMax(positions: FloatArray, start: number, count: number, bias: Nullable<Vector2> = null, stride?: number): { minimum: Vector3; maximum: Vector3 } {
+    var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+    var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+
+    if (!stride) {
+        stride = 3;
+    }
+
+    for (var index = start, offset = start * stride; index < start + count; index++ , offset += stride) {
+        const x = positions[offset];
+        const y = positions[offset + 1];
+        const z = positions[offset + 2];
+        minimum.minimizeInPlaceFromFloats(x, y, z);
+        maximum.maximizeInPlaceFromFloats(x, y, z);
+    }
+
+    if (bias) {
+        minimum.x -= minimum.x * bias.x + bias.y;
+        minimum.y -= minimum.y * bias.x + bias.y;
+        minimum.z -= minimum.z * bias.x + bias.y;
+        maximum.x += maximum.x * bias.x + bias.y;
+        maximum.y += maximum.y * bias.x + bias.y;
+        maximum.z += maximum.z * bias.x + bias.y;
+    }
+
+    return {
+        minimum: minimum,
+        maximum: maximum
+    };
+}

+ 11 - 0
src/Maths/math.ts

@@ -2560,6 +2560,17 @@ export class Vector3 {
     }
 
     /**
+     * Checks if a given vector is inside a specific range
+     * @param v defines the vector to test
+     * @param min defines the minimum range
+     * @param max defines the maximum range
+     */
+    public static CheckExtends(v: Vector3, min: Vector3, max: Vector3): void {
+        min.minimizeInPlace(v);
+        max.maximizeInPlace(v);
+    }
+
+    /**
      * Returns a new Vector3 located for "amount" (float) on the Hermite interpolation spline defined by the vectors "value1", "tangent1", "value2", "tangent2"
      * @param value1 defines the first control point
      * @param tangent1 defines the first tangent vector

+ 2 - 1
src/Meshes/abstractMesh.ts

@@ -24,6 +24,7 @@ import { AbstractActionManager } from '../Actions/abstractActionManager';
 import { _MeshCollisionData } from '../Collisions/meshCollisionData';
 import { _DevTools } from '../Misc/devTools';
 import { RawTexture } from '../Materials/Textures/rawTexture';
+import { extractMinAndMax } from '../Maths/math.functions';
 
 declare type Ray = import("../Culling/ray").Ray;
 declare type Collider = import("../Collisions/collider").Collider;
@@ -1142,7 +1143,7 @@ export class AbstractMesh extends TransformNode implements IDisposable, ICullabl
     /** @hidden */
     public _refreshBoundingInfo(data: Nullable<FloatArray>, bias: Nullable<Vector2>): void {
         if (data) {
-            var extend = Tools.ExtractMinAndMax(data, 0, this.getTotalVertices(), bias);
+            var extend = extractMinAndMax(data, 0, this.getTotalVertices(), bias);
             if (this._boundingInfo) {
                 this._boundingInfo.reConstruct(extend.minimum, extend.maximum);
             }

+ 2 - 1
src/Meshes/geometry.ts

@@ -13,6 +13,7 @@ import { Constants } from "../Engines/constants";
 import { Tools } from "../Misc/tools";
 import { Tags } from "../Misc/tags";
 import { DataBuffer } from './dataBuffer';
+import { extractMinAndMax } from '../Maths/math.functions';
 
 declare type Mesh = import("../Meshes/mesh").Mesh;
 
@@ -693,7 +694,7 @@ export class Geometry implements IGetSetVerticesData {
             data = this.getVerticesData(VertexBuffer.PositionKind)!;
         }
 
-        this._extend = Tools.ExtractMinAndMax(data, 0, this._totalVertices, this.boundingBias, 3);
+        this._extend = extractMinAndMax(data, 0, this._totalVertices, this.boundingBias, 3);
     }
 
     private _applyToMesh(mesh: Mesh): void {

+ 2 - 2
src/Meshes/subMesh.ts

@@ -1,4 +1,3 @@
-import { Tools } from "../Misc/tools";
 import { Nullable, IndicesArray, DeepImmutable, FloatArray } from "../types";
 import { Matrix, Vector3, Plane } from "../Maths/math";
 import { Engine } from "../Engines/engine";
@@ -8,6 +7,7 @@ import { ICullable, BoundingInfo } from "../Culling/boundingInfo";
 import { Effect } from "../Materials/effect";
 import { Constants } from "../Engines/constants";
 import { DataBuffer } from './dataBuffer';
+import { extractMinAndMaxIndexed } from '../Maths/math.functions';
 
 declare type Collider = import("../Collisions/collider").Collider;
 declare type Material = import("../Materials/material").Material;
@@ -235,7 +235,7 @@ export class SubMesh extends BaseSubMesh implements ICullable {
             //the rendering mesh's bounding info can be used, it is the standard submesh for all indices.
             extend = { minimum: boundingInfo.minimum.clone(), maximum: boundingInfo.maximum.clone() };
         } else {
-            extend = Tools.ExtractMinAndMaxIndexed(data, indices, this.indexStart, this.indexCount, this._renderingMesh.geometry.boundingBias);
+            extend = extractMinAndMaxIndexed(data, indices, this.indexStart, this.indexCount, this._renderingMesh.geometry.boundingBias);
         }
 
         if (this._boundingInfo) {

+ 108 - 0
src/Misc/gradients.ts

@@ -0,0 +1,108 @@
+import { Color3, Color4 } from '../Maths/math';
+
+/** Interface used by value gradients (color, factor, ...) */
+export interface IValueGradient {
+    /**
+     * Gets or sets the gradient value (between 0 and 1)
+     */
+    gradient: number;
+}
+
+/** Class used to store color4 gradient */
+export class ColorGradient implements IValueGradient {
+    /**
+     * Gets or sets the gradient value (between 0 and 1)
+     */
+    public gradient: number;
+    /**
+     * Gets or sets first associated color
+     */
+    public color1: Color4;
+    /**
+     * Gets or sets second associated color
+     */
+    public color2?: Color4;
+
+    /**
+     * Will get a color picked randomly between color1 and color2.
+     * If color2 is undefined then color1 will be used
+     * @param result defines the target Color4 to store the result in
+     */
+    public getColorToRef(result: Color4) {
+        if (!this.color2) {
+            result.copyFrom(this.color1);
+            return;
+        }
+
+        Color4.LerpToRef(this.color1, this.color2, Math.random(), result);
+    }
+}
+
+/** Class used to store color 3 gradient */
+export class Color3Gradient implements IValueGradient {
+    /**
+     * Gets or sets the gradient value (between 0 and 1)
+     */
+    public gradient: number;
+    /**
+     * Gets or sets the associated color
+     */
+    public color: Color3;
+}
+
+/** Class used to store factor gradient */
+export class FactorGradient implements IValueGradient {
+    /**
+     * Gets or sets the gradient value (between 0 and 1)
+     */
+    public gradient: number;
+    /**
+     * Gets or sets first associated factor
+     */
+    public factor1: number;
+    /**
+     * Gets or sets second associated factor
+     */
+    public factor2?: number;
+
+    /**
+     * Will get a number picked randomly between factor1 and factor2.
+     * If factor2 is undefined then factor1 will be used
+     * @returns the picked number
+     */
+    public getFactor(): number {
+        if (this.factor2 === undefined) {
+            return this.factor1;
+        }
+
+        return this.factor1 + ((this.factor2 - this.factor1) * Math.random());
+    }
+}
+
+/**
+ * Helper used to simplify some generic gradient tasks
+ */
+export class GradientHelper {
+    /**
+     * Gets the current gradient from an array of IValueGradient
+     * @param ratio defines the current ratio to get
+     * @param gradients defines the array of IValueGradient
+     * @param updateFunc defines the callback function used to get the final value from the selected gradients
+     */
+    public static GetCurrentGradient(ratio: number, gradients: IValueGradient[], updateFunc: (current: IValueGradient, next: IValueGradient, scale: number) => void) {
+        for (var gradientIndex = 0; gradientIndex < gradients.length - 1; gradientIndex++) {
+            let currentGradient = gradients[gradientIndex];
+            let nextGradient = gradients[gradientIndex + 1];
+
+            if (ratio >= currentGradient.gradient && ratio <= nextGradient.gradient) {
+                let scale = (ratio - currentGradient.gradient) / (nextGradient.gradient - currentGradient.gradient);
+                updateFunc(currentGradient, nextGradient, scale);
+                return;
+            }
+        }
+
+        // Use last index if over
+        const lastIndex = gradients.length - 1;
+        updateFunc(gradients[lastIndex], gradients[lastIndex], 1.0);
+    }
+}

+ 2 - 0
src/Misc/index.ts

@@ -33,3 +33,5 @@ export * from "./typeStore";
 export * from "./webRequest";
 export * from "./iInspectable";
 export * from "./brdfTextureTools";
+export * from "./gradients";
+export * from "./perfCounter";

+ 169 - 0
src/Misc/perfCounter.ts

@@ -0,0 +1,169 @@
+import { PrecisionDate } from './precisionDate';
+
+/**
+ * This class is used to track a performance counter which is number based.
+ * The user has access to many properties which give statistics of different nature.
+ *
+ * The implementer can track two kinds of Performance Counter: time and count.
+ * For time you can optionally call fetchNewFrame() to notify the start of a new frame to monitor, then call beginMonitoring() to start and endMonitoring() to record the lapsed time. endMonitoring takes a newFrame parameter for you to specify if the monitored time should be set for a new frame or accumulated to the current frame being monitored.
+ * For count you first have to call fetchNewFrame() to notify the start of a new frame to monitor, then call addCount() how many time required to increment the count value you monitor.
+ */
+export class PerfCounter {
+    /**
+     * Gets or sets a global boolean to turn on and off all the counters
+     */
+    public static Enabled = true;
+
+    /**
+     * Returns the smallest value ever
+     */
+    public get min(): number {
+        return this._min;
+    }
+
+    /**
+     * Returns the biggest value ever
+     */
+    public get max(): number {
+        return this._max;
+    }
+
+    /**
+     * Returns the average value since the performance counter is running
+     */
+    public get average(): number {
+        return this._average;
+    }
+
+    /**
+     * Returns the average value of the last second the counter was monitored
+     */
+    public get lastSecAverage(): number {
+        return this._lastSecAverage;
+    }
+
+    /**
+     * Returns the current value
+     */
+    public get current(): number {
+        return this._current;
+    }
+
+    /**
+     * Gets the accumulated total
+     */
+    public get total(): number {
+        return this._totalAccumulated;
+    }
+
+    /**
+     * Gets the total value count
+     */
+    public get count(): number {
+        return this._totalValueCount;
+    }
+
+    /**
+     * Creates a new counter
+     */
+    constructor() {
+        this._startMonitoringTime = 0;
+        this._min = 0;
+        this._max = 0;
+        this._average = 0;
+        this._lastSecAverage = 0;
+        this._current = 0;
+        this._totalValueCount = 0;
+        this._totalAccumulated = 0;
+        this._lastSecAccumulated = 0;
+        this._lastSecTime = 0;
+        this._lastSecValueCount = 0;
+    }
+
+    /**
+     * Call this method to start monitoring a new frame.
+     * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
+     */
+    public fetchNewFrame() {
+        this._totalValueCount++;
+        this._current = 0;
+        this._lastSecValueCount++;
+    }
+
+    /**
+     * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
+     * @param newCount the count value to add to the monitored count
+     * @param fetchResult true when it's the last time in the frame you add to the counter and you wish to update the statistics properties (min/max/average), false if you only want to update statistics.
+     */
+    public addCount(newCount: number, fetchResult: boolean) {
+        if (!PerfCounter.Enabled) {
+            return;
+        }
+        this._current += newCount;
+        if (fetchResult) {
+            this._fetchResult();
+        }
+    }
+
+    /**
+     * Start monitoring this performance counter
+     */
+    public beginMonitoring() {
+        if (!PerfCounter.Enabled) {
+            return;
+        }
+        this._startMonitoringTime = PrecisionDate.Now;
+    }
+
+    /**
+     * Compute the time lapsed since the previous beginMonitoring() call.
+     * @param newFrame true by default to fetch the result and monitor a new frame, if false the time monitored will be added to the current frame counter
+     */
+    public endMonitoring(newFrame: boolean = true) {
+        if (!PerfCounter.Enabled) {
+            return;
+        }
+
+        if (newFrame) {
+            this.fetchNewFrame();
+        }
+
+        let currentTime = PrecisionDate.Now;
+        this._current = currentTime - this._startMonitoringTime;
+
+        if (newFrame) {
+            this._fetchResult();
+        }
+    }
+
+    private _fetchResult() {
+        this._totalAccumulated += this._current;
+        this._lastSecAccumulated += this._current;
+
+        // Min/Max update
+        this._min = Math.min(this._min, this._current);
+        this._max = Math.max(this._max, this._current);
+        this._average = this._totalAccumulated / this._totalValueCount;
+
+        // Reset last sec?
+        let now = PrecisionDate.Now;
+        if ((now - this._lastSecTime) > 1000) {
+            this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
+            this._lastSecTime = now;
+            this._lastSecAccumulated = 0;
+            this._lastSecValueCount = 0;
+        }
+    }
+
+    private _startMonitoringTime: number;
+    private _min: number;
+    private _max: number;
+    private _average: number;
+    private _current: number;
+    private _totalValueCount: number;
+    private _totalAccumulated: number;
+    private _lastSecAverage: number;
+    private _lastSecAccumulated: number;
+    private _lastSecTime: number;
+    private _lastSecValueCount: number;
+}

+ 9 - 362
src/Misc/tools.ts

@@ -1,6 +1,4 @@
-import { FloatArray, IndicesArray, Nullable } from "../types";
-import { Color4, Color3, Vector2, Vector3 } from "../Maths/math";
-import { Scalar } from "../Maths/math.scalar";
+import { Nullable, float } from "../types";
 import { IOfflineProvider } from "../Offline/IOfflineProvider";
 import { Observable } from "./observable";
 import { FilesInputStore } from "./filesInputStore";
@@ -17,6 +15,13 @@ declare type Camera = import("../Cameras/camera").Camera;
 declare type Engine = import("../Engines/engine").Engine;
 declare type Animation = import("../Animations/animation").Animation;
 
+interface IColor4Like {
+    r: float;
+    g: float;
+    b: float;
+    a: float;
+}
+
 /**
  * Interface for any object that can request an animation frame
  */
@@ -47,85 +52,6 @@ export interface IAnimatable {
     animations: Nullable<Array<Animation>>;
 }
 
-/** Interface used by value gradients (color, factor, ...) */
-export interface IValueGradient {
-    /**
-     * Gets or sets the gradient value (between 0 and 1)
-     */
-    gradient: number;
-}
-
-/** Class used to store color4 gradient */
-export class ColorGradient implements IValueGradient {
-    /**
-     * Gets or sets the gradient value (between 0 and 1)
-     */
-    public gradient: number;
-    /**
-     * Gets or sets first associated color
-     */
-    public color1: Color4;
-    /**
-     * Gets or sets second associated color
-     */
-    public color2?: Color4;
-
-    /**
-     * Will get a color picked randomly between color1 and color2.
-     * If color2 is undefined then color1 will be used
-     * @param result defines the target Color4 to store the result in
-     */
-    public getColorToRef(result: Color4) {
-        if (!this.color2) {
-            result.copyFrom(this.color1);
-            return;
-        }
-
-        Color4.LerpToRef(this.color1, this.color2, Math.random(), result);
-    }
-}
-
-/** Class used to store color 3 gradient */
-export class Color3Gradient implements IValueGradient {
-    /**
-     * Gets or sets the gradient value (between 0 and 1)
-     */
-    public gradient: number;
-    /**
-     * Gets or sets the associated color
-     */
-    public color: Color3;
-}
-
-/** Class used to store factor gradient */
-export class FactorGradient implements IValueGradient {
-    /**
-     * Gets or sets the gradient value (between 0 and 1)
-     */
-    public gradient: number;
-    /**
-     * Gets or sets first associated factor
-     */
-    public factor1: number;
-    /**
-     * Gets or sets second associated factor
-     */
-    public factor2?: number;
-
-    /**
-     * Will get a number picked randomly between factor1 and factor2.
-     * If factor2 is undefined then factor1 will be used
-     * @returns the picked number
-     */
-    public getFactor(): number {
-        if (this.factor2 === undefined) {
-            return this.factor1;
-        }
-
-        return Scalar.Lerp(this.factor1, this.factor2, Math.random());
-    }
-}
-
 /**
  * @ignore
  * Application error to support additional information when loading a file
@@ -252,7 +178,7 @@ export class Tools {
      * @param pixels defines the source byte array
      * @param color defines the output color
      */
-    public static FetchToRef(u: number, v: number, width: number, height: number, pixels: Uint8Array, color: Color4): void {
+    public static FetchToRef(u: number, v: number, width: number, height: number, pixels: Uint8Array, color: IColor4Like): void {
         let wrappedU = ((Math.abs(u) * width) % width) | 0;
         let wrappedV = ((Math.abs(v) * height) % height) | 0;
 
@@ -521,83 +447,6 @@ export class Tools {
     }
 
     /**
-     * Extracts minimum and maximum values from a list of indexed positions
-     * @param positions defines the positions to use
-     * @param indices defines the indices to the positions
-     * @param indexStart defines the start index
-     * @param indexCount defines the end index
-     * @param bias defines bias value to add to the result
-     * @return minimum and maximum values
-     */
-    public static ExtractMinAndMaxIndexed(positions: FloatArray, indices: IndicesArray, indexStart: number, indexCount: number, bias: Nullable<Vector2> = null): { minimum: Vector3; maximum: Vector3 } {
-        var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
-        var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
-
-        for (var index = indexStart; index < indexStart + indexCount; index++) {
-            const offset = indices[index] * 3;
-            const x = positions[offset];
-            const y = positions[offset + 1];
-            const z = positions[offset + 2];
-            minimum.minimizeInPlaceFromFloats(x, y, z);
-            maximum.maximizeInPlaceFromFloats(x, y, z);
-        }
-
-        if (bias) {
-            minimum.x -= minimum.x * bias.x + bias.y;
-            minimum.y -= minimum.y * bias.x + bias.y;
-            minimum.z -= minimum.z * bias.x + bias.y;
-            maximum.x += maximum.x * bias.x + bias.y;
-            maximum.y += maximum.y * bias.x + bias.y;
-            maximum.z += maximum.z * bias.x + bias.y;
-        }
-
-        return {
-            minimum: minimum,
-            maximum: maximum
-        };
-    }
-
-    /**
-     * Extracts minimum and maximum values from a list of positions
-     * @param positions defines the positions to use
-     * @param start defines the start index in the positions array
-     * @param count defines the number of positions to handle
-     * @param bias defines bias value to add to the result
-     * @param stride defines the stride size to use (distance between two positions in the positions array)
-     * @return minimum and maximum values
-     */
-    public static ExtractMinAndMax(positions: FloatArray, start: number, count: number, bias: Nullable<Vector2> = null, stride?: number): { minimum: Vector3; maximum: Vector3 } {
-        var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
-        var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
-
-        if (!stride) {
-            stride = 3;
-        }
-
-        for (var index = start, offset = start * stride; index < start + count; index++ , offset += stride) {
-            const x = positions[offset];
-            const y = positions[offset + 1];
-            const z = positions[offset + 2];
-            minimum.minimizeInPlaceFromFloats(x, y, z);
-            maximum.maximizeInPlaceFromFloats(x, y, z);
-        }
-
-        if (bias) {
-            minimum.x -= minimum.x * bias.x + bias.y;
-            minimum.y -= minimum.y * bias.x + bias.y;
-            minimum.z -= minimum.z * bias.x + bias.y;
-            maximum.x += maximum.x * bias.x + bias.y;
-            maximum.y += maximum.y * bias.x + bias.y;
-            maximum.z += maximum.z * bias.x + bias.y;
-        }
-
-        return {
-            minimum: minimum,
-            maximum: maximum
-        };
-    }
-
-    /**
      * Returns an array if obj is not an array
      * @param obj defines the object to evaluate as an array
      * @param allowsNullUndefined defines a boolean indicating if obj is allowed to be null or undefined
@@ -1187,17 +1036,6 @@ export class Tools {
     }
 
     /**
-     * Checks if a given vector is inside a specific range
-     * @param v defines the vector to test
-     * @param min defines the minimum range
-     * @param max defines the maximum range
-     */
-    public static CheckExtends(v: Vector3, min: Vector3, max: Vector3): void {
-        min.minimizeInPlace(v);
-        max.maximizeInPlace(v);
-    }
-
-    /**
      * Tries to copy an object by duplicating every property
      * @param source defines the source object
      * @param destination defines the target object
@@ -1805,197 +1643,6 @@ export class Tools {
             }, delay);
         });
     }
-
-    /**
-     * Gets the current gradient from an array of IValueGradient
-     * @param ratio defines the current ratio to get
-     * @param gradients defines the array of IValueGradient
-     * @param updateFunc defines the callback function used to get the final value from the selected gradients
-     */
-    public static GetCurrentGradient(ratio: number, gradients: IValueGradient[], updateFunc: (current: IValueGradient, next: IValueGradient, scale: number) => void) {
-        for (var gradientIndex = 0; gradientIndex < gradients.length - 1; gradientIndex++) {
-            let currentGradient = gradients[gradientIndex];
-            let nextGradient = gradients[gradientIndex + 1];
-
-            if (ratio >= currentGradient.gradient && ratio <= nextGradient.gradient) {
-                let scale = (ratio - currentGradient.gradient) / (nextGradient.gradient - currentGradient.gradient);
-                updateFunc(currentGradient, nextGradient, scale);
-                return;
-            }
-        }
-
-        // Use last index if over
-        const lastIndex = gradients.length - 1;
-        updateFunc(gradients[lastIndex], gradients[lastIndex], 1.0);
-    }
-}
-
-/**
- * This class is used to track a performance counter which is number based.
- * The user has access to many properties which give statistics of different nature.
- *
- * The implementer can track two kinds of Performance Counter: time and count.
- * For time you can optionally call fetchNewFrame() to notify the start of a new frame to monitor, then call beginMonitoring() to start and endMonitoring() to record the lapsed time. endMonitoring takes a newFrame parameter for you to specify if the monitored time should be set for a new frame or accumulated to the current frame being monitored.
- * For count you first have to call fetchNewFrame() to notify the start of a new frame to monitor, then call addCount() how many time required to increment the count value you monitor.
- */
-export class PerfCounter {
-    /**
-     * Gets or sets a global boolean to turn on and off all the counters
-     */
-    public static Enabled = true;
-
-    /**
-     * Returns the smallest value ever
-     */
-    public get min(): number {
-        return this._min;
-    }
-
-    /**
-     * Returns the biggest value ever
-     */
-    public get max(): number {
-        return this._max;
-    }
-
-    /**
-     * Returns the average value since the performance counter is running
-     */
-    public get average(): number {
-        return this._average;
-    }
-
-    /**
-     * Returns the average value of the last second the counter was monitored
-     */
-    public get lastSecAverage(): number {
-        return this._lastSecAverage;
-    }
-
-    /**
-     * Returns the current value
-     */
-    public get current(): number {
-        return this._current;
-    }
-
-    /**
-     * Gets the accumulated total
-     */
-    public get total(): number {
-        return this._totalAccumulated;
-    }
-
-    /**
-     * Gets the total value count
-     */
-    public get count(): number {
-        return this._totalValueCount;
-    }
-
-    /**
-     * Creates a new counter
-     */
-    constructor() {
-        this._startMonitoringTime = 0;
-        this._min = 0;
-        this._max = 0;
-        this._average = 0;
-        this._lastSecAverage = 0;
-        this._current = 0;
-        this._totalValueCount = 0;
-        this._totalAccumulated = 0;
-        this._lastSecAccumulated = 0;
-        this._lastSecTime = 0;
-        this._lastSecValueCount = 0;
-    }
-
-    /**
-     * Call this method to start monitoring a new frame.
-     * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
-     */
-    public fetchNewFrame() {
-        this._totalValueCount++;
-        this._current = 0;
-        this._lastSecValueCount++;
-    }
-
-    /**
-     * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
-     * @param newCount the count value to add to the monitored count
-     * @param fetchResult true when it's the last time in the frame you add to the counter and you wish to update the statistics properties (min/max/average), false if you only want to update statistics.
-     */
-    public addCount(newCount: number, fetchResult: boolean) {
-        if (!PerfCounter.Enabled) {
-            return;
-        }
-        this._current += newCount;
-        if (fetchResult) {
-            this._fetchResult();
-        }
-    }
-
-    /**
-     * Start monitoring this performance counter
-     */
-    public beginMonitoring() {
-        if (!PerfCounter.Enabled) {
-            return;
-        }
-        this._startMonitoringTime = PrecisionDate.Now;
-    }
-
-    /**
-     * Compute the time lapsed since the previous beginMonitoring() call.
-     * @param newFrame true by default to fetch the result and monitor a new frame, if false the time monitored will be added to the current frame counter
-     */
-    public endMonitoring(newFrame: boolean = true) {
-        if (!PerfCounter.Enabled) {
-            return;
-        }
-
-        if (newFrame) {
-            this.fetchNewFrame();
-        }
-
-        let currentTime = PrecisionDate.Now;
-        this._current = currentTime - this._startMonitoringTime;
-
-        if (newFrame) {
-            this._fetchResult();
-        }
-    }
-
-    private _fetchResult() {
-        this._totalAccumulated += this._current;
-        this._lastSecAccumulated += this._current;
-
-        // Min/Max update
-        this._min = Math.min(this._min, this._current);
-        this._max = Math.max(this._max, this._current);
-        this._average = this._totalAccumulated / this._totalValueCount;
-
-        // Reset last sec?
-        let now = PrecisionDate.Now;
-        if ((now - this._lastSecTime) > 1000) {
-            this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
-            this._lastSecTime = now;
-            this._lastSecAccumulated = 0;
-            this._lastSecValueCount = 0;
-        }
-    }
-
-    private _startMonitoringTime: number;
-    private _min: number;
-    private _max: number;
-    private _average: number;
-    private _current: number;
-    private _totalValueCount: number;
-    private _totalAccumulated: number;
-    private _lastSecAverage: number;
-    private _lastSecAccumulated: number;
-    private _lastSecTime: number;
-    private _lastSecValueCount: number;
 }
 
 /**

+ 1 - 1
src/Particles/IParticleSystem.ts

@@ -5,7 +5,7 @@ import { BaseTexture } from "../Materials/Textures/baseTexture";
 import { Texture } from "../Materials/Textures/texture";
 import { BoxParticleEmitter, IParticleEmitterType, PointParticleEmitter, HemisphericParticleEmitter, SphereParticleEmitter, SphereDirectedParticleEmitter, CylinderParticleEmitter, ConeParticleEmitter } from "../Particles/EmitterTypes/index";
 import { Scene } from "../scene";
-import { ColorGradient, FactorGradient, Color3Gradient } from "../Misc/tools";
+import { ColorGradient, FactorGradient, Color3Gradient } from "../Misc/gradients";
 
 declare type Animation = import("../Animations/animation").Animation;
 

+ 1 - 1
src/Particles/baseParticleSystem.ts

@@ -5,7 +5,7 @@ import { ImageProcessingConfiguration, ImageProcessingConfigurationDefines } fro
 import { ProceduralTexture } from "../Materials/Textures/Procedurals/proceduralTexture";
 import { RawTexture } from "../Materials/Textures/rawTexture";
 import { Scene } from "../scene";
-import { ColorGradient, FactorGradient, Color3Gradient, IValueGradient } from "../Misc/tools";
+import { ColorGradient, FactorGradient, Color3Gradient, IValueGradient } from "../Misc/gradients";
 import { BoxParticleEmitter, IParticleEmitterType, PointParticleEmitter, HemisphericParticleEmitter, SphereParticleEmitter, SphereDirectedParticleEmitter, CylinderParticleEmitter, CylinderDirectedParticleEmitter, ConeParticleEmitter } from "../Particles/EmitterTypes/index";
 import { Constants } from "../Engines/constants";
 import { Texture } from '../Materials/Textures/texture';

+ 4 - 3
src/Particles/gpuParticleSystem.ts

@@ -1,5 +1,6 @@
 import { Nullable, float } from "../types";
-import { IAnimatable, Tools, IValueGradient, ColorGradient, FactorGradient, Color3Gradient } from "../Misc/tools";
+import { IAnimatable } from "../Misc/tools";
+import { FactorGradient, ColorGradient, Color3Gradient, IValueGradient, GradientHelper } from "../Misc/gradients";
 import { Observable } from "../Misc/observable";
 import { Color4, Color3, Vector3, Matrix, Tmp } from "../Maths/math";
 import { Scalar } from "../Maths/math.scalar";
@@ -1105,7 +1106,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         for (var x = 0; x < this._rawTextureWidth; x++) {
             var ratio = x / this._rawTextureWidth;
 
-            Tools.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
+            GradientHelper.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
                 data[x] = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
             });
         }
@@ -1144,7 +1145,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
         for (var x = 0; x < this._rawTextureWidth; x++) {
             var ratio = x / this._rawTextureWidth;
 
-            Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
+            GradientHelper.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
 
                 Color4.LerpToRef((<ColorGradient>currentGradient).color1, (<ColorGradient>nextGradient).color1, scale, tmpColor);
                 data[x * 4] = tmpColor.r * 255;

+ 1 - 1
src/Particles/particle.ts

@@ -4,7 +4,7 @@ import { Scalar } from "../Maths/math.scalar";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { ParticleSystem } from "./particleSystem";
 import { SubEmitter } from "./subEmitter";
-import { ColorGradient, FactorGradient } from "../Misc/tools";
+import { ColorGradient, FactorGradient } from "../Misc/gradients";
 /**
  * A particle represents one of the element emitted by a particle system.
  * This is mainly define by its coordinates, direction, velocity and age.

+ 14 - 13
src/Particles/particleSystem.ts

@@ -1,5 +1,6 @@
 import { Nullable } from "../types";
-import { IAnimatable, Tools, FactorGradient, ColorGradient, Color3Gradient } from "../Misc/tools";
+import { IAnimatable } from "../Misc/tools";
+import { FactorGradient, ColorGradient, Color3Gradient, GradientHelper } from "../Misc/gradients";
 import { Observable, Observer } from "../Misc/observable";
 import { Color4, Color3, Vector3, Matrix, Tmp, ISize, Vector4 } from "../Maths/math";
 import { Scalar } from "../Maths/math.scalar";
@@ -254,7 +255,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
                 // Color
                 if (this._colorGradients && this._colorGradients.length > 0) {
-                    Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
+                    GradientHelper.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
                         if (currentGradient !== particle._currentColorGradient) {
                             particle._currentColor1.copyFrom(particle._currentColor2);
                             (<ColorGradient>nextGradient).getColorToRef(particle._currentColor2);
@@ -274,7 +275,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
                 // Angular speed
                 if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {
-                    Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
+                    GradientHelper.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
                         if (currentGradient !== particle._currentAngularSpeedGradient) {
                             particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
                             particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();
@@ -290,7 +291,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
                 /// Velocity
                 if (this._velocityGradients && this._velocityGradients.length > 0) {
-                    Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
+                    GradientHelper.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
                         if (currentGradient !== particle._currentVelocityGradient) {
                             particle._currentVelocity1 = particle._currentVelocity2;
                             particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();
@@ -304,7 +305,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
                 /// Limit velocity
                 if (this._limitVelocityGradients && this._limitVelocityGradients.length > 0) {
-                    Tools.GetCurrentGradient(ratio, this._limitVelocityGradients, (currentGradient, nextGradient, scale) => {
+                    GradientHelper.GetCurrentGradient(ratio, this._limitVelocityGradients, (currentGradient, nextGradient, scale) => {
                         if (currentGradient !== particle._currentLimitVelocityGradient) {
                             particle._currentLimitVelocity1 = particle._currentLimitVelocity2;
                             particle._currentLimitVelocity2 = (<FactorGradient>nextGradient).getFactor();
@@ -322,7 +323,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
                 /// Drag
                 if (this._dragGradients && this._dragGradients.length > 0) {
-                    Tools.GetCurrentGradient(ratio, this._dragGradients, (currentGradient, nextGradient, scale) => {
+                    GradientHelper.GetCurrentGradient(ratio, this._dragGradients, (currentGradient, nextGradient, scale) => {
                         if (currentGradient !== particle._currentDragGradient) {
                             particle._currentDrag1 = particle._currentDrag2;
                             particle._currentDrag2 = (<FactorGradient>nextGradient).getFactor();
@@ -358,7 +359,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
                 // Size
                 if (this._sizeGradients && this._sizeGradients.length > 0) {
-                    Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                    GradientHelper.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
                         if (currentGradient !== particle._currentSizeGradient) {
                             particle._currentSize1 = particle._currentSize2;
                             particle._currentSize2 = (<FactorGradient>nextGradient).getFactor();
@@ -371,7 +372,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
                 // Remap data
                 if (this._useRampGradients) {
                     if (this._colorRemapGradients && this._colorRemapGradients.length > 0) {
-                        Tools.GetCurrentGradient(ratio, this._colorRemapGradients, (currentGradient, nextGradient, scale) => {
+                        GradientHelper.GetCurrentGradient(ratio, this._colorRemapGradients, (currentGradient, nextGradient, scale) => {
                             let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
                             let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
 
@@ -381,7 +382,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
                     }
 
                     if (this._alphaRemapGradients && this._alphaRemapGradients.length > 0) {
-                        Tools.GetCurrentGradient(ratio, this._alphaRemapGradients, (currentGradient, nextGradient, scale) => {
+                        GradientHelper.GetCurrentGradient(ratio, this._alphaRemapGradients, (currentGradient, nextGradient, scale) => {
                             let min = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
                             let max = Scalar.Lerp((<FactorGradient>currentGradient).factor2!, (<FactorGradient>nextGradient).factor2!, scale);
 
@@ -737,7 +738,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
         for (var x = 0; x < this._rawTextureWidth; x++) {
             var ratio = x / this._rawTextureWidth;
 
-            Tools.GetCurrentGradient(ratio, this._rampGradients, (currentGradient, nextGradient, scale) => {
+            GradientHelper.GetCurrentGradient(ratio, this._rampGradients, (currentGradient, nextGradient, scale) => {
 
                 Color3.LerpToRef((<Color3Gradient>currentGradient).color, (<Color3Gradient>nextGradient).color, scale, tmpColor);
                 data[x * 4] = tmpColor.r * 255;
@@ -1321,7 +1322,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             // Life time
             if (this.targetStopDuration && this._lifeTimeGradients && this._lifeTimeGradients.length > 0) {
                 let ratio = Scalar.Clamp(this._actualFrame / this.targetStopDuration);
-                Tools.GetCurrentGradient(ratio, this._lifeTimeGradients, (currentGradient, nextGradient) => {
+                GradientHelper.GetCurrentGradient(ratio, this._lifeTimeGradients, (currentGradient, nextGradient) => {
                     let factorGradient1 = (<FactorGradient>currentGradient);
                     let factorGradient2 = (<FactorGradient>nextGradient);
                     let lifeTime1 = factorGradient1.getFactor();
@@ -1353,7 +1354,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
             // Adjust scale by start size
             if (this._startSizeGradients && this._startSizeGradients[0] && this.targetStopDuration) {
                 const ratio = this._actualFrame / this.targetStopDuration;
-                Tools.GetCurrentGradient(ratio, this._startSizeGradients, (currentGradient, nextGradient, scale) => {
+                GradientHelper.GetCurrentGradient(ratio, this._startSizeGradients, (currentGradient, nextGradient, scale) => {
                     if (currentGradient !== this._currentStartSizeGradient) {
                         this._currentStartSize1 = this._currentStartSize2;
                         this._currentStartSize2 = (<FactorGradient>nextGradient).getFactor();
@@ -1615,7 +1616,7 @@ export class ParticleSystem extends BaseParticleSystem implements IDisposable, I
 
             if (this._emitRateGradients && this._emitRateGradients.length > 0 && this.targetStopDuration) {
                 const ratio = this._actualFrame / this.targetStopDuration;
-                Tools.GetCurrentGradient(ratio, this._emitRateGradients, (currentGradient, nextGradient, scale) => {
+                GradientHelper.GetCurrentGradient(ratio, this._emitRateGradients, (currentGradient, nextGradient, scale) => {
                     if (currentGradient !== this._currentEmitRateGradient) {
                         this._currentEmitRate1 = this._currentEmitRate2;
                         this._currentEmitRate2 = (<FactorGradient>nextGradient).getFactor();

+ 2 - 3
src/node.ts

@@ -9,7 +9,6 @@ import { EngineStore } from "./Engines/engineStore";
 import { _DevTools } from './Misc/devTools';
 import { AbstractActionManager } from './Actions/abstractActionManager';
 import { IInspectable } from './Misc/iInspectable';
-import { Tools } from './Misc/tools';
 
 declare type Animatable = import("./Animations/animatable").Animatable;
 declare type AnimationPropertiesOverride = import("./Animations/animationPropertiesOverride").AnimationPropertiesOverride;
@@ -830,8 +829,8 @@ export class Node implements IBehaviorAware<Node> {
                 var minBox = boundingBox.minimumWorld;
                 var maxBox = boundingBox.maximumWorld;
 
-                Tools.CheckExtends(minBox, min, max);
-                Tools.CheckExtends(maxBox, min, max);
+                Vector3.CheckExtends(minBox, min, max);
+                Vector3.CheckExtends(maxBox, min, max);
             }
         }
 

+ 4 - 3
src/scene.ts

@@ -1,5 +1,5 @@
 import { Nullable } from "./types";
-import { IAnimatable, IFileRequest, Tools, PerfCounter } from "./Misc/tools";
+import { IAnimatable, IFileRequest, Tools } from "./Misc/tools";
 import { PrecisionDate } from "./Misc/precisionDate";
 import { Observable, Observer } from "./Misc/observable";
 import { SmartArrayNoDuplicate, SmartArray, ISmartArrayLike } from "./Misc/smartArray";
@@ -47,6 +47,7 @@ import { AbstractActionManager } from './Actions/abstractActionManager';
 import { _DevTools } from './Misc/devTools';
 import { WebRequest } from './Misc/webRequest';
 import { InputManager } from './Inputs/scene.inputManager';
+import { PerfCounter } from './Misc/perfCounter';
 
 declare type Ray = import("./Culling/ray").Ray;
 declare type TrianglePickingPredicate = import("./Culling/ray").TrianglePickingPredicate;
@@ -4156,8 +4157,8 @@ export class Scene extends AbstractScene implements IAnimatable {
             var minBox = boundingInfo.boundingBox.minimumWorld;
             var maxBox = boundingInfo.boundingBox.maximumWorld;
 
-            Tools.CheckExtends(minBox, min, max);
-            Tools.CheckExtends(maxBox, min, max);
+            Vector3.CheckExtends(minBox, min, max);
+            Vector3.CheckExtends(maxBox, min, max);
         });
 
         return {

+ 1 - 1
tests/validation/config.json

@@ -237,7 +237,7 @@
         },
         {
             "title": "Fresnel",
-            "playgroundId": "#603JUZ#0",
+            "playgroundId": "#603JUZ#1",
             "referenceImage": "fresnel.png"
         },
         {