浏览代码

Associated with #6012

David Catuhe 6 年之前
父节点
当前提交
c1160fc318

+ 21 - 38
src/Materials/Node/Blocks/Dual/fogBlock.ts

@@ -1,7 +1,8 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
 import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to add support for scene fog
  * Block used to add support for scene fog
@@ -15,15 +16,15 @@ export class FogBlock extends NodeMaterialBlock {
         super(name, NodeMaterialBlockTargets.VertexAndFragment);
         super(name, NodeMaterialBlockTargets.VertexAndFragment);
 
 
         // Vertex
         // Vertex
-        this.registerInput("worldPos", NodeMaterialBlockConnectionPointTypes.Vector4, NodeMaterialBlockTargets.Vertex);
-        this.registerInput("view", NodeMaterialBlockConnectionPointTypes.Matrix, NodeMaterialBlockTargets.Vertex);
+        this.registerInput("worldPos", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
+        this.registerInput("view", NodeMaterialBlockConnectionPointTypes.Matrix, false, NodeMaterialBlockTargets.Vertex);
 
 
         this.registerOutput("vFogDistance", NodeMaterialBlockConnectionPointTypes.Vector3, NodeMaterialBlockTargets.Vertex);
         this.registerOutput("vFogDistance", NodeMaterialBlockConnectionPointTypes.Vector3, NodeMaterialBlockTargets.Vertex);
 
 
         // Fragment
         // Fragment
-        this.registerInput("input", NodeMaterialBlockConnectionPointTypes.Color3OrColor4, NodeMaterialBlockTargets.Fragment);
-        this.registerInput("fogColor", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
-        this.registerInput("fogParameters", NodeMaterialBlockConnectionPointTypes.Vector4, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("input", NodeMaterialBlockConnectionPointTypes.Color3OrColor4, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("fogColor", NodeMaterialBlockConnectionPointTypes.Color3, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("fogParameters", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Fragment);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
 
 
         // Auto configuration
         // Auto configuration
@@ -33,6 +34,14 @@ export class FogBlock extends NodeMaterialBlock {
         this._outputs[0].isVarying = true;
         this._outputs[0].isVarying = true;
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "FogBlock";
+    }
+
     /** @hidden */
     /** @hidden */
     public get _canAddAtFragmentRoot(): boolean {
     public get _canAddAtFragmentRoot(): boolean {
         return false;
         return false;
@@ -42,38 +51,12 @@ export class FogBlock extends NodeMaterialBlock {
         super._buildBlock(state);
         super._buildBlock(state);
 
 
         if (state.target === NodeMaterialBlockTargets.Fragment) {
         if (state.target === NodeMaterialBlockTargets.Fragment) {
-            state._emitFunction("CalcFogFactor",
-                `
-                    #define FOGMODE_NONE    0.
-                    #define FOGMODE_EXP     1.
-                    #define FOGMODE_EXP2    2.
-                    #define FOGMODE_LINEAR  3.
-                    #define E 2.71828
-
-                    float CalcFogFactor(vec3 vFogDistance, vec4 fogInfos)
-                    {
-                        float fogCoeff = 1.0;
-                        float fogStart = fogInfos.y;
-                        float fogEnd = fogInfos.z;
-                        float fogDensity = fogInfos.w;
-                        float fogDistance = length(vFogDistance);
-
-                        if (FOGMODE_LINEAR == fogInfos.x)
-                        {
-                            fogCoeff = (fogEnd - fogDistance) / (fogEnd - fogStart);
-                        }
-                        else if (FOGMODE_EXP == fogInfos.x)
-                        {
-                            fogCoeff = 1.0 / pow(E, fogDistance * fogDensity);
-                        }
-                        else if (FOGMODE_EXP2 == fogInfos.x)
-                        {
-                            fogCoeff = 1.0 / pow(E, fogDistance * fogDistance * fogDensity * fogDensity);
-                        }
-
-                        return clamp(fogCoeff, 0.0, 1.0);
-                    }
-                `);
+            state._emitFunctionFromInclude("CalcFogFactor", "fogFragmentDeclaration", {
+                removeUniforms: true,
+                removeVaryings: true,
+                removeifDef: true,
+                replaceString: ["float CalcFogFactor()", "float CalcFogFactor(vec3 vFogDistance, vec4 vFogInfos)"]
+            });
 
 
             let tempFogVariablename = state._getFreeVariableName("fog");
             let tempFogVariablename = state._getFreeVariableName("fog");
             let input = this._inputs[2];
             let input = this._inputs[2];

+ 10 - 1
src/Materials/Node/Blocks/Fragment/alphaTestBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to add an alpha test in the fragment shader
  * Block used to add an alpha test in the fragment shader
@@ -22,6 +23,14 @@ export class AlphaTestBlock extends NodeMaterialBlock {
         this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color4);
         this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "AlphaTestBlock";
+    }
+
     /** @hidden */
     /** @hidden */
     public get _canAddAtVertexRoot(): boolean {
     public get _canAddAtVertexRoot(): boolean {
         return false;
         return false;

+ 11 - 2
src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to output the final color
  * Block used to output the final color
@@ -11,11 +12,19 @@ export class FragmentOutputBlock extends NodeMaterialBlock {
      * @param name defines the block name
      * @param name defines the block name
      */
      */
     public constructor(name: string) {
     public constructor(name: string) {
-        super(name, NodeMaterialBlockTargets.Fragment);
+        super(name, NodeMaterialBlockTargets.Fragment, true);
 
 
         this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color4);
         this.registerInput("color", NodeMaterialBlockConnectionPointTypes.Color4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "FragmentOutputBlock";
+    }
+
     /** @hidden */
     /** @hidden */
     public get _canAddAtVertexRoot(): boolean {
     public get _canAddAtVertexRoot(): boolean {
         return false;
         return false;

+ 10 - 1
src/Materials/Node/Blocks/Fragment/rgbMergerBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to create a Color3 out of 3 inputs (one for each component)
  * Block used to create a Color3 out of 3 inputs (one for each component)
@@ -20,6 +21,14 @@ export class RGBMergerBlock extends NodeMaterialBlock {
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color3);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBMergerBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 10 - 1
src/Materials/Node/Blocks/Fragment/rgbSplitterBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to expand a Color3 or a Vector3 into 3 outputs (one for each component)
  * Block used to expand a Color3 or a Vector3 into 3 outputs (one for each component)
@@ -20,6 +21,14 @@ export class RGBSplitterBlock extends NodeMaterialBlock {
         this.registerOutput("b", NodeMaterialBlockConnectionPointTypes.Float);
         this.registerOutput("b", NodeMaterialBlockConnectionPointTypes.Float);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBSplitterBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 10 - 2
src/Materials/Node/Blocks/Fragment/rgbaMergerBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to create a Color4 out of 4 inputs (one for each component)
  * Block used to create a Color4 out of 4 inputs (one for each component)
@@ -22,6 +23,14 @@ export class RGBAMergerBlock extends NodeMaterialBlock {
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color4);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBAMergerBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 
@@ -38,7 +47,6 @@ export class RGBAMergerBlock extends NodeMaterialBlock {
             state.compilationString += this._declareOutput(output, state) + ` = vec4(${rInput.associatedVariableName}, ${gInput.associatedVariableName}, ${bInput.associatedVariableName}, ${aInput.associatedVariableName});\r\n`;
             state.compilationString += this._declareOutput(output, state) + ` = vec4(${rInput.associatedVariableName}, ${gInput.associatedVariableName}, ${bInput.associatedVariableName}, ${aInput.associatedVariableName});\r\n`;
         }
         }
 
 
-
         return this;
         return this;
     }
     }
 }
 }

+ 10 - 1
src/Materials/Node/Blocks/Fragment/rgbaSplitterBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to expand a Color4 or a Vector4 into 4 outputs (one for each component)
  * Block used to expand a Color4 or a Vector4 into 4 outputs (one for each component)
@@ -21,6 +22,14 @@ export class RGBASplitterBlock extends NodeMaterialBlock {
         this.registerOutput("a", NodeMaterialBlockConnectionPointTypes.Float);
         this.registerOutput("a", NodeMaterialBlockConnectionPointTypes.Float);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RGBASplitterBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 11 - 2
src/Materials/Node/Blocks/Vertex/vertexOutputBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to output the vertex position
  * Block used to output the vertex position
@@ -12,11 +13,19 @@ export class VertexOutputBlock extends NodeMaterialBlock {
      * @param name defines the block name
      * @param name defines the block name
      */
      */
     public constructor(name: string) {
     public constructor(name: string) {
-        super(name, NodeMaterialBlockTargets.Vertex);
+        super(name, NodeMaterialBlockTargets.Vertex, true);
 
 
         this.registerInput("vector", NodeMaterialBlockConnectionPointTypes.Vector4);
         this.registerInput("vector", NodeMaterialBlockConnectionPointTypes.Vector4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "VertexOutputBlock";
+    }
+
     /** @hidden */
     /** @hidden */
     public get _canAddAtVertexRoot(): boolean {
     public get _canAddAtVertexRoot(): boolean {
         return false;
         return false;

+ 40 - 0
src/Materials/Node/Blocks/addBlock.ts

@@ -0,0 +1,40 @@
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+/**
+ * Block used to add 2 vector4
+ */
+export class AddBlock extends NodeMaterialBlock {
+    /**
+     * Creates a new AddBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name);
+
+        this.registerInput("vector0", NodeMaterialBlockConnectionPointTypes.Vector4OrColor4);
+        this.registerInput("vector1", NodeMaterialBlockConnectionPointTypes.Vector4OrColor4);
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4OrColor4);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "AddBlock";
+    }
+
+    protected _buildBlock(state: NodeMaterialCompilationState) {
+        super._buildBlock(state);
+
+        let output = this._outputs[0];
+
+        let vector0 = this._inputs[0];
+        let vector1 = this._inputs[1];
+
+        state.compilationString += this._declareOutput(output, state) + ` = ${vector0.associatedVariableName} + ${vector1.associatedVariableName};\r\n`;
+
+        return this;
+    }
+}

+ 44 - 0
src/Materials/Node/Blocks/clampBlock.ts

@@ -0,0 +1,44 @@
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+/**
+ * Block used to clamp a float
+ */
+export class ClampBlock extends NodeMaterialBlock {
+
+    /** Gets or sets the minimum range */
+    public minimum = 0.0;
+    /** Gets or sets the maximum range */
+    public maximum = 1.0;
+
+    /**
+     * Creates a new ClampBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name);
+
+        this.registerInput("value", NodeMaterialBlockConnectionPointTypes.Float);
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Float);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "ClampBlock";
+    }
+
+    protected _buildBlock(state: NodeMaterialCompilationState) {
+        super._buildBlock(state);
+
+        let output = this._outputs[0];
+
+        let value = this._inputs[0];
+
+        state.compilationString += this._declareOutput(output, state) + ` = clamp(${value.associatedVariableName}, ${this._writeFloat(this.minimum)}, ${this._writeFloat(this.maximum)});\r\n`;
+
+        return this;
+    }
+}

+ 3 - 1
src/Materials/Node/Blocks/index.ts

@@ -1,7 +1,9 @@
 export * from "./Vertex/index";
 export * from "./Vertex/index";
 export * from "./Fragment/index";
 export * from "./Fragment/index";
 export * from "./Dual/index";
 export * from "./Dual/index";
-export * from "./mixBlock";
+export * from "./multiplyBlock";
+export * from "./addBlock";
+export * from "./clampBlock";
 export * from "./textureBlock";
 export * from "./textureBlock";
 export * from "./vector2TransformBlock";
 export * from "./vector2TransformBlock";
 export * from "./vector3TransformBlock";
 export * from "./vector3TransformBlock";

+ 11 - 3
src/Materials/Node/Blocks/mixBlock.ts

@@ -2,11 +2,11 @@ import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
 /**
 /**
- * Block used to mix 2 vector4
+ * Block used to multiply 2 vector4
  */
  */
-export class MixBlock extends NodeMaterialBlock {
+export class MultiplyBlock extends NodeMaterialBlock {
     /**
     /**
-     * Creates a new MixBlock
+     * Creates a new MultiplyBlock
      * @param name defines the block name
      * @param name defines the block name
      */
      */
     public constructor(name: string) {
     public constructor(name: string) {
@@ -17,6 +17,14 @@ export class MixBlock extends NodeMaterialBlock {
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4OrColor4);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4OrColor4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "MultiplyBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 10 - 1
src/Materials/Node/Blocks/textureBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to read a texture from a sampler
  * Block used to read a texture from a sampler
@@ -19,6 +20,14 @@ export class TextureBlock extends NodeMaterialBlock {
         this.registerOutput("color", NodeMaterialBlockConnectionPointTypes.Color4);
         this.registerOutput("color", NodeMaterialBlockConnectionPointTypes.Color4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "TextureBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 11 - 1
src/Materials/Node/Blocks/vector2TransformBlock.ts

@@ -1,6 +1,8 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
+
 /**
 /**
  * Block used to transform a vector2 with a matrix
  * Block used to transform a vector2 with a matrix
  */
  */
@@ -27,6 +29,14 @@ export class Vector2TransformBlock extends NodeMaterialBlock {
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector2);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector2);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "Vector2TransformBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 10 - 1
src/Materials/Node/Blocks/vector3TransformBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to transform a vector3 with a matrix
  * Block used to transform a vector3 with a matrix
@@ -23,6 +24,14 @@ export class Vector3TransformBlock extends NodeMaterialBlock {
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "Vector3TransformBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 10 - 1
src/Materials/Node/Blocks/vector4TransformBlock.ts

@@ -1,6 +1,7 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from '../nodeMaterialBlock';
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Block used to transform a vector4 with a matrix
  * Block used to transform a vector4 with a matrix
@@ -19,6 +20,14 @@ export class Vector4TransformBlock extends NodeMaterialBlock {
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4);
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Vector4);
     }
     }
 
 
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "Vector4TransformBlock";
+    }
+
     protected _buildBlock(state: NodeMaterialCompilationState) {
     protected _buildBlock(state: NodeMaterialCompilationState) {
         super._buildBlock(state);
         super._buildBlock(state);
 
 

+ 1 - 0
src/Materials/Node/index.ts

@@ -1,3 +1,4 @@
+export * from "./nodeMaterialBlockTargets";
 export * from "./nodeMaterialBlockConnectionPointTypes";
 export * from "./nodeMaterialBlockConnectionPointTypes";
 export * from "./nodeMaterialBlockConnectionPoint";
 export * from "./nodeMaterialBlockConnectionPoint";
 export * from "./nodeMaterialBlock";
 export * from "./nodeMaterialBlock";

+ 19 - 9
src/Materials/Node/nodeMaterial.ts

@@ -1,16 +1,18 @@
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from './nodeMaterialBlock';
+import { NodeMaterialBlock } from './nodeMaterialBlock';
 import { Material } from '../material';
 import { Material } from '../material';
 import { Scene } from '../../scene';
 import { Scene } from '../../scene';
 import { AbstractMesh } from '../../Meshes/abstractMesh';
 import { AbstractMesh } from '../../Meshes/abstractMesh';
 import { Matrix } from '../../Maths/math';
 import { Matrix } from '../../Maths/math';
 import { Mesh } from '../../Meshes/mesh';
 import { Mesh } from '../../Meshes/mesh';
 import { Engine } from '../../Engines/engine';
 import { Engine } from '../../Engines/engine';
-import { NodeMaterialCompilationState, NodeMaterialCompilationStateSharedData } from './nodeMaterialCompilationState';
+import { NodeMaterialCompilationState } from './nodeMaterialCompilationState';
 import { EffectCreationOptions } from '../effect';
 import { EffectCreationOptions } from '../effect';
 import { BaseTexture } from '../../Materials/Textures/baseTexture';
 import { BaseTexture } from '../../Materials/Textures/baseTexture';
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { Observable } from '../../Misc/observable';
 import { Observable } from '../../Misc/observable';
+import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
+import { NodeMaterialCompilationStateSharedData } from './nodeMaterialCompilationStateSharedData';
 
 
 /**
 /**
  * Class used to configure NodeMaterial
  * Class used to configure NodeMaterial
@@ -224,8 +226,9 @@ export class NodeMaterial extends Material {
 
 
     /**
     /**
      * Build the material and generates the inner effect
      * Build the material and generates the inner effect
+     * @param verbose defines if the build should log activity
      */
      */
-    public build() {
+    public build(verbose: boolean = false) {
         if (this._vertexRootNodes.length === 0) {
         if (this._vertexRootNodes.length === 0) {
             throw "You must define at least one vertexRootNode";
             throw "You must define at least one vertexRootNode";
         }
         }
@@ -234,9 +237,6 @@ export class NodeMaterial extends Material {
             throw "You must define at least one fragmentRootNode";
             throw "You must define at least one fragmentRootNode";
         }
         }
 
 
-        // Go through the nodes and do some magic :)
-        // Needs to create the code and deduce samplers and uniforms in order to populate some lists used during bindings
-
         // Propagate targets
         // Propagate targets
         for (var vertexRootNode of this._vertexRootNodes) {
         for (var vertexRootNode of this._vertexRootNodes) {
             this._propagateTarget(vertexRootNode, NodeMaterialBlockTargets.Vertex);
             this._propagateTarget(vertexRootNode, NodeMaterialBlockTargets.Vertex);
@@ -255,6 +255,7 @@ export class NodeMaterial extends Material {
         this._fragmentCompilationState.sharedData = sharedData;
         this._fragmentCompilationState.sharedData = sharedData;
         sharedData.buildId = this._buildId;
         sharedData.buildId = this._buildId;
         sharedData.emitComments = this._options.emitComments;
         sharedData.emitComments = this._options.emitComments;
+        sharedData.verbose = verbose;
 
 
         for (var vertexRootNode of this._vertexRootNodes) {
         for (var vertexRootNode of this._vertexRootNodes) {
             vertexRootNode.build(this._vertexCompilationState);
             vertexRootNode.build(this._vertexCompilationState);
@@ -263,7 +264,6 @@ export class NodeMaterial extends Material {
         // Fragment
         // Fragment
         this._fragmentCompilationState.target = NodeMaterialBlockTargets.Fragment;
         this._fragmentCompilationState.target = NodeMaterialBlockTargets.Fragment;
         this._fragmentCompilationState._vertexState = this._vertexCompilationState;
         this._fragmentCompilationState._vertexState = this._vertexCompilationState;
-        this._fragmentCompilationState.hints = this._vertexCompilationState.hints;
         this._fragmentCompilationState._uniformConnectionPoints = this._vertexCompilationState._uniformConnectionPoints;
         this._fragmentCompilationState._uniformConnectionPoints = this._vertexCompilationState._uniformConnectionPoints;
 
 
         for (var fragmentRootNode of this._fragmentRootNodes) {
         for (var fragmentRootNode of this._fragmentRootNodes) {
@@ -283,6 +283,16 @@ export class NodeMaterial extends Material {
 
 
         this._buildId++;
         this._buildId++;
 
 
+        // Errors
+        sharedData.emitErrors();
+
+        if (verbose) {
+            console.log("Vertex shader:");
+            console.log(this._vertexCompilationState.compilationString);
+            console.log("Fragment shader:");
+            console.log(this._fragmentCompilationState.compilationString);
+        }
+
         this.onBuildObservable.notifyObservers(this);
         this.onBuildObservable.notifyObservers(this);
     }
     }
 
 
@@ -381,7 +391,7 @@ export class NodeMaterial extends Material {
             return;
             return;
         }
         }
 
 
-        let hints = this._fragmentCompilationState.hints;
+        let hints = this._fragmentCompilationState.sharedData.hints;
         if (hints.needWorldMatrix) {
         if (hints.needWorldMatrix) {
             this._effect.setMatrix("world", world);
             this._effect.setMatrix("world", world);
         }
         }
@@ -408,7 +418,7 @@ export class NodeMaterial extends Material {
         this.bindOnlyWorldMatrix(world);
         this.bindOnlyWorldMatrix(world);
 
 
         if (this._effect && scene.getCachedMaterial() !== this) {
         if (this._effect && scene.getCachedMaterial() !== this) {
-            let hints = this._fragmentCompilationState.hints;
+            let hints = this._fragmentCompilationState.sharedData.hints;
 
 
             if (hints.needViewMatrix) {
             if (hints.needViewMatrix) {
                 this._effect.setMatrix("view", scene.getViewMatrix());
                 this._effect.setMatrix("view", scene.getViewMatrix());

+ 56 - 28
src/Materials/Node/nodeMaterialBlock.ts

@@ -1,27 +1,16 @@
-import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialCompilationState } from './nodeMaterialCompilationState';
 import { NodeMaterialCompilationState } from './nodeMaterialCompilationState';
 import { Nullable } from '../../types';
 import { Nullable } from '../../types';
-
-/**
- * Enum used to define the target of a block
- */
-export enum NodeMaterialBlockTargets {
-    /** Vertex shader */
-    Vertex = 1,
-    /** Fragment shader */
-    Fragment = 2,
-    /** Vertex and Fragment */
-    VertexAndFragment = Vertex | Fragment
-}
+import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
 
 
 /**
 /**
  * Defines a block that can be used inside a node based material
  * Defines a block that can be used inside a node based material
  */
  */
 export class NodeMaterialBlock {
 export class NodeMaterialBlock {
     private _buildId: number;
     private _buildId: number;
-    private _userDefinedTarget: Nullable<NodeMaterialBlockTargets> = null;
-    private _restrictedTarget: Nullable<NodeMaterialBlockTargets> = null;
+    private _target: NodeMaterialBlockTargets;
+    private _isFinalMerger = false;
 
 
     /** @hidden */
     /** @hidden */
     protected _inputs = new Array<NodeMaterialConnectionPoint>();
     protected _inputs = new Array<NodeMaterialConnectionPoint>();
@@ -34,6 +23,13 @@ export class NodeMaterialBlock {
     public name: string;
     public name: string;
 
 
     /**
     /**
+     * Gets a boolean indicating that this block is an end block (e.g. it is generating a system value)
+     */
+    public get isFinalMerger(): boolean {
+        return this._isFinalMerger;
+    }
+
+    /**
      * Gets or sets the build Id
      * Gets or sets the build Id
      */
      */
     public get buildId(): number {
     public get buildId(): number {
@@ -45,18 +41,17 @@ export class NodeMaterialBlock {
     }
     }
 
 
     /**
     /**
-     * Gets or sets the type of the block
+     * Gets or sets the target of the block
      */
      */
     public get target() {
     public get target() {
-        if (this._restrictedTarget !== null) {
-            return this._restrictedTarget;
-        }
-
-        return this._userDefinedTarget;
+        return this._target;
     }
     }
 
 
-    public set target(value: Nullable<NodeMaterialBlockTargets>) {
-        this._userDefinedTarget = value;
+    public set target(value: NodeMaterialBlockTargets) {
+        if ((this._target & value) !== 0) {
+            return;
+        }
+        this._target = value;
     }
     }
 
 
     /**
     /**
@@ -104,13 +99,16 @@ export class NodeMaterialBlock {
     /**
     /**
      * Creates a new NodeMaterialBlock
      * Creates a new NodeMaterialBlock
      * @param name defines the block name
      * @param name defines the block name
-     * @param restrictedTarget defines the target of that block (can be null)
+     * @param target defines the target of that block (Vertex by default)
+     * @param isFinalMerger defines a boolean indicating that this block is an end block (e.g. it is generating a system value). Default is false
      */
      */
-    public constructor(name: string, restrictedTarget?: NodeMaterialBlockTargets) {
+    public constructor(name: string, target = NodeMaterialBlockTargets.Vertex, isFinalMerger = false) {
         this.name = name;
         this.name = name;
 
 
-        if (restrictedTarget !== undefined) {
-            this._restrictedTarget = restrictedTarget;
+        this._target = target;
+
+        if (isFinalMerger) {
+            this._isFinalMerger = true;
         }
         }
     }
     }
 
 
@@ -122,6 +120,15 @@ export class NodeMaterialBlock {
         return `${state._getGLType(output.type)} ${output.associatedVariableName}`;
         return `${state._getGLType(output.type)} ${output.associatedVariableName}`;
     }
     }
 
 
+    protected _writeFloat(value: number) {
+        let stringVersion = value.toString();
+
+        if (stringVersion.indexOf(".") === -1) {
+            stringVersion += ".0";
+        }
+        return `${stringVersion}`;
+    }
+
     /**
     /**
      * Gets the current class name e.g. "NodeMaterialBlock"
      * Gets the current class name e.g. "NodeMaterialBlock"
      * @returns the class name
      * @returns the class name
@@ -253,6 +260,9 @@ export class NodeMaterialBlock {
         // Check if "parent" blocks are compiled
         // Check if "parent" blocks are compiled
         for (var input of this._inputs) {
         for (var input of this._inputs) {
             if (!input.connectedPoint) {
             if (!input.connectedPoint) {
+                if (!input.isOptional && !input.isAttribute && !input.isUniform) { // Emit a warning
+                    state.sharedData.checks.notConnectedNonOptionalInputs.push(input);
+                }
                 continue;
                 continue;
             }
             }
 
 
@@ -270,6 +280,11 @@ export class NodeMaterialBlock {
             return; // Need to check again as inputs can be connected multiple time to this endpoint
             return; // Need to check again as inputs can be connected multiple time to this endpoint
         }
         }
 
 
+        // Logs
+        if (state.sharedData.verbose) {
+            console.log(`${state.target === NodeMaterialBlockTargets.Vertex ? "Vertex shader" : "Fragment shader"}: Building ${this.name} [${this.getClassName()}]`);
+        }
+
         // Build
         // Build
         for (var input of this._inputs) {
         for (var input of this._inputs) {
             if ((input.target & this.target!) === 0) {
             if ((input.target & this.target!) === 0) {
@@ -278,6 +293,19 @@ export class NodeMaterialBlock {
             state._emitUniformOrAttributes(input);
             state._emitUniformOrAttributes(input);
         }
         }
 
 
+        // Checks final outputs
+        if (this.isFinalMerger) {
+            switch (state.target) {
+                case NodeMaterialBlockTargets.Vertex:
+                    state.sharedData.checks.emitVertex = true;
+                    break;
+                case NodeMaterialBlockTargets.Fragment:
+                    state.sharedData.checks.emitFragment = true;
+                    break;
+            }
+        }
+
+        /** Prepare outputs */
         for (var output of this._outputs) {
         for (var output of this._outputs) {
             if ((output.target & this.target!) === 0 || output.associatedVariableName) {
             if ((output.target & this.target!) === 0 || output.associatedVariableName) {
                 continue;
                 continue;
@@ -301,7 +329,7 @@ export class NodeMaterialBlock {
             }
             }
 
 
             for (var block of output.connectedBlocks) {
             for (var block of output.connectedBlocks) {
-                if (block && (!block.target || (block.target & this.target!) !== 0)) {
+                if (block && (block.target & state.target) !== 0) {
                     block.build(state);
                     block.build(state);
                 }
                 }
             }
             }

+ 6 - 4
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -1,9 +1,11 @@
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialBlock, NodeMaterialBlockTargets } from './nodeMaterialBlock';
+import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
 import { Nullable } from '../../types';
 import { Nullable } from '../../types';
 import { Effect } from '../effect';
 import { Effect } from '../effect';
 import { NodeMaterialWellKnownValues } from './nodeMaterialWellKnownValues';
 import { NodeMaterialWellKnownValues } from './nodeMaterialWellKnownValues';
 
 
+declare type NodeMaterialBlock = import("./nodeMaterialBlock").NodeMaterialBlock;
+
 /**
 /**
  * Defines a connection point for a block
  * Defines a connection point for a block
  */
  */
@@ -30,12 +32,12 @@ export class NodeMaterialConnectionPoint {
     public name: string;
     public name: string;
 
 
     /**
     /**
-     * Gets or sets a boolean indicating that this input can be omitted
+     * Gets or sets a boolean indicating that this connection point can be omitted
      */
      */
-    public isOptional: boolean
+    public isOptional: boolean;
 
 
     /** Gets or sets the target of that connection point */
     /** Gets or sets the target of that connection point */
-    public target: NodeMaterialBlockTargets = NodeMaterialBlockTargets.VertexAndFragment
+    public target: NodeMaterialBlockTargets = NodeMaterialBlockTargets.VertexAndFragment;
 
 
     /**
     /**
      * Gets or sets the value of that point.
      * Gets or sets the value of that point.

+ 11 - 0
src/Materials/Node/nodeMaterialBlockTargets.ts

@@ -0,0 +1,11 @@
+/**
+ * Enum used to define the target of a block
+ */
+export enum NodeMaterialBlockTargets {
+    /** Vertex shader */
+    Vertex = 1,
+    /** Fragment shader */
+    Fragment = 2,
+    /** Vertex and Fragment */
+    VertexAndFragment = Vertex | Fragment
+}

+ 52 - 53
src/Materials/Node/nodeMaterialCompilationState.ts

@@ -1,33 +1,9 @@
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialWellKnownValues } from './nodeMaterialWellKnownValues';
 import { NodeMaterialWellKnownValues } from './nodeMaterialWellKnownValues';
-import { NodeMaterialBlockTargets } from './nodeMaterialBlock';
-
-/**
- * Class used to store shared data between 2 NodeMaterialCompilationState
- */
-export class NodeMaterialCompilationStateSharedData {
-    /**
-     * Gets the list of emitted varyings
-     */
-    public varyings = new Array<string>();
-
-    /**
-     * Gets the varying declaration string
-     */
-    public varyingDeclaration = "";
-
-    /**
-     * Build Id used to avoid multiple recompilations
-     */
-    public buildId: number;
-
-    /** List of emitted variables */
-    public variableNames: { [key: string]: number } = {};
-
-    /** Should emit comments? */
-    public emitComments: boolean;
-}
+import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
+import { NodeMaterialCompilationStateSharedData } from './nodeMaterialCompilationStateSharedData';
+import { Effect } from '../../Materials/effect';
 
 
 /**
 /**
  * Class used to store node based material compilation state
  * Class used to store node based material compilation state
@@ -65,20 +41,6 @@ export class NodeMaterialCompilationState {
     /** @hidden */
     /** @hidden */
     public _vertexState: NodeMaterialCompilationState;
     public _vertexState: NodeMaterialCompilationState;
 
 
-    /**
-     * Gets the compilation hints emitted at compilation time
-     */
-    public hints = {
-        needWorldMatrix: false,
-        needViewMatrix: false,
-        needProjectionMatrix: false,
-        needViewProjectionMatrix: false,
-        needWorldViewMatrix: false,
-        needWorldViewProjectionMatrix: false,
-        needFogColor: false,
-        needFogParameters: false
-    };
-
     private _attributeDeclaration = "";
     private _attributeDeclaration = "";
     private _uniformDeclaration = "";
     private _uniformDeclaration = "";
     private _samplerDeclaration = "";
     private _samplerDeclaration = "";
@@ -111,19 +73,19 @@ export class NodeMaterialCompilationState {
         this.compilationString = `${this.compilationString}\r\n}`;
         this.compilationString = `${this.compilationString}\r\n}`;
 
 
         if (this.sharedData.varyingDeclaration) {
         if (this.sharedData.varyingDeclaration) {
-            this.compilationString = `\r\n${emitComments ? "//Varyings\r\n" : ""}${this.sharedData.varyingDeclaration}\r\n\r\n${this.compilationString}`;
+            this.compilationString = `\r\n${emitComments ? "//Varyings\r\n" : ""}${this.sharedData.varyingDeclaration}\r\n${this.compilationString}`;
         }
         }
 
 
         if (this._samplerDeclaration) {
         if (this._samplerDeclaration) {
-            this.compilationString = `\r\n${emitComments ? "//Samplers\r\n" : ""}${this._samplerDeclaration}\r\n\r\n${this.compilationString}`;
+            this.compilationString = `\r\n${emitComments ? "//Samplers\r\n" : ""}${this._samplerDeclaration}\r\n${this.compilationString}`;
         }
         }
 
 
         if (this._uniformDeclaration) {
         if (this._uniformDeclaration) {
-            this.compilationString = `\r\n${emitComments ? "//Uniforms\r\n" : ""}${this._uniformDeclaration}\r\n\r\n${this.compilationString}`;
+            this.compilationString = `\r\n${emitComments ? "//Uniforms\r\n" : ""}${this._uniformDeclaration}\r\n${this.compilationString}`;
         }
         }
 
 
         if (this._attributeDeclaration && !isFragmentMode) {
         if (this._attributeDeclaration && !isFragmentMode) {
-            this.compilationString = `\r\n${emitComments ? "//Attributes\r\n" : ""}${this._attributeDeclaration}\r\n\r\n${this.compilationString}`;
+            this.compilationString = `\r\n${emitComments ? "//Attributes\r\n" : ""}${this._attributeDeclaration}\r\n${this.compilationString}`;
         }
         }
     }
     }
 
 
@@ -174,6 +136,42 @@ export class NodeMaterialCompilationState {
     }
     }
 
 
     /** @hidden */
     /** @hidden */
+    public _emitFunctionFromInclude(name: string, includeName: string, options?: {
+        removeUniforms?: boolean,
+        removeVaryings?: boolean,
+        removeifDef?: boolean,
+        replaceString?: string[],
+    }) {
+        if (this.functions[name]) {
+            return;
+        }
+
+        this.functions[name] = Effect.IncludesShadersStore[includeName];
+
+        if (!options) {
+            return;
+        }
+
+        if (options.removeifDef) {
+            this.functions[name] = this.functions[name].replace(/^\s*?#.+$/gm, "");
+        }
+
+        if (options.removeUniforms) {
+            this.functions[name] = this.functions[name].replace(/^\s*?uniform.+$/gm, "");
+        }
+
+        if (options.removeVaryings) {
+            this.functions[name] = this.functions[name].replace(/^\s*?varying.+$/gm, "");
+        }
+
+        if (options.replaceString) {
+            for (var index = 0; index < options.replaceString.length; index += 2) {
+                this.functions[name] = this.functions[name].replace(options.replaceString[index], options.replaceString[index + 1]);
+            }
+        }
+    }
+
+    /** @hidden */
     public _emitVaryings(point: NodeMaterialConnectionPoint, force = false, fromFragment = false) {
     public _emitVaryings(point: NodeMaterialConnectionPoint, force = false, fromFragment = false) {
         if (point.isVarying || force) {
         if (point.isVarying || force) {
             if (this.sharedData.varyings.indexOf(point.associatedVariableName) !== -1) {
             if (this.sharedData.varyings.indexOf(point.associatedVariableName) !== -1) {
@@ -222,31 +220,32 @@ export class NodeMaterialCompilationState {
             this._uniformDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
             this._uniformDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
 
 
             // well known
             // well known
+            let hints = this.sharedData.hints;
             if (point._wellKnownValue !== null) {
             if (point._wellKnownValue !== null) {
                 switch (point._wellKnownValue) {
                 switch (point._wellKnownValue) {
                     case NodeMaterialWellKnownValues.World:
                     case NodeMaterialWellKnownValues.World:
-                        this.hints.needWorldMatrix = true;
+                        hints.needWorldMatrix = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.View:
                     case NodeMaterialWellKnownValues.View:
-                        this.hints.needViewMatrix = true;
+                        hints.needViewMatrix = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.Projection:
                     case NodeMaterialWellKnownValues.Projection:
-                        this.hints.needProjectionMatrix = true;
+                        hints.needProjectionMatrix = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.ViewProjection:
                     case NodeMaterialWellKnownValues.ViewProjection:
-                        this.hints.needViewProjectionMatrix = true;
+                        hints.needViewProjectionMatrix = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.WorldView:
                     case NodeMaterialWellKnownValues.WorldView:
-                        this.hints.needWorldViewMatrix = true;
+                        hints.needWorldViewMatrix = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.WorldViewProjection:
                     case NodeMaterialWellKnownValues.WorldViewProjection:
-                        this.hints.needWorldViewProjectionMatrix = true;
+                        hints.needWorldViewProjectionMatrix = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.FogColor:
                     case NodeMaterialWellKnownValues.FogColor:
-                        this.hints.needFogColor = true;
+                        hints.needFogColor = true;
                         break;
                         break;
                     case NodeMaterialWellKnownValues.FogParameters:
                     case NodeMaterialWellKnownValues.FogParameters:
-                        this.hints.needFogParameters = true;
+                        hints.needFogParameters = true;
                         break;
                         break;
                 }
                 }
             } else {
             } else {

+ 77 - 0
src/Materials/Node/nodeMaterialCompilationStateSharedData.ts

@@ -0,0 +1,77 @@
+import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+
+/**
+ * Class used to store shared data between 2 NodeMaterialCompilationState
+ */
+export class NodeMaterialCompilationStateSharedData {
+    /**
+     * Gets the list of emitted varyings
+     */
+    public varyings = new Array<string>();
+
+    /**
+     * Gets the varying declaration string
+     */
+    public varyingDeclaration = "";
+
+    /**
+     * Build Id used to avoid multiple recompilations
+     */
+    public buildId: number;
+
+    /** List of emitted variables */
+    public variableNames: { [key: string]: number } = {};
+
+    /** Should emit comments? */
+    public emitComments: boolean;
+
+    /** Emit build activity */
+    public verbose: boolean;
+
+    /**
+     * Gets the compilation hints emitted at compilation time
+     */
+    public hints = {
+        needWorldMatrix: false,
+        needViewMatrix: false,
+        needProjectionMatrix: false,
+        needViewProjectionMatrix: false,
+        needWorldViewMatrix: false,
+        needWorldViewProjectionMatrix: false,
+        needFogColor: false,
+        needFogParameters: false
+    };
+
+    /**
+     * List of compilation checks
+     */
+    public checks = {
+        emitVertex: false,
+        emitFragment: false,
+        notConnectedNonOptionalInputs: new Array<NodeMaterialConnectionPoint>()
+    };
+
+    /**
+     * Emits console errors and exceptions if there is a failing check
+     */
+    public emitErrors() {
+        let shouldThrowError = false;
+
+        if (!this.checks.emitVertex) {
+            shouldThrowError = true;
+            console.error("NodeMaterial does not have a vertex output. You need to at least add a block that generates a glPosition value.");
+        }
+        if (!this.checks.emitFragment) {
+            shouldThrowError = true;
+            console.error("NodeMaterial does not have a fragment output. You need to at least add a block that generates a glFragColor value.");
+        }
+        for (var notConnectedInput of this.checks.notConnectedNonOptionalInputs) {
+            shouldThrowError = true;
+            console.error(`input ${notConnectedInput.name} from block ${notConnectedInput.ownerBlock.name}[${notConnectedInput.ownerBlock.getClassName()}] is not connected and is not optional.`);
+        }
+
+        if (shouldThrowError) {
+            throw "Build of NodeMaterial failed.";
+        }
+    }
+}