function LGraphShaderGraph() {

		//before inputs
        this.subgraph = new LiteGraph.LGraph();
        this.subgraph._subgraph_node = this;
        this.subgraph._is_subgraph = true;
		this.subgraph.filter = "shader";

		this.addInput("in", "texture");
		this.addOutput("out", "texture");
		this.properties = { width: 0, height: 0, alpha: false, precision: typeof(LGraphTexture) != "undefined" ? LGraphTexture.DEFAULT : 2 };

		var inputNode = this.subgraph.findNodesByType("input/uniform")[0];
		inputNode.pos = [200,300];

		var sampler = LiteGraph.createNode("texture/sampler2D");
		sampler.pos = [400,300];
		this.subgraph.add( sampler );

		var outnode = LiteGraph.createNode("output/fragcolor");
		outnode.pos = [600,300];
		this.subgraph.add( outnode );

		inputNode.connect( 0, sampler );
		sampler.connect( 0, outnode );

		this.size = [180,60];
		this.redraw_on_mouse = true; //force redraw

		this._uniforms = {};
		this._shader = null;
		this._context = new LGShaderContext();
		this._context.vs_template = GL.Shader.SCREEN_VERTEX_SHADER;
		this._context.fs_template = LGraphShaderGraph.template;
	}
********************************
	function LGraphCubemapToTexture2D() {
		this.addInput("in", "texture");
		this.addInput("yaw", "number");
		this.addOutput("out", "texture");
		this.properties = { yaw: 0 };
	}

	LGraphCubemapToTexture2D.title = "CubemapToTexture2D";
	LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation";

	LGraphCubemapToTexture2D.prototype.onExecute = function() {
		if (!this.isOutputConnected(0))
			return;

		var tex = this.getInputData(0);
		if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP )
			return;
		if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type ))
			this._last_tex = null;
		var yaw = this.getInputOrProperty("yaw");
		this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw );
		this.setOutputData( 0, this._last_tex );
	};

	LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D );
})(this);

(function(global) {

    if (typeof GL == "undefined")
		return;

    var LiteGraph = global.LiteGraph;
	var LGraphCanvas = global.LGraphCanvas;

	var SHADERNODES_COLOR = "#345";

	var LGShaders = LiteGraph.Shaders = {};

	var GLSL_types = LGShaders.GLSL_types = ["float","vec2","vec3","vec4","mat3","mat4","sampler2D","samplerCube"];
	var GLSL_types_const = LGShaders.GLSL_types_const = ["float","vec2","vec3","vec4"];

	var GLSL_functions_desc = {
		"radians": "T radians(T degrees)",
		"degrees": "T degrees(T radians)",
		"sin": "T sin(T angle)",
		"cos": "T cos(T angle)",
		"tan": "T tan(T angle)",
		"asin": "T asin(T x)",
		"acos": "T acos(T x)",
		"atan": "T atan(T x)",
		"atan2": "T atan(T x,T y)",
		"pow": "T pow(T x,T y)",
		"exp": "T exp(T x)",
		"log": "T log(T x)",
		"exp2": "T exp2(T x)",
		"log2": "T log2(T x)",
		"sqrt": "T sqrt(T x)",
		"inversesqrt": "T inversesqrt(T x)",
		"abs": "T abs(T x)",
		"sign": "T sign(T x)",
		"floor": "T floor(T x)",
		"ceil": "T ceil(T x)",
		"fract": "T fract(T x)",
		"mod": "T mod(T x,T y)", //"T mod(T x,float y)"
		"min": "T min(T x,T y)",
		"max": "T max(T x,T y)",
		"clamp": "T clamp(T x,T minVal,T maxVal)",
		"mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)"
		"step": "T step(T edge, T x)", //"T step(float edge, T x)"
		"smoothstep": "T smoothstep(T edge, T x)", //"T smoothstep(float edge, T x)"
		"length":"float length(T x)",
		"distance":"float distance(T p0, T p1)",
		"normalize":"T normalize(T x)",
		"dot": "float dot(T x,T y)",
		"cross": "vec3 cross(vec3 x,vec3 y)"
	};

	//parse them
	var GLSL_functions = {};
	var GLSL_functions_name = [];
	parseGLSLDescriptions();

	function parseGLSLDescriptions()
	{
		GLSL_functions_name.length = 0;

		for(var i in GLSL_functions_desc)
		{
			var op = GLSL_functions_desc[i];
			var index = op.indexOf(" ");
			var return_type = op.substr(0,index);
			var index2 = op.indexOf("(",index);
			var func_name = op.substr(index,index2-index).trim();
			var params = op.substr(index2 + 1, op.length - index2 - 2).split(",");
			for(var j in params)
			{
				var p = params[j].split(" ");
				params[j] = { type: p[0], name: p[1] };
			}
			GLSL_functions[i] = { return_type: return_type, func: func_name, params: params };
			GLSL_functions_name.push( func_name );
			//console.log( GLSL_functions[i] );
		}
	}

	//common actions to all shader node classes
	function registerShaderNode( type, node_ctor )
	{
		//static attributes
		node_ctor.color = SHADERNODES_COLOR;
		node_ctor.filter = "shader";

		//common methods
		node_ctor.prototype.clearDestination = function(){ this.shader_destination = {};  }
		node_ctor.prototype.propagateDestination = function propagateDestination( dest_name )
		{
			this.shader_destination[ dest_name ] = true;
			if(this.inputs)
			for(var i = 0; i < this.inputs.length; ++i)
			{
				var origin_node = this.getInputNode(i);
				if(origin_node)
					origin_node.propagateDestination( dest_name );
			}
		}

		LiteGraph.registerNodeType( type, node_ctor );
	}

	function getShaderNodeVarName( node, name )
	{
		return "VAR_" + (name || "TEMP") + "_" + node.id;
	}

	function getInputLinkID( node, slot )
	{
		if(!node.inputs)
			return null;
		var link = node.getInputLink( slot );
		if( !link )
			return null;
		var origin_node = node.graph.getNodeById( link.origin_id );
		if( !origin_node )
			return null;
		if(origin_node.getOutputVarName)
			return origin_node.getOutputVarName(link.origin_slot);
		//generate
		return "link_" + origin_node.id + "_" + link.origin_slot;
	}

	function getOutputLinkID( node, slot )
	{
		if (!node.isOutputConnected(0))
			return null;
		return "link_" + node.id + "_" + slot;
	}

	LGShaders.registerShaderNode = registerShaderNode;
	LGShaders.getInputLinkID = getInputLinkID;
	LGShaders.getOutputLinkID = getOutputLinkID;
	LGShaders.getShaderNodeVarName = getShaderNodeVarName;
	LGShaders.parseGLSLDescriptions = parseGLSLDescriptions;

	var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type )
	{
		var n = 5; //num decimals
		if(!type)
		{
			if(v.constructor === Number)
				type = "float";
			else if(v.length)
			{
				switch(v.length)
				{
					case 2: type = "vec2"; break;
					case 3: type = "vec3"; break;
					case 4: type = "vec4"; break;
					case 9: type = "mat3"; break;
					case 16: type = "mat4"; break;
					default:
						throw("unknown type for glsl value size");
				}
			}
			else
				throw("unknown type for glsl value: " + v.constructor);
		}
		switch(type)
		{
			case 'float': return v.toFixed(n); break;
			case 'vec2': return "vec2(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + ")"; break;
			case 'color3':
			case 'vec3': return "vec3(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + ")"; break;
			case 'color4':
			case 'vec4': return "vec4(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + "," + v[3].toFixed(n) + ")"; break;
			case 'mat3': return "mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)"; break; //not fully supported yet
			case 'mat4': return "mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)"; break;//not fully supported yet
			default:
				throw("unknown glsl type in valueToGLSL:", type);
		}

		return "";
	}

	//used to plug incompatible stuff
	var convertVarToGLSLType = LiteGraph.convertVarToGLSLType = function convertVarToGLSLType( varname, type, target_type )
	{
		if(type == target_type)
			return varname;
		if(type == "float")
			return target_type + "(" + varname + ")";
		if(target_type == "vec2") //works for vec2,vec3 and vec4
			return "vec2(" + varname + ".xy)";
		if(target_type == "vec3") //works for vec2,vec3 and vec4
		{
			if(type == "vec2")
				return "vec3(" + varname + ",0.0)";
			if(type == "vec4")
				return "vec4(" + varname + ".xyz)";
		}
		if(target_type == "vec4")
		{
			if(type == "vec2")
				return "vec4(" + varname + ",0.0,0.0)";
			if(target_type == "vec3")
				return "vec4(" + varname + ",1.0)";
		}
		return null;
	}

	//used to host a shader body **************************************
	function LGShaderContext()
	{
		this.vs_template = "";
		this.fs_template = "";
		this._uniforms = {};
		this._codeparts = {};
	}

	LGShaderContext.prototype.clear = function()
	{
		this._uniforms = {};
		this._codeparts = {};
	}

	LGShaderContext.prototype.addUniform = function( name, type )
	{
		this._uniforms[ name ] = type;
	}

	LGShaderContext.prototype.addCode = function( hook, code, destinations )
	{
		destinations = destinations || {"":""};
		for(var i in destinations)
		{
			var h = i ? i + "_" + hook : hook;
			if(!this._codeparts[ h ])
				this._codeparts[ h ] = code + "\n";
			else
				this._codeparts[ h ] += code + "\n";
		}
	}

	LGShaderContext.prototype.computeShaderCode = function()
	{
		var uniforms = "";
		for(var i in this._uniforms)
			uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n";

		var parts = this._codeparts;
		parts.uniforms = uniforms;

		var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, parts );
		var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, parts );
		return {
			vs_code: vs_code,
			fs_code: fs_code
		};
	}

	//generates the shader code from the template and the 
	LGShaderContext.prototype.computeShader = function( shader )
	{
		var finalcode = this.computeShaderCode();
		console.log( finalcode.vs_code, finalcode.fs_code );

		try
		{
			if(shader)
				shader.updateShader( finalcode.vs_code, finalcode.fs_code );
			else
				shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code );
			this._shader_error = false;
			return shader;
		}
		catch (err)
		{
			if(!this._shader_error)
			{
				console.error(err);
				if(err.indexOf("Fragment shader") != -1)
					console.log( finalcode.fs_code );
				else
					console.log( finalcode.vs_code );
			}
			this._shader_error = true;
			return null;
		}

		return null;//never here
	}

	//represents a fragment of code exported by a node
	/*
	function LGShaderCodeBlock( node, code, uniforms )
	{
		this.node = node || null;
		this.uniforms = uniforms || null;
		this.parts = {};
		if(code)
		{
			if(code.constructor === String)
				this.parts.code = code;
			else
				this.parts = code;
		}
	}

	LGShaderCodeBlock.prototype.addUniform = function( name, type )
	{
		if(!this.uniforms)
			this.uniforms = {};
		this.uniforms[ name ] = type;
	}

	LGShaderCodeBlock.prototype.addCode = function( hook, code )
	{
		if(!this.parts[ hook ])
			this.parts[ hook ] = code + "\n";
		else
			this.parts[ hook ] += code + "\n";
	}