Cubees 9 éve
szülő
commit
e0e4bf6ba8
32 módosított fájl, 6254 hozzáadás és 114 törlés
  1. 8 0
      materialsLibrary/config.json
  2. 2 2
      materialsLibrary/dist/babylon.fireMaterial.js
  3. 1 1
      materialsLibrary/dist/babylon.fireMaterial.min.js
  4. 499 0
      materialsLibrary/dist/babylon.furMaterial.js
  5. 1 0
      materialsLibrary/dist/babylon.furMaterial.min.js
  6. 1 1
      materialsLibrary/dist/babylon.lavaMaterial.js
  7. 1 1
      materialsLibrary/dist/babylon.lavaMaterial.min.js
  8. 2 2
      materialsLibrary/dist/babylon.normalMaterial.js
  9. 1 1
      materialsLibrary/dist/babylon.normalMaterial.min.js
  10. 4 4
      materialsLibrary/dist/babylon.pbrMaterial.js
  11. 3 3
      materialsLibrary/dist/babylon.pbrMaterial.min.js
  12. 2 2
      materialsLibrary/dist/babylon.simpleMaterial.js
  13. 1 1
      materialsLibrary/dist/babylon.simpleMaterial.min.js
  14. 3 3
      materialsLibrary/dist/babylon.terrainMaterial.js
  15. 1 1
      materialsLibrary/dist/babylon.terrainMaterial.min.js
  16. 1 1
      materialsLibrary/dist/babylon.waterMaterial.js
  17. 1 1
      materialsLibrary/dist/babylon.waterMaterial.min.js
  18. 30 0
      materialsLibrary/dist/dts/babylon.furMaterial.d.ts
  19. 601 0
      materialsLibrary/materials/fur/babylon.furMaterial.ts
  20. 565 0
      materialsLibrary/materials/fur/fur.fragment.fx
  21. 202 0
      materialsLibrary/materials/fur/fur.vertex.fx
  22. 236 0
      materialsLibrary/materials/fur/readme.md
  23. 54 0
      materialsLibrary/test/add/addfur.js
  24. 173 88
      materialsLibrary/test/babylon.max.js
  25. 9 2
      materialsLibrary/test/index.html
  26. BIN
      materialsLibrary/test/textures/leopard_fur.JPG
  27. BIN
      materialsLibrary/test/textures/speckles.jpg
  28. 1286 0
      pbr/babylon.pbrMaterial.ts
  29. 765 0
      pbr/legacypbr.fragment.fx
  30. 186 0
      pbr/legacypbr.vertex.fx
  31. 1295 0
      pbr/pbr.fragment.fx
  32. 320 0
      pbr/pbr.vertex.fx

+ 8 - 0
materialsLibrary/config.json

@@ -51,6 +51,14 @@
       "output": "babylon.fireMaterial.js"
     },
     {
+      "file": "materials/fur/babylon.furMaterial.ts",
+      "shaderFiles": [
+        "materials/fur/fur.vertex.fx",
+        "materials/fur/fur.fragment.fx"
+      ],
+      "output": "babylon.furMaterial.js"
+    },
+    {
       "file": "materials/terrain/babylon.terrainMaterial.ts",
       "shaderFiles": [
         "materials/terrain/terrain.vertex.fx",

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
materialsLibrary/dist/babylon.fireMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.fireMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 499 - 0
materialsLibrary/dist/babylon.furMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
materialsLibrary/dist/babylon.furMaterial.min.js


+ 1 - 1
materialsLibrary/dist/babylon.lavaMaterial.js

@@ -455,7 +455,7 @@ var BABYLON;
         };
         LavaMaterial.prototype.serialize = function () {
             var serializationObject = _super.prototype.serialize.call(this);
-            serializationObject.customType = "lava";
+            serializationObject.customType = "BABYLON.LavaMaterial";
             serializationObject.diffuseColor = this.diffuseColor.asArray();
             serializationObject.fogColor = this.fogColor.asArray();
             serializationObject.speed = this.speed;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.lavaMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
materialsLibrary/dist/babylon.normalMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.normalMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 4 - 4
materialsLibrary/dist/babylon.pbrMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
materialsLibrary/dist/babylon.pbrMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
materialsLibrary/dist/babylon.simpleMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.simpleMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
materialsLibrary/dist/babylon.terrainMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.terrainMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.waterMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
materialsLibrary/dist/babylon.waterMaterial.min.js


+ 30 - 0
materialsLibrary/dist/dts/babylon.furMaterial.d.ts

@@ -0,0 +1,30 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+declare module BABYLON {
+    class FurMaterial extends Material {
+        diffuseTexture: BaseTexture;
+        heightTexture: BaseTexture;
+        diffuseColor: Color3;
+        furLength: number;
+        furAngle: number;
+        furColor: Color3;
+        disableLighting: boolean;
+        private _worldViewProjectionMatrix;
+        private _scaledDiffuse;
+        private _renderId;
+        private _defines;
+        private _cachedDefines;
+        constructor(name: string, scene: Scene);
+        needAlphaBlending(): boolean;
+        needAlphaTesting(): boolean;
+        getAlphaTestTexture(): BaseTexture;
+        private _checkCache(scene, mesh?, useInstances?);
+        isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean;
+        bindOnlyWorldMatrix(world: Matrix): void;
+        bind(world: Matrix, mesh?: Mesh): void;
+        getAnimatables(): IAnimatable[];
+        dispose(forceDisposeEffect?: boolean): void;
+        clone(name: string): FurMaterial;
+        serialize(): any;
+        static Parse(source: any, scene: Scene, rootUrl: string): FurMaterial;
+    }
+}

+ 601 - 0
materialsLibrary/materials/fur/babylon.furMaterial.ts

@@ -0,0 +1,601 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    var maxSimultaneousLights = 4;
+
+    class FurMaterialDefines extends MaterialDefines {
+        public DIFFUSE = false;
+        public HEIGHTMAP = false;
+        public CLIPPLANE = false;
+        public ALPHATEST = false;
+        public POINTSIZE = false;
+        public FOG = false;
+        public LIGHT0 = false;
+        public LIGHT1 = false;
+        public LIGHT2 = false;
+        public LIGHT3 = false;
+        public SPOTLIGHT0 = false;
+        public SPOTLIGHT1 = false;
+        public SPOTLIGHT2 = false;
+        public SPOTLIGHT3 = false;
+        public HEMILIGHT0 = false;
+        public HEMILIGHT1 = false;
+        public HEMILIGHT2 = false;
+        public HEMILIGHT3 = false;
+        public DIRLIGHT0 = false;
+        public DIRLIGHT1 = false;
+        public DIRLIGHT2 = false;
+        public DIRLIGHT3 = false;
+        public POINTLIGHT0 = false;
+        public POINTLIGHT1 = false;
+        public POINTLIGHT2 = false;
+        public POINTLIGHT3 = false;        
+        public SHADOW0 = false;
+        public SHADOW1 = false;
+        public SHADOW2 = false;
+        public SHADOW3 = false;
+        public SHADOWS = false;
+        public SHADOWVSM0 = false;
+        public SHADOWVSM1 = false;
+        public SHADOWVSM2 = false;
+        public SHADOWVSM3 = false;
+        public SHADOWPCF0 = false;
+        public SHADOWPCF1 = false;
+        public SHADOWPCF2 = false;
+        public SHADOWPCF3 = false;
+        public NORMAL = false;
+        public UV1 = false;
+        public UV2 = false;
+        public VERTEXCOLOR = false;
+        public VERTEXALPHA = false;
+        public BONES = false;
+        public BONES4 = false;
+        public BonesPerMesh = 0;
+        public INSTANCES = false;
+
+        constructor() {
+            super();
+            this._keys = Object.keys(this);
+        }
+    }
+
+    export class FurMaterial extends Material {
+        public diffuseTexture: BaseTexture;
+        public heightTexture: BaseTexture;
+        public diffuseColor = new Color3(1, 1, 1);
+        public furLength: number = 1;
+        public furAngle: number = 0;
+        public furColor = new Color3(0.44,0.21,0.02);
+        public disableLighting = false;
+
+        private _worldViewProjectionMatrix = Matrix.Zero();
+        private _scaledDiffuse = new Color3(1.,1.,1.);
+        private _renderId: number;
+
+        private _defines = new FurMaterialDefines();
+        private _cachedDefines = new FurMaterialDefines();
+
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+
+            this._cachedDefines.BonesPerMesh = -1;
+        }
+
+        public needAlphaBlending(): boolean {
+            return (this.alpha < 1.0);
+        }
+
+        public needAlphaTesting(): boolean {
+            return false;
+        }
+
+        public getAlphaTestTexture(): BaseTexture {
+            return null;
+        }
+
+        // Methods   
+        private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (!mesh) {
+                return true;
+            }
+
+            if (this._defines.INSTANCES !== useInstances) {
+                return false;
+            }
+
+            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 = false;
+            var needUVs = false;
+
+            this._defines.reset();
+
+            // Textures
+            if (scene.texturesEnabled) {
+                if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
+                    if (!this.diffuseTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.DIFFUSE = true;
+                    }
+                } 
+                if (this.heightTexture) {
+                    if (!this.heightTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.HEIGHTMAP = true;
+                    }
+                }               
+            }
+
+            // Effect
+            if (scene.clipPlane) {
+                this._defines.CLIPPLANE = true;
+            }
+
+            if (engine.getAlphaTesting()) {
+                this._defines.ALPHATEST = true;
+            }
+
+            // Point size
+            if (this.pointsCloud || scene.forcePointsCloud) {
+                this._defines.POINTSIZE = true;
+            }
+
+            // Fog
+            if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
+                this._defines.FOG = true;
+            }
+
+            var lightIndex = 0;
+            if (scene.lightsEnabled && !this.disableLighting) {
+                for (var index = 0; index < scene.lights.length; index++) {
+                    var light = scene.lights[index];
+
+                    if (!light.isEnabled()) {
+                        continue;
+                    }
+
+                    // Excluded check
+                    if (light._excludedMeshesIds.length > 0) {
+                        for (var excludedIndex = 0; excludedIndex < light._excludedMeshesIds.length; excludedIndex++) {
+                            var excludedMesh = scene.getMeshByID(light._excludedMeshesIds[excludedIndex]);
+
+                            if (excludedMesh) {
+                                light.excludedMeshes.push(excludedMesh);
+                            }
+                        }
+
+                        light._excludedMeshesIds = [];
+                    }
+
+                    // Included check
+                    if (light._includedOnlyMeshesIds.length > 0) {
+                        for (var includedOnlyIndex = 0; includedOnlyIndex < light._includedOnlyMeshesIds.length; includedOnlyIndex++) {
+                            var includedOnlyMesh = scene.getMeshByID(light._includedOnlyMeshesIds[includedOnlyIndex]);
+
+                            if (includedOnlyMesh) {
+                                light.includedOnlyMeshes.push(includedOnlyMesh);
+                            }
+                        }
+
+                        light._includedOnlyMeshesIds = [];
+                    }
+
+                    if (!light.canAffectMesh(mesh)) {
+                        continue;
+                    }
+                    needNormals = true;
+                    this._defines["LIGHT" + lightIndex] = true;
+
+                    var type;
+                    if (light instanceof SpotLight) {
+                        type = "SPOTLIGHT" + lightIndex;
+                    } else if (light instanceof HemisphericLight) {
+                        type = "HEMILIGHT" + lightIndex;
+                    } else if (light instanceof PointLight) {
+                        type = "POINTLIGHT" + lightIndex;
+                    } else {
+                        type = "DIRLIGHT" + lightIndex;
+                    }
+
+                    this._defines[type] = true;
+
+                    // Shadows
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh && mesh.receiveShadows && shadowGenerator) {
+                            this._defines["SHADOW" + lightIndex] = true;
+
+                            this._defines.SHADOWS = true;
+
+                            if (shadowGenerator.useVarianceShadowMap || shadowGenerator.useBlurVarianceShadowMap) {
+                                this._defines["SHADOWVSM" + lightIndex] = true;
+                            }
+
+                            if (shadowGenerator.usePoissonSampling) {
+                                this._defines["SHADOWPCF" + lightIndex] = true;
+                            }
+                        }
+                    }
+
+                    lightIndex++;
+                    if (lightIndex === maxSimultaneousLights)
+                        break;
+                }
+            }
+
+            // Attribs
+            if (mesh) {
+                if (needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
+                    this._defines.NORMAL = true;
+                }
+                if (needUVs) {
+                    if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
+                        this._defines.UV1 = true;
+                    }
+                    if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
+                        this._defines.UV2 = true;
+                    }
+                }
+                if (mesh.useVertexColors && mesh.isVerticesDataPresent(VertexBuffer.ColorKind)) {
+                    this._defines.VERTEXCOLOR = true;
+
+                    if (mesh.hasVertexAlpha) {
+                        this._defines.VERTEXALPHA = true;
+                    }
+                }
+                if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                    this._defines.BONES = true;
+                    this._defines.BonesPerMesh = (mesh.skeleton.bones.length + 1);
+                    this._defines.BONES4 = true;
+                }
+
+                // Instances
+                if (useInstances) {
+                    this._defines.INSTANCES = true;
+                }
+            }
+
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+
+                scene.resetCachedMaterial();
+
+                // Fallbacks
+                var fallbacks = new EffectFallbacks();             
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+
+                for (lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                    if (!this._defines["LIGHT" + lightIndex]) {
+                        continue;
+                    }
+
+                    if (lightIndex > 0) {
+                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
+                    }
+
+                    if (this._defines["SHADOW" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
+                    }
+
+                    if (this._defines["SHADOWPCF" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                    }
+
+                    if (this._defines["SHADOWVSM" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                    }
+                }
+             
+                if (this._defines.BONES4) {
+                    fallbacks.addFallback(0, "BONES4");
+                }
+
+                //Attributes
+                var attribs = [VertexBuffer.PositionKind];
+
+                if (this._defines.NORMAL) {
+                    attribs.push(VertexBuffer.NormalKind);
+                }
+
+                if (this._defines.UV1) {
+                    attribs.push(VertexBuffer.UVKind);
+                }
+
+                if (this._defines.UV2) {
+                    attribs.push(VertexBuffer.UV2Kind);
+                }
+
+                if (this._defines.VERTEXCOLOR) {
+                    attribs.push(VertexBuffer.ColorKind);
+                }
+
+                if (this._defines.BONES) {
+                    attribs.push(VertexBuffer.MatricesIndicesKind);
+                    attribs.push(VertexBuffer.MatricesWeightsKind);
+                }
+
+                if (this._defines.INSTANCES) {
+                    attribs.push("world0");
+                    attribs.push("world1");
+                    attribs.push("world2");
+                    attribs.push("world3");
+                }
+
+                // Legacy browser patch
+                var shaderName = "fur";
+                var join = this._defines.toString();
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs,
+                    ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vDiffuseColor",
+                        "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
+                        "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
+                        "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
+                        "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
+                        "vFogInfos", "vFogColor", "pointSize",
+                        "vDiffuseInfos", 
+                        "mBones",
+                        "vClipPlane", "diffuseMatrix",
+                        "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3",
+                        "furLength", "furAngle", "furColor"
+                    ],
+                    ["diffuseSampler",
+                        "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3",
+                        "heightTexture"
+                    ],
+                    join, fallbacks, this.onCompiled, this.onError);
+            }
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            this._renderId = scene.getRenderId();
+            this._wasPreviouslyReady = true;
+
+            if (mesh) {
+                if (!mesh._materialDefines) {
+                    mesh._materialDefines = new FurMaterialDefines();
+                }
+
+                this._defines.cloneTo(mesh._materialDefines);
+            }
+
+            return true;
+        }
+
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            this._effect.setMatrix("world", world);
+        }
+
+        public bind(world: Matrix, mesh?: Mesh): void {
+            var scene = this.getScene();
+
+            // Matrices        
+            this.bindOnlyWorldMatrix(world);
+            this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
+
+            // Bones
+            if (mesh && mesh.useBones && mesh.computeBonesUsingShaders) {
+                this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
+            }
+
+            if (scene.getCachedMaterial() !== this) {
+                // Textures        
+                if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
+                    this._effect.setTexture("diffuseSampler", this.diffuseTexture);
+
+                    this._effect.setFloat2("vDiffuseInfos", this.diffuseTexture.coordinatesIndex, this.diffuseTexture.level);
+                    this._effect.setMatrix("diffuseMatrix", this.diffuseTexture.getTextureMatrix());
+                }
+                
+                if (this.heightTexture) {
+                    this._effect.setTexture("heightTexture", this.heightTexture);
+                }
+                
+                // Clip plane
+                if (scene.clipPlane) {
+                    var clipPlane = scene.clipPlane;
+                    this._effect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
+                }
+
+                // Point size
+                if (this.pointsCloud) {
+                    this._effect.setFloat("pointSize", this.pointSize);
+                }
+
+                this._effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.position);                
+            }
+
+            this._effect.setColor4("vDiffuseColor", this._scaledDiffuse, this.alpha * mesh.visibility);
+
+            if (scene.lightsEnabled && !this.disableLighting) {
+                var lightIndex = 0;
+                for (var index = 0; index < scene.lights.length; index++) {
+                    var light = scene.lights[index];
+
+                    if (!light.isEnabled()) {
+                        continue;
+                    }
+
+                    if (!light.canAffectMesh(mesh)) {
+                        continue;
+                    }
+
+                    if (light instanceof PointLight) {
+                        // Point Light
+                        light.transferToEffect(this._effect, "vLightData" + lightIndex);
+                    } else if (light instanceof DirectionalLight) {
+                        // Directional Light
+                        light.transferToEffect(this._effect, "vLightData" + lightIndex);
+                    } else if (light instanceof SpotLight) {
+                        // Spot Light
+                        light.transferToEffect(this._effect, "vLightData" + lightIndex, "vLightDirection" + lightIndex);
+                    } else if (light instanceof HemisphericLight) {
+                        // Hemispheric Light
+                        light.transferToEffect(this._effect, "vLightData" + lightIndex, "vLightGround" + lightIndex);
+                    }
+
+                    light.diffuse.scaleToRef(light.intensity, this._scaledDiffuse);
+                    this._effect.setColor4("vLightDiffuse" + lightIndex, this._scaledDiffuse, light.range);
+
+                    // Shadows
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh.receiveShadows && shadowGenerator) {
+                            this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
+                            this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMapForRendering());
+                            this._effect.setFloat3("shadowsInfo" + lightIndex, shadowGenerator.getDarkness(), shadowGenerator.getShadowMap().getSize().width, shadowGenerator.bias);
+                        }
+                    }
+
+                    lightIndex++;
+
+                    if (lightIndex === maxSimultaneousLights)
+                        break;
+                }
+            }
+
+            // View
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setMatrix("view", scene.getViewMatrix());
+            }
+
+            // Fog
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setFloat4("vFogInfos", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
+                this._effect.setColor3("vFogColor", scene.fogColor);
+            }
+            
+            this._effect.setFloat("furLength", this.furLength);
+            this._effect.setFloat("furAngle", this.furAngle);
+            this._effect.setColor4("furColor", this.furColor, 1.0);
+
+ 
+            super.bind(world, mesh);
+        }
+
+        public getAnimatables(): IAnimatable[] {
+            var results = [];
+
+            if (this.diffuseTexture && this.diffuseTexture.animations && this.diffuseTexture.animations.length > 0) {
+                results.push(this.diffuseTexture);
+            }
+            
+            if (this.heightTexture && this.heightTexture.animations && this.heightTexture.animations.length > 0) {
+                results.push(this.heightTexture);
+            }
+
+            return results;
+        }
+
+        public dispose(forceDisposeEffect?: boolean): void {
+            if (this.diffuseTexture) {
+                this.diffuseTexture.dispose();
+            }
+
+            super.dispose(forceDisposeEffect);
+        }
+
+        public clone(name: string): FurMaterial {
+            var newMaterial = new FurMaterial(name, this.getScene());
+
+            // Base material
+            this.copyTo(newMaterial);
+
+            // Fur material
+            if (this.diffuseTexture && this.diffuseTexture.clone) {
+                newMaterial.diffuseTexture = this.diffuseTexture.clone();
+            }
+            if (this.heightTexture && this.heightTexture.clone) {
+                newMaterial.heightTexture = this.heightTexture.clone();
+            }
+            if (this.diffuseColor && this.diffuseColor.clone) {
+                newMaterial.diffuseColor = this.diffuseColor.clone();
+            }
+            
+            return newMaterial;
+        }
+        
+        public serialize(): any {		
+            var serializationObject = super.serialize();
+            serializationObject.customType      = "BABYLON.FurMaterial";
+            serializationObject.diffuseColor    = this.diffuseColor.asArray();
+            serializationObject.disableLighting = this.disableLighting;
+            serializationObject.furLength = this.furLength;
+            serializationObject.furAngle = this.furAngle;
+            serializationObject.furColor = this.furColor.asArray();
+            
+            if (this.diffuseTexture) {
+                serializationObject.diffuseTexture = this.diffuseTexture.serialize();
+            }
+            
+            if (this.heightTexture) {
+                serializationObject.heightTexture = this.heightTexture.serialize();
+            }
+
+            return serializationObject;
+        }
+
+        public static Parse(source: any, scene: Scene, rootUrl: string): FurMaterial {
+            var material = new FurMaterial(source.name, scene);
+
+            material.diffuseColor       = Color3.FromArray(source.diffuseColor);
+            material.furLength          = source.furLength;
+            material.furAngle           = source.furAngle;
+            material.furColor           = Color3.FromArray(source.furColor);
+            material.disableLighting    = source.disableLighting;
+
+            material.alpha          = source.alpha;
+
+            material.id             = source.id;
+
+            Tags.AddTagsTo(material, source.tags);
+            material.backFaceCulling = source.backFaceCulling;
+            material.wireframe = source.wireframe;
+
+            if (source.diffuseTexture) {
+                material.diffuseTexture = Texture.Parse(source.diffuseTexture, scene, rootUrl);
+            }
+            
+            if (source.heightTexture) {
+                material.heightTexture = Texture.Parse(source.heightTexture, scene, rootUrl);
+            }
+
+            if (source.checkReadyOnlyOnce) {
+                material.checkReadyOnlyOnce = source.checkReadyOnlyOnce;
+            }
+
+            return material;
+        }
+    }
+} 
+

+ 565 - 0
materialsLibrary/materials/fur/fur.fragment.fx

@@ -0,0 +1,565 @@
+precision highp float;
+
+// Constants
+uniform vec3 vEyePosition;
+uniform vec4 vDiffuseColor;
+
+// Input
+uniform vec4 furColor;
+varying vec3 vPositionW;
+varying float vfur_length;
+
+#ifdef NORMAL
+varying vec3 vNormalW;
+#endif
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+// Lights
+#ifdef LIGHT0
+uniform vec4 vLightData0;
+uniform vec4 vLightDiffuse0;
+#ifdef SHADOW0
+#if defined(SPOTLIGHT0) || defined(DIRLIGHT0)
+varying vec4 vPositionFromLight0;
+uniform sampler2D shadowSampler0;
+#else
+uniform samplerCube shadowSampler0;
+#endif
+uniform vec3 shadowsInfo0;
+#endif
+#ifdef SPOTLIGHT0
+uniform vec4 vLightDirection0;
+#endif
+#ifdef HEMILIGHT0
+uniform vec3 vLightGround0;
+#endif
+#endif
+
+#ifdef LIGHT1
+uniform vec4 vLightData1;
+uniform vec4 vLightDiffuse1;
+#ifdef SHADOW1
+#if defined(SPOTLIGHT1) || defined(DIRLIGHT1)
+varying vec4 vPositionFromLight1;
+uniform sampler2D shadowSampler1;
+#else
+uniform samplerCube shadowSampler1;
+#endif
+uniform vec3 shadowsInfo1;
+#endif
+#ifdef SPOTLIGHT1
+uniform vec4 vLightDirection1;
+#endif
+#ifdef HEMILIGHT1
+uniform vec3 vLightGround1;
+#endif
+#endif
+
+#ifdef LIGHT2
+uniform vec4 vLightData2;
+uniform vec4 vLightDiffuse2;
+#ifdef SHADOW2
+#if defined(SPOTLIGHT2) || defined(DIRLIGHT2)
+varying vec4 vPositionFromLight2;
+uniform sampler2D shadowSampler2;
+#else
+uniform samplerCube shadowSampler2;
+#endif
+uniform vec3 shadowsInfo2;
+#endif
+#ifdef SPOTLIGHT2
+uniform vec4 vLightDirection2;
+#endif
+#ifdef HEMILIGHT2
+uniform vec3 vLightGround2;
+#endif
+#endif
+
+#ifdef LIGHT3
+uniform vec4 vLightData3;
+uniform vec4 vLightDiffuse3;
+#ifdef SHADOW3
+#if defined(SPOTLIGHT3) || defined(DIRLIGHT3)
+varying vec4 vPositionFromLight3;
+uniform sampler2D shadowSampler3;
+#else
+uniform samplerCube shadowSampler3;
+#endif
+uniform vec3 shadowsInfo3;
+#endif
+#ifdef SPOTLIGHT3
+uniform vec4 vLightDirection3;
+#endif
+#ifdef HEMILIGHT3
+uniform vec3 vLightGround3;
+#endif
+#endif
+
+// Samplers
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform sampler2D diffuseSampler;
+uniform vec2 vDiffuseInfos;
+#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)
+float computeShadowCube(vec3 lightPosition, samplerCube shadowSampler, float darkness, float bias)
+{
+	vec3 directionToLight = vPositionW - lightPosition;
+	float depth = length(directionToLight);
+
+	depth = clamp(depth, 0., 1.);
+
+	directionToLight.y = 1.0 - directionToLight.y;
+
+	float shadow = unpack(textureCube(shadowSampler, directionToLight)) + bias;
+
+	if (depth > shadow)
+	{
+		return darkness;
+	}
+	return 1.0;
+}
+
+float computeShadowWithPCFCube(vec3 lightPosition, samplerCube shadowSampler, float bias, float darkness)
+{
+	vec3 directionToLight = vPositionW - lightPosition;
+	float depth = length(directionToLight);
+
+	depth = clamp(depth, 0., 1.);
+
+	directionToLight.y = 1.0 - directionToLight.y;
+
+	float visibility = 1.;
+
+	vec3 poissonDisk[4];
+	poissonDisk[0] = vec3(-0.094201624, 0.04, -0.039906216);
+	poissonDisk[1] = vec3(0.094558609, -0.04, -0.076890725);
+	poissonDisk[2] = vec3(-0.094184101, 0.01, -0.092938870);
+	poissonDisk[3] = vec3(0.034495938, -0.01, 0.029387760);
+
+	// Poisson Sampling
+	float biasedDepth = depth - bias;
+
+	if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[0])) < biasedDepth) visibility -= 0.25;
+	if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[1])) < biasedDepth) visibility -= 0.25;
+	if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[2])) < biasedDepth) visibility -= 0.25;
+	if (unpack(textureCube(shadowSampler, directionToLight + poissonDisk[3])) < biasedDepth) visibility -= 0.25;
+
+	return  min(1.0, visibility + darkness);
+}
+#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)
+	{
+		return darkness;
+	}
+	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;
+
+	return  min(1.0, visibility + darkness);
+}
+
+// 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));
+	return min(1.0, 1.0 - ChebychevInequality(moments, depth.z, bias) + darkness);
+}
+#endif
+#endif
+
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+// Fog
+#ifdef FOG
+
+#define FOGMODE_NONE    0.
+#define FOGMODE_EXP     1.
+#define FOGMODE_EXP2    2.
+#define FOGMODE_LINEAR  3.
+#define E 2.71828
+
+uniform vec4 vFogInfos;
+uniform vec3 vFogColor;
+varying float fFogDistance;
+
+float CalcFogFactor()
+{
+	float fogCoeff = 1.0;
+	float fogStart = vFogInfos.y;
+	float fogEnd = vFogInfos.z;
+	float fogDensity = vFogInfos.w;
+
+	if (FOGMODE_LINEAR == vFogInfos.x)
+	{
+		fogCoeff = (fogEnd - fFogDistance) / (fogEnd - fogStart);
+	}
+	else if (FOGMODE_EXP == vFogInfos.x)
+	{
+		fogCoeff = 1.0 / pow(E, fFogDistance * fogDensity);
+	}
+	else if (FOGMODE_EXP2 == vFogInfos.x)
+	{
+		fogCoeff = 1.0 / pow(E, fFogDistance * fFogDistance * fogDensity * fogDensity);
+	}
+
+	return clamp(fogCoeff, 0.0, 1.0);
+}
+#endif
+
+// Light Computing
+struct lightingInfo
+{
+	vec3 diffuse;
+};
+
+lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, float range) {
+	lightingInfo result;
+
+	vec3 lightVectorW;
+	float attenuation = 1.0;
+	if (lightData.w == 0.)
+	{
+		vec3 direction = lightData.xyz - vPositionW;
+
+		attenuation = max(0., 1.0 - length(direction) / range);
+		lightVectorW = normalize(direction);
+	}
+	else
+	{
+		lightVectorW = normalize(-lightData.xyz);
+	}
+
+	// diffuse
+	float ndl = max(0., dot(vNormal, lightVectorW));
+	result.diffuse = ndl * diffuseColor * attenuation;
+
+	return result;
+}
+
+lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, float range) {
+	lightingInfo result;
+
+	vec3 direction = lightData.xyz - vPositionW;
+	vec3 lightVectorW = normalize(direction);
+	float attenuation = max(0., 1.0 - length(direction) / range);
+
+	// diffuse
+	float cosAngle = max(0., dot(-lightDirection.xyz, lightVectorW));
+	float spotAtten = 0.0;
+
+	if (cosAngle >= lightDirection.w)
+	{
+		cosAngle = max(0., pow(cosAngle, lightData.w));
+		spotAtten = clamp((cosAngle - lightDirection.w) / (1. - cosAngle), 0.0, 1.0);
+
+		// Diffuse
+		float ndl = max(0., dot(vNormal, -lightDirection.xyz));
+		result.diffuse = ndl * spotAtten * diffuseColor * attenuation;
+
+		return result;
+	}
+
+	result.diffuse = vec3(0.);
+
+	return result;
+}
+
+lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 groundColor) {
+	lightingInfo result;
+
+	// Diffuse
+	float ndl = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
+	result.diffuse = mix(groundColor, diffuseColor, ndl);
+
+	return result;
+}
+
+float Rand(vec3 rv) {
+	float x = dot(rv, vec3(12.9898,78.233, 24.65487));
+	return fract(sin(x) * 43758.5453);
+}
+
+void main(void) {
+	// Clip plane
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif
+
+	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
+
+	// Base color
+	vec4 baseColor = furColor;
+	vec3 diffuseColor = vDiffuseColor.rgb;
+
+	// Alpha
+	float alpha = vDiffuseColor.a;
+
+#ifdef DIFFUSE
+	baseColor = texture2D(diffuseSampler, vDiffuseUV);
+
+#ifdef ALPHATEST
+	if (baseColor.a < 0.4)
+		discard;
+#endif
+
+	baseColor.rgb *= vDiffuseInfos.y;
+#endif
+
+#ifdef VERTEXCOLOR
+	baseColor.rgb *= vColor.rgb;
+#endif
+
+	// Bump
+#ifdef NORMAL
+	vec3 normalW = normalize(vNormalW);
+#else
+	vec3 normalW = vec3(1.0, 1.0, 1.0);
+#endif
+
+	// Lighting
+	vec3 diffuseBase = vec3(0., 0., 0.);
+	float shadow = 1.;
+
+#ifdef LIGHT0
+#ifdef SPOTLIGHT0
+	lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightDiffuse0.a);
+#endif
+#ifdef HEMILIGHT0
+	lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightGround0);
+#endif
+#if defined(POINTLIGHT0) || defined(DIRLIGHT0)
+	lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightDiffuse0.a);
+#endif
+#ifdef SHADOW0
+#ifdef SHADOWVSM0
+	shadow = computeShadowWithVSM(vPositionFromLight0, shadowSampler0, shadowsInfo0.z, shadowsInfo0.x);
+#else
+#ifdef SHADOWPCF0
+	#if defined(POINTLIGHT0)
+	shadow = computeShadowWithPCFCube(vLightData0.xyz, shadowSampler0, shadowsInfo0.z, shadowsInfo0.x);
+	#else
+	shadow = computeShadowWithPCF(vPositionFromLight0, shadowSampler0, shadowsInfo0.y, shadowsInfo0.z, shadowsInfo0.x);
+	#endif
+#else
+	#if defined(POINTLIGHT0)
+	shadow = computeShadowCube(vLightData0.xyz, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
+	#else
+	shadow = computeShadow(vPositionFromLight0, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
+	#endif
+#endif
+#endif
+#else
+	shadow = 1.;
+#endif
+	diffuseBase += info.diffuse * shadow;
+#endif
+
+#ifdef LIGHT1
+#ifdef SPOTLIGHT1
+	info = computeSpotLighting(viewDirectionW, normalW, vLightData1, vLightDirection1, vLightDiffuse1.rgb, vLightDiffuse1.a);
+#endif
+#ifdef HEMILIGHT1
+	info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightGround1.a);
+#endif
+#if defined(POINTLIGHT1) || defined(DIRLIGHT1)
+	info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightDiffuse1.a);
+#endif
+#ifdef SHADOW1
+#ifdef SHADOWVSM1
+	shadow = computeShadowWithVSM(vPositionFromLight1, shadowSampler1, shadowsInfo1.z, shadowsInfo1.x);
+#else
+#ifdef SHADOWPCF1
+#if defined(POINTLIGHT1)
+	shadow = computeShadowWithPCFCube(vLightData1.xyz, shadowSampler1, shadowsInfo1.z, shadowsInfo1.x);
+#else
+	shadow = computeShadowWithPCF(vPositionFromLight1, shadowSampler1, shadowsInfo1.y, shadowsInfo1.z, shadowsInfo1.x);
+#endif
+#else
+	#if defined(POINTLIGHT1)
+	shadow = computeShadowCube(vLightData1.xyz, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
+	#else
+	shadow = computeShadow(vPositionFromLight1, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
+	#endif
+#endif
+#endif
+#else
+	shadow = 1.;
+#endif
+	diffuseBase += info.diffuse * shadow;
+#endif
+
+#ifdef LIGHT2
+#ifdef SPOTLIGHT2
+	info = computeSpotLighting(viewDirectionW, normalW, vLightData2, vLightDirection2, vLightDiffuse2.rgb, vLightDiffuse2.a);
+#endif
+#ifdef HEMILIGHT2
+	info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightGround2);
+#endif
+#if defined(POINTLIGHT2) || defined(DIRLIGHT2)
+	info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightDiffuse2.a);
+#endif
+#ifdef SHADOW2
+#ifdef SHADOWVSM2
+	shadow = computeShadowWithVSM(vPositionFromLight2, shadowSampler2, shadowsInfo2.z, shadowsInfo2.x);
+#else
+#ifdef SHADOWPCF2
+#if defined(POINTLIGHT2)
+	shadow = computeShadowWithPCFCube(vLightData2.xyz, shadowSampler2, shadowsInfo2.z, shadowsInfo2.x);
+#else
+	shadow = computeShadowWithPCF(vPositionFromLight2, shadowSampler2, shadowsInfo2.y, shadowsInfo2.z, shadowsInfo2.x);
+#endif
+#else
+	#if defined(POINTLIGHT2)
+	shadow = computeShadowCube(vLightData2.xyz, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
+	#else
+	shadow = computeShadow(vPositionFromLight2, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
+	#endif
+#endif	
+#endif	
+#else
+	shadow = 1.;
+#endif
+	diffuseBase += info.diffuse * shadow;
+#endif
+
+#ifdef LIGHT3
+#ifdef SPOTLIGHT3
+	info = computeSpotLighting(viewDirectionW, normalW, vLightData3, vLightDirection3, vLightDiffuse3.rgb, vLightDiffuse3.a);
+#endif
+#ifdef HEMILIGHT3
+	info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightGround3);
+#endif
+#if defined(POINTLIGHT3) || defined(DIRLIGHT3)
+	info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightDiffuse3.a);
+#endif
+#ifdef SHADOW3
+#ifdef SHADOWVSM3
+		shadow = computeShadowWithVSM(vPositionFromLight3, shadowSampler3, shadowsInfo3.z, shadowsInfo3.x);
+#else
+#ifdef SHADOWPCF3
+#if defined(POINTLIGHT3)
+	shadow = computeShadowWithPCFCube(vLightData3.xyz, shadowSampler3, shadowsInfo3.z, shadowsInfo3.x);
+#else
+	shadow = computeShadowWithPCF(vPositionFromLight3, shadowSampler3, shadowsInfo3.y, shadowsInfo3.z, shadowsInfo3.x);
+#endif
+#else
+	#if defined(POINTLIGHT3)
+	shadow = computeShadowCube(vLightData3.xyz, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
+	#else
+	shadow = computeShadow(vPositionFromLight3, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
+	#endif
+#endif	
+#endif	
+#else
+	shadow = 1.;
+#endif
+	diffuseBase += info.diffuse * shadow;
+#endif
+
+#ifdef VERTEXALPHA
+	alpha *= vColor.a;
+#endif
+
+	vec3 finalDiffuse = clamp(diffuseBase * diffuseColor, 0.0, 1.0) * baseColor.rgb;
+
+	// Composition
+	//float r = Rand(vPositionW) * 0.5;
+	float r = vfur_length * 0.5;
+	vec4 color = vec4(finalDiffuse * (0.5 + r), alpha);
+
+#ifdef FOG
+	float fog = CalcFogFactor();
+	color.rgb = fog * color.rgb + (1.0 - fog) * vFogColor;
+#endif
+
+	gl_FragColor = color;
+}

+ 202 - 0
materialsLibrary/materials/fur/fur.vertex.fx

@@ -0,0 +1,202 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+attribute vec3 normal;
+
+#ifdef UV1
+attribute vec2 uv;
+#endif
+#ifdef UV2
+attribute vec2 uv2;
+#endif
+#ifdef VERTEXCOLOR
+attribute vec4 color;
+#endif
+#ifdef BONES
+attribute vec4 matricesIndices;
+attribute vec4 matricesWeights;
+#endif
+
+
+// Uniforms
+uniform float furLength;
+uniform float furAngle;
+#ifdef HEIGHTMAP
+uniform sampler2D heightTexture;
+#endif
+
+#ifdef INSTANCES
+attribute vec4 world0;
+attribute vec4 world1;
+attribute vec4 world2;
+attribute vec4 world3;
+#else
+uniform mat4 world;
+#endif
+
+uniform mat4 view;
+uniform mat4 viewProjection;
+
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform mat4 diffuseMatrix;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef BONES
+uniform mat4 mBones[BonesPerMesh];
+#endif
+
+#ifdef POINTSIZE
+uniform float pointSize;
+#endif
+
+// Output
+varying vec3 vPositionW;
+#ifdef NORMAL
+varying vec3 vNormalW;
+#endif
+varying float vfur_length;
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+#ifdef FOG
+varying float fFogDistance;
+#endif
+
+#ifdef SHADOWS
+#if defined(SPOTLIGHT0) || defined(DIRLIGHT0)
+uniform mat4 lightMatrix0;
+varying vec4 vPositionFromLight0;
+#endif
+#if defined(SPOTLIGHT1) || defined(DIRLIGHT1)
+uniform mat4 lightMatrix1;
+varying vec4 vPositionFromLight1;
+#endif
+#if defined(SPOTLIGHT2) || defined(DIRLIGHT2)
+uniform mat4 lightMatrix2;
+varying vec4 vPositionFromLight2;
+#endif
+#if defined(SPOTLIGHT3) || defined(DIRLIGHT3)
+uniform mat4 lightMatrix3;
+varying vec4 vPositionFromLight3;
+#endif
+#endif
+
+float Rand(vec3 rv) {
+	float x = dot(rv, vec3(12.9898,78.233, 24.65487));
+	return fract(sin(x) * 43758.5453);
+}
+
+void main(void) {
+	mat4 finalWorld;
+
+#ifdef INSTANCES
+	finalWorld = mat4(world0, world1, world2, world3);
+#else
+	finalWorld = world;
+#endif
+
+#ifdef BONES
+	mat4 m0 = mBones[int(matricesIndices.x)] * matricesWeights.x;
+	mat4 m1 = mBones[int(matricesIndices.y)] * matricesWeights.y;
+	mat4 m2 = mBones[int(matricesIndices.z)] * matricesWeights.z;
+
+#ifdef BONES4
+	mat4 m3 = mBones[int(matricesIndices.w)] * matricesWeights.w;
+	finalWorld = finalWorld * (m0 + m1 + m2 + m3);
+#else
+	finalWorld = finalWorld * (m0 + m1 + m2);
+#endif 
+
+#endif
+//FUR
+float r = Rand(position);
+#ifdef HEIGHTMAP	
+	vfur_length = furLength * texture2D(heightTexture, uv).rgb.x;
+#else	
+	vfur_length = (furLength * r);
+#endif
+	vec3 tangent1 = vec3(normal.y, -normal.x, 0);
+	vec3 tangent2 = vec3(-normal.z, 0, normal.x);
+	r = Rand(tangent1*r);
+	float J = (2.0 + 4.0* r);
+	r = Rand(tangent2*r);
+	float K = (2.0 + 2.0* r);
+	tangent1 = tangent1*J + tangent2*K;
+	tangent1 = normalize(tangent1);
+    vec3 newPosition = position + normal * vfur_length*cos(furAngle) + tangent1*vfur_length*sin(furAngle);
+	
+//END FUR
+	gl_Position = viewProjection * finalWorld * vec4(newPosition, 1.0);
+
+	vec4 worldPos = finalWorld * vec4(newPosition, 1.0);
+	vPositionW = vec3(worldPos);
+
+#ifdef NORMAL
+	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
+#endif
+
+	// Texture coordinates
+#ifndef UV1
+	vec2 uv = vec2(0., 0.);
+#endif
+#ifndef UV2
+	vec2 uv2 = vec2(0., 0.);
+#endif
+
+#ifdef DIFFUSE
+	if (vDiffuseInfos.x == 0.)
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+	// Clip plane
+#ifdef CLIPPLANE
+	fClipDistance = dot(worldPos, vClipPlane);
+#endif
+
+	// Fog
+#ifdef FOG
+	fFogDistance = (view * worldPos).z;
+#endif
+
+	// Shadows
+#ifdef SHADOWS
+#if defined(SPOTLIGHT0) || defined(DIRLIGHT0)
+	vPositionFromLight0 = lightMatrix0 * worldPos;
+#endif
+#if defined(SPOTLIGHT1) || defined(DIRLIGHT1)
+	vPositionFromLight1 = lightMatrix1 * worldPos;
+#endif
+#if defined(SPOTLIGHT2) || defined(DIRLIGHT2)
+	vPositionFromLight2 = lightMatrix2 * worldPos;
+#endif
+#if defined(SPOTLIGHT3) || defined(DIRLIGHT3)
+	vPositionFromLight3 = lightMatrix3 * worldPos;
+#endif
+#endif
+
+	// Vertex color
+#ifdef VERTEXCOLOR
+	vColor = color;
+#endif
+
+	// Point size
+#ifdef POINTSIZE
+	gl_PointSize = pointSize;
+#endif
+}

+ 236 - 0
materialsLibrary/materials/fur/readme.md

@@ -0,0 +1,236 @@
+# Fur material
+
+## Using the fur material
+
+The fur material needs a high number of the triangular facets that make up a mesh to work well.
+The number of facets needed also depends on the size of the mesh.
+Example that seem to work for ground and sphere are:
+
+```
+var ground = BABYLON.Mesh.CreateGround("ground", 8, 8, 200, scene);
+var sphere = BABYLON.Mesh.CreateSphere("sphere", 500, 8, scene);
+```
+
+The fur material is created using 
+
+```
+var furMaterial = new BABYLON.furMaterial("fur_material", scene);
+waterMaterial.bumpTexture = new BABYLON.Texture("bump.png", scene); // Set the bump texture
+
+ground.material = waterMaterial;
+```
+
+## Customize the fur material
+
+You can customise three properties of the fur material:
+
+```
+furMaterial.furLength = 3; // Represents the maximum length of the fur, which is then adjusted randomly. Default value is 1.
+furMaterial.furAngle = Math.PI/6; // Represents the angle the fur lies on the mesh from 0 to Math.PI/2. The default angle of 0 gives fur sticking straight up and PI/2 lies along the mesh.
+furMaterial.furColor = new BABYLON.Color3(0.44, 0.21, 0.02); // is the default color if furColor is not set.
+```
+
+## Using textures
+
+#heightTexture
+
+A greyscale image can be used to set the fur length. 
+A speckled greyscale image can produce fur like results.
+Any greyscale image with affect the fur length producing a heightMap type effect.
+
+```
+furMaterial.heightTexture = new BABYLON.Texture("speckles.jpg", scene); // Set the fur length with a texture.
+```
+#diffuseTexture
+A texture can also be used to paint the mesh. 
+The leopard fur texture used in the test is by Martin Wegmann from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Leopard_fur.JPG)
+under the [license](https://creativecommons.org/licenses/by-sa/3.0/deed.en)
+
+```
+furMaterial.diffuseTexture = new BABYLON.Texture("leopard_fur.jpg", scene); // Set the fur length with a texture.
+```
+
+## Meshes where the number of facets is not user controlled on creation.
+
+Unlike the ground mesh where you can supply the number of subdivisions or the sphere mesh where you can supply the number of segments the majority of meshes are created using a minimum number of facets.
+To apply the fur material to these the number of facets per face of the mesh needs to be increased.
+
+The function increasedFacets will do this:
+When n is the number of points per side added to each side of a facet the number of facets is increased by the square of (n + 1).
+
+```
+function increasedFacets(mesh, pps) { //pps points per side		
+	var gaps = pps+1;
+	var n = gaps + 1;
+	var fvs =[];
+	for(var i=0; i<n; i++) {
+		fvs[i] = [];
+	}	
+	var A,B;
+	var d ={x:0,y:0,z:0};
+	var u ={x:0,y:0};
+	var indices = [];
+	var vertexIndex = [];
+	var side = [];
+	var uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
+	var meshIndices = mesh.getIndices();
+	var positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);	
+	var normals =[];	
+	
+	for(var i = 0; i<meshIndices.length; i+=3) {
+		vertexIndex[0] = meshIndices[i];
+		vertexIndex[1] = meshIndices[i + 1];
+		vertexIndex[2] = meshIndices[i + 2];		
+		for(var j = 0; j<3; j++) {
+			A = vertexIndex[j];
+			B = vertexIndex[(j+1)%3];		
+			if(side[A] === undefined  && side[B] ===  undefined) {			
+				side[A] = [];
+				side[B] = [];			
+			}
+			else {
+				if(side[A] === undefined) {					
+					side[A] = [];
+				}
+				if(side[B] === undefined) {					
+					side[B] = [];								
+				}
+			}
+			if(side[A][B]  === undefined  && side[B][A] === undefined) {			
+				side[A][B] = [];
+				d.x = (positions[3 * B] - positions[3 * A])/gaps;
+				d.y = (positions[3 * B + 1] - positions[3 * A + 1])/gaps;
+				d.z = (positions[3 * B + 2] - positions[3 * A + 2])/gaps;
+				u.x = (uvs[2*B] - uvs[2*A])/gaps;
+				u.y = (uvs[2*B + 1] - uvs[2*A + 1])/gaps;
+				side[A][B].push(A);				
+				for(var k=1; k<gaps; k++) {				
+					side[A][B].push(positions.length/3);				
+					positions.push(positions[3 * A] + k*d.x, positions[3 * A + 1] + k*d.y, positions[3 * A + 2] + k*d.z);
+					uvs.push(uvs[2*A] + k*u.x, uvs[2*A + 1] + k*u.y);
+				}				
+				side[A][B].push(B);
+				side[B][A]=[];
+				l = side[A][B].length;
+				for(var a=0; a<l; a++) {
+					side[B][A][a] = side[A][B][l-1-a];
+				}
+			}
+			else {
+				if(side[A][B] === undefined) {			
+					side[A][B]=[];
+					l = side[B][A].length;
+					for(var a=0; a<l; a++) {
+						side[A][B][a] = side[B][A][l-1-a];
+					}
+				}
+				if(side[B][A] === undefined) {			
+					side[B][A]=[];				
+					l = side[A][B].length;
+					for(var a=0; a<l; a++) {
+						side[B][A][a] = side[A][B][l-1-a];
+					}
+				}
+			}					
+		}	
+		fvs[0][0] = meshIndices[i];
+		fvs[1][0] = side[meshIndices[i]][meshIndices[i + 1]][1];
+		fvs[1][1] = side[meshIndices[i]][meshIndices[i + 2]][1];		
+		for(var k = 2; k<gaps; k++) {
+			fvs[k][0] = side[meshIndices[i]][meshIndices[i + 1]][k];
+			fvs[k][k] = side[meshIndices[i]][meshIndices[i + 2]][k];		
+			d.x = (positions[3 * fvs[k][k]] - positions[3 * fvs[k][0]])/k;
+			d.y = (positions[3 * fvs[k][k] + 1] - positions[3 * fvs[k][0] + 1])/k;
+			d.z = (positions[3 * fvs[k][k] + 2] - positions[3 * fvs[k][0] + 2])/k;
+			u.x = (uvs[2*fvs[k][k]] - uvs[2*fvs[k][0]])/k;
+			u.y = (uvs[2*fvs[k][k] + 1] - uvs[2*fvs[k][0] + 1])/k;
+			for(var j = 1; j<k; j++) {				
+				fvs[k][j] = positions.length/3;				
+				positions.push(positions[3 * fvs[k][0]] + j*d.x, positions[3 * fvs[k][0] + 1] + j*d.y, positions[3 * fvs[k][0] + 2] + j*d.z);
+				uvs.push(uvs[2*fvs[k][0]] + j*u.x, uvs[2*fvs[k][0] + 1] + j*u.y);
+			}		
+		}
+		fvs[gaps] = side[meshIndices[i + 1]][meshIndices[i + 2]];
+		
+		indices.push(fvs[0][0],fvs[1][0],fvs[1][1]);
+		for(var k = 1; k<gaps; k++) {
+			for(var j = 0; j<k; j++) {			
+				indices.push(fvs[k][j],fvs[k+1][j],fvs[k+1][j+1]);
+				indices.push(fvs[k][j],fvs[k+1][j+1],fvs[k][j+1]);
+			}		
+			indices.push(fvs[k][j],fvs[k+1][j],fvs[k+1][j+1]);
+		}
+
+	}							
+
+	var vertexData = new BABYLON.VertexData();
+	vertexData.positions = positions;
+	vertexData.indices = indices;
+	vertexData.uvs = uvs;
+	
+	BABYLON.VertexData.ComputeNormals(positions, indices, normals);
+	vertexData.normals = normals;
+	mesh.dispose();
+	var newmesh = new BABYLON.Mesh("newmesh", scene);	
+	vertexData.applyToMesh(newmesh);
+
+	return newmesh;
+}
+```
+
+For sharp edged meshes such as a box the shader can separate the faces since the faces meeting at the corners have there own vertices and normals at these vertices. 
+These meshes are flat shaded. If this separation of the edges is a problem then the function convertToSmoothShadedMesh() can be used.
+However this can then produce some artefacts at the edges.
+
+```
+function convertToSmoothShadedMesh(mesh) {
+	var meshIndices = mesh.getIndices();
+	var meshPositions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
+	var mesh_uvs = mesh.getVerticesData(BABYLON.VertexBuffer.UVKind);
+	var setPositions = [];
+	var indices = [];
+	var positions = [];
+	var uvs = [];
+	var normals = [];
+	var p;
+	var indexMap = [];
+	for(var i=0; i<meshPositions.length; i+=3) {
+		var temp =[];
+		temp.push(i/3, meshPositions[i], meshPositions[i + 1], meshPositions[i + 2], mesh_uvs[2*i/3], mesh_uvs[2*i/3 + 1]);
+		setPositions.push(temp);
+	}	
+	var i=0;
+	while(setPositions.length>0) {
+		p = setPositions.shift();
+		positions.push(p[1],p[2],p[3]);
+		uvs.push(p[4],p[5]);
+		indexMap[p[0]] = i;		
+		var j = 0;
+		while(j<setPositions.length) {		
+			if (Math.abs(p[1] - setPositions[j][1])<Math.pow(0.1, 10) && Math.abs(p[2] - setPositions[j][2])<Math.pow(0.1, 10) && Math.abs(p[3] - setPositions[j][3])<Math.pow(0.1, 10) ) {
+				indexMap[setPositions[j][0]] = i;			
+				setPositions.splice(j,1);
+			}
+			else {
+				j++;
+			}
+		}
+		i++;
+	}	
+	for(var i=0; i<meshIndices.length; i++) {
+		indices.push(indexMap[meshIndices[i]]);
+	}
+	
+	var vertexData = new BABYLON.VertexData();
+	vertexData.positions = positions;
+	vertexData.indices = indices;
+	vertexData.uvs = uvs;
+	
+	BABYLON.VertexData.ComputeNormals(positions, indices, normals);
+	vertexData.normals = normals;
+	vertexData.applyToMesh(mesh);
+
+	return mesh;
+	
+}
+```

+ 54 - 0
materialsLibrary/test/add/addfur.js

@@ -0,0 +1,54 @@
+window.prepareFur = function() {
+	var fur = new BABYLON.FurMaterial("fur", scene);
+	fur.furLength = 1;
+	fur.furAngle = 0;
+    fur.furColor = new BABYLON.Color3(0.44,0.21,0.02);
+
+
+// fur length
+    registerRangeUI("fur", "Fur length", 0, 15, function(value) {
+        fur.furLength = value;
+    }, function() {
+        return fur.furLength;
+    });
+	
+	// fur angle
+    registerRangeUI("fur", "Fur angle", 0, Math.PI/2, function(value) {
+        fur.furAngle = value;
+    }, function() {
+        return fur.furAngle;
+    });
+    
+    // fur color
+	registerColorPicker("fur", "Fur color", "#703605", function(value) {		
+		fur.furColor.r = value.r/255;
+		fur.furColor.g = value.g/255;
+		fur.furColor.b = value.b/255;
+	}, function() {
+		return fur.furColor;
+	});
+	
+	var DTON = false;
+	registerButtonUI("fur", "Tgl Diffuse Tex", function() {
+		DTON = !DTON;
+		if(DTON) {
+			fur.diffuseTexture = new BABYLON.Texture("textures/leopard_fur.jpg", scene);
+		}
+		else {
+			fur.diffuseTexture = null;
+		}
+	})
+	
+	var HTON = false;
+	registerButtonUI("fur", "Tgl Height Tex", function() {
+		HTON = !HTON;
+		if(HTON) {
+			fur.heightTexture = new BABYLON.Texture("textures/speckles.jpg", scene);
+		}
+		else {
+			fur.heightTexture = null;
+		}
+	})
+    
+    return fur;
+};

+ 173 - 88
materialsLibrary/test/babylon.max.js

@@ -4034,10 +4034,10 @@ var BABYLON;
         // Misc.
         Tools.GetPointerPrefix = function () {
             var eventPrefix = "pointer";
-            // Check if hand.js is referenced or if the browser natively supports pointer events
-            //if (!navigator.pointerEnabled) {
-            //    eventPrefix = "mouse";
-            //}
+            // Check if pointer events are supported
+            if (!window.PointerEvent && !navigator.pointerEnabled) {
+                eventPrefix = "mouse";
+            }
             return eventPrefix;
         };
         Tools.QueueNewFrame = function (func) {
@@ -11275,7 +11275,7 @@ var BABYLON;
                     _this._previousPosition.copyFrom(_this.position);
                 }
                 else {
-                    _this.setPosition(_this.position);
+                    _this.setPosition(newPosition);
                     if (_this.onCollide) {
                         _this.onCollide(collidedMesh);
                     }
@@ -12818,6 +12818,38 @@ var BABYLON;
             return null;
         };
         /**
+         * get a bone using its id
+         * @param {string} the bone's id
+         * @return {BABYLON.Bone|null} the bone or null if not found
+         */
+        Scene.prototype.getBoneByID = function (id) {
+            for (var skeletonIndex = 0; skeletonIndex < this.skeletons.length; skeletonIndex++) {
+                var skeleton = this.skeletons[skeletonIndex];
+                for (var boneIndex = 0; boneIndex < skeleton.bones.length; boneIndex++) {
+                    if (skeleton.bones[boneIndex].id === id) {
+                        return skeleton.bones[boneIndex];
+                    }
+                }
+            }
+            return null;
+        };
+        /**
+        * get a bone using its id
+        * @param {string} the bone's name
+        * @return {BABYLON.Bone|null} the bone or null if not found
+        */
+        Scene.prototype.getBoneByName = function (name) {
+            for (var skeletonIndex = 0; skeletonIndex < this.skeletons.length; skeletonIndex++) {
+                var skeleton = this.skeletons[skeletonIndex];
+                for (var boneIndex = 0; boneIndex < skeleton.bones.length; boneIndex++) {
+                    if (skeleton.bones[boneIndex].name === name) {
+                        return skeleton.bones[boneIndex];
+                    }
+                }
+            }
+            return null;
+        };
+        /**
          * get a light node using its name
          * @param {string} the light's name
          * @return {BABYLON.Light|null} the light or null if none found.
@@ -12980,7 +13012,12 @@ var BABYLON;
             if (light) {
                 return light;
             }
-            return this.getCameraByID(id);
+            var camera = this.getCameraByID(id);
+            if (camera) {
+                return camera;
+            }
+            var bone = this.getBoneByID(id);
+            return bone;
         };
         Scene.prototype.getNodeByName = function (name) {
             var mesh = this.getMeshByName(name);
@@ -12991,7 +13028,12 @@ var BABYLON;
             if (light) {
                 return light;
             }
-            return this.getCameraByName(name);
+            var camera = this.getCameraByName(name);
+            if (camera) {
+                return camera;
+            }
+            var bone = this.getBoneByName(name);
+            return bone;
         };
         Scene.prototype.getMeshByName = function (name) {
             for (var index = 0; index < this.meshes.length; index++) {
@@ -14216,6 +14258,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(InstancedMesh.prototype, "renderingGroupId", {
+            get: function () {
+                return this._sourceMesh.renderingGroupId;
+            },
+            enumerable: true,
+            configurable: true
+        });
         InstancedMesh.prototype.getTotalVertices = function () {
             return this._sourceMesh.getTotalVertices();
         };
@@ -14226,8 +14275,8 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
-        InstancedMesh.prototype.getVerticesData = function (kind) {
-            return this._sourceMesh.getVerticesData(kind);
+        InstancedMesh.prototype.getVerticesData = function (kind, copyWhenShared) {
+            return this._sourceMesh.getVerticesData(kind, copyWhenShared);
         };
         InstancedMesh.prototype.isVerticesDataPresent = function (kind) {
             return this._sourceMesh.isVerticesDataPresent(kind);
@@ -15572,6 +15621,14 @@ var BABYLON;
         };
         // Cylinder and cone
         Mesh.CreateCylinder = function (name, height, diameterTop, diameterBottom, tessellation, subdivisions, scene, updatable, sideOrientation) {
+            if (scene === undefined || !(scene instanceof BABYLON.Scene)) {
+                if (scene !== undefined) {
+                    sideOrientation = updatable || Mesh.DEFAULTSIDE;
+                    updatable = scene;
+                }
+                scene = subdivisions;
+                subdivisions = 1;
+            }
             var options = {
                 height: height,
                 diameterTop: diameterTop,
@@ -16891,6 +16948,8 @@ var BABYLON;
             if (this._texture === undefined) {
                 return;
             }
+            // Release
+            this.releaseInternalTexture();
             // Callback
             if (this.onDispose) {
                 this.onDispose();
@@ -17402,6 +17461,9 @@ var BABYLON;
         };
         RenderTargetTexture.prototype.render = function (useCameraPostProcess, dumpForDebug) {
             var scene = this.getScene();
+            if (this.activeCamera && this.activeCamera !== scene.activeCamera) {
+                scene.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix(true));
+            }
             if (this._waitingRenderList) {
                 this.renderList = [];
                 for (var index = 0; index < this._waitingRenderList.length; index++) {
@@ -17445,6 +17507,9 @@ var BABYLON;
             if (this.onAfterUnbind) {
                 this.onAfterUnbind();
             }
+            if (this.activeCamera && this.activeCamera !== scene.activeCamera) {
+                scene.setTransformMatrix(scene.activeCamera.getViewMatrix(), scene.activeCamera.getProjectionMatrix(true));
+            }
             scene.resetCachedMaterial();
         };
         RenderTargetTexture.prototype.renderToTarget = function (faceIndex, currentRenderList, useCameraPostProcess, dumpForDebug) {
@@ -18755,6 +18820,8 @@ var BABYLON;
             serializationObject.id = this.id;
             serializationObject.tags = BABYLON.Tags.GetTags(this);
             serializationObject.backFaceCulling = this.backFaceCulling;
+            serializationObject.checkReadyOnlyOnce = this.checkReadyOnlyOnce;
+            serializationObject.disableDepthWrite = this.disableDepthWrite;
             return serializationObject;
         };
         Material.ParseMultiMaterial = function (parsedMultiMaterial, scene) {
@@ -19490,51 +19557,53 @@ var BABYLON;
                         this._effect.setColor4("emissiveRightColor", this.emissiveFresnelParameters.rightColor, this.emissiveFresnelParameters.bias);
                     }
                 }
-                // Textures        
-                if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
-                    this._effect.setTexture("diffuseSampler", this.diffuseTexture);
-                    this._effect.setFloat2("vDiffuseInfos", this.diffuseTexture.coordinatesIndex, this.diffuseTexture.level);
-                    this._effect.setMatrix("diffuseMatrix", this.diffuseTexture.getTextureMatrix());
-                }
-                if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
-                    this._effect.setTexture("ambientSampler", this.ambientTexture);
-                    this._effect.setFloat2("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level);
-                    this._effect.setMatrix("ambientMatrix", this.ambientTexture.getTextureMatrix());
-                }
-                if (this.opacityTexture && StandardMaterial.OpacityTextureEnabled) {
-                    this._effect.setTexture("opacitySampler", this.opacityTexture);
-                    this._effect.setFloat2("vOpacityInfos", this.opacityTexture.coordinatesIndex, this.opacityTexture.level);
-                    this._effect.setMatrix("opacityMatrix", this.opacityTexture.getTextureMatrix());
-                }
-                if (this.reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
-                    if (this.reflectionTexture.isCube) {
-                        this._effect.setTexture("reflectionCubeSampler", this.reflectionTexture);
+                // Textures     
+                if (scene.texturesEnabled) {
+                    if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
+                        this._effect.setTexture("diffuseSampler", this.diffuseTexture);
+                        this._effect.setFloat2("vDiffuseInfos", this.diffuseTexture.coordinatesIndex, this.diffuseTexture.level);
+                        this._effect.setMatrix("diffuseMatrix", this.diffuseTexture.getTextureMatrix());
                     }
-                    else {
-                        this._effect.setTexture("reflection2DSampler", this.reflectionTexture);
+                    if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
+                        this._effect.setTexture("ambientSampler", this.ambientTexture);
+                        this._effect.setFloat2("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level);
+                        this._effect.setMatrix("ambientMatrix", this.ambientTexture.getTextureMatrix());
+                    }
+                    if (this.opacityTexture && StandardMaterial.OpacityTextureEnabled) {
+                        this._effect.setTexture("opacitySampler", this.opacityTexture);
+                        this._effect.setFloat2("vOpacityInfos", this.opacityTexture.coordinatesIndex, this.opacityTexture.level);
+                        this._effect.setMatrix("opacityMatrix", this.opacityTexture.getTextureMatrix());
+                    }
+                    if (this.reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
+                        if (this.reflectionTexture.isCube) {
+                            this._effect.setTexture("reflectionCubeSampler", this.reflectionTexture);
+                        }
+                        else {
+                            this._effect.setTexture("reflection2DSampler", this.reflectionTexture);
+                        }
+                        this._effect.setMatrix("reflectionMatrix", this.reflectionTexture.getReflectionTextureMatrix());
+                        this._effect.setFloat2("vReflectionInfos", this.reflectionTexture.level, this.roughness);
+                    }
+                    if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
+                        this._effect.setTexture("emissiveSampler", this.emissiveTexture);
+                        this._effect.setFloat2("vEmissiveInfos", this.emissiveTexture.coordinatesIndex, this.emissiveTexture.level);
+                        this._effect.setMatrix("emissiveMatrix", this.emissiveTexture.getTextureMatrix());
+                    }
+                    if (this.lightmapTexture && StandardMaterial.LightmapEnabled) {
+                        this._effect.setTexture("lightmapSampler", this.lightmapTexture);
+                        this._effect.setFloat2("vLightmapInfos", this.lightmapTexture.coordinatesIndex, this.lightmapTexture.level);
+                        this._effect.setMatrix("lightmapMatrix", this.lightmapTexture.getTextureMatrix());
+                    }
+                    if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
+                        this._effect.setTexture("specularSampler", this.specularTexture);
+                        this._effect.setFloat2("vSpecularInfos", this.specularTexture.coordinatesIndex, this.specularTexture.level);
+                        this._effect.setMatrix("specularMatrix", this.specularTexture.getTextureMatrix());
+                    }
+                    if (this.bumpTexture && scene.getEngine().getCaps().standardDerivatives && StandardMaterial.BumpTextureEnabled) {
+                        this._effect.setTexture("bumpSampler", this.bumpTexture);
+                        this._effect.setFloat2("vBumpInfos", this.bumpTexture.coordinatesIndex, 1.0 / this.bumpTexture.level);
+                        this._effect.setMatrix("bumpMatrix", this.bumpTexture.getTextureMatrix());
                     }
-                    this._effect.setMatrix("reflectionMatrix", this.reflectionTexture.getReflectionTextureMatrix());
-                    this._effect.setFloat2("vReflectionInfos", this.reflectionTexture.level, this.roughness);
-                }
-                if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
-                    this._effect.setTexture("emissiveSampler", this.emissiveTexture);
-                    this._effect.setFloat2("vEmissiveInfos", this.emissiveTexture.coordinatesIndex, this.emissiveTexture.level);
-                    this._effect.setMatrix("emissiveMatrix", this.emissiveTexture.getTextureMatrix());
-                }
-                if (this.lightmapTexture && StandardMaterial.LightmapEnabled) {
-                    this._effect.setTexture("lightmapSampler", this.lightmapTexture);
-                    this._effect.setFloat2("vLightmapInfos", this.lightmapTexture.coordinatesIndex, this.lightmapTexture.level);
-                    this._effect.setMatrix("lightmapMatrix", this.lightmapTexture.getTextureMatrix());
-                }
-                if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
-                    this._effect.setTexture("specularSampler", this.specularTexture);
-                    this._effect.setFloat2("vSpecularInfos", this.specularTexture.coordinatesIndex, this.specularTexture.level);
-                    this._effect.setMatrix("specularMatrix", this.specularTexture.getTextureMatrix());
-                }
-                if (this.bumpTexture && scene.getEngine().getCaps().standardDerivatives && StandardMaterial.BumpTextureEnabled) {
-                    this._effect.setTexture("bumpSampler", this.bumpTexture);
-                    this._effect.setFloat2("vBumpInfos", this.bumpTexture.coordinatesIndex, 1.0 / this.bumpTexture.level);
-                    this._effect.setMatrix("bumpMatrix", this.bumpTexture.getTextureMatrix());
                 }
                 // Clip plane
                 if (scene.clipPlane) {
@@ -19838,10 +19907,16 @@ var BABYLON;
             }
             return true;
         };
-        MultiMaterial.prototype.clone = function (name) {
+        MultiMaterial.prototype.clone = function (name, cloneChildren) {
             var newMultiMaterial = new MultiMaterial(name, this.getScene());
             for (var index = 0; index < this.subMaterials.length; index++) {
-                var subMaterial = this.subMaterials[index];
+                var subMaterial = null;
+                if (cloneChildren) {
+                    subMaterial = this.subMaterials[index].clone(name + "-" + this.subMaterials[index].name);
+                }
+                else {
+                    subMaterial = this.subMaterials[index];
+                }
                 newMultiMaterial.subMaterials.push(subMaterial);
             }
             return newMultiMaterial;
@@ -20050,7 +20125,7 @@ var BABYLON;
     var Internals;
     (function (Internals) {
         var parseMaterialById = function (id, parsedData, scene, rootUrl) {
-            for (var index = 0; index < parsedData.materials.length; index++) {
+            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);
@@ -20080,7 +20155,8 @@ var BABYLON;
                 var loadedMaterialsIds = [];
                 var hierarchyIds = [];
                 var index;
-                for (index = 0; index < parsedData.meshes.length; 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) {
@@ -20140,16 +20216,16 @@ var BABYLON;
                         if (parsedMesh.materialId) {
                             var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
                             if (!materialFound && parsedData.multiMaterials) {
-                                for (var multimatIndex = 0; multimatIndex < parsedData.multiMaterials.length; multimatIndex++) {
+                                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; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
+                                        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);
-                                        parsedMultiMaterial.Parse(parsedMultiMaterial, scene);
+                                        BABYLON.Material.ParseMultiMaterial(parsedMultiMaterial, scene);
                                         materialFound = true;
                                         break;
                                     }
@@ -20166,7 +20242,7 @@ var BABYLON;
                         if (parsedMesh.skeletonId > -1 && scene.skeletons) {
                             var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
                             if (!skeletonAlreadyLoaded) {
-                                for (var skeletonIndex = 0; skeletonIndex < parsedData.skeletons.length; skeletonIndex++) {
+                                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));
@@ -20181,7 +20257,7 @@ var BABYLON;
                 }
                 // Connecting parents
                 var currentMesh;
-                for (index = 0; index < scene.meshes.length; index++) {
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     currentMesh = scene.meshes[index];
                     if (currentMesh._waitingParentId) {
                         currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
@@ -20189,7 +20265,7 @@ var BABYLON;
                     }
                 }
                 // freeze world matrix application
-                for (index = 0; index < scene.meshes.length; index++) {
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     currentMesh = scene.meshes[index];
                     if (currentMesh._waitingFreezeWorldMatrix) {
                         currentMesh.freezeWorldMatrix();
@@ -20198,7 +20274,7 @@ var BABYLON;
                 }
                 // Particles
                 if (parsedData.particleSystems) {
-                    for (index = 0; index < parsedData.particleSystems.length; index++) {
+                    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));
@@ -20243,28 +20319,29 @@ var BABYLON;
                     scene.collisionsEnabled = parsedData.collisionsEnabled;
                 }
                 scene.workerCollisions = !!parsedData.workerCollisions;
-                // Lights
                 var index;
-                for (index = 0; index < parsedData.lights.length; index++) {
+                var cache;
+                // Lights
+                for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
                     var parsedLight = parsedData.lights[index];
                     BABYLON.Light.Parse(parsedLight, scene);
                 }
                 // Materials
                 if (parsedData.materials) {
-                    for (index = 0; index < parsedData.materials.length; index++) {
+                    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; index < parsedData.multiMaterials.length; index++) {
+                    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; index < parsedData.skeletons.length; index++) {
+                    for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
                         var parsedSkeleton = parsedData.skeletons[index];
                         BABYLON.Skeleton.Parse(parsedSkeleton, scene);
                     }
@@ -20275,7 +20352,7 @@ var BABYLON;
                     // Boxes
                     var boxes = geometries.boxes;
                     if (boxes) {
-                        for (index = 0; index < boxes.length; index++) {
+                        for (index = 0, cache = boxes.length; index < cache; index++) {
                             var parsedBox = boxes[index];
                             BABYLON.Geometry.Primitives.Box.Parse(parsedBox, scene);
                         }
@@ -20283,7 +20360,7 @@ var BABYLON;
                     // Spheres
                     var spheres = geometries.spheres;
                     if (spheres) {
-                        for (index = 0; index < spheres.length; index++) {
+                        for (index = 0, cache = spheres.length; index < cache; index++) {
                             var parsedSphere = spheres[index];
                             BABYLON.Geometry.Primitives.Sphere.Parse(parsedSphere, scene);
                         }
@@ -20291,7 +20368,7 @@ var BABYLON;
                     // Cylinders
                     var cylinders = geometries.cylinders;
                     if (cylinders) {
-                        for (index = 0; index < cylinders.length; index++) {
+                        for (index = 0, cache = cylinders.length; index < cache; index++) {
                             var parsedCylinder = cylinders[index];
                             BABYLON.Geometry.Primitives.Cylinder.Parse(parsedCylinder, scene);
                         }
@@ -20299,7 +20376,7 @@ var BABYLON;
                     // Toruses
                     var toruses = geometries.toruses;
                     if (toruses) {
-                        for (index = 0; index < toruses.length; index++) {
+                        for (index = 0, cache = toruses.length; index < cache; index++) {
                             var parsedTorus = toruses[index];
                             BABYLON.Geometry.Primitives.Torus.Parse(parsedTorus, scene);
                         }
@@ -20307,7 +20384,7 @@ var BABYLON;
                     // Grounds
                     var grounds = geometries.grounds;
                     if (grounds) {
-                        for (index = 0; index < grounds.length; index++) {
+                        for (index = 0, cache = grounds.length; index < cache; index++) {
                             var parsedGround = grounds[index];
                             BABYLON.Geometry.Primitives.Ground.Parse(parsedGround, scene);
                         }
@@ -20315,7 +20392,7 @@ var BABYLON;
                     // Planes
                     var planes = geometries.planes;
                     if (planes) {
-                        for (index = 0; index < planes.length; index++) {
+                        for (index = 0, cache = planes.length; index < cache; index++) {
                             var parsedPlane = planes[index];
                             BABYLON.Geometry.Primitives.Plane.Parse(parsedPlane, scene);
                         }
@@ -20323,7 +20400,7 @@ var BABYLON;
                     // TorusKnots
                     var torusKnots = geometries.torusKnots;
                     if (torusKnots) {
-                        for (index = 0; index < torusKnots.length; index++) {
+                        for (index = 0, cache = torusKnots.length; index < cache; index++) {
                             var parsedTorusKnot = torusKnots[index];
                             BABYLON.Geometry.Primitives.TorusKnot.Parse(parsedTorusKnot, scene);
                         }
@@ -20331,19 +20408,19 @@ var BABYLON;
                     // VertexData
                     var vertexData = geometries.vertexData;
                     if (vertexData) {
-                        for (index = 0; index < vertexData.length; index++) {
+                        for (index = 0, cache = vertexData.length; index < cache; index++) {
                             var parsedVertexData = vertexData[index];
                             BABYLON.Geometry.Parse(parsedVertexData, scene, rootUrl);
                         }
                     }
                 }
                 // Meshes
-                for (index = 0; index < parsedData.meshes.length; index++) {
+                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; index < parsedData.cameras.length; index++) {
+                for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
                     var parsedCamera = parsedData.cameras[index];
                     BABYLON.Camera.Parse(parsedCamera, scene);
                 }
@@ -20351,14 +20428,14 @@ var BABYLON;
                     scene.setActiveCameraByID(parsedData.activeCameraID);
                 }
                 // Browsing all the graph to connect the dots
-                for (index = 0; index < scene.cameras.length; index++) {
+                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; index < scene.lights.length; index++) {
+                for (index = 0, cache = scene.lights.length; index < cache; index++) {
                     var light = scene.lights[index];
                     if (light._waitingParentId) {
                         light.parent = scene.getLastEntryByID(light._waitingParentId);
@@ -20367,7 +20444,7 @@ var BABYLON;
                 }
                 // Sounds
                 if (BABYLON.AudioEngine && parsedData.sounds) {
-                    for (index = 0; index < parsedData.sounds.length; index++) {
+                    for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
                         var parsedSound = parsedData.sounds[index];
                         if (BABYLON.Engine.audioEngine.canUseWebAudio) {
                             BABYLON.Sound.Parse(parsedSound, scene, rootUrl);
@@ -20378,7 +20455,7 @@ var BABYLON;
                     }
                 }
                 // Connect parents & children and parse actions
-                for (index = 0; index < scene.meshes.length; index++) {
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     var mesh = scene.meshes[index];
                     if (mesh._waitingParentId) {
                         mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
@@ -20390,7 +20467,7 @@ var BABYLON;
                     }
                 }
                 // freeze world matrix application
-                for (index = 0; index < scene.meshes.length; index++) {
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     var currentMesh = scene.meshes[index];
                     if (currentMesh._waitingFreezeWorldMatrix) {
                         currentMesh.freezeWorldMatrix();
@@ -20399,21 +20476,21 @@ var BABYLON;
                 }
                 // Particles Systems
                 if (parsedData.particleSystems) {
-                    for (index = 0; index < parsedData.particleSystems.length; index++) {
+                    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; index < parsedData.lensFlareSystems.length; index++) {
+                    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; index < parsedData.shadowGenerators.length; index++) {
+                    for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
                         var parsedShadowGenerator = parsedData.shadowGenerators[index];
                         BABYLON.ShadowGenerator.Parse(parsedShadowGenerator, scene);
                     }
@@ -30221,10 +30298,14 @@ var BABYLON;
             var serializationInstance = {
                 name: instance.name,
                 position: instance.position.asArray(),
-                rotation: instance.rotation.asArray(),
-                rotationQuaternion: instance.rotationQuaternion.asArray(),
                 scaling: instance.scaling.asArray()
             };
+            if (instance.rotationQuaternion) {
+                serializationInstance.rotationQuaternion = instance.rotationQuaternion.asArray();
+            }
+            else if (instance.rotation) {
+                serializationInstance.rotation = instance.rotation.asArray();
+            }
             serializationObject.instances.push(serializationInstance);
             // Animations
             BABYLON.Animation.AppendSerializedAnimations(instance, serializationInstance);
@@ -36261,6 +36342,10 @@ var BABYLON;
                 // Remove from the scene if found 
                 this._scene.reflectionProbes.splice(index, 1);
             }
+            if (this._renderTargetTexture) {
+                this._renderTargetTexture.dispose();
+                this._renderTargetTexture = null;
+            }
         };
         return ReflectionProbe;
     })();

+ 9 - 2
materialsLibrary/test/index.html

@@ -11,6 +11,7 @@
     <script src="../dist/babylon.lavaMaterial.js"></script>
 	<script src="../dist/babylon.terrainMaterial.js"></script>
 	<script src="../dist/babylon.pbrMaterial.js"></script>
+	<script src="../dist/babylon.furMaterial.js"></script>
 
 	<style>
 		html, body {
@@ -49,6 +50,7 @@
     <script src="add/addlava.js"></script>
     <script src="add/addnormal.js"></script>
 	<script src="add/addwater.js"></script>
+	<script src="add/addfur.js"></script>
 	
 	<script>
 		if (BABYLON.Engine.isSupported()) {
@@ -170,6 +172,8 @@
 
 				var normal = prepareNormal();
 				
+				var fur = prepareFur();
+				
 				var water = prepareWater();
 				water.addToRenderList(skybox);
 				water.addToRenderList(shadowCaster);
@@ -204,7 +208,7 @@
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur']).onFinishChange(function () {
 					water.enableRenderTargets(false);
 					
 					switch (options.material) {
@@ -230,7 +234,10 @@
 							break;
 						case "pbr":
 							currentMaterial = pbr;
-							break;													
+							break;
+						case "fur":
+							currentMaterial = fur;
+							break;														
 						default:
 							currentMaterial = std;
 							break;

BIN
materialsLibrary/test/textures/leopard_fur.JPG


BIN
materialsLibrary/test/textures/speckles.jpg


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1286 - 0
pbr/babylon.pbrMaterial.ts


+ 765 - 0
pbr/legacypbr.fragment.fx

@@ -0,0 +1,765 @@
+precision mediump float;
+
+// Constants
+#define RECIPROCAL_PI2 0.15915494
+#define FRESNEL_MAXIMUM_ON_ROUGH 0.25
+
+uniform vec3 vEyePosition;
+uniform vec3 vAmbientColor;
+uniform vec4 vDiffuseColor;
+uniform vec3 vReflectionColor;
+
+// CUSTOM CONTROLS
+uniform vec4 vLightingIntensity;
+uniform vec4 vCameraInfos;
+
+#ifdef OVERLOADEDVALUES
+uniform vec4 vOverloadedIntensity;
+uniform vec3 vOverloadedAmbient;
+uniform vec3 vOverloadedDiffuse;
+uniform vec3 vOverloadedSpecular;
+uniform vec3 vOverloadedEmissive;
+uniform vec3 vOverloadedReflection;
+uniform vec3 vOverloadedGlossiness;
+#endif
+
+#ifdef OVERLOADEDSHADOWVALUES
+uniform vec4 vOverloadedShadowIntensity;
+#endif
+
+// PBR CUSTOM CONSTANTS
+const float kPi = 3.1415926535897932384626433832795;
+
+// 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;
+}
+
+// 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;
+}
+
+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;
+}
+
+float computeDefaultGlossiness(float glossiness, vec3 specularColor)
+{
+    if (glossiness == 0.)
+    {
+        float kSpecularNoAlphaWorkflow_SmoothnessMax = 0.95;
+
+        float specularLuminance = getLuminance(specularColor);
+        float specularLuma = sqrt(specularLuminance);
+        glossiness = specularLuma * kSpecularNoAlphaWorkflow_SmoothnessMax;
+    }
+    return glossiness;
+}
+
+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));
+}
+
+#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
+        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
+
+#ifdef SPECULARTERM
+uniform vec4 vSpecularColor;
+#endif
+uniform vec3 vEmissiveColor;
+
+// Input
+varying vec3 vPositionW;
+
+#ifdef NORMAL
+varying vec3 vNormalW;
+#endif
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+// Lights
+#ifdef LIGHT0
+uniform vec4 vLightData0;
+uniform vec4 vLightDiffuse0;
+#ifdef SPECULARTERM
+uniform vec3 vLightSpecular0;
+#endif
+#ifdef SHADOW0
+#if defined(SPOTLIGHT0) || defined(DIRLIGHT0)
+varying vec4 vPositionFromLight0;
+uniform sampler2D shadowSampler0;
+#else
+uniform samplerCube shadowSampler0;
+#endif
+uniform vec3 shadowsInfo0;
+#endif
+#ifdef SPOTLIGHT0
+uniform vec4 vLightDirection0;
+#endif
+#ifdef HEMILIGHT0
+uniform vec3 vLightGround0;
+#endif
+#endif
+
+#ifdef LIGHT1
+uniform vec4 vLightData1;
+uniform vec4 vLightDiffuse1;
+#ifdef SPECULARTERM
+uniform vec3 vLightSpecular1;
+#endif
+#ifdef SHADOW1
+#if defined(SPOTLIGHT1) || defined(DIRLIGHT1)
+varying vec4 vPositionFromLight1;
+uniform sampler2D shadowSampler1;
+#else
+uniform samplerCube shadowSampler1;
+#endif
+uniform vec3 shadowsInfo1;
+#endif
+#ifdef SPOTLIGHT1
+uniform vec4 vLightDirection1;
+#endif
+#ifdef HEMILIGHT1
+uniform vec3 vLightGround1;
+#endif
+#endif
+
+#ifdef LIGHT2
+uniform vec4 vLightData2;
+uniform vec4 vLightDiffuse2;
+#ifdef SPECULARTERM
+uniform vec3 vLightSpecular2;
+#endif
+#ifdef SHADOW2
+#if defined(SPOTLIGHT2) || defined(DIRLIGHT2)
+varying vec4 vPositionFromLight2;
+uniform sampler2D shadowSampler2;
+#else
+uniform samplerCube shadowSampler2;
+#endif
+uniform vec3 shadowsInfo2;
+#endif
+#ifdef SPOTLIGHT2
+uniform vec4 vLightDirection2;
+#endif
+#ifdef HEMILIGHT2
+uniform vec3 vLightGround2;
+#endif
+#endif
+
+#ifdef LIGHT3
+uniform vec4 vLightData3;
+uniform vec4 vLightDiffuse3;
+#ifdef SPECULARTERM
+uniform vec3 vLightSpecular3;
+#endif
+#ifdef SHADOW3
+#if defined(SPOTLIGHT3) || defined(DIRLIGHT3)
+varying vec4 vPositionFromLight3;
+uniform sampler2D shadowSampler3;
+#else
+uniform samplerCube shadowSampler3;
+#endif
+uniform vec3 shadowsInfo3;
+#endif
+#ifdef SPOTLIGHT3
+uniform vec4 vLightDirection3;
+#endif
+#ifdef HEMILIGHT3
+uniform vec3 vLightGround3;
+#endif
+#endif
+
+// Samplers
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform sampler2D diffuseSampler;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform sampler2D ambientSampler;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY	
+varying vec2 vOpacityUV;
+uniform sampler2D opacitySampler;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform sampler2D emissiveSampler;
+#endif
+
+#ifdef LIGHTMAP
+varying vec2 vLightmapUV;
+uniform vec2 vLightmapInfos;
+uniform sampler2D lightmapSampler;
+#endif
+
+#if defined(SPECULAR) && defined(SPECULARTERM)
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform sampler2D specularSampler;
+#endif
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+// 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) {
+    lightingInfo result;
+
+    vec3 lightVectorW;
+    float attenuation = 1.0;
+    if (lightData.w == 0.)
+    {
+        vec3 direction = lightData.xyz - vPositionW;
+
+        attenuation = max(0., 1.0 - length(direction) / range);
+        lightVectorW = normalize(direction);
+    }
+    else
+    {
+        lightVectorW = normalize(-lightData.xyz);
+    }
+
+    // diffuse
+    vec3 H = normalize(viewDirectionW + lightVectorW);
+    float NdotL = max(0.00000000001, dot(vNormal, lightVectorW));
+    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 * specularColor * attenuation;
+#endif
+
+    return result;
+}
+
+lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float roughness, float NdotV) {
+    lightingInfo result;
+
+    vec3 direction = lightData.xyz - vPositionW;
+    vec3 lightVectorW = normalize(direction);
+    float attenuation = max(0., 1.0 - length(direction) / range);
+
+    // diffuse
+    float cosAngle = max(0.0000001, dot(-lightDirection.xyz, lightVectorW));
+    float spotAtten = 0.0;
+
+    if (cosAngle >= lightDirection.w)
+    {
+        cosAngle = max(0., pow(cosAngle, lightData.w));
+        spotAtten = clamp((cosAngle - lightDirection.w) / (1. - cosAngle), 0.0, 1.0);
+
+        // Diffuse
+        vec3 H = normalize(viewDirectionW - lightDirection.xyz);
+        float 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 * spotAtten;
+
+#ifdef SPECULARTERM
+        // Specular
+        float NdotH = max(0.00000000001, dot(vNormal, H));
+
+        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+        result.specular = specTerm * specularColor * attenuation * spotAtten;
+#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) {
+    lightingInfo result;
+
+    vec3 lightVectorW = normalize(lightData.xyz);
+
+    // Diffuse
+    float ndl = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
+    result.diffuse = mix(groundColor, diffuseColor, ndl);
+
+#ifdef SPECULARTERM
+    // Specular
+    vec3 H = normalize(viewDirectionW + lightVectorW);
+    float NdotH = max(0.00000000001, dot(vNormal, H));
+    float NdotL = max(0.00000000001, ndl);
+    float VdotH = clamp(0.00000000001, 1.0, dot(viewDirectionW, H));
+
+    vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, specularColor);
+    result.specular = specTerm * specularColor;
+#endif
+
+    return result;
+}
+
+void main(void) {
+    // Clip plane
+#ifdef CLIPPLANE
+    if (fClipDistance > 0.0)
+        discard;
+#endif
+
+    vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
+
+    // Base color
+    vec4 baseColor = vec4(1., 1., 1., 1.);
+    vec3 diffuseColor = vDiffuseColor.rgb;
+    
+    // Alpha
+    float alpha = vDiffuseColor.a;
+
+#ifdef DIFFUSE
+    baseColor = texture2D(diffuseSampler, vDiffuseUV);
+    baseColor = vec4(toLinearSpace(baseColor.rgb), baseColor.a);
+
+#ifdef ALPHATEST
+    if (baseColor.a < 0.4)
+        discard;
+#endif
+
+#ifdef ALPHAFROMDIFFUSE
+    alpha *= baseColor.a;
+#endif
+
+    baseColor.rgb *= vDiffuseInfos.y;
+#endif
+
+#ifdef VERTEXCOLOR
+    baseColor.rgb *= vColor.rgb;
+#endif
+
+#ifdef OVERLOADEDVALUES
+    baseColor.rgb = mix(baseColor.rgb, vOverloadedDiffuse, vOverloadedIntensity.y);
+    diffuseColor.rgb = mix(diffuseColor.rgb, vOverloadedDiffuse, vOverloadedIntensity.y);
+#endif
+
+    // Bump
+#ifdef NORMAL
+    vec3 normalW = normalize(vNormalW);
+#else
+    vec3 normalW = vec3(1.0, 1.0, 1.0);
+#endif
+
+    // Ambient color
+    vec3 baseAmbientColor = vec3(1., 1., 1.);
+
+#ifdef AMBIENT
+    baseAmbientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
+    #ifdef OVERLOADEDVALUES
+        baseAmbientColor.rgb = mix(baseAmbientColor.rgb, vOverloadedAmbient, vOverloadedIntensity.x);
+    #endif
+#endif
+
+    // Specular map
+#ifdef SPECULARTERM
+    float glossiness = vSpecularColor.a;
+    vec3 specularColor = vSpecularColor.rgb;
+
+    #ifdef OVERLOADEDVALUES
+        specularColor.rgb = mix(specularColor.rgb, vOverloadedSpecular, vOverloadedIntensity.z);
+    #endif
+
+    #ifdef SPECULAR
+            vec4 specularMapColor = texture2D(specularSampler, vSpecularUV);
+            specularColor = toLinearSpace(specularMapColor.rgb);
+
+        #ifdef OVERLOADEDVALUES
+                specularColor.rgb = mix(specularColor.rgb, vOverloadedSpecular, vOverloadedIntensity.z);
+        #endif
+
+        #ifdef GLOSSINESSFROMSPECULARMAP
+            glossiness = specularMapColor.a;
+        #else
+            glossiness = computeDefaultGlossiness(glossiness, specularColor);
+        #endif
+    #endif
+
+    #ifdef OVERLOADEDVALUES
+        glossiness = mix(glossiness, vOverloadedGlossiness.x, vOverloadedGlossiness.y);
+    #endif
+#else
+    float glossiness = 0.;
+    #ifdef OVERLOADEDVALUES
+        glossiness = mix(glossiness, vOverloadedGlossiness.x, vOverloadedGlossiness.y);
+    #endif
+
+    vec3 specularColor = vec3(0., 0., 0);
+    #ifdef OVERLOADEDVALUES
+            specularColor.rgb = mix(specularColor.rgb, vOverloadedSpecular, vOverloadedIntensity.z);
+    #endif
+#endif
+
+    // Apply Energy Conservation taking in account the environment level only if the environment is present.
+    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
+    baseColor.rgb = (1. - reflectance) * baseColor.rgb;
+
+    // Compute Specular Fresnel + Reflectance.
+    float NdotV = max(0.00000000001, dot(normalW, viewDirectionW));
+
+    // Adapt glossiness.
+    glossiness = clamp(glossiness, 0., 1.) * 0.98;
+
+    // Call rough to not conflict with previous one.
+    float rough = clamp(1. - glossiness, 0.000001, 1.0);
+
+    // Lighting
+    vec3 diffuseBase = vec3(0., 0., 0.);
+
+#ifdef OVERLOADEDSHADOWVALUES
+    vec3 shadowedOnlyDiffuseBase = vec3(1., 1., 1.);
+#endif
+
+#ifdef SPECULARTERM
+    vec3 specularBase = vec3(0., 0., 0.);
+#endif
+    float shadow = 1.;
+
+#ifdef LIGHT0
+#ifndef SPECULARTERM
+    vec3 vLightSpecular0 = vec3(0.0);
+#endif
+#ifdef SPOTLIGHT0
+    lightingInfo info = computeSpotLighting(viewDirectionW, normalW, vLightData0, vLightDirection0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, rough, NdotV);
+#endif
+#ifdef HEMILIGHT0
+    lightingInfo info = computeHemisphericLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightGround0, rough, NdotV);
+#endif
+#if defined(POINTLIGHT0) || defined(DIRLIGHT0)
+    lightingInfo info = computeLighting(viewDirectionW, normalW, vLightData0, vLightDiffuse0.rgb, vLightSpecular0, vLightDiffuse0.a, rough, NdotV);
+#endif
+
+    shadow = 1.;
+    diffuseBase += info.diffuse * shadow;
+#ifdef OVERLOADEDSHADOWVALUES
+    shadowedOnlyDiffuseBase *= shadow;
+#endif
+
+#ifdef SPECULARTERM
+    specularBase += info.specular * shadow;
+#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, rough, NdotV);
+#endif
+#ifdef HEMILIGHT1
+    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightGround1, rough, NdotV);
+#endif
+#if defined(POINTLIGHT1) || defined(DIRLIGHT1)
+    info = computeLighting(viewDirectionW, normalW, vLightData1, vLightDiffuse1.rgb, vLightSpecular1, vLightDiffuse1.a, rough, NdotV);
+#endif
+
+    shadow = 1.;
+    diffuseBase += info.diffuse * shadow;
+#ifdef OVERLOADEDSHADOWVALUES
+    shadowedOnlyDiffuseBase *= shadow;
+#endif
+
+#ifdef SPECULARTERM
+    specularBase += info.specular * shadow;
+#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, rough, NdotV);
+#endif
+#ifdef HEMILIGHT2
+    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightGround2, rough, NdotV);
+#endif
+#if defined(POINTLIGHT2) || defined(DIRLIGHT2)
+    info = computeLighting(viewDirectionW, normalW, vLightData2, vLightDiffuse2.rgb, vLightSpecular2, vLightDiffuse2.a, rough, NdotV);
+#endif
+
+    shadow = 1.;
+    diffuseBase += info.diffuse * shadow;
+#ifdef OVERLOADEDSHADOWVALUES
+    shadowedOnlyDiffuseBase *= shadow;
+#endif
+
+#ifdef SPECULARTERM
+    specularBase += info.specular * shadow;
+#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, rough, NdotV);
+#endif
+#ifdef HEMILIGHT3
+    info = computeHemisphericLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightGround3, rough, NdotV);
+#endif
+#if defined(POINTLIGHT3) || defined(DIRLIGHT3)
+    info = computeLighting(viewDirectionW, normalW, vLightData3, vLightDiffuse3.rgb, vLightSpecular3, vLightDiffuse3.a, rough, NdotV);
+#endif
+
+    shadow = 1.;
+    diffuseBase += info.diffuse * shadow;
+#ifdef OVERLOADEDSHADOWVALUES
+    shadowedOnlyDiffuseBase *= shadow;
+#endif
+
+#ifdef SPECULARTERM
+    specularBase += info.specular * shadow;
+#endif
+#endif
+
+// Reflection
+vec3 reflectionColor = vReflectionColor.rgb;
+vec3 ambientReflectionColor = vReflectionColor.rgb;
+
+reflectionColor *= vLightingIntensity.z;
+ambientReflectionColor *= vLightingIntensity.z;
+
+// Compute reflection specular fresnel
+vec3 specularEnvironmentR0 = specularColor.rgb;
+vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0);
+vec3 specularEnvironmentReflectanceViewer = FresnelSchlickEnvironmentGGX(clamp(NdotV, 0., 1.), specularEnvironmentR0, specularEnvironmentR90, sqrt(glossiness));
+reflectionColor *= specularEnvironmentReflectanceViewer;
+
+#ifdef OVERLOADEDVALUES
+    ambientReflectionColor = mix(ambientReflectionColor, vOverloadedReflection, vOverloadedGlossiness.z);
+    reflectionColor = mix(reflectionColor, vOverloadedReflection, vOverloadedGlossiness.z);
+#endif
+
+#ifdef OPACITY
+    vec4 opacityMap = texture2D(opacitySampler, vOpacityUV);
+
+#ifdef OPACITYRGB
+    opacityMap.rgb = opacityMap.rgb * vec3(0.3, 0.59, 0.11);
+    alpha *= (opacityMap.x + opacityMap.y + opacityMap.z)* vOpacityInfos.y;
+#else
+    alpha *= opacityMap.a * vOpacityInfos.y;
+#endif
+
+#endif
+
+#ifdef VERTEXALPHA
+    alpha *= vColor.a;
+#endif
+
+    // Emissive
+    vec3 emissiveColor = vEmissiveColor;
+#ifdef EMISSIVE
+    vec3 emissiveColorTex = texture2D(emissiveSampler, vEmissiveUV).rgb;
+    emissiveColor = toLinearSpace(emissiveColorTex.rgb) * emissiveColor * vEmissiveInfos.y;
+#endif
+
+#ifdef OVERLOADEDVALUES
+    emissiveColor = mix(emissiveColor, vOverloadedEmissive, vOverloadedIntensity.w);
+#endif
+
+    // Composition
+#ifdef EMISSIVEASILLUMINATION
+    vec3 finalDiffuse = max(diffuseBase * diffuseColor + vAmbientColor, 0.0) * baseColor.rgb;
+
+    #ifdef OVERLOADEDSHADOWVALUES
+        shadowedOnlyDiffuseBase = max(shadowedOnlyDiffuseBase * diffuseColor + vAmbientColor, 0.0) * baseColor.rgb;
+    #endif
+#else
+    #ifdef LINKEMISSIVEWITHDIFFUSE
+        vec3 finalDiffuse = max((diffuseBase + emissiveColor) * diffuseColor + vAmbientColor, 0.0) * baseColor.rgb;
+        #ifdef OVERLOADEDSHADOWVALUES
+                shadowedOnlyDiffuseBase = max((shadowedOnlyDiffuseBase + emissiveColor) * diffuseColor + vAmbientColor, 0.0) * baseColor.rgb;
+        #endif
+    #else
+        vec3 finalDiffuse = max(diffuseBase * diffuseColor + emissiveColor + vAmbientColor, 0.0) * baseColor.rgb;
+        #ifdef OVERLOADEDSHADOWVALUES
+            shadowedOnlyDiffuseBase = max(shadowedOnlyDiffuseBase * diffuseColor + emissiveColor + vAmbientColor, 0.0) * baseColor.rgb;
+        #endif
+    #endif
+#endif
+
+#ifdef OVERLOADEDSHADOWVALUES
+      finalDiffuse = mix(finalDiffuse, shadowedOnlyDiffuseBase, (1.0 - vOverloadedShadowIntensity.y));
+#endif
+
+// diffuse lighting from environment 0.2 replaces Harmonic...
+// Ambient Reflection already includes the environment intensity.
+finalDiffuse += baseColor.rgb * ambientReflectionColor * 0.2;
+
+#ifdef SPECULARTERM
+    vec3 finalSpecular = specularBase * specularColor;
+#else
+    vec3 finalSpecular = vec3(0.0);
+#endif
+
+#ifdef SPECULAROVERALPHA
+    alpha = clamp(alpha + dot(finalSpecular, vec3(0.3, 0.59, 0.11)), 0., 1.);
+#endif
+
+// Composition
+// Reflection already includes the environment intensity.
+#ifdef EMISSIVEASILLUMINATION
+    vec4 color = vec4(finalDiffuse * baseAmbientColor * vLightingIntensity.x + finalSpecular * vLightingIntensity.x + reflectionColor + emissiveColor * vLightingIntensity.y, alpha);
+#else
+    vec4 color = vec4(finalDiffuse * baseAmbientColor * vLightingIntensity.x + finalSpecular * vLightingIntensity.x + reflectionColor, alpha);
+#endif
+
+    color = max(color, 0.0);
+
+#ifdef CAMERATONEMAP
+    color.rgb = toneMaps(color.rgb);
+#endif
+
+    color.rgb = toGammaSpace(color.rgb);
+
+#ifdef CAMERACONTRAST
+    color = contrasts(color);
+#endif
+
+    gl_FragColor = color;
+}

+ 186 - 0
pbr/legacypbr.vertex.fx

@@ -0,0 +1,186 @@
+precision mediump float;
+
+// Attributes
+attribute vec3 position;
+attribute vec3 normal;
+#ifdef UV1
+attribute vec2 uv;
+#endif
+#ifdef UV2
+attribute vec2 uv2;
+#endif
+#ifdef VERTEXCOLOR
+attribute vec4 color;
+#endif
+
+#if NUM_BONE_INFLUENCERS > 0
+uniform mat4 mBones[BonesPerMesh];
+
+attribute vec4 matricesIndices;
+attribute vec4 matricesWeights;
+#if NUM_BONE_INFLUENCERS > 4
+attribute vec4 matricesIndicesExtra;
+attribute vec4 matricesWeightsExtra;
+#endif
+#endif
+
+// Uniforms
+uniform mat4 world;
+uniform mat4 view;
+uniform mat4 viewProjection;
+
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform mat4 diffuseMatrix;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform mat4 ambientMatrix;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY
+varying vec2 vOpacityUV;
+uniform mat4 opacityMatrix;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform mat4 emissiveMatrix;
+#endif
+
+#if defined(SPECULAR) && defined(SPECULARTERM)
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform mat4 specularMatrix;
+#endif
+
+// Output
+varying vec3 vPositionW;
+varying vec3 vNormalW;
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+void main(void) {
+    mat4 finalWorld = world;
+
+#if NUM_BONE_INFLUENCERS > 0
+    mat4 influence;
+    influence = mBones[int(matricesIndices[0])] * matricesWeights[0];
+
+#if NUM_BONE_INFLUENCERS > 1
+    influence += mBones[int(matricesIndices[1])] * matricesWeights[1];
+#endif 
+#if NUM_BONE_INFLUENCERS > 2
+    influence += mBones[int(matricesIndices[2])] * matricesWeights[2];
+#endif	
+#if NUM_BONE_INFLUENCERS > 3
+    influence += mBones[int(matricesIndices[3])] * matricesWeights[3];
+#endif	
+
+#if NUM_BONE_INFLUENCERS > 4
+    influence += mBones[int(matricesIndicesExtra[0])] * matricesWeightsExtra[0];
+#endif
+#if NUM_BONE_INFLUENCERS > 5
+    influence += mBones[int(matricesIndicesExtra[1])] * matricesWeightsExtra[1];
+#endif	
+#if NUM_BONE_INFLUENCERS > 6
+    influence += mBones[int(matricesIndicesExtra[2])] * matricesWeightsExtra[2];
+#endif	
+#if NUM_BONE_INFLUENCERS > 7
+    influence += mBones[int(matricesIndicesExtra[3])] * matricesWeightsExtra[3];
+#endif	
+
+    finalWorld = finalWorld * influence;
+#endif
+
+	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
+
+	vec4 worldPos = finalWorld * vec4(position, 1.0);
+	vPositionW = vec3(worldPos);
+	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
+
+	// Texture coordinates
+#ifndef UV1
+	vec2 uv = vec2(0., 0.);
+#endif
+#ifndef UV2
+	vec2 uv2 = vec2(0., 0.);
+#endif
+
+#ifdef DIFFUSE
+	if (vDiffuseInfos.x == 0.)
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef AMBIENT
+	if (vAmbientInfos.x == 0.)
+	{
+		vAmbientUV = vec2(ambientMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vAmbientUV = vec2(ambientMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef OPACITY
+	if (vOpacityInfos.x == 0.)
+	{
+		vOpacityUV = vec2(opacityMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vOpacityUV = vec2(opacityMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef EMISSIVE
+	if (vEmissiveInfos.x == 0.)
+	{
+		vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vEmissiveUV = vec2(emissiveMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#if defined(SPECULAR) && defined(SPECULARTERM)
+	if (vSpecularInfos.x == 0.)
+	{
+		vSpecularUV = vec2(specularMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vSpecularUV = vec2(specularMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+	// Clip plane
+#ifdef CLIPPLANE
+	fClipDistance = dot(worldPos, vClipPlane);
+#endif
+
+	// Vertex color
+#ifdef VERTEXCOLOR
+	vColor = color;
+#endif
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1295 - 0
pbr/pbr.fragment.fx


+ 320 - 0
pbr/pbr.vertex.fx

@@ -0,0 +1,320 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+#ifdef NORMAL
+attribute vec3 normal;
+#endif
+#ifdef UV1
+attribute vec2 uv;
+#endif
+#ifdef UV2
+attribute vec2 uv2;
+#endif
+#ifdef VERTEXCOLOR
+attribute vec4 color;
+#endif
+#if NUM_BONE_INFLUENCERS > 0
+uniform mat4 mBones[BonesPerMesh];
+
+attribute vec4 matricesIndices;
+attribute vec4 matricesWeights;
+#if NUM_BONE_INFLUENCERS > 4
+attribute vec4 matricesIndicesExtra;
+attribute vec4 matricesWeightsExtra;
+#endif
+#endif
+
+// Uniforms
+
+#ifdef INSTANCES
+attribute vec4 world0;
+attribute vec4 world1;
+attribute vec4 world2;
+attribute vec4 world3;
+#else
+uniform mat4 world;
+#endif
+
+uniform mat4 view;
+uniform mat4 viewProjection;
+
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform mat4 diffuseMatrix;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform mat4 ambientMatrix;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY
+varying vec2 vOpacityUV;
+uniform mat4 opacityMatrix;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform mat4 emissiveMatrix;
+#endif
+
+#ifdef LIGHTMAP
+varying vec2 vLightmapUV;
+uniform vec2 vLightmapInfos;
+uniform mat4 lightmapMatrix;
+#endif
+
+#if defined(SPECULAR) && defined(SPECULARTERM)
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform mat4 specularMatrix;
+#endif
+
+#ifdef BUMP
+varying vec2 vBumpUV;
+uniform vec2 vBumpInfos;
+uniform mat4 bumpMatrix;
+#endif
+
+#ifdef POINTSIZE
+uniform float pointSize;
+#endif
+
+// Output
+varying vec3 vPositionW;
+#ifdef NORMAL
+varying vec3 vNormalW;
+#endif
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+#ifdef FOG
+varying float fFogDistance;
+#endif
+
+#ifdef SHADOWS
+#if defined(SPOTLIGHT0) || defined(DIRLIGHT0)
+uniform mat4 lightMatrix0;
+varying vec4 vPositionFromLight0;
+#endif
+#if defined(SPOTLIGHT1) || defined(DIRLIGHT1)
+uniform mat4 lightMatrix1;
+varying vec4 vPositionFromLight1;
+#endif
+#if defined(SPOTLIGHT2) || defined(DIRLIGHT2)
+uniform mat4 lightMatrix2;
+varying vec4 vPositionFromLight2;
+#endif
+#if defined(SPOTLIGHT3) || defined(DIRLIGHT3)
+uniform mat4 lightMatrix3;
+varying vec4 vPositionFromLight3;
+#endif
+#endif
+
+#ifdef REFLECTIONMAP_SKYBOX
+varying vec3 vPositionUVW;
+#endif
+
+#ifdef REFLECTIONMAP_EQUIRECTANGULAR
+varying vec3 vDirectionW;
+#endif
+
+#ifdef LOGARITHMICDEPTH
+uniform float logarithmicDepthConstant;
+varying float vFragmentDepth;
+#endif
+
+void main(void) {
+
+#ifdef REFLECTIONMAP_SKYBOX
+    vPositionUVW = position;
+#endif 
+
+#ifdef INSTANCES
+    mat4 finalWorld = mat4(world0, world1, world2, world3);
+#else
+    mat4 finalWorld = world;
+#endif
+
+#if NUM_BONE_INFLUENCERS > 0
+    mat4 influence;
+    influence = mBones[int(matricesIndices[0])] * matricesWeights[0];
+
+#if NUM_BONE_INFLUENCERS > 1
+    influence += mBones[int(matricesIndices[1])] * matricesWeights[1];
+#endif 
+#if NUM_BONE_INFLUENCERS > 2
+    influence += mBones[int(matricesIndices[2])] * matricesWeights[2];
+#endif	
+#if NUM_BONE_INFLUENCERS > 3
+    influence += mBones[int(matricesIndices[3])] * matricesWeights[3];
+#endif	
+
+#if NUM_BONE_INFLUENCERS > 4
+    influence += mBones[int(matricesIndicesExtra[0])] * matricesWeightsExtra[0];
+#endif
+#if NUM_BONE_INFLUENCERS > 5
+    influence += mBones[int(matricesIndicesExtra[1])] * matricesWeightsExtra[1];
+#endif	
+#if NUM_BONE_INFLUENCERS > 6
+    influence += mBones[int(matricesIndicesExtra[2])] * matricesWeightsExtra[2];
+#endif	
+#if NUM_BONE_INFLUENCERS > 7
+    influence += mBones[int(matricesIndicesExtra[3])] * matricesWeightsExtra[3];
+#endif	
+
+    finalWorld = finalWorld * influence;
+#endif
+
+    gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
+
+    vec4 worldPos = finalWorld * vec4(position, 1.0);
+    vPositionW = vec3(worldPos);
+
+#ifdef NORMAL
+    vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
+#endif
+
+#ifdef REFLECTIONMAP_EQUIRECTANGULAR
+    vDirectionW = normalize(vec3(finalWorld * vec4(position, 0.0)));
+#endif
+
+    // Texture coordinates
+#ifndef UV1
+    vec2 uv = vec2(0., 0.);
+#endif
+#ifndef UV2
+    vec2 uv2 = vec2(0., 0.);
+#endif
+
+#ifdef DIFFUSE
+    if (vDiffuseInfos.x == 0.)
+    {
+        vDiffuseUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+#ifdef AMBIENT
+    if (vAmbientInfos.x == 0.)
+    {
+        vAmbientUV = vec2(ambientMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vAmbientUV = vec2(ambientMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+#ifdef OPACITY
+    if (vOpacityInfos.x == 0.)
+    {
+        vOpacityUV = vec2(opacityMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vOpacityUV = vec2(opacityMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+#ifdef EMISSIVE
+    if (vEmissiveInfos.x == 0.)
+    {
+        vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vEmissiveUV = vec2(emissiveMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+#ifdef LIGHTMAP
+    if (vLightmapInfos.x == 0.)
+    {
+        vLightmapUV = vec2(lightmapMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vLightmapUV = vec2(lightmapMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+#if defined(SPECULAR) && defined(SPECULARTERM)
+    if (vSpecularInfos.x == 0.)
+    {
+        vSpecularUV = vec2(specularMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vSpecularUV = vec2(specularMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+#ifdef BUMP
+    if (vBumpInfos.x == 0.)
+    {
+        vBumpUV = vec2(bumpMatrix * vec4(uv, 1.0, 0.0));
+    }
+    else
+    {
+        vBumpUV = vec2(bumpMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
+    // Clip plane
+#ifdef CLIPPLANE
+    fClipDistance = dot(worldPos, vClipPlane);
+#endif
+
+    // Fog
+#ifdef FOG
+    fFogDistance = (view * worldPos).z;
+#endif
+
+    // Shadows
+#ifdef SHADOWS
+#if defined(SPOTLIGHT0) || defined(DIRLIGHT0)
+    vPositionFromLight0 = lightMatrix0 * worldPos;
+#endif
+#if defined(SPOTLIGHT1) || defined(DIRLIGHT1)
+    vPositionFromLight1 = lightMatrix1 * worldPos;
+#endif
+#if defined(SPOTLIGHT2) || defined(DIRLIGHT2)
+    vPositionFromLight2 = lightMatrix2 * worldPos;
+#endif
+#if defined(SPOTLIGHT3) || defined(DIRLIGHT3)
+    vPositionFromLight3 = lightMatrix3 * worldPos;
+#endif
+#endif
+
+    // Vertex color
+#ifdef VERTEXCOLOR
+    vColor = color;
+#endif
+
+    // Point size
+#ifdef POINTSIZE
+    gl_PointSize = pointSize;
+#endif
+
+    // Log. depth
+#ifdef LOGARITHMICDEPTH
+    vFragmentDepth = 1.0 + gl_Position.w;
+    gl_Position.z = log2(max(0.000001, vFragmentDepth)) * logarithmicDepthConstant;
+#endif
+}