// Provides helpers to setup a post-process chain
//
o3djs.provide( 'patapi.postprocess' );

o3djs.require( 'patapi' );
o3djs.require( 'patapi.effectsmanager' );

o3djs.require('o3djs.util');
o3djs.require('o3djs.camera');
o3djs.require('o3djs.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.pack');
o3djs.require('o3djs.scene');
o3djs.require('o3djs.effect');
o3djs.require('o3djs.primitives');

var Math3D = o3djs.math;	// O3D Math

// Declare the postprocess "namespace"
patapi.postprocess = patapi.postprocess || {};


////////////////////////////////////////////////////////////////////////////
// Declares a class holding informations on a post-process chain
// You can later bind post-process passes to this object
//
//	_O3DClient, the O3D client
//	_O3DPack, the O3D pack into which create the objects
//	_Format, the format of the render target into which the main scene will render
//	_optEffectsManager, the optional effects manager to create better FX that support includes and shader interfaces
//
patapi.PostProcessChain = function( _O3DClient, _O3DPack, _Format, _optEffectsManager )
{
	this.m_Client = _O3DClient;	// Save the client for later use
	this.m_Pack = _O3DPack;		// Save the pack for later use
	this.m_EffectsManager = _optEffectsManager ? _optEffectsManager : null;


	////////////////////////////////////////////////////////////////////////////
	// Create the rendering pipeline root
	//

	// Create the render surface set under which the standard scene should render
	this.m_RenderSurfaceSet = this.m_Pack.createObject( 'RenderSurfaceSet' );
	this.m_RenderSurfaceSet.name = "Scene Root";

	// Create our single state-set
	// The state-set will be the root of every drawpasses, one drawpass per post-process
	var State = this.m_Pack.createObject( 'State' );
		State.getStateParam( 'ColorWriteEnable' ).value = 15;		// Write alpha
		State.getStateParam( 'o3d.ZEnable' ).value = false;			// Don't enable zbuffer
		State.getStateParam( 'o3d.ZWriteEnable' ).value = false;	// Don't write it either

	this.m_StateSet = this.m_Pack.createObject( 'StateSet' );
	this.m_StateSet.name = "PostProcess StateSet";
	this.m_StateSet.state = State;


	////////////////////////////////////////////////////////////////////////////
	// At this point, the hierarchy of render nodes looks like this
	//	and we are ready to welcome the post-process passes :
	//
	//          [???]  <== No parent yet (done in BindToScene)
	//            |
	//            +---------------------+
	//            |                     |
	//    [RenderSurfaceSet]        [StateSet]
	//      : : : : : : :            : : : :
	//      ? ? ? ? ? ? ?            ? ? ? ?
	//            ^                     ^
	//            |                     |
	//      Standard scene     Post-Process passes
	//


	this.m_Passes = [];		// No pass was bound for the moment...


	////////////////////////////////////////////////////////////////////////////
	// Create a temporary render-target into which the main scene will render
	// NOTE: There should be at least a single post-process to transfer this temporary render-target to the screen
	//
	this.m_Format = _Format;
	this.m_RenderTarget = null;		// The render target for the main pass
	this.m_DepthStencil = null;		// The depth-stencil target for the main pass

	this.Resize( this.m_Client.width, this.m_Client.height );
};

patapi.PostProcessChain.prototype = 
{
	// Gets the root render node under which any standard scene should be built to benefit of the post-process chain
	//
	getDefaultRootRenderNode : function()			{ return this.m_RenderSurfaceSet; },

	// Gets the main scene pass
	// This is important to the first pass that uses it as input
	//
	getMainSceneRenderTarget : function()			{ return this.m_RenderTarget; },

	// Gets the main scene depth stencil surface
	//
	getMainSceneDepthStencilSurface : function()	{ return this.m_DepthStencil; },


	// Disposes of used resources
	// Call this when exiting the application
	//
	Dispose : function()
	{
		if ( this.m_RenderTarget != null )
		{
			this.m_RenderTarget.Dispose();
			this.m_RenderTarget = null;
		}
		if ( this.m_DepthStencil != null )
		{
			this.m_Pack.removeObject( this.m_DepthStencil );
			this.m_DepthStencil = null;
		}

		throw "TODO!"
	},

	// Resizes the post-process chain for a new screen dimension
	//
	Resize : function( _Width, _Height )
	{
		if ( this.m_RenderTarget != null && this.m_RenderTarget.getWidth() == _Width && this.m_RenderTarget.getHeight() == _Height )
			return;	// No need to resize

		// Destroy any existing render target & depthstencil
		if ( this.m_RenderTarget != null )
		{
			this.m_RenderTarget.Dispose();
			this.m_RenderTarget = null;
		}
		if ( this.m_DepthStencil != null )
		{
			this.m_Pack.removeObject( this.m_DepthStencil );
			this.m_DepthStencil = null;
		}

		try
		{
			this.m_RenderTarget = new patapi.RenderTarget( this.m_Pack, "MainScene RenderTarget", _Width, _Height, this.m_Format );
		}
		catch ( e )
		{
			throw "Failed to create the MainScene render target ! " + e
		};

//alert( "Created render target !\n" + window.patapi.helpers.EnumerateProperties( this.m_RenderTarget ) )


		try
		{
			this.m_DepthStencil = this.m_Pack.createDepthStencilSurface( this.m_RenderTarget.getTargetWidth(), this.m_RenderTarget.getTargetHeight() );
		}
		catch ( e )
		{
			throw "Failed to create the default depth stencil ! " + e
		};

//alert( "Created DepthStencil !\n" + window.patapi.helpers.EnumerateProperties( this.m_DepthStencil ) )


		this.m_RenderSurfaceSet.renderSurface = this.m_RenderTarget.getSurface();
		this.m_RenderSurfaceSet.renderDepthStencilSurface = this.m_DepthStencil;
		
		// Update the main scene root Viewport node to render exactly the needed size
		this.UpdateMainSceneRootViewport_();
	},

	// Binds the post-process pass hierarchy to the given scene
	//	_SceneViewInfos, the standard ViewInfo structure created by a call to o3djs.rendergraph.createView()
	//
	BindToScene : function( _SceneViewInfos )
	{
		if ( _SceneViewInfos == null || typeof(_SceneViewInfos) == typeof(undefined) )
			throw "Invalid SceneViewInfo !";

		this.m_SceneViewInfos = _SceneViewInfos;

		////////////////////////////////////////////////////////////////////////////
		// Make the RenderSurfaceSet the first node of the hierarchy
		this.m_RenderSurfaceSet.parent = this.m_Client.renderGraphRoot;	// Should make us the first child node, the standard scene will render under this root node
		this.m_RenderSurfaceSet.priority = 0;


		////////////////////////////////////////////////////////////////////////////
		// Make the StartSet the second node of the hierarchy
		this.m_StateSet.parent = this.m_Client.renderGraphRoot;
		this.m_StateSet.priority = 1;									// Should make us the second and last child node, all post-process nodes will lie under this root node

 
		////////////////////////////////////////////////////////////////////////////
		// Tie the scene's root Viewport node to our RenderSurfaceSet
		this.m_SceneViewInfos.viewport.parent = null;						// Clear parent first
		this.m_SceneViewInfos.viewport.parent = this.m_RenderSurfaceSet;	// Re-parent
		this.m_SceneViewInfos.priority = 0;

		// Store the main scene's viewport root somewhere for later update
		this.m_MainSceneViewportRootNode = this.m_SceneViewInfos.viewport;


		////////////////////////////////////////////////////////////////////////////
 		// Update the viewport root node with the correct cropping size
		this.UpdateMainSceneRootViewport_();
	},

	// Updates the main scene's viewport root node with the correct cropping size
	//
	UpdateMainSceneRootViewport_ : function()
	{
		if ( typeof(this.m_MainSceneViewportRootNode) == typeof(undefined) )
			return;	// Not defined yet...

		// We have the rendering dimensions as m_Width and m_Height
		// We know the size of the offscreen render target
		// We can then compute the main scene viewport's cropping dimensions
		this.m_MainSceneViewportRootNode.viewport = this.m_RenderTarget.getViewport();
	},

	// Binds a post-process pass to this chain (NOTE: the order in which you bind passes is the order in which they will be rendered!)
	//	_Pass, the pass to bind
	//	_RenderTarget, the render target to render to
	//
	BindPass : function( _Pass )
	{
		if ( typeof(_Pass) == typeof(undefined) || _Pass == null )
			throw "Invalid pass to bind!";
		if ( _Pass.m_Bound )
			throw "Pass " + _Pass.m_Name + " is already bound !";

		////////////////////////////////////////////////////////////////////////////
		// Create a render surface set that will setup this pass's render surface (if needed)
		//
		var RootRenderNode = this.m_StateSet;
		var	ViewportCrop = [0.0, 0.0, 1.0, 1.0];	// Default viewport crop is the entire screen

		if ( _Pass.getRenderTarget() != null )
		{	// We must create a RenderSurfaceSet render node to render into that texture
			var OldRootRenderNode = RootRenderNode;

			RootRenderNode = this.m_Pack.createObject( 'RenderSurfaceSet' );
			RootRenderNode.name = _Pass.m_Name + " RenderSurfaceSet";
			RootRenderNode.parent = OldRootRenderNode;
			RootRenderNode.priority = this.m_Passes.length;
			RootRenderNode.renderSurface = _Pass.getRenderTarget().getSurface();

			// Store that render node into the pass
			_Pass.m_ParentRenderSurfaceSet = RootRenderNode;

//window.alert( "Created RenderSurfaceSet node for rendertarget \"" + _Pass.getRenderTarget().getName() + "\" ==> " + RootRenderNode.renderSurface )

			ViewportCrop = _Pass.getRenderTarget().getViewport();
		}
		else
			_Pass.m_Viewport.priority = this.m_Passes.length;

		// Tie the pass's render graph to the root render node
		_Pass.m_Viewport.parent = RootRenderNode;
		_Pass.m_Viewport.viewport = ViewportCrop;		// Setup the pass's viewport crop dimensions

		// Add this pass
		this.m_Passes.push( _Pass );
		_Pass.m_Bound = true;


		////////////////////////////////////////////////////////////////////////////
		// Create as many texture samplers as input passes
		//
		var InputSamplers = [];
		var TextureCoords = [];

		var Inputs = _Pass.getInputRenderTargets();
		for ( var InputIndex=0; InputIndex < Inputs.length; InputIndex++ )
		{
			var	Input = Inputs[InputIndex];

// 			var	TextureSampler = g_pack.createObject( 'Sampler' );
// 				TextureSampler.name = Input.getName();
// 				TextureSampler.texture = Input.getTexture();
// 				TextureSampler.addressModeU = o3djs.base.o3d.Sampler.CLAMP;
// 				TextureSampler.addressModeV = o3djs.base.o3d.Sampler.CLAMP;
// 				TextureSampler.minFilter = o3djs.base.o3d.Sampler.POINT;
// 				TextureSampler.magFilter = o3djs.base.o3d.Sampler.POINT;
// 				TextureSampler.mipFilter = o3djs.base.o3d.Sampler.NONE;

			InputSamplers.push( Input.getSampler() );
			TextureCoords.push( Input.getViewport() );

// window.alert( "InputSampler #" + InputIndex + " => Texture = \"" + Input.getTexture().name + "\"" )
		}

		// Call the setup callback function
		// The user should setup the input texture samplers for his pass in that callback
		try
		{
			_Pass.m_InputSamplersSetupCallback( _Pass, InputSamplers, TextureCoords );
		}
		catch ( e )
		{
			throw "An exception occurred while calling pass \"" + _Pass.getName() + "\" setup callback function " + e;
		}
	},

	// Helper function that creates a post-process material given a shader path
	//	_ShaderPath, the path to the shader, relative to the current path (e.g. 'Data/Shaders/MyShader.shader')
	//	_optName, an optional name for the material
	//
	CreatePostProcessMaterial : function( _ShaderPath, _optName )
	{
		// Create the material
		var PostMaterial = this.m_Pack.createObject( 'Material' );
			PostMaterial.name = typeof(_optName) != typeof(undefined) ? _optName : "#PostProcess Material";		// <== Use the '#' prefix so the material can be recognized as "internal"

		// Load the FX
		if ( this.m_EffectsManager != null )
		{
			var PostEffect = this.m_EffectsManager.CreateEffect( typeof(_optName) != typeof(undefined) ? _optName : "PostProcess Effect", _ShaderPath );
				PostEffect.Load();
				PostEffect.BindObject( PostMaterial );
		}
		else
		{
			var	PostEffect = this.m_Pack.createObject( 'Effect' );
			o3djs.effect.loadEffect( PostEffect, _ShaderPath );

			// Assign the effect to the material and create uniform parameters
			PostMaterial.effect = PostEffect;
			PostEffect.createUniformParameters( PostMaterial );
		}

		return	PostMaterial;
	}
};


////////////////////////////////////////////////////////////////////////////
// Declares a class holding informations on a post-process pass
//
//	_O3DClient, the O3D client
//	_O3DPack, the pack in which to operate
//	_Name, the name of the pass
//	_InputRenderTargets, the array of render targets to use as input
//	_RenderTarget, the output render target this pass will render to (if null, the pass is considered to be the last one and renders directly to the screen)
//	_Material, the material to apply to perform the post-process pass (hint: you can use the PostProcessChain's CreatePostProcessMaterial() function to create one easily)
//	_InputSamplersSetupCallback, the callback that will be called to setup the input samplers of the material
//	_optQuadSubdivisionsCount, the optional count of subdivisions for the fullscreen quad (useful if you want to move some low frequency calculations in the vertex shader and let the card interpolate them)
//
//	NOTES:
//	 The callback should be of the form :
//		_ Callback( _Pass, _InputSamplers, _TextureCoords )
//
//		with	_InputSamplers being the array of input samplers created for each input render target requested by this pass
//		and		_TextureCoords being the array of texture coords into which the input render targets were written to
//
patapi.PostProcessPass = function( _O3DClient, _O3DPack, _Name, _InputRenderTargets, _OutputRenderTarget, _Material, _InputSamplersSetupCallback, _optQuadSubdivisionsCount )
{
	if ( _InputRenderTargets == null || _InputRenderTargets.length == 0 )
		throw "Invalid InputRendertTargets for pass \"" + _Name + "\": you must specify an array of at least one render target"

	this.m_Client = _O3DClient;
	this.m_Pack = _O3DPack;
	this.m_Name = _Name;
	this.m_Inputs = _InputRenderTargets;
	this.m_RenderTarget = _OutputRenderTarget;
	this.m_Material = _Material;
	this.m_InputSamplersSetupCallback = _InputSamplersSetupCallback;

	// Delay-bound data
	this.m_Bound = false;
	this.m_ParentRenderSurfaceSet = null;	

	this.Initialize( _optQuadSubdivisionsCount );
};

patapi.PostProcessPass.prototype =
{
	// Gets the pass' name
	getName : function()					{ return this.m_Name; },

	// Gets the pass' material
	getMaterial : function()				{ return this.m_Material; },

	// Gets or sets the visible flag
	getVisible : function()					{ return this.m_QuadTransform.visible; },
	setVisible : function( value )			{ this.m_QuadTransform.visible = value; },

	// Gets the StateSet render object so you can specify which render states should be used to render that post-process pass
	getStateSet : function()				{ return this.m_StateSet; },

	// Gets the output render target
	getRenderTarget : function()			{ return this.m_RenderTarget; },

	// Gets the input render targets used by this pass
	getInputRenderTargets : function()		{ return this.m_Inputs; },

	// Gets the OPTIONAL parent "RenderSurfaceSet" render node (valid only if the pass isn't the final pass and is rendering to render target)
	// You can use that to dynamically change the render target of a pass if necessary
	getParentRenderSurfaceSet : function()	{ return this.m_ParentRenderSurfaceSet; },

	// Initializes the post-process pass
	//
	Initialize : function( _optQuadSubdivisionsCount )
	{
		////////////////////////////////////////////////////////////////////////////
		// Create the quad object that will stand in front of the camera to simulate post-process
		//
		this.m_QuadTransform = this.m_Pack.createObject( 'Transform' );						// Create a Root Transform for the image processing scene.
		this.m_QuadShape = this.CreateFullscreenQuadShape_( _optQuadSubdivisionsCount );	// Creates the quad in the XY plane
		this.m_QuadTransform.addShape( this.m_QuadShape );									// Attach the quad to the image processing scene.


		////////////////////////////////////////////////////////////////////////////
		// Create the render nodes
		//

		// Create a simple viewport render node that will be the root of our hierarchy of post-processes
		this.m_Viewport = this.m_Pack.createObject( 'Viewport' );
		this.m_Viewport.name = this.m_Name + " ViewportCrop";

		// Create the draw context with a simple orthographic canonical projection and an identity camera matrix
		// As the camera views along -Z and we created a quad aligned in the Z plane, it will occupy the entire screen
		this.m_DrawContext = this.m_Pack.createObject( 'DrawContext' );
		this.m_DrawContext.name = this.m_Name + " DrawContext";
		this.m_DrawContext.view = Math3D.matrix4.identity();
		this.m_DrawContext.projection = Math3D.matrix4.orthographic( -1, +1, -1, +1, -1, +1 );

		// Create our single draw-list
		this.m_DrawList = this.m_Pack.createObject( 'DrawList' );
		this.m_DrawList.name = this.m_Name + " DrawList";

		// Create a tree-traversal render node bound to our single quad object
		var	TreeTraversal = this.m_Pack.createObject( 'TreeTraversal' );
			TreeTraversal.name = this.m_Name + " TreeTraversal";
			TreeTraversal.transform = this.m_QuadTransform;									// Tie to the quad transform as it's the only object we're going to render
			TreeTraversal.registerDrawList( this.m_DrawList, this.m_DrawContext, true );	// Tie to our only draw list
			TreeTraversal.parent = this.m_Viewport;
			TreeTraversal.priority = 0;

		// Create an empty state set above the draw pass
		var	State = this.m_Pack.createObject( 'State' );

		this.m_StateSet = this.m_Pack.createObject( 'StateSet' );
		this.m_StateSet.name = this.m_Name + " StateSet";
		this.m_StateSet.state = State;
		this.m_StateSet.parent = this.m_Viewport;
		this.m_StateSet.priority = 1;

		// Create a single drawpass for that post-process
		var DrawPass = this.m_Pack.createObject( 'DrawPass' );
			DrawPass.name = this.m_Name + " DrawPass";
			DrawPass.drawList = this.m_DrawList;
			DrawPass.parent = this.m_StateSet;
			DrawPass.priority = 0;

		// Assign the material's draw list
		this.m_Material.drawList = this.m_DrawList;


		////////////////////////////////////////////////////////////////////////////
		// At this point, the hierarchy of render nodes looks like this:
		//
		//        [Viewport]
		//            |
		//            +---------------+
		//            |               |
		//     [TreeTraversal]    [StateSet]
		//                            |
		//                        [DrawPass]
		//
	},

	// Creates the quad shape that we will render to apply this post-process pass
	//
	CreateFullscreenQuadShape_ : function( _optQuadSubdivisionsCount )
	{
		var vertexInfo = o3djs.primitives.createVertexInfo();
		var positionStream = vertexInfo.addStream( 3, o3djs.base.o3d.Stream.POSITION );
		var texCoordStream = vertexInfo.addStream( 2, o3djs.base.o3d.Stream.TEXCOORD, 0 );

		// If you read DirectX's documentation about "Directly Mapping Texels to Pixels", you'll understand why this offset
		var	OffsetX = 0;
		var	OffsetY = 0;
		if ( this.m_RenderTarget != null )
		{
			OffsetX = -0.5 * 2.0 / this.m_RenderTarget.getTargetWidth();
			OffsetY = -0.5 * 2.0 / this.m_RenderTarget.getTargetHeight();
		}
		else
		{	// In case we render directly to screen
			OffsetX = -0.5 * 2.0 / this.m_Client.width;
			OffsetY = -0.5 * 2.0 / this.m_Client.height;
		}

		// Generate the individual vertices in our vertex buffer.
// 		positionStream.addElement( -1.0 + OffsetX, +1.0 - OffsetY, 0.0 );
// 		positionStream.addElement( -1.0 + OffsetX, -1.0 - OffsetY, 0.0 );
// 		positionStream.addElement( +1.0 + OffsetX, -1.0 - OffsetY, 0.0 );
// 		positionStream.addElement( +1.0 + OffsetX, +1.0 - OffsetY, 0.0 );
// 		texCoordStream.addElement( 0.0, 0.0 );
// 		texCoordStream.addElement( 0.0, 1.0 );
// 		texCoordStream.addElement( 1.0, 1.0 );
// 		texCoordStream.addElement( 1.0, 0.0 );
// 
// 		vertexInfo.addTriangle( 0, 1, 2 );
// 		vertexInfo.addTriangle( 0, 2, 3 );

		var	SubdivisionsCount = 1 + (_optQuadSubdivisionsCount ? _optQuadSubdivisionsCount : 0);
		for ( var X=0; X <= SubdivisionsCount; X++ )
		{
			var	U = 1.0 * X / SubdivisionsCount
			var	PosX = -1.0 + OffsetX + 2.0 * U;
			for ( var Y=0; Y <= SubdivisionsCount; Y++ )
			{
				var	V = 1.0 * Y / SubdivisionsCount
				var	PosY = +1.0 - OffsetY - 2.0 * V;
		 		positionStream.addElement( PosX, PosY, 0.0 );
		 		texCoordStream.addElement( U, V );
			}
		}

		for ( var X=0; X < SubdivisionsCount; X++ )
			for ( var Y=0; Y < SubdivisionsCount; Y++ )
			{
		 		vertexInfo.addTriangle(	(SubdivisionsCount+1) * (Y+0) + (X+0),
		 								(SubdivisionsCount+1) * (Y+1) + (X+1),
		 								(SubdivisionsCount+1) * (Y+1) + (X+0) );

		 		vertexInfo.addTriangle(	(SubdivisionsCount+1) * (Y+0) + (X+0),
		 								(SubdivisionsCount+1) * (Y+0) + (X+1),
		 								(SubdivisionsCount+1) * (Y+1) + (X+1) );
			}
		
		var	ResultingShape = vertexInfo.createShape( this.m_Pack, this.m_Material );
			ResultingShape.name = "Post-Process Shape " + this.m_Name;

		return	ResultingShape;
	}
};


////////////////////////////////////////////////////////////////////////////
// Declares a class holding the Texture and TextureSampler used as a Render Target
// It automatically handles the fact that Render Targets have to have dimensions of a power of 2
//	and holds the "Viewport" property that tells into which part of the render target the rendering
//	should take place so the final rendered image is of the requested size.
//
//	_O3DPack, the pack in which to operate
//	_Name, the name of the render target (useful for debugging)
//	_Width, _Height, _Format, the dimensions of the render target
//	_optFilterMode, the optional sampling filter mode of type 'o3djs.base.o3d.Sampler' (if null or undefined, the o3djs.base.o3d.Sampler.POINT is used)
//	_optAddressMode, the optional address mode of type 'o3djs.base.o3d.Sampler' (if null or undefined, the o3djs.base.o3d.Sampler.CLAMP is used)
//
patapi.RenderTarget = function( _O3DPack, _Name, _Width, _Height, _Format, _optFilterMode, _optAddressMode )
{
	this.m_Pack = _O3DPack;
	this.m_Name = _Name;
	this.m_Width = _Width;
	this.m_Height = _Height;
	this.m_Format = _Format;
	this.m_TargetWidth = 1 << Math.ceil( Math.log( this.m_Width ) / Math.LN2 );
	this.m_TargetHeight = 1 << Math.ceil( Math.log( this.m_Height ) / Math.LN2 );
	this.m_ViewportRectangle = [0, 0, _Width / this.m_TargetWidth, _Height / this.m_TargetHeight];

	try
	{
		this.m_RenderTarget = this.m_Pack.createTexture2D( this.m_TargetWidth, this.m_TargetHeight, this.m_Format, 1, true );
		if ( this.m_RenderTarget == null )
			throw "CreateTexture2D failed with dimensions " + this.m_TargetWidth + "x" + this.m_TargetHeight + " (Format =" + this.m_Format + ") !";

		this.m_RenderTarget.name = this.m_Name + " (Tex)";
		this.m_RenderSurface = this.m_RenderTarget.getRenderSurface( 0, this.m_Pack );
		this.m_RenderSurface.name = this.m_Name + " (Sur)";
	}
	catch ( e )
	{
		throw "Failed to create the render target for \"" + this.m_Name + "\" ! " + e
	};

	// Create the texture sampler
	try
	{
		var FilterMode = o3djs.base.o3d.Sampler.POINT;
		if ( typeof(_optFilterMode) != typeof(undefined) && _optFilterMode != null )
			FilterMode = _optFilterMode;

		var AddressMode = o3djs.base.o3d.Sampler.CLAMP;
		if ( typeof(_optAddressMode) != typeof(undefined) && _optAddressMode != null )
			AddressMode = _optAddressMode;

// 		window.alert( "FilterMode = " + FilterMode + "\n"
// 					+ "AddressMode = " + AddressMode )

		this.m_RenderTargetSampler = this.m_Pack.createObject( 'Sampler' );
		this.m_RenderTargetSampler.name = this.m_Name;
		this.m_RenderTargetSampler.texture = this.m_RenderTarget;
		this.m_RenderTargetSampler.addressModeU = AddressMode;
		this.m_RenderTargetSampler.addressModeV = AddressMode;
		this.m_RenderTargetSampler.minFilter = FilterMode;
		this.m_RenderTargetSampler.magFilter = FilterMode;
		this.m_RenderTargetSampler.mipFilter = o3djs.base.o3d.Sampler.NONE;
	}
	catch ( e )
	{
		throw "Failed to create the texture sampler for \"" + this.m_Name + "\" ! " + e
	};
};

patapi.RenderTarget.prototype =
{
	// Gets the render target name
	//
	getName : function()			{ return this.m_Name; },

	// Gets the render target requested width
	//
	getWidth : function()			{ return this.m_Width; },

	// Gets the render target requested height
	//
	getHeight : function()			{ return this.m_Height; },

	// Gets the render target requested format
	//
	getFormat : function()			{ return this.m_Format; },

	// Gets the render target actual width (a power of 2)
	//
	getTargetWidth : function()		{ return this.m_TargetWidth; },

	// Gets the render target actual height (a power of 2)
	//
	getTargetHeight : function()	{ return this.m_TargetHeight; },

	// Gets the render target's viewport rectangle (to make the engine render requested width/height into actual width/height)
	//
	getViewport : function()		{ return this.m_ViewportRectangle; },

	// Gets the render target texture (READ access)
	//
	getTexture : function()			{ return this.m_RenderTarget; },

	// Gets the render target texture sampler (READ access)
	//
	getSampler : function()			{ return this.m_RenderTargetSampler; },

	// Gets the render target surface (WRITE access)
	//
	getSurface : function()			{ return this.m_RenderSurface; },

	// Disposes of used resources
	//
	Dispose : function()
	{
		this.m_Pack.removeObject( this.m_RenderTargetSampler );
		this.m_Pack.removeObject( this.m_RenderSurface );
		this.m_Pack.removeObject( this.m_RenderTarget );

		delete this.m_RenderTargetSampler;
		delete this.m_RenderSurface;
		delete this.m_RenderTarget;
	}
};
