Bladeren bron

Adding support for swizzles

David Catuhe 6 jaren geleden
bovenliggende
commit
d1c533a7b4
26 gewijzigde bestanden met toevoegingen van 444 en 115 verwijderingen
  1. 2 2
      src/Loading/sceneLoader.ts
  2. 16 4
      src/Materials/Node/Blocks/Dual/fogBlock.ts
  3. 4 2
      src/Materials/Node/Blocks/Fragment/alphaTestBlock.ts
  4. 7 2
      src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts
  5. 3 3
      src/Materials/Node/Blocks/Fragment/rgbMergerBlock.ts
  6. 2 2
      src/Materials/Node/Blocks/Fragment/rgbSplitterBlock.ts
  7. 5 5
      src/Materials/Node/Blocks/Fragment/rgbaMergerBlock.ts
  8. 2 2
      src/Materials/Node/Blocks/Fragment/rgbaSplitterBlock.ts
  9. 91 0
      src/Materials/Node/Blocks/Vertex/bonesBlock.ts
  10. 2 1
      src/Materials/Node/Blocks/Vertex/index.ts
  11. 2 2
      src/Materials/Node/Blocks/Vertex/vertexOutputBlock.ts
  12. 2 2
      src/Materials/Node/Blocks/addBlock.ts
  13. 2 2
      src/Materials/Node/Blocks/clampBlock.ts
  14. 2 1
      src/Materials/Node/Blocks/index.ts
  15. 41 0
      src/Materials/Node/Blocks/matrixMultiplication.ts
  16. 2 2
      src/Materials/Node/Blocks/multiplyBlock.ts
  17. 2 2
      src/Materials/Node/Blocks/textureBlock.ts
  18. 2 2
      src/Materials/Node/Blocks/vector2TransformBlock.ts
  19. 2 2
      src/Materials/Node/Blocks/vector3TransformBlock.ts
  20. 2 2
      src/Materials/Node/Blocks/vector4TransformBlock.ts
  21. 71 40
      src/Materials/Node/nodeMaterial.ts
  22. 52 10
      src/Materials/Node/nodeMaterialBlock.ts
  23. 41 4
      src/Materials/Node/nodeMaterialBlockConnectionPoint.ts
  24. 2 0
      src/Materials/Node/nodeMaterialBlockConnectionPointTypes.ts
  25. 49 18
      src/Materials/Node/nodeMaterialCompilationState.ts
  26. 36 3
      src/Materials/Node/nodeMaterialCompilationStateSharedData.ts

+ 2 - 2
src/Loading/sceneLoader.ts

@@ -584,7 +584,7 @@ export class SceneLoader {
                 onProgress(event);
             }
             catch (e) {
-                errorHandler("Error in onProgress callback", e);
+                errorHandler("Error in onProgress callback: " + e, e);
             }
         } : undefined;
 
@@ -596,7 +596,7 @@ export class SceneLoader {
                     onSuccess(meshes, particleSystems, skeletons, animationGroups);
                 }
                 catch (e) {
-                    errorHandler("Error in onSuccess callback", e);
+                    errorHandler("Error in onSuccess callback: " + e, e);
                 }
             }
 

+ 16 - 4
src/Materials/Node/Blocks/Dual/fogBlock.ts

@@ -1,8 +1,10 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Effect } from '../../../effect';
 
 /**
  * Block used to add support for scene fog
@@ -13,7 +15,7 @@ export class FogBlock extends NodeMaterialBlock {
      * @param name defines the block name
      */
     public constructor(name: string) {
-        super(name, NodeMaterialBlockTargets.VertexAndFragment);
+        super(name, NodeMaterialBlockTargets.VertexAndFragment, true);
 
         // Vertex
         this.registerInput("worldPos", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
@@ -42,12 +44,22 @@ export class FogBlock extends NodeMaterialBlock {
         return "FogBlock";
     }
 
+    public bind(effect: Effect, mesh?: Mesh) {
+        if (!mesh) {
+            return;
+        }
+
+        const scene = mesh.getScene();
+        effect.setColor3("fogColor", scene.fogColor);
+        effect.setFloat4("fogParameters", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
+    }
+
     /** @hidden */
     public get _canAddAtFragmentRoot(): boolean {
         return false;
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         if (state.target === NodeMaterialBlockTargets.Fragment) {
@@ -55,7 +67,7 @@ export class FogBlock extends NodeMaterialBlock {
                 removeUniforms: true,
                 removeVaryings: true,
                 removeifDef: true,
-                replaceString: ["float CalcFogFactor()", "float CalcFogFactor(vec3 vFogDistance, vec4 vFogInfos)"]
+                replaceStrings: [{ search: /float CalcFogFactor\(\)/, replace: "float CalcFogFactor(vec3 vFogDistance, vec4 vFogInfos)" }]
             });
 
             let tempFogVariablename = state._getFreeVariableName("fog");

+ 4 - 2
src/Materials/Node/Blocks/Fragment/alphaTestBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -41,11 +41,13 @@ export class AlphaTestBlock extends NodeMaterialBlock {
         return false;
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let input = this._inputs[0];
 
+        state.sharedData.hints.needAlphaTesting = true;
+
         state.compilationString += `if (${input.associatedVariableName}.a < ${this.alphaCutOff}) discard;\r\n`;
 
         return this;

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

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -8,6 +8,10 @@ import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
  */
 export class FragmentOutputBlock extends NodeMaterialBlock {
     /**
+     * Gets or sets a boolean indicating if this block will output an alpha value
+     */
+    public alphaBlendingEnabled = false;
+    /**
      * Create a new FragmentOutputBlock
      * @param name defines the block name
      */
@@ -35,10 +39,11 @@ export class FragmentOutputBlock extends NodeMaterialBlock {
         return false;
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let input = this._inputs[0];
+        state.sharedData.hints.needAlphaBlending = this.alphaBlendingEnabled;
 
         state.compilationString += `gl_FragColor = ${input.associatedVariableName};\r\n`;
 

+ 3 - 3
src/Materials/Node/Blocks/Fragment/rgbMergerBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -29,7 +29,7 @@ export class RGBMergerBlock extends NodeMaterialBlock {
         return "RGBMergerBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let rInput = this._inputs[0];
@@ -38,7 +38,7 @@ export class RGBMergerBlock extends NodeMaterialBlock {
 
         let output = this._outputs[0];
 
-        state.compilationString += this._declareOutput(output, state) + ` = vec3(${rInput.associatedVariableName}, ${gInput.associatedVariableName}, ${bInput.associatedVariableName});\r\n`;
+        state.compilationString += this._declareOutput(output, state) + ` = vec3(${this._writeVariable(rInput)}, ${this._writeVariable(gInput)}, ${this._writeVariable(bInput)});\r\n`;
 
         return this;
     }

+ 2 - 2
src/Materials/Node/Blocks/Fragment/rgbSplitterBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -29,7 +29,7 @@ export class RGBSplitterBlock extends NodeMaterialBlock {
         return "RGBSplitterBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let input = this._inputs[0];

+ 5 - 5
src/Materials/Node/Blocks/Fragment/rgbaMergerBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -17,7 +17,7 @@ export class RGBAMergerBlock extends NodeMaterialBlock {
         this.registerInput("r", NodeMaterialBlockConnectionPointTypes.Float, true);
         this.registerInput("g", NodeMaterialBlockConnectionPointTypes.Float, true);
         this.registerInput("b", NodeMaterialBlockConnectionPointTypes.Float, true);
-        this.registerInput("rgb", NodeMaterialBlockConnectionPointTypes.Vector3OrColor3, true);
+        this.registerInput("rgb", NodeMaterialBlockConnectionPointTypes.Vector3OrColor3OrVector4OrColor4, true);
         this.registerInput("a", NodeMaterialBlockConnectionPointTypes.Float, true);
 
         this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Color4);
@@ -31,7 +31,7 @@ export class RGBAMergerBlock extends NodeMaterialBlock {
         return "RGBAMergerBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let rgbInput = this._inputs[3];
@@ -39,12 +39,12 @@ export class RGBAMergerBlock extends NodeMaterialBlock {
         let output = this._outputs[0];
 
         if (rgbInput.connectedPoint) {
-            state.compilationString += this._declareOutput(output, state) + ` = vec4(${rgbInput.associatedVariableName}, ${aInput.associatedVariableName});\r\n`;
+            state.compilationString += this._declareOutput(output, state) + ` = vec4(${rgbInput.associatedVariableName}.rgb, ${this._writeVariable(aInput)});\r\n`;
         } else {
             let rInput = this._inputs[0];
             let gInput = this._inputs[1];
             let bInput = this._inputs[2];
-            state.compilationString += this._declareOutput(output, state) + ` = vec4(${rInput.associatedVariableName}, ${gInput.associatedVariableName}, ${bInput.associatedVariableName}, ${aInput.associatedVariableName});\r\n`;
+            state.compilationString += this._declareOutput(output, state) + ` = vec4(${this._writeVariable(rInput)}, ${this._writeVariable(gInput)}, ${this._writeVariable(bInput)}, ${this._writeVariable(aInput)});\r\n`;
         }
 
         return this;

+ 2 - 2
src/Materials/Node/Blocks/Fragment/rgbaSplitterBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -30,7 +30,7 @@ export class RGBASplitterBlock extends NodeMaterialBlock {
         return "RGBASplitterBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let input = this._inputs[0];

+ 91 - 0
src/Materials/Node/Blocks/Vertex/bonesBlock.ts

@@ -0,0 +1,91 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialWellKnownValues } from '../../nodeMaterialWellKnownValues';
+import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
+import { Mesh } from '../../../../Meshes/mesh';
+import { Effect } from '../../../effect';
+import { MaterialHelper } from '../../../materialHelper';
+
+/**
+ * Block used to add support for vertex skinning (bones)
+ */
+export class BonesBlock extends NodeMaterialBlock {
+    /**
+     * Creates a new BonesBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Vertex, false, true);
+
+        this.registerInput("matricesIndices", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("matricesWeights", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("matricesIndicesExtra", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("matricesWeightsExtra", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerInput("world", NodeMaterialBlockConnectionPointTypes.Matrix);
+
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Matrix);
+
+        // Auto configuration
+        this._inputs[0].setAsAttribute();
+        this._inputs[1].setAsAttribute();
+        this._inputs[2].setAsAttribute();
+        this._inputs[3].setAsAttribute();
+        this._inputs[4].setAsWellKnownValue(NodeMaterialWellKnownValues.World);
+    }
+
+    /**
+     * Initialize the block and prepare the context for build
+     * @param state defines the state that will be used for the build
+     */
+    public initialize(state: NodeMaterialBuildState) {
+        state._excludeVariableName("boneSampler");
+        state._excludeVariableName("boneTextureWidth");
+        state._excludeVariableName("mBones");
+        state._excludeVariableName("BonesPerMesh");
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "BonesBlock";
+    }
+
+    public bind(effect: Effect, mesh?: Mesh) {
+        MaterialHelper.BindBonesParameters(mesh, effect);
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        state._emitFunctionFromInclude("BonesDeclaration", "bonesDeclaration", {
+            removeAttributes: true,
+            removeUniforms: false,
+            removeVaryings: true,
+            removeifDef: false
+        });
+
+        let influenceVariablename = state._getFreeVariableName("influence");
+
+        state.compilationString += state._emitCodeFromInclude("bonesVertex", {
+            replaceStrings: [
+                {
+                    search: /finalWorld=finalWorld\*influence;/,
+                    replace: ""
+                },
+                {
+                    search: /influence/gm,
+                    replace: influenceVariablename
+                }
+            ]
+        });
+
+        let output = this._outputs[0];
+        let worldInput = this._inputs[4];
+
+        state.compilationString += this._declareOutput(output, state) + ` = ${worldInput.associatedVariableName} * ${influenceVariablename};`;
+        return this;
+    }
+}

+ 2 - 1
src/Materials/Node/Blocks/Vertex/index.ts

@@ -1 +1,2 @@
-export * from "./vertexOutputBlock";
+export * from "./vertexOutputBlock";
+export * from "./bonesBlock";

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

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../../nodeMaterialBlockTargets';
 
 /**
@@ -36,7 +36,7 @@ export class VertexOutputBlock extends NodeMaterialBlock {
         return false;
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let input = this._inputs[0];

+ 2 - 2
src/Materials/Node/Blocks/addBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 /**
  * Block used to add 2 vector4
  */
@@ -25,7 +25,7 @@ export class AddBlock extends NodeMaterialBlock {
         return "AddBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let output = this._outputs[0];

+ 2 - 2
src/Materials/Node/Blocks/clampBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 /**
  * Block used to clamp a float
  */
@@ -30,7 +30,7 @@ export class ClampBlock extends NodeMaterialBlock {
         return "ClampBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let output = this._outputs[0];

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

@@ -7,4 +7,5 @@ export * from "./clampBlock";
 export * from "./textureBlock";
 export * from "./vector2TransformBlock";
 export * from "./vector3TransformBlock";
-export * from "./vector4TransformBlock";
+export * from "./vector4TransformBlock";
+export * from "./matrixMultiplication";

+ 41 - 0
src/Materials/Node/Blocks/matrixMultiplication.ts

@@ -0,0 +1,41 @@
+import { NodeMaterialBlock } from '../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
+import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
+
+/**
+ * Block used to multiply two matrices
+ */
+export class MatrixMultiplicationBlock extends NodeMaterialBlock {
+    /**
+     * Creates a new MatrixMultiplicationBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Vertex);
+
+        this.registerInput("left", NodeMaterialBlockConnectionPointTypes.Matrix);
+        this.registerInput("right", NodeMaterialBlockConnectionPointTypes.Matrix);
+        this.registerOutput("output", NodeMaterialBlockConnectionPointTypes.Matrix);
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "MatrixMultiplicationBlock";
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        super._buildBlock(state);
+
+        let output = this._outputs[0];
+        let input0 = this._inputs[0];
+        let input1 = this._inputs[1];
+
+        state.compilationString += this._declareOutput(output, state) + ` = ${input0.associatedVariableName} * ${input1.associatedVariableName};\r\n`;
+
+        return this;
+    }
+}

+ 2 - 2
src/Materials/Node/Blocks/multiplyBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 /**
  * Block used to multiply 2 vector4
  */
@@ -25,7 +25,7 @@ export class MultiplyBlock extends NodeMaterialBlock {
         return "MultiplyBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let output = this._outputs[0];

+ 2 - 2
src/Materials/Node/Blocks/textureBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 /**
@@ -28,7 +28,7 @@ export class TextureBlock extends NodeMaterialBlock {
         return "TextureBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let uvInput = this._inputs[0];

+ 2 - 2
src/Materials/Node/Blocks/vector2TransformBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 /**
@@ -37,7 +37,7 @@ export class Vector2TransformBlock extends NodeMaterialBlock {
         return "Vector2TransformBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let output = this._outputs[0];

+ 2 - 2
src/Materials/Node/Blocks/vector3TransformBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 /**
@@ -32,7 +32,7 @@ export class Vector3TransformBlock extends NodeMaterialBlock {
         return "Vector3TransformBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let output = this._outputs[0];

+ 2 - 2
src/Materials/Node/Blocks/vector4TransformBlock.ts

@@ -1,6 +1,6 @@
 import { NodeMaterialBlock } from '../nodeMaterialBlock';
 import { NodeMaterialBlockConnectionPointTypes } from '../nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from '../nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from '../nodeMaterialBuildState';
 import { NodeMaterialBlockTargets } from '../nodeMaterialBlockTargets';
 
 /**
@@ -28,7 +28,7 @@ export class Vector4TransformBlock extends NodeMaterialBlock {
         return "Vector4TransformBlock";
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         super._buildBlock(state);
 
         let output = this._outputs[0];

+ 71 - 40
src/Materials/Node/nodeMaterial.ts

@@ -5,29 +5,20 @@ import { AbstractMesh } from '../../Meshes/abstractMesh';
 import { Matrix } from '../../Maths/math';
 import { Mesh } from '../../Meshes/mesh';
 import { Engine } from '../../Engines/engine';
-import { NodeMaterialCompilationState } from './nodeMaterialCompilationState';
-import { EffectCreationOptions } from '../effect';
+import { NodeMaterialBuildState } from './nodeMaterialBuildState';
+import { EffectCreationOptions, EffectFallbacks } from '../effect';
 import { BaseTexture } from '../../Materials/Textures/baseTexture';
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { Observable } from '../../Misc/observable';
 import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
-import { NodeMaterialCompilationStateSharedData } from './nodeMaterialCompilationStateSharedData';
+import { NodeMaterialBuildStateSharedData } from './NodeMaterialBuildStateSharedData';
 
 /**
  * Class used to configure NodeMaterial
  */
 export interface INodeMaterialOptions {
     /**
-     * Defines if the material needs alpha blending
-     */
-    needAlphaBlending: boolean;
-    /**
-     * Defines if the material needs alpha testing
-     */
-    needAlphaTesting: boolean;
-
-    /**
      * Defines if blocks should emit comments
      */
     emitComments: boolean;
@@ -38,8 +29,8 @@ export interface INodeMaterialOptions {
  */
 export class NodeMaterial extends Material {
     private _options: INodeMaterialOptions;
-    private _vertexCompilationState: NodeMaterialCompilationState;
-    private _fragmentCompilationState: NodeMaterialCompilationState;
+    private _vertexCompilationState: NodeMaterialBuildState;
+    private _fragmentCompilationState: NodeMaterialBuildState;
     private _buildId: number = 0;
     private _renderId: number;
     private _effectCompileId: number = 0;
@@ -81,8 +72,6 @@ export class NodeMaterial extends Material {
         super(name, scene || Engine.LastCreatedScene!);
 
         this._options = {
-            needAlphaBlending: false,
-            needAlphaTesting: false,
             emitComments: false,
             ...options
         };
@@ -187,7 +176,7 @@ export class NodeMaterial extends Material {
      * @returns a boolean specifying if alpha blending is needed
      */
     public needAlphaBlending(): boolean {
-        return (this.alpha < 1.0) || this._options.needAlphaBlending;
+        return (this.alpha < 1.0) || this._fragmentCompilationState.sharedData.hints.needAlphaBlending;
     }
 
     /**
@@ -195,16 +184,17 @@ export class NodeMaterial extends Material {
      * @returns a boolean specifying if an alpha test is needed.
      */
     public needAlphaTesting(): boolean {
-        return this._options.needAlphaTesting;
+        return this._fragmentCompilationState.sharedData.hints.needAlphaTesting;
     }
 
-    private _propagateTarget(node: NodeMaterialBlock, target: NodeMaterialBlockTargets) {
+    private _propagateTarget(node: NodeMaterialBlock, target: NodeMaterialBlockTargets, state: NodeMaterialBuildState) {
         node.target = target;
+        node.initialize(state);
 
         for (var exitPoint of node.outputs) {
             for (var block of exitPoint.connectedBlocks) {
                 if (block) {
-                    this._propagateTarget(block, target);
+                    this._propagateTarget(block, target, state);
                 }
             }
         }
@@ -237,34 +227,36 @@ export class NodeMaterial extends Material {
             throw "You must define at least one fragmentRootNode";
         }
 
+        // Compilation state
+        this._vertexCompilationState = new NodeMaterialBuildState();
+        this._vertexCompilationState.target = NodeMaterialBlockTargets.Vertex;
+        this._fragmentCompilationState = new NodeMaterialBuildState();
+        this._fragmentCompilationState.target = NodeMaterialBlockTargets.Fragment;
+
+        // Shared data
+        let sharedData = new NodeMaterialBuildStateSharedData();
+        this._vertexCompilationState.sharedData = sharedData;
+        this._fragmentCompilationState.sharedData = sharedData;
+        sharedData.buildId = this._buildId;
+        sharedData.emitComments = this._options.emitComments;
+        sharedData.verbose = verbose;
+
         // Propagate targets
         for (var vertexRootNode of this._vertexRootNodes) {
-            this._propagateTarget(vertexRootNode, NodeMaterialBlockTargets.Vertex);
+            this._propagateTarget(vertexRootNode, NodeMaterialBlockTargets.Vertex, this._vertexCompilationState);
         }
 
         for (var fragmentRootNode of this._fragmentRootNodes) {
-            this._propagateTarget(fragmentRootNode, NodeMaterialBlockTargets.Fragment);
+            this._propagateTarget(fragmentRootNode, NodeMaterialBlockTargets.Fragment, this._fragmentCompilationState);
         }
 
         // Vertex
-        this._vertexCompilationState = new NodeMaterialCompilationState();
-        this._vertexCompilationState.target = NodeMaterialBlockTargets.Vertex;
-        this._fragmentCompilationState = new NodeMaterialCompilationState();
-        let sharedData = new NodeMaterialCompilationStateSharedData();
-        this._vertexCompilationState.sharedData = sharedData;
-        this._fragmentCompilationState.sharedData = sharedData;
-        sharedData.buildId = this._buildId;
-        sharedData.emitComments = this._options.emitComments;
-        sharedData.verbose = verbose;
-
         for (var vertexRootNode of this._vertexRootNodes) {
             vertexRootNode.build(this._vertexCompilationState);
         }
 
         // Fragment
-        this._fragmentCompilationState.target = NodeMaterialBlockTargets.Fragment;
         this._fragmentCompilationState._vertexState = this._vertexCompilationState;
-        this._fragmentCompilationState._uniformConnectionPoints = this._vertexCompilationState._uniformConnectionPoints;
 
         for (var fragmentRootNode of this._fragmentRootNodes) {
             this._resetDualBlocks(fragmentRootNode, this._buildId - 1);
@@ -279,7 +271,7 @@ export class NodeMaterial extends Material {
         this._fragmentCompilationState.finalize(this._fragmentCompilationState);
 
         // Textures
-        this._textureConnectionPoints = this._fragmentCompilationState._uniformConnectionPoints.filter((u) => u.type === NodeMaterialBlockConnectionPointTypes.Texture);
+        this._textureConnectionPoints = sharedData.uniformConnectionPoints.filter((u) => u.type === NodeMaterialBlockConnectionPointTypes.Texture);
 
         this._buildId++;
 
@@ -305,6 +297,8 @@ export class NodeMaterial extends Material {
     public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
         var scene = this.getScene();
         var engine = scene.getEngine();
+        let defines: string[] = [];
+        var fallbacks = new EffectFallbacks();
 
         if (!this.checkReadyOnEveryCall) {
             if (this._renderId === scene.getRenderId()) {
@@ -349,10 +343,39 @@ export class NodeMaterial extends Material {
             if (index === -1) {
                 mergedSamplers.push(s);
             }
-
         });
 
+        // Bones
+        if (mesh && mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
+            const skeleton = mesh.skeleton;
+
+            defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
+            fallbacks.addCPUSkinningFallback(0, mesh);
+
+            if (skeleton.isUsingTextureForMatrices) {
+                defines.push("#define BONETEXTURE");
+
+                if (mergedUniforms.indexOf("boneTextureWidth") === -1) {
+                    mergedUniforms.push("boneTextureWidth");
+                }
+
+                if (mergedSamplers.indexOf("boneSampler") === -1) {
+                    mergedSamplers.push("boneSampler");
+                }
+            } else {
+                defines.push("#define BonesPerMesh " + (skeleton.bones.length + 1));
+
+                if (mergedUniforms.indexOf("mBones") === -1) {
+                    mergedUniforms.push("mBones");
+                }
+            }
+
+        } else {
+            defines.push("#define NUM_BONE_INFLUENCERS 0");
+        }
+
         // Compilation
+        var join = defines.join("\n");
         this._effect = engine.createEffect({
             vertex: "nodeMaterial" + this._buildId,
             fragment: "nodeMaterial" + this._buildId,
@@ -361,8 +384,9 @@ export class NodeMaterial extends Material {
         }, <EffectCreationOptions>{
             attributes: this._vertexCompilationState.attributes,
             uniformsNames: mergedUniforms,
-            samplers: this._fragmentCompilationState.samplers,
-            defines: "",
+            samplers: mergedSamplers,
+            defines: join,
+            fallbacks: fallbacks,
             onCompiled: this.onCompiled,
             onError: this.onError
         }, engine);
@@ -417,8 +441,9 @@ export class NodeMaterial extends Material {
         // Std values
         this.bindOnlyWorldMatrix(world);
 
+        let sharedData = this._fragmentCompilationState.sharedData;
         if (this._effect && scene.getCachedMaterial() !== this) {
-            let hints = this._fragmentCompilationState.sharedData.hints;
+            let hints = sharedData.hints;
 
             if (hints.needViewMatrix) {
                 this._effect.setMatrix("view", scene.getViewMatrix());
@@ -440,9 +465,15 @@ export class NodeMaterial extends Material {
                 this._effect.setFloat4("fogParameters", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
             }
 
-            for (var connectionPoint of this._fragmentCompilationState._uniformConnectionPoints) {
+            // Connection points
+            for (var connectionPoint of sharedData.uniformConnectionPoints) {
                 connectionPoint.transmit(this._effect);
             }
+
+            // Bindable blocks
+            for (var block of sharedData.activeBlocks) {
+                block.bind(this._effect, mesh);
+            }
         }
 
         this._afterBind(mesh);

+ 52 - 10
src/Materials/Node/nodeMaterialBlock.ts

@@ -1,8 +1,10 @@
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
-import { NodeMaterialCompilationState } from './nodeMaterialCompilationState';
+import { NodeMaterialBuildState } from './nodeMaterialBuildState';
 import { Nullable } from '../../types';
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
 import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
+import { Effect } from '../effect';
+import { Mesh } from '../../Meshes/mesh';
 
 /**
  * Defines a block that can be used inside a node based material
@@ -11,6 +13,7 @@ export class NodeMaterialBlock {
     private _buildId: number;
     private _target: NodeMaterialBlockTargets;
     private _isFinalMerger = false;
+    private _isBindable = false;
 
     /** @hidden */
     protected _inputs = new Array<NodeMaterialConnectionPoint>();
@@ -30,6 +33,13 @@ export class NodeMaterialBlock {
     }
 
     /**
+     * Gets a boolean indicating that this block needs to bind data to effect
+     */
+    public get isBindable(): boolean {
+        return this._isBindable;
+    }
+
+    /**
      * Gets or sets the build Id
      */
     public get buildId(): number {
@@ -101,18 +111,37 @@ export class NodeMaterialBlock {
      * @param name defines the block name
      * @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
+     * @param isBindable defines that this block needs to bind data to effect
      */
-    public constructor(name: string, target = NodeMaterialBlockTargets.Vertex, isFinalMerger = false) {
+    public constructor(name: string, target = NodeMaterialBlockTargets.Vertex, isFinalMerger = false, isBindable = false) {
         this.name = name;
 
         this._target = target;
+        this._isBindable = isBindable;
 
         if (isFinalMerger) {
             this._isFinalMerger = true;
         }
     }
 
-    protected _declareOutput(output: NodeMaterialConnectionPoint, state: NodeMaterialCompilationState): string {
+    /**
+     * Initialize the block and prepare the context for build
+     * @param state defines the state that will be used for the build
+     */
+    public initialize(state: NodeMaterialBuildState) {
+        // Do nothing
+    }
+
+    /**
+     * Bind data to effect. Will only be called for blocks with isBindable === true
+     * @param effect defines the effect to bind data to
+     * @param mesh defines the mesh that will be rendered
+     */
+    public bind(effect: Effect, mesh?: Mesh) {
+        // Do nothing
+    }
+
+    protected _declareOutput(output: NodeMaterialConnectionPoint, state: NodeMaterialBuildState): string {
         if (output.isVarying) {
             return `${output.associatedVariableName}`;
         }
@@ -120,6 +149,11 @@ export class NodeMaterialBlock {
         return `${state._getGLType(output.type)} ${output.associatedVariableName}`;
     }
 
+    protected _writeVariable(currentPoint: NodeMaterialConnectionPoint): string {
+        let connectionPoint = currentPoint.connectedPoint!;
+        return `${currentPoint.associatedVariableName}${connectionPoint.swizzle ? "." + connectionPoint.swizzle : ""}`;
+    }
+
     protected _writeFloat(value: number) {
         let stringVersion = value.toString();
 
@@ -212,19 +246,23 @@ export class NodeMaterialBlock {
     /**
      * Connect current block with another block
      * @param other defines the block to connect with
-     * @param inputName define the name of the other block input (will take the first available one if not defined)
-     * @param outputName define the name of current block output (will take the first one if not defined)
+     * @param options define the various options to help pick the right connections
      * @returns the current block
      */
-    public connectTo(other: NodeMaterialBlock, inputName?: string, outputName?: string) {
+    public connectTo(other: NodeMaterialBlock, options?: {
+        input?: string,
+        output?: string,
+        outputSwizzle?: string
+    }) {
         if (this._outputs.length === 0) {
             return;
         }
 
-        let output = outputName ? this.getOutputByName(outputName) : this.getFirstAvailableOutput(other);
-        let input = inputName ? other.getInputByName(inputName) : other.getFirstAvailableInput(output);
+        let output = options && options.output ? this.getOutputByName(options.output) : this.getFirstAvailableOutput(other);
+        let input = options && options.input ? other.getInputByName(options.input) : other.getFirstAvailableInput(output);
 
         if (output && input) {
+            output.swizzle = options ? options.outputSwizzle || "" : "";
             output.connectTo(input);
         } else {
             throw "Unable to find a compatible match";
@@ -233,7 +271,7 @@ export class NodeMaterialBlock {
         return this;
     }
 
-    protected _buildBlock(state: NodeMaterialCompilationState) {
+    protected _buildBlock(state: NodeMaterialBuildState) {
         // Empty. Must be defined by child nodes
     }
 
@@ -252,7 +290,7 @@ export class NodeMaterialBlock {
      * @param state defines the current compilation state (uniforms, samplers, current string)
      * @returns the current block
      */
-    public build(state: NodeMaterialCompilationState) {
+    public build(state: NodeMaterialBuildState) {
         if (this._buildId === state.sharedData.buildId) {
             return;
         }
@@ -280,6 +318,10 @@ export class NodeMaterialBlock {
             return; // Need to check again as inputs can be connected multiple time to this endpoint
         }
 
+        if (this.isBindable) {
+            state.sharedData.activeBlocks.push(this);
+        }
+
         // Logs
         if (state.sharedData.verbose) {
             console.log(`${state.target === NodeMaterialBlockTargets.Vertex ? "Vertex shader" : "Fragment shader"}: Building ${this.name} [${this.getClassName()}]`);

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

@@ -32,6 +32,11 @@ export class NodeMaterialConnectionPoint {
     public name: string;
 
     /**
+     * Gets or sets the swizzle to apply to this connection point when reading or writing
+     */
+    public swizzle: string;
+
+    /**
      * Gets or sets a boolean indicating that this connection point can be omitted
      */
     public isOptional: boolean;
@@ -160,11 +165,13 @@ export class NodeMaterialConnectionPoint {
 
     /**
      * Set the source of this connection point to a vertex attribute
-     * @param attributeName defines the attribute name (position, uv, normal, etc...)
+     * @param attributeName defines the attribute name (position, uv, normal, etc...). If not specified it will take the connection point name
      * @returns the current connection point
      */
-    public setAsAttribute(attributeName: string): NodeMaterialConnectionPoint {
-        this.name = attributeName;
+    public setAsAttribute(attributeName?: string): NodeMaterialConnectionPoint {
+        if (attributeName) {
+            this.name = attributeName;
+        }
         this.isAttribute = true;
         return this;
     }
@@ -206,6 +213,23 @@ export class NodeMaterialConnectionPoint {
         return this;
     }
 
+    private _getTypeLength(type: NodeMaterialBlockConnectionPointTypes) {
+        switch (type) {
+            case NodeMaterialBlockConnectionPointTypes.Float:
+                return 1;
+            case NodeMaterialBlockConnectionPointTypes.Vector2:
+                return 2;
+            case NodeMaterialBlockConnectionPointTypes.Vector3:
+            case NodeMaterialBlockConnectionPointTypes.Color3:
+                return 3;
+            case NodeMaterialBlockConnectionPointTypes.Vector4:
+            case NodeMaterialBlockConnectionPointTypes.Color4:
+                return 3;
+        }
+
+        return -1;
+    }
+
     /**
      * Connect this point to another connection point
      * @param connectionPoint defines the other connection point
@@ -213,7 +237,20 @@ export class NodeMaterialConnectionPoint {
      */
     public connectTo(connectionPoint: NodeMaterialConnectionPoint): NodeMaterialConnectionPoint {
         if ((this.type & connectionPoint.type) === 0) {
-            throw "Cannot connect two different connection types.";
+            let fail = true;
+            // Check swizzle
+            if (this.swizzle) {
+                let swizzleLength = this.swizzle.length;
+                let connectionLength = this._getTypeLength(connectionPoint.type);
+
+                if (swizzleLength === connectionLength) {
+                    fail = false;
+                }
+            }
+
+            if (fail) {
+                throw "Cannot connect two different connection types.";
+            }
         }
 
         this._endpoints.push(connectionPoint);

+ 2 - 0
src/Materials/Node/nodeMaterialBlockConnectionPointTypes.ts

@@ -26,4 +26,6 @@ export enum NodeMaterialBlockConnectionPointTypes {
     Vector4OrColor4 = Vector4 | Color4,
     /** Color3 or Color4 */
     Color3OrColor4 = Color3 | Color4,
+    /** Vector3 or Color3 */
+    Vector3OrColor3OrVector4OrColor4 = Vector3 | Color3 | Vector4 | Color4,
 }

+ 49 - 18
src/Materials/Node/nodeMaterialCompilationState.ts

@@ -2,13 +2,13 @@ import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint'
 import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 import { NodeMaterialWellKnownValues } from './nodeMaterialWellKnownValues';
 import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
-import { NodeMaterialCompilationStateSharedData } from './nodeMaterialCompilationStateSharedData';
-import { Effect } from '../../Materials/effect';
+import { NodeMaterialBuildStateSharedData } from './NodeMaterialBuildStateSharedData';
+import { Effect } from '../effect';
 
 /**
- * Class used to store node based material compilation state
+ * Class used to store node based material build state
  */
-export class NodeMaterialCompilationState {
+export class NodeMaterialBuildState {
     /**
      * Gets the list of emitted attributes
      */
@@ -31,15 +31,12 @@ export class NodeMaterialCompilationState {
     public target: NodeMaterialBlockTargets;
 
     /**
-     * Shared data between multiple NodeMaterialCompilationState instances
+     * Shared data between multiple NodeMaterialBuildState instances
      */
-    public sharedData: NodeMaterialCompilationStateSharedData;
+    public sharedData: NodeMaterialBuildStateSharedData;
 
     /** @hidden */
-    public _uniformConnectionPoints = new Array<NodeMaterialConnectionPoint>();
-
-    /** @hidden */
-    public _vertexState: NodeMaterialCompilationState;
+    public _vertexState: NodeMaterialBuildState;
 
     private _attributeDeclaration = "";
     private _uniformDeclaration = "";
@@ -55,7 +52,7 @@ export class NodeMaterialCompilationState {
      * Finalize the compilation strings
      * @param state defines the current compilation state
      */
-    public finalize(state: NodeMaterialCompilationState) {
+    public finalize(state: NodeMaterialBuildState) {
         let emitComments = state.sharedData.emitComments;
         let isFragmentMode = (this.target === NodeMaterialBlockTargets.Fragment);
 
@@ -101,6 +98,11 @@ export class NodeMaterialCompilationState {
     }
 
     /** @hidden */
+    public _excludeVariableName(name: string) {
+        this.sharedData.variableNames[name] = 0;
+    }
+
+    /** @hidden */
     public _getGLType(type: NodeMaterialBlockConnectionPointTypes): string {
         switch (type) {
             case NodeMaterialBlockConnectionPointTypes.Float:
@@ -136,11 +138,32 @@ export class NodeMaterialCompilationState {
     }
 
     /** @hidden */
+    public _emitCodeFromInclude(includeName: string, options?: {
+        replaceStrings?: { search: RegExp, replace: string }[],
+    }) {
+        let code = Effect.IncludesShadersStore[includeName] + "\r\n";
+
+        if (!options) {
+            return code;
+        }
+
+        if (options.replaceStrings) {
+            for (var index = 0; index < options.replaceStrings.length; index++) {
+                let replaceString = options.replaceStrings[index];
+                code = code.replace(replaceString.search, replaceString.replace);
+            }
+        }
+
+        return code;
+    }
+
+    /** @hidden */
     public _emitFunctionFromInclude(name: string, includeName: string, options?: {
+        removeAttributes?: boolean,
         removeUniforms?: boolean,
         removeVaryings?: boolean,
         removeifDef?: boolean,
-        replaceString?: string[],
+        replaceStrings?: { search: RegExp, replace: string }[],
     }) {
         if (this.functions[name]) {
             return;
@@ -153,7 +176,14 @@ export class NodeMaterialCompilationState {
         }
 
         if (options.removeifDef) {
-            this.functions[name] = this.functions[name].replace(/^\s*?#.+$/gm, "");
+            this.functions[name] = this.functions[name].replace(/^\s*?#ifdef.+$/gm, "");
+            this.functions[name] = this.functions[name].replace(/^\s*?#endif.*$/gm, "");
+            this.functions[name] = this.functions[name].replace(/^\s*?#else.*$/gm, "");
+            this.functions[name] = this.functions[name].replace(/^\s*?#elif.*$/gm, "");
+        }
+
+        if (options.removeAttributes) {
+            this.functions[name] = this.functions[name].replace(/^\s*?attribute.+$/gm, "");
         }
 
         if (options.removeUniforms) {
@@ -164,9 +194,10 @@ export class NodeMaterialCompilationState {
             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]);
+        if (options.replaceStrings) {
+            for (var index = 0; index < options.replaceStrings.length; index++) {
+                let replaceString = options.replaceStrings[index];
+                this.functions[name] = this.functions[name].replace(replaceString.search, replaceString.replace);
             }
         }
     }
@@ -200,7 +231,7 @@ export class NodeMaterialCompilationState {
 
             this.samplers.push(point.name);
             this._samplerDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
-            this._uniformConnectionPoints.push(point);
+            this.sharedData.uniformConnectionPoints.push(point);
             return;
         }
 
@@ -249,7 +280,7 @@ export class NodeMaterialCompilationState {
                         break;
                 }
             } else {
-                this._uniformConnectionPoints.push(point);
+                this.sharedData.uniformConnectionPoints.push(point);
             }
 
             return;

+ 36 - 3
src/Materials/Node/nodeMaterialCompilationStateSharedData.ts

@@ -1,9 +1,10 @@
 import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlock } from './nodeMaterialBlock';
 
 /**
- * Class used to store shared data between 2 NodeMaterialCompilationState
+ * Class used to store shared data between 2 NodeMaterialBuildState
  */
-export class NodeMaterialCompilationStateSharedData {
+export class NodeMaterialBuildStateSharedData {
     /**
      * Gets the list of emitted varyings
      */
@@ -15,6 +16,16 @@ export class NodeMaterialCompilationStateSharedData {
     public varyingDeclaration = "";
 
     /**
+     * Uniform connection points
+     */
+    public uniformConnectionPoints = new Array<NodeMaterialConnectionPoint>();
+
+    /**
+     * Active blocks (Blocks that need to set data to the effect)
+     */
+    public activeBlocks = new Array<NodeMaterialBlock>();
+
+    /**
      * Build Id used to avoid multiple recompilations
      */
     public buildId: number;
@@ -39,7 +50,9 @@ export class NodeMaterialCompilationStateSharedData {
         needWorldViewMatrix: false,
         needWorldViewProjectionMatrix: false,
         needFogColor: false,
-        needFogParameters: false
+        needFogParameters: false,
+        needAlphaBlending: false,
+        needAlphaTesting: false
     };
 
     /**
@@ -51,6 +64,26 @@ export class NodeMaterialCompilationStateSharedData {
         notConnectedNonOptionalInputs: new Array<NodeMaterialConnectionPoint>()
     };
 
+    /** Creates a new shared data */
+    public constructor() {
+        // Exclude usual attributes from free variable names
+        this.variableNames["position"] = 0;
+        this.variableNames["normal"] = 0;
+        this.variableNames["tangent"] = 0;
+        this.variableNames["uv"] = 0;
+        this.variableNames["uv2"] = 0;
+        this.variableNames["uv3"] = 0;
+        this.variableNames["uv4"] = 0;
+        this.variableNames["uv4"] = 0;
+        this.variableNames["uv5"] = 0;
+        this.variableNames["uv6"] = 0;
+        this.variableNames["color"] = 0;
+        this.variableNames["matricesIndices"] = 0;
+        this.variableNames["matricesWeights"] = 0;
+        this.variableNames["matricesIndicesExtra"] = 0;
+        this.variableNames["matricesWeightsExtra"] = 0;
+    }
+
     /**
      * Emits console errors and exceptions if there is a failing check
      */