فهرست منبع

Make a base class for the reflection texture, to be reused for the PBR reflection block

Popov72 5 سال پیش
والد
کامیت
5b266dbe94

+ 378 - 0
src/Materials/Node/Blocks/Dual/reflectionTextureBaseBlock.ts

@@ -0,0 +1,378 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
+import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
+import { BaseTexture } from '../../../Textures/baseTexture';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { Effect } from '../../../effect';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Nullable } from '../../../../types';
+import { _TypeStore } from '../../../../Misc/typeStore';
+import { Scene } from '../../../../scene';
+import { InputBlock } from '../Input/inputBlock';
+import { NodeMaterialSystemValues } from '../../Enums/nodeMaterialSystemValues';
+import { Constants } from '../../../../Engines/constants';
+
+import "../../../../Shaders/ShadersInclude/reflectionFunction";
+import { CubeTexture } from '../../../Textures/cubeTexture';
+import { Texture } from '../../../Textures/texture';
+
+/**
+ * Block used to read a reflection texture from a sampler
+ */
+export abstract class ReflectionTextureBaseBlock extends NodeMaterialBlock {
+    private _define3DName: string;
+    private _defineCubicName: string;
+    private _defineExplicitName: string;
+    private _defineProjectionName: string;
+    private _defineLocalCubicName: string;
+    private _defineSphericalName: string;
+    private _definePlanarName: string;
+    private _defineEquirectangularName: string;
+    private _defineMirroredEquirectangularFixedName: string;
+    private _defineEquirectangularFixedName: string;
+    private _defineSkyboxName: string;
+    private _cubeSamplerName: string;
+    private _2DSamplerName: string;
+    private _positionUVWName: string;
+    private _directionWName: string;
+    private _reflectionCoordsName: string;
+    private _reflection2DCoordsName: string;
+    private _reflectionMatrixName: string;
+
+    protected _reflectionColorName: string;
+
+    /**
+     * Gets or sets the texture associated with the node
+     */
+    public texture: Nullable<BaseTexture>;
+
+    /**
+     * Create a new TextureBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.VertexAndFragment);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "ReflectionTextureBaseBlock";
+    }
+
+    /**
+     * Gets the world position input component
+     */
+    public abstract get position(): NodeMaterialConnectionPoint;
+
+    /**
+     * Gets the world position input component
+     */
+    public abstract get worldPosition(): NodeMaterialConnectionPoint;
+
+    /**
+     * Gets the world normal input component
+     */
+    public abstract get worldNormal(): NodeMaterialConnectionPoint;
+
+    /**
+     * Gets the world input component
+     */
+    public abstract get world(): NodeMaterialConnectionPoint;
+
+    /**
+    * Gets the camera (or eye) position component
+    */
+    public abstract get cameraPosition(): NodeMaterialConnectionPoint;
+
+    /**
+     * Gets the view input component
+     */
+    public abstract get view(): NodeMaterialConnectionPoint;
+
+    public autoConfigure(material: NodeMaterial) {
+        if (!this.position.isConnected) {
+            let positionInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "position");
+
+            if (!positionInput) {
+                positionInput = new InputBlock("position");
+                positionInput.setAsAttribute();
+            }
+            positionInput.output.connectTo(this.position);
+        }
+
+        if (!this.world.isConnected) {
+            let worldInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.World);
+
+            if (!worldInput) {
+                worldInput = new InputBlock("world");
+                worldInput.setAsSystemValue(NodeMaterialSystemValues.World);
+            }
+            worldInput.output.connectTo(this.world);
+        }
+
+        if (!this.view.isConnected) {
+            let viewInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.View);
+
+            if (!viewInput) {
+                viewInput = new InputBlock("view");
+                viewInput.setAsSystemValue(NodeMaterialSystemValues.View);
+            }
+            viewInput.output.connectTo(this.view);
+        }
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        if (!defines._areTexturesDirty) {
+            return;
+        }
+
+        if (!this.texture || !this.texture.getTextureMatrix) {
+            return;
+        }
+
+        defines.setValue(this._define3DName, this.texture.isCube);
+        defines.setValue(this._defineLocalCubicName, (<any>this.texture).boundingBoxSize ? true : false);
+        defines.setValue(this._defineExplicitName, this.texture.coordinatesMode === Constants.TEXTURE_EXPLICIT_MODE);
+        defines.setValue(this._defineSkyboxName, this.texture.coordinatesMode === Constants.TEXTURE_SKYBOX_MODE);
+        defines.setValue(this._defineCubicName, this.texture.coordinatesMode === Constants.TEXTURE_CUBIC_MODE);
+        defines.setValue(this._defineSphericalName, this.texture.coordinatesMode === Constants.TEXTURE_SPHERICAL_MODE);
+        defines.setValue(this._definePlanarName, this.texture.coordinatesMode === Constants.TEXTURE_PLANAR_MODE);
+        defines.setValue(this._defineProjectionName, this.texture.coordinatesMode === Constants.TEXTURE_PROJECTION_MODE);
+        defines.setValue(this._defineEquirectangularName, this.texture.coordinatesMode === Constants.TEXTURE_EQUIRECTANGULAR_MODE);
+        defines.setValue(this._defineEquirectangularFixedName, this.texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MODE);
+        defines.setValue(this._defineMirroredEquirectangularFixedName, this.texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE);
+    }
+
+    public isReady() {
+        if (this.texture && !this.texture.isReadyOrNotBlocking()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
+        if (!mesh || !this.texture) {
+            return;
+        }
+
+        effect.setMatrix(this._reflectionMatrixName, this.texture.getReflectionTextureMatrix());
+
+        if (this.texture.isCube) {
+            effect.setTexture(this._cubeSamplerName, this.texture);
+        } else {
+            effect.setTexture(this._2DSamplerName, this.texture);
+        }
+    }
+
+    public handleVertexSide(state: NodeMaterialBuildState): string {
+        this._define3DName = state._getFreeDefineName("REFLECTIONMAP_3D");
+        this._defineCubicName = state._getFreeDefineName("REFLECTIONMAP_CUBIC");
+        this._defineSphericalName = state._getFreeDefineName("REFLECTIONMAP_SPHERICAL");
+        this._definePlanarName = state._getFreeDefineName("REFLECTIONMAP_PLANAR");
+        this._defineProjectionName = state._getFreeDefineName("REFLECTIONMAP_PROJECTION");
+        this._defineExplicitName = state._getFreeDefineName("REFLECTIONMAP_EXPLICIT");
+        this._defineEquirectangularName = state._getFreeDefineName("REFLECTIONMAP_EQUIRECTANGULAR");
+        this._defineLocalCubicName = state._getFreeDefineName("USE_LOCAL_REFLECTIONMAP_CUBIC");
+        this._defineMirroredEquirectangularFixedName = state._getFreeDefineName("REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED");
+        this._defineEquirectangularFixedName = state._getFreeDefineName("REFLECTIONMAP_EQUIRECTANGULAR_FIXED");
+        this._defineSkyboxName = state._getFreeDefineName("REFLECTIONMAP_SKYBOX");
+
+        let code = "";
+
+        let worldPosVaryingName = "v_" + this.worldPosition.associatedVariableName;
+        if (state._emitVaryingFromString(worldPosVaryingName, "vec4")) {
+            code += `${worldPosVaryingName} = ${this.worldPosition.associatedVariableName};\r\n`;
+        }
+
+        this._positionUVWName = state._getFreeVariableName("positionUVW");
+        this._directionWName = state._getFreeVariableName("directionW");
+
+        if (state._emitVaryingFromString(this._positionUVWName, "vec3", this._defineSkyboxName)) {
+            code += `#ifdef ${this._defineSkyboxName}\r\n`;
+            code += `${this._positionUVWName} = ${this.position.associatedVariableName}.xyz;\r\n`;
+            code += `#endif\r\n`;
+        }
+
+        if (state._emitVaryingFromString(this._directionWName, "vec3", `defined(${this._defineEquirectangularFixedName}) || defined(${this._defineMirroredEquirectangularFixedName})`)) {
+            code += `#if defined(${this._defineEquirectangularFixedName}) || defined(${this._defineMirroredEquirectangularFixedName})\r\n`;
+            code += `${this._directionWName} = normalize(vec3(${this.world.associatedVariableName} * vec4(${this.position.associatedVariableName}.xyz, 0.0)));\r\n`;
+            code += `#endif\r\n`;
+        }
+
+        return code;
+    }
+
+    public handleFragmentSideInits(state: NodeMaterialBuildState) {
+        state.sharedData.blockingBlocks.push(this);
+        state.sharedData.textureBlocks.push(this);
+
+        // Samplers
+        this._cubeSamplerName = state._getFreeVariableName(this.name + "CubeSampler");
+        state.samplers.push(this._cubeSamplerName);
+
+        this._2DSamplerName = state._getFreeVariableName(this.name + "2DSampler");
+        state.samplers.push(this._2DSamplerName);
+
+        state._samplerDeclaration += `#ifdef ${this._define3DName}\r\n`;
+        state._samplerDeclaration += `uniform samplerCube ${this._cubeSamplerName};\r\n`;
+        state._samplerDeclaration += `#else\r\n`;
+        state._samplerDeclaration += `uniform sampler2D ${this._2DSamplerName};\r\n`;
+        state._samplerDeclaration += `#endif\r\n`;
+
+        // Fragment
+        state.sharedData.blocksWithDefines.push(this);
+        state.sharedData.bindableBlocks.push(this);
+
+        let comments = `//${this.name}`;
+        state._emitFunction("ReciprocalPI", "#define RECIPROCAL_PI2 0.15915494", "");
+        state._emitFunctionFromInclude("reflectionFunction", comments);
+
+        this._reflectionColorName = state._getFreeVariableName("reflectionColor");
+        this._reflectionCoordsName = state._getFreeVariableName("reflectionUVW");
+        this._reflection2DCoordsName = state._getFreeVariableName("reflectionUV");
+        this._reflectionMatrixName = state._getFreeVariableName("reflectionMatrix");
+
+        state._emitUniformFromString(this._reflectionMatrixName, "mat4");
+    }
+
+    public handleFragmentSideCodeReflectionCoords(): string {
+        let worldPos = `v_${this.worldPosition.associatedVariableName}`;
+        let worldNormal = this.worldNormal.associatedVariableName + ".xyz";
+        let reflectionMatrix = this._reflectionMatrixName;
+        let direction = `normalize(${this._directionWName})`;
+        let positionUVW = `${this._positionUVWName}`;
+        let vEyePosition = `${this.cameraPosition.associatedVariableName}`;
+        let view = `${this.view.associatedVariableName}`;
+
+        let code = "";
+
+        code += `vec3 ${this._reflectionColorName};\r\n`;
+        code += `#ifdef ${this._defineMirroredEquirectangularFixedName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computeMirroredFixedEquirectangularCoords(${worldPos}, ${worldNormal}, ${direction});\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._defineEquirectangularFixedName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computeFixedEquirectangularCoords(${worldPos}, ${worldNormal}, ${direction});\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._defineEquirectangularName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computeEquirectangularCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix});\r\n`;
+        code += ` #endif\r\n`;
+
+        code += `#ifdef ${this._defineSphericalName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computeSphericalCoords(${worldPos}, ${worldNormal}, ${view}, ${reflectionMatrix});\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._definePlanarName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computePlanarCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix});\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._defineCubicName}\r\n`;
+        code += `    #ifdef ${this._defineLocalCubicName}\r\n`;
+        code += `        vec3 ${this._reflectionCoordsName} = computeCubicLocalCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix}, vReflectionSize, vReflectionPosition);\r\n`;
+        code += `    #else\r\n`;
+        code += `       vec3 ${this._reflectionCoordsName} = computeCubicCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix});\r\n`;
+        code += `    #endif\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._defineProjectionName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computeProjectionCoords(${worldPos}, ${view}, ${reflectionMatrix});\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._defineSkyboxName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = computeSkyBoxCoords(${positionUVW}, ${reflectionMatrix});\r\n`;
+        code += `#endif\r\n`;
+
+        code += `#ifdef ${this._defineExplicitName}\r\n`;
+        code += `    vec3 ${this._reflectionCoordsName} = vec3(0, 0, 0);\r\n`;
+        code += `#endif\r\n`;
+
+        return code;
+    }
+
+    public handleFragmentSideCodeReflectionColor(): string {
+        let code = "";
+
+        code += `#ifdef ${this._define3DName}\r\n`;
+        code += `${this._reflectionColorName} = textureCube(${this._cubeSamplerName}, ${this._reflectionCoordsName}).rgb;\r\n`;
+        code += `#else\r\n`;
+        code += `vec2 ${this._reflection2DCoordsName} = ${this._reflectionCoordsName}.xy;\r\n`;
+
+        code += `#ifdef ${this._defineProjectionName}\r\n`;
+        code += `${this._reflection2DCoordsName} /= ${this._reflectionCoordsName}.z;\r\n`;
+        code += `#endif\r\n`;
+
+        code += `${this._reflection2DCoordsName}.y = 1.0 - ${this._reflection2DCoordsName}.y;\r\n`;
+        code += `${this._reflectionColorName} = texture2D(${this._2DSamplerName}, ${this._reflection2DCoordsName}).rgb;\r\n`;
+        code += `#endif\r\n`;
+
+        return code;
+    }
+
+    public writeOutputs(state: NodeMaterialBuildState, varName: string): string {
+        let code = "";
+
+        if (state.target === NodeMaterialBlockTargets.Fragment) {
+            for (var output of this._outputs) {
+                if (output.hasEndpoints) {
+                    code += `${this._declareOutput(output, state)} = ${varName}.${output.name};\r\n`;
+                }
+            }
+        }
+
+        return code;
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+        return this;
+    }
+
+    protected _dumpPropertiesCode() {
+        if (!this.texture) {
+            return "";
+        }
+
+        let codeString: string;
+
+        if (this.texture.isCube) {
+            codeString = `${this._codeVariableName}.texture = new BABYLON.CubeTexture("${this.texture.name}");\r\n`;
+        } else {
+            codeString = `${this._codeVariableName}.texture = new BABYLON.Texture("${this.texture.name}");\r\n`;
+        }
+        codeString += `${this._codeVariableName}.texture.coordinatesMode = ${this.texture.coordinatesMode};\r\n`;
+
+        return codeString;
+    }
+
+    public serialize(): any {
+        let serializationObject = super.serialize();
+
+        if (this.texture) {
+            serializationObject.texture = this.texture.serialize();
+        }
+
+        return serializationObject;
+    }
+
+    public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
+        super._deserialize(serializationObject, scene, rootUrl);
+
+        if (serializationObject.texture) {
+            rootUrl = serializationObject.texture.url.indexOf("data:") === 0 ? "" : rootUrl;
+            if (serializationObject.texture.isCube) {
+                this.texture = CubeTexture.Parse(serializationObject.texture, scene, rootUrl);
+            } else {
+                this.texture = Texture.Parse(serializationObject.texture, scene, rootUrl);
+            }
+        }
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.ReflectionTextureBaseBlock"] = ReflectionTextureBaseBlock;

+ 14 - 297
src/Materials/Node/Blocks/Dual/reflectionTextureBlock.ts

@@ -1,59 +1,25 @@
-import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
 import { NodeMaterialConnectionPoint } from '../../nodeMaterialBlockConnectionPoint';
-import { BaseTexture } from '../../../Textures/baseTexture';
-import { AbstractMesh } from '../../../../Meshes/abstractMesh';
-import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
-import { Effect } from '../../../effect';
-import { Mesh } from '../../../../Meshes/mesh';
-import { Nullable } from '../../../../types';
+import { NodeMaterial } from '../../nodeMaterial';
 import { _TypeStore } from '../../../../Misc/typeStore';
-import { Scene } from '../../../../scene';
 import { InputBlock } from '../Input/inputBlock';
 import { NodeMaterialSystemValues } from '../../Enums/nodeMaterialSystemValues';
-import { Constants } from '../../../../Engines/constants';
 
 import "../../../../Shaders/ShadersInclude/reflectionFunction";
-import { CubeTexture } from '../../../Textures/cubeTexture';
-import { Texture } from '../../../Textures/texture';
+import { ReflectionTextureBaseBlock } from './reflectionTextureBaseBlock';
 
 /**
  * Block used to read a reflection texture from a sampler
  */
-export class ReflectionTextureBlock extends NodeMaterialBlock {
-    private _define3DName: string;
-    private _defineCubicName: string;
-    private _defineExplicitName: string;
-    private _defineProjectionName: string;
-    private _defineLocalCubicName: string;
-    private _defineSphericalName: string;
-    private _definePlanarName: string;
-    private _defineEquirectangularName: string;
-    private _defineMirroredEquirectangularFixedName: string;
-    private _defineEquirectangularFixedName: string;
-    private _defineSkyboxName: string;
-    private _cubeSamplerName: string;
-    private _2DSamplerName: string;
-    private _positionUVWName: string;
-    private _directionWName: string;
-    private _reflectionCoordsName: string;
-    private _reflection2DCoordsName: string;
-    private _reflectionColorName: string;
-    private _reflectionMatrixName: string;
-
-    /**
-     * Gets or sets the texture associated with the node
-     */
-    public texture: Nullable<BaseTexture>;
-
+export class ReflectionTextureBlock extends ReflectionTextureBaseBlock {
     /**
-     * Create a new TextureBlock
+     * Create a new ReflectionTextureBlock
      * @param name defines the block name
      */
     public constructor(name: string) {
-        super(name, NodeMaterialBlockTargets.VertexAndFragment);
+        super(name);
 
         this.registerInput("position", NodeMaterialBlockConnectionPointTypes.Vector3, false, NodeMaterialBlockTargets.Vertex);
         this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
@@ -150,25 +116,7 @@ export class ReflectionTextureBlock extends NodeMaterialBlock {
     }
 
     public autoConfigure(material: NodeMaterial) {
-        if (!this.position.isConnected) {
-            let positionInput = material.getInputBlockByPredicate((b) => b.isAttribute && b.name === "position");
-
-            if (!positionInput) {
-                positionInput = new InputBlock("position");
-                positionInput.setAsAttribute();
-            }
-            positionInput.output.connectTo(this.position);
-        }
-
-        if (!this.world.isConnected) {
-            let worldInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.World);
-
-            if (!worldInput) {
-                worldInput = new InputBlock("world");
-                worldInput.setAsSystemValue(NodeMaterialSystemValues.World);
-            }
-            worldInput.output.connectTo(this.world);
-        }
+        super.autoConfigure(material);
 
         if (!this.cameraPosition.isConnected) {
             let cameraPositionInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.CameraPosition);
@@ -179,262 +127,31 @@ export class ReflectionTextureBlock extends NodeMaterialBlock {
             }
             cameraPositionInput.output.connectTo(this.cameraPosition);
         }
-
-        if (!this.view.isConnected) {
-            let viewInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.View);
-
-            if (!viewInput) {
-                viewInput = new InputBlock("view");
-                viewInput.setAsSystemValue(NodeMaterialSystemValues.View);
-            }
-            viewInput.output.connectTo(this.view);
-        }
-    }
-
-    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
-        if (!defines._areTexturesDirty) {
-            return;
-        }
-
-        if (!this.texture || !this.texture.getTextureMatrix) {
-            return;
-        }
-
-        defines.setValue(this._define3DName, this.texture.isCube);
-        defines.setValue(this._defineLocalCubicName, (<any>this.texture).boundingBoxSize ? true : false);
-        defines.setValue(this._defineExplicitName, this.texture.coordinatesMode === Constants.TEXTURE_EXPLICIT_MODE);
-        defines.setValue(this._defineSkyboxName, this.texture.coordinatesMode === Constants.TEXTURE_SKYBOX_MODE);
-        defines.setValue(this._defineCubicName, this.texture.coordinatesMode === Constants.TEXTURE_CUBIC_MODE);
-        defines.setValue(this._defineSphericalName, this.texture.coordinatesMode === Constants.TEXTURE_SPHERICAL_MODE);
-        defines.setValue(this._definePlanarName, this.texture.coordinatesMode === Constants.TEXTURE_PLANAR_MODE);
-        defines.setValue(this._defineProjectionName, this.texture.coordinatesMode === Constants.TEXTURE_PROJECTION_MODE);
-        defines.setValue(this._defineEquirectangularName, this.texture.coordinatesMode === Constants.TEXTURE_EQUIRECTANGULAR_MODE);
-        defines.setValue(this._defineEquirectangularFixedName, this.texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MODE);
-        defines.setValue(this._defineMirroredEquirectangularFixedName, this.texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE);
-    }
-
-    public isReady() {
-        if (this.texture && !this.texture.isReadyOrNotBlocking()) {
-            return false;
-        }
-
-        return true;
-    }
-
-    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
-        if (!mesh || !this.texture) {
-            return;
-        }
-
-        effect.setMatrix(this._reflectionMatrixName, this.texture.getReflectionTextureMatrix());
-
-        if (this.texture.isCube) {
-            effect.setTexture(this._cubeSamplerName, this.texture);
-        } else {
-            effect.setTexture(this._2DSamplerName, this.texture);
-        }
-    }
-
-    private _injectVertexCode(state: NodeMaterialBuildState) {
-        let worldPosVaryingName = "v_" + this.worldPosition.associatedVariableName;
-        if (state._emitVaryingFromString(worldPosVaryingName, "vec4")) {
-            state.compilationString += `${worldPosVaryingName} = ${this.worldPosition.associatedVariableName};\r\n`;
-        }
-
-        this._positionUVWName = state._getFreeVariableName("positionUVW");
-        this._directionWName = state._getFreeVariableName("directionW");
-
-        if (state._emitVaryingFromString(this._positionUVWName, "vec3", this._defineSkyboxName)) {
-            state.compilationString += `#ifdef ${this._defineSkyboxName}\r\n`;
-            state.compilationString += `${this._positionUVWName} = ${this.position.associatedVariableName}.xyz;\r\n`;
-            state.compilationString += `#endif\r\n`;
-        }
-
-        if (state._emitVaryingFromString(this._directionWName, "vec3", `defined(${this._defineEquirectangularFixedName}) || defined(${this._defineMirroredEquirectangularFixedName})`)) {
-            state.compilationString += `#if defined(${this._defineEquirectangularFixedName}) || defined(${this._defineMirroredEquirectangularFixedName})\r\n`;
-            state.compilationString += `${this._directionWName} = normalize(vec3(${this.world.associatedVariableName} * vec4(${this.position.associatedVariableName}.xyz, 0.0)));\r\n`;
-            state.compilationString += `#endif\r\n`;
-        }
-    }
-
-    private _writeOutput(state: NodeMaterialBuildState, output: NodeMaterialConnectionPoint, swizzle: string) {
-        state.compilationString += `${this._declareOutput(output, state)} = ${this._reflectionColorName}.${swizzle};\r\n`;
     }
 
     protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         if (!this.texture) {
-            if (state.target === NodeMaterialBlockTargets.Fragment) {
-                for (var output of this._outputs) {
-                    if (output.hasEndpoints) {
-                        state.compilationString += `${this._declareOutput(output, state)} = vec3(0.).${output.name};\r\n`;
-                    }
-                }
-            }
-            return;
+            state.compilationString += this.writeOutputs(state, "vec3(0.)");
+            return this;
         }
 
         if (state.target !== NodeMaterialBlockTargets.Fragment) {
-            this._define3DName = state._getFreeDefineName("REFLECTIONMAP_3D");
-            this._defineCubicName = state._getFreeDefineName("REFLECTIONMAP_CUBIC");
-            this._defineSphericalName = state._getFreeDefineName("REFLECTIONMAP_SPHERICAL");
-            this._definePlanarName = state._getFreeDefineName("REFLECTIONMAP_PLANAR");
-            this._defineProjectionName = state._getFreeDefineName("REFLECTIONMAP_PROJECTION");
-            this._defineExplicitName = state._getFreeDefineName("REFLECTIONMAP_EXPLICIT");
-            this._defineEquirectangularName = state._getFreeDefineName("REFLECTIONMAP_EQUIRECTANGULAR");
-            this._defineLocalCubicName = state._getFreeDefineName("USE_LOCAL_REFLECTIONMAP_CUBIC");
-            this._defineMirroredEquirectangularFixedName = state._getFreeDefineName("REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED");
-            this._defineEquirectangularFixedName = state._getFreeDefineName("REFLECTIONMAP_EQUIRECTANGULAR_FIXED");
-            this._defineSkyboxName = state._getFreeDefineName("REFLECTIONMAP_SKYBOX");
-
-            // Vertex
-            this._injectVertexCode(state);
-            return;
+            state.compilationString += this.handleVertexSide(state);
+            return this;
         }
 
-        state.sharedData.blockingBlocks.push(this);
-        state.sharedData.textureBlocks.push(this);
-
-        // Samplers
-        this._cubeSamplerName = state._getFreeVariableName(this.name + "CubeSampler");
-        state.samplers.push(this._cubeSamplerName);
-
-        this._2DSamplerName = state._getFreeVariableName(this.name + "2DSampler");
-        state.samplers.push(this._2DSamplerName);
-
-        state._samplerDeclaration += `#ifdef ${this._define3DName}\r\n`;
-        state._samplerDeclaration += `uniform samplerCube ${this._cubeSamplerName};\r\n`;
-        state._samplerDeclaration += `#else\r\n`;
-        state._samplerDeclaration += `uniform sampler2D ${this._2DSamplerName};\r\n`;
-        state._samplerDeclaration += `#endif\r\n`;
-
-        // Fragment
-        state.sharedData.blocksWithDefines.push(this);
-        state.sharedData.bindableBlocks.push(this);
-
-        let comments = `//${this.name}`;
-        state._emitFunction("ReciprocalPI", "#define RECIPROCAL_PI2 0.15915494", "");
-        state._emitFunctionFromInclude("reflectionFunction", comments);
-
-        this._reflectionColorName = state._getFreeVariableName("reflectionColor");
-        this._reflectionCoordsName = state._getFreeVariableName("reflectionUVW");
-        this._reflection2DCoordsName = state._getFreeVariableName("reflectionUV");
-        this._reflectionMatrixName = state._getFreeVariableName("reflectionMatrix");
-
-        state._emitUniformFromString(this._reflectionMatrixName, "mat4");
-
-        // Code
-        let worldPos = `v_${this.worldPosition.associatedVariableName}`;
-        let worldNormal = this.worldNormal.associatedVariableName + ".xyz";
-        let reflectionMatrix = this._reflectionMatrixName;
-        let direction = `normalize(${this._directionWName})`;
-        let positionUVW = `${this._positionUVWName}`;
-        let vEyePosition = `${this.cameraPosition.associatedVariableName}`;
-        let view = `${this.view.associatedVariableName}`;
-
-        state.compilationString += `vec3 ${this._reflectionColorName};\r\n`;
-        state.compilationString += `#ifdef ${this._defineMirroredEquirectangularFixedName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computeMirroredFixedEquirectangularCoords(${worldPos}, ${worldNormal}, ${direction});\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineEquirectangularFixedName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computeFixedEquirectangularCoords(${worldPos}, ${worldNormal}, ${direction});\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineEquirectangularName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computeEquirectangularCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix});\r\n`;
-        state.compilationString += ` #endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineSphericalName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computeSphericalCoords(${worldPos}, ${worldNormal}, ${view}, ${reflectionMatrix});\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._definePlanarName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computePlanarCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix});\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineCubicName}\r\n`;
-        state.compilationString += `    #ifdef ${this._defineLocalCubicName}\r\n`;
-        state.compilationString += `        vec3 ${this._reflectionCoordsName} = computeCubicLocalCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix}, vReflectionSize, vReflectionPosition);\r\n`;
-        state.compilationString += `    #else\r\n`;
-        state.compilationString += `       vec3 ${this._reflectionCoordsName} = computeCubicCoords(${worldPos}, ${worldNormal}, ${vEyePosition}.xyz, ${reflectionMatrix});\r\n`;
-        state.compilationString += `    #endif\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineProjectionName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computeProjectionCoords(${worldPos}, ${view}, ${reflectionMatrix});\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineSkyboxName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = computeSkyBoxCoords(${positionUVW}, ${reflectionMatrix});\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._defineExplicitName}\r\n`;
-        state.compilationString += `    vec3 ${this._reflectionCoordsName} = vec3(0, 0, 0);\r\n`;
-        state.compilationString += `#endif\r\n`;
-
-        state.compilationString += `#ifdef ${this._define3DName}\r\n`;
-        state.compilationString += `${this._reflectionColorName} = textureCube(${this._cubeSamplerName}, ${this._reflectionCoordsName}).rgb;\r\n`;
-        state.compilationString += `#else\r\n`;
-        state.compilationString += `vec2 ${this._reflection2DCoordsName} = ${this._reflectionCoordsName}.xy;\r\n`;
+        this.handleFragmentSideInits(state);
 
-        state.compilationString += `#ifdef ${this._defineProjectionName}\r\n`;
-        state.compilationString += `${this._reflection2DCoordsName} /= ${this._reflectionCoordsName}.z;\r\n`;
-        state.compilationString += `#endif\r\n`;
+        state.compilationString += this.handleFragmentSideCodeReflectionCoords();
 
-        state.compilationString += `${this._reflection2DCoordsName}.y = 1.0 - ${this._reflection2DCoordsName}.y;\r\n`;
-        state.compilationString += `${this._reflectionColorName} = texture2D(${this._2DSamplerName}, ${this._reflection2DCoordsName}).rgb;\r\n`;
-        state.compilationString += `#endif\r\n`;
+        state.compilationString += this.handleFragmentSideCodeReflectionColor();
 
-        for (var output of this._outputs) {
-            if (output.hasEndpoints) {
-                this._writeOutput(state, output, output.name);
-            }
-        }
+        state.compilationString += this.writeOutputs(state, this._reflectionColorName);
 
         return this;
     }
-
-    protected _dumpPropertiesCode() {
-        if (!this.texture) {
-            return "";
-        }
-
-        let codeString: string;
-
-        if (this.texture.isCube) {
-            codeString = `${this._codeVariableName}.texture = new BABYLON.CubeTexture("${this.texture.name}");\r\n`;
-        } else {
-            codeString = `${this._codeVariableName}.texture = new BABYLON.Texture("${this.texture.name}");\r\n`;
-        }
-        codeString += `${this._codeVariableName}.texture.coordinatesMode = ${this.texture.coordinatesMode};\r\n`;
-
-        return codeString;
-    }
-
-    public serialize(): any {
-        let serializationObject = super.serialize();
-
-        if (this.texture) {
-            serializationObject.texture = this.texture.serialize();
-        }
-
-        return serializationObject;
-    }
-
-    public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
-        super._deserialize(serializationObject, scene, rootUrl);
-
-        if (serializationObject.texture) {
-            rootUrl = serializationObject.texture.url.indexOf("data:") === 0 ? "" : rootUrl;
-            if (serializationObject.texture.isCube) {
-                this.texture = CubeTexture.Parse(serializationObject.texture, scene, rootUrl);
-            } else {
-                this.texture = Texture.Parse(serializationObject.texture, scene, rootUrl);
-            }
-        }
-    }
 }
 
 _TypeStore.RegisteredTypes["BABYLON.ReflectionTextureBlock"] = ReflectionTextureBlock;

+ 2 - 2
src/Materials/Node/nodeMaterial.ts

@@ -26,7 +26,7 @@ import { InputBlock } from './Blocks/Input/inputBlock';
 import { _TypeStore } from '../../Misc/typeStore';
 import { SerializationHelper } from '../../Misc/decorators';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
-import { ReflectionTextureBlock } from './Blocks/Dual/reflectionTextureBlock';
+import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
 import { EffectFallbacks } from '../effectFallbacks';
 import { WebRequest } from '../../Misc/webRequest';
 import { Effect } from '../effect';
@@ -937,7 +937,7 @@ export class NodeMaterial extends PushMaterial {
      * Gets the list of texture blocks
      * @returns an array of texture blocks
      */
-    public getTextureBlocks(): (TextureBlock | ReflectionTextureBlock)[] {
+    public getTextureBlocks(): (TextureBlock | ReflectionTextureBaseBlock)[] {
         if (!this._sharedData) {
             return [];
         }

+ 2 - 2
src/Materials/Node/nodeMaterialBuildStateSharedData.ts

@@ -2,7 +2,7 @@ import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint'
 import { NodeMaterialBlock } from './nodeMaterialBlock';
 import { InputBlock } from './Blocks/Input/inputBlock';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
-import { ReflectionTextureBlock } from './Blocks/Dual/reflectionTextureBlock';
+import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
 import { Scene } from '../../scene';
 
 /**
@@ -32,7 +32,7 @@ export class NodeMaterialBuildStateSharedData {
     /**
      * Input blocks
      */
-    public textureBlocks = new Array<TextureBlock | ReflectionTextureBlock>();
+    public textureBlocks = new Array<TextureBlock | ReflectionTextureBaseBlock>();
 
     /**
      * Bindable blocks (Blocks that need to set data to the effect)