function LGraphShaderOperation()
	{
		this.addInput("A", LGShaders.ALL_TYPES );
		this.addInput("B", LGShaders.ALL_TYPES );
		this.addOutput("out","");
		this.properties = {
			operation: "*"
		};
		this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations });
	}
******************************

	function LGraphShaderConstant()
	{
		this.addOutput("","float");

		this.properties = {
			type: "float",
			value: 0
		};

		this.addWidget("combo","type","float",null, { values: GLSL_types_const, property: "type" } );
		this.updateWidgets();
	}

	LGraphShaderConstant.title = "const";

	LGraphShaderConstant.prototype.getTitle = function()
	{
		if(this.flags.collapsed)
			return valueToGLSL( this.properties.value, this.properties.type, 2 );
		return "Const";
	}

	LGraphShaderConstant.prototype.onPropertyChanged = function(name,value)
	{
		var that = this;
		if(name == "type")
		{
			if(this.outputs[0].type != value)
			{
				this.disconnectOutput(0);
				this.outputs[0].type = value;
			}
			this.widgets.length = 1; //remove extra widgets
			this.updateWidgets();
		}
		if(name == "value")
		{
			if(!value.length)
				this.widgets[1].value = value;
			else
			{
				this.widgets[1].value = value[1];
				if(value.length > 2)
					this.widgets[2].value = value[2];
				if(value.length > 3)
					this.widgets[3].value = value[3];
			}
		}
	}

	LGraphShaderConstant.prototype.updateWidgets = function( old_value )
	{
		var that = this;
		var old_value = this.properties.value;
		var options = { step: 0.01 };
		switch(this.properties.type)
		{
			case 'float': 
				this.properties.value = 0;
				this.addWidget("number","v",0,{ step:0.01, property: "value" });
				break;
			case 'vec2': 
				this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0];
				this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); 
				this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); 
				break;
			case 'vec3': 
				this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0];
				this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); 
				this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); 
				this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); 
				break;
			case 'vec4': 
				this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0];
				this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); 
				this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); 
				this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); 
				this.addWidget("number","w",this.properties.value[3], function(v){ that.properties.value[3] = v; },options); 
				break;
			default:
				console.error("unknown type for constant");
		}
	}

	LGraphShaderConstant.prototype.onGetCode = function( context )
	{
		if(!this.shader_destination)
			return;

		var value = valueToGLSL( this.properties.value, this.properties.type );
		var link_name = getOutputLinkID(this,0);
		if(!link_name) //not connected
			return;

		var code = "	" + this.properties.type + " " + link_name + " = " + value + ";";
		context.addCode( "code", code, this.shader_destination );

		this.setOutputData( 0, this.properties.type );
	}

	registerShaderNode( "const/const", LGraphShaderConstant );

	function LGraphShaderVec2()
	{
		this.addInput("xy","vec2");
		this.addInput("x","float");
		this.addInput("y","float");
		this.addOutput("xy","vec2");
		this.addOutput("x","float");
		this.addOutput("y","float");

		this.properties = { x: 0, y: 0 };
	}

	LGraphShaderVec2.title = "vec2";
	LGraphShaderVec2.varmodes = ["xy","x","y"];

	LGraphShaderVec2.prototype.onPropertyChanged = function()
	{
		if(this.graph)
			 this.graph._version++;
	}

	LGraphShaderVec2.prototype.onGetCode = function( context )
	{
		if(!this.shader_destination)
			return;

		var props = this.properties;

		var varname = getShaderNodeVarName(this);
		var code = "	vec2 " + varname + " = " + valueToGLSL([props.x,props.y]) + ";\n";

		for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i)
		{
			var varmode = LGraphShaderVec2.varmodes[i];
			var inlink = getInputLinkID(this,i);
			if(!inlink)
				continue;
			code += "	" + varname + "."+varmode+" = " + inlink + ";\n";
		}

		for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i)
		{
			var varmode = LGraphShaderVec2.varmodes[i];
			var outlink = getOutputLinkID(this,i);
			if(!outlink)
				continue;
			var type = GLSL_types_const[varmode.length - 1];
			code += "	"+type+" " + outlink + " = " + varname + "." + varmode + ";\n";
			this.setOutputData( i, type );
		}

		context.addCode( "code", code, this.shader_destination );
	}

	registerShaderNode( "const/vec2", LGraphShaderVec2 );	

	function LGraphShaderVec3()
	{
		this.addInput("xyz","vec3");
		this.addInput("x","float");
		this.addInput("y","float");
		this.addInput("z","float");
		this.addInput("xy","vec2");
		this.addInput("xz","vec2");
		this.addInput("yz","vec2");
		this.addOutput("xyz","vec3");
		this.addOutput("x","float");
		this.addOutput("y","float");
		this.addOutput("z","float");
		this.addOutput("xy","vec2");
		this.addOutput("xz","vec2");
		this.addOutput("yz","vec2");

		this.properties = { x:0, y: 0, z: 0 };
	}

	LGraphShaderVec3.title = "vec3";
	LGraphShaderVec3.varmodes = ["xyz","x","y","z","xy","xz","yz"];

	LGraphShaderVec3.prototype.onPropertyChanged = function()
	{
		if(this.graph)
			this.graph._version++;
	}

	LGraphShaderVec3.prototype.onGetCode = function( context )
	{
		if(!this.shader_destination)
			return;

		var props = this.properties;

		var varname = getShaderNodeVarName(this);
		var code = "vec3 " + varname + " = " + valueToGLSL([props.x,props.y,props.z]) + ";\n";

		for(var i = 0;i < LGraphShaderVec3.varmodes.length; ++i)
		{
			var varmode = LGraphShaderVec3.varmodes[i];
			var inlink = getInputLinkID(this,i);
			if(!inlink)
				continue;
			code += "	" + varname + "."+varmode+" = " + inlink + ";\n";
		}

		for(var i = 0; i < LGraphShaderVec3.varmodes.length; ++i)
		{
			var varmode = LGraphShaderVec3.varmodes[i];
			var outlink = getOutputLinkID(this,i);
			if(!outlink)
				continue;
			var type = GLSL_types_const[varmode.length - 1];
			code += "	"+type+" " + outlink + " = " + varname + "." + varmode + ";\n";
			this.setOutputData( i, type );
		}

		context.addCode( "code", code, this.shader_destination );
	}

	registerShaderNode( "const/vec3", LGraphShaderVec3 );	


	function LGraphShaderVec4()
	{
		this.addInput("xyzw","vec4");
		this.addInput("xyz","vec3");
		this.addInput("x","float");
		this.addInput("y","float");
		this.addInput("z","float");
		this.addInput("w","float");
		this.addInput("xy","vec2");
		this.addInput("yz","vec2");
		this.addInput("zw","vec2");
		this.addOutput("xyzw","vec4");
		this.addOutput("xyz","vec3");
		this.addOutput("x","float");
		this.addOutput("y","float");
		this.addOutput("z","float");
		this.addOutput("xy","vec2");
		this.addOutput("yz","vec2");
		this.addOutput("zw","vec2");

		this.properties = { x:0, y: 0, z: 0, w: 0 };
	}

	LGraphShaderVec4.title = "vec4";
	LGraphShaderVec4.varmodes = ["xyzw","xyz","x","y","z","w","xy","yz","zw"];

	LGraphShaderVec4.prototype.onPropertyChanged = function()
	{
		if(this.graph)
			this.graph._version++;
	}

	LGraphShaderVec4.prototype.onGetCode = function( context )
	{
		if(!this.shader_destination)
			return;

		var props = this.properties;

		var varname = getShaderNodeVarName(this);
		var code = "vec4 " + varname + " = " + valueToGLSL([props.x,props.y,props.z,props.w]) + ";\n";

		for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i)
		{
			var varmode = LGraphShaderVec4.varmodes[i];
			var inlink = getInputLinkID(this,i);
			if(!inlink)
				continue;
			code += "	" + varname + "."+varmode+" = " + inlink + ";\n";
		}

		for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i)
		{
			var varmode = LGraphShaderVec4.varmodes[i];
			var outlink = getOutputLinkID(this,i);
			if(!outlink)
				continue;
			var type = GLSL_types_const[varmode.length - 1];
			code += "	"+type+" " + outlink + " = " + varname + "." + varmode + ";\n";
			this.setOutputData( i, type );
		}

		context.addCode( "code", code, this.shader_destination );

	}

	registerShaderNode( "const/vec4", LGraphShaderVec4 );	
	
	//*********************************

	function LGraphShaderFragColor() {
		this.addInput("color", LGShaders.ALL_TYPES );
		this.block_delete = true;
	}

	LGraphShaderFragColor.title = "FragColor";
	LGraphShaderFragColor.desc = "Pixel final color";

	LGraphShaderFragColor.prototype.onGetCode = function( context )
	{
		var link_name = getInputLinkID( this, 0 );
		if(!link_name)
			return;
		var type = this.getInputData(0);
		var code = varToTypeGLSL( link_name, type, "vec4" );
		context.addCode("fs_code", "fragcolor = " + code + ";");
	}

	registerShaderNode( "output/fragcolor", LGraphShaderFragColor );


	/*
	function LGraphShaderDiscard()
	{
		this.addInput("v","T");
		this.addInput("min","T");
		this.properties = { min_value: 0.0 };
		this.addWidget("number","min",0,{ step: 0.01, property: "min_value" });
	}

	LGraphShaderDiscard.title = "Discard";

	LGraphShaderDiscard.prototype.onGetCode = function( context )
	{
		if(!this.isOutputConnected(0))
			return;

		var inlink = getInputLinkID(this,0);
		var inlink1 = getInputLinkID(this,1);

		if(!inlink && !inlink1) //not connected
			return;
		context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination );
		this.setOutputData( 0, return_type );
	}

	registerShaderNode( "output/discard", LGraphShaderDiscard );