Browse Source

First exploration

David Catuhe 6 years ago
parent
commit
7d0a96275b

+ 19 - 0
src/Materials/Node/Blocks/Fragment/fragmentOutputBlock.ts

@@ -0,0 +1,19 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from 'Materials/Node/nodeMaterialCompilationState';
+
+export class FragmentOutputBlock extends NodeMaterialBlock {
+    public constructor(name: string) {
+        super(name);
+
+        this.registerEntryPoint("color", NodeMaterialBlockConnectionPointTypes.Color4);
+    }
+
+    public compile(state: NodeMaterialCompilationState) {
+        super.compile(state);
+
+        let entryPoint = this.entryPoints[0];
+
+        state.compilationString += `gl_FragColor = ${entryPoint.associatedVariableName};\r\n`;
+    }
+}

+ 3 - 0
src/Materials/Node/Blocks/Fragment/index.ts

@@ -0,0 +1,3 @@
+
+export * from "./fragmentOutputBlock";
+export * from "./textureBlock";

+ 25 - 0
src/Materials/Node/Blocks/Fragment/textureBlock.ts

@@ -0,0 +1,25 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from 'Materials/Node/nodeMaterialCompilationState';
+
+export class TextureBlock extends NodeMaterialBlock {
+    public constructor(name: string) {
+        super(name);
+
+        this.registerEntryPoint("uv", NodeMaterialBlockConnectionPointTypes.Vector2);
+        this.registerEntryPoint("texture", NodeMaterialBlockConnectionPointTypes.Texture);
+
+        this.registerExitPoint("color", NodeMaterialBlockConnectionPointTypes.Color4);
+    }
+
+    public compile(state: NodeMaterialCompilationState) {
+        super.compile(state);
+
+        let uvEntryPoint = this.entryPoints[0];
+        let samplerEntryPoint = this.entryPoints[1];
+
+        let output = this.exitPoints[0];
+
+        state.compilationString += `vec4 ${output.associatedVariableName} = texture2D(${samplerEntryPoint.associatedVariableName}, ${uvEntryPoint.associatedVariableName});\r\n`;
+    }
+}

+ 3 - 0
src/Materials/Node/Blocks/Vertex/index.ts

@@ -0,0 +1,3 @@
+export * from "./vertexOutputBlock";
+export * from "./vector3TransformBlock";
+export * from "./vector4TransformBlock";

+ 28 - 0
src/Materials/Node/Blocks/Vertex/vector3TransformBlock.ts

@@ -0,0 +1,28 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+
+export class Vector3TransformBlock extends NodeMaterialBlock {
+    /**
+     * Defines the value to use to complement Vector3 to transform it to a Vector4
+     */
+    public complement = 1;
+
+    public constructor(name: string) {
+        super(name);
+
+        this.registerEntryPoint("vector", NodeMaterialBlockConnectionPointTypes.Vector3);
+        this.registerEntryPoint("transform", NodeMaterialBlockConnectionPointTypes.Matrix);
+        this.registerExitPoint("output", NodeMaterialBlockConnectionPointTypes.Vector4);
+    }
+
+    public compile(state: NodeMaterialCompilationState) {
+        super.compile(state);
+
+        let output = this._exitPoints[0];
+        let vector = this._entryPoints[0];
+        let transform = this._entryPoints[1];
+
+        state.compilationString += `vec4 ${output.associatedVariableName} = ${transform.associatedVariableName} * vec4(${vector.associatedVariableName}, ${this.complement});\r\n`;
+    }
+}

+ 24 - 0
src/Materials/Node/Blocks/Vertex/vector4TransformBlock.ts

@@ -0,0 +1,24 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from '../../nodeMaterialCompilationState';
+
+export class Vector4TransformBlock extends NodeMaterialBlock {
+
+    public constructor(name: string) {
+        super(name);
+
+        this.registerEntryPoint("vector", NodeMaterialBlockConnectionPointTypes.Vector4);
+        this.registerEntryPoint("transform", NodeMaterialBlockConnectionPointTypes.Matrix);
+        this.registerExitPoint("output", NodeMaterialBlockConnectionPointTypes.Vector4);
+    }
+
+    public compile(state: NodeMaterialCompilationState) {
+        super.compile(state);
+
+        let output = this._exitPoints[0];
+        let vector = this._entryPoints[0];
+        let transform = this._entryPoints[1];
+
+        state.compilationString += `vec4 ${output.associatedVariableName} = ${transform.associatedVariableName} * ${vector.associatedVariableName};\r\n`;
+    }
+}

+ 19 - 0
src/Materials/Node/Blocks/Vertex/vertexOutputBlock.ts

@@ -0,0 +1,19 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from 'Materials/Node/nodeMaterialCompilationState';
+
+export class VertexOutputBlock extends NodeMaterialBlock {
+    public constructor(name: string) {
+        super(name);
+
+        this.registerEntryPoint("vector", NodeMaterialBlockConnectionPointTypes.Vector3);
+    }
+
+    public compile(state: NodeMaterialCompilationState) {
+        super.compile(state);
+
+        let entryPoint = this.entryPoints[0];
+
+        state.compilationString += `gl_Position = ${entryPoint.associatedVariableName};\r\n`;
+    }
+}

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

@@ -0,0 +1,2 @@
+export * from "./Vertex/index";
+export * from "./Fragment/index";

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

@@ -0,0 +1,5 @@
+export * from "./nodeMaterialBlockConnectionPointTypes";
+export * from "./nodeMaterialBlockConnectionPoint";
+export * from "./nodeMaterialBlock";
+export * from "./nodeMaterial";
+export * from "./Blocks/index";

+ 195 - 21
src/Materials/Node/nodeMaterial.ts

@@ -4,6 +4,12 @@ import { Scene } from '../../scene';
 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 { BaseTexture } from '../../Materials/Textures/baseTexture';
+import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
 
 export interface INodeMaterialOptions {
     needAlphaBlending: boolean,
@@ -12,12 +18,24 @@ export interface INodeMaterialOptions {
 
 export class NodeMaterial extends Material {
     private _options: INodeMaterialOptions;
+    private _vertexCompilationState: NodeMaterialCompilationState;
+    private _fragmentCompilationState: NodeMaterialCompilationState;
+    private _compileId: number = 0;
     private _renderId: number;
+    private _effectCompileId: number = 0;
+    private _cachedWorldViewMatrix = new Matrix();
+    private _cachedWorldViewProjectionMatrix = new Matrix();
+    private _textureConnectionPoints = new Array<NodeMaterialConnectionPoint>()
 
     /**
-     * Gets or sets the root node of the material
+     * Gets or sets the root node of the material vertex shader
      */
-    public rootNode: NodeMaterialBlock;
+    public vertexRootNode: NodeMaterialBlock;
+
+    /**
+     * Gets or sets the root node of the material fragment (pixel) shader
+     */
+    public fragmentRootNode: NodeMaterialBlock;
 
     /** Gets or sets options to control the node material overall behavior */
     public get options() {
@@ -28,8 +46,8 @@ export class NodeMaterial extends Material {
         this._options = options;
     }
 
-    constructor(name: string, scene: Scene, options: Partial<INodeMaterialOptions> = {}) {
-        super(name, scene);
+    constructor(name: string, scene?: Scene, options: Partial<INodeMaterialOptions> = {}) {
+        super(name, scene || Engine.LastCreatedScene!);
 
         this._options = {
             needAlphaBlending: false,
@@ -38,7 +56,6 @@ export class NodeMaterial extends Material {
         };
     }
 
-
     /**
      * Gets the current class name of the material e.g. "NodeMaterial"
      * @returns the class name
@@ -51,27 +68,125 @@ export class NodeMaterial extends Material {
      * Compile the material and generates the inner effect
      */
     public compile() {
+        if (!this.vertexRootNode) {
+            throw "You must define a vertexRootNode";
+        }
+
+        if (!this.fragmentRootNode) {
+            throw "You must define a 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
+
+        // Vertex
+        this._vertexCompilationState = new NodeMaterialCompilationState();
 
+        this.vertexRootNode.compile(this._vertexCompilationState);
+        this.vertexRootNode.compileChildren(this._vertexCompilationState);
+
+        // Fragment
+        this._fragmentCompilationState = new NodeMaterialCompilationState();
+        this._fragmentCompilationState.isInFragmentMode = true;
+        this._fragmentCompilationState.vertexState = this._vertexCompilationState;
+        this._fragmentCompilationState.hints = this._vertexCompilationState.hints;
+        this._fragmentCompilationState.uniformConnectionPoints = this._vertexCompilationState.uniformConnectionPoints;
+
+        this.fragmentRootNode.compile(this._fragmentCompilationState);
+        this.fragmentRootNode.compileChildren(this._fragmentCompilationState);
+
+        // Finalize
+        this._vertexCompilationState.varyings = this._fragmentCompilationState.varyings;
+        this._vertexCompilationState.finalize();
+        this._fragmentCompilationState.finalize();
+
+        // Textures
+        this._textureConnectionPoints = this._fragmentCompilationState.uniformConnectionPoints.filter(u => u.type === NodeMaterialBlockConnectionPointTypes.Texture);
+
+        this._compileId++;
     }
 
     /**
      * Checks if the material is ready to render the requested mesh
-     * @param mesh Define the mesh to render
-     * @param useInstances Define whether or not the material is used with instances
+     * @param mesh defines the mesh to render
+     * @param useInstances defines whether or not the material is used with instances
      * @returns true if ready, otherwise false
      */
     public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
         var scene = this.getScene();
-        //var engine = scene.getEngine();
+        var engine = scene.getEngine();
 
         if (!this.checkReadyOnEveryCall) {
             if (this._renderId === scene.getRenderId()) {
-                // if (this._checkCache(mesh, useInstances)) {
                 return true;
-                // }
             }
         }
 
+        // Textures
+        for (var connectionPoint of this._textureConnectionPoints) {
+            let texture = connectionPoint.value as BaseTexture;
+            if (texture && !texture.isReady()) {
+                return false;
+            }
+        }
+
+        this._renderId = scene.getRenderId();
+
+        if (this._effectCompileId === this._compileId) {
+            return true;
+        }
+
+        var previousEffect = this._effect;
+
+        // Uniforms
+        let mergedUniforms = this._vertexCompilationState.uniforms;
+
+        this._fragmentCompilationState.uniforms.forEach(u => {
+            let index = mergedUniforms.indexOf(u);
+
+            if (index === -1) {
+                mergedUniforms.push(u);
+            }
+
+        });
+
+        // Samplers
+        let mergedSamplers = this._vertexCompilationState.samplers;
+
+        this._fragmentCompilationState.samplers.forEach(s => {
+            let index = mergedSamplers.indexOf(s);
+
+            if (index === -1) {
+                mergedSamplers.push(s);
+            }
+
+        });
+
+        // Compilation
+        this._effect = engine.createEffect({
+            vertex: "nodeMaterial" + this._compileId,
+            fragment: "nodeMaterial" + this._compileId,
+            vertexSource: this._vertexCompilationState.compilationString,
+            fragmentSource: this._fragmentCompilationState.compilationString
+        }, <EffectCreationOptions>{
+            attributes: this._vertexCompilationState.attributes,
+            uniformsNames: mergedUniforms,
+            samplers: this._fragmentCompilationState.samplers,
+            defines: "",
+            onCompiled: this.onCompiled,
+            onError: this.onError
+        }, engine);
+
+        if (!this._effect.isReady()) {
+            return false;
+        }
+
+        if (previousEffect !== this._effect) {
+            scene.resetCachedMaterial();
+        }
+
+        this._effectCompileId = this._compileId;
+
         return true;
     }
 
@@ -80,24 +195,26 @@ export class NodeMaterial extends Material {
      * @param world defines the world transformation matrix
      */
     public bindOnlyWorldMatrix(world: Matrix): void {
-        //  var scene = this.getScene();
+        var scene = this.getScene();
 
         if (!this._effect) {
             return;
         }
 
-        // if (this._options.uniforms.indexOf("world") !== -1) {
-        //     this._effect.setMatrix("world", world);
-        // }
+        let hints = this._fragmentCompilationState.hints;
+        if (hints.needWorldMatrix) {
+            this._effect.setMatrix("world", world);
+        }
 
-        // if (this._options.uniforms.indexOf("worldView") !== -1) {
-        //     world.multiplyToRef(scene.getViewMatrix(), this._cachedWorldViewMatrix);
-        //     this._effect.setMatrix("worldView", this._cachedWorldViewMatrix);
-        // }
+        if (hints.needWorldViewMatrix) {
+            world.multiplyToRef(scene.getViewMatrix(), this._cachedWorldViewMatrix);
+            this._effect.setMatrix("worldView", this._cachedWorldViewMatrix);
+        }
 
-        // if (this._options.uniforms.indexOf("worldViewProjection") !== -1) {
-        //     this._effect.setMatrix("worldViewProjection", world.multiply(scene.getTransformMatrix()));
-        // }
+        if (hints.needWorldViewProjectionMatrix) {
+            world.multiplyToRef(scene.getTransformMatrix(), this._cachedWorldViewProjectionMatrix)
+            this._effect.setMatrix("worldViewProjection", this._cachedWorldViewProjectionMatrix);
+        }
     }
 
     /**
@@ -108,6 +225,63 @@ export class NodeMaterial extends Material {
     public bind(world: Matrix, mesh?: Mesh): void {
         // Std values
         this.bindOnlyWorldMatrix(world);
+
+        if (this._effect && this.getScene().getCachedMaterial() !== this) {
+            let hints = this._fragmentCompilationState.hints;
+
+            if (hints.needViewMatrix) {
+                this._effect.setMatrix("view", this.getScene().getViewMatrix());
+            }
+
+            if (hints.needProjectionMatrix) {
+                this._effect.setMatrix("projection", this.getScene().getProjectionMatrix());
+            }
+
+            if (hints.needViewProjectionMatrix) {
+                this._effect.setMatrix("viewProjection", this.getScene().getTransformMatrix());
+            }
+
+            for (var connectionPoint of this._fragmentCompilationState.uniformConnectionPoints) {
+                connectionPoint.transmit(this._effect);
+            }
+        }
+
+        this._afterBind(mesh);
     }
 
+
+    /**
+     * Gets the active textures from the material
+     * @returns an array of textures
+     */
+    public getActiveTextures(): BaseTexture[] {
+        var activeTextures = super.getActiveTextures();
+
+        for (var connectionPoint of this._textureConnectionPoints) {
+            if (connectionPoint.value) {
+                activeTextures.push(connectionPoint.value);
+            }
+        }
+
+        return activeTextures;
+    }
+
+    /**
+     * Specifies if the material uses a texture
+     * @param texture defines the texture to check against the material
+     * @returns a boolean specifying if the material uses the texture
+     */
+    public hasTexture(texture: BaseTexture): boolean {
+        if (super.hasTexture(texture)) {
+            return true;
+        }
+
+        for (var connectionPoint of this._textureConnectionPoints) {
+            if (connectionPoint.value === texture) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }

+ 134 - 0
src/Materials/Node/nodeMaterialBlock.ts

@@ -1,10 +1,144 @@
+import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialCompilationState } from './nodeMaterialCompilationState';
+
 export class NodeMaterialBlock {
+    public _entryPoints = new Array<NodeMaterialConnectionPoint>();
+    public _exitPoints = new Array<NodeMaterialConnectionPoint>();
+
     /**
      * Gets or sets the name of the block
      */
     public name: string;
 
+    /**
+     * Gets the list of entry points
+     */
+    public get entryPoints(): NodeMaterialConnectionPoint[] {
+        return this._entryPoints;
+    }
+
+    /** Gets the list of exit points */
+    public get exitPoints(): NodeMaterialConnectionPoint[] {
+        return this._exitPoints;
+    }
+
+    /**
+     * Find an entry point by its name
+     * @param name defines the name of the entry point to look for
+     * @returns the entry point or null if not found
+     */
+    public getEntryPointByName(name: string) {
+        let filter = this._entryPoints.filter(e => e.name === name);
+
+        if (filter.length) {
+            return filter[0];
+        }
+
+        return null;
+    }
+
+    /**
+     * Find an exit point by its name
+     * @param name defines the name of the exit point to look for
+     * @returns the exit point or null if not found
+     */
+    public getExitPointByName(name: string) {
+        let filter = this._exitPoints.filter(e => e.name === name);
+
+        if (filter.length) {
+            return filter[0];
+        }
+
+        return null;
+    }
+
+    public constructor(name: string) {
+        this.name = name;
+    }
+
     public getClassName() {
         return "NodeMaterialBlock";
     }
+
+    public registerEntryPoint(name: string, type: NodeMaterialBlockConnectionPointTypes) {
+        let point = new NodeMaterialConnectionPoint(name, this);
+        point.type = type;
+
+        this._entryPoints.push(point);
+    }
+
+    public registerExitPoint(name: string, type: NodeMaterialBlockConnectionPointTypes) {
+        let point = new NodeMaterialConnectionPoint(name, this);
+        point.type = type;
+
+        this._exitPoints.push(point);
+    }
+
+    /**
+     * Will return the first available entry point e.g. the first one which is not an uniform or an attribute
+     */
+    public getFirstAvailableEntryPoint() {
+        for (var entryPoint of this._entryPoints) {
+            if (!entryPoint.isUniform && !entryPoint.isAttribute) {
+                return entryPoint;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Connect current block with another block
+     * @param other defines the block to connect with
+     * @param entryPointName define the name of the other block entry point (will take the first available one if not defined)
+     * @param exitPointName define the name of current block exit point (will take the first one if not defined)
+     */
+    public connectTo(other: NodeMaterialBlock, entryPointName?: string, exitPointName?: string) {
+        if (this._exitPoints.length === 0) {
+            return;
+        }
+
+        let output = exitPointName ? this.getExitPointByName(exitPointName) : this._exitPoints[0];
+        let input = entryPointName ? other.getEntryPointByName(entryPointName) : other.getFirstAvailableEntryPoint();
+
+        if (output && input) {
+            output.connectTo(input);
+        }
+    }
+
+    /**
+     * Compile the current node and generate the shader code
+     * @param state defines the current compilation state (uniforms, samplers, current string)
+     */
+    public compile(state: NodeMaterialCompilationState) {
+        for (var entryPoint of this._entryPoints) {
+            state.emitUniformOrAttributes(entryPoint);
+        }
+
+        for (var exitPoint of this._exitPoints) {
+            exitPoint.associatedVariableName = state.getFreeVariableName(exitPoint.name);
+            state.emitVaryings(exitPoint);
+        }
+    }
+
+    public compileChildren(state: NodeMaterialCompilationState) {
+        // Compile blocks
+        for (var exitPoint of this._exitPoints) {
+            let block = exitPoint.connectedBlock;
+
+            if (block) {
+                block.compile(state);
+            }
+        }
+
+        // Compile children
+        for (var exitPoint of this._exitPoints) {
+            let block = exitPoint.connectedBlock;
+
+            if (block) {
+                block.compileChildren(state);
+            }
+        }
+    }
 }

+ 141 - 0
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -0,0 +1,141 @@
+import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBlock } from './nodeMaterialBlock';
+import { Nullable } from '../../types';
+import { Effect } from '../effect';
+
+export class NodeMaterialConnectionPoint {
+    private _ownerBlock: NodeMaterialBlock;
+    private _connectedPoint: Nullable<NodeMaterialConnectionPoint>;
+    private _associatedVariableName: string;
+
+    private _storedValue: any;
+
+    /**
+     * Gets or sets the connection point type (default is float)
+     */
+    public type: NodeMaterialBlockConnectionPointTypes = NodeMaterialBlockConnectionPointTypes.Float;
+
+    /**
+     * Gets or sets the connection point name
+     */
+    public name: string;
+
+    /**
+     * Gets or sets the value of that point (when defined as isUniform === true)
+     */
+    public get value(): any {
+        return this._storedValue;
+    }
+
+    public set value(value: any) {
+        this._storedValue = value;
+    }
+
+    /**
+     * Gets or sets the associated variable name in the shader
+     */
+    public get associatedVariableName(): string {
+        if (!this._associatedVariableName && this._connectedPoint) {
+            return this._connectedPoint.associatedVariableName;
+        }
+
+        return this._associatedVariableName;
+    }
+
+    public set associatedVariableName(value: string) {
+        this._associatedVariableName = value;
+    }
+
+    /** 
+     * Gets or sets a boolean indicating that this connection point is coming from an uniform.
+     * In this case the connection point name must be the name of the uniform to use.
+     * Can only be set on entry points
+     */
+    public isUniform: boolean;
+
+    /** 
+     * Gets or sets a boolean indicating that this connection point is coming from an attribute.
+     * In this case the connection point name must be the name of the attribute to use
+     * Can only be set on entry points
+     */
+    public isAttribute: boolean;
+
+    /**
+     * Gets or sets a boolean indicating that this connection point is generating a varying variable.
+     * Can only be set on exit points
+     */
+    public isVarying: boolean;
+
+    /** Get the other side of the connection (if any) */
+    public get connectedPoint(): Nullable<NodeMaterialConnectionPoint> {
+        return this._connectedPoint;
+    }
+
+    /** Get the block that owns this connection point */
+    public get ownerBlock(): NodeMaterialBlock {
+        return this._ownerBlock;
+    }
+
+    /** Get the block connected on the other side of this connection (if any) */
+    public get connectedBlock(): Nullable<NodeMaterialBlock> {
+        if (!this._connectedPoint) {
+            return null;
+        }
+
+        return this._connectedPoint.ownerBlock;
+    }
+
+    public constructor(name: string, ownerBlock: NodeMaterialBlock) {
+        this._ownerBlock = ownerBlock;
+        this.name = name;
+    }
+
+    /**
+     * Gets the current class name e.g. "NodeMaterialConnectionPoint"
+     * @returns the class name
+     */
+    public getClassName(): string {
+        return "NodeMaterialConnectionPoint";
+    }
+
+    public connectTo(connectionPoint: NodeMaterialConnectionPoint) {
+        this._connectedPoint = connectionPoint;
+        connectionPoint._connectedPoint = this;
+    }
+
+    /**
+     * When connection point is an uniform, this function will send its value to the effect
+     * @param effect defines the effect to transmit value to
+     */
+    public transmit(effect: Effect) {
+        switch (this.type) {
+            case NodeMaterialBlockConnectionPointTypes.Float:
+                effect.setFloat(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Int:
+                effect.setInt(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Color3:
+                effect.setColor3(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Color4:
+                effect.setDirectColor4(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector2:
+                effect.setVector2(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector3:
+                effect.setVector3(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Vector4:
+                effect.setVector4(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Matrix:
+                effect.setMatrix(this.name, this.value);
+                break;
+            case NodeMaterialBlockConnectionPointTypes.Texture:
+                effect.setTexture(this.name, this.value);
+                break;
+        }
+    }
+}

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

@@ -0,0 +1,11 @@
+export enum NodeMaterialBlockConnectionPointTypes {
+    Float,
+    Int,
+    Vector2,
+    Vector3,
+    Vector4,
+    Color3,
+    Color4,
+    Matrix,
+    Texture
+}

+ 180 - 0
src/Materials/Node/nodeMaterialCompilationState.ts

@@ -0,0 +1,180 @@
+import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
+
+export class NodeMaterialCompilationState {
+    public attributes = new Array<string>();
+    public uniforms = new Array<string>();
+    public samplers = new Array<string>();
+    public variableNames: { [key: string]: number } = {};
+    public uniformConnectionPoints = new Array<NodeMaterialConnectionPoint>();
+    public varyings = new Array<string>();
+    public isInFragmentMode = false;
+
+    public vertexState: NodeMaterialCompilationState;
+
+    public hints = {
+        needWorldMatrix: false,
+        needViewMatrix: false,
+        needProjectionMatrix: false,
+        needViewProjectionMatrix: false,
+        needWorldViewMatrix: false,
+        needWorldViewProjectionMatrix: false
+    }
+
+    private _attributeDeclaration = "";
+    private _uniformDeclaration = "";
+    private _samplerDeclaration = "";
+    private _varyingDeclaration = "";
+    private _varyingTransfer = "";
+
+    public compilationString = "";
+
+    public finalize() {
+        this.compilationString = `\r\n//Entry point\r\nvoid main(void) {\r\n${this.compilationString}`;
+
+        if (!this.isInFragmentMode && this.varyings.length > 0) {
+            this.compilationString = `${this.compilationString}\r\n${this._varyingTransfer}`;
+        }
+
+        this.compilationString = `${this.compilationString}\r\n}`;
+
+        if (this._varyingDeclaration) {
+            this.compilationString = `\r\n//Varyings\r\n${this._varyingDeclaration}\r\n\r\n${this.compilationString}`;
+        }
+
+        if (this._samplerDeclaration) {
+            this.compilationString = `\r\n//Samplers\r\n${this._samplerDeclaration}\r\n\r\n${this.compilationString}`;
+        }
+
+        if (this._uniformDeclaration) {
+            this.compilationString = `\r\n//Uniforms\r\n${this._uniformDeclaration}\r\n\r\n${this.compilationString}`;
+        }
+
+        if (this._attributeDeclaration && !this.isInFragmentMode) {
+            this.compilationString = `\r\n//Attributes\r\n${this._attributeDeclaration}\r\n\r\n${this.compilationString}`;
+        }
+    }
+
+    public getFreeVariableName(prefix: string): string {
+        if (this.variableNames[prefix] === undefined) {
+            this.variableNames[prefix] = 0;
+        } else {
+            this.variableNames[prefix]++;
+        }
+
+        return prefix + this.variableNames[prefix];
+    }
+
+    private _getGLType(type: NodeMaterialBlockConnectionPointTypes): string {
+        switch (type) {
+            case NodeMaterialBlockConnectionPointTypes.Float:
+                return "float";
+            case NodeMaterialBlockConnectionPointTypes.Int:
+                return "int";
+            case NodeMaterialBlockConnectionPointTypes.Vector2:
+                return "vec2";
+            case NodeMaterialBlockConnectionPointTypes.Color3:
+            case NodeMaterialBlockConnectionPointTypes.Vector3:
+                return "vec3";
+            case NodeMaterialBlockConnectionPointTypes.Color4:
+            case NodeMaterialBlockConnectionPointTypes.Vector4:
+                return "vec4";
+            case NodeMaterialBlockConnectionPointTypes.Matrix:
+                return "mat4";
+            case NodeMaterialBlockConnectionPointTypes.Texture:
+                return "sampler2D";
+        }
+    }
+
+    public emitVaryings(point: NodeMaterialConnectionPoint, force = false) {
+        if (point.isVarying || force) {
+            if (this.varyings.indexOf(point.associatedVariableName) !== -1) {
+                return;
+            }
+
+            this.varyings.push(point.associatedVariableName);
+            this._varyingDeclaration += `varying ${this._getGLType(point.type)} ${point.associatedVariableName};\r\n`;
+
+            if (!this.isInFragmentMode) {
+                this._varyingTransfer += `${point.associatedVariableName} = ${point.name};\r\n`;
+            }
+        }
+    }
+
+    public emitUniformOrAttributes(point: NodeMaterialConnectionPoint) {
+        // Samplers
+        if (point.type === NodeMaterialBlockConnectionPointTypes.Texture) {
+            point.name = this.getFreeVariableName(point.name);
+            point.associatedVariableName = point.name;
+
+            if (this.samplers.indexOf(point.name) !== -1) {
+                return;
+            }
+
+            this.samplers.push(point.name);
+            this._samplerDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
+            this.uniformConnectionPoints.push(point);
+            return;
+        }
+
+        if (!point.isUniform && !point.isAttribute) {
+            return;
+        }
+
+        point.associatedVariableName = point.name;
+
+        // Uniforms
+        if (point.isUniform) {
+            if (this.uniforms.indexOf(point.name) !== -1) {
+                return;
+            }
+
+            this.uniforms.push(point.name);
+            this._uniformDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
+
+            // well known
+            switch (point.name) {
+                case "world":
+                    this.hints.needWorldMatrix = true;
+                    break;
+                case "view":
+                    this.hints.needViewMatrix = true;
+                    break;
+                case "projection":
+                    this.hints.needProjectionMatrix = true;
+                    break;
+                case "viewProjection":
+                    this.hints.needViewProjectionMatrix = true;
+                    break;
+                case "worldView":
+                    this.hints.needWorldViewMatrix = true;
+                    break;
+                case "worldViewProjection":
+                    this.hints.needWorldViewProjectionMatrix = true;
+                    break;
+                default:
+                    this.uniformConnectionPoints.push(point);
+                    break;
+            }
+
+            return;
+        }
+
+        if (point.isAttribute) {
+            if (this.isInFragmentMode) {
+                this.vertexState.emitUniformOrAttributes(point);
+                point.associatedVariableName = this.getFreeVariableName(point.name);
+                this.emitVaryings(point, true);
+                this.vertexState.emitVaryings(point, true);
+                return;
+            }
+
+            if (this.attributes.indexOf(point.name) !== -1) {
+                return;
+            }
+
+            this.attributes.push(point.name);
+            this._attributeDeclaration += `attribute ${this._getGLType(point.type)} ${point.name};\r\n`;
+        }
+    }
+}

+ 14 - 0
src/Materials/effect.ts

@@ -329,6 +329,8 @@ export class Effect {
             if (!vertexSource) {
                 vertexSource = baseName.vertexElement;
             }
+        } else if (baseName.vertexSource) {
+            vertexSource = "source:" + baseName.vertexSource;
         } else {
             vertexSource = baseName.vertex || baseName;
         }
@@ -339,6 +341,8 @@ export class Effect {
             if (!fragmentSource) {
                 fragmentSource = baseName.fragmentElement;
             }
+        } else if (baseName.fragmentSource) {
+            fragmentSource = "source:" + baseName.fragmentSource;
         } else {
             fragmentSource = baseName.fragment || baseName;
         }
@@ -504,6 +508,11 @@ export class Effect {
 
     /** @hidden */
     public _loadVertexShader(vertex: any, callback: (data: any) => void): void {
+        if (vertex.substr(0, 7) === "source:") {
+            callback(vertex.substr(7));
+            return;
+        }
+
         if (DomManagement.IsWindowObjectExist()) {
             // DOM element ?
             if (vertex instanceof HTMLElement) {
@@ -540,6 +549,11 @@ export class Effect {
 
     /** @hidden */
     public _loadFragmentShader(fragment: any, callback: (data: any) => void): void {
+        if (fragment.substr(0, 7) === "source:") {
+            callback(fragment.substr(7));
+            return;
+        }
+
         if (DomManagement.IsWindowObjectExist()) {
             // DOM element ?
             if (fragment instanceof HTMLElement) {

+ 2 - 1
src/Materials/index.ts

@@ -13,4 +13,5 @@ export * from "./shaderMaterial";
 export * from "./standardMaterial";
 export * from "./Textures/index";
 export * from "./uniformBuffer";
-export * from "./materialFlags";
+export * from "./materialFlags";
+export * from "./Node/index"

+ 4 - 1
src/Materials/shaderMaterial.ts

@@ -78,6 +78,7 @@ export class ShaderMaterial extends Material {
     private _vectors2Arrays: { [name: string]: number[] } = {};
     private _vectors3Arrays: { [name: string]: number[] } = {};
     private _cachedWorldViewMatrix = new Matrix();
+    private _cachedWorldViewProjectionMatrix = new Matrix();
     private _renderId: number;
 
     /**
@@ -514,7 +515,9 @@ export class ShaderMaterial extends Material {
         }
 
         if (this._options.uniforms.indexOf("worldViewProjection") !== -1) {
-            this._effect.setMatrix("worldViewProjection", world.multiply(scene.getTransformMatrix()));
+            world.multiplyToRef(scene.getTransformMatrix(), this._cachedWorldViewProjectionMatrix)
+            this._effect.setMatrix("worldViewProjection", this._cachedWorldViewProjectionMatrix);
+
         }
     }