function LGraphFXStack()
{
	this.addInput("Color","Texture");
	this.addInput("Depth","Texture");
	this.addInput("Intensity","number");
	this.addOutput("Final","Texture");
	this.properties = { intensity: 1, preserve_aspect: true };

	this._fx_stack = new LS.FXStack();
	this._fx_options = {};
}
*********************************

	global.LGraphFrame = function LGraphFrame()
	{
		this.addOutput("Color","Texture");
		this.addOutput("Depth","Texture");
		this.addOutput("Extra","Texture");
		this.addOutput("Camera","Camera");
		this.properties = {};
	}

	LGraphFrame.title = "Frame";
	LGraphFrame.desc = "One frame rendered from the scene renderer";

	LGraphFrame.prototype.onExecute = function()
	{
		this.setOutputData(0, LGraphTexture.getTexture( this._color_texture ) );
		this.setOutputData(1, LGraphTexture.getTexture( this._depth_texture ) );
		this.setOutputData(2, LGraphTexture.getTexture( this._extra_texture ) );
		this.setOutputData(3, this._camera );
	}

	LGraphFrame.prototype.onDrawBackground = function( ctx )
	{
		if( this.flags.collapsed || this.size[1] <= 20 )
			return;

		if(!this._color_texture)
			return;

		if( !ctx.webgl )
			return; //is not working well

		//Different texture? then get it from the GPU
		if(this._last_preview_tex != this._last_tex || !this._last_preview_tex)
		{
			if( ctx.webgl && this._canvas && this._canvas.constructor === GL.Texture )
			{
				this._canvas = this._last_tex;
			}
			else
			{
				var texture = LGraphTexture.getTexture( this._color_texture );
				if(!texture)
					return;

				var tex_canvas = LGraphTexture.generateLowResTexturePreview( texture );
				if(!tex_canvas) 
					return;
				this._last_preview_tex = this._last_tex;
				this._canvas = cloneCanvas( tex_canvas );
			}
		}

		if(!this._canvas)
			return;

		//render to graph canvas
		ctx.save();
		if(!ctx.webgl) //reverse image
		{
			if( this._canvas.constructor === GL.Texture )
			{
				this._canvas = null;
				return;
			}

			ctx.translate( 0,this.size[1] );
			ctx.scale(1,-1);
		}
		ctx.drawImage( this._canvas, 0, 0, this.size[0], this.size[1] );
		ctx.restore();
	}

	LGraphFrame.prototype.onInspect = function( inspector )
	{
		var that = this;
		if(this.graph.component)
		{
			var render_context = this.graph.component.frame;
			inspector.showObjectFields( render_context );
			inspector.addSeparator();
			inspector.addCheckbox("Antialiasing", this.graph.component.use_antialiasing, function(v){ that.graph.component.use_antialiasing = v; });
		}
	}

	LiteGraph.registerNodeType("scene/frame", LGraphFrame );
};


///@FILE:../src/graph/components.js
///@INFO: GRAPHS
if(typeof(LiteGraph) != "undefined")
{
	//generic for all components that do not define their own node type
	global.LGraphComponent = function()
	{
		this.properties = {
			node_id: "",
			component_id: ""
		};

		//this.addInput("Component", undefined, { locked: true });

		this._component = null;
	}

	LGraphComponent.title = "Component";
	LGraphComponent.desc = "A component from a node";
	LGraphComponent.highlight_color = "#CCC";

	LGraphComponent.prototype.onRemoved = function()
	{
		this.bindComponentEvents(null); //remove binding		
	}

	LGraphComponent.prototype.onConnectionsChange = function( side )
	{
		this.bindComponentEvents( this._component );
	}

	LGraphComponent.prototype.onInit = function()
	{
		var compo = this.getComponent();
		if(!compo)
			return;
		this.processOutputs( compo );
	}

	LGraphComponent.prototype.processOutputs = function( compo )
	{
		if(!this.outputs || !this.outputs.length  )
			return;

		//write outputs
		for(var i = 0; i < this.outputs.length; i++)
		{
			var output = this.outputs[i];
			if( !output.links || !output.links.length || output.type == LiteGraph.EVENT )
				continue;
			if(output.name == "Component")
				this.setOutputData(i, compo );
			else
			{
				if(compo.getProperty)
				{
					var v = compo.getProperty(output.name);
					if(v !== undefined)
						this.setOutputData(i, v );
					else
						this.setOutputData(i, compo[ output.name ] );
				}
				else
					this.setOutputData(i, compo[ output.name ] );
			}
		}
	}


	LGraphComponent.prototype.onExecute = function()
	{
		var compo = this.getComponent();
		if(!compo)
			return;

		//read inputs (skip 1, is the component)
		if(this.inputs)
		for(var i = 0; i < this.inputs.length; i++)
		{
			var input = this.inputs[i];
			if( input.type === LiteGraph.ACTION )
				continue;
			var v = this.getInputData(i);
			if(v === undefined)
				continue;
			LS.setObjectProperty( compo, input.name, v );
		}

		//write outputs (done in a function so it can be reused by other methods)
		this.processOutputs( compo );
	}

	LGraphComponent.updateOutputData = function( slot )
	{
		if(!this.outputs || slot >= this.outputs.length  )
			return;

		var output = this.outputs[i];
		if( !output.links || !output.links.length || output.type == LiteGraph.EVENT )
			return;

		var compo = this.getComponent();
		if(!compo)
			return;

		if(output.name == "Component")
			this.setOutputData( slot, compo );
		else
			this.setOutputData( slot, compo[ output.name ] );
	}

	LGraphComponent.prototype.onDrawBackground = function(ctx)
	{
		var compo = this.getComponent();
		if(compo && compo._root)
		{
			this.boxcolor = null;
			var color = null;
			if(compo._root._is_selected)
				color = LGraphComponent.highlight_color;
			if(compo._is_selected)
				color = "#39F";

			if(color)
			{
				this.boxcolor = color;
				if(!this.flags.collapsed)
				{
					ctx.fillStyle = color;
					ctx.fillRect(0,0,this.size[0],2);
				}
			}

			this.title = LS.getClassName( compo.constructor );
		}
		else
			this.boxcolor = "red";
	}

	LGraphComponent.prototype.onConnectionsChange = function( type, slot, created, link_info, slot_info )
	{
		if(type == LiteGraph.INPUT && slot_info && slot_info.name == "Component" )
		{
			var node = this.getInputNode(slot);
			if(node && node.onExecute)
			{
				node.onExecute();
				this.setDirtyCanvas(true,true);
			}
		}
	}

	LGraphComponent.prototype.getComponent = function()
	{
		var scene = this.graph._scene || LS.GlobalScene;
		//TODO: if no graph found, then crawl up in the graph hierarchy because probalby it is a subgraph

		var node_id = this.properties.node_id;
		if(!node_id)
		{
			if( this.inputs && this.inputs.length )
			{
				var slot = this.findInputSlot("Component");
				if(slot != -1)
				{
					var component = this.getInputData(slot);
					return component ? component : null;
				}
			}

			return null;
		}

		//find node
		var node = scene.getNode( node_id );
		if(!node)
			return null;

		//find compo
		var compo_id = this.properties.component_id;
		var compo = null;
		if(compo_id.charAt(0) == "@")
			compo = node.getComponentByUId( compo_id );
		else if( LS.Components[ compo_id ] )
			compo = node.getComponent( LS.Components[ compo_id ] );
		else
			return null;

		if(compo && !compo.constructor.is_component)
			return null;

		if(this._component != compo)
			this.bindComponentEvents( compo );

		this._component = compo;
		return compo;
	}

	//bind events attached to this component
	LGraphComponent.prototype.bindComponentEvents = function( component )
	{
		if(this._component)
			LEvent.unbindAll( this._component, this );

		this._component = component;
		if( !this._component )
			return;
		
		//iterate outputs
		if(this.outputs && this.outputs.length)
			for(var i = 0; i < this.outputs.length; ++i )
			{
				var output = this.outputs[i];
				if( output.type !== LiteGraph.EVENT )
					continue;
				var event_name = output.name.substr(3);
				LEvent.bind( this._component, event_name, this.onComponentEvent, this );
			}
	}

	LGraphComponent.prototype.onComponentEvent = function ( e, params )
	{
		this.trigger( "on_" + e, params );
	}

	LGraphComponent.prototype.getComponentProperties = function( get_inputs, result )
	{
		var compo = this.getComponent();
		if(!compo)
			return null;

		var attrs = null;
		if(compo.getPropertiesInfo)
			attrs = compo.getPropertiesInfo( get_inputs );
		else
			attrs = LS.getObjectProperties( compo );

		result = result || [];
		for(var i in attrs)
			result.push( [i, attrs[i]] );

		if(compo.constructor.getExtraProperties)
			compo.constructor.getExtraProperties( result );

		if(compo.getExtraProperties)
			compo.getExtraProperties( result );

		return result;
	}

	LGraphComponent.prototype.onAction = function( action_name, params ) { 
		if(!action_name)
			return;
		var compo = this.getComponent();
		if(!compo)
			return;
		if(compo.onAction)
			compo.onAction( action_name, params );
		else if( compo[ action_name ] )
			compo[ action_name ](); //params will be mostly MouseEvent, so for now I wont pass it
	}

	//used by the LGraphSetValue node
	LGraphComponent.prototype.onSetValue = function( property_name, value ) { 
		var compo = this.getComponent();
		if(!compo)
			return;

		var current = compo[ property_name ];
		var final_value;

		if( current == null)
		{
			if(value && value.constructor === String)
				final_value = value;
		}
		else
		{
			switch( current.constructor )
			{
				case Number: final_value = Number( value ); break;
				case Boolean: final_value = (value == "true" || value == "1"); break;
				case String: final_value = String( value ); break;
				case Array:
				case Float32Array: 
					if( value != null )
					{
						if( value.constructor === String )
							final_value = JSON.parse("["+value+"]");
						else if( value.constructor === Number )
							final_value = [value];
						else
							final_value = value;
					}
					else
						final_value = value;
					break;
			}
		}

		if(final_value === undefined)
			return;

		if(compo.setPropertyValue)
			compo.setPropertyValue( property_name, final_value );
		else
			compo[ property_name ] = final_value;
	}

	LGraphComponent.prototype.onGetInputs = function()
	{ 
		var inputs = [["Node",0],["Component",0],null];

		this.getComponentProperties("input", inputs);

		var compo = this.getComponent();
		if(compo && compo.getActions)
		{
			var actions = compo.getActions({});
			if(actions)
			{
				if(actions.constructor === Array)
					for(var i = 0; i < actions.length; ++i)
						inputs.push( [ actions[i], LiteGraph.ACTION ] );
				else
					for(var i in actions)
						inputs.push( [i, LiteGraph.ACTION ] );
			}
		}

		return inputs;
	}

	LGraphComponent.prototype.onGetOutputs = function()
	{ 
		var outputs = [];
		outputs.push( ["Component", "Component" ], null ); //compo + separator

		this.getComponentProperties( "output", outputs);

		var compo = this.getComponent();
		if(compo && compo.getEvents)
		{
			var events = compo.getEvents();
			if(events)
			{
				if(events.constructor === Array)
					for(var i = 0; i < events.length; ++i)
						outputs.push( ["on_" + events[i], LiteGraph.EVENT ] );
				else
					for(var i in events)
						outputs.push( ["on_" + i, LiteGraph.EVENT ] );
			}
		}
		return outputs;
	}

	LGraphComponent.prototype.onInspect = function( inspector )
	{ 
		var that = this;

		var component = this.getComponent();
		if(component && component.onInspectNode )
			component.onInspectNode( inspector, this );

		inspector.addButton(null, "Inspect Component", function(){
			var compo = that.getComponent();
			if(!compo)
				return;
			EditorModule.inspect( compo );
		});
	}


	LiteGraph.registerNodeType("scene/component", LGraphComponent );
	
	//********************************************************

	/* LGraphNode representing an object in the Scene