David Catuhe 9 سال پیش
والد
کامیت
bb93ebbeaa

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 26 - 25
dist/preview release/babylon.core.js


+ 293 - 256
dist/preview release/babylon.d.ts

@@ -124,8 +124,10 @@ declare module BABYLON {
         private _alphaMode;
         private _loadedTexturesCache;
         private _maxTextureChannels;
+        private _activeTexture;
         private _activeTexturesCache;
         private _currentEffect;
+        private _currentProgram;
         private _compiledEffects;
         private _vertexAttribArraysEnabled;
         private _vertexAttribArraysToUse;
@@ -136,6 +138,8 @@ declare module BABYLON {
         private _currentRenderTarget;
         private _uintIndicesCurrentlySet;
         private _currentBoundBuffer;
+        private _currentFramebuffer;
+        private _currentBufferPointers;
         private _currentInstanceLocations;
         private _currentInstanceBuffers;
         private _workingCanvas;
@@ -231,6 +235,7 @@ declare module BABYLON {
          */
         setSize(width: number, height: number): void;
         bindFramebuffer(texture: WebGLTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number): void;
+        private bindUnboundFramebuffer(framebuffer);
         unBindFramebuffer(texture: WebGLTexture, disableGenerateMipMaps?: boolean): void;
         generateMipMapsForCubemap(texture: WebGLTexture): void;
         flushFramebuffer(): void;
@@ -242,8 +247,10 @@ declare module BABYLON {
         private _resetIndexBufferBinding();
         createIndexBuffer(indices: number[] | Int32Array): WebGLBuffer;
         bindArrayBuffer(buffer: WebGLBuffer): void;
-        updateArrayBuffer(data: Float32Array): void;
+        private bindIndexBuffer(buffer);
         private bindBuffer(buffer, target);
+        updateArrayBuffer(data: Float32Array): void;
+        private vertexAttribPointer(buffer, indx, size, type, normalized, stride, offset);
         bindBuffersDirectly(vertexBuffer: WebGLBuffer, indexBuffer: WebGLBuffer, vertexDeclaration: number[], vertexStrideSize: number, effect: Effect): void;
         bindBuffers(vertexBuffers: {
             [key: string]: VertexBuffer;
@@ -304,7 +311,10 @@ declare module BABYLON {
         updateTextureSize(texture: WebGLTexture, width: number, height: number): void;
         createRawCubeTexture(url: string, scene: Scene, size: number, format: number, type: number, noMipmap: boolean, callback: (ArrayBuffer) => ArrayBufferView[], mipmmapGenerator: ((faces: ArrayBufferView[]) => ArrayBufferView[][])): WebGLTexture;
         _releaseTexture(texture: WebGLTexture): void;
+        private setProgram(program);
         bindSamplers(effect: Effect): void;
+        private activateTexture(texture);
+        _bindTextureDirectly(target: number, texture: WebGLTexture): void;
         _bindTexture(channel: number, texture: WebGLTexture): void;
         setTextureFromPostProcess(channel: number, postProcess: PostProcess): void;
         unbindAllTextures(): void;
@@ -1488,182 +1498,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class Analyser {
-        SMOOTHING: number;
-        FFT_SIZE: number;
-        BARGRAPHAMPLITUDE: number;
-        DEBUGCANVASPOS: {
-            x: number;
-            y: number;
-        };
-        DEBUGCANVASSIZE: {
-            width: number;
-            height: number;
-        };
-        private _byteFreqs;
-        private _byteTime;
-        private _floatFreqs;
-        private _webAudioAnalyser;
-        private _debugCanvas;
-        private _debugCanvasContext;
-        private _scene;
-        private _registerFunc;
-        private _audioEngine;
-        constructor(scene: Scene);
-        getFrequencyBinCount(): number;
-        getByteFrequencyData(): Uint8Array;
-        getByteTimeDomainData(): Uint8Array;
-        getFloatFrequencyData(): Uint8Array;
-        drawDebugCanvas(): void;
-        stopDebugCanvas(): void;
-        connectAudioNodes(inputAudioNode: AudioNode, outputAudioNode: AudioNode): void;
-        dispose(): void;
-    }
-}
-
-declare module BABYLON {
-    class AudioEngine {
-        private _audioContext;
-        private _audioContextInitialized;
-        canUseWebAudio: boolean;
-        masterGain: GainNode;
-        private _connectedAnalyser;
-        WarnedWebAudioUnsupported: boolean;
-        unlocked: boolean;
-        onAudioUnlocked: () => any;
-        audioContext: AudioContext;
-        constructor();
-        private _unlockiOSaudio();
-        private _initializeAudioContext();
-        dispose(): void;
-        getGlobalVolume(): number;
-        setGlobalVolume(newVolume: number): void;
-        connectToAnalyser(analyser: Analyser): void;
-    }
-}
-
-declare module BABYLON {
-    class Sound {
-        name: string;
-        autoplay: boolean;
-        loop: boolean;
-        useCustomAttenuation: boolean;
-        soundTrackId: number;
-        spatialSound: boolean;
-        refDistance: number;
-        rolloffFactor: number;
-        maxDistance: number;
-        distanceModel: string;
-        private _panningModel;
-        onended: () => any;
-        private _playbackRate;
-        private _streaming;
-        private _startTime;
-        private _startOffset;
-        private _position;
-        private _localDirection;
-        private _volume;
-        private _isLoaded;
-        private _isReadyToPlay;
-        isPlaying: boolean;
-        isPaused: boolean;
-        private _isDirectional;
-        private _readyToPlayCallback;
-        private _audioBuffer;
-        private _soundSource;
-        private _streamingSource;
-        private _soundPanner;
-        private _soundGain;
-        private _inputAudioNode;
-        private _ouputAudioNode;
-        private _coneInnerAngle;
-        private _coneOuterAngle;
-        private _coneOuterGain;
-        private _scene;
-        private _connectedMesh;
-        private _customAttenuationFunction;
-        private _registerFunc;
-        private _isOutputConnected;
-        private _htmlAudioElement;
-        /**
-        * Create a sound and attach it to a scene
-        * @param name Name of your sound
-        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer
-        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-        */
-        constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: () => void, options?: any);
-        dispose(): void;
-        private _soundLoaded(audioData);
-        setAudioBuffer(audioBuffer: AudioBuffer): void;
-        updateOptions(options: any): void;
-        private _createSpatialParameters();
-        private _updateSpatialParameters();
-        switchPanningModelToHRTF(): void;
-        switchPanningModelToEqualPower(): void;
-        private _switchPanningModel();
-        connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
-        /**
-        * Transform this sound into a directional source
-        * @param coneInnerAngle Size of the inner cone in degree
-        * @param coneOuterAngle Size of the outer cone in degree
-        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-        */
-        setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
-        setPosition(newPosition: Vector3): void;
-        setLocalDirectionToMesh(newLocalDirection: Vector3): void;
-        private _updateDirection();
-        updateDistanceFromListener(): void;
-        setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
-        /**
-        * Play the sound
-        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-        */
-        play(time?: number): void;
-        private _onended();
-        /**
-        * Stop the sound
-        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-        */
-        stop(time?: number): void;
-        pause(): void;
-        setVolume(newVolume: number, time?: number): void;
-        setPlaybackRate(newPlaybackRate: number): void;
-        getVolume(): number;
-        attachToMesh(meshToConnectTo: AbstractMesh): void;
-        detachFromMesh(): void;
-        private _onRegisterAfterWorldMatrixUpdate(connectedMesh);
-        clone(): Sound;
-        getAudioBuffer(): AudioBuffer;
-        static Parse(parsedSound: any, scene: Scene, rootUrl: string, sourceSound?: Sound): Sound;
-    }
-}
-
-declare module BABYLON {
-    class SoundTrack {
-        private _outputAudioNode;
-        private _inputAudioNode;
-        private _trackConvolver;
-        private _scene;
-        id: number;
-        soundCollection: Array<Sound>;
-        private _isMainTrack;
-        private _connectedAnalyser;
-        private _options;
-        private _isInitialized;
-        constructor(scene: Scene, options?: any);
-        private _initializeSoundTrackAudioGraph();
-        dispose(): void;
-        AddSound(sound: Sound): void;
-        RemoveSound(sound: Sound): void;
-        setVolume(newVolume: number): void;
-        switchPanningModelToHRTF(): void;
-        switchPanningModelToEqualPower(): void;
-        connectToAnalyser(analyser: Analyser): void;
-    }
-}
-
-declare module BABYLON {
     class Animatable {
         target: any;
         fromFrame: number;
@@ -1768,7 +1602,10 @@ declare module BABYLON {
         getRange(name: string): AnimationRange;
         reset(): void;
         isStopped(): boolean;
-        getKeys(): any[];
+        getKeys(): Array<{
+            frame: number;
+            value: any;
+        }>;
         getHighestFrame(): number;
         getEasingFunction(): IEasingFunction;
         setEasingFunction(easingFunction: EasingFunction): void;
@@ -1780,7 +1617,10 @@ declare module BABYLON {
         color3InterpolateFunction(startValue: Color3, endValue: Color3, gradient: number): Color3;
         matrixInterpolateFunction(startValue: Matrix, endValue: Matrix, gradient: number): Matrix;
         clone(): Animation;
-        setKeys(values: Array<any>): void;
+        setKeys(values: Array<{
+            frame: number;
+            value: any;
+        }>): void;
         private _getKeyValue(value);
         private _interpolate(currentFrame, repeatCount, loopMode, offsetValue?, highLimitValue?);
         setValue(currentValue: any, blend?: boolean): void;
@@ -1885,6 +1725,182 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    class Analyser {
+        SMOOTHING: number;
+        FFT_SIZE: number;
+        BARGRAPHAMPLITUDE: number;
+        DEBUGCANVASPOS: {
+            x: number;
+            y: number;
+        };
+        DEBUGCANVASSIZE: {
+            width: number;
+            height: number;
+        };
+        private _byteFreqs;
+        private _byteTime;
+        private _floatFreqs;
+        private _webAudioAnalyser;
+        private _debugCanvas;
+        private _debugCanvasContext;
+        private _scene;
+        private _registerFunc;
+        private _audioEngine;
+        constructor(scene: Scene);
+        getFrequencyBinCount(): number;
+        getByteFrequencyData(): Uint8Array;
+        getByteTimeDomainData(): Uint8Array;
+        getFloatFrequencyData(): Uint8Array;
+        drawDebugCanvas(): void;
+        stopDebugCanvas(): void;
+        connectAudioNodes(inputAudioNode: AudioNode, outputAudioNode: AudioNode): void;
+        dispose(): void;
+    }
+}
+
+declare module BABYLON {
+    class AudioEngine {
+        private _audioContext;
+        private _audioContextInitialized;
+        canUseWebAudio: boolean;
+        masterGain: GainNode;
+        private _connectedAnalyser;
+        WarnedWebAudioUnsupported: boolean;
+        unlocked: boolean;
+        onAudioUnlocked: () => any;
+        audioContext: AudioContext;
+        constructor();
+        private _unlockiOSaudio();
+        private _initializeAudioContext();
+        dispose(): void;
+        getGlobalVolume(): number;
+        setGlobalVolume(newVolume: number): void;
+        connectToAnalyser(analyser: Analyser): void;
+    }
+}
+
+declare module BABYLON {
+    class Sound {
+        name: string;
+        autoplay: boolean;
+        loop: boolean;
+        useCustomAttenuation: boolean;
+        soundTrackId: number;
+        spatialSound: boolean;
+        refDistance: number;
+        rolloffFactor: number;
+        maxDistance: number;
+        distanceModel: string;
+        private _panningModel;
+        onended: () => any;
+        private _playbackRate;
+        private _streaming;
+        private _startTime;
+        private _startOffset;
+        private _position;
+        private _localDirection;
+        private _volume;
+        private _isLoaded;
+        private _isReadyToPlay;
+        isPlaying: boolean;
+        isPaused: boolean;
+        private _isDirectional;
+        private _readyToPlayCallback;
+        private _audioBuffer;
+        private _soundSource;
+        private _streamingSource;
+        private _soundPanner;
+        private _soundGain;
+        private _inputAudioNode;
+        private _ouputAudioNode;
+        private _coneInnerAngle;
+        private _coneOuterAngle;
+        private _coneOuterGain;
+        private _scene;
+        private _connectedMesh;
+        private _customAttenuationFunction;
+        private _registerFunc;
+        private _isOutputConnected;
+        private _htmlAudioElement;
+        /**
+        * Create a sound and attach it to a scene
+        * @param name Name of your sound
+        * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer
+        * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+        * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+        */
+        constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback?: () => void, options?: any);
+        dispose(): void;
+        private _soundLoaded(audioData);
+        setAudioBuffer(audioBuffer: AudioBuffer): void;
+        updateOptions(options: any): void;
+        private _createSpatialParameters();
+        private _updateSpatialParameters();
+        switchPanningModelToHRTF(): void;
+        switchPanningModelToEqualPower(): void;
+        private _switchPanningModel();
+        connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode): void;
+        /**
+        * Transform this sound into a directional source
+        * @param coneInnerAngle Size of the inner cone in degree
+        * @param coneOuterAngle Size of the outer cone in degree
+        * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+        */
+        setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void;
+        setPosition(newPosition: Vector3): void;
+        setLocalDirectionToMesh(newLocalDirection: Vector3): void;
+        private _updateDirection();
+        updateDistanceFromListener(): void;
+        setAttenuationFunction(callback: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number): void;
+        /**
+        * Play the sound
+        * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+        */
+        play(time?: number): void;
+        private _onended();
+        /**
+        * Stop the sound
+        * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+        */
+        stop(time?: number): void;
+        pause(): void;
+        setVolume(newVolume: number, time?: number): void;
+        setPlaybackRate(newPlaybackRate: number): void;
+        getVolume(): number;
+        attachToMesh(meshToConnectTo: AbstractMesh): void;
+        detachFromMesh(): void;
+        private _onRegisterAfterWorldMatrixUpdate(connectedMesh);
+        clone(): Sound;
+        getAudioBuffer(): AudioBuffer;
+        static Parse(parsedSound: any, scene: Scene, rootUrl: string, sourceSound?: Sound): Sound;
+    }
+}
+
+declare module BABYLON {
+    class SoundTrack {
+        private _outputAudioNode;
+        private _inputAudioNode;
+        private _trackConvolver;
+        private _scene;
+        id: number;
+        soundCollection: Array<Sound>;
+        private _isMainTrack;
+        private _connectedAnalyser;
+        private _options;
+        private _isInitialized;
+        constructor(scene: Scene, options?: any);
+        private _initializeSoundTrackAudioGraph();
+        dispose(): void;
+        AddSound(sound: Sound): void;
+        RemoveSound(sound: Sound): void;
+        setVolume(newVolume: number): void;
+        switchPanningModelToHRTF(): void;
+        switchPanningModelToEqualPower(): void;
+        connectToAnalyser(analyser: Analyser): void;
+    }
+}
+
+declare module BABYLON {
     class Bone extends Node {
         name: string;
         children: Bone[];
@@ -5588,50 +5604,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    interface ISceneLoaderPlugin {
-        extensions: string;
-        importMesh: (meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => boolean;
-        load: (scene: Scene, data: string, rootUrl: string) => boolean;
-    }
-    interface ISceneLoaderPluginAsync {
-        extensions: string;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onsuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onerror?: () => void) => void;
-        loadAsync: (scene: Scene, data: string, rootUrl: string, onsuccess: () => void, onerror: () => void) => boolean;
-    }
-    class SceneLoader {
-        private static _ForceFullSceneLoadingForIncremental;
-        private static _ShowLoadingScreen;
-        static NO_LOGGING: number;
-        static MINIMAL_LOGGING: number;
-        static SUMMARY_LOGGING: number;
-        static DETAILED_LOGGING: number;
-        private static _loggingLevel;
-        static ForceFullSceneLoadingForIncremental: boolean;
-        static ShowLoadingScreen: boolean;
-        static loggingLevel: number;
-        private static _registeredPlugins;
-        private static _getPluginForFilename(sceneFilename);
-        static GetPluginForExtension(extension: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
-        static RegisterPlugin(plugin: ISceneLoaderPlugin): void;
-        static ImportMesh(meshesNames: any, rootUrl: string, sceneFilename: string, scene: Scene, onsuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, progressCallBack?: () => void, onerror?: (scene: Scene, message: string, exception?: any) => void): void;
-        /**
-        * Load a scene
-        * @param rootUrl a string that defines the root url for scene and resources
-        * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
-        * @param engine is the instance of BABYLON.Engine to use to create the scene
-        */
-        static Load(rootUrl: string, sceneFilename: any, engine: Engine, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void;
-        /**
-        * Append a scene
-        * @param rootUrl a string that defines the root url for scene and resources
-        * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
-        * @param scene is the instance of BABYLON.Scene to append to
-        */
-        static Append(rootUrl: string, sceneFilename: any, scene: Scene, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void;
-    }
-}
-
-declare module BABYLON {
     class DirectionalLight extends Light implements IShadowLight {
         position: Vector3;
         direction: Vector3;
@@ -5766,6 +5738,50 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    interface ISceneLoaderPlugin {
+        extensions: string;
+        importMesh: (meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => boolean;
+        load: (scene: Scene, data: string, rootUrl: string) => boolean;
+    }
+    interface ISceneLoaderPluginAsync {
+        extensions: string;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onsuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onerror?: () => void) => void;
+        loadAsync: (scene: Scene, data: string, rootUrl: string, onsuccess: () => void, onerror: () => void) => boolean;
+    }
+    class SceneLoader {
+        private static _ForceFullSceneLoadingForIncremental;
+        private static _ShowLoadingScreen;
+        static NO_LOGGING: number;
+        static MINIMAL_LOGGING: number;
+        static SUMMARY_LOGGING: number;
+        static DETAILED_LOGGING: number;
+        private static _loggingLevel;
+        static ForceFullSceneLoadingForIncremental: boolean;
+        static ShowLoadingScreen: boolean;
+        static loggingLevel: number;
+        private static _registeredPlugins;
+        private static _getPluginForFilename(sceneFilename);
+        static GetPluginForExtension(extension: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
+        static RegisterPlugin(plugin: ISceneLoaderPlugin): void;
+        static ImportMesh(meshesNames: any, rootUrl: string, sceneFilename: string, scene: Scene, onsuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, progressCallBack?: () => void, onerror?: (scene: Scene, message: string, exception?: any) => void): void;
+        /**
+        * Load a scene
+        * @param rootUrl a string that defines the root url for scene and resources
+        * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
+        * @param engine is the instance of BABYLON.Engine to use to create the scene
+        */
+        static Load(rootUrl: string, sceneFilename: any, engine: Engine, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void;
+        /**
+        * Append a scene
+        * @param rootUrl a string that defines the root url for scene and resources
+        * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
+        * @param scene is the instance of BABYLON.Scene to append to
+        */
+        static Append(rootUrl: string, sceneFilename: any, scene: Scene, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void;
+    }
+}
+
+declare module BABYLON {
     /**
      * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT).
      * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects.
@@ -6052,10 +6068,10 @@ declare module BABYLON {
         _bindTexture(channel: string, texture: WebGLTexture): void;
         setTexture(channel: string, texture: BaseTexture): void;
         setTextureFromPostProcess(channel: string, postProcess: PostProcess): void;
-        _cacheMatrix(uniformName: any, matrix: any): void;
-        _cacheFloat2(uniformName: string, x: number, y: number): void;
-        _cacheFloat3(uniformName: string, x: number, y: number, z: number): void;
-        _cacheFloat4(uniformName: string, x: number, y: number, z: number, w: number): void;
+        _cacheMatrix(uniformName: string, matrix: Matrix): boolean;
+        _cacheFloat2(uniformName: string, x: number, y: number): boolean;
+        _cacheFloat3(uniformName: string, x: number, y: number, z: number): boolean;
+        _cacheFloat4(uniformName: string, x: number, y: number, z: number, w: number): boolean;
         setArray(uniformName: string, array: number[]): Effect;
         setArray2(uniformName: string, array: number[]): Effect;
         setArray3(uniformName: string, array: number[]): Effect;
@@ -6063,7 +6079,7 @@ declare module BABYLON {
         setMatrices(uniformName: string, matrices: Float32Array): Effect;
         setMatrix(uniformName: string, matrix: Matrix): Effect;
         setMatrix3x3(uniformName: string, matrix: Float32Array): Effect;
-        setMatrix2x2(uniformname: string, matrix: Float32Array): Effect;
+        setMatrix2x2(uniformName: string, matrix: Float32Array): Effect;
         setFloat(uniformName: string, value: number): Effect;
         setBool(uniformName: string, bool: boolean): Effect;
         setVector2(uniformName: string, vector2: Vector2): Effect;
@@ -6846,6 +6862,8 @@ declare module BABYLON {
         static CrossToRef(left: Vector3, right: Vector3, result: Vector3): void;
         static Normalize(vector: Vector3): Vector3;
         static NormalizeToRef(vector: Vector3, result: Vector3): void;
+        private static _viewportMatrixCache;
+        private static _matrixCache;
         static Project(vector: Vector3, world: Matrix, transform: Matrix, viewport: Viewport): Vector3;
         static UnprojectFromTransform(source: Vector3, viewportWidth: number, viewportHeight: number, world: Matrix, transform: Matrix): Vector3;
         static Unproject(source: Vector3, viewportWidth: number, viewportHeight: number, world: Matrix, view: Matrix, projection: Matrix): Vector3;
@@ -6973,9 +6991,10 @@ declare module BABYLON {
         static Inverse(q: Quaternion): Quaternion;
         static Identity(): Quaternion;
         static RotationAxis(axis: Vector3, angle: number): Quaternion;
+        static RotationAxisToRef(axis: Vector3, angle: number, result: Quaternion): Quaternion;
         static FromArray(array: number[], offset?: number): Quaternion;
         static RotationYawPitchRoll(yaw: number, pitch: number, roll: number): Quaternion;
-        static RotationYawPitchRollToRef(yaw: number, pitch: number, roll: number, result: Quaternion): void;
+        static RotationYawPitchRollToRef(yaw: number, pitch: number, roll: number, result: Quaternion): Quaternion;
         static RotationAlphaBetaGamma(alpha: number, beta: number, gamma: number): Quaternion;
         static RotationAlphaBetaGammaToRef(alpha: number, beta: number, gamma: number, result: Quaternion): void;
         static Slerp(left: Quaternion, right: Quaternion, amount: number): Quaternion;
@@ -7427,6 +7446,7 @@ declare module BABYLON {
         freezeWorldMatrix(): void;
         unfreezeWorldMatrix(): void;
         isWorldMatrixFrozen: boolean;
+        private static _rotationAxisCache;
         rotate(axis: Vector3, amount: number, space?: Space): void;
         translate(axis: Vector3, distance: number, space?: Space): void;
         getAbsolutePosition(): Vector3;
@@ -7482,6 +7502,7 @@ declare module BABYLON {
         setPositionWithLocalVector(vector3: Vector3): void;
         getPositionExpressedInLocalSpace(): Vector3;
         locallyTranslate(vector3: Vector3): void;
+        private static _lookAtVectorCache;
         lookAt(targetPoint: Vector3, yawCor: number, pitchCor: number, rollCor: number): void;
         attachToBone(bone: Bone, affectedMesh: AbstractMesh): void;
         detachFromBone(): void;
@@ -7644,6 +7665,7 @@ declare module BABYLON {
         private _applyToMesh(mesh);
         private notifyUpdate(kind?);
         load(scene: Scene, onLoaded?: () => void): void;
+        private _queueLoad(scene, onLoaded?);
         /**
          * Invert the geometry to move from a right handed system to a left handed one.
          */
@@ -8259,12 +8281,13 @@ declare module BABYLON {
         unregisterAfterRender(func: (mesh: AbstractMesh) => void): void;
         _getInstancesRenderList(subMeshId: number): _InstancesBatch;
         _renderWithInstances(subMesh: SubMesh, fillMode: number, batch: _InstancesBatch, effect: Effect, engine: Engine): void;
-        _processRendering(subMesh: SubMesh, effect: Effect, fillMode: number, batch: _InstancesBatch, hardwareInstancedRendering: boolean, onBeforeDraw: (isInstance: boolean, world: Matrix) => void): void;
+        _processRendering(subMesh: SubMesh, effect: Effect, fillMode: number, batch: _InstancesBatch, hardwareInstancedRendering: boolean, onBeforeDraw: (isInstance: boolean, world: Matrix, effectiveMaterial?: Material) => void, effectiveMaterial?: Material): void;
         /**
          * Triggers the draw call for the mesh.
          * Usually, you don't need to call this method by your own because the mesh rendering is handled by the scene rendering manager.
          */
         render(subMesh: SubMesh, enableAlphaMode: boolean): void;
+        private _onBeforeDraw(isInstance, world, effectiveMaterial);
         /**
          * Returns an array populated with ParticleSystem objects whose the mesh is the emitter.
          */
@@ -8274,6 +8297,7 @@ declare module BABYLON {
          */
         getHierarchyEmittedParticleSystems(): ParticleSystem[];
         _checkDelayState(): void;
+        private _queueLoad(mesh, scene);
         /**
          * Boolean, true is the mesh in the frustum defined by the Plane objects from the `frustumPlanes` array parameter.
          */
@@ -8792,6 +8816,7 @@ declare module BABYLON {
             closePath?: boolean;
             offset?: number;
             sideOrientation?: number;
+            invertUV?: boolean;
         }): VertexData;
         static CreateBox(options: {
             size?: number;
@@ -9020,6 +9045,7 @@ declare module BABYLON {
          * The optional parameter `instance` is an instance of an existing Ribbon object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#ribbon
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         static CreateRibbon(name: string, options: {
@@ -9030,6 +9056,7 @@ declare module BABYLON {
             updatable?: boolean;
             sideOrientation?: number;
             instance?: Mesh;
+            invertUV?: boolean;
         }, scene?: Scene): Mesh;
         /**
          * Creates a cylinder or a cone mesh.
@@ -9176,6 +9203,7 @@ declare module BABYLON {
          * Remember you can only change the shape or path point positions, not their number when updating an extruded shape.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         static ExtrudeShape(name: string, options: {
@@ -9187,6 +9215,7 @@ declare module BABYLON {
             updatable?: boolean;
             sideOrientation?: number;
             instance?: Mesh;
+            invertUV?: boolean;
         }, scene: Scene): Mesh;
         /**
          * Creates an custom extruded shape mesh.
@@ -9220,6 +9249,7 @@ declare module BABYLON {
          * Remember you can only change the shape or path point positions, not their number when updating an extruded shape.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         static ExtrudeShapeCustom(name: string, options: {
@@ -9233,6 +9263,7 @@ declare module BABYLON {
             updatable?: boolean;
             sideOrientation?: number;
             instance?: Mesh;
+            invertUV?: boolean;
         }, scene: Scene): Mesh;
         /**
          * Creates lathe mesh.
@@ -9248,6 +9279,7 @@ declare module BABYLON {
          * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         static CreateLathe(name: string, options: {
@@ -9259,6 +9291,7 @@ declare module BABYLON {
             updatable?: boolean;
             sideOrientation?: number;
             cap?: number;
+            invertUV?: boolean;
         }, scene: Scene): Mesh;
         /**
          * Creates a plane mesh.
@@ -9364,6 +9397,7 @@ declare module BABYLON {
          * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#tube
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         static CreateTube(name: string, options: {
@@ -9378,6 +9412,7 @@ declare module BABYLON {
             updatable?: boolean;
             sideOrientation?: number;
             instance?: Mesh;
+            invertUV?: boolean;
         }, scene: Scene): Mesh;
         /**
          * Creates a polyhedron mesh.
@@ -9424,7 +9459,7 @@ declare module BABYLON {
             size?: Vector3;
             angle?: number;
         }): Mesh;
-        private static _ExtrudeShapeGeneric(name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance);
+        private static _ExtrudeShapeGeneric(name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance, invertUV);
     }
 }
 
@@ -10449,6 +10484,28 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    class ReflectionProbe {
+        name: string;
+        private _scene;
+        private _renderTargetTexture;
+        private _projectionMatrix;
+        private _viewMatrix;
+        private _target;
+        private _add;
+        private _attachedMesh;
+        invertYAxis: boolean;
+        position: Vector3;
+        constructor(name: string, size: number, scene: Scene, generateMipMaps?: boolean);
+        refreshRate: number;
+        getScene(): Scene;
+        cubeTexture: RenderTargetTexture;
+        renderList: AbstractMesh[];
+        attachToMesh(mesh: AbstractMesh): void;
+        dispose(): void;
+    }
+}
+
+declare module BABYLON {
     class AnaglyphPostProcess extends PostProcess {
         private _passedProcess;
         constructor(name: string, options: number | PostProcessOptions, rigCameras: Camera[], samplingMode?: number, engine?: Engine, reusable?: boolean);
@@ -11065,28 +11122,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class ReflectionProbe {
-        name: string;
-        private _scene;
-        private _renderTargetTexture;
-        private _projectionMatrix;
-        private _viewMatrix;
-        private _target;
-        private _add;
-        private _attachedMesh;
-        invertYAxis: boolean;
-        position: Vector3;
-        constructor(name: string, size: number, scene: Scene, generateMipMaps?: boolean);
-        refreshRate: number;
-        getScene(): Scene;
-        cubeTexture: RenderTargetTexture;
-        renderList: AbstractMesh[];
-        attachToMesh(mesh: AbstractMesh): void;
-        dispose(): void;
-    }
-}
-
-declare module BABYLON {
     class BoundingBoxRenderer {
         frontColor: Color3;
         backColor: Color3;
@@ -11726,6 +11761,7 @@ declare module BABYLON {
         * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
         */
         constructor(mask: number, skipNextObservers?: boolean);
+        initalize(mask: number, skipNextObservers?: boolean): EventState;
         /**
          * An Observer can set this property to true to prevent subsequent observers of being notified
          */
@@ -11751,6 +11787,7 @@ declare module BABYLON {
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
     class Observable<T> {
+        private static _pooledEventState;
         _observers: Observer<T>[];
         /**
          * Create a new Observer with the specified callback
@@ -12626,9 +12663,6 @@ declare module BABYLON {
     }
 }
 
-declare module BABYLON.Internals {
-}
-
 declare module BABYLON {
     class ShadowGenerator {
         private static _FILTER_NONE;
@@ -12687,6 +12721,9 @@ declare module BABYLON {
     }
 }
 
+declare module BABYLON.Internals {
+}
+
 declare module BABYLON {
     class BaseTexture {
         name: string;

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 37 - 37
dist/preview release/babylon.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 395 - 242
dist/preview release/babylon.max.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 36 - 36
dist/preview release/babylon.noworker.js


+ 1 - 3
src/Animations/babylon.animation.js

@@ -127,9 +127,7 @@ var BABYLON;
                 return null;
             }
             var animation = new Animation(name, targetProperty, framePerSecond, dataType, loopMode);
-            var keys = [];
-            keys.push({ frame: 0, value: from });
-            keys.push({ frame: totalFrame, value: to });
+            var keys = [{ frame: 0, value: from }, { frame: totalFrame, value: to }];
             animation.setKeys(keys);
             if (easingFunction !== undefined) {
                 animation.setEasingFunction(easingFunction);

+ 1 - 1
src/Canvas2d/babylon.prim2dBase.js

@@ -1174,7 +1174,7 @@ var BABYLON;
                     if (!owner) {
                         throw new Error("Parent " + parent.id + " of " + settings.id + " doesn't have a valid owner!");
                     }
-                    if (!(this instanceof BABYLON.Group2D) && !(this instanceof BABYLON.Sprite2D && settings.id !== null && settings.id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
+                    if (!(this instanceof BABYLON.Group2D) && !(this instanceof BABYLON.Sprite2D && settings.id != null && settings.id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
                         throw new Error("Can't create a primitive with the canvas as direct parent when the caching strategy is TOPLEVELGROUPS. You need to create a Group below the canvas and use it as the parent for the primitive");
                     }
                 }

+ 2 - 2
src/Canvas2d/babylon.sprite2d.js

@@ -180,13 +180,13 @@ var BABYLON;
             this.texture = texture;
             this.texture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
-            this.size = settings.spriteSize || null;
+            this.size = settings.spriteSize;
             this.spriteLocation = settings.spriteLocation || new BABYLON.Vector2(0, 0);
             this.spriteFrame = 0;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
             this._isTransparent = true;
-            if (!this.size) {
+            if (settings.spriteSize == null) {
                 var s = texture.getSize();
                 this.size = new BABYLON.Size(s.width, s.height);
             }

+ 27 - 23
src/Collisions/babylon.collider.js

@@ -15,31 +15,35 @@ var BABYLON;
             return false;
         return true;
     };
-    var getLowestRoot = function (a, b, c, maxR) {
-        var determinant = b * b - 4.0 * a * c;
+    var getLowestRoot = (function () {
         var result = { root: 0, found: false };
-        if (determinant < 0)
-            return result;
-        var sqrtD = Math.sqrt(determinant);
-        var r1 = (-b - sqrtD) / (2.0 * a);
-        var r2 = (-b + sqrtD) / (2.0 * a);
-        if (r1 > r2) {
-            var temp = r2;
-            r2 = r1;
-            r1 = temp;
-        }
-        if (r1 > 0 && r1 < maxR) {
-            result.root = r1;
-            result.found = true;
-            return result;
-        }
-        if (r2 > 0 && r2 < maxR) {
-            result.root = r2;
-            result.found = true;
+        return function (a, b, c, maxR) {
+            result.root = 0;
+            result.found = false;
+            var determinant = b * b - 4.0 * a * c;
+            if (determinant < 0)
+                return result;
+            var sqrtD = Math.sqrt(determinant);
+            var r1 = (-b - sqrtD) / (2.0 * a);
+            var r2 = (-b + sqrtD) / (2.0 * a);
+            if (r1 > r2) {
+                var temp = r2;
+                r2 = r1;
+                r1 = temp;
+            }
+            if (r1 > 0 && r1 < maxR) {
+                result.root = r1;
+                result.found = true;
+                return result;
+            }
+            if (r2 > 0 && r2 < maxR) {
+                result.root = r2;
+                result.found = true;
+                return result;
+            }
             return result;
-        }
-        return result;
-    };
+        };
+    })();
     var Collider = (function () {
         function Collider() {
             this.radius = new BABYLON.Vector3(1, 1, 1);

+ 7 - 7
src/Materials/Textures/babylon.fontTexture.js

@@ -129,22 +129,22 @@ var BABYLON;
             var measure = this._context.measureText(char);
             var textureSize = this.getSize();
             // we reached the end of the current line?
-            var xMargin = 2;
-            var yMargin = 2;
-            var width = measure.width;
+            var width = Math.round(measure.width);
+            var xMargin = Math.ceil(this._lineHeight / 20);
+            var yMargin = xMargin;
             if (this._currentFreePosition.x + width + xMargin > textureSize.width) {
                 this._currentFreePosition.x = 0;
-                this._currentFreePosition.y += this._lineHeight + yMargin; // +2 for safety margin
+                this._currentFreePosition.y += this._lineHeight + yMargin;
                 // No more room?
                 if (this._currentFreePosition.y > textureSize.height) {
                     return this.getChar("!");
                 }
             }
             // Draw the character in the texture
-            this._context.fillText(char, this._currentFreePosition.x - 0.5, this._currentFreePosition.y - this._offset - 0.5);
+            this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
             // Fill the CharInfo object
             info.topLeftUV = new BABYLON.Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
-            info.bottomRightUV = new BABYLON.Vector2(info.topLeftUV.x + (width / textureSize.width), info.topLeftUV.y + ((this._lineHeight + 2) / textureSize.height));
+            info.bottomRightUV = new BABYLON.Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeight + 2) / textureSize.height));
             info.charWidth = width;
             // Add the info structure
             this._charInfos[char] = info;
@@ -218,7 +218,7 @@ var BABYLON;
                     }
                 }
             }
-            return { height: end - start, offset: start - 1 };
+            return { height: (end - start) + 1, offset: start - 1 };
         };
         Object.defineProperty(FontTexture.prototype, "canRescale", {
             get: function () {

+ 114 - 62
src/Materials/babylon.effect.js

@@ -53,7 +53,7 @@ var BABYLON;
             var _this = this;
             this._isReady = false;
             this._compilationError = "";
-            this._valueCache = [];
+            this._valueCache = {};
             this._engine = engine;
             this.name = baseName;
             this.defines = defines;
@@ -322,143 +322,195 @@ var BABYLON;
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
         };
         Effect.prototype._cacheMatrix = function (uniformName, matrix) {
-            if (!this._valueCache[uniformName]) {
-                this._valueCache[uniformName] = new BABYLON.Matrix();
+            var changed = false;
+            var cache = this._valueCache[uniformName];
+            if (!cache || !(cache instanceof BABYLON.Matrix)) {
+                changed = true;
+                cache = new BABYLON.Matrix();
             }
+            var tm = cache.m;
+            var om = matrix.m;
             for (var index = 0; index < 16; index++) {
-                this._valueCache[uniformName].m[index] = matrix.m[index];
+                if (tm[index] !== om[index]) {
+                    tm[index] = om[index];
+                    changed = true;
+                }
             }
+            this._valueCache[uniformName] = cache;
+            return changed;
         };
         Effect.prototype._cacheFloat2 = function (uniformName, x, y) {
-            if (!this._valueCache[uniformName]) {
-                this._valueCache[uniformName] = [x, y];
-                return;
+            var cache = this._valueCache[uniformName];
+            if (!cache) {
+                cache = [x, y];
+                this._valueCache[uniformName] = cache;
+                return true;
+            }
+            var changed = false;
+            if (cache[0] !== x) {
+                cache[0] = x;
+                changed = true;
+            }
+            if (cache[1] !== y) {
+                cache[1] = y;
+                changed = true;
             }
-            this._valueCache[uniformName][0] = x;
-            this._valueCache[uniformName][1] = y;
+            this._valueCache[uniformName] = cache;
+            return changed;
         };
         Effect.prototype._cacheFloat3 = function (uniformName, x, y, z) {
-            if (!this._valueCache[uniformName]) {
-                this._valueCache[uniformName] = [x, y, z];
-                return;
+            var cache = this._valueCache[uniformName];
+            if (!cache) {
+                cache = [x, y, z];
+                this._valueCache[uniformName] = cache;
+                return true;
+            }
+            var changed = false;
+            if (cache[0] !== x) {
+                cache[0] = x;
+                changed = true;
+            }
+            if (cache[1] !== y) {
+                cache[1] = y;
+                changed = true;
+            }
+            if (cache[2] !== z) {
+                cache[2] = z;
+                changed = true;
             }
-            this._valueCache[uniformName][0] = x;
-            this._valueCache[uniformName][1] = y;
-            this._valueCache[uniformName][2] = z;
+            this._valueCache[uniformName] = cache;
+            return changed;
         };
         Effect.prototype._cacheFloat4 = function (uniformName, x, y, z, w) {
-            if (!this._valueCache[uniformName]) {
-                this._valueCache[uniformName] = [x, y, z, w];
-                return;
+            var cache = this._valueCache[uniformName];
+            if (!cache) {
+                cache = [x, y, z, w];
+                this._valueCache[uniformName] = cache;
+                return true;
+            }
+            var changed = false;
+            if (cache[0] !== x) {
+                cache[0] = x;
+                changed = true;
+            }
+            if (cache[1] !== y) {
+                cache[1] = y;
+                changed = true;
+            }
+            if (cache[2] !== z) {
+                cache[2] = z;
+                changed = true;
             }
-            this._valueCache[uniformName][0] = x;
-            this._valueCache[uniformName][1] = y;
-            this._valueCache[uniformName][2] = z;
-            this._valueCache[uniformName][3] = w;
+            if (cache[3] !== w) {
+                cache[3] = w;
+                changed = true;
+            }
+            this._valueCache[uniformName] = cache;
+            return changed;
         };
         Effect.prototype.setArray = function (uniformName, array) {
+            this._valueCache[uniformName] = null;
             this._engine.setArray(this.getUniform(uniformName), array);
             return this;
         };
         Effect.prototype.setArray2 = function (uniformName, array) {
+            this._valueCache[uniformName] = null;
             this._engine.setArray2(this.getUniform(uniformName), array);
             return this;
         };
         Effect.prototype.setArray3 = function (uniformName, array) {
+            this._valueCache[uniformName] = null;
             this._engine.setArray3(this.getUniform(uniformName), array);
             return this;
         };
         Effect.prototype.setArray4 = function (uniformName, array) {
+            this._valueCache[uniformName] = null;
             this._engine.setArray4(this.getUniform(uniformName), array);
             return this;
         };
         Effect.prototype.setMatrices = function (uniformName, matrices) {
+            this._valueCache[uniformName] = null;
             this._engine.setMatrices(this.getUniform(uniformName), matrices);
             return this;
         };
         Effect.prototype.setMatrix = function (uniformName, matrix) {
-            //if (this._valueCache[uniformName] && this._valueCache[uniformName].equals(matrix))
-            //    return this;
-            // this._cacheMatrix(uniformName, matrix);
-            this._engine.setMatrix(this.getUniform(uniformName), matrix);
+            if (this._cacheMatrix(uniformName, matrix)) {
+                this._engine.setMatrix(this.getUniform(uniformName), matrix);
+            }
             return this;
         };
         Effect.prototype.setMatrix3x3 = function (uniformName, matrix) {
+            this._valueCache[uniformName] = null;
             this._engine.setMatrix3x3(this.getUniform(uniformName), matrix);
             return this;
         };
-        Effect.prototype.setMatrix2x2 = function (uniformname, matrix) {
-            this._engine.setMatrix2x2(this.getUniform(uniformname), matrix);
+        Effect.prototype.setMatrix2x2 = function (uniformName, matrix) {
+            this._valueCache[uniformName] = null;
+            this._engine.setMatrix2x2(this.getUniform(uniformName), matrix);
             return this;
         };
         Effect.prototype.setFloat = function (uniformName, value) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName] === value)
+            var cache = this._valueCache[uniformName];
+            if (cache && cache === value)
                 return this;
             this._valueCache[uniformName] = value;
             this._engine.setFloat(this.getUniform(uniformName), value);
             return this;
         };
         Effect.prototype.setBool = function (uniformName, bool) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName] === bool)
+            var cache = this._valueCache[uniformName];
+            if (cache && cache === bool)
                 return this;
             this._valueCache[uniformName] = bool;
             this._engine.setBool(this.getUniform(uniformName), bool ? 1 : 0);
             return this;
         };
         Effect.prototype.setVector2 = function (uniformName, vector2) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === vector2.x && this._valueCache[uniformName][1] === vector2.y)
-                return this;
-            this._cacheFloat2(uniformName, vector2.x, vector2.y);
-            this._engine.setFloat2(this.getUniform(uniformName), vector2.x, vector2.y);
+            if (this._cacheFloat2(uniformName, vector2.x, vector2.y)) {
+                this._engine.setFloat2(this.getUniform(uniformName), vector2.x, vector2.y);
+            }
             return this;
         };
         Effect.prototype.setFloat2 = function (uniformName, x, y) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === x && this._valueCache[uniformName][1] === y)
-                return this;
-            this._cacheFloat2(uniformName, x, y);
-            this._engine.setFloat2(this.getUniform(uniformName), x, y);
+            if (this._cacheFloat2(uniformName, x, y)) {
+                this._engine.setFloat2(this.getUniform(uniformName), x, y);
+            }
             return this;
         };
         Effect.prototype.setVector3 = function (uniformName, vector3) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === vector3.x && this._valueCache[uniformName][1] === vector3.y && this._valueCache[uniformName][2] === vector3.z)
-                return this;
-            this._cacheFloat3(uniformName, vector3.x, vector3.y, vector3.z);
-            this._engine.setFloat3(this.getUniform(uniformName), vector3.x, vector3.y, vector3.z);
+            if (this._cacheFloat3(uniformName, vector3.x, vector3.y, vector3.z)) {
+                this._engine.setFloat3(this.getUniform(uniformName), vector3.x, vector3.y, vector3.z);
+            }
             return this;
         };
         Effect.prototype.setFloat3 = function (uniformName, x, y, z) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === x && this._valueCache[uniformName][1] === y && this._valueCache[uniformName][2] === z)
-                return this;
-            this._cacheFloat3(uniformName, x, y, z);
-            this._engine.setFloat3(this.getUniform(uniformName), x, y, z);
+            if (this._cacheFloat3(uniformName, x, y, z)) {
+                this._engine.setFloat3(this.getUniform(uniformName), x, y, z);
+            }
             return this;
         };
         Effect.prototype.setVector4 = function (uniformName, vector4) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === vector4.x && this._valueCache[uniformName][1] === vector4.y && this._valueCache[uniformName][2] === vector4.z && this._valueCache[uniformName][3] === vector4.w)
-                return this;
-            this._cacheFloat4(uniformName, vector4.x, vector4.y, vector4.z, vector4.w);
-            this._engine.setFloat4(this.getUniform(uniformName), vector4.x, vector4.y, vector4.z, vector4.w);
+            if (this._cacheFloat4(uniformName, vector4.x, vector4.y, vector4.z, vector4.w)) {
+                this._engine.setFloat4(this.getUniform(uniformName), vector4.x, vector4.y, vector4.z, vector4.w);
+            }
             return this;
         };
         Effect.prototype.setFloat4 = function (uniformName, x, y, z, w) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === x && this._valueCache[uniformName][1] === y && this._valueCache[uniformName][2] === z && this._valueCache[uniformName][3] === w)
-                return this;
-            this._cacheFloat4(uniformName, x, y, z, w);
-            this._engine.setFloat4(this.getUniform(uniformName), x, y, z, w);
+            if (this._cacheFloat4(uniformName, x, y, z, w)) {
+                this._engine.setFloat4(this.getUniform(uniformName), x, y, z, w);
+            }
             return this;
         };
         Effect.prototype.setColor3 = function (uniformName, color3) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === color3.r && this._valueCache[uniformName][1] === color3.g && this._valueCache[uniformName][2] === color3.b)
-                return this;
-            this._cacheFloat3(uniformName, color3.r, color3.g, color3.b);
-            this._engine.setColor3(this.getUniform(uniformName), color3);
+            if (this._cacheFloat3(uniformName, color3.r, color3.g, color3.b)) {
+                this._engine.setColor3(this.getUniform(uniformName), color3);
+            }
             return this;
         };
         Effect.prototype.setColor4 = function (uniformName, color3, alpha) {
-            if (this._valueCache[uniformName] && this._valueCache[uniformName][0] === color3.r && this._valueCache[uniformName][1] === color3.g && this._valueCache[uniformName][2] === color3.b && this._valueCache[uniformName][3] === alpha)
-                return this;
-            this._cacheFloat4(uniformName, color3.r, color3.g, color3.b, alpha);
-            this._engine.setColor4(this.getUniform(uniformName), color3, alpha);
+            if (this._cacheFloat4(uniformName, color3.r, color3.g, color3.b, alpha)) {
+                this._engine.setColor4(this.getUniform(uniformName), color3, alpha);
+            }
             return this;
         };
         // Statics

+ 16 - 9
src/Math/babylon.math.js

@@ -918,12 +918,16 @@ var BABYLON;
             var ch = viewport.height;
             var cx = viewport.x;
             var cy = viewport.y;
-            var viewportMatrix = Matrix.FromValues(cw / 2.0, 0, 0, 0, 0, -ch / 2.0, 0, 0, 0, 0, 1, 0, cx + cw / 2.0, ch / 2.0 + cy, 0, 1);
-            var finalMatrix = world.multiply(transform).multiply(viewportMatrix);
-            return Vector3.TransformCoordinates(vector, finalMatrix);
+            var viewportMatrix = Vector3._viewportMatrixCache ? Vector3._viewportMatrixCache : (Vector3._viewportMatrixCache = new Matrix());
+            Matrix.FromValuesToRef(cw / 2.0, 0, 0, 0, 0, -ch / 2.0, 0, 0, 0, 0, 1, 0, cx + cw / 2.0, ch / 2.0 + cy, 0, 1, viewportMatrix);
+            var matrix = Vector3._matrixCache ? Vector3._matrixCache : (Vector3._matrixCache = new Matrix());
+            world.multiplyToRef(transform, matrix);
+            matrix.multiplyToRef(viewportMatrix, matrix);
+            return Vector3.TransformCoordinates(vector, matrix);
         };
         Vector3.UnprojectFromTransform = function (source, viewportWidth, viewportHeight, world, transform) {
-            var matrix = world.multiply(transform);
+            var matrix = Vector3._matrixCache ? Vector3._matrixCache : (Vector3._matrixCache = new Matrix());
+            world.multiplyToRef(transform, matrix);
             matrix.invert();
             source.x = source.x / viewportWidth * 2 - 1;
             source.y = -(source.y / viewportHeight * 2 - 1);
@@ -935,7 +939,9 @@ var BABYLON;
             return vector;
         };
         Vector3.Unproject = function (source, viewportWidth, viewportHeight, world, view, projection) {
-            var matrix = world.multiply(view).multiply(projection);
+            var matrix = Vector3._matrixCache ? Vector3._matrixCache : (Vector3._matrixCache = new Matrix());
+            world.multiplyToRef(view, matrix);
+            matrix.multiplyToRef(projection, matrix);
             matrix.invert();
             var screenSource = new Vector3(source.x / viewportWidth * 2 - 1, -(source.y / viewportHeight * 2 - 1), source.z);
             var vector = Vector3.TransformCoordinates(screenSource, matrix);
@@ -1633,7 +1639,9 @@ var BABYLON;
             return new Quaternion(0, 0, 0, 1);
         };
         Quaternion.RotationAxis = function (axis, angle) {
-            var result = new Quaternion();
+            return Quaternion.RotationAxisToRef(axis, angle, new Quaternion());
+        };
+        Quaternion.RotationAxisToRef = function (axis, angle, result) {
             var sin = Math.sin(angle / 2);
             axis.normalize();
             result.w = Math.cos(angle / 2);
@@ -1649,9 +1657,7 @@ var BABYLON;
             return new Quaternion(array[offset], array[offset + 1], array[offset + 2], array[offset + 3]);
         };
         Quaternion.RotationYawPitchRoll = function (yaw, pitch, roll) {
-            var result = new Quaternion();
-            Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, result);
-            return result;
+            return Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, new Quaternion());
         };
         Quaternion.RotationYawPitchRollToRef = function (yaw, pitch, roll, result) {
             // Produces a quaternion from Euler angles in the z-y-x orientation (Tait-Bryan angles)
@@ -1668,6 +1674,7 @@ var BABYLON;
             result.y = (sinYaw * cosPitch * cosRoll) - (cosYaw * sinPitch * sinRoll);
             result.z = (cosYaw * cosPitch * sinRoll) - (sinYaw * sinPitch * cosRoll);
             result.w = (cosYaw * cosPitch * cosRoll) + (sinYaw * sinPitch * sinRoll);
+            return result;
         };
         Quaternion.RotationAlphaBetaGamma = function (alpha, beta, gamma) {
             var result = new Quaternion();

+ 9 - 6
src/Mesh/babylon.abstractMesh.js

@@ -357,8 +357,8 @@ var BABYLON;
             }
             var rotationQuaternion;
             if (!space || space === BABYLON.Space.LOCAL) {
-                rotationQuaternion = BABYLON.Quaternion.RotationAxis(axis, amount);
-                this.rotationQuaternion = this.rotationQuaternion.multiply(rotationQuaternion);
+                rotationQuaternion = BABYLON.Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
+                this.rotationQuaternion.multiplyToRef(rotationQuaternion, this.rotationQuaternion);
             }
             else {
                 if (this.parent) {
@@ -366,8 +366,8 @@ var BABYLON;
                     invertParentWorldMatrix.invert();
                     axis = BABYLON.Vector3.TransformNormal(axis, invertParentWorldMatrix);
                 }
-                rotationQuaternion = BABYLON.Quaternion.RotationAxis(axis, amount);
-                this.rotationQuaternion = rotationQuaternion.multiply(this.rotationQuaternion);
+                rotationQuaternion = BABYLON.Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
+                rotationQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
             }
         };
         AbstractMesh.prototype.translate = function (axis, distance, space) {
@@ -677,11 +677,12 @@ var BABYLON;
             yawCor = yawCor || 0; // default to zero if undefined
             pitchCor = pitchCor || 0;
             rollCor = rollCor || 0;
-            var dv = targetPoint.subtract(this.position);
+            var dv = AbstractMesh._lookAtVectorCache;
+            targetPoint.subtractToRef(this.position, dv);
             var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
             var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var pitch = Math.atan2(dv.y, len);
-            this.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(yaw + yawCor, pitch + pitchCor, rollCor);
+            BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
         };
         AbstractMesh.prototype.attachToBone = function (bone, affectedMesh) {
             this._meshToBoneReferal = affectedMesh;
@@ -1045,6 +1046,8 @@ var BABYLON;
         AbstractMesh._BILLBOARDMODE_Y = 2;
         AbstractMesh._BILLBOARDMODE_Z = 4;
         AbstractMesh._BILLBOARDMODE_ALL = 7;
+        AbstractMesh._rotationAxisCache = new BABYLON.Quaternion();
+        AbstractMesh._lookAtVectorCache = new BABYLON.Vector3(0, 0, 0);
         return AbstractMesh;
     })(BABYLON.Node);
     BABYLON.AbstractMesh = AbstractMesh;

+ 4 - 1
src/Mesh/babylon.geometry.js

@@ -324,7 +324,6 @@ var BABYLON;
             }
         };
         Geometry.prototype.load = function (scene, onLoaded) {
-            var _this = this;
             if (this.delayLoadState === BABYLON.Engine.DELAYLOADSTATE_LOADING) {
                 return;
             }
@@ -335,6 +334,10 @@ var BABYLON;
                 return;
             }
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_LOADING;
+            this._queueLoad(scene, onLoaded);
+        };
+        Geometry.prototype._queueLoad = function (scene, onLoaded) {
+            var _this = this;
             scene._addPendingData(this);
             BABYLON.Tools.LoadFile(this.delayLoadingFile, function (data) {
                 _this._delayLoadingFunction(JSON.parse(data), _this);

+ 27 - 24
src/Mesh/babylon.mesh.js

@@ -895,7 +895,7 @@ var BABYLON;
             this._draw(subMesh, fillMode, instancesCount);
             engine.unbindInstanceAttributes();
         };
-        Mesh.prototype._processRendering = function (subMesh, effect, fillMode, batch, hardwareInstancedRendering, onBeforeDraw) {
+        Mesh.prototype._processRendering = function (subMesh, effect, fillMode, batch, hardwareInstancedRendering, onBeforeDraw, effectiveMaterial) {
             var scene = this.getScene();
             var engine = scene.getEngine();
             if (hardwareInstancedRendering) {
@@ -905,7 +905,7 @@ var BABYLON;
                 if (batch.renderSelf[subMesh._id]) {
                     // Draw
                     if (onBeforeDraw) {
-                        onBeforeDraw(false, this.getWorldMatrix());
+                        onBeforeDraw(false, this.getWorldMatrix(), effectiveMaterial);
                     }
                     this._draw(subMesh, fillMode, this._overridenInstanceCount);
                 }
@@ -915,7 +915,7 @@ var BABYLON;
                         // World
                         var world = instance.getWorldMatrix();
                         if (onBeforeDraw) {
-                            onBeforeDraw(true, world);
+                            onBeforeDraw(true, world, effectiveMaterial);
                         }
                         // Draw
                         this._draw(subMesh, fillMode);
@@ -966,11 +966,7 @@ var BABYLON;
                 engine.setAlphaMode(effectiveMaterial.alphaMode);
             }
             // Draw
-            this._processRendering(subMesh, effect, fillMode, batch, hardwareInstancedRendering, function (isInstance, world) {
-                if (isInstance) {
-                    effectiveMaterial.bindOnlyWorldMatrix(world);
-                }
-            });
+            this._processRendering(subMesh, effect, fillMode, batch, hardwareInstancedRendering, this._onBeforeDraw);
             // Unbind
             effectiveMaterial.unbind();
             // Outline - step 2
@@ -989,6 +985,11 @@ var BABYLON;
             }
             this.onAfterRenderObservable.notifyObservers(this);
         };
+        Mesh.prototype._onBeforeDraw = function (isInstance, world, effectiveMaterial) {
+            if (isInstance) {
+                effectiveMaterial.bindOnlyWorldMatrix(world);
+            }
+        };
         /**
          * Returns an array populated with ParticleSystem objects whose the mesh is the emitter.
          */
@@ -1018,28 +1019,30 @@ var BABYLON;
             return results;
         };
         Mesh.prototype._checkDelayState = function () {
-            var _this = this;
-            var that = this;
             var scene = this.getScene();
             if (this._geometry) {
                 this._geometry.load(scene);
             }
-            else if (that.delayLoadState === BABYLON.Engine.DELAYLOADSTATE_NOTLOADED) {
-                that.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_LOADING;
-                scene._addPendingData(that);
-                var getBinaryData = (this.delayLoadingFile.indexOf(".babylonbinarymeshdata") !== -1);
-                BABYLON.Tools.LoadFile(this.delayLoadingFile, function (data) {
-                    if (data instanceof ArrayBuffer) {
-                        _this._delayLoadingFunction(data, _this);
-                    }
-                    else {
-                        _this._delayLoadingFunction(JSON.parse(data), _this);
-                    }
-                    _this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_LOADED;
-                    scene._removePendingData(_this);
-                }, function () { }, scene.database, getBinaryData);
+            else if (this.delayLoadState === BABYLON.Engine.DELAYLOADSTATE_NOTLOADED) {
+                this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_LOADING;
+                this._queueLoad(this, scene);
             }
         };
+        Mesh.prototype._queueLoad = function (mesh, scene) {
+            var _this = this;
+            scene._addPendingData(mesh);
+            var getBinaryData = (this.delayLoadingFile.indexOf(".babylonbinarymeshdata") !== -1);
+            BABYLON.Tools.LoadFile(this.delayLoadingFile, function (data) {
+                if (data instanceof ArrayBuffer) {
+                    _this._delayLoadingFunction(data, _this);
+                }
+                else {
+                    _this._delayLoadingFunction(JSON.parse(data), _this);
+                }
+                _this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_LOADED;
+                scene._removePendingData(_this);
+            }, function () { }, scene.database, getBinaryData);
+        };
         /**
          * Boolean, true is the mesh in the frustum defined by the Plane objects from the `frustumPlanes` array parameter.
          */

+ 12 - 6
src/Mesh/babylon.mesh.vertexData.js

@@ -322,6 +322,7 @@ var BABYLON;
             var pathArray = options.pathArray;
             var closeArray = options.closeArray || false;
             var closePath = options.closePath || false;
+            var invertUV = options.invertUV || false;
             var defaultOffset = Math.floor(pathArray[0].length / 2);
             var offset = options.offset || defaultOffset;
             offset = offset > defaultOffset ? defaultOffset : Math.floor(offset); // offset max allowed : defaultOffset
@@ -429,7 +430,12 @@ var BABYLON;
                 for (i = 0; i < minlg + closePathCorr; i++) {
                     u = us[p][i] / uTotalDistance[p];
                     v = vs[i][p] / vTotalDistance[i];
-                    uvs.push(u, v);
+                    if (invertUV) {
+                        uvs.push(v, u);
+                    }
+                    else {
+                        uvs.push(u, v);
+                    }
                 }
             }
             // indices
@@ -1011,10 +1017,10 @@ var BABYLON;
             return vertexData;
         };
         VertexData.CreateTiledGround = function (options) {
-            var xmin = options.xmin;
-            var zmin = options.zmin;
-            var xmax = options.xmax;
-            var zmax = options.zmax;
+            var xmin = options.xmin || -1.0;
+            var zmin = options.zmin || -1.0;
+            var xmax = options.xmax || 1.0;
+            var zmax = options.zmax || 1.0;
             var subdivisions = options.subdivisions || { w: 1, h: 1 };
             var precision = options.precision || { w: 1, h: 1 };
             var indices = [];
@@ -1022,7 +1028,7 @@ var BABYLON;
             var normals = [];
             var uvs = [];
             var row, col, tileRow, tileCol;
-            subdivisions.h = (subdivisions.w < 1) ? 1 : subdivisions.h;
+            subdivisions.h = (subdivisions.h < 1) ? 1 : subdivisions.h;
             subdivisions.w = (subdivisions.w < 1) ? 1 : subdivisions.w;
             precision.w = (precision.w < 1) ? 1 : precision.w;
             precision.h = (precision.h < 1) ? 1 : precision.h;

+ 25 - 16
src/Mesh/babylon.meshBuilder.js

@@ -85,6 +85,7 @@ var BABYLON;
          * The optional parameter `instance` is an instance of an existing Ribbon object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#ribbon
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.CreateRibbon = function (name, options, scene) {
@@ -368,6 +369,7 @@ var BABYLON;
          * Remember you can only change the shape or path point positions, not their number when updating an extruded shape.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.ExtrudeShape = function (name, options, scene) {
@@ -379,7 +381,8 @@ var BABYLON;
             var updatable = options.updatable;
             var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE;
             var instance = options.instance;
-            return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, instance);
+            var invertUV = options.invertUV || false;
+            return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, instance, invertUV);
         };
         /**
          * Creates an custom extruded shape mesh.
@@ -413,6 +416,7 @@ var BABYLON;
          * Remember you can only change the shape or path point positions, not their number when updating an extruded shape.
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.ExtrudeShapeCustom = function (name, options, scene) {
@@ -426,7 +430,8 @@ var BABYLON;
             var updatable = options.updatable;
             var sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE;
             var instance = options.instance;
-            return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, instance);
+            var invertUV = options.invertUV || false;
+            return MeshBuilder._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, instance, invertUV);
         };
         /**
          * Creates lathe mesh.
@@ -442,6 +447,7 @@ var BABYLON;
          * The parameter `cap` sets the way the extruded shape is capped. Possible values : BABYLON.Mesh.NO_CAP (default), BABYLON.Mesh.CAP_START, BABYLON.Mesh.CAP_END, BABYLON.Mesh.CAP_ALL
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.CreateLathe = function (name, options, scene) {
@@ -455,6 +461,7 @@ var BABYLON;
             var cap = options.cap || BABYLON.Mesh.NO_CAP;
             var pi2 = Math.PI * 2;
             var paths = new Array();
+            var invertUV = options.invertUV || false;
             var i = 0;
             var p = 0;
             var step = pi2 / tessellation * arc;
@@ -478,7 +485,7 @@ var BABYLON;
                 paths.push(path);
             }
             // lathe ribbon
-            var lathe = MeshBuilder.CreateRibbon(name, { pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable }, scene);
+            var lathe = MeshBuilder.CreateRibbon(name, { pathArray: paths, closeArray: closed, sideOrientation: sideOrientation, updatable: updatable, invertUV: invertUV }, scene);
             return lathe;
         };
         /**
@@ -560,19 +567,19 @@ var BABYLON;
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.CreateGroundFromHeightMap = function (name, url, options, scene) {
-            var width = options.width || 10;
-            var height = options.height || 10;
-            var subdivisions = options.subdivisions || 1;
-            var minHeight = options.minHeight;
-            var maxHeight = options.maxHeight || 10;
+            var width = options.width || 10.0;
+            var height = options.height || 10.0;
+            var subdivisions = options.subdivisions || 1 | 0;
+            var minHeight = options.minHeight || 0.0;
+            var maxHeight = options.maxHeight || 10.0;
             var updatable = options.updatable;
             var onReady = options.onReady;
             var ground = new BABYLON.GroundMesh(name, scene);
             ground._subdivisions = subdivisions;
             ground._width = width;
             ground._height = height;
-            ground._maxX = ground._width / 2;
-            ground._maxZ = ground._height / 2;
+            ground._maxX = ground._width / 2.0;
+            ground._maxZ = ground._height / 2.0;
             ground._minX = -ground._maxX;
             ground._minZ = -ground._maxZ;
             ground._setReady(false);
@@ -625,18 +632,20 @@ var BABYLON;
          * The optional parameter `instance` is an instance of an existing Tube object to be updated with the passed `pathArray` parameter : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#tube
          * You can also set the mesh side orientation with the values : BABYLON.Mesh.FRONTSIDE (default), BABYLON.Mesh.BACKSIDE or BABYLON.Mesh.DOUBLESIDE
          * Detail here : http://doc.babylonjs.com/tutorials/02._Discover_Basic_Elements#side-orientation
+         * The optional parameter `invertUV` (boolean, default false) swaps in the geometry the U and V coordinates to apply a texture.
          * The mesh can be set to updatable with the boolean parameter `updatable` (default false) if its internal geometry is supposed to change once created.
          */
         MeshBuilder.CreateTube = function (name, options, scene) {
             var path = options.path;
-            var radius = options.radius || 1;
-            var tessellation = options.tessellation || 64;
+            var radius = options.radius || 1.0;
+            var tessellation = options.tessellation || 64 | 0;
             var radiusFunction = options.radiusFunction;
             var cap = options.cap || BABYLON.Mesh.NO_CAP;
+            var invertUV = options.invertUV || false;
             var updatable = options.updatable;
             var sideOrientation = options.sideOrientation || BABYLON.Mesh.DEFAULTSIDE;
             var instance = options.instance;
-            options.arc = (options.arc <= 0 || options.arc > 1) ? 1 : options.arc || 1;
+            options.arc = (options.arc <= 0.0 || options.arc > 1.0) ? 1.0 : options.arc || 1.0;
             // tube geometry
             var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap, arc) {
                 var tangents = path3D.getTangents();
@@ -713,7 +722,7 @@ var BABYLON;
             var newPathArray = new Array();
             cap = (cap < 0 || cap > 3) ? 0 : cap;
             pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap, options.arc);
-            var tube = MeshBuilder.CreateRibbon(name, { pathArray: pathArray, closePath: true, closeArray: false, updatable: updatable, sideOrientation: sideOrientation }, scene);
+            var tube = MeshBuilder.CreateRibbon(name, { pathArray: pathArray, closePath: true, closeArray: false, updatable: updatable, sideOrientation: sideOrientation, invertUV: invertUV }, scene);
             tube.pathArray = pathArray;
             tube.path3D = path3D;
             tube.tessellation = tessellation;
@@ -921,7 +930,7 @@ var BABYLON;
             return decal;
         };
         // Privates
-        MeshBuilder._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance) {
+        MeshBuilder._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance, invertUV) {
             // extrusion geometry
             var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom) {
                 var tangents = path3D.getTangents();
@@ -1000,7 +1009,7 @@ var BABYLON;
             var newShapePaths = new Array();
             cap = (cap < 0 || cap > 3) ? 0 : cap;
             pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom);
-            var extrudedGeneric = BABYLON.Mesh.CreateRibbon(name, pathArray, rbCA, rbCP, 0, scene, updtbl, side);
+            var extrudedGeneric = MeshBuilder.CreateRibbon(name, { pathArray: pathArray, closeArray: rbCA, closePath: rbCP, updatable: updtbl, sideOrientation: side, invertUV: invertUV }, scene);
             extrudedGeneric.pathArray = pathArray;
             extrudedGeneric.path3D = path3D;
             extrudedGeneric.cap = cap;

+ 1 - 1
src/Particles/babylon.solidParticleSystem.js

@@ -279,7 +279,7 @@ var BABYLON;
                 if (this._copy.color) {
                     this._color = this._copy.color;
                 }
-                else if (meshCol && meshCol[c]) {
+                else if (meshCol && meshCol[c] !== undefined) {
                     this._color.r = meshCol[c];
                     this._color.g = meshCol[c + 1];
                     this._color.b = meshCol[c + 2];

+ 10 - 2
src/Tools/babylon.observable.js

@@ -9,9 +9,14 @@ var BABYLON;
         */
         function EventState(mask, skipNextObservers) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
+            this.initalize(mask, skipNextObservers);
+        }
+        EventState.prototype.initalize = function (mask, skipNextObservers) {
+            if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.mask = mask;
             this.skipNextObservers = skipNextObservers;
-        }
+            return this;
+        };
         return EventState;
     })();
     BABYLON.EventState = EventState;
@@ -90,7 +95,8 @@ var BABYLON;
          */
         Observable.prototype.notifyObservers = function (eventData, mask) {
             if (mask === void 0) { mask = -1; }
-            var state = new EventState(mask);
+            var state = Observable._pooledEventState ? Observable._pooledEventState.initalize(mask) : new EventState(mask);
+            Observable._pooledEventState = null;
             for (var _i = 0, _a = this._observers; _i < _a.length; _i++) {
                 var obs = _a[_i];
                 if (obs.mask & mask) {
@@ -100,6 +106,7 @@ var BABYLON;
                     break;
                 }
             }
+            Observable._pooledEventState = state;
         };
         /**
          * return true is the Observable has at least one Observer registered
@@ -121,6 +128,7 @@ var BABYLON;
             result._observers = this._observers.slice(0);
             return result;
         };
+        Observable._pooledEventState = null;
         return Observable;
     })();
     BABYLON.Observable = Observable;

+ 137 - 77
src/babylon.engine.js

@@ -55,7 +55,7 @@ var BABYLON;
         var engine = scene.getEngine();
         var potWidth = BABYLON.Tools.GetExponentOfTwo(width, engine.getCaps().maxTextureSize);
         var potHeight = BABYLON.Tools.GetExponentOfTwo(height, engine.getCaps().maxTextureSize);
-        gl.bindTexture(gl.TEXTURE_2D, texture);
+        engine._bindTextureDirectly(gl.TEXTURE_2D, texture);
         gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
         texture._baseWidth = width;
         texture._baseHeight = height;
@@ -69,7 +69,7 @@ var BABYLON;
         if (!noMipmap && !isCompressed) {
             gl.generateMipmap(gl.TEXTURE_2D);
         }
-        gl.bindTexture(gl.TEXTURE_2D, null);
+        engine._bindTextureDirectly(gl.TEXTURE_2D, null);
         engine.resetTextureCache();
         scene._removePendingData(texture);
         if (onLoad) {
@@ -153,6 +153,7 @@ var BABYLON;
             this._compiledEffects = {};
             this._uintIndicesCurrentlySet = false;
             this._currentBoundBuffer = new Array();
+            this._currentBufferPointers = [];
             this._currentInstanceLocations = new Array();
             this._currentInstanceBuffers = new Array();
             this._renderingCanvas = canvas;
@@ -681,7 +682,7 @@ var BABYLON;
         Engine.prototype.bindFramebuffer = function (texture, faceIndex, requiredWidth, requiredHeight) {
             this._currentRenderTarget = texture;
             var gl = this._gl;
-            gl.bindFramebuffer(gl.FRAMEBUFFER, texture._framebuffer);
+            this.bindUnboundFramebuffer(texture._framebuffer);
             if (texture.isCube) {
                 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture, 0);
             }
@@ -691,23 +692,29 @@ var BABYLON;
             this._gl.viewport(0, 0, requiredWidth || texture._width, requiredHeight || texture._height);
             this.wipeCaches();
         };
+        Engine.prototype.bindUnboundFramebuffer = function (framebuffer) {
+            if (this._currentFramebuffer !== framebuffer) {
+                this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, framebuffer);
+                this._currentFramebuffer = framebuffer;
+            }
+        };
         Engine.prototype.unBindFramebuffer = function (texture, disableGenerateMipMaps) {
             if (disableGenerateMipMaps === void 0) { disableGenerateMipMaps = false; }
             this._currentRenderTarget = null;
             if (texture.generateMipMaps && !disableGenerateMipMaps) {
                 var gl = this._gl;
-                gl.bindTexture(gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(gl.TEXTURE_2D, texture);
                 gl.generateMipmap(gl.TEXTURE_2D);
-                gl.bindTexture(gl.TEXTURE_2D, null);
+                this._bindTextureDirectly(gl.TEXTURE_2D, null);
             }
-            this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
+            this.bindUnboundFramebuffer(null);
         };
         Engine.prototype.generateMipMapsForCubemap = function (texture) {
             if (texture.generateMipMaps) {
                 var gl = this._gl;
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
             }
         };
         Engine.prototype.flushFramebuffer = function () {
@@ -715,18 +722,18 @@ var BABYLON;
         };
         Engine.prototype.restoreDefaultFramebuffer = function () {
             this._currentRenderTarget = null;
-            this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
+            this.bindUnboundFramebuffer(null);
             this.setViewport(this._cachedViewport);
             this.wipeCaches();
         };
         // VBOs
         Engine.prototype._resetVertexBufferBinding = function () {
-            this.bindBuffer(null, this._gl.ARRAY_BUFFER);
+            this.bindArrayBuffer(null);
             this._cachedVertexBuffers = null;
         };
         Engine.prototype.createVertexBuffer = function (vertices) {
             var vbo = this._gl.createBuffer();
-            this.bindBuffer(vbo, this._gl.ARRAY_BUFFER);
+            this.bindArrayBuffer(vbo);
             if (vertices instanceof Float32Array) {
                 this._gl.bufferData(this._gl.ARRAY_BUFFER, vertices, this._gl.STATIC_DRAW);
             }
@@ -739,7 +746,7 @@ var BABYLON;
         };
         Engine.prototype.createDynamicVertexBuffer = function (vertices) {
             var vbo = this._gl.createBuffer();
-            this.bindBuffer(vbo, this._gl.ARRAY_BUFFER);
+            this.bindArrayBuffer(vbo);
             if (vertices instanceof Float32Array) {
                 this._gl.bufferData(this._gl.ARRAY_BUFFER, vertices, this._gl.DYNAMIC_DRAW);
             }
@@ -751,7 +758,7 @@ var BABYLON;
             return vbo;
         };
         Engine.prototype.updateDynamicVertexBuffer = function (vertexBuffer, vertices, offset, count) {
-            this.bindBuffer(vertexBuffer, this._gl.ARRAY_BUFFER);
+            this.bindArrayBuffer(vertexBuffer);
             if (offset === undefined) {
                 offset = 0;
             }
@@ -774,12 +781,12 @@ var BABYLON;
             this._resetVertexBufferBinding();
         };
         Engine.prototype._resetIndexBufferBinding = function () {
-            this.bindBuffer(null, this._gl.ELEMENT_ARRAY_BUFFER);
+            this.bindIndexBuffer(null);
             this._cachedIndexBuffer = null;
         };
         Engine.prototype.createIndexBuffer = function (indices) {
             var vbo = this._gl.createBuffer();
-            this.bindBuffer(vbo, this._gl.ELEMENT_ARRAY_BUFFER);
+            this.bindIndexBuffer(vbo);
             // Check for 32 bits indices
             var arrayBuffer;
             var need32Bits = false;
@@ -804,8 +811,8 @@ var BABYLON;
         Engine.prototype.bindArrayBuffer = function (buffer) {
             this.bindBuffer(buffer, this._gl.ARRAY_BUFFER);
         };
-        Engine.prototype.updateArrayBuffer = function (data) {
-            this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, data);
+        Engine.prototype.bindIndexBuffer = function (buffer) {
+            this.bindBuffer(buffer, this._gl.ELEMENT_ARRAY_BUFFER);
         };
         Engine.prototype.bindBuffer = function (buffer, target) {
             if (this._currentBoundBuffer[target] !== buffer) {
@@ -813,23 +820,63 @@ var BABYLON;
                 this._currentBoundBuffer[target] = buffer;
             }
         };
+        Engine.prototype.updateArrayBuffer = function (data) {
+            this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, data);
+        };
+        Engine.prototype.vertexAttribPointer = function (buffer, indx, size, type, normalized, stride, offset) {
+            var pointer = this._currentBufferPointers[indx];
+            var changed = false;
+            if (!pointer) {
+                changed = true;
+                this._currentBufferPointers[indx] = { indx: indx, size: size, type: type, normalized: normalized, stride: stride, offset: offset, buffer: buffer };
+            }
+            else {
+                if (pointer.buffer !== buffer) {
+                    pointer.buffer = buffer;
+                    changed = true;
+                }
+                if (pointer.size !== size) {
+                    pointer.size = size;
+                    changed = true;
+                }
+                if (pointer.type !== type) {
+                    pointer.type = type;
+                    changed = true;
+                }
+                if (pointer.normalized !== normalized) {
+                    pointer.normalized = normalized;
+                    changed = true;
+                }
+                if (pointer.stride !== stride) {
+                    pointer.stride = stride;
+                    changed = true;
+                }
+                if (pointer.offset !== offset) {
+                    pointer.offset = offset;
+                    changed = true;
+                }
+            }
+            if (changed) {
+                this.bindArrayBuffer(buffer);
+                this._gl.vertexAttribPointer(indx, size, type, normalized, stride, offset);
+            }
+        };
         Engine.prototype.bindBuffersDirectly = function (vertexBuffer, indexBuffer, vertexDeclaration, vertexStrideSize, effect) {
             if (this._cachedVertexBuffers !== vertexBuffer || this._cachedEffectForVertexBuffers !== effect) {
                 this._cachedVertexBuffers = vertexBuffer;
                 this._cachedEffectForVertexBuffers = effect;
-                this.bindBuffer(vertexBuffer, this._gl.ARRAY_BUFFER);
                 var offset = 0;
                 for (var index = 0; index < vertexDeclaration.length; index++) {
                     var order = effect.getAttributeLocation(index);
                     if (order >= 0) {
-                        this._gl.vertexAttribPointer(order, vertexDeclaration[index], this._gl.FLOAT, false, vertexStrideSize, offset);
+                        this.vertexAttribPointer(vertexBuffer, order, vertexDeclaration[index], this._gl.FLOAT, false, vertexStrideSize, offset);
                     }
                     offset += vertexDeclaration[index] * 4;
                 }
             }
             if (this._cachedIndexBuffer !== indexBuffer) {
                 this._cachedIndexBuffer = indexBuffer;
-                this.bindBuffer(indexBuffer, this._gl.ELEMENT_ARRAY_BUFFER);
+                this.bindIndexBuffer(indexBuffer);
                 this._uintIndicesCurrentlySet = indexBuffer.is32Bits;
             }
         };
@@ -846,8 +893,7 @@ var BABYLON;
                             continue;
                         }
                         var buffer = vertexBuffer.getBuffer();
-                        this.bindBuffer(buffer, this._gl.ARRAY_BUFFER);
-                        this._gl.vertexAttribPointer(order, vertexBuffer.getSize(), this._gl.FLOAT, false, vertexBuffer.getStrideSize() * 4, vertexBuffer.getOffset() * 4);
+                        this.vertexAttribPointer(buffer, order, vertexBuffer.getSize(), this._gl.FLOAT, false, vertexBuffer.getStrideSize() * 4, vertexBuffer.getOffset() * 4);
                         if (vertexBuffer.getIsInstanced()) {
                             this._caps.instancedArrays.vertexAttribDivisorANGLE(order, 1);
                             this._currentInstanceLocations.push(order);
@@ -858,7 +904,7 @@ var BABYLON;
             }
             if (indexBuffer != null && this._cachedIndexBuffer !== indexBuffer) {
                 this._cachedIndexBuffer = indexBuffer;
-                this.bindBuffer(indexBuffer, this._gl.ELEMENT_ARRAY_BUFFER);
+                this.bindIndexBuffer(indexBuffer);
                 this._uintIndicesCurrentlySet = indexBuffer.is32Bits;
             }
         };
@@ -868,7 +914,7 @@ var BABYLON;
                 var instancesBuffer = this._currentInstanceBuffers[i];
                 if (boundBuffer != instancesBuffer) {
                     boundBuffer = instancesBuffer;
-                    this.bindBuffer(instancesBuffer, this._gl.ARRAY_BUFFER);
+                    this.bindArrayBuffer(instancesBuffer);
                 }
                 var offsetLocation = this._currentInstanceLocations[i];
                 this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation, 0);
@@ -887,7 +933,7 @@ var BABYLON;
         Engine.prototype.createInstancesBuffer = function (capacity) {
             var buffer = this._gl.createBuffer();
             buffer.capacity = capacity;
-            this.bindBuffer(buffer, this._gl.ARRAY_BUFFER);
+            this.bindArrayBuffer(buffer);
             this._gl.bufferData(this._gl.ARRAY_BUFFER, capacity, this._gl.DYNAMIC_DRAW);
             return buffer;
         };
@@ -895,7 +941,7 @@ var BABYLON;
             this._gl.deleteBuffer(buffer);
         };
         Engine.prototype.updateAndBindInstancesBuffer = function (instancesBuffer, data, offsetLocations) {
-            this.bindBuffer(instancesBuffer, this._gl.ARRAY_BUFFER);
+            this.bindArrayBuffer(instancesBuffer);
             if (data) {
                 this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, data);
             }
@@ -908,7 +954,7 @@ var BABYLON;
                 for (var i = 0; i < offsetLocations.length; i++) {
                     var ai = offsetLocations[i];
                     this._gl.enableVertexAttribArray(ai.index);
-                    this._gl.vertexAttribPointer(ai.index, ai.attributeSize, ai.attribyteType || this._gl.FLOAT, ai.normalized || false, stride, ai.offset);
+                    this.vertexAttribPointer(instancesBuffer, ai.index, ai.attributeSize, ai.attribyteType || this._gl.FLOAT, ai.normalized || false, stride, ai.offset);
                     this._caps.instancedArrays.vertexAttribDivisorANGLE(ai.index, 1);
                     this._currentInstanceLocations.push(ai.index);
                     this._currentInstanceBuffers.push(instancesBuffer);
@@ -918,7 +964,7 @@ var BABYLON;
                 for (var index = 0; index < 4; index++) {
                     var offsetLocation = offsetLocations[index];
                     this._gl.enableVertexAttribArray(offsetLocation);
-                    this._gl.vertexAttribPointer(offsetLocation, 4, this._gl.FLOAT, false, 64, index * 16);
+                    this.vertexAttribPointer(instancesBuffer, offsetLocation, 4, this._gl.FLOAT, false, 64, index * 16);
                     this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation, 1);
                     this._currentInstanceLocations.push(offsetLocation);
                     this._currentInstanceBuffers.push(instancesBuffer);
@@ -1039,7 +1085,7 @@ var BABYLON;
             this._vertexAttribArraysToUse = this._vertexAttribArraysToUse || [];
             this._vertexAttribArraysEnabled = this._vertexAttribArraysEnabled || [];
             // Use program
-            this._gl.useProgram(effect.getProgram());
+            this.setProgram(effect.getProgram());
             var i, ul;
             for (i = 0, ul = this._vertexAttribArraysToUse.length; i < ul; i++) {
                 this._vertexAttribArraysToUse[i] = false;
@@ -1238,7 +1284,7 @@ var BABYLON;
         };
         Engine.prototype.setSamplingMode = function (texture, samplingMode) {
             var gl = this._gl;
-            gl.bindTexture(gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(gl.TEXTURE_2D, texture);
             var magFilter = gl.NEAREST;
             var minFilter = gl.NEAREST;
             if (samplingMode === BABYLON.Texture.BILINEAR_SAMPLINGMODE) {
@@ -1251,7 +1297,7 @@ var BABYLON;
             }
             gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
             gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
-            gl.bindTexture(gl.TEXTURE_2D, null);
+            this._bindTextureDirectly(gl.TEXTURE_2D, null);
             texture.samplingMode = samplingMode;
         };
         Engine.prototype.createTexture = function (url, noMipmap, invertY, scene, samplingMode, onLoad, onError, buffer) {
@@ -1377,7 +1423,7 @@ var BABYLON;
         Engine.prototype.updateRawTexture = function (texture, data, format, invertY, compression) {
             if (compression === void 0) { compression = null; }
             var internalFormat = this._getInternalFormat(format);
-            this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
             if (texture._width % 4 !== 0) {
                 this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
@@ -1391,7 +1437,7 @@ var BABYLON;
             if (texture.generateMipMaps) {
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
             }
-            this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             this.resetTextureCache();
             texture.isReady = true;
         };
@@ -1404,12 +1450,12 @@ var BABYLON;
             texture._height = height;
             texture.references = 1;
             this.updateRawTexture(texture, data, format, invertY, compression);
-            this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
             // Filters
             var filters = getSamplingParameters(samplingMode, generateMipMaps, this._gl);
             this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
             this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
-            this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             texture.samplingMode = samplingMode;
             this._loadedTexturesCache.push(texture);
             return texture;
@@ -1436,21 +1482,21 @@ var BABYLON;
         Engine.prototype.updateTextureSamplingMode = function (samplingMode, texture) {
             var filters = getSamplingParameters(samplingMode, texture.generateMipMaps, this._gl);
             if (texture.isCube) {
-                this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, texture);
+                this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MIN_FILTER, filters.min);
-                this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
+                this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             }
             else {
-                this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
-                this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             }
         };
         Engine.prototype.updateDynamicTexture = function (texture, canvas, invertY, premulAlpha) {
             if (premulAlpha === void 0) { premulAlpha = false; }
-            this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY ? 1 : 0);
             if (premulAlpha) {
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
@@ -1459,7 +1505,7 @@ var BABYLON;
             if (texture.generateMipMaps) {
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
             }
-            this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             this.resetTextureCache();
             texture.isReady = true;
         };
@@ -1467,7 +1513,7 @@ var BABYLON;
             if (texture._isDisabled) {
                 return;
             }
-            this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY ? 0 : 1); // Video are upside down by default
             try {
                 // Testing video texture support
@@ -1497,7 +1543,7 @@ var BABYLON;
                 if (texture.generateMipMaps) {
                     this._gl.generateMipmap(this._gl.TEXTURE_2D);
                 }
-                this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this.resetTextureCache();
                 texture.isReady = true;
             }
@@ -1529,7 +1575,7 @@ var BABYLON;
             }
             var gl = this._gl;
             var texture = gl.createTexture();
-            gl.bindTexture(gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(gl.TEXTURE_2D, texture);
             var width = size.width || size;
             var height = size.height || size;
             var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
@@ -1551,7 +1597,7 @@ var BABYLON;
             }
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
-            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+            this.bindUnboundFramebuffer(framebuffer);
             if (generateDepthBuffer) {
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
             }
@@ -1559,9 +1605,9 @@ var BABYLON;
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
             }
             // Unbind
-            gl.bindTexture(gl.TEXTURE_2D, null);
+            this._bindTextureDirectly(gl.TEXTURE_2D, null);
             gl.bindRenderbuffer(gl.RENDERBUFFER, null);
-            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+            this.bindUnboundFramebuffer(null);
             texture._framebuffer = framebuffer;
             if (generateDepthBuffer) {
                 texture._depthBuffer = depthBuffer;
@@ -1595,7 +1641,7 @@ var BABYLON;
             texture.references = 1;
             texture.samplingMode = samplingMode;
             var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
-            gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
             for (var face = 0; face < 6; face++) {
                 gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
             }
@@ -1609,17 +1655,17 @@ var BABYLON;
             gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
-            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+            this.bindUnboundFramebuffer(framebuffer);
             gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
             // Mipmaps
             if (texture.generateMipMaps) {
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
             }
             // Unbind
-            gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
             gl.bindRenderbuffer(gl.RENDERBUFFER, null);
-            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+            this.bindUnboundFramebuffer(null);
             texture._framebuffer = framebuffer;
             texture._depthBuffer = depthBuffer;
             this.resetTextureCache();
@@ -1641,7 +1687,7 @@ var BABYLON;
                 BABYLON.Tools.LoadFile(rootUrl, function (data) {
                     var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
                     var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
-                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                    _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
                     BABYLON.Internals.DDSTools.UploadDDSLevels(_this._gl, _this.getCaps().s3tc, data, info, loadMipmap, 6);
                     if (!noMipmap && !info.isFourCC && info.mipmapCount === 1) {
@@ -1651,7 +1697,7 @@ var BABYLON;
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+                    _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
                     _this.resetTextureCache();
                     texture._width = info.width;
                     texture._height = info.height;
@@ -1669,7 +1715,7 @@ var BABYLON;
                         gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
                         gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
                     ];
-                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                    _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
                     for (var index = 0; index < faces.length; index++) {
                         _this._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
@@ -1682,7 +1728,7 @@ var BABYLON;
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, noMipmap ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-                    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+                    _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
                     _this.resetTextureCache();
                     texture._width = width;
                     texture._height = height;
@@ -1728,7 +1774,7 @@ var BABYLON;
                 width = texture._width;
                 height = texture._height;
                 isPot = (BABYLON.Tools.IsExponentOfTwo(width) && BABYLON.Tools.IsExponentOfTwo(height));
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
                 if (!noMipmap && isPot) {
                     if (mipmmapGenerator) {
@@ -1803,7 +1849,7 @@ var BABYLON;
                 }
                 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                 gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-                gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+                _this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
                 texture.isReady = true;
                 _this.resetTextureCache();
                 scene._removePendingData(texture);
@@ -1830,8 +1876,14 @@ var BABYLON;
                 this._loadedTexturesCache.splice(index, 1);
             }
         };
+        Engine.prototype.setProgram = function (program) {
+            if (this._currentProgram !== program) {
+                this._gl.useProgram(program);
+                this._currentProgram = program;
+            }
+        };
         Engine.prototype.bindSamplers = function (effect) {
-            this._gl.useProgram(effect.getProgram());
+            this.setProgram(effect.getProgram());
             var samplers = effect.getSamplers();
             for (var index = 0; index < samplers.length; index++) {
                 var uniform = effect.getUniform(samplers[index]);
@@ -1839,23 +1891,33 @@ var BABYLON;
             }
             this._currentEffect = null;
         };
+        Engine.prototype.activateTexture = function (texture) {
+            if (this._activeTexture !== texture) {
+                this._gl.activeTexture(texture);
+                this._activeTexture = texture;
+            }
+        };
+        Engine.prototype._bindTextureDirectly = function (target, texture) {
+            if (this._activeTexturesCache[this._activeTexture] !== texture) {
+                this._gl.bindTexture(target, texture);
+                this._activeTexturesCache[this._activeTexture] = texture;
+            }
+        };
         Engine.prototype._bindTexture = function (channel, texture) {
             if (channel < 0) {
                 return;
             }
-            this._gl.activeTexture(this._gl["TEXTURE" + channel]);
-            this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
-            this._activeTexturesCache[channel] = null;
+            this.activateTexture(this._gl["TEXTURE" + channel]);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
         };
         Engine.prototype.setTextureFromPostProcess = function (channel, postProcess) {
             this._bindTexture(channel, postProcess._textures.data[postProcess._currentRenderTextureInd]);
         };
         Engine.prototype.unbindAllTextures = function () {
             for (var channel = 0; channel < this._caps.maxTexturesImageUnits; channel++) {
-                this._gl.activeTexture(this._gl["TEXTURE" + channel]);
-                this._gl.bindTexture(this._gl.TEXTURE_2D, null);
-                this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
-                this._activeTexturesCache[channel] = null;
+                this.activateTexture(this._gl["TEXTURE" + channel]);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
+                this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             }
         };
         Engine.prototype.setTexture = function (channel, texture) {
@@ -1865,17 +1927,16 @@ var BABYLON;
             // Not ready?
             if (!texture || !texture.isReady()) {
                 if (this._activeTexturesCache[channel] != null) {
-                    this._gl.activeTexture(this._gl["TEXTURE" + channel]);
-                    this._gl.bindTexture(this._gl.TEXTURE_2D, null);
-                    this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
-                    this._activeTexturesCache[channel] = null;
+                    this.activateTexture(this._gl["TEXTURE" + channel]);
+                    this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
+                    this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
                 }
                 return;
             }
             // Video
             var alreadyActivated = false;
             if (texture instanceof BABYLON.VideoTexture) {
-                this._gl.activeTexture(this._gl["TEXTURE" + channel]);
+                this.activateTexture(this._gl["TEXTURE" + channel]);
                 alreadyActivated = true;
                 texture.update();
             }
@@ -1883,16 +1944,15 @@ var BABYLON;
                 texture.delayLoad();
                 return;
             }
-            if (this._activeTexturesCache[channel] === texture) {
+            var internalTexture = texture.getInternalTexture();
+            if (this._activeTexturesCache[channel] === internalTexture) {
                 return;
             }
-            this._activeTexturesCache[channel] = texture;
-            var internalTexture = texture.getInternalTexture();
             if (!alreadyActivated) {
-                this._gl.activeTexture(this._gl["TEXTURE" + channel]);
+                this.activateTexture(this._gl["TEXTURE" + channel]);
             }
             if (internalTexture.isCube) {
-                this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, internalTexture);
+                this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, internalTexture);
                 if (internalTexture._cachedCoordinatesMode !== texture.coordinatesMode) {
                     internalTexture._cachedCoordinatesMode = texture.coordinatesMode;
                     // CUBIC_MODE and SKYBOX_MODE both require CLAMP_TO_EDGE.  All other modes use REPEAT.
@@ -1903,7 +1963,7 @@ var BABYLON;
                 this._setAnisotropicLevel(this._gl.TEXTURE_CUBE_MAP, texture);
             }
             else {
-                this._gl.bindTexture(this._gl.TEXTURE_2D, internalTexture);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, internalTexture);
                 if (internalTexture._cachedWrapU !== texture.wrapU) {
                     internalTexture._cachedWrapU = texture.wrapU;
                     switch (texture.wrapU) {