Browse Source

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

gleborgne 9 years ago
parent
commit
4614e87448
50 changed files with 53720 additions and 53388 deletions
  1. 2 1
      Exporters/Blender/io_export_babylon.py
  2. 19 19
      dist/preview release/babylon.core.js
  3. 1242 1146
      dist/preview release/babylon.d.ts
  4. 30 40
      dist/preview release/babylon.js
  5. 41899 42324
      dist/preview release/babylon.max.js
  6. 26 26
      dist/preview release/babylon.noworker.js
  7. 16 1
      materialsLibrary/config.json
  8. 178 0
      materialsLibrary/dist/babylon.gridMaterial.js
  9. 1 0
      materialsLibrary/dist/babylon.gridMaterial.min.js
  10. 8 4
      materialsLibrary/dist/babylon.pbrMaterial.js
  11. 2 2
      materialsLibrary/dist/babylon.pbrMaterial.min.js
  12. 55 0
      materialsLibrary/dist/dts/babylon.gridMaterial.d.ts
  13. 192 0
      materialsLibrary/materials/grid/babylon.gridmaterial.ts
  14. 94 0
      materialsLibrary/materials/grid/grid.fragment.fx
  15. 19 0
      materialsLibrary/materials/grid/grid.vertex.fx
  16. 3 0
      materialsLibrary/materials/grid/legacygrid.fragment.fx
  17. 11 0
      materialsLibrary/materials/grid/legacygrid.vertex.fx
  18. 31 0
      materialsLibrary/materials/pbr/includes/harmonicsFunctions.fx
  19. 0 18
      materialsLibrary/materials/pbr/includes/helperFunctions.fx
  20. 197 0
      materialsLibrary/materials/pbr/includes/pbrFunctions.fx
  21. 130 0
      materialsLibrary/materials/pbr/includes/pbrLightFunctions.fx
  22. 50 0
      materialsLibrary/materials/pbr/includes/pbrLightFunctionsCall.fx
  23. 171 0
      materialsLibrary/materials/pbr/includes/pbrShadowFunctions.fx
  24. 9 420
      materialsLibrary/materials/pbr/legacypbr.fragment.fx
  25. 12 713
      materialsLibrary/materials/pbr/pbr.fragment.fx
  26. 1 1
      materialsLibrary/materials/pbr/pbr.vertex.fx
  27. 65 0
      materialsLibrary/test/add/addgrid.js
  28. 8 1
      materialsLibrary/test/index.html
  29. 702 680
      src/Animations/babylon.animation.js
  30. 23 0
      src/Animations/babylon.animation.ts
  31. 129 129
      src/Bones/babylon.bone.js
  32. 1 1
      src/Bones/babylon.bone.ts
  33. 296 273
      src/Bones/babylon.skeleton.js
  34. 27 2
      src/Bones/babylon.skeleton.ts
  35. 628 620
      src/Cameras/babylon.camera.js
  36. 16 0
      src/Cameras/babylon.camera.ts
  37. 177 162
      src/Lights/babylon.light.js
  38. 16 0
      src/Lights/babylon.light.ts
  39. 490 412
      src/Loading/Plugins/babylon.babylonFileLoader.js
  40. 426 356
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  41. 229 186
      src/Loading/babylon.sceneLoader.js
  42. 31 0
      src/Loading/babylon.sceneLoader.ts
  43. 328 318
      src/Materials/babylon.material.js
  44. 11 0
      src/Materials/babylon.material.ts
  45. 2913 2856
      src/Math/babylon.math.js
  46. 65 4
      src/Math/babylon.math.ts
  47. 981 966
      src/Mesh/babylon.abstractMesh.js
  48. 16 0
      src/Mesh/babylon.abstractMesh.ts
  49. 1724 1707
      src/Mesh/babylon.mesh.js
  50. 20 0
      src/Mesh/babylon.mesh.ts

+ 2 - 1
Exporters/Blender/io_export_babylon.py

@@ -1,7 +1,7 @@
 bl_info = {
     'name': 'Babylon.js',
     'author': 'David Catuhe, Jeff Palmer',
-    'version': (4, 4, 1),
+    'version': (4, 4, 2),
     'blender': (2, 75, 0),
     'location': 'File > Export > Babylon.js (.babylon)',
     'description': 'Export Babylon.js scenes (.babylon)',
@@ -399,6 +399,7 @@ class Main(bpy.types.Operator, bpy_extras.io_utils.ExportHelper):
         # Open file
         file_handler = io.open(self.filepathMinusExtension + '.babylon', 'w', encoding='utf8')
         file_handler.write('{')
+        file_handler.write('"producer":{"name":"Blender","version":"' + bpy.app.version_string + '","exporter_version":"' + format_version() + '","file":"' + Main.nameSpace + '.babylon"},\n')
         self.world.to_scene_file(file_handler)
 
         # Materials

File diff suppressed because it is too large
+ 19 - 19
dist/preview release/babylon.core.js


File diff suppressed because it is too large
+ 1242 - 1146
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 30 - 40
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 41899 - 42324
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 26 - 26
dist/preview release/babylon.noworker.js


+ 16 - 1
materialsLibrary/config.json

@@ -12,7 +12,11 @@
     {
       "file": "materials/pbr/babylon.pbrMaterial.ts",
       "referenceFiles": [
-        "materials/pbr/includes/helperFunctions.fx"  
+        "materials/pbr/includes/pbrFunctions.fx",
+        "materials/pbr/includes/harmonicsFunctions.fx",
+        "materials/pbr/includes/pbrLightFunctions.fx",
+        "materials/pbr/includes/pbrLightFunctionsCall.fx",
+        "materials/pbr/includes/pbrShadowFunctions.fx"  
       ],
       "shaderFiles": [
         "materials/pbr/pbr.vertex.fx",
@@ -94,6 +98,17 @@
         "materials/sky/sky.fragment.fx"
       ],
       "output": "babylon.skyMaterial.js"
+    },
+    {
+      "file": "materials/grid/babylon.gridMaterial.ts",
+      "shaderFiles": [
+        "materials/grid/grid.vertex.fx",
+        "materials/grid/grid.fragment.fx",
+        "materials/grid/legacygrid.vertex.fx",
+        "materials/grid/legacygrid.fragment.fx"
+      ],
+      "output": "babylon.gridMaterial.js",
+      "declarationFilename": "babylon.gridMaterial.d.ts"
     }
   ],
   "build": {

File diff suppressed because it is too large
+ 178 - 0
materialsLibrary/dist/babylon.gridMaterial.js


File diff suppressed because it is too large
+ 1 - 0
materialsLibrary/dist/babylon.gridMaterial.min.js


File diff suppressed because it is too large
+ 8 - 4
materialsLibrary/dist/babylon.pbrMaterial.js


File diff suppressed because it is too large
+ 2 - 2
materialsLibrary/dist/babylon.pbrMaterial.min.js


+ 55 - 0
materialsLibrary/dist/dts/babylon.gridMaterial.d.ts

@@ -0,0 +1,55 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+declare module BABYLON {
+    /**
+     * The grid materials allows you to wrap any shape with a grid.
+     * Colors are customizable.
+     */
+    class GridMaterial extends BABYLON.Material {
+        /**
+         * Main color of the grid (e.g. between lines)
+         */
+        mainColor: Color3;
+        /**
+         * Color of the grid lines.
+         */
+        lineColor: Color3;
+        /**
+         * The scale of the grid compared to unit.
+         */
+        gridRatio: number;
+        /**
+         * The frequency of thicker lines.
+         */
+        majorUnitFrequency: number;
+        /**
+         * The visibility of minor units in the grid.
+         */
+        minorUnitVisibility: number;
+        /**
+         * The grid opacity outside of the lines.
+         */
+        opacity: number;
+        private _gridControl;
+        private _renderId;
+        private _defines;
+        private _cachedDefines;
+        /**
+         * constructor
+         * @param name The name given to the material in order to identify it afterwards.
+         * @param scene The scene the material is used in.
+         */
+        constructor(name: string, scene: Scene);
+        /**
+         * Returns wehter or not the grid requires alpha blending.
+         */
+        needAlphaBlending(): boolean;
+        private _checkCache(scene, mesh?, useInstances?);
+        isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean;
+        bindOnlyWorldMatrix(world: Matrix): void;
+        bind(world: Matrix, mesh?: Mesh): void;
+        dispose(forceDisposeEffect?: boolean): void;
+        clone(name: string): GridMaterial;
+        serialize(): any;
+        static Parse(source: any, scene: Scene, rootUrl: string): GridMaterial;
+    }
+}

+ 192 - 0
materialsLibrary/materials/grid/babylon.gridmaterial.ts

@@ -0,0 +1,192 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    class GRIDMaterialDefines extends MaterialDefines {
+        public TRANSPARENT = false;
+
+        constructor() {
+            super();
+            this._keys = Object.keys(this);
+        }
+    }
+    
+    /**
+     * The grid materials allows you to wrap any shape with a grid.
+     * Colors are customizable.
+     */
+    export class GridMaterial extends BABYLON.Material {
+        
+        /**
+         * Main color of the grid (e.g. between lines)
+         */
+        @serializeAsColor3()
+        public mainColor = Color3.White();
+        
+        /**
+         * Color of the grid lines.
+         */
+        @serializeAsColor3()
+        public lineColor = Color3.Black();
+        
+        /**
+         * The scale of the grid compared to unit.
+         */
+        @serialize()
+        public gridRatio = 1.0;
+        
+        /**
+         * The frequency of thicker lines.
+         */
+        @serialize()
+        public majorUnitFrequency = 10;
+        
+        /**
+         * The visibility of minor units in the grid.
+         */
+        @serialize()
+        public minorUnitVisibility = 0.33;
+        
+        /**
+         * The grid opacity outside of the lines.
+         */
+        @serialize()
+        public opacity = 1.0;
+        
+        private _gridControl: Vector4 = new Vector4(this.gridRatio, this.majorUnitFrequency, this.minorUnitVisibility, this.opacity);
+        
+        private _renderId: number;
+        private _defines = new GRIDMaterialDefines();
+        private _cachedDefines = new GRIDMaterialDefines();
+        
+        /**
+         * constructor
+         * @param name The name given to the material in order to identify it afterwards.
+         * @param scene The scene the material is used in.
+         */
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+        }
+        
+        /**
+         * Returns wehter or not the grid requires alpha blending.
+         */
+        public needAlphaBlending(): boolean {
+            return this.opacity < 1.0;
+        }
+          
+        private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (!mesh) {
+                return true;
+            }
+
+            if (mesh._materialDefines && mesh._materialDefines.isEqual(this._defines)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (this.checkReadyOnlyOnce) {
+                if (this._wasPreviouslyReady) {
+                    return true;
+                }
+            }
+
+            var scene = this.getScene();
+
+            if (!this.checkReadyOnEveryCall) {
+                if (this._renderId === scene.getRenderId()) {
+                    if (this._checkCache(scene, mesh, useInstances)) {
+                        return true;
+                    }
+                }
+            }
+
+            var engine = scene.getEngine();
+            var needNormals = true;
+
+            this._defines.reset();
+
+            if (this.opacity < 1.0) {
+                this._defines.TRANSPARENT = true;
+            }
+
+            // Get correct effect      
+            if (!this._effect || !this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+                scene.resetCachedMaterial();
+
+                // Attributes
+                var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
+
+                // Effect
+                var shaderName = scene.getEngine().getCaps().standardDerivatives ? "grid" : "legacygrid";
+                
+                // Defines
+                var join = this._defines.toString();
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs,
+                    ["worldViewProjection", "mainColor", "lineColor", "gridControl"],
+                    [],
+                    join, 
+                    null, 
+                    this.onCompiled, 
+                    this.onError);
+            }
+            
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            this._renderId = scene.getRenderId();
+            this._wasPreviouslyReady = true;
+
+            return true;
+        }
+        
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            var scene = this.getScene();
+
+            this._effect.setMatrix("worldViewProjection", world.multiply(scene.getTransformMatrix()));
+        }
+
+        public bind(world: Matrix, mesh?: Mesh): void {
+            var scene = this.getScene();
+
+            // Matrices        
+            this.bindOnlyWorldMatrix(world);
+            
+            // Uniforms
+            if (scene.getCachedMaterial() !== (<BABYLON.Material>this)) {
+                this._effect.setColor3("mainColor", this.mainColor);
+                this._effect.setColor3("lineColor", this.lineColor);
+                
+                this._gridControl.x = this.gridRatio;
+                this._gridControl.y = Math.round(this.majorUnitFrequency);
+                this._gridControl.z = this.minorUnitVisibility;
+                this._gridControl.w = this.opacity;
+                this._effect.setVector4("gridControl", this._gridControl);
+            }
+            super.bind(world, mesh);
+        }
+        
+        public dispose(forceDisposeEffect?: boolean): void {
+            super.dispose(forceDisposeEffect);
+        }
+        
+        public clone(name: string): GridMaterial {
+            return SerializationHelper.Clone(() => new GridMaterial(name, this.getScene()), this);
+        }
+
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this); 
+            serializationObject.customType = "BABYLON.GridMaterial"; 
+            return serializationObject;
+        }
+
+        public static Parse(source: any, scene: Scene, rootUrl: string): GridMaterial {
+            return SerializationHelper.Parse(() => new GridMaterial(source.name, scene), source, scene, rootUrl);
+        }
+    }
+}

+ 94 - 0
materialsLibrary/materials/grid/grid.fragment.fx

@@ -0,0 +1,94 @@
+#extension GL_OES_standard_derivatives : enable
+
+#define SQRT2 1.41421356
+#define PI 3.14159
+
+precision highp float;
+
+uniform vec3 mainColor;
+uniform vec3 lineColor;
+uniform vec4 gridControl;
+
+// Varying
+varying vec3 vPosition;
+varying vec3 vNormal;
+
+float getVisibility(float position) {
+    // Major grid line every Frequency defined in material.
+    float majorGridFrequency = gridControl.y;
+    if (floor(position + 0.5) == floor(position / majorGridFrequency + 0.5) * majorGridFrequency)
+    {
+        return 1.0;
+    }  
+    
+    return gridControl.z;
+}
+
+float getAnisotropicAttenuation(float differentialLength) {
+    const float maxNumberOfLines = 10.0;
+    return clamp(1.0 / (differentialLength + 1.0) - 1.0 / maxNumberOfLines, 0.0, 1.0);
+}
+
+float isPointOnLine(float position, float differentialLength) {
+    float fractionPartOfPosition = position - floor(position + 0.5); // fract part around unit [-0.5; 0.5]
+    fractionPartOfPosition /= differentialLength; // adapt to the screen space size it takes
+    fractionPartOfPosition = clamp(fractionPartOfPosition, -1., 1.);
+    
+    float result = 0.5 + 0.5 * cos(fractionPartOfPosition * PI); // Convert to 0-1 for antialiasing.
+    return result;    
+}
+
+float contributionOnAxis(float position) {
+    float differentialLength = length(vec2(dFdx(position), dFdy(position)));
+    differentialLength *= SQRT2;  // Multiply by SQRT2 for diagonal length
+    
+    // Is the point on the line.
+    float result = isPointOnLine(position, differentialLength);
+    
+    // Add dynamic visibility.
+    float visibility = getVisibility(position);
+    result *= visibility;
+    
+    // Anisotropic filtering.
+    float anisotropicAttenuation = getAnisotropicAttenuation(differentialLength);
+    result *= anisotropicAttenuation;
+    
+    return result;
+}
+
+float normalImpactOnAxis(float x) {
+    float normalImpact = clamp(1.0 - 2.8 * abs(x * x * x), 0.0, 1.0);
+    return normalImpact;
+}
+
+void main(void) {
+    
+    // Scale position to the requested ratio.
+    float gridRatio = gridControl.x;
+    vec3 gridPos = vPosition / gridRatio;
+    
+    // Find the contribution of each coords.
+    float x = contributionOnAxis(gridPos.x);
+    float y = contributionOnAxis(gridPos.y);
+    float z = contributionOnAxis(gridPos.z); 
+    
+    // Find the normal contribution.
+    vec3 normal = normalize(vNormal);
+    x *= normalImpactOnAxis(normal.x);
+    y *= normalImpactOnAxis(normal.y);
+    z *= normalImpactOnAxis(normal.z);
+    
+    // Create the grid value by combining axis.
+    float grid = clamp(x + y + z, 0., 1.);
+    
+    // Create the color.
+    vec3 gridColor = mix(mainColor, lineColor, grid);
+
+#ifdef TRANSPARENT
+    float opacity = clamp(grid, 0.08, gridControl.w);
+    gl_FragColor = vec4(gridColor.rgb, opacity);
+#else
+    // Apply the color.
+    gl_FragColor = vec4(gridColor.rgb, 1.0);
+#endif
+}

+ 19 - 0
materialsLibrary/materials/grid/grid.vertex.fx

@@ -0,0 +1,19 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+attribute vec3 normal;
+
+// Uniforms
+uniform mat4 worldViewProjection;
+
+// Varying
+varying vec3 vPosition;
+varying vec3 vNormal;
+
+void main(void) {
+    gl_Position = worldViewProjection * vec4(position, 1.0);
+
+    vPosition = position;
+    vNormal = normal;
+}

+ 3 - 0
materialsLibrary/materials/grid/legacygrid.fragment.fx

@@ -0,0 +1,3 @@
+void main(void) {
+    gl_FragColor = vec4(1, 1, 1, 0.1);
+}

+ 11 - 0
materialsLibrary/materials/grid/legacygrid.vertex.fx

@@ -0,0 +1,11 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+
+// Uniforms
+uniform mat4 worldViewProjection;
+
+void main(void) {
+    gl_Position = worldViewProjection * vec4(position, 1.0);
+}

+ 31 - 0
materialsLibrary/materials/pbr/includes/harmonicsFunctions.fx

@@ -0,0 +1,31 @@
+#ifdef USESPHERICALFROMREFLECTIONMAP
+    uniform vec3 vSphericalX;
+    uniform vec3 vSphericalY;
+    uniform vec3 vSphericalZ;
+    uniform vec3 vSphericalXX;
+    uniform vec3 vSphericalYY;
+    uniform vec3 vSphericalZZ;
+    uniform vec3 vSphericalXY;
+    uniform vec3 vSphericalYZ;
+    uniform vec3 vSphericalZX;
+
+    vec3 EnvironmentIrradiance(vec3 normal)
+    {
+        // Note: 'normal' is assumed to be normalised (or near normalised)
+        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be incorrect nonetheless.
+
+        // TODO: switch to optimal implementation
+        vec3 result =
+            vSphericalX * normal.x +
+            vSphericalY * normal.y +
+            vSphericalZ * normal.z +
+            vSphericalXX * normal.x * normal.x +
+            vSphericalYY * normal.y * normal.y +
+            vSphericalZZ * normal.z * normal.z +
+            vSphericalYZ * normal.y * normal.z +
+            vSphericalZX * normal.z * normal.x +
+            vSphericalXY * normal.x * normal.y;
+
+        return result.rgb;
+    }
+#endif

+ 0 - 18
materialsLibrary/materials/pbr/includes/helperFunctions.fx

@@ -1,18 +0,0 @@
-// PBR HELPER METHODS
-float Square(float value)
-{
-    return value * value;
-}
-
-float getLuminance(vec3 color)
-{
-    return clamp(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0., 1.);
-}
-
-float convertRoughnessToAverageSlope(float roughness)
-{
-    // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
-    const float kMinimumVariance = 0.0005;
-    float alphaG = Square(roughness) + kMinimumVariance;
-    return alphaG;
-}

+ 197 - 0
materialsLibrary/materials/pbr/includes/pbrFunctions.fx

@@ -0,0 +1,197 @@
+// Constants
+#define RECIPROCAL_PI2 0.15915494
+#define FRESNEL_MAXIMUM_ON_ROUGH 0.25
+
+// PBR CUSTOM CONSTANTS
+const float kPi = 3.1415926535897932384626433832795;
+const float kRougnhessToAlphaScale = 0.1;
+const float kRougnhessToAlphaOffset = 0.29248125;
+
+float Square(float value)
+{
+    return value * value;
+}
+
+float getLuminance(vec3 color)
+{
+    return clamp(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0., 1.);
+}
+
+float convertRoughnessToAverageSlope(float roughness)
+{
+    // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
+    const float kMinimumVariance = 0.0005;
+    float alphaG = Square(roughness) + kMinimumVariance;
+    return alphaG;
+}
+
+// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
+float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
+{
+    // do not take in account lower mips hence -1... and wait from proper preprocess.
+    // formula comes from approximation of the mathematical solution.
+    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
+    
+    // In the mean time 
+    // Always [0..1] goes from max mip to min mip in a log2 way.  
+    // Change 5 to nummip below.
+    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
+    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
+    
+    return clamp(mip, 0., maxMipLevel);
+}
+
+float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
+{
+    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
+    
+    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
+    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
+}
+
+// From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
+float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
+{
+    float tanSquared = (1.0 - dot * dot) / (dot * dot);
+    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
+}
+
+float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
+{
+    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
+}
+
+// Trowbridge-Reitz (GGX)
+// Generalised Trowbridge-Reitz with gamma power=2.0
+float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
+{
+    // Note: alphaG is average slope (gradient) of the normals in slope-space.
+    // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
+    // a tangent (gradient) closer to the macrosurface than this slope.
+    float a2 = Square(alphaG);
+    float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
+    return a2 / (kPi * d * d);
+}
+
+vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
+{
+    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
+}
+
+vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
+{
+    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
+    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
+    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
+}
+
+// Cook Torance Specular computation.
+vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
+{
+    float alphaG = convertRoughnessToAverageSlope(roughness);
+    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
+    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
+    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
+
+    vec3 fresnel = fresnelSchlickGGX(VdotH, specularColor, vec3(1., 1., 1.));
+
+    float specTerm = max(0., visibility * distribution) * NdotL;
+    return fresnel * specTerm * kPi; // TODO: audit pi constants
+}
+
+float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
+{
+    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
+    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
+    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
+    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
+    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
+    float diffuseFresnelTerm =
+        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
+        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
+
+
+    return diffuseFresnelTerm * NdotL;
+    // PI Test
+    // diffuseFresnelTerm /= kPi;
+}
+
+float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
+{
+    // At small angle this approximation works. 
+    float lightRoughness = lightRadius / lightDistance;
+    // Distribution can sum.
+    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
+    return totalRoughness;
+}
+
+float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
+{
+    float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
+
+    float reflectivityLuminance = getLuminance(reflectivityColor);
+    float reflectivityLuma = sqrt(reflectivityLuminance);
+    microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
+
+    return microSurface;
+}
+
+vec3 toLinearSpace(vec3 color)
+{
+    return vec3(pow(color.r, 2.2), pow(color.g, 2.2), pow(color.b, 2.2));
+}
+
+vec3 toGammaSpace(vec3 color)
+{
+    return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
+}
+
+float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
+{
+    #ifdef USEPHYSICALLIGHTFALLOFF
+        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
+        return lightDistanceFalloff;
+    #else
+        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
+        return lightFalloff;
+    #endif
+}
+
+#ifdef CAMERATONEMAP
+    vec3 toneMaps(vec3 color)
+    {
+        color = max(color, 0.0);
+
+        // TONE MAPPING / EXPOSURE
+        color.rgb = color.rgb * vCameraInfos.x;
+
+        float tuning = 1.5; // TODO: sync up so e.g. 18% greys are matched to exposure appropriately
+        // PI Test
+        // tuning *=  kPi;
+        vec3 tonemapped = 1.0 - exp2(-color.rgb * tuning); // simple local photographic tonemapper
+        color.rgb = mix(color.rgb, tonemapped, 1.0);
+        return color;
+    }
+#endif
+
+#ifdef CAMERACONTRAST
+    vec4 contrasts(vec4 color)
+    {
+        color = clamp(color, 0.0, 1.0);
+
+        vec3 resultHighContrast = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb);
+        float contrast = vCameraInfos.y;
+        if (contrast < 1.0)
+        {
+            // Decrease contrast: interpolate towards zero-contrast image (flat grey)
+            color.rgb = mix(vec3(0.5, 0.5, 0.5), color.rgb, contrast);
+        }
+        else
+        {
+            // Increase contrast: apply simple shoulder-toe high contrast curve
+            color.rgb = mix(color.rgb, resultHighContrast, contrast - 1.0);
+        }
+
+        return color;
+    }
+#endif

+ 130 - 0
materialsLibrary/materials/pbr/includes/pbrLightFunctions.fx

@@ -0,0 +1,130 @@
+// Light Computing
+struct lightingInfo
+{
+    vec3 diffuse;
+    #ifdef SPECULARTERM
+        vec3 specular;
+    #endif
+};
+
+lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
+    lightingInfo result;
+
+    vec3 lightDirection;
+    float attenuation = 1.0;
+    float lightDistance;
+    
+    // Point
+    if (lightData.w == 0.)
+    {
+        vec3 lightOffset = lightData.xyz - vPositionW;
+        float lightDistanceSquared = dot(lightOffset, lightOffset);
+        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
+        
+        lightDistance = sqrt(lightDistanceSquared);
+        lightDirection = normalize(lightOffset);
+    }
+    // Directional
+    else
+    {
+        lightDistance = length(-lightData.xyz);
+        lightDirection = normalize(-lightData.xyz);
+    }
+    
+    // Roughness
+    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+    
+    // diffuse
+    vec3 H = normalize(viewDirectionW + lightDirection);
+    NdotL = max(0.00000000001, dot(vNormal, lightDirection));
+    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
+
+    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
+    result.diffuse = diffuseTerm * diffuseColor * attenuation;
+
+    #ifdef SPECULARTERM
+        // Specular
+        float NdotH = max(0.00000000001, dot(vNormal, H));
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm * attenuation;
+    #endif
+
+    return result;
+}
+
+lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
+    lightingInfo result;
+
+    vec3 lightOffset = lightData.xyz - vPositionW;
+    vec3 lightVectorW = normalize(lightOffset);
+
+    // diffuse
+    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
+    
+    if (cosAngle >= lightDirection.w)
+    {
+        cosAngle = max(0., pow(cosAngle, lightData.w));
+        
+        // Inverse squared falloff.
+        float lightDistanceSquared = dot(lightOffset, lightOffset);
+        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
+        
+        // Directional falloff.
+        attenuation *= cosAngle;
+        
+        // Roughness.
+        float lightDistance = sqrt(lightDistanceSquared);
+        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
+        
+        // Diffuse
+        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
+        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
+        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
+
+        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
+        result.diffuse = diffuseTerm * diffuseColor * attenuation;
+
+        #ifdef SPECULARTERM
+            // Specular
+            float NdotH = max(0.00000000001, dot(vNormal, H));
+
+            vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+            result.specular = specTerm  * attenuation;
+        #endif
+
+        return result;
+    }
+
+    result.diffuse = vec3(0.);
+    #ifdef SPECULARTERM
+        result.specular = vec3(0.);
+    #endif
+
+    return result;
+}
+
+lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius, out float NdotL) {
+    lightingInfo result;
+
+    // Roughness
+    // Do not touch roughness on hemispheric.
+
+    // Diffuse
+    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
+    result.diffuse = mix(groundColor, diffuseColor, NdotL);
+
+    #ifdef SPECULARTERM
+        // Specular
+        vec3 lightVectorW = normalize(lightData.xyz);
+        vec3 H = normalize(viewDirectionW + lightVectorW);
+        float NdotH = max(0.00000000001, dot(vNormal, H));
+        NdotL = max(0.00000000001, NdotL);
+        float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm;
+    #endif
+
+    return result;
+}

+ 50 - 0
materialsLibrary/materials/pbr/includes/pbrLightFunctionsCall.fx

@@ -0,0 +1,50 @@
+#ifdef LIGHT{X}
+    #ifndef SPECULARTERM
+        vec3 vLightSpecular{X} = vec3(0.0);
+    #endif
+    #ifdef SPOTLIGHT{X}
+        info = computeSpotLighting(viewDirectionW, normalW, vLightData{X}, vLightDirection{X}, vLightDiffuse{X}.rgb, vLightSpecular{X}, vLightDiffuse{X}.a, roughness, NdotV, vLightRadiuses[{X}], NdotL);
+    #endif
+    #ifdef HEMILIGHT{X}
+        info = computeHemisphericLighting(viewDirectionW, normalW, vLightData{X}, vLightDiffuse{X}.rgb, vLightSpecular{X}, vLightGround{X}, roughness, NdotV, vLightRadiuses[{X}], NdotL);
+    #endif
+    #if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
+        info = computeLighting(viewDirectionW, normalW, vLightData{X}, vLightDiffuse{X}.rgb, vLightSpecular{X}, vLightDiffuse{X}.a, roughness, NdotV, vLightRadiuses[{X}], NdotL);
+    #endif
+    
+    #ifdef SHADOW{X}
+        #ifdef SHADOWVSM{X}
+            notShadowLevel = computeShadowWithVSM(vPositionFromLight{X}, shadowSampler{X}, shadowsInfo{X}.z, shadowsInfo{X}.x);
+        #else
+            #ifdef SHADOWPCF{X}
+                #if defined(POINTLIGHT{X})
+                    notShadowLevel = computeShadowWithPCFCube(vLightData{X}.xyz, shadowSampler{X}, shadowsInfo{X}.y, shadowsInfo{X}.z, shadowsInfo{X}.x);
+                #else
+                    notShadowLevel = computeShadowWithPCF(vPositionFromLight{X}, shadowSampler{X}, shadowsInfo{X}.y, shadowsInfo{X}.z, shadowsInfo{X}.x);
+                #endif
+            #else
+                #if defined(POINTLIGHT{X})
+                    notShadowLevel = computeShadowCube(vLightData{X}.xyz, shadowSampler{X}, shadowsInfo{X}.x, shadowsInfo{X}.z);
+                #else
+                    notShadowLevel = computeShadow(vPositionFromLight{X}, shadowSampler{X}, shadowsInfo{X}.x, shadowsInfo{X}.z);
+                #endif
+            #endif
+        #endif
+    #else
+        notShadowLevel = 1.;
+    #endif
+    
+    lightDiffuseContribution += info.diffuse * notShadowLevel;
+    
+    #ifdef OVERLOADEDSHADOWVALUES
+        if (NdotL < 0.000000000011)
+        {
+            notShadowLevel = 1.;
+        }
+        shadowedOnlyLightDiffuseContribution *= notShadowLevel;
+    #endif
+
+    #ifdef SPECULARTERM
+        lightSpecularContribution += info.specular * notShadowLevel;
+    #endif
+#endif

+ 171 - 0
materialsLibrary/materials/pbr/includes/pbrShadowFunctions.fx

@@ -0,0 +1,171 @@
+// Shadows
+#ifdef SHADOWS
+
+    float unpack(vec4 color)
+    {
+        const vec4 bit_shift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
+        return dot(color, bit_shift);
+    }
+
+    #if defined(POINTLIGHT0) || defined(POINTLIGHT1) || defined(POINTLIGHT2) || defined(POINTLIGHT3)
+        uniform vec2 depthValues;
+
+        float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float bias)
+        {
+            vec3 directionToLight = vPositionW - lightPosition;
+            float depth = length(directionToLight);
+            depth = clamp(depth, 0., 1.0);
+
+            directionToLight = normalize(directionToLight);
+            directionToLight.y = - directionToLight.y;
+
+            float shadow = unpack(textureCube(shadowSampler, directionToLight)) + bias;
+
+            if (depth > shadow)
+            {
+                #ifdef OVERLOADEDSHADOWVALUES
+                    return mix(1.0, darkness, vOverloadedShadowIntensity.x);
+                #else
+                    return darkness;
+                #endif
+            }
+            return 1.0;
+        }
+
+        float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float bias, float darkness)
+        {
+            vec3 directionToLight = vPositionW - lightPosition;
+            float depth = length(directionToLight);
+
+            depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
+            depth = clamp(depth, 0., 1.0);
+
+            directionToLight = normalize(directionToLight);
+            directionToLight.y = -directionToLight.y;
+
+            float visibility = 1.;
+
+            vec3 poissonDisk[4];
+            poissonDisk[0] = vec3(-1.0, 1.0, -1.0);
+            poissonDisk[1] = vec3(1.0, -1.0, -1.0);
+            poissonDisk[2] = vec3(-1.0, -1.0, -1.0);
+            poissonDisk[3] = vec3(1.0, -1.0, 1.0);
+
+            // Poisson Sampling
+            float biasedDepth = depth - bias;
+
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
+
+            #ifdef OVERLOADEDSHADOWVALUES
+                return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
+            #else
+                return  min(1.0, visibility + darkness);
+            #endif
+        }
+    #endif
+
+    #if defined(SPOTLIGHT0) || defined(SPOTLIGHT1) || defined(SPOTLIGHT2) || defined(SPOTLIGHT3) ||  defined(DIRLIGHT0) || defined(DIRLIGHT1) || defined(DIRLIGHT2) || defined(DIRLIGHT3)
+        float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness, float bias)
+        {
+            vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
+            depth = 0.5 * depth + vec3(0.5);
+            vec2 uv = depth.xy;
+
+            if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            float shadow = unpack(texture2D(shadowSampler, uv)) + bias;
+
+            if (depth.z > shadow)
+            {
+                #ifdef OVERLOADEDSHADOWVALUES
+                    return mix(1.0, darkness, vOverloadedShadowIntensity.x);
+                #else
+                    return darkness;
+                #endif
+            }
+            return 1.;
+        }
+
+        float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler, float mapSize, float bias, float darkness)
+        {
+            vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
+            depth = 0.5 * depth + vec3(0.5);
+            vec2 uv = depth.xy;
+
+            if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
+            {
+                return 1.0;
+            }
+
+            float visibility = 1.;
+
+            vec2 poissonDisk[4];
+            poissonDisk[0] = vec2(-0.94201624, -0.39906216);
+            poissonDisk[1] = vec2(0.94558609, -0.76890725);
+            poissonDisk[2] = vec2(-0.094184101, -0.92938870);
+            poissonDisk[3] = vec2(0.34495938, 0.29387760);
+
+            // Poisson Sampling
+            float biasedDepth = depth.z - bias;
+
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
+            if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
+
+            #ifdef OVERLOADEDSHADOWVALUES
+                return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
+            #else
+                return  min(1.0, visibility + darkness);
+            #endif
+        }
+
+        // Thanks to http://devmaster.net/
+        float unpackHalf(vec2 color)
+        {
+            return color.x + (color.y / 255.0);
+        }
+
+        float linstep(float low, float high, float v) {
+            return clamp((v - low) / (high - low), 0.0, 1.0);
+        }
+
+        float ChebychevInequality(vec2 moments, float compare, float bias)
+        {
+            float p = smoothstep(compare - bias, compare, moments.x);
+            float variance = max(moments.y - moments.x * moments.x, 0.02);
+            float d = compare - moments.x;
+            float p_max = linstep(0.2, 1.0, variance / (variance + d * d));
+
+            return clamp(max(p, p_max), 0.0, 1.0);
+        }
+
+        float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler, float bias, float darkness)
+        {
+            vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
+            depth = 0.5 * depth + vec3(0.5);
+            vec2 uv = depth.xy;
+
+            if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0 || depth.z >= 1.0)
+            {
+                return 1.0;
+            }
+
+            vec4 texel = texture2D(shadowSampler, uv);
+
+            vec2 moments = vec2(unpackHalf(texel.xy), unpackHalf(texel.zw));
+            #ifdef OVERLOADEDSHADOWVALUES
+                return min(1.0, mix(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness, vOverloadedShadowIntensity.x));
+            #else
+                return min(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness);
+            #endif
+        }
+    #endif
+
+#endif

+ 9 - 420
materialsLibrary/materials/pbr/legacypbr.fragment.fx

@@ -28,185 +28,6 @@ uniform vec3 vOverloadedMicroSurface;
 uniform vec4 vOverloadedShadowIntensity;
 #endif
 
-// PBR CUSTOM CONSTANTS
-const float kPi = 3.1415926535897932384626433832795;
-const float kRougnhessToAlphaScale = 0.1;
-const float kRougnhessToAlphaOffset = 0.29248125;
-
-#include<helperFunctions>
-
-// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
-float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
-{
-    // do not take in account lower mips hence -1... and wait from proper preprocess.
-    // formula comes from approximation of the mathematical solution.
-    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
-    
-    // In the mean time 
-    // Always [0..1] goes from max mip to min mip in a log2 way.  
-    // Change 5 to nummip below.
-    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
-    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
-    
-    return clamp(mip, 0., maxMipLevel);
-}
-
-float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
-{
-    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
-    
-    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
-    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
-}
-
-// From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
-float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
-{
-    float tanSquared = (1.0 - dot * dot) / (dot * dot);
-    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
-}
-
-float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
-{
-    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
-}
-
-// Trowbridge-Reitz (GGX)
-// Generalised Trowbridge-Reitz with gamma power=2.0
-float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
-{
-    // Note: alphaG is average slope (gradient) of the normals in slope-space.
-    // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
-    // a tangent (gradient) closer to the macrosurface than this slope.
-    float a2 = Square(alphaG);
-    float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
-    return a2 / (kPi * d * d);
-}
-
-vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
-{
-    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
-}
-
-vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
-{
-    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
-    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
-    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
-}
-
-// Cook Torance Specular computation.
-vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
-{
-    float alphaG = convertRoughnessToAverageSlope(roughness);
-    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
-    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
-    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
-
-    vec3 fresnel = fresnelSchlickGGX(VdotH, specularColor, vec3(1., 1., 1.));
-
-    float specTerm = max(0., visibility * distribution) * NdotL;
-    return fresnel * specTerm * kPi; // TODO: audit pi constants
-}
-
-float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
-{
-    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
-    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
-    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
-    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
-    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
-    float diffuseFresnelTerm =
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
-
-
-    return diffuseFresnelTerm * NdotL;
-    // PI Test
-    // diffuseFresnelTerm /= kPi;
-}
-
-float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
-{
-    // At small angle this approximation works. 
-    float lightRoughness = lightRadius / lightDistance;
-    // Distribution can sum.
-    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
-    return totalRoughness;
-}
-
-float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
-{
-    float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
-
-    float reflectivityLuminance = getLuminance(reflectivityColor);
-    float reflectivityLuma = sqrt(reflectivityLuminance);
-    microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
-
-    return microSurface;
-}
-
-vec3 toLinearSpace(vec3 color)
-{
-    return vec3(pow(color.r, 2.2), pow(color.g, 2.2), pow(color.b, 2.2));
-}
-
-vec3 toGammaSpace(vec3 color)
-{
-    return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
-}
-
-float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
-{
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
-        return lightDistanceFalloff;
-    #else
-        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
-        return lightFalloff;
-    #endif
-}
-
-#ifdef CAMERATONEMAP
-    vec3 toneMaps(vec3 color)
-    {
-        color = max(color, 0.0);
-
-        // TONE MAPPING / EXPOSURE
-        color.rgb = color.rgb * vCameraInfos.x;
-
-        float tuning = 1.5; // TODO: sync up so e.g. 18% greys are matched to exposure appropriately
-        // PI Test
-        // tuning *=  kPi;
-        vec3 tonemapped = 1.0 - exp2(-color.rgb * tuning); // simple local photographic tonemapper
-        color.rgb = mix(color.rgb, tonemapped, 1.0);
-        return color;
-    }
-#endif
-
-#ifdef CAMERACONTRAST
-    vec4 contrasts(vec4 color)
-    {
-        color = clamp(color, 0.0, 1.0);
-
-        vec3 resultHighContrast = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb);
-        float contrast = vCameraInfos.y;
-        if (contrast < 1.0)
-        {
-            // Decrease contrast: interpolate towards zero-contrast image (flat grey)
-            color.rgb = mix(vec3(0.5, 0.5, 0.5), color.rgb, contrast);
-        }
-        else
-        {
-            // Increase contrast: apply simple shoulder-toe high contrast curve
-            color.rgb = mix(color.rgb, resultHighContrast, contrast - 1.0);
-        }
-
-        return color;
-    }
-#endif
-// END PBR HELPER METHODS
-
 uniform vec4 vReflectivityColor;
 uniform vec3 vEmissiveColor;
 
@@ -266,136 +87,10 @@ uniform sampler2D reflectivitySampler;
 
 #include<clipPlaneFragmentDeclaration>
 
-// Light Computing
-struct lightingInfo
-{
-    vec3 diffuse;
-#ifdef SPECULARTERM
-    vec3 specular;
-#endif
-};
-
-lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightDirection;
-    float attenuation = 1.0;
-    float lightDistance;
-    
-    // Point
-    if (lightData.w == 0.)
-    {
-        vec3 lightOffset = lightData.xyz - vPositionW;
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        lightDistance = sqrt(lightDistanceSquared);
-        lightDirection = normalize(lightOffset);
-    }
-    // Directional
-    else
-    {
-        lightDistance = length(-lightData.xyz);
-        lightDirection = normalize(-lightData.xyz);
-    }
-    
-    // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // diffuse
-    vec3 H = normalize(viewDirectionW + lightDirection);
-    NdotL = max(0.00000000001, dot(vNormal, lightDirection));
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-    // Specular
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm * attenuation;
-#endif
-
-    return result;
-}
-
-lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightOffset = lightData.xyz - vPositionW;
-    vec3 lightVectorW = normalize(lightOffset);
-
-    // diffuse
-    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
-    
-    if (cosAngle >= lightDirection.w)
-    {
-        cosAngle = max(0., pow(cosAngle, lightData.w));
-        
-        // Inverse squared falloff.
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        // Directional falloff.
-        attenuation *= cosAngle;
-        
-        // Roughness.
-        float lightDistance = sqrt(lightDistanceSquared);
-        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-        
-        // Diffuse
-        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
-        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
-        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
-
-        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-        result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-        // Specular
-        float NdotH = max(0.00000000001, dot(vNormal, H));
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-        result.specular = specTerm  * attenuation;
-#endif
-
-        return result;
-    }
-
-    result.diffuse = vec3(0.);
-#ifdef SPECULARTERM
-    result.specular = vec3(0.);
-#endif
-
-    return result;
-}
-
-lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    // Roughness
-    // Do not touch roughness on hemispheric.
-
-    // Diffuse
-    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
-    result.diffuse = mix(groundColor, diffuseColor, NdotL);
-
-#ifdef SPECULARTERM
-    // Specular
-    vec3 lightVectorW = normalize(lightData.xyz);
-    vec3 H = normalize(viewDirectionW + lightVectorW);
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-    NdotL = max(0.00000000001, NdotL);
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm;
-#endif
-
-    return result;
-}
+// PBR
+#include<pbrFunctions>
+#include<harmonicsFunctions>
+#include<pbrLightFunctions>
 
 void main(void) {
 #include<clipPlaneFragment>
@@ -506,118 +201,12 @@ void main(void) {
 #endif
     float notShadowLevel = 1.; // 1 - shadowLevel
     float NdotL = -1.;
+    lightingInfo info;
 
-#ifdef LIGHT0
-#ifndef SPECULARTERM
-    vec3 vLightSpecular0 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT0
-    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#ifdef HEMILIGHT0
-    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#if defined(POINTLIGHT0) || defined(DIRLIGHT0)
-    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT1
-#ifndef SPECULARTERM
-    vec3 vLightSpecular1 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT1
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#ifdef HEMILIGHT1
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#if defined(POINTLIGHT1) || defined(DIRLIGHT1)
-    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT2
-#ifndef SPECULARTERM
-    vec3 vLightSpecular2 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT2
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#ifdef HEMILIGHT2
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#if defined(POINTLIGHT2) || defined(DIRLIGHT2)
-    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT3
-#ifndef SPECULARTERM
-    vec3 vLightSpecular3 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT3
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#ifdef HEMILIGHT3
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#if defined(POINTLIGHT3) || defined(DIRLIGHT3)
-    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-
-    notShadowLevel = 1.;
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
+#include<pbrLightFunctionsCall>[0]
+#include<pbrLightFunctionsCall>[1]
+#include<pbrLightFunctionsCall>[2]
+#include<pbrLightFunctionsCall>[3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 12 - 713
materialsLibrary/materials/pbr/pbr.fragment.fx

@@ -12,10 +12,6 @@
 
 precision highp float;
 
-// Constants
-#define RECIPROCAL_PI2 0.15915494
-#define FRESNEL_MAXIMUM_ON_ROUGH 0.25
-
 uniform vec3 vEyePosition;
 uniform vec3 vAmbientColor;
 uniform vec3 vReflectionColor;
@@ -40,223 +36,12 @@ uniform vec4 vCameraInfos;
     uniform vec4 vOverloadedShadowIntensity;
 #endif
 
-#ifdef USESPHERICALFROMREFLECTIONMAP
-    uniform vec3 vSphericalX;
-    uniform vec3 vSphericalY;
-    uniform vec3 vSphericalZ;
-    uniform vec3 vSphericalXX;
-    uniform vec3 vSphericalYY;
-    uniform vec3 vSphericalZZ;
-    uniform vec3 vSphericalXY;
-    uniform vec3 vSphericalYZ;
-    uniform vec3 vSphericalZX;
-
-    vec3 EnvironmentIrradiance(vec3 normal)
-    {
-        // Note: 'normal' is assumed to be normalised (or near normalised)
-        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be incorrect nonetheless.
-
-        // TODO: switch to optimal implementation
-        vec3 result =
-            vSphericalX * normal.x +
-            vSphericalY * normal.y +
-            vSphericalZ * normal.z +
-            vSphericalXX * normal.x * normal.x +
-            vSphericalYY * normal.y * normal.y +
-            vSphericalZZ * normal.z * normal.z +
-            vSphericalYZ * normal.y * normal.z +
-            vSphericalZX * normal.z * normal.x +
-            vSphericalXY * normal.x * normal.y;
-
-        return result.rgb;
-    }
-#endif
-
 #if defined(REFLECTION) || defined(REFRACTION)
     uniform vec2 vMicrosurfaceTextureLods;
 #endif
 
-// PBR CUSTOM CONSTANTS
-const float kPi = 3.1415926535897932384626433832795;
-const float kRougnhessToAlphaScale = 0.1;
-const float kRougnhessToAlphaOffset = 0.29248125;
-
-#include<helperFunctions>
-
-// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
-float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
-{
-    // do not take in account lower mips hence -1... and wait from proper preprocess.
-    // formula comes from approximation of the mathematical solution.
-    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
-    
-    // In the mean time 
-    // Always [0..1] goes from max mip to min mip in a log2 way.  
-    // Change 5 to nummip below.
-    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
-    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
-    
-    return clamp(mip, 0., maxMipLevel);
-}
-
-float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
-{
-    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
-    
-    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
-    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
-}
-
-// From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
-float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
-{
-    float tanSquared = (1.0 - dot * dot) / (dot * dot);
-    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
-}
-
-float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
-{
-    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
-}
-
-// Trowbridge-Reitz (GGX)
-// Generalised Trowbridge-Reitz with gamma power=2.0
-float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
-{
-    // Note: alphaG is average slope (gradient) of the normals in slope-space.
-    // It is also the (trigonometric) tangent of the median distribution value, i.e. 50% of normals have
-    // a tangent (gradient) closer to the macrosurface than this slope.
-    float a2 = Square(alphaG);
-    float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
-    return a2 / (kPi * d * d);
-}
-
-vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
-{
-    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
-}
-
-vec3 FresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
-{
-    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
-    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
-    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
-}
-
-// Cook Torance Specular computation.
-vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 specularColor)
-{
-    float alphaG = convertRoughnessToAverageSlope(roughness);
-    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
-    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
-    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
-
-    vec3 fresnel = fresnelSchlickGGX(VdotH, specularColor, vec3(1., 1., 1.));
-
-    float specTerm = max(0., visibility * distribution) * NdotL;
-    return fresnel * specTerm * kPi; // TODO: audit pi constants
-}
-
-float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
-{
-    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
-    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
-    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
-    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
-    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
-    float diffuseFresnelTerm =
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
-
-
-    return diffuseFresnelTerm * NdotL;
-    // PI Test
-    // diffuseFresnelTerm /= kPi;
-}
-
-float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
-{
-    // At small angle this approximation works. 
-    float lightRoughness = lightRadius / lightDistance;
-    // Distribution can sum.
-    float totalRoughness = clamp(lightRoughness + roughness, 0., 1.);
-    return totalRoughness;
-}
-
-float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
-{
-    float kReflectivityNoAlphaWorkflow_SmoothnessMax = 0.95;
-
-    float reflectivityLuminance = getLuminance(reflectivityColor);
-    float reflectivityLuma = sqrt(reflectivityLuminance);
-    microSurface = reflectivityLuma * kReflectivityNoAlphaWorkflow_SmoothnessMax;
-
-    return microSurface;
-}
-
-vec3 toLinearSpace(vec3 color)
-{
-    return vec3(pow(color.r, 2.2), pow(color.g, 2.2), pow(color.b, 2.2));
-}
-
-vec3 toGammaSpace(vec3 color)
-{
-    return vec3(pow(color.r, 1.0 / 2.2), pow(color.g, 1.0 / 2.2), pow(color.b, 1.0 / 2.2));
-}
-
-float computeLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range)
-{
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        float lightDistanceFalloff = 1.0 / ((lightDistanceSquared + 0.0001));
-        return lightDistanceFalloff;
-    #else
-        float lightFalloff = max(0., 1.0 - length(lightOffset) / range);
-        return lightFalloff;
-    #endif
-}
-
-#ifdef CAMERATONEMAP
-    vec3 toneMaps(vec3 color)
-    {
-        color = max(color, 0.0);
-
-        // TONE MAPPING / EXPOSURE
-        color.rgb = color.rgb * vCameraInfos.x;
-
-        float tuning = 1.5; // TODO: sync up so e.g. 18% greys are matched to exposure appropriately
-        // PI Test
-        // tuning *=  kPi;
-        vec3 tonemapped = 1.0 - exp2(-color.rgb * tuning); // simple local photographic tonemapper
-        color.rgb = mix(color.rgb, tonemapped, 1.0);
-        return color;
-    }
-#endif
-
-#ifdef CAMERACONTRAST
-    vec4 contrasts(vec4 color)
-    {
-        color = clamp(color, 0.0, 1.0);
-
-        vec3 resultHighContrast = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb);
-        float contrast = vCameraInfos.y;
-        if (contrast < 1.0)
-        {
-            // Decrease contrast: interpolate towards zero-contrast image (flat grey)
-            color.rgb = mix(vec3(0.5, 0.5, 0.5), color.rgb, contrast);
-        }
-        else
-        {
-            // Increase contrast: apply simple shoulder-toe high contrast curve
-            color.rgb = mix(color.rgb, resultHighContrast, contrast - 1.0);
-        }
-
-        return color;
-    }
-#endif
-// END PBR HELPER METHODS
-
-    uniform vec4 vReflectivityColor;
-    uniform vec3 vEmissiveColor;
+uniform vec4 vReflectivityColor;
+uniform vec3 vEmissiveColor;
 
 // Input
 varying vec3 vPositionW;
@@ -367,177 +152,11 @@ varying vec3 vPositionUVW;
 
 #endif
 
-// Shadows
-#ifdef SHADOWS
-
-float unpack(vec4 color)
-{
-    const vec4 bit_shift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
-    return dot(color, bit_shift);
-}
-
-#if defined(POINTLIGHT0) || defined(POINTLIGHT1) || defined(POINTLIGHT2) || defined(POINTLIGHT3)
-uniform vec2 depthValues;
-
-float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float bias)
-{
-	vec3 directionToLight = vPositionW - lightPosition;
-	float depth = length(directionToLight);
-	depth = clamp(depth, 0., 1.0);
-
-	directionToLight = normalize(directionToLight);
-	directionToLight.y = - directionToLight.y;
-
-	float shadow = unpack(textureCube(shadowSampler, directionToLight)) + bias;
-
-    if (depth > shadow)
-    {
-#ifdef OVERLOADEDSHADOWVALUES
-        return mix(1.0, darkness, vOverloadedShadowIntensity.x);
-#else
-        return darkness;
-#endif
-    }
-    return 1.0;
-}
-
-float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float mapSize, float bias, float darkness)
-{
-    vec3 directionToLight = vPositionW - lightPosition;
-    float depth = length(directionToLight);
-
-    depth = (depth - depthValues.x) / (depthValues.y - depthValues.x);
-    depth = clamp(depth, 0., 1.0);
-
-    directionToLight = normalize(directionToLight);
-    directionToLight.y = -directionToLight.y;
-
-    float visibility = 1.;
-
-    vec3 poissonDisk[4];
-    poissonDisk[0] = vec3(-1.0, 1.0, -1.0);
-    poissonDisk[1] = vec3(1.0, -1.0, -1.0);
-    poissonDisk[2] = vec3(-1.0, -1.0, -1.0);
-    poissonDisk[3] = vec3(1.0, -1.0, 1.0);
-
-    // Poisson Sampling
-    float biasedDepth = depth - bias;
-
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
-
-#ifdef OVERLOADEDSHADOWVALUES
-    return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
-#else
-    return  min(1.0, visibility + darkness);
-#endif
-}
-#endif
-
-#if defined(SPOTLIGHT0) || defined(SPOTLIGHT1) || defined(SPOTLIGHT2) || defined(SPOTLIGHT3) ||  defined(DIRLIGHT0) || defined(DIRLIGHT1) || defined(DIRLIGHT2) || defined(DIRLIGHT3)
-float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness, float bias)
-{
-    vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-    depth = 0.5 * depth + vec3(0.5);
-    vec2 uv = depth.xy;
-
-    if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
-    {
-        return 1.0;
-    }
-
-    float shadow = unpack(texture2D(shadowSampler, uv)) + bias;
-
-    if (depth.z > shadow)
-    {
-#ifdef OVERLOADEDSHADOWVALUES
-        return mix(1.0, darkness, vOverloadedShadowIntensity.x);
-#else
-        return darkness;
-#endif
-    }
-    return 1.;
-}
-
-float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler, float mapSize, float bias, float darkness)
-{
-    vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-    depth = 0.5 * depth + vec3(0.5);
-    vec2 uv = depth.xy;
-
-    if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
-    {
-        return 1.0;
-    }
-
-    float visibility = 1.;
-
-    vec2 poissonDisk[4];
-    poissonDisk[0] = vec2(-0.94201624, -0.39906216);
-    poissonDisk[1] = vec2(0.94558609, -0.76890725);
-    poissonDisk[2] = vec2(-0.094184101, -0.92938870);
-    poissonDisk[3] = vec2(0.34495938, 0.29387760);
-
-    // Poisson Sampling
-    float biasedDepth = depth.z - bias;
-
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] * mapSize)) < biasedDepth) visibility -= 0.25;
-    if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] * mapSize)) < biasedDepth) visibility -= 0.25;
-
-#ifdef OVERLOADEDSHADOWVALUES
-    return  min(1.0, mix(1.0, visibility + darkness, vOverloadedShadowIntensity.x));
-#else
-    return  min(1.0, visibility + darkness);
-#endif
-}
-
-// Thanks to http://devmaster.net/
-float unpackHalf(vec2 color)
-{
-    return color.x + (color.y / 255.0);
-}
-
-float linstep(float low, float high, float v) {
-    return clamp((v - low) / (high - low), 0.0, 1.0);
-}
-
-float ChebychevInequality(vec2 moments, float compare, float bias)
-{
-    float p = smoothstep(compare - bias, compare, moments.x);
-    float variance = max(moments.y - moments.x * moments.x, 0.02);
-    float d = compare - moments.x;
-    float p_max = linstep(0.2, 1.0, variance / (variance + d * d));
-
-    return clamp(max(p, p_max), 0.0, 1.0);
-}
-
-float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler, float bias, float darkness)
-{
-    vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
-    depth = 0.5 * depth + vec3(0.5);
-    vec2 uv = depth.xy;
-
-    if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0 || depth.z >= 1.0)
-    {
-        return 1.0;
-    }
-
-    vec4 texel = texture2D(shadowSampler, uv);
-
-    vec2 moments = vec2(unpackHalf(texel.xy), unpackHalf(texel.zw));
-#ifdef OVERLOADEDSHADOWVALUES
-    return min(1.0, mix(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness, vOverloadedShadowIntensity.x));
-#else
-    return min(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness);
-#endif
-}
-#endif
-
-#endif
+// PBR
+#include<pbrShadowFunctions>
+#include<pbrFunctions>
+#include<harmonicsFunctions>
+#include<pbrLightFunctions>
 
 #include<bumpFragmentFunctions>
 #include<clipPlaneFragmentDeclaration>
@@ -546,137 +165,6 @@ float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler, flo
 // Fog
 #include<fogFragmentDeclaration>
 
-// Light Computing
-struct lightingInfo
-{
-    vec3 diffuse;
-#ifdef SPECULARTERM
-    vec3 specular;
-#endif
-};
-
-lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightDirection;
-    float attenuation = 1.0;
-    float lightDistance;
-    
-    // Point
-    if (lightData.w == 0.)
-    {
-        vec3 lightOffset = lightData.xyz - vPositionW;
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        lightDistance = sqrt(lightDistanceSquared);
-        lightDirection = normalize(lightOffset);
-    }
-    // Directional
-    else
-    {
-        lightDistance = length(-lightData.xyz);
-        lightDirection = normalize(-lightData.xyz);
-    }
-    
-    // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // diffuse
-    vec3 H = normalize(viewDirectionW + lightDirection);
-    NdotL = max(0.00000000001, dot(vNormal, lightDirection));
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-    // Specular
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm * attenuation;
-#endif
-
-    return result;
-}
-
-lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    vec3 lightOffset = lightData.xyz - vPositionW;
-    vec3 lightVectorW = normalize(lightOffset);
-
-    // diffuse
-    float cosAngle = max(0.000000000000001, dot(-lightDirection.xyz, lightVectorW));
-    
-    if (cosAngle >= lightDirection.w)
-    {
-        cosAngle = max(0., pow(cosAngle, lightData.w));
-        
-        // Inverse squared falloff.
-        float lightDistanceSquared = dot(lightOffset, lightOffset);
-        float attenuation = computeLightFalloff(lightOffset, lightDistanceSquared, range);
-        
-        // Directional falloff.
-        attenuation *= cosAngle;
-        
-        // Roughness.
-        float lightDistance = sqrt(lightDistanceSquared);
-        roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-        
-        // Diffuse
-        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
-        NdotL = max(0.00000000001, dot(vNormal, -lightDirection.xyz));
-        float VdotH = clamp(dot(viewDirectionW, H), 0.00000000001, 1.0);
-
-        float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-        result.diffuse = diffuseTerm * diffuseColor * attenuation;
-
-#ifdef SPECULARTERM
-        // Specular
-        float NdotH = max(0.00000000001, dot(vNormal, H));
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-        result.specular = specTerm  * attenuation;
-#endif
-
-        return result;
-    }
-
-    result.diffuse = vec3(0.);
-#ifdef SPECULARTERM
-    result.specular = vec3(0.);
-#endif
-
-    return result;
-}
-
-lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, float lightRadius, out float NdotL) {
-    lightingInfo result;
-
-    // Roughness
-    // Do not touch roughness on hemispheric.
-
-    // Diffuse
-    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
-    result.diffuse = mix(groundColor, diffuseColor, NdotL);
-
-#ifdef SPECULARTERM
-    // Specular
-    vec3 lightVectorW = normalize(lightData.xyz);
-    vec3 H = normalize(viewDirectionW + lightVectorW);
-    float NdotH = max(0.00000000001, dot(vNormal, H));
-    NdotL = max(0.00000000001, NdotL);
-    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
-
-    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
-    result.specular = specTerm;
-#endif
-
-    return result;
-}
-
 void main(void) {
 #include<clipPlaneFragment>
 
@@ -790,201 +278,12 @@ void main(void) {
 #endif
     float notShadowLevel = 1.; // 1 - shadowLevel
     float NdotL = -1.;
+    lightingInfo info;
 
-#ifdef LIGHT0
-#ifndef SPECULARTERM
-    vec3 vLightSpecular0 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT0
-    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#ifdef HEMILIGHT0
-    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#if defined(POINTLIGHT0) || defined(DIRLIGHT0)
-    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, roughness, NdotV, vLightRadiuses[0], NdotL);
-#endif
-#ifdef SHADOW0
-#ifdef SHADOWVSM0
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight0, shadowSampler0, shadowsInfo0.z, shadowsInfo0.x);
-#else
-#ifdef SHADOWPCF0
-#if defined(POINTLIGHT0)
-    notShadowLevel = computeShadowWithPCFCube(vLightData0.xyz, shadowSampler0, shadowsInfo0.y, shadowsInfo0.z, shadowsInfo0.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight0, shadowSampler0, shadowsInfo0.y, shadowsInfo0.z, shadowsInfo0.x);
-#endif
-#else
-#if defined(POINTLIGHT0)
-    notShadowLevel = computeShadowCube(vLightData0.xyz, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight0, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
-#endif
-#endif
-#endif
-#else
-    notShadowLevel = 1.;
-#endif
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT1
-#ifndef SPECULARTERM
-    vec3 vLightSpecular1 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT1
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#ifdef HEMILIGHT1
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#if defined(POINTLIGHT1) || defined(DIRLIGHT1)
-    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, roughness, NdotV, vLightRadiuses[1], NdotL);
-#endif
-#ifdef SHADOW1
-#ifdef SHADOWVSM1
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight1, shadowSampler1, shadowsInfo1.z, shadowsInfo1.x);
-#else
-#ifdef SHADOWPCF1
-#if defined(POINTLIGHT1)
-    notShadowLevel = computeShadowWithPCFCube(vLightData1.xyz, shadowSampler1, shadowsInfo1.y, shadowsInfo1.z, shadowsInfo1.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight1, shadowSampler1, shadowsInfo1.y, shadowsInfo1.z, shadowsInfo1.x);
-#endif
-#else
-#if defined(POINTLIGHT1)
-    notShadowLevel = computeShadowCube(vLightData1.xyz, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight1, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
-#endif
-#endif
-#endif
-#else
-    notShadowLevel = 1.;
-#endif
-
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT2
-#ifndef SPECULARTERM
-    vec3 vLightSpecular2 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT2
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#ifdef HEMILIGHT2
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#if defined(POINTLIGHT2) || defined(DIRLIGHT2)
-    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, roughness, NdotV, vLightRadiuses[2], NdotL);
-#endif
-#ifdef SHADOW2
-#ifdef SHADOWVSM2
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight2, shadowSampler2, shadowsInfo2.z, shadowsInfo2.x);
-#else
-#ifdef SHADOWPCF2
-#if defined(POINTLIGHT2)
-    notShadowLevel = computeShadowWithPCFCube(vLightData2.xyz, shadowSampler2, shadowsInfo2.y, shadowsInfo2.z, shadowsInfo2.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight2, shadowSampler2, shadowsInfo2.y, shadowsInfo2.z, shadowsInfo2.x);
-#endif
-#else
-#if defined(POINTLIGHT2)
-    notShadowLevel = computeShadowCube(vLightData2.xyz, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight2, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
-#endif
-#endif	
-#endif	
-#else
-    notShadowLevel = 1.;
-#endif
-
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
-
-#ifdef LIGHT3
-#ifndef SPECULARTERM
-    vec3 vLightSpecular3 = vec3(0.0);
-#endif
-#ifdef SPOTLIGHT3
-    info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#ifdef HEMILIGHT3
-    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#if defined(POINTLIGHT3) || defined(DIRLIGHT3)
-    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, roughness, NdotV, vLightRadiuses[3], NdotL);
-#endif
-#ifdef SHADOW3
-#ifdef SHADOWVSM3
-    notShadowLevel = computeShadowWithVSM(vPositionFromLight3, shadowSampler3, shadowsInfo3.z, shadowsInfo3.x);
-#else
-#ifdef SHADOWPCF3
-#if defined(POINTLIGHT3)
-    notShadowLevel = computeShadowWithPCFCube(vLightData3.xyz, shadowSampler3, shadowsInfo3.y, shadowsInfo3.z, shadowsInfo3.x);
-#else
-    notShadowLevel = computeShadowWithPCF(vPositionFromLight3, shadowSampler3, shadowsInfo3.y, shadowsInfo3.z, shadowsInfo3.x);
-#endif
-#else
-#if defined(POINTLIGHT3)
-    notShadowLevel = computeShadowCube(vLightData3.xyz, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
-#else
-    notShadowLevel = computeShadow(vPositionFromLight3, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
-#endif
-#endif	
-#endif	
-#else
-    notShadowLevel = 1.;
-#endif
-
-    lightDiffuseContribution += info.diffuse * notShadowLevel;
-#ifdef OVERLOADEDSHADOWVALUES
-    if (NdotL < 0.000000000011)
-    {
-        notShadowLevel = 1.;
-    }
-    shadowedOnlyLightDiffuseContribution *= notShadowLevel;
-#endif
-
-#ifdef SPECULARTERM
-    lightSpecularContribution += info.specular * notShadowLevel;
-#endif
-#endif
+#include<pbrLightFunctionsCall>[0]
+#include<pbrLightFunctionsCall>[1]
+#include<pbrLightFunctionsCall>[2]
+#include<pbrLightFunctionsCall>[3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 1 - 1
materialsLibrary/materials/pbr/pbr.vertex.fx

@@ -61,7 +61,7 @@ uniform mat4 reflectivityMatrix;
 
 #ifdef BUMP
 varying vec2 vBumpUV;
-uniform vec2 vBumpInfos;
+uniform vec3 vBumpInfos;
 uniform mat4 bumpMatrix;
 #endif
 

+ 65 - 0
materialsLibrary/test/add/addgrid.js

@@ -0,0 +1,65 @@
+window.prepareGrid = function() {
+	var grid = new BABYLON.GridMaterial("grid", scene);
+
+	registerRangeUI("grid", "LineColorR", 0, 1, function(value) {
+		grid.lineColor.r = value;
+	}, function() {
+		return grid.lineColor.r;
+	});
+    
+    registerRangeUI("grid", "LineColorG", 0, 1, function(value) {
+		grid.lineColor.g = value;
+	}, function() {
+		return grid.lineColor.g;
+	});
+    
+    registerRangeUI("grid", "LineColorB", 0, 1, function(value) {
+		grid.lineColor.b = value;
+	}, function() {
+		return grid.lineColor.b;
+	});
+    
+    registerRangeUI("grid", "MainColorR", 0, 1, function(value) {
+		grid.mainColor.r = value;
+	}, function() {
+		return grid.mainColor.r;
+	});
+    
+    registerRangeUI("grid", "MainColorG", 0, 1, function(value) {
+		grid.mainColor.g = value;
+	}, function() {
+		return grid.mainColor.g;
+	});
+    
+    registerRangeUI("grid", "MainColorB", 0, 1, function(value) {
+		grid.mainColor.b = value;
+	}, function() {
+		return grid.mainColor.b;
+	});
+    
+    registerRangeUI("grid", "GridRatio", 0, 10, function(value) {
+		grid.gridRatio = value;
+	}, function() {
+		return grid.gridRatio;
+	});
+    
+    registerRangeUI("grid", "MajorUnitFrequency", 1, 10, function(value) {
+		grid.majorUnitFrequency = value;
+	}, function() {
+		return grid.majorUnitFrequency;
+	});
+    
+    registerRangeUI("grid", "MinorUnitVisibility", 0, 1, function(value) {
+		grid.minorUnitVisibility = value;
+	}, function() {
+		return grid.minorUnitVisibility;
+	});
+    
+    registerRangeUI("grid", "Opacity", 0, 1, function(value) {
+		grid.opacity = value;
+	}, function() {
+		return grid.opacity;
+	});
+
+	return grid;
+}

+ 8 - 1
materialsLibrary/test/index.html

@@ -15,6 +15,7 @@
 	<script src="../dist/babylon.triPlanarMaterial.js"></script>
 	<script src="../dist/babylon.gradientMaterial.js"></script>
 	<script src="../dist/babylon.skyMaterial.js"></script>
+	<script src="../dist/babylon.gridMaterial.js"></script>
 
 	<style>
 		html, body {
@@ -59,6 +60,7 @@
 	<script src="add/addtriplanar.js"></script>
 	<script src="add/addgradient.js"></script>
 	<script src="add/addsky.js"></script>
+	<script src="add/addgrid.js"></script>
 	
 	<script>
 		if (BABYLON.Engine.isSupported()) {
@@ -204,13 +206,15 @@
 				var triPlanar = prepareTriPlanar();
 				
 				var sky = prepareSky();
+                
+                var grid = prepareGrid();
 				
 				// Default to std
 				var currentMaterial = std;
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky', 'grid']).onFinishChange(function () {
 					water.enableRenderTargets(false);
 					skybox.material = skyboxMaterial;
 					currentMesh.isVisible = true;
@@ -254,6 +258,9 @@
 							skybox.setEnabled(true);
 							skybox.material = sky;
 							break;
+                        case "grid":
+                            currentMaterial = grid;
+                            break;
 						default:
 							currentMaterial = std;
 							break;

File diff suppressed because it is too large
+ 702 - 680
src/Animations/babylon.animation.js


+ 23 - 0
src/Animations/babylon.animation.ts

@@ -172,6 +172,29 @@
 
         // Methods
         /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = "Name: " + this.name + ", property: " + this.targetProperty;
+            ret += ", datatype: " + (["Float", "Vector3", "Quaternion", "Matrix", "Color3", "Vector2"])[this.dataType];
+            ret += ", nKeys: " + (this._keys ? this._keys.length : "none");
+            ret += ", nRanges: " + (this._ranges ? Object.keys(this._ranges).length : "none");
+            if (fullDetails){
+                ret += ", Ranges: {" 
+                var first = true;
+                for (var name in this._ranges) {
+                    if (!first){
+                        ret + ", ";
+                        first = false; 
+                    }
+                    ret += name; 
+                }
+                ret += "}";
+            }
+            return ret;
+        } 
+        
+        /**
          * Add an event to this animation.
          */
         public addEvent(event: AnimationEvent): void {

+ 129 - 129
src/Bones/babylon.bone.js

@@ -1,129 +1,129 @@
-var __extends = (this && this.__extends) || function (d, b) {
-    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
-    function __() { this.constructor = d; }
-    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
-};
-var BABYLON;
-(function (BABYLON) {
-    var Bone = (function (_super) {
-        __extends(Bone, _super);
-        function Bone(name, skeleton, parentBone, matrix, restPose) {
-            _super.call(this, name, skeleton.getScene());
-            this.name = name;
-            this.children = new Array();
-            this.animations = new Array();
-            this._worldTransform = new BABYLON.Matrix();
-            this._absoluteTransform = new BABYLON.Matrix();
-            this._invertedAbsoluteTransform = new BABYLON.Matrix();
-            this._skeleton = skeleton;
-            this._matrix = matrix;
-            this._baseMatrix = matrix;
-            this._restPose = restPose ? restPose : matrix.clone();
-            skeleton.bones.push(this);
-            if (parentBone) {
-                this._parent = parentBone;
-                parentBone.children.push(this);
-            }
-            else {
-                this._parent = null;
-            }
-            this._updateDifferenceMatrix();
-        }
-        // Members
-        Bone.prototype.getParent = function () {
-            return this._parent;
-        };
-        Bone.prototype.getLocalMatrix = function () {
-            return this._matrix;
-        };
-        Bone.prototype.getBaseMatrix = function () {
-            return this._baseMatrix;
-        };
-        Bone.prototype.getRestPose = function () {
-            return this._restPose;
-        };
-        Bone.prototype.returnToRest = function () {
-            this.updateMatrix(this._restPose.clone());
-        };
-        Bone.prototype.getWorldMatrix = function () {
-            return this._worldTransform;
-        };
-        Bone.prototype.getInvertedAbsoluteTransform = function () {
-            return this._invertedAbsoluteTransform;
-        };
-        Bone.prototype.getAbsoluteTransform = function () {
-            return this._absoluteTransform;
-        };
-        // Methods
-        Bone.prototype.updateMatrix = function (matrix) {
-            this._baseMatrix = matrix.clone();
-            this._matrix = matrix.clone();
-            this._skeleton._markAsDirty();
-            this._updateDifferenceMatrix();
-        };
-        Bone.prototype._updateDifferenceMatrix = function (rootMatrix) {
-            if (!rootMatrix) {
-                rootMatrix = this._baseMatrix;
-            }
-            if (this._parent) {
-                rootMatrix.multiplyToRef(this._parent._absoluteTransform, this._absoluteTransform);
-            }
-            else {
-                this._absoluteTransform.copyFrom(rootMatrix);
-            }
-            this._absoluteTransform.invertToRef(this._invertedAbsoluteTransform);
-            for (var index = 0; index < this.children.length; index++) {
-                this.children[index]._updateDifferenceMatrix();
-            }
-        };
-        Bone.prototype.markAsDirty = function () {
-            this._currentRenderId++;
-            this._skeleton._markAsDirty();
-        };
-        Bone.prototype.copyAnimationRange = function (source, rangeName, frameOffset, rescaleAsRequired) {
-            if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
-            // all animation may be coming from a library skeleton, so may need to create animation
-            if (this.animations.length === 0) {
-                this.animations.push(new BABYLON.Animation(this.name, "_matrix", source.animations[0].framePerSecond, BABYLON.Animation.ANIMATIONTYPE_MATRIX, 0));
-                this.animations[0].setKeys([{}]);
-            }
-            // get animation info / verify there is such a range from the source bone
-            var sourceRange = source.animations[0].getRange(rangeName);
-            if (!sourceRange) {
-                return false;
-            }
-            var from = sourceRange.from;
-            var to = sourceRange.to;
-            var sourceKeys = source.animations[0].getKeys();
-            // rescaling prep
-            var sourceBoneLength = source.length;
-            var scalingReqd = rescaleAsRequired && sourceBoneLength && this.length && sourceBoneLength !== this.length;
-            var ratio = scalingReqd ? this.length / sourceBoneLength : null;
-            var destKeys = this.animations[0].getKeys();
-            // loop vars declaration / initialization
-            var orig;
-            var origScale = scalingReqd ? BABYLON.Vector3.Zero() : null;
-            var origRotation = scalingReqd ? new BABYLON.Quaternion() : null;
-            var origTranslation = scalingReqd ? BABYLON.Vector3.Zero() : null;
-            var mat;
-            for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
-                orig = sourceKeys[key];
-                if (orig.frame >= from && orig.frame <= to) {
-                    if (scalingReqd) {
-                        orig.value.decompose(origScale, origRotation, origTranslation);
-                        origTranslation.scaleInPlace(ratio);
-                        mat = BABYLON.Matrix.Compose(origScale, origRotation, origTranslation);
-                    }
-                    else {
-                        mat = orig.value;
-                    }
-                    destKeys.push({ frame: orig.frame + frameOffset, value: mat });
-                }
-            }
-            this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
-            return true;
-        };
-        return Bone;
-    }(BABYLON.Node));
-    BABYLON.Bone = Bone;
-})(BABYLON || (BABYLON = {}));
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var BABYLON;
+(function (BABYLON) {
+    var Bone = (function (_super) {
+        __extends(Bone, _super);
+        function Bone(name, skeleton, parentBone, matrix, restPose) {
+            _super.call(this, name, skeleton.getScene());
+            this.name = name;
+            this.children = new Array();
+            this.animations = new Array();
+            this._worldTransform = new BABYLON.Matrix();
+            this._absoluteTransform = new BABYLON.Matrix();
+            this._invertedAbsoluteTransform = new BABYLON.Matrix();
+            this._skeleton = skeleton;
+            this._matrix = matrix;
+            this._baseMatrix = matrix;
+            this._restPose = restPose ? restPose : matrix.clone();
+            skeleton.bones.push(this);
+            if (parentBone) {
+                this._parent = parentBone;
+                parentBone.children.push(this);
+            }
+            else {
+                this._parent = null;
+            }
+            this._updateDifferenceMatrix();
+        }
+        // Members
+        Bone.prototype.getParent = function () {
+            return this._parent;
+        };
+        Bone.prototype.getLocalMatrix = function () {
+            return this._matrix;
+        };
+        Bone.prototype.getBaseMatrix = function () {
+            return this._baseMatrix;
+        };
+        Bone.prototype.getRestPose = function () {
+            return this._restPose;
+        };
+        Bone.prototype.returnToRest = function () {
+            this.updateMatrix(this._restPose.clone());
+        };
+        Bone.prototype.getWorldMatrix = function () {
+            return this._worldTransform;
+        };
+        Bone.prototype.getInvertedAbsoluteTransform = function () {
+            return this._invertedAbsoluteTransform;
+        };
+        Bone.prototype.getAbsoluteTransform = function () {
+            return this._absoluteTransform;
+        };
+        // Methods
+        Bone.prototype.updateMatrix = function (matrix) {
+            this._baseMatrix = matrix.clone();
+            this._matrix = matrix.clone();
+            this._skeleton._markAsDirty();
+            this._updateDifferenceMatrix();
+        };
+        Bone.prototype._updateDifferenceMatrix = function (rootMatrix) {
+            if (!rootMatrix) {
+                rootMatrix = this._baseMatrix;
+            }
+            if (this._parent) {
+                rootMatrix.multiplyToRef(this._parent._absoluteTransform, this._absoluteTransform);
+            }
+            else {
+                this._absoluteTransform.copyFrom(rootMatrix);
+            }
+            this._absoluteTransform.invertToRef(this._invertedAbsoluteTransform);
+            for (var index = 0; index < this.children.length; index++) {
+                this.children[index]._updateDifferenceMatrix();
+            }
+        };
+        Bone.prototype.markAsDirty = function () {
+            this._currentRenderId++;
+            this._skeleton._markAsDirty();
+        };
+        Bone.prototype.copyAnimationRange = function (source, rangeName, frameOffset, rescaleAsRequired) {
+            if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
+            // all animation may be coming from a library skeleton, so may need to create animation
+            if (this.animations.length === 0) {
+                this.animations.push(new BABYLON.Animation(this.name, "_matrix", source.animations[0].framePerSecond, BABYLON.Animation.ANIMATIONTYPE_MATRIX, 0));
+                this.animations[0].setKeys([]);
+            }
+            // get animation info / verify there is such a range from the source bone
+            var sourceRange = source.animations[0].getRange(rangeName);
+            if (!sourceRange) {
+                return false;
+            }
+            var from = sourceRange.from;
+            var to = sourceRange.to;
+            var sourceKeys = source.animations[0].getKeys();
+            // rescaling prep
+            var sourceBoneLength = source.length;
+            var scalingReqd = rescaleAsRequired && sourceBoneLength && this.length && sourceBoneLength !== this.length;
+            var ratio = scalingReqd ? this.length / sourceBoneLength : null;
+            var destKeys = this.animations[0].getKeys();
+            // loop vars declaration / initialization
+            var orig;
+            var origScale = scalingReqd ? BABYLON.Vector3.Zero() : null;
+            var origRotation = scalingReqd ? new BABYLON.Quaternion() : null;
+            var origTranslation = scalingReqd ? BABYLON.Vector3.Zero() : null;
+            var mat;
+            for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
+                orig = sourceKeys[key];
+                if (orig.frame >= from && orig.frame <= to) {
+                    if (scalingReqd) {
+                        orig.value.decompose(origScale, origRotation, origTranslation);
+                        origTranslation.scaleInPlace(ratio);
+                        mat = BABYLON.Matrix.Compose(origScale, origRotation, origTranslation);
+                    }
+                    else {
+                        mat = orig.value;
+                    }
+                    destKeys.push({ frame: orig.frame + frameOffset, value: mat });
+                }
+            }
+            this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
+            return true;
+        };
+        return Bone;
+    })(BABYLON.Node);
+    BABYLON.Bone = Bone;
+})(BABYLON || (BABYLON = {}));

+ 1 - 1
src/Bones/babylon.bone.ts

@@ -102,7 +102,7 @@
             // all animation may be coming from a library skeleton, so may need to create animation
             if (this.animations.length === 0) {
                 this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
-                this.animations[0].setKeys([{}]);
+                this.animations[0].setKeys([]);
             }
 
             // get animation info / verify there is such a range from the source bone

+ 296 - 273
src/Bones/babylon.skeleton.js

@@ -1,273 +1,296 @@
-var BABYLON;
-(function (BABYLON) {
-    var Skeleton = (function () {
-        function Skeleton(name, id, scene) {
-            this.name = name;
-            this.id = id;
-            this.bones = new Array();
-            this.needInitialSkinMatrix = false;
-            this._isDirty = true;
-            this._meshesWithPoseMatrix = new Array();
-            this._identity = BABYLON.Matrix.Identity();
-            this._ranges = {};
-            this.bones = [];
-            this._scene = scene;
-            scene.skeletons.push(this);
-            this.prepare();
-            //make sure it will recalculate the matrix next time prepare is called.
-            this._isDirty = true;
-        }
-        // Members
-        Skeleton.prototype.getTransformMatrices = function (mesh) {
-            if (this.needInitialSkinMatrix && mesh._bonesTransformMatrices) {
-                return mesh._bonesTransformMatrices;
-            }
-            return this._transformMatrices;
-        };
-        Skeleton.prototype.getScene = function () {
-            return this._scene;
-        };
-        // Methods
-        Skeleton.prototype.createAnimationRange = function (name, from, to) {
-            // check name not already in use
-            if (!this._ranges[name]) {
-                this._ranges[name] = new BABYLON.AnimationRange(name, from, to);
-                for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
-                    if (this.bones[i].animations[0]) {
-                        this.bones[i].animations[0].createRange(name, from, to);
-                    }
-                }
-            }
-        };
-        Skeleton.prototype.deleteAnimationRange = function (name, deleteFrames) {
-            if (deleteFrames === void 0) { deleteFrames = true; }
-            for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
-                if (this.bones[i].animations[0]) {
-                    this.bones[i].animations[0].deleteRange(name, deleteFrames);
-                }
-            }
-            this._ranges[name] = undefined; // said much faster than 'delete this._range[name]' 
-        };
-        Skeleton.prototype.getAnimationRange = function (name) {
-            return this._ranges[name];
-        };
-        /**
-         *  note: This is not for a complete retargeting, only between very similar skeleton's with only possible bone length differences
-         */
-        Skeleton.prototype.copyAnimationRange = function (source, name, rescaleAsRequired) {
-            if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
-            if (this._ranges[name] || !source.getAnimationRange(name)) {
-                return false;
-            }
-            var ret = true;
-            var frameOffset = this._getHighestAnimationFrame() + 1;
-            // make a dictionary of source skeleton's bones, so exact same order or doublely nested loop is not required
-            var boneDict = {};
-            var sourceBones = source.bones;
-            for (var i = 0, nBones = sourceBones.length; i < nBones; i++) {
-                boneDict[sourceBones[i].name] = sourceBones[i];
-            }
-            for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
-                var boneName = this.bones[i].name;
-                var sourceBone = boneDict[boneName];
-                if (sourceBone) {
-                    ret = ret && this.bones[i].copyAnimationRange(sourceBone, name, frameOffset, rescaleAsRequired);
-                }
-                else {
-                    BABYLON.Tools.Warn("copyAnimationRange: not same rig, missing source bone " + boneName);
-                    ret = false;
-                }
-            }
-            // do not call createAnimationRange(), since it also is done to bones, which was already done
-            var range = source.getAnimationRange(name);
-            this._ranges[name] = new BABYLON.AnimationRange(name, range.from + frameOffset, range.to + frameOffset);
-            return ret;
-        };
-        Skeleton.prototype.returnToRest = function () {
-            for (var index = 0; index < this.bones.length; index++) {
-                this.bones[index].returnToRest();
-            }
-        };
-        Skeleton.prototype._getHighestAnimationFrame = function () {
-            var ret = 0;
-            for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
-                if (this.bones[i].animations[0]) {
-                    var highest = this.bones[i].animations[0].getHighestFrame();
-                    if (ret < highest) {
-                        ret = highest;
-                    }
-                }
-            }
-            return ret;
-        };
-        Skeleton.prototype.beginAnimation = function (name, loop, speedRatio, onAnimationEnd) {
-            var range = this.getAnimationRange(name);
-            if (!range) {
-                return null;
-            }
-            this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
-        };
-        Skeleton.prototype._markAsDirty = function () {
-            this._isDirty = true;
-        };
-        Skeleton.prototype._registerMeshWithPoseMatrix = function (mesh) {
-            this._meshesWithPoseMatrix.push(mesh);
-        };
-        Skeleton.prototype._unregisterMeshWithPoseMatrix = function (mesh) {
-            var index = this._meshesWithPoseMatrix.indexOf(mesh);
-            if (index > -1) {
-                this._meshesWithPoseMatrix.splice(index, 1);
-            }
-        };
-        Skeleton.prototype._computeTransformMatrices = function (targetMatrix, initialSkinMatrix) {
-            for (var index = 0; index < this.bones.length; index++) {
-                var bone = this.bones[index];
-                var parentBone = bone.getParent();
-                if (parentBone) {
-                    bone.getLocalMatrix().multiplyToRef(parentBone.getWorldMatrix(), bone.getWorldMatrix());
-                }
-                else {
-                    if (initialSkinMatrix) {
-                        bone.getLocalMatrix().multiplyToRef(initialSkinMatrix, bone.getWorldMatrix());
-                    }
-                    else {
-                        bone.getWorldMatrix().copyFrom(bone.getLocalMatrix());
-                    }
-                }
-                bone.getInvertedAbsoluteTransform().multiplyToArray(bone.getWorldMatrix(), targetMatrix, index * 16);
-            }
-            this._identity.copyToArray(targetMatrix, this.bones.length * 16);
-        };
-        Skeleton.prototype.prepare = function () {
-            if (!this._isDirty) {
-                return;
-            }
-            if (this.needInitialSkinMatrix) {
-                for (var index = 0; index < this._meshesWithPoseMatrix.length; index++) {
-                    var mesh = this._meshesWithPoseMatrix[index];
-                    if (!mesh._bonesTransformMatrices || mesh._bonesTransformMatrices.length !== 16 * (this.bones.length + 1)) {
-                        mesh._bonesTransformMatrices = new Float32Array(16 * (this.bones.length + 1));
-                    }
-                    var poseMatrix = mesh.getPoseMatrix();
-                    // Prepare bones
-                    for (var boneIndex = 0; boneIndex < this.bones.length; boneIndex++) {
-                        var bone = this.bones[boneIndex];
-                        if (!bone.getParent()) {
-                            var matrix = bone.getBaseMatrix();
-                            matrix.multiplyToRef(poseMatrix, BABYLON.Tmp.Matrix[0]);
-                            bone._updateDifferenceMatrix(BABYLON.Tmp.Matrix[0]);
-                        }
-                    }
-                    this._computeTransformMatrices(mesh._bonesTransformMatrices, poseMatrix);
-                }
-            }
-            else {
-                if (!this._transformMatrices || this._transformMatrices.length !== 16 * (this.bones.length + 1)) {
-                    this._transformMatrices = new Float32Array(16 * (this.bones.length + 1));
-                }
-                this._computeTransformMatrices(this._transformMatrices, null);
-            }
-            this._isDirty = false;
-            this._scene._activeBones += this.bones.length;
-        };
-        Skeleton.prototype.getAnimatables = function () {
-            if (!this._animatables || this._animatables.length !== this.bones.length) {
-                this._animatables = [];
-                for (var index = 0; index < this.bones.length; index++) {
-                    this._animatables.push(this.bones[index]);
-                }
-            }
-            return this._animatables;
-        };
-        Skeleton.prototype.clone = function (name, id) {
-            var result = new Skeleton(name, id || name, this._scene);
-            result.needInitialSkinMatrix = this.needInitialSkinMatrix;
-            for (var index = 0; index < this.bones.length; index++) {
-                var source = this.bones[index];
-                var parentBone = null;
-                if (source.getParent()) {
-                    var parentIndex = this.bones.indexOf(source.getParent());
-                    parentBone = result.bones[parentIndex];
-                }
-                var bone = new BABYLON.Bone(source.name, result, parentBone, source.getBaseMatrix().clone(), source.getRestPose().clone());
-                BABYLON.Tools.DeepCopy(source.animations, bone.animations);
-            }
-            if (this._ranges) {
-                result._ranges = {};
-                for (var rangeName in this._ranges) {
-                    result._ranges[rangeName] = this._ranges[rangeName].clone();
-                }
-            }
-            result.prepare();
-            return result;
-        };
-        Skeleton.prototype.dispose = function () {
-            this._meshesWithPoseMatrix = [];
-            // Animations
-            this.getScene().stopAnimation(this);
-            // Remove from scene
-            this.getScene().removeSkeleton(this);
-        };
-        Skeleton.prototype.serialize = function () {
-            var serializationObject = {};
-            serializationObject.name = this.name;
-            serializationObject.id = this.id;
-            serializationObject.bones = [];
-            serializationObject.needInitialSkinMatrix = this.needInitialSkinMatrix;
-            for (var index = 0; index < this.bones.length; index++) {
-                var bone = this.bones[index];
-                var serializedBone = {
-                    parentBoneIndex: bone.getParent() ? this.bones.indexOf(bone.getParent()) : -1,
-                    name: bone.name,
-                    matrix: bone.getLocalMatrix().toArray(),
-                    rest: bone.getRestPose().toArray()
-                };
-                serializationObject.bones.push(serializedBone);
-                if (bone.length) {
-                    serializedBone.length = bone.length;
-                }
-                if (bone.animations && bone.animations.length > 0) {
-                    serializedBone.animation = bone.animations[0].serialize();
-                }
-                serializationObject.ranges = [];
-                for (var name in this._ranges) {
-                    var range = {};
-                    range.name = name;
-                    range.from = this._ranges[name].from;
-                    range.to = this._ranges[name].to;
-                    serializationObject.ranges.push(range);
-                }
-            }
-            return serializationObject;
-        };
-        Skeleton.Parse = function (parsedSkeleton, scene) {
-            var skeleton = new Skeleton(parsedSkeleton.name, parsedSkeleton.id, scene);
-            skeleton.needInitialSkinMatrix = parsedSkeleton.needInitialSkinMatrix;
-            for (var index = 0; index < parsedSkeleton.bones.length; index++) {
-                var parsedBone = parsedSkeleton.bones[index];
-                var parentBone = null;
-                if (parsedBone.parentBoneIndex > -1) {
-                    parentBone = skeleton.bones[parsedBone.parentBoneIndex];
-                }
-                var rest = parsedBone.rest ? BABYLON.Matrix.FromArray(parsedBone.rest) : null;
-                var bone = new BABYLON.Bone(parsedBone.name, skeleton, parentBone, BABYLON.Matrix.FromArray(parsedBone.matrix), rest);
-                if (parsedBone.length) {
-                    bone.length = parsedBone.length;
-                }
-                if (parsedBone.animation) {
-                    bone.animations.push(BABYLON.Animation.Parse(parsedBone.animation));
-                }
-            }
-            // placed after bones, so createAnimationRange can cascade down
-            if (parsedSkeleton.ranges) {
-                for (var index = 0; index < parsedSkeleton.ranges.length; index++) {
-                    var data = parsedSkeleton.ranges[index];
-                    skeleton.createAnimationRange(data.name, data.from, data.to);
-                }
-            }
-            return skeleton;
-        };
-        return Skeleton;
-    }());
-    BABYLON.Skeleton = Skeleton;
-})(BABYLON || (BABYLON = {}));
+var BABYLON;
+(function (BABYLON) {
+    var Skeleton = (function () {
+        function Skeleton(name, id, scene) {
+            this.name = name;
+            this.id = id;
+            this.bones = new Array();
+            this.needInitialSkinMatrix = false;
+            this._isDirty = true;
+            this._meshesWithPoseMatrix = new Array();
+            this._identity = BABYLON.Matrix.Identity();
+            this._ranges = {};
+            this.bones = [];
+            this._scene = scene;
+            scene.skeletons.push(this);
+            //make sure it will recalculate the matrix next time prepare is called.
+            this._isDirty = true;
+        }
+        // Members
+        Skeleton.prototype.getTransformMatrices = function (mesh) {
+            if (this.needInitialSkinMatrix && mesh._bonesTransformMatrices) {
+                return mesh._bonesTransformMatrices;
+            }
+            return this._transformMatrices;
+        };
+        Skeleton.prototype.getScene = function () {
+            return this._scene;
+        };
+        // Methods
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        Skeleton.prototype.toString = function (fullDetails) {
+            var ret = "Name: " + this.name + ", nBones: " + this.bones.length;
+            ret += ", nAnimationRanges: " + (this._ranges ? Object.keys(this._ranges).length : "none");
+            if (fullDetails) {
+                ret += ", Ranges: {";
+                var first = true;
+                for (var name in this._ranges) {
+                    if (!first) {
+                        ret + ", ";
+                        first = false;
+                    }
+                    ret += name;
+                }
+                ret += "}";
+            }
+            return ret;
+        };
+        Skeleton.prototype.createAnimationRange = function (name, from, to) {
+            // check name not already in use
+            if (!this._ranges[name]) {
+                this._ranges[name] = new BABYLON.AnimationRange(name, from, to);
+                for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
+                    if (this.bones[i].animations[0]) {
+                        this.bones[i].animations[0].createRange(name, from, to);
+                    }
+                }
+            }
+        };
+        Skeleton.prototype.deleteAnimationRange = function (name, deleteFrames) {
+            if (deleteFrames === void 0) { deleteFrames = true; }
+            for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
+                if (this.bones[i].animations[0]) {
+                    this.bones[i].animations[0].deleteRange(name, deleteFrames);
+                }
+            }
+            this._ranges[name] = undefined; // said much faster than 'delete this._range[name]' 
+        };
+        Skeleton.prototype.getAnimationRange = function (name) {
+            return this._ranges[name];
+        };
+        /**
+         *  note: This is not for a complete retargeting, only between very similar skeleton's with only possible bone length differences
+         */
+        Skeleton.prototype.copyAnimationRange = function (source, name, rescaleAsRequired) {
+            if (rescaleAsRequired === void 0) { rescaleAsRequired = false; }
+            if (this._ranges[name] || !source.getAnimationRange(name)) {
+                return false;
+            }
+            var ret = true;
+            var frameOffset = this._getHighestAnimationFrame() + 1;
+            // make a dictionary of source skeleton's bones, so exact same order or doublely nested loop is not required
+            var boneDict = {};
+            var sourceBones = source.bones;
+            for (var i = 0, nBones = sourceBones.length; i < nBones; i++) {
+                boneDict[sourceBones[i].name] = sourceBones[i];
+            }
+            if (this.bones.length !== sourceBones.length) {
+                BABYLON.Tools.Warn("copyAnimationRange: this rig has " + this.bones.length + " bones, while source as " + sourceBones.length);
+                ret = false;
+            }
+            for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
+                var boneName = this.bones[i].name;
+                var sourceBone = boneDict[boneName];
+                if (sourceBone) {
+                    ret = ret && this.bones[i].copyAnimationRange(sourceBone, name, frameOffset, rescaleAsRequired);
+                }
+                else {
+                    BABYLON.Tools.Warn("copyAnimationRange: not same rig, missing source bone " + boneName);
+                    ret = false;
+                }
+            }
+            // do not call createAnimationRange(), since it also is done to bones, which was already done
+            var range = source.getAnimationRange(name);
+            this._ranges[name] = new BABYLON.AnimationRange(name, range.from + frameOffset, range.to + frameOffset);
+            return ret;
+        };
+        Skeleton.prototype.returnToRest = function () {
+            for (var index = 0; index < this.bones.length; index++) {
+                this.bones[index].returnToRest();
+            }
+        };
+        Skeleton.prototype._getHighestAnimationFrame = function () {
+            var ret = 0;
+            for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
+                if (this.bones[i].animations[0]) {
+                    var highest = this.bones[i].animations[0].getHighestFrame();
+                    if (ret < highest) {
+                        ret = highest;
+                    }
+                }
+            }
+            return ret;
+        };
+        Skeleton.prototype.beginAnimation = function (name, loop, speedRatio, onAnimationEnd) {
+            var range = this.getAnimationRange(name);
+            if (!range) {
+                return null;
+            }
+            this._scene.beginAnimation(this, range.from, range.to, loop, speedRatio, onAnimationEnd);
+        };
+        Skeleton.prototype._markAsDirty = function () {
+            this._isDirty = true;
+        };
+        Skeleton.prototype._registerMeshWithPoseMatrix = function (mesh) {
+            this._meshesWithPoseMatrix.push(mesh);
+        };
+        Skeleton.prototype._unregisterMeshWithPoseMatrix = function (mesh) {
+            var index = this._meshesWithPoseMatrix.indexOf(mesh);
+            if (index > -1) {
+                this._meshesWithPoseMatrix.splice(index, 1);
+            }
+        };
+        Skeleton.prototype._computeTransformMatrices = function (targetMatrix, initialSkinMatrix) {
+            for (var index = 0; index < this.bones.length; index++) {
+                var bone = this.bones[index];
+                var parentBone = bone.getParent();
+                if (parentBone) {
+                    bone.getLocalMatrix().multiplyToRef(parentBone.getWorldMatrix(), bone.getWorldMatrix());
+                }
+                else {
+                    if (initialSkinMatrix) {
+                        bone.getLocalMatrix().multiplyToRef(initialSkinMatrix, bone.getWorldMatrix());
+                    }
+                    else {
+                        bone.getWorldMatrix().copyFrom(bone.getLocalMatrix());
+                    }
+                }
+                bone.getInvertedAbsoluteTransform().multiplyToArray(bone.getWorldMatrix(), targetMatrix, index * 16);
+            }
+            this._identity.copyToArray(targetMatrix, this.bones.length * 16);
+        };
+        Skeleton.prototype.prepare = function () {
+            if (!this._isDirty) {
+                return;
+            }
+            if (this.needInitialSkinMatrix) {
+                for (var index = 0; index < this._meshesWithPoseMatrix.length; index++) {
+                    var mesh = this._meshesWithPoseMatrix[index];
+                    if (!mesh._bonesTransformMatrices || mesh._bonesTransformMatrices.length !== 16 * (this.bones.length + 1)) {
+                        mesh._bonesTransformMatrices = new Float32Array(16 * (this.bones.length + 1));
+                    }
+                    var poseMatrix = mesh.getPoseMatrix();
+                    // Prepare bones
+                    for (var boneIndex = 0; boneIndex < this.bones.length; boneIndex++) {
+                        var bone = this.bones[boneIndex];
+                        if (!bone.getParent()) {
+                            var matrix = bone.getBaseMatrix();
+                            matrix.multiplyToRef(poseMatrix, BABYLON.Tmp.Matrix[0]);
+                            bone._updateDifferenceMatrix(BABYLON.Tmp.Matrix[0]);
+                        }
+                    }
+                    this._computeTransformMatrices(mesh._bonesTransformMatrices, poseMatrix);
+                }
+            }
+            else {
+                if (!this._transformMatrices || this._transformMatrices.length !== 16 * (this.bones.length + 1)) {
+                    this._transformMatrices = new Float32Array(16 * (this.bones.length + 1));
+                }
+                this._computeTransformMatrices(this._transformMatrices, null);
+            }
+            this._isDirty = false;
+            this._scene._activeBones += this.bones.length;
+        };
+        Skeleton.prototype.getAnimatables = function () {
+            if (!this._animatables || this._animatables.length !== this.bones.length) {
+                this._animatables = [];
+                for (var index = 0; index < this.bones.length; index++) {
+                    this._animatables.push(this.bones[index]);
+                }
+            }
+            return this._animatables;
+        };
+        Skeleton.prototype.clone = function (name, id) {
+            var result = new Skeleton(name, id || name, this._scene);
+            result.needInitialSkinMatrix = this.needInitialSkinMatrix;
+            for (var index = 0; index < this.bones.length; index++) {
+                var source = this.bones[index];
+                var parentBone = null;
+                if (source.getParent()) {
+                    var parentIndex = this.bones.indexOf(source.getParent());
+                    parentBone = result.bones[parentIndex];
+                }
+                var bone = new BABYLON.Bone(source.name, result, parentBone, source.getBaseMatrix().clone(), source.getRestPose().clone());
+                BABYLON.Tools.DeepCopy(source.animations, bone.animations);
+            }
+            if (this._ranges) {
+                result._ranges = {};
+                for (var rangeName in this._ranges) {
+                    result._ranges[rangeName] = this._ranges[rangeName].clone();
+                }
+            }
+            this._isDirty = true;
+            return result;
+        };
+        Skeleton.prototype.dispose = function () {
+            this._meshesWithPoseMatrix = [];
+            // Animations
+            this.getScene().stopAnimation(this);
+            // Remove from scene
+            this.getScene().removeSkeleton(this);
+        };
+        Skeleton.prototype.serialize = function () {
+            var serializationObject = {};
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+            serializationObject.bones = [];
+            serializationObject.needInitialSkinMatrix = this.needInitialSkinMatrix;
+            for (var index = 0; index < this.bones.length; index++) {
+                var bone = this.bones[index];
+                var serializedBone = {
+                    parentBoneIndex: bone.getParent() ? this.bones.indexOf(bone.getParent()) : -1,
+                    name: bone.name,
+                    matrix: bone.getLocalMatrix().toArray(),
+                    rest: bone.getRestPose().toArray()
+                };
+                serializationObject.bones.push(serializedBone);
+                if (bone.length) {
+                    serializedBone.length = bone.length;
+                }
+                if (bone.animations && bone.animations.length > 0) {
+                    serializedBone.animation = bone.animations[0].serialize();
+                }
+                serializationObject.ranges = [];
+                for (var name in this._ranges) {
+                    var range = {};
+                    range.name = name;
+                    range.from = this._ranges[name].from;
+                    range.to = this._ranges[name].to;
+                    serializationObject.ranges.push(range);
+                }
+            }
+            return serializationObject;
+        };
+        Skeleton.Parse = function (parsedSkeleton, scene) {
+            var skeleton = new Skeleton(parsedSkeleton.name, parsedSkeleton.id, scene);
+            skeleton.needInitialSkinMatrix = parsedSkeleton.needInitialSkinMatrix;
+            for (var index = 0; index < parsedSkeleton.bones.length; index++) {
+                var parsedBone = parsedSkeleton.bones[index];
+                var parentBone = null;
+                if (parsedBone.parentBoneIndex > -1) {
+                    parentBone = skeleton.bones[parsedBone.parentBoneIndex];
+                }
+                var rest = parsedBone.rest ? BABYLON.Matrix.FromArray(parsedBone.rest) : null;
+                var bone = new BABYLON.Bone(parsedBone.name, skeleton, parentBone, BABYLON.Matrix.FromArray(parsedBone.matrix), rest);
+                if (parsedBone.length) {
+                    bone.length = parsedBone.length;
+                }
+                if (parsedBone.animation) {
+                    bone.animations.push(BABYLON.Animation.Parse(parsedBone.animation));
+                }
+            }
+            // placed after bones, so createAnimationRange can cascade down
+            if (parsedSkeleton.ranges) {
+                for (var index = 0; index < parsedSkeleton.ranges.length; index++) {
+                    var data = parsedSkeleton.ranges[index];
+                    skeleton.createAnimationRange(data.name, data.from, data.to);
+                }
+            }
+            return skeleton;
+        };
+        return Skeleton;
+    })();
+    BABYLON.Skeleton = Skeleton;
+})(BABYLON || (BABYLON = {}));

+ 27 - 2
src/Bones/babylon.skeleton.ts

@@ -20,7 +20,6 @@
 
             scene.skeletons.push(this);
 
-            this.prepare();
             //make sure it will recalculate the matrix next time prepare is called.
             this._isDirty = true;
         }
@@ -38,6 +37,27 @@
         }
 
         // Methods
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = "Name: " + this.name + ", nBones: " + this.bones.length;
+            ret += ", nAnimationRanges: " + (this._ranges ? Object.keys(this._ranges).length : "none");
+            if (fullDetails){
+                ret += ", Ranges: {" 
+                var first = true;
+                for (var name in this._ranges) {
+                    if (!first){
+                        ret + ", ";
+                        first = false; 
+                    }
+                    ret += name; 
+                }
+                ret += "}";
+            }
+            return ret;
+        } 
+        
         public createAnimationRange(name: string, from: number, to: number): void {
             // check name not already in use
             if (!this._ranges[name]) {
@@ -80,6 +100,11 @@
                 boneDict[sourceBones[i].name] = sourceBones[i];
             }
 
+            if (this.bones.length !== sourceBones.length){
+                BABYLON.Tools.Warn("copyAnimationRange: this rig has " + this.bones.length + " bones, while source as " + sourceBones.length);
+                ret = false;
+            }
+            
             for (var i = 0, nBones = this.bones.length; i < nBones; i++) {
                 var boneName = this.bones[i].name;
                 var sourceBone = boneDict[boneName];
@@ -240,7 +265,7 @@
                 }
             }
 
-            result.prepare();
+            this._isDirty = true;
 
             return result;
         }

File diff suppressed because it is too large
+ 628 - 620
src/Cameras/babylon.camera.js


+ 16 - 0
src/Cameras/babylon.camera.ts

@@ -138,6 +138,22 @@
             this.position = position;
         }
 
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = "Name: " + this.name;
+            ret += ", type: " + this.getTypeName();
+            if (this.animations){
+                for (var i = 0; i < this.animations.length; i++){
+                   ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
+                }
+            }
+            if (fullDetails){
+            }
+            return ret;
+        } 
+        
         public get globalPosition(): Vector3 {
             return this._globalPosition;
         }

+ 177 - 162
src/Lights/babylon.light.js

@@ -1,162 +1,177 @@
-var __extends = (this && this.__extends) || function (d, b) {
-    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
-    function __() { this.constructor = d; }
-    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
-};
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var BABYLON;
-(function (BABYLON) {
-    var Light = (function (_super) {
-        __extends(Light, _super);
-        function Light(name, scene) {
-            _super.call(this, name, scene);
-            this.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
-            this.specular = new BABYLON.Color3(1.0, 1.0, 1.0);
-            this.intensity = 1.0;
-            this.range = Number.MAX_VALUE;
-            this.includeOnlyWithLayerMask = 0;
-            this.includedOnlyMeshes = new Array();
-            this.excludedMeshes = new Array();
-            this.excludeWithLayerMask = 0;
-            // PBR Properties.
-            this.radius = 0.00001;
-            this._excludedMeshesIds = new Array();
-            this._includedOnlyMeshesIds = new Array();
-            scene.addLight(this);
-        }
-        Light.prototype.getShadowGenerator = function () {
-            return this._shadowGenerator;
-        };
-        Light.prototype.getAbsolutePosition = function () {
-            return BABYLON.Vector3.Zero();
-        };
-        Light.prototype.transferToEffect = function (effect, uniformName0, uniformName1) {
-        };
-        Light.prototype._getWorldMatrix = function () {
-            return BABYLON.Matrix.Identity();
-        };
-        Light.prototype.canAffectMesh = function (mesh) {
-            if (!mesh) {
-                return true;
-            }
-            if (this.includedOnlyMeshes.length > 0 && this.includedOnlyMeshes.indexOf(mesh) === -1) {
-                return false;
-            }
-            if (this.excludedMeshes.length > 0 && this.excludedMeshes.indexOf(mesh) !== -1) {
-                return false;
-            }
-            if (this.includeOnlyWithLayerMask !== 0 && (this.includeOnlyWithLayerMask & mesh.layerMask) === 0) {
-                return false;
-            }
-            if (this.excludeWithLayerMask !== 0 && this.excludeWithLayerMask & mesh.layerMask) {
-                return false;
-            }
-            return true;
-        };
-        Light.prototype.getWorldMatrix = function () {
-            this._currentRenderId = this.getScene().getRenderId();
-            var worldMatrix = this._getWorldMatrix();
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (!this._parentedWorldMatrix) {
-                    this._parentedWorldMatrix = BABYLON.Matrix.Identity();
-                }
-                worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._parentedWorldMatrix);
-                this._markSyncedWithParent();
-                return this._parentedWorldMatrix;
-            }
-            return worldMatrix;
-        };
-        Light.prototype.dispose = function () {
-            if (this._shadowGenerator) {
-                this._shadowGenerator.dispose();
-                this._shadowGenerator = null;
-            }
-            // Animations
-            this.getScene().stopAnimation(this);
-            // Remove from scene
-            this.getScene().removeLight(this);
-        };
-        Light.prototype.getTypeID = function () {
-            return 0;
-        };
-        Light.prototype.clone = function (name) {
-            return BABYLON.SerializationHelper.Clone(Light.GetConstructorFromName(this.getTypeID(), name, this.getScene()), this);
-        };
-        Light.prototype.serialize = function () {
-            var serializationObject = BABYLON.SerializationHelper.Serialize(this);
-            // Type
-            serializationObject.type = this.getTypeID();
-            // Parent
-            if (this.parent) {
-                serializationObject.parentId = this.parent.id;
-            }
-            // Animations  
-            BABYLON.Animation.AppendSerializedAnimations(this, serializationObject);
-            serializationObject.ranges = this.serializeAnimationRanges();
-            return serializationObject;
-        };
-        Light.GetConstructorFromName = function (type, name, scene) {
-            switch (type) {
-                case 0:
-                    return function () { return new BABYLON.PointLight(name, BABYLON.Vector3.Zero(), scene); };
-                case 1:
-                    return function () { return new BABYLON.DirectionalLight(name, BABYLON.Vector3.Zero(), scene); };
-                case 2:
-                    return function () { return new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Zero(), 0, 0, scene); };
-                case 3:
-                    return function () { return new BABYLON.HemisphericLight(name, BABYLON.Vector3.Zero(), scene); };
-            }
-        };
-        Light.Parse = function (parsedLight, scene) {
-            var light = BABYLON.SerializationHelper.Parse(Light.GetConstructorFromName(parsedLight.type, parsedLight.name, scene), parsedLight, scene);
-            // Inclusion / exclusions
-            if (parsedLight.excludedMeshesIds) {
-                light._excludedMeshesIds = parsedLight.excludedMeshesIds;
-            }
-            if (parsedLight.includedOnlyMeshesIds) {
-                light._includedOnlyMeshesIds = parsedLight.includedOnlyMeshesIds;
-            }
-            // Parent
-            if (parsedLight.parentId) {
-                light._waitingParentId = parsedLight.parentId;
-            }
-            // Animations
-            if (parsedLight.animations) {
-                for (var animationIndex = 0; animationIndex < parsedLight.animations.length; animationIndex++) {
-                    var parsedAnimation = parsedLight.animations[animationIndex];
-                    light.animations.push(BABYLON.Animation.Parse(parsedAnimation));
-                }
-                BABYLON.Node.ParseAnimationRanges(light, parsedLight, scene);
-            }
-            if (parsedLight.autoAnimate) {
-                scene.beginAnimation(light, parsedLight.autoAnimateFrom, parsedLight.autoAnimateTo, parsedLight.autoAnimateLoop, parsedLight.autoAnimateSpeed || 1.0);
-            }
-            return light;
-        };
-        __decorate([
-            BABYLON.serializeAsColor3()
-        ], Light.prototype, "diffuse", void 0);
-        __decorate([
-            BABYLON.serializeAsColor3()
-        ], Light.prototype, "specular", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Light.prototype, "intensity", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Light.prototype, "range", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Light.prototype, "includeOnlyWithLayerMask", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Light.prototype, "radius", void 0);
-        return Light;
-    }(BABYLON.Node));
-    BABYLON.Light = Light;
-})(BABYLON || (BABYLON = {}));
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var Light = (function (_super) {
+        __extends(Light, _super);
+        function Light(name, scene) {
+            _super.call(this, name, scene);
+            this.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
+            this.specular = new BABYLON.Color3(1.0, 1.0, 1.0);
+            this.intensity = 1.0;
+            this.range = Number.MAX_VALUE;
+            this.includeOnlyWithLayerMask = 0;
+            this.includedOnlyMeshes = new Array();
+            this.excludedMeshes = new Array();
+            this.excludeWithLayerMask = 0;
+            // PBR Properties.
+            this.radius = 0.00001;
+            this._excludedMeshesIds = new Array();
+            this._includedOnlyMeshesIds = new Array();
+            scene.addLight(this);
+        }
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        Light.prototype.toString = function (fullDetails) {
+            var ret = "Name: " + this.name;
+            ret += ", type: " + (["Point", "Directional", "Spot", "Hemispheric"])[this.getTypeID()];
+            if (this.animations) {
+                for (var i = 0; i < this.animations.length; i++) {
+                    ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
+                }
+            }
+            if (fullDetails) {
+            }
+            return ret;
+        };
+        Light.prototype.getShadowGenerator = function () {
+            return this._shadowGenerator;
+        };
+        Light.prototype.getAbsolutePosition = function () {
+            return BABYLON.Vector3.Zero();
+        };
+        Light.prototype.transferToEffect = function (effect, uniformName0, uniformName1) {
+        };
+        Light.prototype._getWorldMatrix = function () {
+            return BABYLON.Matrix.Identity();
+        };
+        Light.prototype.canAffectMesh = function (mesh) {
+            if (!mesh) {
+                return true;
+            }
+            if (this.includedOnlyMeshes.length > 0 && this.includedOnlyMeshes.indexOf(mesh) === -1) {
+                return false;
+            }
+            if (this.excludedMeshes.length > 0 && this.excludedMeshes.indexOf(mesh) !== -1) {
+                return false;
+            }
+            if (this.includeOnlyWithLayerMask !== 0 && (this.includeOnlyWithLayerMask & mesh.layerMask) === 0) {
+                return false;
+            }
+            if (this.excludeWithLayerMask !== 0 && this.excludeWithLayerMask & mesh.layerMask) {
+                return false;
+            }
+            return true;
+        };
+        Light.prototype.getWorldMatrix = function () {
+            this._currentRenderId = this.getScene().getRenderId();
+            var worldMatrix = this._getWorldMatrix();
+            if (this.parent && this.parent.getWorldMatrix) {
+                if (!this._parentedWorldMatrix) {
+                    this._parentedWorldMatrix = BABYLON.Matrix.Identity();
+                }
+                worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._parentedWorldMatrix);
+                this._markSyncedWithParent();
+                return this._parentedWorldMatrix;
+            }
+            return worldMatrix;
+        };
+        Light.prototype.dispose = function () {
+            if (this._shadowGenerator) {
+                this._shadowGenerator.dispose();
+                this._shadowGenerator = null;
+            }
+            // Animations
+            this.getScene().stopAnimation(this);
+            // Remove from scene
+            this.getScene().removeLight(this);
+        };
+        Light.prototype.getTypeID = function () {
+            return 0;
+        };
+        Light.prototype.clone = function (name) {
+            return BABYLON.SerializationHelper.Clone(Light.GetConstructorFromName(this.getTypeID(), name, this.getScene()), this);
+        };
+        Light.prototype.serialize = function () {
+            var serializationObject = BABYLON.SerializationHelper.Serialize(this);
+            // Type
+            serializationObject.type = this.getTypeID();
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
+            }
+            // Animations  
+            BABYLON.Animation.AppendSerializedAnimations(this, serializationObject);
+            serializationObject.ranges = this.serializeAnimationRanges();
+            return serializationObject;
+        };
+        Light.GetConstructorFromName = function (type, name, scene) {
+            switch (type) {
+                case 0:
+                    return function () { return new BABYLON.PointLight(name, BABYLON.Vector3.Zero(), scene); };
+                case 1:
+                    return function () { return new BABYLON.DirectionalLight(name, BABYLON.Vector3.Zero(), scene); };
+                case 2:
+                    return function () { return new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Zero(), 0, 0, scene); };
+                case 3:
+                    return function () { return new BABYLON.HemisphericLight(name, BABYLON.Vector3.Zero(), scene); };
+            }
+        };
+        Light.Parse = function (parsedLight, scene) {
+            var light = BABYLON.SerializationHelper.Parse(Light.GetConstructorFromName(parsedLight.type, parsedLight.name, scene), parsedLight, scene);
+            // Inclusion / exclusions
+            if (parsedLight.excludedMeshesIds) {
+                light._excludedMeshesIds = parsedLight.excludedMeshesIds;
+            }
+            if (parsedLight.includedOnlyMeshesIds) {
+                light._includedOnlyMeshesIds = parsedLight.includedOnlyMeshesIds;
+            }
+            // Parent
+            if (parsedLight.parentId) {
+                light._waitingParentId = parsedLight.parentId;
+            }
+            // Animations
+            if (parsedLight.animations) {
+                for (var animationIndex = 0; animationIndex < parsedLight.animations.length; animationIndex++) {
+                    var parsedAnimation = parsedLight.animations[animationIndex];
+                    light.animations.push(BABYLON.Animation.Parse(parsedAnimation));
+                }
+                BABYLON.Node.ParseAnimationRanges(light, parsedLight, scene);
+            }
+            if (parsedLight.autoAnimate) {
+                scene.beginAnimation(light, parsedLight.autoAnimateFrom, parsedLight.autoAnimateTo, parsedLight.autoAnimateLoop, parsedLight.autoAnimateSpeed || 1.0);
+            }
+            return light;
+        };
+        __decorate([
+            BABYLON.serializeAsColor3()
+        ], Light.prototype, "diffuse", void 0);
+        __decorate([
+            BABYLON.serializeAsColor3()
+        ], Light.prototype, "specular", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Light.prototype, "intensity", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Light.prototype, "range", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Light.prototype, "includeOnlyWithLayerMask", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Light.prototype, "radius", void 0);
+        return Light;
+    })(BABYLON.Node);
+    BABYLON.Light = Light;
+})(BABYLON || (BABYLON = {}));

+ 16 - 0
src/Lights/babylon.light.ts

@@ -55,6 +55,22 @@
             scene.addLight(this);
         }
 
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = "Name: " + this.name;
+            ret += ", type: " + (["Point", "Directional", "Spot", "Hemispheric"])[this.getTypeID()];
+            if (this.animations){
+                for (var i = 0; i < this.animations.length; i++){
+                   ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
+                }
+            }
+            if (fullDetails){
+            }
+            return ret;
+        } 
+        
         public getShadowGenerator(): ShadowGenerator {
             return this._shadowGenerator;
         }

+ 490 - 412
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -1,412 +1,490 @@
-var BABYLON;
-(function (BABYLON) {
-    var Internals;
-    (function (Internals) {
-        var parseMaterialById = function (id, parsedData, scene, rootUrl) {
-            for (var index = 0, cache = parsedData.materials.length; index < cache; index++) {
-                var parsedMaterial = parsedData.materials[index];
-                if (parsedMaterial.id === id) {
-                    return BABYLON.Material.Parse(parsedMaterial, scene, rootUrl);
-                }
-            }
-            return null;
-        };
-        var isDescendantOf = function (mesh, names, hierarchyIds) {
-            names = (names instanceof Array) ? names : [names];
-            for (var i in names) {
-                if (mesh.name === names[i]) {
-                    hierarchyIds.push(mesh.id);
-                    return true;
-                }
-            }
-            if (mesh.parentId && hierarchyIds.indexOf(mesh.parentId) !== -1) {
-                hierarchyIds.push(mesh.id);
-                return true;
-            }
-            return false;
-        };
-        BABYLON.SceneLoader.RegisterPlugin({
-            extensions: ".babylon",
-            importMesh: function (meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons) {
-                var parsedData = JSON.parse(data);
-                var loadedSkeletonsIds = [];
-                var loadedMaterialsIds = [];
-                var hierarchyIds = [];
-                var index;
-                var cache;
-                for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                    var parsedMesh = parsedData.meshes[index];
-                    if (!meshesNames || isDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
-                        if (meshesNames instanceof Array) {
-                            // Remove found mesh name from list.
-                            delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
-                        }
-                        //Geometry?
-                        if (parsedMesh.geometryId) {
-                            //does the file contain geometries?
-                            if (parsedData.geometries) {
-                                //find the correct geometry and add it to the scene
-                                var found = false;
-                                ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"].forEach(function (geometryType) {
-                                    if (found || !parsedData.geometries[geometryType] || !(parsedData.geometries[geometryType] instanceof Array)) {
-                                        return;
-                                    }
-                                    else {
-                                        parsedData.geometries[geometryType].forEach(function (parsedGeometryData) {
-                                            if (parsedGeometryData.id === parsedMesh.geometryId) {
-                                                switch (geometryType) {
-                                                    case "boxes":
-                                                        BABYLON.Geometry.Primitives.Box.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "spheres":
-                                                        BABYLON.Geometry.Primitives.Sphere.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "cylinders":
-                                                        BABYLON.Geometry.Primitives.Cylinder.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "toruses":
-                                                        BABYLON.Geometry.Primitives.Torus.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "grounds":
-                                                        BABYLON.Geometry.Primitives.Ground.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "planes":
-                                                        BABYLON.Geometry.Primitives.Plane.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "torusKnots":
-                                                        BABYLON.Geometry.Primitives.TorusKnot.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "vertexData":
-                                                        BABYLON.Geometry.Parse(parsedGeometryData, scene, rootUrl);
-                                                        break;
-                                                }
-                                                found = true;
-                                            }
-                                        });
-                                    }
-                                });
-                                if (!found) {
-                                    BABYLON.Tools.Warn("Geometry not found for mesh " + parsedMesh.id);
-                                }
-                            }
-                        }
-                        // Material ?
-                        if (parsedMesh.materialId) {
-                            var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
-                            if (!materialFound && parsedData.multiMaterials) {
-                                for (var multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
-                                    var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
-                                    if (parsedMultiMaterial.id === parsedMesh.materialId) {
-                                        for (var matIndex = 0, matCache = parsedMultiMaterial.materials.length; matIndex < matCache; matIndex++) {
-                                            var subMatId = parsedMultiMaterial.materials[matIndex];
-                                            loadedMaterialsIds.push(subMatId);
-                                            parseMaterialById(subMatId, parsedData, scene, rootUrl);
-                                        }
-                                        loadedMaterialsIds.push(parsedMultiMaterial.id);
-                                        BABYLON.Material.ParseMultiMaterial(parsedMultiMaterial, scene);
-                                        materialFound = true;
-                                        break;
-                                    }
-                                }
-                            }
-                            if (!materialFound) {
-                                loadedMaterialsIds.push(parsedMesh.materialId);
-                                if (!parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl)) {
-                                    BABYLON.Tools.Warn("Material not found for mesh " + parsedMesh.id);
-                                }
-                            }
-                        }
-                        // Skeleton ?
-                        if (parsedMesh.skeletonId > -1 && scene.skeletons) {
-                            var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
-                            if (!skeletonAlreadyLoaded) {
-                                for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
-                                    var parsedSkeleton = parsedData.skeletons[skeletonIndex];
-                                    if (parsedSkeleton.id === parsedMesh.skeletonId) {
-                                        skeletons.push(BABYLON.Skeleton.Parse(parsedSkeleton, scene));
-                                        loadedSkeletonsIds.push(parsedSkeleton.id);
-                                    }
-                                }
-                            }
-                        }
-                        var mesh = BABYLON.Mesh.Parse(parsedMesh, scene, rootUrl);
-                        meshes.push(mesh);
-                    }
-                }
-                // Connecting parents
-                var currentMesh;
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    currentMesh = scene.meshes[index];
-                    if (currentMesh._waitingParentId) {
-                        currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
-                        currentMesh._waitingParentId = undefined;
-                    }
-                }
-                // freeze and compute world matrix application
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    currentMesh = scene.meshes[index];
-                    if (currentMesh._waitingFreezeWorldMatrix) {
-                        currentMesh.freezeWorldMatrix();
-                        currentMesh._waitingFreezeWorldMatrix = undefined;
-                    }
-                    else {
-                        currentMesh.computeWorldMatrix(true);
-                    }
-                }
-                // Particles
-                if (parsedData.particleSystems) {
-                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
-                        var parsedParticleSystem = parsedData.particleSystems[index];
-                        if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
-                            particleSystems.push(BABYLON.ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl));
-                        }
-                    }
-                }
-                return true;
-            },
-            load: function (scene, data, rootUrl) {
-                var parsedData = JSON.parse(data);
-                // Scene
-                scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental;
-                scene.autoClear = parsedData.autoClear;
-                scene.clearColor = BABYLON.Color3.FromArray(parsedData.clearColor);
-                scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
-                if (parsedData.gravity) {
-                    scene.gravity = BABYLON.Vector3.FromArray(parsedData.gravity);
-                }
-                // Fog
-                if (parsedData.fogMode && parsedData.fogMode !== 0) {
-                    scene.fogMode = parsedData.fogMode;
-                    scene.fogColor = BABYLON.Color3.FromArray(parsedData.fogColor);
-                    scene.fogStart = parsedData.fogStart;
-                    scene.fogEnd = parsedData.fogEnd;
-                    scene.fogDensity = parsedData.fogDensity;
-                }
-                //Physics
-                if (parsedData.physicsEnabled) {
-                    var physicsPlugin;
-                    if (parsedData.physicsEngine === "cannon") {
-                        physicsPlugin = new BABYLON.CannonJSPlugin();
-                    }
-                    else if (parsedData.physicsEngine === "oimo") {
-                        physicsPlugin = new BABYLON.OimoJSPlugin();
-                    }
-                    //else - default engine, which is currently oimo
-                    var physicsGravity = parsedData.physicsGravity ? BABYLON.Vector3.FromArray(parsedData.physicsGravity) : null;
-                    scene.enablePhysics(physicsGravity, physicsPlugin);
-                }
-                //collisions, if defined. otherwise, default is true
-                if (parsedData.collisionsEnabled != undefined) {
-                    scene.collisionsEnabled = parsedData.collisionsEnabled;
-                }
-                scene.workerCollisions = !!parsedData.workerCollisions;
-                var index;
-                var cache;
-                // Lights
-                for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
-                    var parsedLight = parsedData.lights[index];
-                    BABYLON.Light.Parse(parsedLight, scene);
-                }
-                // Animations
-                if (parsedData.animations) {
-                    for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
-                        var parsedAnimation = parsedData.animations[index];
-                        scene.animations.push(BABYLON.Animation.Parse(parsedAnimation));
-                    }
-                }
-                if (parsedData.autoAnimate) {
-                    scene.beginAnimation(scene, parsedData.autoAnimateFrom, parsedData.autoAnimateTo, parsedData.autoAnimateLoop, parsedData.autoAnimateSpeed || 1.0);
-                }
-                // Materials
-                if (parsedData.materials) {
-                    for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
-                        var parsedMaterial = parsedData.materials[index];
-                        BABYLON.Material.Parse(parsedMaterial, scene, rootUrl);
-                    }
-                }
-                if (parsedData.multiMaterials) {
-                    for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
-                        var parsedMultiMaterial = parsedData.multiMaterials[index];
-                        BABYLON.Material.ParseMultiMaterial(parsedMultiMaterial, scene);
-                    }
-                }
-                // Skeletons
-                if (parsedData.skeletons) {
-                    for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
-                        var parsedSkeleton = parsedData.skeletons[index];
-                        BABYLON.Skeleton.Parse(parsedSkeleton, scene);
-                    }
-                }
-                // Geometries
-                var geometries = parsedData.geometries;
-                if (geometries) {
-                    // Boxes
-                    var boxes = geometries.boxes;
-                    if (boxes) {
-                        for (index = 0, cache = boxes.length; index < cache; index++) {
-                            var parsedBox = boxes[index];
-                            BABYLON.Geometry.Primitives.Box.Parse(parsedBox, scene);
-                        }
-                    }
-                    // Spheres
-                    var spheres = geometries.spheres;
-                    if (spheres) {
-                        for (index = 0, cache = spheres.length; index < cache; index++) {
-                            var parsedSphere = spheres[index];
-                            BABYLON.Geometry.Primitives.Sphere.Parse(parsedSphere, scene);
-                        }
-                    }
-                    // Cylinders
-                    var cylinders = geometries.cylinders;
-                    if (cylinders) {
-                        for (index = 0, cache = cylinders.length; index < cache; index++) {
-                            var parsedCylinder = cylinders[index];
-                            BABYLON.Geometry.Primitives.Cylinder.Parse(parsedCylinder, scene);
-                        }
-                    }
-                    // Toruses
-                    var toruses = geometries.toruses;
-                    if (toruses) {
-                        for (index = 0, cache = toruses.length; index < cache; index++) {
-                            var parsedTorus = toruses[index];
-                            BABYLON.Geometry.Primitives.Torus.Parse(parsedTorus, scene);
-                        }
-                    }
-                    // Grounds
-                    var grounds = geometries.grounds;
-                    if (grounds) {
-                        for (index = 0, cache = grounds.length; index < cache; index++) {
-                            var parsedGround = grounds[index];
-                            BABYLON.Geometry.Primitives.Ground.Parse(parsedGround, scene);
-                        }
-                    }
-                    // Planes
-                    var planes = geometries.planes;
-                    if (planes) {
-                        for (index = 0, cache = planes.length; index < cache; index++) {
-                            var parsedPlane = planes[index];
-                            BABYLON.Geometry.Primitives.Plane.Parse(parsedPlane, scene);
-                        }
-                    }
-                    // TorusKnots
-                    var torusKnots = geometries.torusKnots;
-                    if (torusKnots) {
-                        for (index = 0, cache = torusKnots.length; index < cache; index++) {
-                            var parsedTorusKnot = torusKnots[index];
-                            BABYLON.Geometry.Primitives.TorusKnot.Parse(parsedTorusKnot, scene);
-                        }
-                    }
-                    // VertexData
-                    var vertexData = geometries.vertexData;
-                    if (vertexData) {
-                        for (index = 0, cache = vertexData.length; index < cache; index++) {
-                            var parsedVertexData = vertexData[index];
-                            BABYLON.Geometry.Parse(parsedVertexData, scene, rootUrl);
-                        }
-                    }
-                }
-                // Meshes
-                for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                    var parsedMesh = parsedData.meshes[index];
-                    BABYLON.Mesh.Parse(parsedMesh, scene, rootUrl);
-                }
-                // Cameras
-                for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
-                    var parsedCamera = parsedData.cameras[index];
-                    BABYLON.Camera.Parse(parsedCamera, scene);
-                }
-                if (parsedData.activeCameraID) {
-                    scene.setActiveCameraByID(parsedData.activeCameraID);
-                }
-                // Browsing all the graph to connect the dots
-                for (index = 0, cache = scene.cameras.length; index < cache; index++) {
-                    var camera = scene.cameras[index];
-                    if (camera._waitingParentId) {
-                        camera.parent = scene.getLastEntryByID(camera._waitingParentId);
-                        camera._waitingParentId = undefined;
-                    }
-                }
-                for (index = 0, cache = scene.lights.length; index < cache; index++) {
-                    var light = scene.lights[index];
-                    if (light._waitingParentId) {
-                        light.parent = scene.getLastEntryByID(light._waitingParentId);
-                        light._waitingParentId = undefined;
-                    }
-                }
-                // Sounds
-                var loadedSounds = [];
-                var loadedSound;
-                if (BABYLON.AudioEngine && parsedData.sounds) {
-                    for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
-                        var parsedSound = parsedData.sounds[index];
-                        if (BABYLON.Engine.audioEngine.canUseWebAudio) {
-                            if (!parsedSound.url)
-                                parsedSound.url = parsedSound.name;
-                            if (!loadedSounds[parsedSound.url]) {
-                                loadedSound = BABYLON.Sound.Parse(parsedSound, scene, rootUrl);
-                                loadedSounds[parsedSound.url] = loadedSound;
-                            }
-                            else {
-                                BABYLON.Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
-                            }
-                        }
-                        else {
-                            var emptySound = new BABYLON.Sound(parsedSound.name, null, scene);
-                        }
-                    }
-                }
-                loadedSounds = [];
-                // Connect parents & children and parse actions
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    var mesh = scene.meshes[index];
-                    if (mesh._waitingParentId) {
-                        mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
-                        mesh._waitingParentId = undefined;
-                    }
-                    if (mesh._waitingActions) {
-                        BABYLON.ActionManager.Parse(mesh._waitingActions, mesh, scene);
-                        mesh._waitingActions = undefined;
-                    }
-                }
-                // freeze world matrix application
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    var currentMesh = scene.meshes[index];
-                    if (currentMesh._waitingFreezeWorldMatrix) {
-                        currentMesh.freezeWorldMatrix();
-                        currentMesh._waitingFreezeWorldMatrix = undefined;
-                    }
-                    else {
-                        currentMesh.computeWorldMatrix(true);
-                    }
-                }
-                // Particles Systems
-                if (parsedData.particleSystems) {
-                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
-                        var parsedParticleSystem = parsedData.particleSystems[index];
-                        BABYLON.ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
-                    }
-                }
-                // Lens flares
-                if (parsedData.lensFlareSystems) {
-                    for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
-                        var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
-                        BABYLON.LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
-                    }
-                }
-                // Shadows
-                if (parsedData.shadowGenerators) {
-                    for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
-                        var parsedShadowGenerator = parsedData.shadowGenerators[index];
-                        BABYLON.ShadowGenerator.Parse(parsedShadowGenerator, scene);
-                    }
-                }
-                // Actions (scene)
-                if (parsedData.actions) {
-                    BABYLON.ActionManager.Parse(parsedData.actions, null, scene);
-                }
-                // Finish
-                return true;
-            }
-        });
-    })(Internals = BABYLON.Internals || (BABYLON.Internals = {}));
-})(BABYLON || (BABYLON = {}));
+var BABYLON;
+(function (BABYLON) {
+    var Internals;
+    (function (Internals) {
+        var parseMaterialById = function (id, parsedData, scene, rootUrl) {
+            for (var index = 0, cache = parsedData.materials.length; index < cache; index++) {
+                var parsedMaterial = parsedData.materials[index];
+                if (parsedMaterial.id === id) {
+                    return BABYLON.Material.Parse(parsedMaterial, scene, rootUrl);
+                }
+            }
+            return null;
+        };
+        var isDescendantOf = function (mesh, names, hierarchyIds) {
+            names = (names instanceof Array) ? names : [names];
+            for (var i in names) {
+                if (mesh.name === names[i]) {
+                    hierarchyIds.push(mesh.id);
+                    return true;
+                }
+            }
+            if (mesh.parentId && hierarchyIds.indexOf(mesh.parentId) !== -1) {
+                hierarchyIds.push(mesh.id);
+                return true;
+            }
+            return false;
+        };
+        var logOperation = function (operation, producer) {
+            return operation + " of " + (producer ? producer.file + " from " + producer.name + " version: " + producer.version + ", exporter version: " + producer.exporter_version : "unknown");
+        };
+        BABYLON.SceneLoader.RegisterPlugin({
+            extensions: ".babylon",
+            importMesh: function (meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons) {
+                // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
+                // when SceneLoader.debugLogging = true (default), or exception encountered.
+                // Everything stored in var log instead of writing separate lines to support only writing in exception,
+                // and avoid problems with multiple concurrent .babylon loads.
+                var log = "importMesh has failed JSON parse";
+                try {
+                    var parsedData = JSON.parse(data);
+                    log = "";
+                    var fullDetails = BABYLON.SceneLoader.loggingLevel === BABYLON.SceneLoader.DETAILED_LOGGING;
+                    var loadedSkeletonsIds = [];
+                    var loadedMaterialsIds = [];
+                    var hierarchyIds = [];
+                    var index;
+                    var cache;
+                    for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                        var parsedMesh = parsedData.meshes[index];
+                        if (!meshesNames || isDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
+                            if (meshesNames instanceof Array) {
+                                // Remove found mesh name from list.
+                                delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
+                            }
+                            //Geometry?
+                            if (parsedMesh.geometryId) {
+                                //does the file contain geometries?
+                                if (parsedData.geometries) {
+                                    //find the correct geometry and add it to the scene
+                                    var found = false;
+                                    ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"].forEach(function (geometryType) {
+                                        if (found || !parsedData.geometries[geometryType] || !(parsedData.geometries[geometryType] instanceof Array)) {
+                                            return;
+                                        }
+                                        else {
+                                            parsedData.geometries[geometryType].forEach(function (parsedGeometryData) {
+                                                if (parsedGeometryData.id === parsedMesh.geometryId) {
+                                                    switch (geometryType) {
+                                                        case "boxes":
+                                                            BABYLON.Geometry.Primitives.Box.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "spheres":
+                                                            BABYLON.Geometry.Primitives.Sphere.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "cylinders":
+                                                            BABYLON.Geometry.Primitives.Cylinder.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "toruses":
+                                                            BABYLON.Geometry.Primitives.Torus.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "grounds":
+                                                            BABYLON.Geometry.Primitives.Ground.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "planes":
+                                                            BABYLON.Geometry.Primitives.Plane.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "torusKnots":
+                                                            BABYLON.Geometry.Primitives.TorusKnot.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "vertexData":
+                                                            BABYLON.Geometry.Parse(parsedGeometryData, scene, rootUrl);
+                                                            break;
+                                                    }
+                                                    found = true;
+                                                }
+                                            });
+                                        }
+                                    });
+                                    if (!found) {
+                                        BABYLON.Tools.Warn("Geometry not found for mesh " + parsedMesh.id);
+                                    }
+                                }
+                            }
+                            // Material ?
+                            if (parsedMesh.materialId) {
+                                var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
+                                if (!materialFound && parsedData.multiMaterials) {
+                                    for (var multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
+                                        var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
+                                        if (parsedMultiMaterial.id === parsedMesh.materialId) {
+                                            for (var matIndex = 0, matCache = parsedMultiMaterial.materials.length; matIndex < matCache; matIndex++) {
+                                                var subMatId = parsedMultiMaterial.materials[matIndex];
+                                                loadedMaterialsIds.push(subMatId);
+                                                var mat = parseMaterialById(subMatId, parsedData, scene, rootUrl);
+                                                log += "\n\tMaterial " + mat.toString(fullDetails);
+                                            }
+                                            loadedMaterialsIds.push(parsedMultiMaterial.id);
+                                            var mmat = BABYLON.Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                                            materialFound = true;
+                                            log += "\n\tMulti-Material " + mmat.toString(fullDetails);
+                                            break;
+                                        }
+                                    }
+                                }
+                                if (!materialFound) {
+                                    loadedMaterialsIds.push(parsedMesh.materialId);
+                                    var mat = parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl);
+                                    if (!mat) {
+                                        BABYLON.Tools.Warn("Material not found for mesh " + parsedMesh.id);
+                                    }
+                                    else {
+                                        log += "\n\tMaterial " + mat.toString(fullDetails);
+                                    }
+                                }
+                            }
+                            // Skeleton ?
+                            if (parsedMesh.skeletonId > -1 && scene.skeletons) {
+                                var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
+                                if (!skeletonAlreadyLoaded) {
+                                    for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
+                                        var parsedSkeleton = parsedData.skeletons[skeletonIndex];
+                                        if (parsedSkeleton.id === parsedMesh.skeletonId) {
+                                            var skeleton = BABYLON.Skeleton.Parse(parsedSkeleton, scene);
+                                            skeletons.push(skeleton);
+                                            loadedSkeletonsIds.push(parsedSkeleton.id);
+                                            log += "\n\tSkeleton " + skeleton.toString(fullDetails);
+                                        }
+                                    }
+                                }
+                            }
+                            var mesh = BABYLON.Mesh.Parse(parsedMesh, scene, rootUrl);
+                            meshes.push(mesh);
+                            log += "\n\tMesh " + mesh.toString(fullDetails);
+                        }
+                    }
+                    // Connecting parents
+                    var currentMesh;
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingParentId) {
+                            currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
+                            currentMesh._waitingParentId = undefined;
+                        }
+                    }
+                    // freeze and compute world matrix application
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingFreezeWorldMatrix) {
+                            currentMesh.freezeWorldMatrix();
+                            currentMesh._waitingFreezeWorldMatrix = undefined;
+                        }
+                        else {
+                            currentMesh.computeWorldMatrix(true);
+                        }
+                    }
+                    // Particles
+                    if (parsedData.particleSystems) {
+                        for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
+                            var parsedParticleSystem = parsedData.particleSystems[index];
+                            if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
+                                particleSystems.push(BABYLON.ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl));
+                            }
+                        }
+                    }
+                    return true;
+                }
+                catch (err) {
+                    BABYLON.Tools.Log(logOperation("importMesh", parsedData.producer) + log);
+                    log = null;
+                    throw err;
+                }
+                finally {
+                    if (log !== null && BABYLON.SceneLoader.loggingLevel !== BABYLON.SceneLoader.NO_LOGGING) {
+                        BABYLON.Tools.Log(logOperation("importMesh", parsedData.producer) + (BABYLON.SceneLoader.loggingLevel !== BABYLON.SceneLoader.MINIMAL_LOGGING ? log : ""));
+                    }
+                }
+            },
+            load: function (scene, data, rootUrl) {
+                // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
+                // when SceneLoader.debugLogging = true (default), or exception encountered.
+                // Everything stored in var log instead of writing separate lines to support only writing in exception,
+                // and avoid problems with multiple concurrent .babylon loads.
+                var log = "importScene has failed JSON parse";
+                try {
+                    var parsedData = JSON.parse(data);
+                    log = "";
+                    var fullDetails = BABYLON.SceneLoader.loggingLevel === BABYLON.SceneLoader.DETAILED_LOGGING;
+                    // Scene
+                    scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental;
+                    scene.autoClear = parsedData.autoClear;
+                    scene.clearColor = BABYLON.Color3.FromArray(parsedData.clearColor);
+                    scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
+                    if (parsedData.gravity) {
+                        scene.gravity = BABYLON.Vector3.FromArray(parsedData.gravity);
+                    }
+                    // Fog
+                    if (parsedData.fogMode && parsedData.fogMode !== 0) {
+                        scene.fogMode = parsedData.fogMode;
+                        scene.fogColor = BABYLON.Color3.FromArray(parsedData.fogColor);
+                        scene.fogStart = parsedData.fogStart;
+                        scene.fogEnd = parsedData.fogEnd;
+                        scene.fogDensity = parsedData.fogDensity;
+                        log += "\tFog mode for scene:  ";
+                        switch (scene.fogMode) {
+                            // getters not compiling, so using hardcoded
+                            case 1:
+                                log += "exp\n";
+                                break;
+                            case 2:
+                                log += "exp2\n";
+                                break;
+                            case 3:
+                                log += "linear\n";
+                                break;
+                        }
+                    }
+                    //Physics
+                    if (parsedData.physicsEnabled) {
+                        var physicsPlugin;
+                        if (parsedData.physicsEngine === "cannon") {
+                            physicsPlugin = new BABYLON.CannonJSPlugin();
+                        }
+                        else if (parsedData.physicsEngine === "oimo") {
+                            physicsPlugin = new BABYLON.OimoJSPlugin();
+                        }
+                        log = "\tPhysics engine " + (parsedData.physicsEngine ? parsedData.physicsEngine : "oimo") + " enabled\n";
+                        //else - default engine, which is currently oimo
+                        var physicsGravity = parsedData.physicsGravity ? BABYLON.Vector3.FromArray(parsedData.physicsGravity) : null;
+                        scene.enablePhysics(physicsGravity, physicsPlugin);
+                    }
+                    //collisions, if defined. otherwise, default is true
+                    if (parsedData.collisionsEnabled != undefined) {
+                        scene.collisionsEnabled = parsedData.collisionsEnabled;
+                    }
+                    scene.workerCollisions = !!parsedData.workerCollisions;
+                    var index;
+                    var cache;
+                    // Lights
+                    for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
+                        var parsedLight = parsedData.lights[index];
+                        var light = BABYLON.Light.Parse(parsedLight, scene);
+                        log += (index === 0 ? "\n\tLights:" : "");
+                        log += "\n\t\t" + light.toString(fullDetails);
+                    }
+                    // Animations
+                    if (parsedData.animations) {
+                        for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
+                            var parsedAnimation = parsedData.animations[index];
+                            var animation = BABYLON.Animation.Parse(parsedAnimation);
+                            scene.animations.push(animation);
+                            log += (index === 0 ? "\n\tAnimations:" : "");
+                            log += "\n\t\t" + animation.toString(fullDetails);
+                        }
+                    }
+                    // Materials
+                    if (parsedData.materials) {
+                        for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
+                            var parsedMaterial = parsedData.materials[index];
+                            var mat = BABYLON.Material.Parse(parsedMaterial, scene, rootUrl);
+                            log += (index === 0 ? "\n\tMaterials:" : "");
+                            log += "\n\t\t" + mat.toString(fullDetails);
+                        }
+                    }
+                    if (parsedData.multiMaterials) {
+                        for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
+                            var parsedMultiMaterial = parsedData.multiMaterials[index];
+                            var mmat = BABYLON.Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                            log += (index === 0 ? "\n\tMultiMaterials:" : "");
+                            log += "\n\t\t" + mmat.toString(fullDetails);
+                        }
+                    }
+                    // Skeletons
+                    if (parsedData.skeletons) {
+                        for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
+                            var parsedSkeleton = parsedData.skeletons[index];
+                            var skeleton = BABYLON.Skeleton.Parse(parsedSkeleton, scene);
+                            log += (index === 0 ? "\n\tSkeletons:" : "");
+                            log += "\n\t\t" + skeleton.toString(fullDetails);
+                        }
+                    }
+                    // Geometries
+                    var geometries = parsedData.geometries;
+                    if (geometries) {
+                        // Boxes
+                        var boxes = geometries.boxes;
+                        if (boxes) {
+                            for (index = 0, cache = boxes.length; index < cache; index++) {
+                                var parsedBox = boxes[index];
+                                BABYLON.Geometry.Primitives.Box.Parse(parsedBox, scene);
+                            }
+                        }
+                        // Spheres
+                        var spheres = geometries.spheres;
+                        if (spheres) {
+                            for (index = 0, cache = spheres.length; index < cache; index++) {
+                                var parsedSphere = spheres[index];
+                                BABYLON.Geometry.Primitives.Sphere.Parse(parsedSphere, scene);
+                            }
+                        }
+                        // Cylinders
+                        var cylinders = geometries.cylinders;
+                        if (cylinders) {
+                            for (index = 0, cache = cylinders.length; index < cache; index++) {
+                                var parsedCylinder = cylinders[index];
+                                BABYLON.Geometry.Primitives.Cylinder.Parse(parsedCylinder, scene);
+                            }
+                        }
+                        // Toruses
+                        var toruses = geometries.toruses;
+                        if (toruses) {
+                            for (index = 0, cache = toruses.length; index < cache; index++) {
+                                var parsedTorus = toruses[index];
+                                BABYLON.Geometry.Primitives.Torus.Parse(parsedTorus, scene);
+                            }
+                        }
+                        // Grounds
+                        var grounds = geometries.grounds;
+                        if (grounds) {
+                            for (index = 0, cache = grounds.length; index < cache; index++) {
+                                var parsedGround = grounds[index];
+                                BABYLON.Geometry.Primitives.Ground.Parse(parsedGround, scene);
+                            }
+                        }
+                        // Planes
+                        var planes = geometries.planes;
+                        if (planes) {
+                            for (index = 0, cache = planes.length; index < cache; index++) {
+                                var parsedPlane = planes[index];
+                                BABYLON.Geometry.Primitives.Plane.Parse(parsedPlane, scene);
+                            }
+                        }
+                        // TorusKnots
+                        var torusKnots = geometries.torusKnots;
+                        if (torusKnots) {
+                            for (index = 0, cache = torusKnots.length; index < cache; index++) {
+                                var parsedTorusKnot = torusKnots[index];
+                                BABYLON.Geometry.Primitives.TorusKnot.Parse(parsedTorusKnot, scene);
+                            }
+                        }
+                        // VertexData
+                        var vertexData = geometries.vertexData;
+                        if (vertexData) {
+                            for (index = 0, cache = vertexData.length; index < cache; index++) {
+                                var parsedVertexData = vertexData[index];
+                                BABYLON.Geometry.Parse(parsedVertexData, scene, rootUrl);
+                            }
+                        }
+                    }
+                    // Meshes
+                    for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                        var parsedMesh = parsedData.meshes[index];
+                        var mesh = BABYLON.Mesh.Parse(parsedMesh, scene, rootUrl);
+                        log += (index === 0 ? "\n\tMeshes:" : "");
+                        log += "\n\t\t" + mesh.toString(fullDetails);
+                    }
+                    // Cameras
+                    for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
+                        var parsedCamera = parsedData.cameras[index];
+                        var camera = BABYLON.Camera.Parse(parsedCamera, scene);
+                        log += (index === 0 ? "\n\tCameras:" : "");
+                        log += "\n\t\t" + camera.toString(fullDetails);
+                    }
+                    if (parsedData.activeCameraID) {
+                        scene.setActiveCameraByID(parsedData.activeCameraID);
+                    }
+                    // Browsing all the graph to connect the dots
+                    for (index = 0, cache = scene.cameras.length; index < cache; index++) {
+                        var camera = scene.cameras[index];
+                        if (camera._waitingParentId) {
+                            camera.parent = scene.getLastEntryByID(camera._waitingParentId);
+                            camera._waitingParentId = undefined;
+                        }
+                    }
+                    for (index = 0, cache = scene.lights.length; index < cache; index++) {
+                        var light = scene.lights[index];
+                        if (light._waitingParentId) {
+                            light.parent = scene.getLastEntryByID(light._waitingParentId);
+                            light._waitingParentId = undefined;
+                        }
+                    }
+                    // Sounds
+                    var loadedSounds = [];
+                    var loadedSound;
+                    if (BABYLON.AudioEngine && parsedData.sounds) {
+                        for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
+                            var parsedSound = parsedData.sounds[index];
+                            if (BABYLON.Engine.audioEngine.canUseWebAudio) {
+                                if (!parsedSound.url)
+                                    parsedSound.url = parsedSound.name;
+                                if (!loadedSounds[parsedSound.url]) {
+                                    loadedSound = BABYLON.Sound.Parse(parsedSound, scene, rootUrl);
+                                    loadedSounds[parsedSound.url] = loadedSound;
+                                }
+                                else {
+                                    BABYLON.Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
+                                }
+                            }
+                            else {
+                                var emptySound = new BABYLON.Sound(parsedSound.name, null, scene);
+                            }
+                        }
+                        log += (index === 0 ? "\n\tSounds:" : "");
+                        log += "\n\t\t" + mat.toString(fullDetails);
+                    }
+                    loadedSounds = [];
+                    // Connect parents & children and parse actions
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        var mesh = scene.meshes[index];
+                        if (mesh._waitingParentId) {
+                            mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
+                            mesh._waitingParentId = undefined;
+                        }
+                        if (mesh._waitingActions) {
+                            BABYLON.ActionManager.Parse(mesh._waitingActions, mesh, scene);
+                            mesh._waitingActions = undefined;
+                        }
+                    }
+                    // freeze world matrix application
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        var currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingFreezeWorldMatrix) {
+                            currentMesh.freezeWorldMatrix();
+                            currentMesh._waitingFreezeWorldMatrix = undefined;
+                        }
+                        else {
+                            currentMesh.computeWorldMatrix(true);
+                        }
+                    }
+                    // Particles Systems
+                    if (parsedData.particleSystems) {
+                        for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
+                            var parsedParticleSystem = parsedData.particleSystems[index];
+                            BABYLON.ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
+                        }
+                    }
+                    // Lens flares
+                    if (parsedData.lensFlareSystems) {
+                        for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
+                            var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
+                            BABYLON.LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
+                        }
+                    }
+                    // Shadows
+                    if (parsedData.shadowGenerators) {
+                        for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
+                            var parsedShadowGenerator = parsedData.shadowGenerators[index];
+                            BABYLON.ShadowGenerator.Parse(parsedShadowGenerator, scene);
+                        }
+                    }
+                    // Actions (scene)
+                    if (parsedData.actions) {
+                        BABYLON.ActionManager.Parse(parsedData.actions, null, scene);
+                    }
+                    // Finish
+                    return true;
+                }
+                catch (err) {
+                    BABYLON.Tools.Log(logOperation("importScene", parsedData.producer) + log);
+                    log = null;
+                    throw err;
+                }
+                finally {
+                    if (log !== null && BABYLON.SceneLoader.loggingLevel !== BABYLON.SceneLoader.NO_LOGGING) {
+                        BABYLON.Tools.Log(logOperation("importScene", parsedData.producer) + (BABYLON.SceneLoader.loggingLevel !== BABYLON.SceneLoader.MINIMAL_LOGGING ? log : ""));
+                    }
+                }
+            }
+        });
+    })(Internals = BABYLON.Internals || (BABYLON.Internals = {}));
+})(BABYLON || (BABYLON = {}));

+ 426 - 356
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -25,424 +25,494 @@
         return false;
     };
 
+    var logOperation = (operation, producer) => {
+        return operation + " of " + (producer ? producer.file + " from " + producer.name + " version: " + producer.version + ", exporter version: " + producer.exporter_version : "unknown");
+    }
+
     SceneLoader.RegisterPlugin({
         extensions: ".babylon",
         importMesh: (meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]): boolean => {
-            var parsedData = JSON.parse(data);
-
-            var loadedSkeletonsIds = [];
-            var loadedMaterialsIds = [];
-            var hierarchyIds = [];
-            var index: number;
-            var cache: number;
-            for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                var parsedMesh = parsedData.meshes[index];
-
-                if (!meshesNames || isDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
-                    if (meshesNames instanceof Array) {
-                        // Remove found mesh name from list.
-                        delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
-                    }
-
-                    //Geometry?
-                    if (parsedMesh.geometryId) {
-                        //does the file contain geometries?
-                        if (parsedData.geometries) {
-                            //find the correct geometry and add it to the scene
-                            var found: boolean = false;
-                            ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"].forEach((geometryType: string) => {
-                                if (found || !parsedData.geometries[geometryType] || !(parsedData.geometries[geometryType] instanceof Array)) {
-                                    return;
-                                } else {
-                                    parsedData.geometries[geometryType].forEach((parsedGeometryData) => {
-                                        if (parsedGeometryData.id === parsedMesh.geometryId) {
-                                            switch (geometryType) {
-                                                case "boxes":
-                                                    Geometry.Primitives.Box.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "spheres":
-                                                    Geometry.Primitives.Sphere.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "cylinders":
-                                                    Geometry.Primitives.Cylinder.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "toruses":
-                                                    Geometry.Primitives.Torus.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "grounds":
-                                                    Geometry.Primitives.Ground.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "planes":
-                                                    Geometry.Primitives.Plane.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "torusKnots":
-                                                    Geometry.Primitives.TorusKnot.Parse(parsedGeometryData, scene);
-                                                    break;
-                                                case "vertexData":
-                                                    Geometry.Parse(parsedGeometryData, scene, rootUrl);
-                                                    break;
+            // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
+            // when SceneLoader.debugLogging = true (default), or exception encountered.
+            // Everything stored in var log instead of writing separate lines to support only writing in exception,
+            // and avoid problems with multiple concurrent .babylon loads.
+            var log = "importMesh has failed JSON parse";
+            try {
+                var parsedData = JSON.parse(data);
+                log = "";
+                var fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
+
+                var loadedSkeletonsIds = [];
+                var loadedMaterialsIds = [];
+                var hierarchyIds = [];
+                var index: number;
+                var cache: number;
+                for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                    var parsedMesh = parsedData.meshes[index];
+
+                    if (!meshesNames || isDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
+                        if (meshesNames instanceof Array) {
+                            // Remove found mesh name from list.
+                            delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
+                        }
+    
+                        //Geometry?
+                        if (parsedMesh.geometryId) {
+                            //does the file contain geometries?
+                            if (parsedData.geometries) {
+                                //find the correct geometry and add it to the scene
+                                var found: boolean = false;
+                                ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"].forEach((geometryType: string) => {
+                                    if (found || !parsedData.geometries[geometryType] || !(parsedData.geometries[geometryType] instanceof Array)) {
+                                        return;
+                                    } else {
+                                        parsedData.geometries[geometryType].forEach((parsedGeometryData) => {
+                                            if (parsedGeometryData.id === parsedMesh.geometryId) {
+                                                switch (geometryType) {
+                                                    case "boxes":
+                                                        Geometry.Primitives.Box.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "spheres":
+                                                        Geometry.Primitives.Sphere.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "cylinders":
+                                                        Geometry.Primitives.Cylinder.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "toruses":
+                                                        Geometry.Primitives.Torus.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "grounds":
+                                                        Geometry.Primitives.Ground.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "planes":
+                                                        Geometry.Primitives.Plane.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "torusKnots":
+                                                        Geometry.Primitives.TorusKnot.Parse(parsedGeometryData, scene);
+                                                        break;
+                                                    case "vertexData":
+                                                        Geometry.Parse(parsedGeometryData, scene, rootUrl);
+                                                        break;
+                                                }
+                                                found = true;
                                             }
-                                            found = true;
-                                        }
-                                    });
+                                        });
 
+                                    }
+                                });
+                                if (!found) {
+                                    Tools.Warn("Geometry not found for mesh " + parsedMesh.id);
                                 }
-                            });
-                            if (!found) {
-                                Tools.Warn("Geometry not found for mesh " + parsedMesh.id);
                             }
                         }
-                    }
-
-                    // Material ?
-                    if (parsedMesh.materialId) {
-                        var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
-                        if (!materialFound && parsedData.multiMaterials) {
-                            for (var multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
-                                var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
-                                if (parsedMultiMaterial.id === parsedMesh.materialId) {
-                                    for (var matIndex = 0, matCache = parsedMultiMaterial.materials.length; matIndex < matCache; matIndex++) {
-                                        var subMatId = parsedMultiMaterial.materials[matIndex];
-                                        loadedMaterialsIds.push(subMatId);
-                                        parseMaterialById(subMatId, parsedData, scene, rootUrl);
+    
+                        // Material ?
+                        if (parsedMesh.materialId) {
+                            var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
+                            if (!materialFound && parsedData.multiMaterials) {
+                                for (var multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
+                                    var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
+                                    if (parsedMultiMaterial.id === parsedMesh.materialId) {
+                                        for (var matIndex = 0, matCache = parsedMultiMaterial.materials.length; matIndex < matCache; matIndex++) {
+                                            var subMatId = parsedMultiMaterial.materials[matIndex];
+                                            loadedMaterialsIds.push(subMatId);
+                                            var mat = parseMaterialById(subMatId, parsedData, scene, rootUrl);
+                                            log += "\n\tMaterial " + mat.toString(fullDetails);
+                                        }
+                                        loadedMaterialsIds.push(parsedMultiMaterial.id);
+                                        var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                                        materialFound = true;
+                                        log += "\n\tMulti-Material " + mmat.toString(fullDetails);
+                                        break;
                                     }
-                                    loadedMaterialsIds.push(parsedMultiMaterial.id);
-                                    Material.ParseMultiMaterial(parsedMultiMaterial, scene);
-                                    materialFound = true;
-                                    break;
                                 }
                             }
-                        }
 
-                        if (!materialFound) {
-                            loadedMaterialsIds.push(parsedMesh.materialId);
-                            if (!parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl)) {
-                                Tools.Warn("Material not found for mesh " + parsedMesh.id);
+                            if (!materialFound) {
+                                loadedMaterialsIds.push(parsedMesh.materialId);
+                                var mat = parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl);
+                                if (!mat) {
+                                    Tools.Warn("Material not found for mesh " + parsedMesh.id);
+                                } else {
+                                    log += "\n\tMaterial " + mat.toString(fullDetails);
+                                }
                             }
                         }
-                    }
-
-                    // Skeleton ?
-                    if (parsedMesh.skeletonId > -1 && scene.skeletons) {
-                        var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
-                        if (!skeletonAlreadyLoaded) {
-                            for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
-                                var parsedSkeleton = parsedData.skeletons[skeletonIndex];
-                                if (parsedSkeleton.id === parsedMesh.skeletonId) {
-                                    skeletons.push(Skeleton.Parse(parsedSkeleton, scene));
-                                    loadedSkeletonsIds.push(parsedSkeleton.id);
+    
+                        // Skeleton ?
+                        if (parsedMesh.skeletonId > -1 && scene.skeletons) {
+                            var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
+                            if (!skeletonAlreadyLoaded) {
+                                for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
+                                    var parsedSkeleton = parsedData.skeletons[skeletonIndex];
+                                    if (parsedSkeleton.id === parsedMesh.skeletonId) {
+                                        var skeleton = Skeleton.Parse(parsedSkeleton, scene);
+                                        skeletons.push(skeleton);
+                                        loadedSkeletonsIds.push(parsedSkeleton.id);
+                                        log += "\n\tSkeleton " + skeleton.toString(fullDetails);
+                                    }
                                 }
                             }
                         }
-                    }
 
-                    var mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
-                    meshes.push(mesh);
+                        var mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
+                        meshes.push(mesh);
+                        log += "\n\tMesh " + mesh.toString(fullDetails);
+                    }
                 }
-            }
-
-            // Connecting parents
-            var currentMesh: AbstractMesh;
-            for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                currentMesh = scene.meshes[index];
-                if (currentMesh._waitingParentId) {
-                    currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
-                    currentMesh._waitingParentId = undefined;
+    
+                // Connecting parents
+                var currentMesh: AbstractMesh;
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                    currentMesh = scene.meshes[index];
+                    if (currentMesh._waitingParentId) {
+                        currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
+                        currentMesh._waitingParentId = undefined;
+                    }
                 }
-            }
-
-            // freeze and compute world matrix application
-            for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                currentMesh = scene.meshes[index];
-                if (currentMesh._waitingFreezeWorldMatrix) {
-                    currentMesh.freezeWorldMatrix();
-                    currentMesh._waitingFreezeWorldMatrix = undefined;
-                } else {
-                    currentMesh.computeWorldMatrix(true);
+    
+                // freeze and compute world matrix application
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                    currentMesh = scene.meshes[index];
+                    if (currentMesh._waitingFreezeWorldMatrix) {
+                        currentMesh.freezeWorldMatrix();
+                        currentMesh._waitingFreezeWorldMatrix = undefined;
+                    } else {
+                        currentMesh.computeWorldMatrix(true);
+                    }
                 }
-            }
-
-            // Particles
-            if (parsedData.particleSystems) {
-                for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
-                    var parsedParticleSystem = parsedData.particleSystems[index];
-                    if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
-                        particleSystems.push(ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl));
+    
+                // Particles
+                if (parsedData.particleSystems) {
+                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
+                        var parsedParticleSystem = parsedData.particleSystems[index];
+                        if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
+                            particleSystems.push(ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl));
+                        }
                     }
                 }
-            }
+                return true;
 
-            return true;
-        },
-        load: (scene: Scene, data: string, rootUrl: string): boolean => {
-            var parsedData = JSON.parse(data);
+            } catch (err) {
+                Tools.Log(logOperation("importMesh", parsedData.producer) + log);
+                log = null;
+                throw err;
 
-            // Scene
-            scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental;
-            scene.autoClear = parsedData.autoClear;
-            scene.clearColor = BABYLON.Color3.FromArray(parsedData.clearColor);
-            scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
-            if (parsedData.gravity) {
-                scene.gravity = BABYLON.Vector3.FromArray(parsedData.gravity);
-            }
-            
-            // Fog
-            if (parsedData.fogMode && parsedData.fogMode !== 0) {
-                scene.fogMode = parsedData.fogMode;
-                scene.fogColor = BABYLON.Color3.FromArray(parsedData.fogColor);
-                scene.fogStart = parsedData.fogStart;
-                scene.fogEnd = parsedData.fogEnd;
-                scene.fogDensity = parsedData.fogDensity;
-            }
-            
-            //Physics
-            if (parsedData.physicsEnabled) {
-                var physicsPlugin;
-                if (parsedData.physicsEngine === "cannon") {
-                    physicsPlugin = new BABYLON.CannonJSPlugin();
-                } else if (parsedData.physicsEngine === "oimo") {
-                    physicsPlugin = new BABYLON.OimoJSPlugin();
+            } finally {
+                if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
+                    Tools.Log(logOperation("importMesh", parsedData.producer) + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
                 }
-                //else - default engine, which is currently oimo
-                var physicsGravity = parsedData.physicsGravity ? BABYLON.Vector3.FromArray(parsedData.physicsGravity) : null;
-                scene.enablePhysics(physicsGravity, physicsPlugin);
             }
-            
-            //collisions, if defined. otherwise, default is true
-            if (parsedData.collisionsEnabled != undefined) {
-                scene.collisionsEnabled = parsedData.collisionsEnabled;
-            }
-            scene.workerCollisions = !!parsedData.workerCollisions;
-
-            var index: number;
-            var cache: number;
-            // Lights
-            for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
-                var parsedLight = parsedData.lights[index];
-                Light.Parse(parsedLight, scene);
-            }
-
-            // Animations
-            if (parsedData.animations) {
-                for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
-                    var parsedAnimation = parsedData.animations[index];
-                    scene.animations.push(Animation.Parse(parsedAnimation));
+        },
+        load: (scene: Scene, data: string, rootUrl: string): boolean => {
+            // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
+            // when SceneLoader.debugLogging = true (default), or exception encountered.
+            // Everything stored in var log instead of writing separate lines to support only writing in exception,
+            // and avoid problems with multiple concurrent .babylon loads.
+            var log = "importScene has failed JSON parse";
+            try {
+                var parsedData = JSON.parse(data);
+                log = "";
+                var fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
+                
+                // Scene
+                scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental;
+                scene.autoClear = parsedData.autoClear;
+                scene.clearColor = BABYLON.Color3.FromArray(parsedData.clearColor);
+                scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
+                if (parsedData.gravity) {
+                    scene.gravity = BABYLON.Vector3.FromArray(parsedData.gravity);
                 }
-            }
-
-            if (parsedData.autoAnimate) {
-                scene.beginAnimation(scene, parsedData.autoAnimateFrom, parsedData.autoAnimateTo, parsedData.autoAnimateLoop, parsedData.autoAnimateSpeed || 1.0);
-            }
-
-            // Materials
-            if (parsedData.materials) {
-                for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
-                    var parsedMaterial = parsedData.materials[index];
-                    Material.Parse(parsedMaterial, scene, rootUrl);
+                
+                // Fog
+                if (parsedData.fogMode && parsedData.fogMode !== 0) {
+                    scene.fogMode = parsedData.fogMode;
+                    scene.fogColor = BABYLON.Color3.FromArray(parsedData.fogColor);
+                    scene.fogStart = parsedData.fogStart;
+                    scene.fogEnd = parsedData.fogEnd;
+                    scene.fogDensity = parsedData.fogDensity;
+                    log += "\tFog mode for scene:  ";
+                    switch (scene.fogMode) {
+                        // getters not compiling, so using hardcoded
+                        case 1: log += "exp\n"; break;
+                        case 2: log += "exp2\n"; break;
+                        case 3: log += "linear\n"; break;
+                    }
                 }
-            }
-
-            if (parsedData.multiMaterials) {
-                for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
-                    var parsedMultiMaterial = parsedData.multiMaterials[index];
-                    Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                
+                //Physics
+                if (parsedData.physicsEnabled) {
+                    var physicsPlugin;
+                    if (parsedData.physicsEngine === "cannon") {
+                        physicsPlugin = new BABYLON.CannonJSPlugin();
+                    } else if (parsedData.physicsEngine === "oimo") {
+                        physicsPlugin = new BABYLON.OimoJSPlugin();
+                    }
+                    log = "\tPhysics engine " + (parsedData.physicsEngine ? parsedData.physicsEngine : "oimo") + " enabled\n";
+                    //else - default engine, which is currently oimo
+                    var physicsGravity = parsedData.physicsGravity ? BABYLON.Vector3.FromArray(parsedData.physicsGravity) : null;
+                    scene.enablePhysics(physicsGravity, physicsPlugin);
                 }
-            }
-
-            // Skeletons
-            if (parsedData.skeletons) {
-                for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
-                    var parsedSkeleton = parsedData.skeletons[index];
-                    Skeleton.Parse(parsedSkeleton, scene);
+                
+                //collisions, if defined. otherwise, default is true
+                if (parsedData.collisionsEnabled != undefined) {
+                    scene.collisionsEnabled = parsedData.collisionsEnabled;
                 }
-            }
-
-            // Geometries
-            var geometries = parsedData.geometries;
-            if (geometries) {
-                // Boxes
-                var boxes = geometries.boxes;
-                if (boxes) {
-                    for (index = 0, cache = boxes.length; index < cache; index++) {
-                        var parsedBox = boxes[index];
-                        Geometry.Primitives.Box.Parse(parsedBox, scene);
-                    }
+                scene.workerCollisions = !!parsedData.workerCollisions;
+
+                var index: number;
+                var cache: number;
+                // Lights
+                for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
+                    var parsedLight = parsedData.lights[index];
+                    var light = Light.Parse(parsedLight, scene);
+                    log += (index === 0 ? "\n\tLights:" : "");
+                    log += "\n\t\t" + light.toString(fullDetails);
                 }
-
-                // Spheres
-                var spheres = geometries.spheres;
-                if (spheres) {
-                    for (index = 0, cache = spheres.length; index < cache; index++) {
-                        var parsedSphere = spheres[index];
-                        Geometry.Primitives.Sphere.Parse(parsedSphere, scene);
+    
+                // Animations
+                if (parsedData.animations) {
+                    for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
+                        var parsedAnimation = parsedData.animations[index];
+                        var animation = Animation.Parse(parsedAnimation);
+                        scene.animations.push(animation);
+                        log += (index === 0 ? "\n\tAnimations:" : "");
+                        log += "\n\t\t" + animation.toString(fullDetails);
                     }
                 }
-
-                // Cylinders
-                var cylinders = geometries.cylinders;
-                if (cylinders) {
-                    for (index = 0, cache = cylinders.length; index < cache; index++) {
-                        var parsedCylinder = cylinders[index];
-                        Geometry.Primitives.Cylinder.Parse(parsedCylinder, scene);
+    
+                // Materials
+                if (parsedData.materials) {
+                    for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
+                        var parsedMaterial = parsedData.materials[index];
+                        var mat = Material.Parse(parsedMaterial, scene, rootUrl);
+                        log += (index === 0 ? "\n\tMaterials:" : "");
+                        log += "\n\t\t" + mat.toString(fullDetails);
                     }
                 }
 
-                // Toruses
-                var toruses = geometries.toruses;
-                if (toruses) {
-                    for (index = 0, cache = toruses.length; index < cache; index++) {
-                        var parsedTorus = toruses[index];
-                        Geometry.Primitives.Torus.Parse(parsedTorus, scene);
+                if (parsedData.multiMaterials) {
+                    for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
+                        var parsedMultiMaterial = parsedData.multiMaterials[index];
+                        var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                        log += (index === 0 ? "\n\tMultiMaterials:" : "");
+                        log += "\n\t\t" + mmat.toString(fullDetails);
                     }
                 }
-
-                // Grounds
-                var grounds = geometries.grounds;
-                if (grounds) {
-                    for (index = 0, cache = grounds.length; index < cache; index++) {
-                        var parsedGround = grounds[index];
-                        Geometry.Primitives.Ground.Parse(parsedGround, scene);
+    
+                // Skeletons
+                if (parsedData.skeletons) {
+                    for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
+                        var parsedSkeleton = parsedData.skeletons[index];
+                        var skeleton = Skeleton.Parse(parsedSkeleton, scene);
+                        log += (index === 0 ? "\n\tSkeletons:" : "");
+                        log += "\n\t\t" + skeleton.toString(fullDetails);
                     }
                 }
-
-                // Planes
-                var planes = geometries.planes;
-                if (planes) {
-                    for (index = 0, cache = planes.length; index < cache; index++) {
-                        var parsedPlane = planes[index];
-                        Geometry.Primitives.Plane.Parse(parsedPlane, scene);
+    
+                // Geometries
+                var geometries = parsedData.geometries;
+                if (geometries) {
+                    // Boxes
+                    var boxes = geometries.boxes;
+                    if (boxes) {
+                        for (index = 0, cache = boxes.length; index < cache; index++) {
+                            var parsedBox = boxes[index];
+                            Geometry.Primitives.Box.Parse(parsedBox, scene);
+                        }
+                    }
+    
+                    // Spheres
+                    var spheres = geometries.spheres;
+                    if (spheres) {
+                        for (index = 0, cache = spheres.length; index < cache; index++) {
+                            var parsedSphere = spheres[index];
+                            Geometry.Primitives.Sphere.Parse(parsedSphere, scene);
+                        }
+                    }
+    
+                    // Cylinders
+                    var cylinders = geometries.cylinders;
+                    if (cylinders) {
+                        for (index = 0, cache = cylinders.length; index < cache; index++) {
+                            var parsedCylinder = cylinders[index];
+                            Geometry.Primitives.Cylinder.Parse(parsedCylinder, scene);
+                        }
+                    }
+    
+                    // Toruses
+                    var toruses = geometries.toruses;
+                    if (toruses) {
+                        for (index = 0, cache = toruses.length; index < cache; index++) {
+                            var parsedTorus = toruses[index];
+                            Geometry.Primitives.Torus.Parse(parsedTorus, scene);
+                        }
+                    }
+    
+                    // Grounds
+                    var grounds = geometries.grounds;
+                    if (grounds) {
+                        for (index = 0, cache = grounds.length; index < cache; index++) {
+                            var parsedGround = grounds[index];
+                            Geometry.Primitives.Ground.Parse(parsedGround, scene);
+                        }
+                    }
+    
+                    // Planes
+                    var planes = geometries.planes;
+                    if (planes) {
+                        for (index = 0, cache = planes.length; index < cache; index++) {
+                            var parsedPlane = planes[index];
+                            Geometry.Primitives.Plane.Parse(parsedPlane, scene);
+                        }
+                    }
+    
+                    // TorusKnots
+                    var torusKnots = geometries.torusKnots;
+                    if (torusKnots) {
+                        for (index = 0, cache = torusKnots.length; index < cache; index++) {
+                            var parsedTorusKnot = torusKnots[index];
+                            Geometry.Primitives.TorusKnot.Parse(parsedTorusKnot, scene);
+                        }
+                    }
+    
+                    // VertexData
+                    var vertexData = geometries.vertexData;
+                    if (vertexData) {
+                        for (index = 0, cache = vertexData.length; index < cache; index++) {
+                            var parsedVertexData = vertexData[index];
+                            Geometry.Parse(parsedVertexData, scene, rootUrl);
+                        }
                     }
                 }
-
-                // TorusKnots
-                var torusKnots = geometries.torusKnots;
-                if (torusKnots) {
-                    for (index = 0, cache = torusKnots.length; index < cache; index++) {
-                        var parsedTorusKnot = torusKnots[index];
-                        Geometry.Primitives.TorusKnot.Parse(parsedTorusKnot, scene);
+    
+                // Meshes
+                for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                    var parsedMesh = parsedData.meshes[index];
+                    var mesh = <AbstractMesh>Mesh.Parse(parsedMesh, scene, rootUrl);
+                    log += (index === 0 ? "\n\tMeshes:" : "");
+                    log += "\n\t\t" + mesh.toString(fullDetails);
+                }
+    
+                // Cameras
+                for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
+                    var parsedCamera = parsedData.cameras[index];
+                    var camera = Camera.Parse(parsedCamera, scene);
+                    log += (index === 0 ? "\n\tCameras:" : "");
+                    log += "\n\t\t" + camera.toString(fullDetails);
+                }
+                if (parsedData.activeCameraID) {
+                    scene.setActiveCameraByID(parsedData.activeCameraID);
+                }
+    
+                // Browsing all the graph to connect the dots
+                for (index = 0, cache = scene.cameras.length; index < cache; index++) {
+                    var camera = scene.cameras[index];
+                    if (camera._waitingParentId) {
+                        camera.parent = scene.getLastEntryByID(camera._waitingParentId);
+                        camera._waitingParentId = undefined;
                     }
                 }
 
-                // VertexData
-                var vertexData = geometries.vertexData;
-                if (vertexData) {
-                    for (index = 0, cache = vertexData.length; index < cache; index++) {
-                        var parsedVertexData = vertexData[index];
-                        Geometry.Parse(parsedVertexData, scene, rootUrl);
+                for (index = 0, cache = scene.lights.length; index < cache; index++) {
+                    var light = scene.lights[index];
+                    if (light._waitingParentId) {
+                        light.parent = scene.getLastEntryByID(light._waitingParentId);
+                        light._waitingParentId = undefined;
                     }
                 }
-            }
-
-            // Meshes
-            for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                var parsedMesh = parsedData.meshes[index];
-                Mesh.Parse(parsedMesh, scene, rootUrl);
-            }
-
-            // Cameras
-            for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
-                var parsedCamera = parsedData.cameras[index];
-                Camera.Parse(parsedCamera, scene);
-            }
-            if (parsedData.activeCameraID) {
-                scene.setActiveCameraByID(parsedData.activeCameraID);
-            }
-
-            // Browsing all the graph to connect the dots
-            for (index = 0, cache = scene.cameras.length; index < cache; index++) {
-                var camera = scene.cameras[index];
-                if (camera._waitingParentId) {
-                    camera.parent = scene.getLastEntryByID(camera._waitingParentId);
-                    camera._waitingParentId = undefined;
+    
+                // Sounds
+                var loadedSounds: Sound[] = [];
+                var loadedSound: Sound;
+                if (AudioEngine && parsedData.sounds) {
+                    for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
+                        var parsedSound = parsedData.sounds[index];
+                        if (Engine.audioEngine.canUseWebAudio) {
+                            if (!parsedSound.url) parsedSound.url = parsedSound.name;
+                            if (!loadedSounds[parsedSound.url]) {
+                                loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
+                                loadedSounds[parsedSound.url] = loadedSound;
+                            }
+                            else {
+                                Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
+                            }
+                        } else {
+                            var emptySound = new Sound(parsedSound.name, null, scene);
+                        }
+                    }
+                    log += (index === 0 ? "\n\tSounds:" : "");
+                    log += "\n\t\t" + mat.toString(fullDetails);
                 }
-            }
 
-            for (index = 0, cache = scene.lights.length; index < cache; index++) {
-                var light = scene.lights[index];
-                if (light._waitingParentId) {
-                    light.parent = scene.getLastEntryByID(light._waitingParentId);
-                    light._waitingParentId = undefined;
+                loadedSounds = [];
+    
+                // Connect parents & children and parse actions
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                    var mesh = scene.meshes[index];
+                    if (mesh._waitingParentId) {
+                        mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
+                        mesh._waitingParentId = undefined;
+                    }
+                    if (mesh._waitingActions) {
+                        ActionManager.Parse(mesh._waitingActions, mesh, scene);
+                        mesh._waitingActions = undefined;
+                    }
                 }
-            }
-
-            // Sounds
-            var loadedSounds: Sound[] = [];
-            var loadedSound: Sound;
-            if (AudioEngine && parsedData.sounds) {
-                for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
-                    var parsedSound = parsedData.sounds[index];
-                    if (Engine.audioEngine.canUseWebAudio) {
-                        if (!parsedSound.url) parsedSound.url = parsedSound.name;
-                        if (!loadedSounds[parsedSound.url]) {
-                            loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
-                            loadedSounds[parsedSound.url] = loadedSound;
-                        }
-                        else {
-                            Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
-                        }
+    
+                // freeze world matrix application
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                    var currentMesh = scene.meshes[index];
+                    if (currentMesh._waitingFreezeWorldMatrix) {
+                        currentMesh.freezeWorldMatrix();
+                        currentMesh._waitingFreezeWorldMatrix = undefined;
                     } else {
-                        var emptySound = new Sound(parsedSound.name, null, scene);
+                        currentMesh.computeWorldMatrix(true);
                     }
                 }
-            }
-
-            loadedSounds = [];
-
-            // Connect parents & children and parse actions
-            for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                var mesh = scene.meshes[index];
-                if (mesh._waitingParentId) {
-                    mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
-                    mesh._waitingParentId = undefined;
+    
+                // Particles Systems
+                if (parsedData.particleSystems) {
+                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
+                        var parsedParticleSystem = parsedData.particleSystems[index];
+                        ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
+                    }
                 }
-                if (mesh._waitingActions) {
-                    ActionManager.Parse(mesh._waitingActions, mesh, scene);
-                    mesh._waitingActions = undefined;
+    
+                // Lens flares
+                if (parsedData.lensFlareSystems) {
+                    for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
+                        var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
+                        LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
+                    }
                 }
-            }
-
-            // freeze world matrix application
-            for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                var currentMesh = scene.meshes[index];
-                if (currentMesh._waitingFreezeWorldMatrix) {
-                    currentMesh.freezeWorldMatrix();
-                    currentMesh._waitingFreezeWorldMatrix = undefined;
-                } else {
-                    currentMesh.computeWorldMatrix(true);
+    
+                // Shadows
+                if (parsedData.shadowGenerators) {
+                    for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
+                        var parsedShadowGenerator = parsedData.shadowGenerators[index];
+                        ShadowGenerator.Parse(parsedShadowGenerator, scene);
+                    }
                 }
-            }
-
-            // Particles Systems
-            if (parsedData.particleSystems) {
-                for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
-                    var parsedParticleSystem = parsedData.particleSystems[index];
-                    ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
+    
+                // Actions (scene)
+                if (parsedData.actions) {
+                    ActionManager.Parse(parsedData.actions, null, scene);
                 }
-            }
+    
+                // Finish
+                return true;
 
-            // Lens flares
-            if (parsedData.lensFlareSystems) {
-                for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
-                    var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
-                    LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
-                }
-            }
+            } catch (err) {
+                Tools.Log(logOperation("importScene", parsedData.producer) + log);
+                log = null;
+                throw err;
 
-            // Shadows
-            if (parsedData.shadowGenerators) {
-                for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
-                    var parsedShadowGenerator = parsedData.shadowGenerators[index];
-                    ShadowGenerator.Parse(parsedShadowGenerator, scene);
+            } finally {
+                if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
+                    Tools.Log(logOperation("importScene", parsedData.producer) + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
                 }
             }
-
-            // Actions (scene)
-            if (parsedData.actions) {
-                ActionManager.Parse(parsedData.actions, null, scene);
-            }
-
-            // Finish
-            return true;
         }
     });
 }

+ 229 - 186
src/Loading/babylon.sceneLoader.js

@@ -1,186 +1,229 @@
-var BABYLON;
-(function (BABYLON) {
-    var SceneLoader = (function () {
-        function SceneLoader() {
-        }
-        Object.defineProperty(SceneLoader, "ForceFullSceneLoadingForIncremental", {
-            get: function () {
-                return SceneLoader._ForceFullSceneLoadingForIncremental;
-            },
-            set: function (value) {
-                SceneLoader._ForceFullSceneLoadingForIncremental = value;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(SceneLoader, "ShowLoadingScreen", {
-            get: function () {
-                return SceneLoader._ShowLoadingScreen;
-            },
-            set: function (value) {
-                SceneLoader._ShowLoadingScreen = value;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        SceneLoader._getPluginForFilename = function (sceneFilename) {
-            var dotPosition = sceneFilename.lastIndexOf(".");
-            var queryStringPosition = sceneFilename.indexOf("?");
-            if (queryStringPosition === -1) {
-                queryStringPosition = sceneFilename.length;
-            }
-            var extension = sceneFilename.substring(dotPosition, queryStringPosition).toLowerCase();
-            for (var index = 0; index < this._registeredPlugins.length; index++) {
-                var plugin = this._registeredPlugins[index];
-                if (plugin.extensions.indexOf(extension) !== -1) {
-                    return plugin;
-                }
-            }
-            return this._registeredPlugins[this._registeredPlugins.length - 1];
-        };
-        // Public functions
-        SceneLoader.GetPluginForExtension = function (extension) {
-            for (var index = 0; index < this._registeredPlugins.length; index++) {
-                var plugin = this._registeredPlugins[index];
-                if (plugin.extensions.indexOf(extension) !== -1) {
-                    return plugin;
-                }
-            }
-            return null;
-        };
-        SceneLoader.RegisterPlugin = function (plugin) {
-            plugin.extensions = plugin.extensions.toLowerCase();
-            SceneLoader._registeredPlugins.push(plugin);
-        };
-        SceneLoader.ImportMesh = function (meshesNames, rootUrl, sceneFilename, scene, onsuccess, progressCallBack, onerror) {
-            if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
-                BABYLON.Tools.Error("Wrong sceneFilename parameter");
-                return;
-            }
-            var loadingToken = {};
-            scene._addPendingData(loadingToken);
-            var manifestChecked = function (success) {
-                scene.database = database;
-                var plugin = SceneLoader._getPluginForFilename(sceneFilename);
-                var importMeshFromData = function (data) {
-                    var meshes = [];
-                    var particleSystems = [];
-                    var skeletons = [];
-                    try {
-                        if (!plugin.importMesh(meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons)) {
-                            if (onerror) {
-                                onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename);
-                            }
-                            scene._removePendingData(loadingToken);
-                            return;
-                        }
-                    }
-                    catch (e) {
-                        if (onerror) {
-                            onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename + ' (Exception: ' + e + ')');
-                        }
-                        scene._removePendingData(loadingToken);
-                        return;
-                    }
-                    if (onsuccess) {
-                        scene.importedMeshesFiles.push(rootUrl + sceneFilename);
-                        onsuccess(meshes, particleSystems, skeletons);
-                        scene._removePendingData(loadingToken);
-                    }
-                };
-                if (sceneFilename.substr && sceneFilename.substr(0, 5) === "data:") {
-                    // Direct load
-                    importMeshFromData(sceneFilename.substr(5));
-                    return;
-                }
-                BABYLON.Tools.LoadFile(rootUrl + sceneFilename, function (data) {
-                    importMeshFromData(data);
-                }, progressCallBack, database);
-            };
-            if (scene.getEngine().enableOfflineSupport && !(sceneFilename.substr && sceneFilename.substr(0, 5) === "data:")) {
-                // Checking if a manifest file has been set for this scene and if offline mode has been requested
-                var database = new BABYLON.Database(rootUrl + sceneFilename, manifestChecked);
-            }
-            else {
-                // If the scene is a data stream or offline support is not enabled, it's a direct load
-                manifestChecked(true);
-            }
-        };
-        /**
-        * 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
-        */
-        SceneLoader.Load = function (rootUrl, sceneFilename, engine, onsuccess, progressCallBack, onerror) {
-            SceneLoader.Append(rootUrl, sceneFilename, new BABYLON.Scene(engine), onsuccess, progressCallBack, onerror);
-        };
-        /**
-        * 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
-        */
-        SceneLoader.Append = function (rootUrl, sceneFilename, scene, onsuccess, progressCallBack, onerror) {
-            if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
-                BABYLON.Tools.Error("Wrong sceneFilename parameter");
-                return;
-            }
-            var plugin = this._getPluginForFilename(sceneFilename.name || sceneFilename);
-            var database;
-            var loadingToken = {};
-            scene._addPendingData(loadingToken);
-            if (SceneLoader.ShowLoadingScreen) {
-                scene.getEngine().displayLoadingUI();
-            }
-            var loadSceneFromData = function (data) {
-                scene.database = database;
-                if (!plugin.load(scene, data, rootUrl)) {
-                    if (onerror) {
-                        onerror(scene);
-                    }
-                    scene._removePendingData(loadingToken);
-                    scene.getEngine().hideLoadingUI();
-                    return;
-                }
-                if (onsuccess) {
-                    onsuccess(scene);
-                }
-                scene._removePendingData(loadingToken);
-                if (SceneLoader.ShowLoadingScreen) {
-                    scene.executeWhenReady(function () {
-                        scene.getEngine().hideLoadingUI();
-                    });
-                }
-            };
-            var manifestChecked = function (success) {
-                BABYLON.Tools.LoadFile(rootUrl + sceneFilename, loadSceneFromData, progressCallBack, database);
-            };
-            if (sceneFilename.substr && sceneFilename.substr(0, 5) === "data:") {
-                // Direct load
-                loadSceneFromData(sceneFilename.substr(5));
-                return;
-            }
-            if (rootUrl.indexOf("file:") === -1) {
-                if (scene.getEngine().enableOfflineSupport) {
-                    // Checking if a manifest file has been set for this scene and if offline mode has been requested
-                    database = new BABYLON.Database(rootUrl + sceneFilename, manifestChecked);
-                }
-                else {
-                    manifestChecked(true);
-                }
-            }
-            else {
-                BABYLON.Tools.ReadFile(sceneFilename, loadSceneFromData, progressCallBack);
-            }
-        };
-        // Flags
-        SceneLoader._ForceFullSceneLoadingForIncremental = false;
-        SceneLoader._ShowLoadingScreen = true;
-        // Members
-        SceneLoader._registeredPlugins = new Array();
-        return SceneLoader;
-    }());
-    BABYLON.SceneLoader = SceneLoader;
-    ;
-})(BABYLON || (BABYLON = {}));
+var BABYLON;
+(function (BABYLON) {
+    var SceneLoader = (function () {
+        function SceneLoader() {
+        }
+        Object.defineProperty(SceneLoader, "NO_LOGGING", {
+            get: function () {
+                return 0;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneLoader, "MINIMAL_LOGGING", {
+            get: function () {
+                return 1;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneLoader, "SUMMARY_LOGGING", {
+            get: function () {
+                return 2;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneLoader, "DETAILED_LOGGING", {
+            get: function () {
+                return 3;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneLoader, "ForceFullSceneLoadingForIncremental", {
+            get: function () {
+                return SceneLoader._ForceFullSceneLoadingForIncremental;
+            },
+            set: function (value) {
+                SceneLoader._ForceFullSceneLoadingForIncremental = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneLoader, "ShowLoadingScreen", {
+            get: function () {
+                return SceneLoader._ShowLoadingScreen;
+            },
+            set: function (value) {
+                SceneLoader._ShowLoadingScreen = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneLoader, "loggingLevel", {
+            get: function () {
+                return SceneLoader._loggingLevel;
+            },
+            set: function (value) {
+                SceneLoader._loggingLevel = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        SceneLoader._getPluginForFilename = function (sceneFilename) {
+            var dotPosition = sceneFilename.lastIndexOf(".");
+            var queryStringPosition = sceneFilename.indexOf("?");
+            if (queryStringPosition === -1) {
+                queryStringPosition = sceneFilename.length;
+            }
+            var extension = sceneFilename.substring(dotPosition, queryStringPosition).toLowerCase();
+            for (var index = 0; index < this._registeredPlugins.length; index++) {
+                var plugin = this._registeredPlugins[index];
+                if (plugin.extensions.indexOf(extension) !== -1) {
+                    return plugin;
+                }
+            }
+            return this._registeredPlugins[this._registeredPlugins.length - 1];
+        };
+        // Public functions
+        SceneLoader.GetPluginForExtension = function (extension) {
+            for (var index = 0; index < this._registeredPlugins.length; index++) {
+                var plugin = this._registeredPlugins[index];
+                if (plugin.extensions.indexOf(extension) !== -1) {
+                    return plugin;
+                }
+            }
+            return null;
+        };
+        SceneLoader.RegisterPlugin = function (plugin) {
+            plugin.extensions = plugin.extensions.toLowerCase();
+            SceneLoader._registeredPlugins.push(plugin);
+        };
+        SceneLoader.ImportMesh = function (meshesNames, rootUrl, sceneFilename, scene, onsuccess, progressCallBack, onerror) {
+            if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
+                BABYLON.Tools.Error("Wrong sceneFilename parameter");
+                return;
+            }
+            var loadingToken = {};
+            scene._addPendingData(loadingToken);
+            var manifestChecked = function (success) {
+                scene.database = database;
+                var plugin = SceneLoader._getPluginForFilename(sceneFilename);
+                var importMeshFromData = function (data) {
+                    var meshes = [];
+                    var particleSystems = [];
+                    var skeletons = [];
+                    try {
+                        if (!plugin.importMesh(meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons)) {
+                            if (onerror) {
+                                onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename);
+                            }
+                            scene._removePendingData(loadingToken);
+                            return;
+                        }
+                    }
+                    catch (e) {
+                        if (onerror) {
+                            onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename + ' (Exception: ' + e + ')');
+                        }
+                        scene._removePendingData(loadingToken);
+                        return;
+                    }
+                    if (onsuccess) {
+                        scene.importedMeshesFiles.push(rootUrl + sceneFilename);
+                        onsuccess(meshes, particleSystems, skeletons);
+                        scene._removePendingData(loadingToken);
+                    }
+                };
+                if (sceneFilename.substr && sceneFilename.substr(0, 5) === "data:") {
+                    // Direct load
+                    importMeshFromData(sceneFilename.substr(5));
+                    return;
+                }
+                BABYLON.Tools.LoadFile(rootUrl + sceneFilename, function (data) {
+                    importMeshFromData(data);
+                }, progressCallBack, database);
+            };
+            if (scene.getEngine().enableOfflineSupport && !(sceneFilename.substr && sceneFilename.substr(0, 5) === "data:")) {
+                // Checking if a manifest file has been set for this scene and if offline mode has been requested
+                var database = new BABYLON.Database(rootUrl + sceneFilename, manifestChecked);
+            }
+            else {
+                // If the scene is a data stream or offline support is not enabled, it's a direct load
+                manifestChecked(true);
+            }
+        };
+        /**
+        * 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
+        */
+        SceneLoader.Load = function (rootUrl, sceneFilename, engine, onsuccess, progressCallBack, onerror) {
+            if (!SceneLoader._warned) {
+                BABYLON.Tools.Warn("SceneLoader.Load deprecated since 2.4.  Use SceneLoader.Append.");
+                SceneLoader._warned = true;
+            }
+            SceneLoader.Append(rootUrl, sceneFilename, new BABYLON.Scene(engine), onsuccess, progressCallBack, onerror);
+        };
+        /**
+        * 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
+        */
+        SceneLoader.Append = function (rootUrl, sceneFilename, scene, onsuccess, progressCallBack, onerror) {
+            if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
+                BABYLON.Tools.Error("Wrong sceneFilename parameter");
+                return;
+            }
+            var plugin = this._getPluginForFilename(sceneFilename.name || sceneFilename);
+            var database;
+            var loadingToken = {};
+            scene._addPendingData(loadingToken);
+            if (SceneLoader.ShowLoadingScreen) {
+                scene.getEngine().displayLoadingUI();
+            }
+            var loadSceneFromData = function (data) {
+                scene.database = database;
+                if (!plugin.load(scene, data, rootUrl)) {
+                    if (onerror) {
+                        onerror(scene);
+                    }
+                    scene._removePendingData(loadingToken);
+                    scene.getEngine().hideLoadingUI();
+                    return;
+                }
+                if (onsuccess) {
+                    onsuccess(scene);
+                }
+                scene._removePendingData(loadingToken);
+                if (SceneLoader.ShowLoadingScreen) {
+                    scene.executeWhenReady(function () {
+                        scene.getEngine().hideLoadingUI();
+                    });
+                }
+            };
+            var manifestChecked = function (success) {
+                BABYLON.Tools.LoadFile(rootUrl + sceneFilename, loadSceneFromData, progressCallBack, database);
+            };
+            if (sceneFilename.substr && sceneFilename.substr(0, 5) === "data:") {
+                // Direct load
+                loadSceneFromData(sceneFilename.substr(5));
+                return;
+            }
+            if (rootUrl.indexOf("file:") === -1) {
+                if (scene.getEngine().enableOfflineSupport) {
+                    // Checking if a manifest file has been set for this scene and if offline mode has been requested
+                    database = new BABYLON.Database(rootUrl + sceneFilename, manifestChecked);
+                }
+                else {
+                    manifestChecked(true);
+                }
+            }
+            else {
+                BABYLON.Tools.ReadFile(sceneFilename, loadSceneFromData, progressCallBack);
+            }
+        };
+        // Flags
+        SceneLoader._ForceFullSceneLoadingForIncremental = false;
+        SceneLoader._ShowLoadingScreen = true;
+        SceneLoader._loggingLevel = SceneLoader.NO_LOGGING;
+        // Members
+        SceneLoader._registeredPlugins = new Array();
+        return SceneLoader;
+    })();
+    BABYLON.SceneLoader = SceneLoader;
+    ;
+})(BABYLON || (BABYLON = {}));

+ 31 - 0
src/Loading/babylon.sceneLoader.ts

@@ -10,6 +10,24 @@
         private static _ForceFullSceneLoadingForIncremental = false;
         private static _ShowLoadingScreen = true;
 
+        public static get NO_LOGGING(): number {
+            return 0;
+        }
+
+        public static get MINIMAL_LOGGING(): number {
+            return 1;
+        }
+
+        public static get SUMMARY_LOGGING(): number {
+            return 2;
+        }
+
+        public static get DETAILED_LOGGING(): number {
+            return 3;
+        }
+
+        private static _loggingLevel = SceneLoader.NO_LOGGING;
+
         public static get ForceFullSceneLoadingForIncremental() {
             return SceneLoader._ForceFullSceneLoadingForIncremental;
         }
@@ -26,6 +44,14 @@
             SceneLoader._ShowLoadingScreen = value;
         }
 
+        public static get loggingLevel() {
+            return SceneLoader._loggingLevel;
+        }
+
+        public static set loggingLevel(value: number) {
+            SceneLoader._loggingLevel = value;
+        }
+
         // Members
         private static _registeredPlugins = new Array<ISceneLoaderPlugin>();
 
@@ -133,6 +159,7 @@
             }
         }
 
+        private static _warned : boolean;
         /**
         * Load a scene
         * @param rootUrl a string that defines the root url for scene and resources
@@ -140,6 +167,10 @@
         * @param engine is the instance of BABYLON.Engine to use to create the scene
         */
         public static Load(rootUrl: string, sceneFilename: any, engine: Engine, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void {
+            if (!SceneLoader._warned) {
+                Tools.Warn("SceneLoader.Load deprecated since 2.4.  Use SceneLoader.Append.");
+                SceneLoader._warned = true;
+            }
             SceneLoader.Append(rootUrl, sceneFilename, new Scene(engine), onsuccess, progressCallBack, onerror);
         }
 

+ 328 - 318
src/Materials/babylon.material.js

@@ -1,318 +1,328 @@
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var BABYLON;
-(function (BABYLON) {
-    var MaterialDefines = (function () {
-        function MaterialDefines() {
-        }
-        MaterialDefines.prototype.isEqual = function (other) {
-            for (var index = 0; index < this._keys.length; index++) {
-                var prop = this._keys[index];
-                if (this[prop] !== other[prop]) {
-                    return false;
-                }
-            }
-            return true;
-        };
-        MaterialDefines.prototype.cloneTo = function (other) {
-            for (var index = 0; index < this._keys.length; index++) {
-                var prop = this._keys[index];
-                other[prop] = this[prop];
-            }
-        };
-        MaterialDefines.prototype.reset = function () {
-            for (var index = 0; index < this._keys.length; index++) {
-                var prop = this._keys[index];
-                if (typeof (this[prop]) === "number") {
-                    this[prop] = 0;
-                }
-                else {
-                    this[prop] = false;
-                }
-            }
-        };
-        MaterialDefines.prototype.toString = function () {
-            var result = "";
-            for (var index = 0; index < this._keys.length; index++) {
-                var prop = this._keys[index];
-                if (typeof (this[prop]) === "number") {
-                    result += "#define " + prop + " " + this[prop] + "\n";
-                }
-                else if (this[prop]) {
-                    result += "#define " + prop + "\n";
-                }
-            }
-            return result;
-        };
-        return MaterialDefines;
-    }());
-    BABYLON.MaterialDefines = MaterialDefines;
-    var Material = (function () {
-        function Material(name, scene, doNotAdd) {
-            this.name = name;
-            this.checkReadyOnEveryCall = false;
-            this.checkReadyOnlyOnce = false;
-            this.state = "";
-            this.alpha = 1.0;
-            this.backFaceCulling = true;
-            this.sideOrientation = Material.CounterClockWiseSideOrientation;
-            this.alphaMode = BABYLON.Engine.ALPHA_COMBINE;
-            this.disableDepthWrite = false;
-            this.fogEnabled = true;
-            this.pointSize = 1.0;
-            this.zOffset = 0;
-            this._wasPreviouslyReady = false;
-            this._fillMode = Material.TriangleFillMode;
-            this.id = name;
-            this._scene = scene;
-            if (!doNotAdd) {
-                scene.materials.push(this);
-            }
-        }
-        Object.defineProperty(Material, "TriangleFillMode", {
-            get: function () {
-                return Material._TriangleFillMode;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material, "WireFrameFillMode", {
-            get: function () {
-                return Material._WireFrameFillMode;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material, "PointFillMode", {
-            get: function () {
-                return Material._PointFillMode;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material, "ClockWiseSideOrientation", {
-            get: function () {
-                return Material._ClockWiseSideOrientation;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material, "CounterClockWiseSideOrientation", {
-            get: function () {
-                return Material._CounterClockWiseSideOrientation;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material.prototype, "wireframe", {
-            get: function () {
-                return this._fillMode === Material.WireFrameFillMode;
-            },
-            set: function (value) {
-                this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material.prototype, "pointsCloud", {
-            get: function () {
-                return this._fillMode === Material.PointFillMode;
-            },
-            set: function (value) {
-                this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material.prototype, "fillMode", {
-            get: function () {
-                return this._fillMode;
-            },
-            set: function (value) {
-                this._fillMode = value;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(Material.prototype, "isFrozen", {
-            get: function () {
-                return this.checkReadyOnlyOnce;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Material.prototype.freeze = function () {
-            this.checkReadyOnlyOnce = true;
-        };
-        Material.prototype.unfreeze = function () {
-            this.checkReadyOnlyOnce = false;
-        };
-        Material.prototype.isReady = function (mesh, useInstances) {
-            return true;
-        };
-        Material.prototype.getEffect = function () {
-            return this._effect;
-        };
-        Material.prototype.getScene = function () {
-            return this._scene;
-        };
-        Material.prototype.needAlphaBlending = function () {
-            return (this.alpha < 1.0);
-        };
-        Material.prototype.needAlphaTesting = function () {
-            return false;
-        };
-        Material.prototype.getAlphaTestTexture = function () {
-            return null;
-        };
-        Material.prototype.trackCreation = function (onCompiled, onError) {
-        };
-        Material.prototype.markDirty = function () {
-            this._wasPreviouslyReady = false;
-        };
-        Material.prototype._preBind = function () {
-            var engine = this._scene.getEngine();
-            engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling, this.zOffset, false, this.sideOrientation === Material.ClockWiseSideOrientation);
-        };
-        Material.prototype.bind = function (world, mesh) {
-            this._scene._cachedMaterial = this;
-            if (this.onBind) {
-                this.onBind(this, mesh);
-            }
-            if (this.disableDepthWrite) {
-                var engine = this._scene.getEngine();
-                this._cachedDepthWriteState = engine.getDepthWrite();
-                engine.setDepthWrite(false);
-            }
-        };
-        Material.prototype.bindOnlyWorldMatrix = function (world) {
-        };
-        Material.prototype.unbind = function () {
-            if (this.disableDepthWrite) {
-                var engine = this._scene.getEngine();
-                engine.setDepthWrite(this._cachedDepthWriteState);
-            }
-        };
-        Material.prototype.clone = function (name) {
-            return null;
-        };
-        Material.prototype.getBindedMeshes = function () {
-            var result = new Array();
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    result.push(mesh);
-                }
-            }
-            return result;
-        };
-        Material.prototype.dispose = function (forceDisposeEffect) {
-            // Animations
-            this.getScene().stopAnimation(this);
-            // Remove from scene
-            var index = this._scene.materials.indexOf(this);
-            if (index >= 0) {
-                this._scene.materials.splice(index, 1);
-            }
-            // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
-            if (forceDisposeEffect && this._effect) {
-                this._scene.getEngine()._releaseEffect(this._effect);
-                this._effect = null;
-            }
-            // Remove from meshes
-            for (index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                if (mesh.material === this) {
-                    mesh.material = null;
-                }
-            }
-            // Callback
-            if (this.onDispose) {
-                this.onDispose();
-            }
-        };
-        Material.prototype.serialize = function () {
-            return BABYLON.SerializationHelper.Serialize(this);
-        };
-        Material.ParseMultiMaterial = function (parsedMultiMaterial, scene) {
-            var multiMaterial = new BABYLON.MultiMaterial(parsedMultiMaterial.name, scene);
-            multiMaterial.id = parsedMultiMaterial.id;
-            BABYLON.Tags.AddTagsTo(multiMaterial, parsedMultiMaterial.tags);
-            for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
-                var subMatId = parsedMultiMaterial.materials[matIndex];
-                if (subMatId) {
-                    multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
-                }
-                else {
-                    multiMaterial.subMaterials.push(null);
-                }
-            }
-            return multiMaterial;
-        };
-        Material.Parse = function (parsedMaterial, scene, rootUrl) {
-            if (!parsedMaterial.customType) {
-                return BABYLON.StandardMaterial.Parse(parsedMaterial, scene, rootUrl);
-            }
-            var materialType = BABYLON.Tools.Instantiate(parsedMaterial.customType);
-            return materialType.Parse(parsedMaterial, scene, rootUrl);
-            ;
-        };
-        Material._TriangleFillMode = 0;
-        Material._WireFrameFillMode = 1;
-        Material._PointFillMode = 2;
-        Material._ClockWiseSideOrientation = 0;
-        Material._CounterClockWiseSideOrientation = 1;
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "id", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "checkReadyOnEveryCall", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "checkReadyOnlyOnce", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "state", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "alpha", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "backFaceCulling", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "sideOrientation", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "alphaMode", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "disableDepthWrite", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "fogEnabled", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "pointSize", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "zOffset", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "wireframe", null);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "pointsCloud", null);
-        __decorate([
-            BABYLON.serialize()
-        ], Material.prototype, "fillMode", null);
-        return Material;
-    }());
-    BABYLON.Material = Material;
-})(BABYLON || (BABYLON = {}));
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var MaterialDefines = (function () {
+        function MaterialDefines() {
+        }
+        MaterialDefines.prototype.isEqual = function (other) {
+            for (var index = 0; index < this._keys.length; index++) {
+                var prop = this._keys[index];
+                if (this[prop] !== other[prop]) {
+                    return false;
+                }
+            }
+            return true;
+        };
+        MaterialDefines.prototype.cloneTo = function (other) {
+            for (var index = 0; index < this._keys.length; index++) {
+                var prop = this._keys[index];
+                other[prop] = this[prop];
+            }
+        };
+        MaterialDefines.prototype.reset = function () {
+            for (var index = 0; index < this._keys.length; index++) {
+                var prop = this._keys[index];
+                if (typeof (this[prop]) === "number") {
+                    this[prop] = 0;
+                }
+                else {
+                    this[prop] = false;
+                }
+            }
+        };
+        MaterialDefines.prototype.toString = function () {
+            var result = "";
+            for (var index = 0; index < this._keys.length; index++) {
+                var prop = this._keys[index];
+                if (typeof (this[prop]) === "number") {
+                    result += "#define " + prop + " " + this[prop] + "\n";
+                }
+                else if (this[prop]) {
+                    result += "#define " + prop + "\n";
+                }
+            }
+            return result;
+        };
+        return MaterialDefines;
+    })();
+    BABYLON.MaterialDefines = MaterialDefines;
+    var Material = (function () {
+        function Material(name, scene, doNotAdd) {
+            this.name = name;
+            this.checkReadyOnEveryCall = false;
+            this.checkReadyOnlyOnce = false;
+            this.state = "";
+            this.alpha = 1.0;
+            this.backFaceCulling = true;
+            this.sideOrientation = Material.CounterClockWiseSideOrientation;
+            this.alphaMode = BABYLON.Engine.ALPHA_COMBINE;
+            this.disableDepthWrite = false;
+            this.fogEnabled = true;
+            this.pointSize = 1.0;
+            this.zOffset = 0;
+            this._wasPreviouslyReady = false;
+            this._fillMode = Material.TriangleFillMode;
+            this.id = name;
+            this._scene = scene;
+            if (!doNotAdd) {
+                scene.materials.push(this);
+            }
+        }
+        Object.defineProperty(Material, "TriangleFillMode", {
+            get: function () {
+                return Material._TriangleFillMode;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material, "WireFrameFillMode", {
+            get: function () {
+                return Material._WireFrameFillMode;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material, "PointFillMode", {
+            get: function () {
+                return Material._PointFillMode;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material, "ClockWiseSideOrientation", {
+            get: function () {
+                return Material._ClockWiseSideOrientation;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material, "CounterClockWiseSideOrientation", {
+            get: function () {
+                return Material._CounterClockWiseSideOrientation;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material.prototype, "wireframe", {
+            get: function () {
+                return this._fillMode === Material.WireFrameFillMode;
+            },
+            set: function (value) {
+                this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material.prototype, "pointsCloud", {
+            get: function () {
+                return this._fillMode === Material.PointFillMode;
+            },
+            set: function (value) {
+                this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material.prototype, "fillMode", {
+            get: function () {
+                return this._fillMode;
+            },
+            set: function (value) {
+                this._fillMode = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         * subclasses should override adding information pertainent to themselves
+         */
+        Material.prototype.toString = function (fullDetails) {
+            var ret = "Name: " + this.name;
+            if (fullDetails) {
+            }
+            return ret;
+        };
+        Object.defineProperty(Material.prototype, "isFrozen", {
+            get: function () {
+                return this.checkReadyOnlyOnce;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Material.prototype.freeze = function () {
+            this.checkReadyOnlyOnce = true;
+        };
+        Material.prototype.unfreeze = function () {
+            this.checkReadyOnlyOnce = false;
+        };
+        Material.prototype.isReady = function (mesh, useInstances) {
+            return true;
+        };
+        Material.prototype.getEffect = function () {
+            return this._effect;
+        };
+        Material.prototype.getScene = function () {
+            return this._scene;
+        };
+        Material.prototype.needAlphaBlending = function () {
+            return (this.alpha < 1.0);
+        };
+        Material.prototype.needAlphaTesting = function () {
+            return false;
+        };
+        Material.prototype.getAlphaTestTexture = function () {
+            return null;
+        };
+        Material.prototype.trackCreation = function (onCompiled, onError) {
+        };
+        Material.prototype.markDirty = function () {
+            this._wasPreviouslyReady = false;
+        };
+        Material.prototype._preBind = function () {
+            var engine = this._scene.getEngine();
+            engine.enableEffect(this._effect);
+            engine.setState(this.backFaceCulling, this.zOffset, false, this.sideOrientation === Material.ClockWiseSideOrientation);
+        };
+        Material.prototype.bind = function (world, mesh) {
+            this._scene._cachedMaterial = this;
+            if (this.onBind) {
+                this.onBind(this, mesh);
+            }
+            if (this.disableDepthWrite) {
+                var engine = this._scene.getEngine();
+                this._cachedDepthWriteState = engine.getDepthWrite();
+                engine.setDepthWrite(false);
+            }
+        };
+        Material.prototype.bindOnlyWorldMatrix = function (world) {
+        };
+        Material.prototype.unbind = function () {
+            if (this.disableDepthWrite) {
+                var engine = this._scene.getEngine();
+                engine.setDepthWrite(this._cachedDepthWriteState);
+            }
+        };
+        Material.prototype.clone = function (name) {
+            return null;
+        };
+        Material.prototype.getBindedMeshes = function () {
+            var result = new Array();
+            for (var index = 0; index < this._scene.meshes.length; index++) {
+                var mesh = this._scene.meshes[index];
+                if (mesh.material === this) {
+                    result.push(mesh);
+                }
+            }
+            return result;
+        };
+        Material.prototype.dispose = function (forceDisposeEffect) {
+            // Animations
+            this.getScene().stopAnimation(this);
+            // Remove from scene
+            var index = this._scene.materials.indexOf(this);
+            if (index >= 0) {
+                this._scene.materials.splice(index, 1);
+            }
+            // Shader are kept in cache for further use but we can get rid of this by using forceDisposeEffect
+            if (forceDisposeEffect && this._effect) {
+                this._scene.getEngine()._releaseEffect(this._effect);
+                this._effect = null;
+            }
+            // Remove from meshes
+            for (index = 0; index < this._scene.meshes.length; index++) {
+                var mesh = this._scene.meshes[index];
+                if (mesh.material === this) {
+                    mesh.material = null;
+                }
+            }
+            // Callback
+            if (this.onDispose) {
+                this.onDispose();
+            }
+        };
+        Material.prototype.serialize = function () {
+            return BABYLON.SerializationHelper.Serialize(this);
+        };
+        Material.ParseMultiMaterial = function (parsedMultiMaterial, scene) {
+            var multiMaterial = new BABYLON.MultiMaterial(parsedMultiMaterial.name, scene);
+            multiMaterial.id = parsedMultiMaterial.id;
+            BABYLON.Tags.AddTagsTo(multiMaterial, parsedMultiMaterial.tags);
+            for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
+                var subMatId = parsedMultiMaterial.materials[matIndex];
+                if (subMatId) {
+                    multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
+                }
+                else {
+                    multiMaterial.subMaterials.push(null);
+                }
+            }
+            return multiMaterial;
+        };
+        Material.Parse = function (parsedMaterial, scene, rootUrl) {
+            if (!parsedMaterial.customType) {
+                return BABYLON.StandardMaterial.Parse(parsedMaterial, scene, rootUrl);
+            }
+            var materialType = BABYLON.Tools.Instantiate(parsedMaterial.customType);
+            return materialType.Parse(parsedMaterial, scene, rootUrl);
+            ;
+        };
+        Material._TriangleFillMode = 0;
+        Material._WireFrameFillMode = 1;
+        Material._PointFillMode = 2;
+        Material._ClockWiseSideOrientation = 0;
+        Material._CounterClockWiseSideOrientation = 1;
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "id", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "checkReadyOnEveryCall", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "checkReadyOnlyOnce", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "state", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "alpha", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "backFaceCulling", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "sideOrientation", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "alphaMode", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "disableDepthWrite", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "fogEnabled", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "pointSize", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "zOffset", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "wireframe", null);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "pointsCloud", null);
+        __decorate([
+            BABYLON.serialize()
+        ], Material.prototype, "fillMode", null);
+        return Material;
+    })();
+    BABYLON.Material = Material;
+})(BABYLON || (BABYLON = {}));

+ 11 - 0
src/Materials/babylon.material.ts

@@ -166,6 +166,17 @@
             }
         }
 
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         * subclasses should override adding information pertainent to themselves
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = "Name: " + this.name;
+            if (fullDetails){
+            }
+            return ret;
+        } 
+        
         public get isFrozen(): boolean {
             return this.checkReadyOnlyOnce;
         }

File diff suppressed because it is too large
+ 2913 - 2856
src/Math/babylon.math.js


+ 65 - 4
src/Math/babylon.math.ts

@@ -3131,6 +3131,8 @@
 
         /**
         * new Path3D(path, normal, raw)
+        * Creates a Path3D. A Path3D is a logical math object, so not a mesh.  
+        * please read the description in the tutorial :  http://doc.babylonjs.com/tutorials/How_to_use_Path3D  
         * path : an array of Vector3, the curve axis of the Path3D
         * normal (optional) : Vector3, the first wanted normal to the curve. Ex (0, 1, 0) for a vertical normal.
         * raw (optional, default false) : boolean, if true the returned Path3D isn't normalized. Useful to depict path acceleration or speed.
@@ -3143,26 +3145,49 @@
             this._compute(firstNormal);
         }
 
+        /**
+         * Returns the Path3D array of successive Vector3 designing its curve.  
+         */
         public getCurve(): Vector3[] {
             return this._curve;
         }
 
+        /**
+         * Returns an array populated with tangent vectors on each Path3D curve point.
+         */
         public getTangents(): Vector3[] {
             return this._tangents;
         }
 
+
+        /**
+         * Returns an array populated with normal vectors on each Path3D curve point.
+         */
         public getNormals(): Vector3[] {
             return this._normals;
         }
 
+
+        /**
+         * Returns an array populated with binormal vectors on each Path3D curve point.
+         */
         public getBinormals(): Vector3[] {
             return this._binormals;
         }
 
+
+        /**
+         * Returns an array populated with distances (float) of the i-th point from the first curve point.
+         */
         public getDistances(): number[] {
             return this._distances;
         }
 
+
+        /**
+         * Forces the Path3D tangent, normal, binormal and distance recomputation.
+         * Returns the same object updated.  
+         */
         public update(path: Vector3[], firstNormal?: Vector3): Path3D {
             for (var p = 0; p < path.length; p++) {
                 this._curve[p].x = path[p].x;
@@ -3289,7 +3314,13 @@
         private _points: Vector3[];
         private _length: number = 0;
 
-        // QuadraticBezier(origin_V3, control_V3, destination_V3, nbPoints)
+        /**
+         * Returns a Curve3 object along a Quadratic Bezier curve : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#quadratic-bezier-curve  
+         * @param v0 (Vector3) the origin point of the Quadratic Bezier
+         * @param v1 (Vector3) the control point
+         * @param v2 (Vector3) the end point of the Quadratic Bezier
+         * @param nbPoints (integer) the wanted number of points in the curve
+         */
         public static CreateQuadraticBezier(v0: Vector3, v1: Vector3, v2: Vector3, nbPoints: number): Curve3 {
             nbPoints = nbPoints > 2 ? nbPoints : 3;
             var bez = new Array<Vector3>();
@@ -3302,8 +3333,15 @@
             }
             return new Curve3(bez);
         }
-
-        // CubicBezier(origin_V3, control1_V3, control2_V3, destination_V3, nbPoints)
+        
+        /**
+         * Returns a Curve3 object along a Cubic Bezier curve : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#cubic-bezier-curve  
+         * @param v0 (Vector3) the origin point of the Cubic Bezier
+         * @param v1 (Vector3) the first control point
+         * @param v2 (Vector3) the second control point
+         * @param v3 (Vector3) the end point of the Cubic Bezier
+         * @param nbPoints (integer) the wanted number of points in the curve
+         */
         public static CreateCubicBezier(v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, nbPoints: number): Curve3 {
             nbPoints = nbPoints > 3 ? nbPoints : 4;
             var bez = new Array<Vector3>();
@@ -3317,7 +3355,14 @@
             return new Curve3(bez);
         }
 
-        // HermiteSpline(origin_V3, originTangent_V3, destination_V3, destinationTangent_V3, nbPoints)
+        /**
+         * Returns a Curve3 object along a Hermite Spline curve : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#hermite-spline  
+         * @param p1 (Vector3) the origin point of the Hermite Spline
+         * @param t1 (Vector3) the tangent vector at the origin point
+         * @param p2 (Vector3) the end point of the Hermite Spline
+         * @param t2 (Vector3) the tangent vector at the end point
+         * @param nbPoints (integer) the wanted number of points in the curve
+         */
         public static CreateHermiteSpline(p1: Vector3, t1: Vector3, p2: Vector3, t2: Vector3, nbPoints: number): Curve3 {
             var hermite = new Array<Vector3>();
             var step = 1 / nbPoints;
@@ -3327,19 +3372,35 @@
             return new Curve3(hermite);
         }
 
+        /**
+         * A Curve3 object is a logical object, so not a mesh, to handle curves in the 3D geometric space.  
+         * A Curve3 is designed from a series of successive Vector3.  
+         * Tuto : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#curve3-object
+         */
         constructor(points: Vector3[]) {
             this._points = points;
             this._length = this._computeLength(points);
         }
 
+        /**
+         * Returns the Curve3 stored array of successive Vector3
+         */
         public getPoints() {
             return this._points;
         }
 
+        /**
+         * Returns the computed length (float) of the curve.
+         */
         public length() {
             return this._length;
         }
 
+        /**
+         * Returns a new instance of Curve3 object : var curve = curveA.continue(curveB);  
+         * This new Curve3 is built by translating and sticking the curveB at the end of the curveA.  
+         * curveA and curveB keep unchanged.  
+         */
         public continue(curve: Curve3): Curve3 {
             var lastPoint = this._points[this._points.length - 1];
             var continuedPoints = this._points.slice();

File diff suppressed because it is too large
+ 981 - 966
src/Mesh/babylon.abstractMesh.js


+ 16 - 0
src/Mesh/babylon.abstractMesh.ts

@@ -157,6 +157,22 @@
         }
 
         /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = "Name: " + this.name + ", isInstance: " + (this instanceof InstancedMesh ? "YES" : "NO");
+            ret += ", # of submeshes: " + (this.subMeshes ? this.subMeshes.length : 0);
+            if (this._skeleton) {
+                ret += ", skeleton: " + this._skeleton.name;
+            }
+            if (fullDetails){
+                ret += ", billboard mode: " + (["NONE", "X", "Y", null, "Z", null, null, "ALL"])[this.billboardMode];
+                ret += ", freeze wrld mat: " + (this._isWorldMatrixFrozen || this._waitingFreezeWorldMatrix ? "YES" : "NO");
+            }
+            return ret;
+        } 
+        
+        /**
          * Getting the rotation object. 
          * If rotation quaternion is set, this vector will (almost always) be the Zero vector!
          */

File diff suppressed because it is too large
+ 1724 - 1707
src/Mesh/babylon.mesh.js


+ 20 - 0
src/Mesh/babylon.mesh.ts

@@ -134,6 +134,26 @@
         }
 
         // Methods
+        /**
+         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         */
+        public toString(fullDetails? : boolean) : string {
+            var ret = super.toString(fullDetails);
+            ret += ", n vertices: " + this.getTotalVertices();
+            ret += ", parent: " + (this._waitingParentId ? this._waitingParentId : (this.parent ? this.parent.name : "NONE"));
+
+            if (this.animations){
+                for (var i = 0; i < this.animations.length; i++){
+                   ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
+                }
+            }
+            
+            if (fullDetails){
+                ret += ", flat shading: " + (this._geometry ? (this.getVerticesData(VertexBuffer.PositionKind).length / 3 === this.getIndices().length ? "YES" : "NO") : "UNKNOWN");
+            }
+            return ret;
+        } 
+        
         public get hasLODLevels(): boolean {
             return this._LODLevels.length > 0;
         }