//////////////////////////////////////////////////////////////////////////////
//
// Tools_Entity_DynamicLight.cpp
//
// Copyright (c) 2007 Rebellion (Derby). All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_TARGET_SM3


// This file's header.
#include "Entity/Tools_Entity_DynamicLight.h"

// Engine independent.
#include "Animation/Asura_Animation.h"
#include "ChunkSystem/Asura_Chunk_DynamicLightTemplate.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/DynamicLight/Asura_ClientEntity_DynamicLight.h"
#include "Entity/DynamicLight/Asura_DynamicLight_Messages.h"
#include "Entity/DynamicLight/Asura_ServerEntity_DynamicLight.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Graph_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_TexturePicker_Dialog.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Network/Asura_Message.h"
#include "Rendering/Asura_Render.h"

// Tools.
#include "Entity/Tools_Entity_Data.h"
#include "Interface/Tools_ColourPicker.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Movement/Tools_Movement.h"
#include "Objects/Tools_Object_System.h"
#include "PC/FileSystem/Tools_FileSystem.h"
#include "Texturing/Tools_PickTexture.h"

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

static const float			fTEXTURE_PICKER_SIZE = 64.0f;

Tools_Entity_DynamicLight*  Tools_Entity_DynamicLight::s_pxSelected = NULL;
bool						Tools_Entity_DynamicLight::m_bShowVolume = false;
bool						Tools_Entity_DynamicLight::s_bModified = false;
bool					    Tools_Entity_DynamicLight::s_bResetCurrentEntityPtrOnDeselect = true;
Asura_Tabs_Dialog		   *Tools_Entity_DynamicLight::s_pxTabs = NULL;
u_int						Tools_Entity_DynamicLight::s_uTab = 0;
int							Tools_Entity_DynamicLight::s_iShowLightVolumesMode = 0;		// 0=none, 1=selected, 2=all

//////////////////////
TOOLS_DECLARE_SHARED_PARAM(int,		0x8F472E42,	DynamicLight_Type)
//photon light
TOOLS_DECLARE_SHARED_PARAM(Asura_Hash_ID,	0x5815B89C,	DynamicLight_ProjCubeTextureID)
TOOLS_DECLARE_SHARED_PARAM(Asura_String,	0xA98C49CC,	DynamicLight_ProjCubeTextureName)
TOOLS_DECLARE_SHARED_PARAM(Asura_Hash_ID,	0x563C0806,	DynamicLight_ProjCubeBlurredTextureID)
TOOLS_DECLARE_SHARED_PARAM(Asura_String,	0xB75C66B6,	DynamicLight_ProjCubeBlurredTextureName)
TOOLS_DECLARE_SHARED_PARAM(float,	0x59777B35,	DynamicLight_Range)
TOOLS_DECLARE_SHARED_PARAM(float,	0x36CCD3CF,	DynamicLight_SharpBlurredBlendDist)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x6E121A46,	DynamicLight_UseLimitingAngles)
TOOLS_DECLARE_SHARED_PARAM(float,	0x691B93F2,	DynamicLight_LimitingAngleInner)
TOOLS_DECLARE_SHARED_PARAM(float,	0x69736617,	DynamicLight_LimitingAngleOuter)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x99CAB000,	DynamicLight_UseCustomLightVolume)

//projector
TOOLS_DECLARE_SHARED_PARAM(int,		0xF091CF6C,	DynamicLight_BlendMode)
TOOLS_DECLARE_SHARED_PARAM(Asura_Hash_ID,	0x7E7E8D6E,	DynamicLight_TextureID)
TOOLS_DECLARE_SHARED_PARAM(Asura_String,	0xD913321E,	DynamicLight_TextureName)
TOOLS_DECLARE_SHARED_PARAM(Asura_Hash_ID,	0x123DE63D,	DynamicLight_DetailTextureID)
TOOLS_DECLARE_SHARED_PARAM(Asura_String,	0x7A5F932D,	DynamicLight_DetailTextureName)
TOOLS_DECLARE_SHARED_PARAM(float,	0xA289FD0D,	DynamicLight_UScrollSpeed)
TOOLS_DECLARE_SHARED_PARAM(float,	0xA2753603,	DynamicLight_UScrollRange)
TOOLS_DECLARE_SHARED_PARAM(float,	0xAA3BA26C,	DynamicLight_VScrollSpeed)
TOOLS_DECLARE_SHARED_PARAM(float,	0xAA26DB62,	DynamicLight_VScrollRange)
TOOLS_DECLARE_SHARED_PARAM(float,	0xD1DA04E8,	DynamicLight_DetailTextureScale)
TOOLS_DECLARE_SHARED_PARAM(float,	0xFC5CECC5,	DynamicLight_FoV)
TOOLS_DECLARE_SHARED_PARAM(float,	0x731E2F6A,	DynamicLight_UVAspectRatio)
TOOLS_DECLARE_SHARED_PARAM(float,	0xD8DC48D2,	DynamicLight_NearPlaneDist)
TOOLS_DECLARE_SHARED_PARAM(float,	0x63E86FD3,	DynamicLight_FarPlaneDist)
TOOLS_DECLARE_SHARED_PARAM(float,	0x0C4AAFDE,	DynamicLight_ParallelProjScale)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x65300C9E,	DynamicLight_ParallelProjection)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x77FC4156,	DynamicLight_UseLimitingPlane)
TOOLS_DECLARE_SHARED_PARAM(float,	0x5FDC71BF,	DynamicLight_InnerFoV)

//colour flicker
TOOLS_DECLARE_SHARED_PARAM(float,	0xD765C02D,	DynamicLight_BaseColourR)
TOOLS_DECLARE_SHARED_PARAM(float,	0xD765C022,	DynamicLight_BaseColourG)
TOOLS_DECLARE_SHARED_PARAM(float,	0xD765C01D,	DynamicLight_BaseColourB)
TOOLS_DECLARE_SHARED_PARAM(float,	0x3C6EF67A,	DynamicLight_BaseIntensity)
TOOLS_DECLARE_SHARED_PARAM(float,	0x162BAADA,	DynamicLight_GraphDuration)
TOOLS_DECLARE_SHARED_PARAM(float,	0x38977912,	DynamicLight_GraphTimerOffset)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xB41642B6,	DynamicLight_Looping)
//colour/range sub-tab
TOOLS_DECLARE_SHARED_PARAM(bool,	0x937B91BB,	DynamicLight_UseGraphColour)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x6065AB6E,	DynamicLight_UseGraphRange)
//flicker sub-tab
TOOLS_DECLARE_SHARED_PARAM(bool,	0x755AEBE9,	DynamicLight_UseGraphFlicker)
//strobe sub-tab
TOOLS_DECLARE_SHARED_PARAM(bool,	0xD23A3F60,	DynamicLight_UseStrobe)
TOOLS_DECLARE_SHARED_PARAM(float,	0xE081EE35,	DynamicLight_StrobeMinTimeOn)
TOOLS_DECLARE_SHARED_PARAM(float,	0x2FBBD7D9,	DynamicLight_StrobeMinTimeOff)
TOOLS_DECLARE_SHARED_PARAM(float,	0xB276D7C7,	DynamicLight_StrobeMaxTimeOn)
TOOLS_DECLARE_SHARED_PARAM(float,	0x9C642087,	DynamicLight_StrobeMaxTimeOff)

//falloff tab
TOOLS_DECLARE_SHARED_PARAM(float,	0x08FEEC81,	DynamicLight_FalloffInnerRatio)
TOOLS_DECLARE_SHARED_PARAM(float,	0x8C3A2C79,	DynamicLight_FalloffPower)
TOOLS_DECLARE_SHARED_PARAM(float,	0x0D9D2758,	DynamicLight_FadeoutStart)
TOOLS_DECLARE_SHARED_PARAM(float,	0x006973F0,	DynamicLight_FadeoutDist)

//shadows tab
TOOLS_DECLARE_SHARED_PARAM(int,		0x6C455BCB,	DynamicLight_ShadowMode)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xF96F748B,	DynamicLight_ScaleShadowStrengthByBrightness)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x087A2E4C,	DynamicLight_ShadowDarkeningPass)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xE4568C1B,	DynamicLight_ShadowUseNoise)
TOOLS_DECLARE_SHARED_PARAM(float,	0x5F2A1E69,	DynamicLight_ShadowStrength)
TOOLS_DECLARE_SHARED_PARAM(float,	0x430427A9,	DynamicLight_ShadowSoftness)
TOOLS_DECLARE_SHARED_PARAM(float,	0xB4F56BB1,	DynamicLight_ShadowVerticalOffset)

//move tab
TOOLS_DECLARE_SHARED_PARAM(bool,	0xB7B2683A,	DynamicLight_EnableMovement)
TOOLS_DECLARE_SHARED_PARAM(float,	0x8F274BA0,	DynamicLight_RotSpeedX)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xD8609E42,	DynamicLight_SwingX)
TOOLS_DECLARE_SHARED_PARAM(float,	0x5D9B505C,	DynamicLight_SwingDistX)
TOOLS_DECLARE_SHARED_PARAM(float,	0x8F274BA1,	DynamicLight_RotSpeedY)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xD8609E43,	DynamicLight_SwingY)
TOOLS_DECLARE_SHARED_PARAM(float,	0x5D9B505D,	DynamicLight_SwingDistY)
TOOLS_DECLARE_SHARED_PARAM(float,	0x8F274BA2,	DynamicLight_RotSpeedZ)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xD8609E44,	DynamicLight_SwingZ)
TOOLS_DECLARE_SHARED_PARAM(float,	0x5D9B505E,	DynamicLight_SwingDistZ)
TOOLS_DECLARE_SHARED_PARAM(float,   0x17AB89D2,	DynamicLight_RotationTimerOffset)

//special fx tab
TOOLS_DECLARE_SHARED_PARAM(bool,	0xC5EEB42E,	DynamicLight_HasCorona)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x6A374D2C,	DynamicLight_DontFadeCoronaWithDistance)
TOOLS_DECLARE_SHARED_PARAM(float,	0xB36E35B6,	DynamicLight_CoronaRadius)
TOOLS_DECLARE_SHARED_PARAM(float,	0x9762223D,	DynamicLight_CoronaVolumetricDepthScale)
TOOLS_DECLARE_SHARED_PARAM(Asura_Hash_ID,	0x830CFBD2,	DynamicLight_CoronaTextureID)
TOOLS_DECLARE_SHARED_PARAM(Asura_String,	0xF3BF9782,	DynamicLight_CoronaTextureName)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xE7A70256,	DynamicLight_CoronaUsesModLinking)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x96914467,	DynamicLight_CastEnvironmentShadows)









Tools_Entity_DynamicLight::Tools_Entity_DynamicLight( Asura_ServerEntity_DynamicLight* pxEntity ) : PARENT( pxEntity )
{
	Type					= TOT_Entity_DynamicLight;
	m_pxServerEntity		= pxEntity;
    m_xParams				= pxEntity->GetParams();
    m_bParamsModified		= true;	
	s_bModified				= false;
	Position				= m_xParams.m_xPosition;
	Orientation				= m_xParams.m_xOrientation;
	
	m_pxVolumeController	= 0;
	m_pxLimitingPlane		= 0;
	
	Asura_String mesh_name	= "ToolsShape_Light";
	Mesh = Asura_Mesh_System::GetMesh(mesh_name);

	ReloadTextures();

	pxEntity->SetParams( m_xParams );  		   
}

void Tools_Entity_DynamicLight::ReloadTextures(void) const
{
	Asura_Texture_Management::AddTexture(m_xParams.m_xTextureName);
	Asura_Texture_Management::AddTexture(m_xParams.m_xDetailTextureName);
	Asura_Texture_Management::AddTexture(m_xParams.m_xCoronaTextureName);
	Asura_Texture_Management::AddTexture(m_xParams.m_xProjCubeTextureName);
	Asura_Texture_Management::AddTexture(m_xParams.m_xProjCubeBlurredTextureName);
	Asura_Texture_Management::LoadTextures();
}


Tools_Entity_DynamicLight::~Tools_Entity_DynamicLight( void )
{
	if (m_pxVolumeController)
	{
		delete m_pxVolumeController;
	};	

	if (m_pxLimitingPlane)
	{
		delete m_pxLimitingPlane;
	};	
}






void Tools_Entity_DynamicLight::RefreshOverlay()
{	

	s_pxSelected->CreateOverlay();

}

bool Tools_Entity_DynamicLight::CanCastDarkeningShadow()
{
	return s_pxSelected->m_xParams.m_iShadowMode == 2 || s_pxSelected->GetShadowFlag(ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS);
}
bool Tools_Entity_DynamicLight::CanCastShadow()
{
	return s_pxSelected->m_xParams.m_iShadowMode != 0 || s_pxSelected->GetShadowFlag(ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS);
}



void Tools_Entity_DynamicLight::AddOverlayControls( Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset )
{
	PARENT::AddOverlayControls(pxOverlay, xOffset);

    Asura_Dialog* pxDialog;

	xOffset.y -= Asura_Dialog::DefaultSpacing();
	xOffset.x += 14;	
	
	xOffset.x += 16;
	pxDialog = new Asura_Dialog("Show Volumes", xOffset);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x += 10;

	static const char* aszShowVolumeModes[] = { "None","Selected Only","All"};
	pxDialog = new Asura_DropDown_List_Dialog(xOffset, aszShowVolumeModes , &s_iShowLightVolumesMode,3,3,NULL);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 26;

	xOffset.x -= 14;
	xOffset.y += Asura_Dialog::DefaultSpacing();	


    // Sort out spacing between our gadgets and default object stuff.
    if( pxOverlay->m_pxDialogList )
    {
        xOffset.y += Asura_Dialog::LargeSpacing() - Asura_Dialog::DefaultSpacing();
    }

	// Create the tabs
	static const char* aszTabNames[] = { "Light Type", "Colour/Flicker", "Falloff", "Shadows", "Movement", "Special FX"};

	Asura_Tabs_Dialog *pxTabs = NULL;
	pxDialog = s_pxTabs = pxTabs = new Asura_Tabs_Dialog(xOffset, aszTabNames, 6, 70);
	pxOverlay->AddDialog(pxDialog);

	Asura_DynamicLight_Parameters &xParams = s_pxSelected->m_xParams;

	xOffset.y += Asura_Dialog::LargeSpacing();
	float fTabTop = xOffset.y;

	// ======================================================================================================
	// LIGHT TYPE TAB
	// ======================================================================================================
	pxTabs->SelectTab(0);

	static const char* aszTypeNames[] = { "Omni", "Projector", "Spotlight", "Parallel (Infinite)"};

	// Type selector drop-down
	pxDialog = new Asura_DropDown_List_Dialog(xOffset, aszTypeNames, &g_xParam_DynamicLight_Type, 4,4, RefreshOverlay, NULL, "Type");
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();	

	// Add the controls for each light type	
	switch (xParams.m_eType)
	{
		// Omni/Photon lights
		// --------------------------------------------------------------------------------------------------		
		case ASURA_DYNAMICLIGHT_PHOTON:
			{
				pxDialog = new Asura_Numerical_Dialog("Range", xOffset, &g_xParam_DynamicLight_Range, 0.0f, 5000.0f, 1.0f, "%0.02f");
				pxTabs->AddDialog(pxDialog);
				xOffset.y += Asura_Dialog::LargeSpacing();

				pxDialog = new Asura_TexturePicker_Dialog( "Projected Cubemap (sharp)", xOffset, NULL, &g_xParam_DynamicLight_ProjCubeTextureID, &g_xParam_DynamicLight_ProjCubeTextureName, fTEXTURE_PICKER_SIZE, fTEXTURE_PICKER_SIZE, &s_bModified);
				pxTabs->AddDialog( pxDialog );
				
				xOffset.x += 20.0f;
				pxDialog = new Asura_TexturePicker_Dialog( "Projected Cubemap (blurred)", xOffset, NULL, &g_xParam_DynamicLight_ProjCubeBlurredTextureID, &g_xParam_DynamicLight_ProjCubeBlurredTextureName, fTEXTURE_PICKER_SIZE, fTEXTURE_PICKER_SIZE, &s_bModified);
				pxTabs->AddDialog( pxDialog );
				xOffset.x -= 20.0f;

				xOffset.y += Asura_Dialog::LargeSpacing() * 3.5f;

				pxDialog = new Asura_Numerical_Dialog("Sharp->Blurred blend distance", xOffset, &g_xParam_DynamicLight_SharpBlurredBlendDist, 0.0f, 250.0f, 0.1f, "%0.02f");
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::LargeSpacing();

			   	pxDialog = new Asura_SharedTickbox_Dialog("Use Limiting Angles", xOffset,&g_xParam_DynamicLight_UseLimitingAngles);
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::LargeSpacing();
				pxDialog = new Asura_Numerical_Dialog("Inner Angle", xOffset, &g_xParam_DynamicLight_LimitingAngleInner, 0.0f, 360.0f, 5.0f, "%0.02f");
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::DefaultSpacing();
				pxDialog = new Asura_Numerical_Dialog("Outer Angle", xOffset, &g_xParam_DynamicLight_LimitingAngleOuter, 0.0f, 360.0f, 5.0f, "%0.02f");
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::LargeSpacing();
				pxDialog = new Asura_Dialog("(Spotlights are *much* more efficient for narrow beams)",xOffset);
				pxTabs->AddDialog( pxDialog);
				

				xOffset.y += Asura_Dialog::LargeSpacing();

				pxDialog = new Asura_SharedTickbox_Dialog("Use Custom Light Volume", xOffset, &g_xParam_DynamicLight_UseCustomLightVolume);
				pxTabs->AddDialog(pxDialog);
				
				xOffset.x += 23;
				pxDialog = new Asura_Function_Dialog("Edit Volume", xOffset, EditCustomLightVolume, &s_bModified);
				pxTabs->AddDialog(pxDialog);

				xOffset.x += 12;
				pxDialog = new Asura_Function_Dialog("Reset Volume", xOffset, ResetCustomLightVolume, &s_bModified);
				pxTabs->AddDialog(pxDialog);
				xOffset.x -= 35;
				xOffset.y += Asura_Dialog::DefaultSpacing();
			}
			break;

		// Projectors
		// --------------------------------------------------------------------------------------------------
		case ASURA_DYNAMICLIGHT_PROJECTOR:
			{			
				xOffset.y -= Asura_Dialog::LargeSpacing();	
				xOffset.x += 25;				
				static const char * const g_aszBlendModes[] = {"Deferred Light", "Fake Light", "Additive", "Multiply (0-100%)", "Multiply X2 (0-200%)", "Standard Alpha Blend"};
				pxDialog = new Asura_DropDown_List_Dialog(xOffset, g_aszBlendModes, &g_xParam_DynamicLight_BlendMode, 6,6, RefreshOverlay, NULL, "Blend Mode");
				pxTabs->AddDialog(pxDialog);
				xOffset.x -= 25;
				xOffset.y += Asura_Dialog::LargeSpacing();	

				pxDialog = new Asura_TexturePicker_Dialog( "Projected Texture", xOffset, NULL, &g_xParam_DynamicLight_TextureID, &g_xParam_DynamicLight_TextureName, fTEXTURE_PICKER_SIZE, fTEXTURE_PICKER_SIZE, &s_bModified );
				pxTabs->AddDialog( pxDialog );
				xOffset.x += 25.0f;

				pxDialog = new Asura_TexturePicker_Dialog( "Detail Texture", xOffset, NULL, &g_xParam_DynamicLight_DetailTextureID, &g_xParam_DynamicLight_DetailTextureName, fTEXTURE_PICKER_SIZE, fTEXTURE_PICKER_SIZE, &s_bModified);
				pxTabs->AddDialog( pxDialog );

				xOffset.x += 13.0f;
				pxDialog = new Asura_Dialog("UV Scrolling", xOffset);
				pxDialog->SetHelpText("UV Scrolling for the detail texture. If range=0 then scrolling will be continuous, else it will be sin() based");
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::DefaultSpacing();				
				pxDialog = new Asura_Numerical_Dialog("U Speed", xOffset, &g_xParam_DynamicLight_UScrollSpeed , 0.0f, 99.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::DefaultSpacing();				
				pxDialog = new Asura_Numerical_Dialog("U Range", xOffset, &g_xParam_DynamicLight_UScrollRange , 0.0f, 5.0f, 0.001f, "%0.03f");
				pxTabs->AddDialog( pxDialog);				
				xOffset.y += Asura_Dialog::DefaultSpacing();				
				pxDialog = new Asura_Numerical_Dialog("V Speed", xOffset, &g_xParam_DynamicLight_VScrollSpeed , 0.0f, 99.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog);
				xOffset.y += Asura_Dialog::DefaultSpacing();				
				pxDialog = new Asura_Numerical_Dialog("V Range", xOffset, &g_xParam_DynamicLight_VScrollRange , 0.0f, 5.0f, 0.001f, "%0.03f");
				pxTabs->AddDialog( pxDialog);
				xOffset.x -= 13.0f;
				xOffset.y -= (4.0f*Asura_Dialog::DefaultSpacing());

				xOffset.x -= 25.0f;
				xOffset.y += Asura_Dialog::LargeSpacing() * 3.5f;
				
				pxDialog = new Asura_Numerical_Dialog("Detail Tex Repeats", xOffset, &g_xParam_DynamicLight_DetailTextureScale , 0.0f, 150.0f, 0.1f, "%0.01f");
				pxTabs->AddDialog( pxDialog );	
				xOffset.y += Asura_Dialog::DefaultSpacing();

				pxDialog = new Asura_Numerical_Dialog("FoV", xOffset, &g_xParam_DynamicLight_FoV , 0.0f, 150.0f, 0.1f, "%0.01f");
				pxTabs->AddDialog( pxDialog );	
				xOffset.y += Asura_Dialog::DefaultSpacing();

				pxDialog = new Asura_Numerical_Dialog("U:V Aspect Ratio", xOffset, &g_xParam_DynamicLight_UVAspectRatio , 0.1f, 10.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog ); 
				xOffset.y += Asura_Dialog::DefaultSpacing();

				xOffset.y -= (3 *Asura_Dialog::DefaultSpacing());
				xOffset.x +=25;

				pxDialog = new Asura_Numerical_Dialog("Near Plane Distance", xOffset, &g_xParam_DynamicLight_NearPlaneDist , 0.0f, 2500.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog );	
				xOffset.y += Asura_Dialog::DefaultSpacing();

				pxDialog = new Asura_Numerical_Dialog("Far Plane Distance", xOffset, &g_xParam_DynamicLight_FarPlaneDist , 0.0f, 2500.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog );
				xOffset.y += Asura_Dialog::DefaultSpacing();

				pxDialog = new Asura_Numerical_Dialog("Parallel Proj Scale", xOffset, &g_xParam_DynamicLight_ParallelProjScale , 0.1f, 2500.0f, 0.1f, "%0.02f");
				pxTabs->AddDialog( pxDialog ); 
				xOffset.y += Asura_Dialog::LargeSpacing();
				xOffset.x -=25;

			   	pxDialog = new Asura_SharedTickbox_Dialog("Parallel Projection", xOffset,&g_xParam_DynamicLight_ParallelProjection);
				pxTabs->AddDialog(pxDialog);

				xOffset.y += Asura_Dialog::LargeSpacing();

			   	pxDialog = new Asura_SharedTickbox_Dialog("Use Limiting Plane", xOffset,&g_xParam_DynamicLight_UseLimitingPlane);
				pxTabs->AddDialog(pxDialog);
				xOffset.x +=25;				
				pxDialog = new Asura_Function_Dialog("Edit Limiting Plane", xOffset, EditLimitingPlane, &s_bModified);
				pxTabs->AddDialog(pxDialog);
				xOffset.x -=25;				

				xOffset.y += Asura_Dialog::DefaultSpacing();

			}
			break;

		// Spotlights (projectors again, really)
		// --------------------------------------------------------------------------------------------------
		case ASURA_DYNAMICLIGHT_SPOTLIGHT:
			{
				pxDialog = new Asura_Numerical_Dialog("Outer Angle", xOffset, &g_xParam_DynamicLight_FoV , 0.0f, 150.0f, 0.1f, "%0.01f");
				pxTabs->AddDialog( pxDialog );	
				xOffset.y += Asura_Dialog::DefaultSpacing();

				pxDialog = new Asura_Numerical_Dialog("Inner Angle", xOffset, &g_xParam_DynamicLight_InnerFoV , 0.0f, 150.0f, 0.1f, "%0.01f");
				pxTabs->AddDialog( pxDialog ); 
				xOffset.y += Asura_Dialog::DefaultSpacing();

				xOffset.y -= (2 *Asura_Dialog::DefaultSpacing());
				xOffset.x +=25;

				pxDialog = new Asura_Numerical_Dialog("Near Plane Distance", xOffset, &g_xParam_DynamicLight_NearPlaneDist , 0.0f, 2500.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog );	
				xOffset.y += Asura_Dialog::DefaultSpacing();

				pxDialog = new Asura_Numerical_Dialog("Far Plane Distance", xOffset, &g_xParam_DynamicLight_FarPlaneDist , 0.0f, 2500.0f, 0.01f, "%0.02f");
				pxTabs->AddDialog( pxDialog );				
				xOffset.x -=25;

				xOffset.y += Asura_Dialog::LargeSpacing();

			   	pxDialog = new Asura_SharedTickbox_Dialog("Use Limiting Plane", xOffset,&g_xParam_DynamicLight_UseLimitingPlane);
				pxTabs->AddDialog(pxDialog);
				xOffset.x +=25;				
				pxDialog = new Asura_Function_Dialog("Edit Limiting Plane", xOffset, EditLimitingPlane, &s_bModified);
				pxTabs->AddDialog(pxDialog);
				xOffset.x -=25;				
				xOffset.y += Asura_Dialog::DefaultSpacing();


			}
			break;

		// Infinite Parallel Lights
		// --------------------------------------------------------------------------------------------------
		case ASURA_DYNAMICLIGHT_INFINITE:
			{
				pxDialog = new Asura_Dialog("** For experimentation only **", xOffset);
				pxTabs->AddDialog(pxDialog);				
				xOffset.y += Asura_Dialog::DefaultSpacing();
				
				pxDialog = new Asura_Dialog("Using this type of light is currently not recommended.", xOffset);
				pxTabs->AddDialog(pxDialog);
				xOffset.y += Asura_Dialog::LargeSpacing();

				pxDialog = new Asura_Numerical_Dialog("Range", xOffset, &g_xParam_DynamicLight_Range, 0.0f, 5000.0f, 1.0f, "%0.02f");
				pxTabs->AddDialog(pxDialog);
				xOffset.y += Asura_Dialog::LargeSpacing();

				pxDialog = new Asura_SharedTickbox_Dialog("Use Custom Light Volume", xOffset, &g_xParam_DynamicLight_UseCustomLightVolume);
				pxTabs->AddDialog(pxDialog);

				xOffset.x += 23;
				pxDialog = new Asura_Function_Dialog("Edit Volume", xOffset, EditCustomLightVolume, &s_bModified);
				pxTabs->AddDialog(pxDialog);

				xOffset.x += 12;
				pxDialog = new Asura_Function_Dialog("Reset Volume", xOffset, ResetCustomLightVolume, &s_bModified);
				pxTabs->AddDialog(pxDialog);
				xOffset.x -= 35;
				xOffset.y += Asura_Dialog::DefaultSpacing();
			}
			break;
	};



	// ======================================================================================================
	// COLOUR/FLICKER TAB
	// ======================================================================================================
	pxTabs->SelectTab(1);
	xOffset.y = fTabTop;

	bool bEnabled = true;

	if (xParams.m_eType==ASURA_DYNAMICLIGHT_PROJECTOR && xParams.m_iBlendMode!=0)
	{
		// It's a projector, set to a non-light blending mode
		bEnabled = false;
	}

	if (bEnabled)
	{
		// Base colour+intensity
		const Asura_Vector_2 xColourSize(2.5f, 1.0f);
		pxDialog = new Tools_ColourPicker_Dialog("Base Colour",xOffset, xColourSize, &g_xParam_DynamicLight_BaseColourR, &g_xParam_DynamicLight_BaseColourG, &g_xParam_DynamicLight_BaseColourB, 1.0f, &s_bModified);
		pxTabs->AddDialog(pxDialog);

		// Another quirk of the tools UI... no labels on colour pickers, so we have to add our own 
		xOffset.x+=4.0f;
		pxDialog = new Asura_Dialog("Base Colour", xOffset);
		pxTabs->AddDialog(pxDialog);
		xOffset.x+=18.0f;

		pxDialog = new Asura_Numerical_Dialog("Base Intensity", xOffset, &g_xParam_DynamicLight_BaseIntensity , 0.0f, 1000.0f, 0.01f, "%0.2f");
		pxTabs->AddDialog(pxDialog);
		xOffset.x-=22.0f;					
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Graph Duration", xOffset, &g_xParam_DynamicLight_GraphDuration , 0.0f, 999.0f, 1.0f, "%0.1f");
		pxDialog->SetHelpText("In seconds");
		pxTabs->AddDialog(pxDialog);
		xOffset.x+=22.0f;					

		pxDialog = new Asura_Numerical_Dialog("Graph Timer Offset", xOffset, &g_xParam_DynamicLight_GraphTimerOffset , 0.0f, 999.0f, 1.0f, "%0.1f");
		pxDialog->SetHelpText("Used to de-sync lights that use the same graphs, so they don't all flicker in sync");
		pxTabs->AddDialog(pxDialog);
		xOffset.x+=22.0f;					

	   	pxDialog = new Asura_SharedTickbox_Dialog("Looping", xOffset,&g_xParam_DynamicLight_Looping);
		pxDialog->SetHelpText("Looping will normally be used, but can be turned off for scripted 'one-shot' effects when turning a light on/off - use the Trigger/Untrigger messages to play the graph forwards/backwards");
		pxTabs->AddDialog(pxDialog);
		xOffset.x-=44.0f;							
		xOffset.y+=Asura_Dialog::LargeSpacing();

		// Add the inner tabs
		static const char* aszInnerTabNames[] = { "Colour/Range", "Flicker", "Strobe"};
		Asura_Tabs_Dialog *pxInnerTabs = NULL;
		pxDialog = pxInnerTabs = new Asura_Tabs_Dialog(xOffset, aszInnerTabNames, 3, 65);
		pxTabs->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::DefaultSpacing()*1.2f;

		float fInnerTabTop = xOffset.y;
		
		// ----------------------------------------------------------
		// Colour/Range sub-tab
		// ----------------------------------------------------------
		pxInnerTabs->SelectTab(0);
		xOffset.y = fInnerTabTop;

		pxDialog = new Asura_Graph_Dialog<Asura_RGBA>("Colour/Range", xOffset, &xParams.m_xColourRangeGraph, 0.0f, 2.0f, &s_bModified);
		pxInnerTabs->AddDialog(pxDialog);

		xOffset.x+=35.0f;
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_SharedTickbox_Dialog("Use graph colour (RGB)", xOffset, &g_xParam_DynamicLight_UseGraphColour);
		pxInnerTabs->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();
		pxDialog = new Asura_SharedTickbox_Dialog("Use graph range (A)", xOffset, &g_xParam_DynamicLight_UseGraphRange);
		pxInnerTabs->AddDialog(pxDialog);
		xOffset.x-=35.0f;					
		
		
		// ----------------------------------------------------------
		// Flicker sub-tab
		// ----------------------------------------------------------
		pxInnerTabs->SelectTab(1);
		xOffset.y = fInnerTabTop;		

		pxDialog = new Asura_Graph_Dialog<float>("Flicker", xOffset, &xParams.m_xFlickerGraph, 0.0f, 1.0f, &s_bModified);
		pxInnerTabs->AddDialog(pxDialog);

		xOffset.x+=35.0f;					
		xOffset.y+=Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_SharedTickbox_Dialog("Use graph flicker", xOffset, &g_xParam_DynamicLight_UseGraphFlicker);
		pxInnerTabs->AddDialog(pxDialog);		
		xOffset.y+=Asura_Dialog::LargeSpacing();
		xOffset.x-=35.0f;					

		// ----------------------------------------------------------
		// Strobe sub-tab
		// ----------------------------------------------------------
		pxInnerTabs->SelectTab(2);
		xOffset.y = fInnerTabTop;
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_SharedTickbox_Dialog("Use strobe", xOffset, &g_xParam_DynamicLight_UseStrobe);
		pxInnerTabs->AddDialog(pxDialog);		
		xOffset.y+=Asura_Dialog::LargeSpacing();

		pxDialog = new Asura_Numerical_Dialog("Min Time Off",xOffset,&g_xParam_DynamicLight_StrobeMinTimeOff, 0,100.0f,0.01f,"%0.2f");
		pxInnerTabs->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Max Time Off",xOffset,&g_xParam_DynamicLight_StrobeMaxTimeOff, 0,100.0f,0.01f,"%0.2f");
		pxInnerTabs->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Min Time On",xOffset,&g_xParam_DynamicLight_StrobeMinTimeOn, 0,100.0f,0.01f,"%0.2f");
		pxInnerTabs->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Max Time On",xOffset,&g_xParam_DynamicLight_StrobeMaxTimeOn, 0,100.0f,0.01f,"%0.2f");
		pxInnerTabs->AddDialog(pxDialog);
		
		

		// Strobe settings, as used by old advanced lights
		static u_int s_uSelectedInnerTab = 0;
		pxInnerTabs->SetSelectedTabPointer(&s_uSelectedInnerTab);
	}
	else
	{
		pxDialog = new Asura_Dialog("(These controls aren't available for non-light projectors)", xOffset);
		pxTabs->AddDialog(pxDialog);
	}



	// ======================================================================================================
	// FALLOFF TAB
	// ======================================================================================================
	pxTabs->SelectTab(2);
	xOffset.y = fTabTop;

	// Add the graph
	pxDialog = new Asura_Dialog_DynamicLight_FalloffGraph(xOffset,560,120);	
	xOffset.y+=7.5f*Asura_Dialog::DefaultSpacing();
	pxTabs->AddDialog(pxDialog);

	// Values that control the graph
	pxDialog = new Asura_Numerical_Dialog("Inner radius ratio", xOffset, &g_xParam_DynamicLight_FalloffInnerRatio , 0.0f, 0.99f, 0.01f, "%0.2f");
	pxDialog->SetHelpText("Controls the 'inner radius' of the light - the area where it is at max strength");
	pxTabs->AddDialog( pxDialog );
	xOffset.x+=25;

	pxDialog = new Asura_Numerical_Dialog("Power", xOffset, &g_xParam_DynamicLight_FalloffPower , 0.2f, 5.0f, 0.01f, "%0.2f");
	pxDialog->SetHelpText("Controls the shape of the falloff curve");
	pxTabs->AddDialog( pxDialog );
	xOffset.x-=25;
	xOffset.y += Asura_Dialog::LargeSpacing();	


	// Add the distance fade here, too
	pxDialog = new Asura_Numerical_Dialog("Distance Fade: Start Dist", xOffset,&g_xParam_DynamicLight_FadeoutStart , 0.0f, 2500.0f, 1.0f, "%0.1f");
	pxDialog->SetHelpText("For fading out (and disabling) the entire light/projector based on distance from the camera");
	pxTabs->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Distance Fade: Range", xOffset, &g_xParam_DynamicLight_FadeoutDist , 0.0f, 2500.0f, 1.0f, "%0.1f");
	pxDialog->SetHelpText("For fading out (and disabling) the entire light/projector based on distance from the camera");
	pxTabs->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();	


	// ======================================================================================================
	// SHADOWS TAB
	// ======================================================================================================
	pxTabs->SelectTab(3);
	xOffset.y = fTabTop;

	static const char * const g_aszShadowModes[] = {"No Shadows", "Contribute To Averaged Shadow", "Cast Own Shadows"};	
	pxDialog = new Asura_DropDown_List_Dialog(xOffset, g_aszShadowModes, &g_xParam_DynamicLight_ShadowMode, 3, 3, RefreshOverlay, NULL, "Object Shadow Mode");

	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	if (xParams.m_iShadowMode == 1)
	{
		pxDialog = new Asura_SharedTickbox_Dialog("Scale strength contribution by light brightness", xOffset, &g_xParam_DynamicLight_ScaleShadowStrengthByBrightness);
		pxTabs->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::LargeSpacing();
		
	}

	pxDialog = new Asura_SharedTickbox_Dialog("Shadows as darkening pass", xOffset, &g_xParam_DynamicLight_ShadowDarkeningPass);
	pxDialog->SetIsEnabledFunction(CanCastDarkeningShadow);
	pxTabs->AddDialog(pxDialog);		
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_SharedTickbox_Dialog("Use noise to filter shadows", xOffset, &g_xParam_DynamicLight_ShadowUseNoise);
	pxDialog->SetIsEnabledFunction(CanCastShadow);
	pxTabs->AddDialog(pxDialog);		
	xOffset.y += Asura_Dialog::LargeSpacing();


	pxDialog = new Asura_Numerical_Dialog( "Shadow Strength", xOffset, &g_xParam_DynamicLight_ShadowStrength, 0.0f, 4.0f, 0.1f,"%.03f");
	pxDialog->SetHelpText("For darkening pass shadows, including the averaged shadows: 0 = feint, 1 = strong");
	pxDialog->SetIsEnabledFunction(CanCastShadow);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog( "Softness Scale", xOffset, &g_xParam_DynamicLight_ShadowSoftness, -10.0f, 10.0f, 0.1f,"%.01f");		
	pxDialog->SetIsEnabledFunction(CanCastShadow);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog( "Vertical offset for casting shadows", xOffset, &g_xParam_DynamicLight_ShadowVerticalOffset, -10.0f, 10.0f, 0.1f,"%.01f");
	pxDialog->SetHelpText("Adjust the shadow-casting position of the light, may help improve appearance in some glitchy cases");
	pxDialog->SetIsEnabledFunction(CanCastShadow);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_SharedTickbox_Dialog("Cast Environment Shadows", xOffset,&g_xParam_DynamicLight_CastEnvironmentShadows);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// ======================================================================================================
	// MOVEMENT TAB
	// ======================================================================================================
	pxTabs->SelectTab(4);
	xOffset.y = fTabTop;

	// Movement Params
	pxDialog = new Asura_SharedTickbox_Dialog("Enable (basic code-driven movement)", xOffset, &g_xParam_DynamicLight_EnableMovement);
	pxTabs->AddDialog( pxDialog );		

	xOffset.y += Asura_Dialog::LargeSpacing();

	for(int i=0;i<3;i++)
	{
		char *aszLabels[3] = {"X","Y","Z"};
		Asura_Dialog_SharedValue<float> *apxRotSpeedParams[3] = {&g_xParam_DynamicLight_RotSpeedX, &g_xParam_DynamicLight_RotSpeedY, &g_xParam_DynamicLight_RotSpeedZ};
		Asura_Dialog_SharedValue<bool> *apxSwingParams[3] = {&g_xParam_DynamicLight_SwingX, &g_xParam_DynamicLight_SwingY, &g_xParam_DynamicLight_SwingZ};
		Asura_Dialog_SharedValue<float> *apxSwingDistParams[3] = {&g_xParam_DynamicLight_SwingDistX, &g_xParam_DynamicLight_SwingDistY, &g_xParam_DynamicLight_SwingDistZ};
		
		float x = xOffset.x;

		pxDialog = new Asura_Dialog(aszLabels[i], xOffset);
		pxTabs->AddDialog( pxDialog );
		xOffset.x+=1.0f;

		pxDialog = new Asura_Numerical_Dialog("Speed", xOffset, apxRotSpeedParams[i] , -720.0f, 720.0f, 0.01f, "%0.02f");
		pxTabs->AddDialog( pxDialog );
		xOffset.x+=20.0f;

		pxDialog = new Asura_SharedTickbox_Dialog("Swing", xOffset, apxSwingParams[i]);
		pxTabs->AddDialog( pxDialog );
		xOffset.x+=9.0f;

		pxDialog = new Asura_Numerical_Dialog("Range", xOffset, apxSwingDistParams[i] , -360.0f, 360.0f, 0.1f, "%0.02f");
		pxTabs->AddDialog( pxDialog );
		xOffset.x = x;
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}

	xOffset.y += Asura_Dialog::DefaultSpacing();
	pxDialog = new Asura_Numerical_Dialog("Rotation Timer Offset (seconds)", xOffset, &g_xParam_DynamicLight_RotationTimerOffset , -360.0f, 360.0f, 0.1f, "%0.02f");
	pxTabs->AddDialog( pxDialog );



	// ======================================================================================================
	// SPECIAL FX TAB
	// ======================================================================================================
	pxTabs->SelectTab(5);
	xOffset.y = fTabTop;

	pxDialog = new Asura_SharedTickbox_Dialog("Has Corona", xOffset, &g_xParam_DynamicLight_HasCorona);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_SharedTickbox_Dialog("Don't fade corona with distance", xOffset, &g_xParam_DynamicLight_DontFadeCoronaWithDistance);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_SharedTickbox_Dialog("Corona uses Modlinking", xOffset, &g_xParam_DynamicLight_CoronaUsesModLinking);
	pxTabs->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Numerical_Dialog("Corona Radius",xOffset,&g_xParam_DynamicLight_CoronaRadius,0.0f,99.0f,0.1f,"%0.2f");
	pxDialog->SetHelpText("Radius of corona in metres.");
	pxTabs->AddDialog(pxDialog);	

	xOffset.y += Asura_Dialog::DefaultSpacing();
	pxDialog = new Asura_Numerical_Dialog("Corona Volumetric Depth Scale",xOffset,&g_xParam_DynamicLight_CoronaVolumetricDepthScale,0.0f,99.0f,0.1f,"%0.2f");
	pxDialog->SetHelpText("Controls softness of corona when it intersects other geometry");
	pxTabs->AddDialog(pxDialog);

	xOffset.y = fTabTop;
	xOffset.x += 32;
	pxDialog = new Asura_TexturePicker_Dialog( "Corona Texture", xOffset, NULL, &g_xParam_DynamicLight_CoronaTextureID, &g_xParam_DynamicLight_CoronaTextureName, fTEXTURE_PICKER_SIZE, fTEXTURE_PICKER_SIZE, &s_bModified );
	pxTabs->AddDialog( pxDialog );	
	xOffset.x -= 32;
	xOffset.y += Asura_Dialog::LargeSpacing() * 3.5f;
	

	// ======================================================================================================
	// Controls below tabbed area
	// ======================================================================================================
	pxTabs->SetSelectedTabPointer(&s_uTab);

	// Get correct y-pos for bottom of tabbed area
	xOffset.y = pxTabs->GetMaxCoords().y;	

	// Add the attach/detach buttons
	xOffset.y += Asura_Dialog::DefaultSpacing() * 0.5f;
	pxDialog = new Asura_Function_Dialog("Attach To Object/Lightshaft", xOffset, CreateAttachDlg, &s_bModified );
	pxDialog->SetHelpText("Select object that this light should be attached to (must be a PhysicalObjecct)");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Detach From Object/Lightshaft", xOffset, UnattachLight, &s_bModified );
	pxDialog->SetHelpText("Detach this light from whatever object it was attached to.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	xOffset.y-= Asura_Dialog::DefaultSpacing() * 2.0f;
	xOffset.x+=40.0f;
	pxDialog = new Asura_Function_Dialog("Export Parameters", xOffset, ExportParams, &s_bModified );
	pxDialog->SetHelpText("Save light parameters to a .light file");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Function_Dialog("Import Parameters", xOffset, ImportParams, &s_bModified );
	pxDialog->SetHelpText("Load light parameters from a .light file");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();
}


bool Tools_Entity_DynamicLight::CanBeCopied( void ) const
{
    return (Type == TOT_Entity_DynamicLight);
}


void Tools_Entity_DynamicLight::Copy(const Tools_Object* pxObject)
{
	Tools_Entity_Object::Copy(pxObject);

	const Tools_Entity_DynamicLight* pxFromObject = (const Tools_Entity_DynamicLight*) pxObject;

	//copy between the tools objects
	m_xParams = pxFromObject->m_xParams;

	// and update the server entity
	Asura_ServerEntity_DynamicLight* pxEntity = static_cast<Asura_ServerEntity_DynamicLight*>( Asura_Entity_Server::FindEntity(GetGuid()) );
	
	if( pxEntity )
	{ 
		pxEntity->SetParams( m_xParams );
	}
	
	Orientation = m_xParams.m_xOrientation;	
}


void Tools_Entity_DynamicLight::Create( void )
{
    Tools_Entity_DynamicLight* pxToolsObj = CreateMain();

	if ( pxToolsObj )
	{    
    	Tools_Object_System::SelectObject( pxToolsObj );
	}
}


Tools_Entity_DynamicLight* Tools_Entity_DynamicLight::CreateMain()
{
    const Asura_Guid uGUID = Asura_Entity_System::GetCreatedByToolsGuid();

    Tools_Entity_Data*					pxToolsData = NULL;
    Asura_ServerEntity_DynamicLight*	pxEntity    = NULL;
    Tools_Entity_DynamicLight*			pxToolsObj  = NULL;

    pxToolsData = CreateToolsDataWithDefaultName( "DynamicLight", "DynamicLight", uGUID );
    if( !pxToolsData ) { goto CreateFailed; }

	pxEntity = Asura_ServerEntity_DynamicLight::CreateFromTools( uGUID,pxToolsData->Position);
    pxEntity->ToolsData = pxToolsData;
    if( !pxEntity ) { goto CreateFailed; }

	//create the client entity as well
	Asura_ClientEntity_DynamicLight::CreateFromTools(pxEntity->GetGuid(), pxEntity->GetParams());


    pxToolsObj = new Tools_Entity_DynamicLight( pxEntity );
    if( !pxToolsObj ) { goto CreateFailed; }

    Tools_SaveAsura::SetEntityModified();

    return pxToolsObj;

CreateFailed:

    if( !pxEntity ) { delete pxToolsObj; }
    delete pxEntity;
    delete pxToolsData;
	return 0;
}


void Tools_Entity_DynamicLight::Create( Asura_Entity* pxEntity )
{
    Asura_Assert( AsuraEntityClass_DynamicLight == pxEntity->GetClassification(), "" );
    new Tools_Entity_DynamicLight( static_cast<Asura_ServerEntity_DynamicLight*>(pxEntity) );
}


void Tools_Entity_DynamicLight::OnDeselection( void )
{	
	PARENT::OnDeselection();

	if(s_bResetCurrentEntityPtrOnDeselect)
	{
		s_pxSelected = NULL;
	}			
}


void Tools_Entity_DynamicLight::OnSelection( void )
{
	s_pxSelected = this;

	PARENT::OnSelection();

	if(m_pxServerEntity)
	{				
		// Fill in the params
	}	
}


void Tools_Entity_DynamicLight::Render( void )
{


    PARENT::Render();

	if( IsSelected() )
	{
		if (s_pxSelected)
		{
			// If attached to something, draw a line to the parent entity
			Tools_Object* pxMaster = Tools_Object_System::GetObjectFromGuid( s_pxSelected->m_xParams.m_uMasterEntity);
			if(pxMaster) 
			{
				Asura_Render::RenderLine( Position, pxMaster->Position, 0xff00ff00 );
			}

			Tools_Object* pxShaft = Tools_Object_System::GetObjectFromGuid( s_pxSelected->m_xParams.m_uAttachedLightShaft);
			if(pxShaft) 
			{
				Asura_Render::RenderLine( Position, pxShaft->Position, 0xffffff00 );
			}
		}
	}	

	if (m_pxServerEntity)
	{

		if (s_iShowLightVolumesMode==2 || (IsSelected() && s_iShowLightVolumesMode==1))
		{			
			Asura_ClientEntity_DynamicLight *pxClient = (Asura_ClientEntity_DynamicLight *)Asura_ClientEntity_DynamicLight::FindEntity(m_pxServerEntity->GetGuid());

			if (!pxClient)
			{
				// There's no client entity yet - create one	
				pxClient = Asura_ClientEntity_DynamicLight::CreateFromTools(m_pxServerEntity->GetGuid(), m_pxServerEntity->GetParams());
			}
			if(pxClient)
			{
				pxClient->RenderLightVolume();
			}
		}
	}
}


const char* Tools_Entity_DynamicLight::GetTypeDescription( void )
{
	return "Dynamic Light";
}




void Tools_Entity_DynamicLight::Update()
{
	PARENT::Update();

	// Update limiting plane
	if(m_pxLimitingPlane)
	{
		if(m_pxLimitingPlane->m_bGameEntitiesNeedUpdating)
		{			
			// Vector has moved,  so update collision plane
			// Server (calculate from object)
			m_xParams.m_xLimitingPlaneDirection.x = m_pxLimitingPlane->Orientation.e13;
			m_xParams.m_xLimitingPlaneDirection.y = m_pxLimitingPlane->Orientation.e23;
			m_xParams.m_xLimitingPlaneDirection.z = m_pxLimitingPlane->Orientation.e33;
			m_xParams.m_xLimitingPlaneDirection.Normalise();
			m_xParams.m_xLimitingPlanePosition = m_pxLimitingPlane->Position - this->Position;

			// Reset flag
			m_pxLimitingPlane->m_bGameEntitiesNeedUpdating = false;						
			s_bModified = true;

			m_pxServerEntity->SetParams( m_xParams );  		   
		}

		//Remove object if neccessary...
		if(m_pxLimitingPlane->m_bReadyToDelete)
		{
			delete m_pxLimitingPlane;
			m_pxLimitingPlane = NULL;
			
			if (s_pxSelected)
			{			
				Tools_Object_System::DeselectAllObjects();
				Tools_Object_System::SelectObject(s_pxSelected);
			}

			s_bResetCurrentEntityPtrOnDeselect = true;
		}
	}

	// Has the tools object moved?
	// Had to add this extra case to make it work with templates, as the UpdateWhileSelected isn't called.
	Asura_ServerEntity_DynamicLight* pxEntity = static_cast<Asura_ServerEntity_DynamicLight*>( Asura_Entity_Server::FindEntity(GetGuid()) );
	if (pxEntity)
	{	
		if( m_xParams.m_xPosition    != Position  ||
			m_xParams.m_xOrientation != Orientation)
		{
			m_xParams.m_xPosition    = Position;
			m_xParams.m_xOrientation = Orientation;        
			pxEntity->SetParams( m_xParams );  					
		}
	}
}


void Tools_Entity_DynamicLight::UpdateWhileSelected( void )
{
    PARENT::UpdateWhileSelected();

	if (s_bModified)
	{
		SetEntityModified();
		s_bModified = false;
	}


    // Find corresponding entity.
	Asura_ServerEntity_DynamicLight* pxEntity = static_cast<Asura_ServerEntity_DynamicLight*>( Asura_Entity_Server::FindEntity(GetGuid()) );
	
    if( !pxEntity ) { return; }

    // Has the tools object moved?
    if( m_xParams.m_xPosition	 != Tools_Object_System::SelectedObject->Position  ||
		m_xParams.m_xOrientation != Tools_Object_System::SelectedObject->Orientation)
    {
        m_xParams.m_xPosition	 = Tools_Object_System::SelectedObject->Position;
		m_xParams.m_xOrientation = Tools_Object_System::SelectedObject->Orientation;        
		s_bModified = true;
    }


	// Convert the shadow mode to a valid set of Asura_ShadowParameters
	uint32 uMask = 0xffff - (ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS + ASURA_SHADOWFLAG_CONTRIBUTES_TO_AVERAGE_SHADOW);
	m_xParams.m_xShadowParams.m_uFlags = m_xParams.m_xShadowParams.m_uFlags & uMask;

	if (m_xParams.m_iShadowMode == 1)
	{
		m_xParams.m_xShadowParams.m_uFlags |= ASURA_SHADOWFLAG_CONTRIBUTES_TO_AVERAGE_SHADOW;
	}

	if (m_xParams.m_iShadowMode == 2)
	{
		m_xParams.m_xShadowParams.m_uFlags |= ASURA_SHADOWFLAG_CASTS_OWN_SHADOWS;
	}


	// Enforce constraints on any params that need it
	if (m_xParams.m_fInnerFoV>m_xParams.m_fFoV)
	{
		m_xParams.m_fInnerFoV = m_xParams.m_fFoV;
	}

	if (m_xParams.m_fLimitingAngleInner > m_xParams.m_fLimitingAngleOuter)
	{
		m_xParams.m_fLimitingAngleOuter = m_xParams.m_fLimitingAngleInner;
	}



    // Update entity with new parameters
    pxEntity->SetParams( m_xParams );  		   
}




void Tools_Entity_DynamicLight::SetEntityModified()
{
	PARENT::SetEntityModified();

	Asura_ServerEntity_DynamicLight* pxEntity = static_cast<Asura_ServerEntity_DynamicLight*>( Asura_Entity_Server::FindEntity(GetGuid()) );
	
	if (pxEntity)
	{
		pxEntity->SetParams( m_xParams );
	}
}

void Tools_Entity_DynamicLight::EditCustomLightVolume()
{
	Asura_Assert(ThisEntity,"");
	
	// Create controller for editing volumes if it hasn't been done yet
	if(!s_pxSelected->m_pxVolumeController)
	{
		s_pxSelected->m_pxVolumeController = new Tools_DynamicLight_VolumeEdit_Controller(s_pxSelected);
	}

	// Convert OBB from object-relative to absolute world-space
	Asura_Vector_3 xOfs = s_pxSelected->m_xParams.m_xLightVolume.CalculateCentre();
	s_pxSelected->m_xParams.m_xLightVolume.Displace(-xOfs);
	s_pxSelected->m_xParams.m_xLightVolume.Displace( s_pxSelected->Position + (xOfs * s_pxSelected->Orientation) );	
	Asura_Matrix_3x3 xMtx;
	s_pxSelected->m_xParams.m_xLightVolumeOrient.ToMatrix(xMtx);
	xMtx = xMtx  * s_pxSelected->Orientation;
	s_pxSelected->m_xParams.m_xLightVolumeOrient.FromMatrix(xMtx);


	// And get it to create the volume objects
	s_pxSelected->m_pxVolumeController->StartVolumeEditing();
}


void Tools_DynamicLight_VolumeEdit_Controller::EndVolumeEditing()
{
	Tools_VolumeEdit_Controller::EndVolumeEditing();
	//select the owner
	Tools_Object_System::SelectObject(m_pxToolsEntity);

	
	// Convert the volume back to object-relative
	Asura_Vector_3 xPos = m_pxToolsEntity->m_xParams.m_xLightVolume.CalculateCentre();	
	m_pxToolsEntity->m_xParams.m_xLightVolume.Displace(-xPos);
	Asura_Vector_3 xRelPos = xPos - m_pxToolsEntity->Position;

	Asura_Matrix_3x3 xOrient = m_pxToolsEntity->Orientation;
	xOrient.Transpose();
	xRelPos = xRelPos * xOrient;
	m_pxToolsEntity->m_xParams.m_xLightVolume.Displace(xRelPos);

	Asura_Matrix_3x3 xMtx;
	m_pxToolsEntity->m_xParams.m_xLightVolumeOrient.ToMatrix(xMtx);
	xMtx = xMtx  * xOrient;
	m_pxToolsEntity->m_xParams.m_xLightVolumeOrient.FromMatrix(xMtx);
}



void Tools_Entity_DynamicLight::ResetCustomLightVolume()
{
	Asura_Assert(ThisEntity,"");

	// And get it to create the volume objects
	s_pxSelected->m_xParams.m_xLightVolume.Reset();
	s_pxSelected->m_xParams.m_xLightVolume.Set(Asura_Vector_3(0,0,0)); //(s_pxSelected->Position);
	s_pxSelected->m_xParams.m_xLightVolume.Grow(s_pxSelected->m_xParams.m_fPhotonRange);
	s_pxSelected->m_xParams.m_xLightVolumeOrient.Identity();
}




void Tools_Entity_DynamicLight::EditLimitingPlane(void)
{
	Asura_Assert(s_pxSelected, "No current effect to edit collision plane for.");

	if (s_pxSelected)
	{	
		s_bResetCurrentEntityPtrOnDeselect = false;

		Tools_Entity_DynamicLight *pxLight = s_pxSelected;

		if(!pxLight->m_pxLimitingPlane)
		{
			pxLight->m_pxLimitingPlane = new Tools_DynamicLight_LimitingPlane_Object();
		}

		Asura_Assert(pxLight->m_pxLimitingPlane, "Failed to create plane editing object");

		if(pxLight->m_pxLimitingPlane)
		{
			//Set orientation and position based on current collision plane
			//s_pxCurrentObject->m_pxCollisionPlane->Orientation.Identity();
			Asura_Maths::OrientationFromDirection(pxLight->m_pxLimitingPlane->Orientation, pxLight->m_pxServerEntity->m_xParams.m_xLimitingPlaneDirection);
			
			// We store a relative position, but edit it as an absolute position
			// (Orientation is always absolute, to avoid problems with swinging lights...)
			pxLight->m_pxLimitingPlane->Position = pxLight->Position + pxLight->m_pxServerEntity->m_xParams.m_xLimitingPlanePosition;

			//Select new object.			
			Tools_Object_System::SelectObject(pxLight->m_pxLimitingPlane);
		}
	}
}







Tools_Object* Tools_Entity_DynamicLight::CreateObjectOfThisType() const
{
	return CreateMain();	
}


//---------------------------------------------------
// Asura_Dialog_FalloffGraph			 
// For displaying the falloff as a simple graph	     
//---------------------------------------------------

Asura_Dialog_FalloffGraph::Asura_Dialog_FalloffGraph(const Asura_Vector_2& xOffset, float fWidth, float fHeight)
	 : Asura_Dialog( "", xOffset )
	 , m_xSize( fWidth, fHeight )
{
	m_xMaxCoord.x = xOffset.x + (fWidth  / ASURA_TEXT_OUTPUT_FONT_WIDTH);
	m_xMaxCoord.y = xOffset.y + (fHeight / ASURA_TEXT_OUTPUT_FONT_HEIGHT);
}

void Asura_Dialog_FalloffGraph::Render(const Asura_Vector_2& xOrigin)
{
	if( !IsHidden() )
	{
		Asura_Vector_2 xPosition = xOrigin + m_xOffset;
		xPosition.x *= ASURA_TEXT_OUTPUT_FONT_WIDTH;
		xPosition.y *= ASURA_TEXT_OUTPUT_FONT_HEIGHT;
		RenderGrid(xPosition);
		float fInnerRatio = 0.0f;
		float fPower = 1.0f;
		if (GetParameters(fInnerRatio, fPower))
		{
			RenderFunction(xPosition, fInnerRatio, fPower);
		}
	}
}

// Draw the grid lines
void Asura_Dialog_FalloffGraph::RenderGrid(const Asura_Vector_2& xPosition) const
{
	const float fX = xPosition.x;
	const float fY = xPosition.y;

	for (int i=0; i<=10; i++)
	{	
		const float fN = static_cast<float>(i)/10.0f;
		
		u_int uColour = 0x20ffffff;		
		if (i==0 || i==10) uColour = 0x80ffffff;
		if (i==5) uColour = 0x40ffffff;

		{
			const Asura_Vector_2 x1(fX, fY + (m_xSize.y*fN));
			Asura_Vector_2 x2 = x1;
			x2.x += m_xSize.x;
			Asura_Render::Render2DLine(x1, x2, uColour);
		}

		{
			const Asura_Vector_2 x1(fX + (m_xSize.x*fN), fY);
			Asura_Vector_2 x2 = x1;
			x2.y += m_xSize.y;
			Asura_Render::Render2DLine(x1,x2,uColour);
		}
	}
}

// Draw the curve
void Asura_Dialog_FalloffGraph::RenderFunction(const Asura_Vector_2& xPosition, float fInnerRatio, float fPower) const
{
	const float fX = xPosition.x;
	const float fY = xPosition.y;
	const int iNumPoints = 200;

	for (int i=0; i<iNumPoints; i++)
	{
		const float fA = static_cast<float>(i)/iNumPoints;
		const float fB = static_cast<float>(i+1)/iNumPoints;
		
		const float fScale = 1.0f/(1.0f - fInnerRatio);

		const float fAA = Asura_Maths::ClampToRange((1.0f-fA)*fScale, 0.0f, 1.0f);
		const float fBB = Asura_Maths::ClampToRange((1.0f-fB)*fScale, 0.0f, 1.0f);
		const float fValA = pow(fAA, fPower);
		const float fValB = pow(fBB, fPower);

		const Asura_Vector_2 x1(fX + m_xSize.x*fA, fY + m_xSize.y*(1.0f-fValA));
		const Asura_Vector_2 x2(fX + m_xSize.x*fB, fY + m_xSize.y*(1.0f-fValB));	

		Asura_Render::Render2DLine(x1,x2, 0xffffffff);
	}
}



// ---------------------------------------------------------------------------------------------------------------------
// Attachement Handling. As Asura still has no standard way of attaching/parenting entities, all the code that follows
// is basically a copy+paste from the AdvancedLight entity :(
// ---------------------------------------------------------------------------------------------------------------------

void Tools_Entity_DynamicLight::CreateAttachDlg(void)
{
	Asura_Overlay* pxNewOverlay = new Asura_Overlay( "Dynamic Light Attachment", false );
	Asura_Dialog* pxNewDialog;
	Asura_Vector_2 xOffset(1.0f,3.0f);

	s_bResetCurrentEntityPtrOnDeselect = false;

	if(!s_pxSelected) return;
	Tools_Entity_DynamicLight* pxObject = s_pxSelected;

	pxNewDialog = new Asura_Function_Dialog("Attach to Object", xOffset, SetMasterObject );
	pxNewDialog->SetHelpText("Attaches this projector to the selected object.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Function_Dialog("Attach Lightshaft", xOffset, SetLightShaft );
	pxNewDialog->SetHelpText("Attaches the selected lightshaft to this light.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Function_Dialog("Set Offset", xOffset, SetAttachOffset );
	pxNewDialog->SetHelpText("Projector should maintain the current offset from the object that it is attached to.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Function_Dialog("Set Marker", xOffset, SetAttachMarker );
	pxNewDialog->SetHelpText("Attaches the projector to a marker point on an animating object.");
	pxNewDialog->m_iControlID = DialogID_SelectMarker;
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();

	pxNewDialog = new Asura_FlagTickbox_Dialog( "Use Master Orient", xOffset, &(pxObject->m_xParams.m_uFlags), ASURA_DYNAMICLIGHTFLAG_USEMASTERORIENT, &s_bModified );
	pxNewDialog->SetHelpText("Projector should use the orientation of the object/marker point that it is attached to.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewOverlay->m_uID = TOOLS_OVERLAY_DYNAMICLIGHTATTACHMAIN;
	Asura_Overlay_System::SubmitNewOverlay(pxNewOverlay);	

	UpdateAttachDlg();
}

void Tools_Entity_DynamicLight::UpdateAttachDlg()
{
	Asura_Overlay* pxOverlay = Asura_Overlay_System::GetOverlay(TOOLS_OVERLAY_DYNAMICLIGHTATTACHMAIN);
	if(!pxOverlay) return;

	//get marker name
	Asura_String xName;
	GetAttachMarkerName(xName);

	char szText[200];
	sprintf(szText,"Marker : %s",xName.GetString());
	//update the marker point control
	pxOverlay->UpdateDialogLabel(DialogID_SelectMarker,szText);
}

void Tools_Entity_DynamicLight::CreateSelectMasterDlg(void)
{
	Asura_Overlay* pxNewOverlay = new Asura_Overlay( "Select Master", false );
	Asura_Dialog* pxNewDialog;
	Asura_Vector_2 xOffset(1.0f,3.0f);

	pxNewDialog = new Asura_Function_Dialog("Attach", xOffset, SetMasterObject );
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewOverlay->m_uID = TOOLS_OVERLAY_DYNAMICLIGHTATTACH;
	Asura_Overlay_System::SubmitNewOverlay(pxNewOverlay,false,false);	
}


void Tools_Entity_DynamicLight::SetMasterObject(void)
{
	if(!s_pxSelected) 
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - lost light pointer!" );	
	}
	else
	{
		if( Tools_Object_System::SelectedObject->GetGuid() != s_pxSelected->GetGuid())		 
		{
			s_pxSelected->m_xParams.m_uMasterEntity = Tools_Object_System::SelectedObject->GetGuid();
		}
		else
		{
			Tools_Timed_Message_System::AddMessage( 0xff0000, "Can't attach light to itself! Select a different object!" );
		}
	}	

	s_bResetCurrentEntityPtrOnDeselect = true;
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_DYNAMICLIGHTATTACH);

	Asura_Overlay* pxOverlay = Asura_Overlay_System::GetCurrentOverlay(); // GetOverlay(TOOLS_OVERLAY_DYNAMICLIGHTATTACH);
	if(pxOverlay)
	{
		s_bModified = true;
		pxOverlay->m_bShouldClose = true;
	}	

	Tools_Object_System::SelectObject(s_pxSelected);
}


void Tools_Entity_DynamicLight::SetLightShaft(void)
{
	if(!s_pxSelected) 
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - lost light pointer!" );	
	}
	else
	{
		if( Tools_Object_System::SelectedObject->GetGuid() != s_pxSelected->GetGuid())		 
		{
			s_pxSelected->m_xParams.m_uAttachedLightShaft = Tools_Object_System::SelectedObject->GetGuid();
		}
		else
		{
			Tools_Timed_Message_System::AddMessage( 0xff0000, "Can't attach light to itself! Select a different object!" );
		}
	}	

	s_bResetCurrentEntityPtrOnDeselect = true;

	Asura_Overlay* pxOverlay = Asura_Overlay_System::GetCurrentOverlay(); //::GetOverlay(TOOLS_OVERLAY_DYNAMICLIGHTATTACH);
	if(pxOverlay)
	{
		s_bModified = true;
		pxOverlay->m_bShouldClose = true;
	}	

	Tools_Object_System::SelectObject(s_pxSelected);
}

void Tools_Entity_DynamicLight::SetAttachOffset(void)
{
	if( NULL == s_pxSelected)
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - No Selected projector" );
		return;
	}
	
	Tools_Object* pxMaster = Tools_Object_System::GetObjectFromGuid( s_pxSelected->m_xParams.m_uMasterEntity);
	if(!pxMaster) 
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - Couldn't find master object" );
		return;
	}

	s_bModified = true;

	s_pxSelected->m_xParams.m_xOffsetFromMaster = s_pxSelected->Position - pxMaster->Position;

	// Todo - orientation?
}


void Tools_Entity_DynamicLight::SetAttachMarker()
{
	if( NULL == s_pxSelected)
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - No Selected projector" );
		return;
	}

	Asura_ServerEntity_PhysicalObject* pxMaster = Asura_ServerEntity_PhysicalObject::FindEntity(s_pxSelected->m_xParams.m_uMasterEntity);
	if(!pxMaster) 
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - Couldn't find master object" );
		return;
	}

	//does the object use a skin?
	Asura_Hash_ID uSkin = pxMaster->GetSkinID();
	if(uSkin == ASURA_HASH_ID_UNSET)
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - Master object doesn't have a skin/hierarchy" );
		return;
	}

	//get the skin
	Asura_Hierarchy_Skin* pxSkin = Asura_Animation_System::GetSkinByHash(uSkin);
	if(!pxSkin)
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"Failed to find skin for master object. Unable to give list of marker points.");
		return;
	}

	if(!pxSkin->NumberOfMarkerPoints)
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"The master object's skin doesn't have any marker points");
		return;
	}

	//add all the marker points to a selection overlay
	ListSelection_Overlay xOverlay("Select marker point to attach projector to");
	for(int i=0;i<pxSkin->NumberOfMarkerPoints;i++)
	{
		xOverlay.AddItem(pxSkin->MarkerPoint[i].GetName().GetString(),Asura_GetHashID(pxSkin->MarkerPoint[i].GetName().GetString()));
	}
	//add option for clearing marker point
	xOverlay.AddItem("*None*",ASURA_HASH_ID_UNSET);
	if(!xOverlay.DoModal()) return;

	s_bModified = true;
	s_pxSelected->m_xParams.m_uMasterMarker = (Asura_Hash_ID)xOverlay.GetSelectionData();

	UpdateAttachDlg();

}

void Tools_Entity_DynamicLight::GetAttachMarkerName(Asura_String& xName)
{
	if( NULL == s_pxSelected)
	{
		Tools_Timed_Message_System::AddMessage( 0xff0000, "ERROR - No Selected projector" );
		return;
	}

	Asura_Hash_ID uMarkerHash = s_pxSelected->m_xParams.m_uMasterMarker;
	if(!uMarkerHash)
	{
		xName = "*None*";
		return;
	}
	//default to "unknown"
	xName = "*Unknown*";

	Asura_ServerEntity_PhysicalObject* pxMaster = Asura_ServerEntity_PhysicalObject::FindEntity(s_pxSelected->m_xParams.m_uMasterEntity);
	if(!pxMaster) 
	{
		return;
	}

	//does the object use a skin?
	Asura_Hash_ID uSkin = pxMaster->GetSkinID();
	if(uSkin == ASURA_HASH_ID_UNSET)
	{
		return;
	}

	//get the skin
	Asura_Hierarchy_Skin* pxSkin = Asura_Animation_System::GetSkinByHash(uSkin);
	if(!pxSkin)
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"Failed to find skin for master object. Unable to give list of marker points.");
		return;
	}

	//check the marker points for one with the correct hash
	for(int i=0;i<pxSkin->NumberOfMarkerPoints;i++)
	{
		if(uMarkerHash == Asura_GetHashID(pxSkin->MarkerPoint[i].GetName().GetString()))
		{
			xName = pxSkin->MarkerPoint[i].GetName().GetString();
			return;
		}
	}
}


void Tools_Entity_DynamicLight::UnattachLight(void)
{
	s_pxSelected->m_xParams.m_uMasterEntity       = ASURA_GUID_UNREGISTERED;
	s_pxSelected->m_xParams.m_xOffsetFromMaster   = Asura_Maths::ZeroVector3;
	s_pxSelected->m_xParams.m_uFlags		     |= ASURA_DYNAMICLIGHTFLAG_USEMASTERORIENT;
	s_pxSelected->m_xParams.m_uMasterMarker       = ASURA_HASH_ID_UNSET;
	s_pxSelected->m_xParams.m_uAttachedLightShaft = ASURA_GUID_UNREGISTERED;
	s_bModified = true;
}



bool Tools_Entity_DynamicLight::HandlesThisMessage (int iMessageID)
{
	switch(iMessageID)
	{		
		case ASURA_MSG_ACTIVATE:
		case ASURA_MSG_DEACTIVATE:
		case ASURA_MSG_TRIGGER:
		case ASURA_MSG_UNTRIGGER:
		case ASURA_MSG_DYNAMICLIGHT_FADE:
		{
			return(true);
		}

	default:
		{
			return(PARENT::HandlesThisMessage(iMessageID));
		}
	}		// End of switch.
}


void Tools_Entity_DynamicLight::AddMessageOverlayControls_Fade(Asura_StaticMessage* pxMessage, Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset)
{
	Asura_Dialog*	pxDialog;			
	Asura_Message_DynamicLight_Fade* pxData = (Asura_Message_DynamicLight_Fade*)pxMessage->m_pData;
	
	const Asura_Vector_2 xColourSize(2.5f, 1.0f);
	pxDialog = new Tools_ColourPicker_Dialog("Target Base Colour",xOffset, xColourSize, &pxData->m_fTargetR, &pxData->m_fTargetG, &pxData->m_fTargetB, 1.0f, NULL);
	pxOverlay->AddDialog(pxDialog);

	// Another quirk of the tools UI... no labels on colour pickers, so we have to add our own 
	xOffset.x+=4.0f;
	pxDialog = new Asura_Dialog("Target Base Colour", xOffset);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x+=18.0f;

	pxDialog = new Asura_FlagTickbox_Dialog("Use Colour", xOffset, &pxData->m_uFlags, ASURA_DYNAMICLIGHT_FADE_FLAG_USECOLOUR);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x-=22.0f;
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Target Intensity", xOffset, &pxData->m_fTargetIntensity,0.0f,1000.0f,1.0f,"%.002f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x+=22.0f;
	pxDialog = new Asura_FlagTickbox_Dialog("Use Intensity", xOffset, &pxData->m_uFlags, ASURA_DYNAMICLIGHT_FADE_FLAG_USEINTENSITY);
	xOffset.x-=22.0f;	
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Target Range", xOffset, &pxData->m_fTargetRange,0.0f,500.0f,1.0f,"%.02f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x+=22.0f;
	pxDialog = new Asura_FlagTickbox_Dialog("Use Range", xOffset, &pxData->m_uFlags, ASURA_DYNAMICLIGHT_FADE_FLAG_USERANGE);
	xOffset.x-=22.0f;	
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Numerical_Dialog("Fade Duration (Secs)", xOffset, &pxData->m_fDuration,0.0f,100.0f,1.0f,"%.02f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_FlagTickbox_Dialog("Activate at start", xOffset, &pxData->m_uFlags, ASURA_DYNAMICLIGHT_FADE_FLAG_ACTIVATE);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
	pxDialog = new Asura_FlagTickbox_Dialog("Deactivate at end", xOffset, &pxData->m_uFlags, ASURA_DYNAMICLIGHT_FADE_FLAG_DEACTIVATE);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
}



// Export/Import
void Tools_Entity_DynamicLight::ImportParams(void)
{
	// File open dialog
	Tools_FileSystem_DialogInfo xInfo;

	xInfo.DefaultExtension		= "";
	xInfo.DefaultFilename		= ""; 
	xInfo.DefaultPath			= "Lights\\";
	xInfo.FileFilter			= "Dynamic Light Templates\0*.light\0";
	xInfo.WindowTitle			= "Load Light Template...";
	xInfo.AllowMultipleSelect	= false;
	if (!xInfo.DefaultFilename) xInfo.DefaultFilename = "";

	// Get the name, and strip the extension
	Asura_String name = Tools_FileSystem::GetLoadFilename(xInfo);

	if(name.IsNull())
	{
		// Cancelled
		return;
	}

	// Load the template into temporary storage
	Asura_ChunkLoading_System::LoadAsuraFile(name.GetString());

	// Display dialog - what to import
	Asura_DynamicLight_ImportOverlay *pxOverlay = new Asura_DynamicLight_ImportOverlay();

	if (pxOverlay->DoModal())
	{
		if (s_pxSelected)
		{	
			// Import it - copy the bits we want
			s_pxSelected->m_xParams.CopyToThis(Asura_Chunk_DynamicLightTemplate::s_xParams,
												pxOverlay->m_bLightType, 
												false, 
												pxOverlay->m_bFalloff, 
												pxOverlay->m_bMovement, 
												pxOverlay->m_bColourAndFlicker, 
												pxOverlay->m_bShadows, 
												pxOverlay->m_bSpecialFX);

			s_pxSelected->SetEntityActive((s_pxSelected->m_xParams.m_uFlags & ASURA_DYNAMICLIGHTFLAG_ACTIVEATSTART) != 0);

			s_pxSelected->SetEntityModified();

			// Reload the textures
			s_pxSelected->ReloadTextures();
		}
	}

	delete pxOverlay;

};


void Tools_Entity_DynamicLight::ExportParams(void) 
{
	// File save dialog
	Tools_FileSystem_DialogInfo xInfo;

	xInfo.DefaultExtension		= "";
	xInfo.DefaultFilename		= ""; 
	xInfo.DefaultPath			= "Lights\\";
	xInfo.FileFilter			= "Dynamic Light Templates\0*.light\0";
	xInfo.WindowTitle			= "Save Light Template...";
	xInfo.AllowMultipleSelect	= false;
	if (!xInfo.DefaultFilename) xInfo.DefaultFilename = "";

	char *szChosenFilename = Tools_FileSystem::GetSaveFilename( xInfo );

	if( !szChosenFilename || !s_pxSelected ) return;

	if (s_pxSelected->Entity->GetEntityActiveIgnoringTemplate())
	{
		s_pxSelected->m_xParams.m_uFlags |= ASURA_DYNAMICLIGHTFLAG_ACTIVEATSTART;
	}
	else
	{
		s_pxSelected->m_xParams.m_uFlags &= ~ASURA_DYNAMICLIGHTFLAG_ACTIVEATSTART;
	}

	Asura_Chunk_DynamicLightTemplate::Create(s_pxSelected->m_xParams, szChosenFilename);
};




/*---------------------------------------------------*
* Asura_DynamicLight_ImportOverlay					 *
* For selecting which parts of a template to import  *
*---------------------------------------------------*/

Asura_DynamicLight_ImportOverlay::Asura_DynamicLight_ImportOverlay() :Tools_Modal_Overlay("Import from light template")
{
	m_bLightType		= true;
	m_bFalloff			= true;
	m_bMovement			= true;
	m_bColourAndFlicker = true;
	m_bShadows			= true;
	m_bSpecialFX		= true;
}

bool Asura_DynamicLight_ImportOverlay::DoModal()
{
	m_bReturnValue = false;
	ShowOverlay();
	return m_bReturnValue;
}

void Asura_DynamicLight_ImportOverlay::OkaySelected(int iData)
{
	Asura_Assert(iData,"");
	*(bool*)iData = true;
}

void Asura_DynamicLight_ImportOverlay::CancelSelected(int iData)
{
	Asura_Assert(iData,"");
	*(bool*)iData = false;
}

void Asura_DynamicLight_ImportOverlay::InitialiseOverlay(class Asura_Overlay* pxOverlay)
{
	//setup the controls on the overlay
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);
	Asura_Dialog* pxDialog;

	pxDialog = new Asura_Tickbox_Dialog("Light Type", xOffset, &m_bLightType);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Colour+Flicker", xOffset, &m_bColourAndFlicker);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Falloff", xOffset, &m_bFalloff);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Shadows", xOffset, &m_bShadows);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Movement", xOffset, &m_bMovement);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Special FX", xOffset, &m_bSpecialFX);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing()*1.5f;

	pxDialog = new Asura_Function_Dialog("Okay",xOffset,OkaySelected,(int)&m_bReturnValue,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x+=7.0f;

	pxDialog = new Asura_Function_Dialog("Cancel",xOffset,CancelSelected,(int)&m_bReturnValue,&pxOverlay->m_bShouldClose);
	pxOverlay->AddDialog(pxDialog);
}



//------------------------------------------------------------------------------------------------------
// The plane editing object - more-or-less a copy of the PFX collision plane
// editing code from Tools_PFX_Vector_Object.cpp
//------------------------------------------------------------------------------------------------------


Tools_DynamicLight_LimitingPlane_Object::Tools_DynamicLight_LimitingPlane_Object()
{	
	Asura_String xMeshName("ToolsShape_CutsceneTrackPoint");
	Mesh = Asura_Mesh_System::GetMesh(xMeshName);
	Asura_Overlay_System::SetHideOverlays(true);
	Tools_Object_System::SelectObject(this);
	Tools_Movement::SetFlyingObject(this);

	m_xTypeDescription = "Dynamic Light Limiting Plane";
	m_xName = "Limits the range of a projector or spotlight";
	m_bGameEntitiesNeedUpdating = false;
	m_bReadyToDelete = false;
}

Tools_DynamicLight_LimitingPlane_Object::~Tools_DynamicLight_LimitingPlane_Object()
{
	Asura_Overlay_System::SetHideOverlays(false);;
	Tools_Movement::SetFlyingObject(NULL);
}

void Tools_DynamicLight_LimitingPlane_Object::UpdateWhileSelected()
{
	if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_SPACE))
	{
		Tools_Object_System::SelectObject(NULL);
	}
}

void Tools_DynamicLight_LimitingPlane_Object::UpdatePositionInAsuraData(bool bUpdateLinkedObjects)
{
	m_bGameEntitiesNeedUpdating = true;
}


void Tools_DynamicLight_LimitingPlane_Object::OnSelection()
{
	m_bReadyToDelete = false;
}

void Tools_DynamicLight_LimitingPlane_Object::OnDeselection()
{
	//On deselection this object should delete itself
	m_bReadyToDelete = true;
}

void Tools_DynamicLight_LimitingPlane_Object::Render()
{
	Tools_Object::Render();

	static float fDX = .5f;
	static float fDY = .5f;
	static float fWX = 5.f;
	static float fWY = 5.f;
	const Asura_Vector_3 xVectorX = Asura_Vector_3(Orientation.e11, Orientation.e21, Orientation.e31);
	const Asura_Vector_3 xVectorY = Asura_Vector_3(Orientation.e12, Orientation.e22, Orientation.e32);
	for(float fX = -fWX; fX <= fWX; fX += fDX)
	{
		const Asura_Vector_3 xPoint1 = Position + xVectorX * fX + xVectorY * (-fWY);
		const Asura_Vector_3 xPoint2 = Position + xVectorX * fX + xVectorY * ( fWY);
		Asura_Render::RenderLine(xPoint1, xPoint2, 0xff00ff00);
	}
	for(float fY = -fWY; fY <= fWY; fY += fDY)
	{
		const Asura_Vector_3 xPoint1 = Position + xVectorX * (-fWX) + xVectorY * (fY);
		const Asura_Vector_3 xPoint2 = Position + xVectorX * ( fWX) + xVectorY * (fY);
		Asura_Render::RenderLine(xPoint1, xPoint2, 0xff00ff00);
	}
}


bool Tools_Entity_DynamicLight::GetParameter(Asura_Hash_ID uParamID,class Tools_Parameter* pxParam) const
{
	switch(uParamID)
	{
		case g_uHash_DynamicLight_Type :
			pxParam->SetInt(m_xParams.m_eType);
			break;


		//photon light
		case g_uHash_DynamicLight_ProjCubeTextureID :
			pxParam->SetHash(m_xParams.m_xProjCubeTextureID);
			break;

		case g_uHash_DynamicLight_ProjCubeTextureName :
			pxParam->SetString(m_xParams.m_xProjCubeTextureName);
			break;

		case g_uHash_DynamicLight_ProjCubeBlurredTextureID :
			pxParam->SetHash(m_xParams.m_xProjCubeBlurredTextureID);
			break;

		case g_uHash_DynamicLight_ProjCubeBlurredTextureName :
			pxParam->SetString(m_xParams.m_xProjCubeBlurredTextureName);
			break;

		case g_uHash_DynamicLight_Range :
			pxParam->SetFloat(m_xParams.m_fPhotonRange);
			break;

		case g_uHash_DynamicLight_SharpBlurredBlendDist :
			pxParam->SetFloat(m_xParams.m_fProjCubeLerpDist);
			break;

		case g_uHash_DynamicLight_UseLimitingAngles :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USEPHOTONLIMITINGANGLES));
			break;

		case g_uHash_DynamicLight_LimitingAngleInner :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_fLimitingAngleInner));
			break;

		case g_uHash_DynamicLight_LimitingAngleOuter :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_fLimitingAngleOuter));
			break;

		case g_uHash_DynamicLight_UseCustomLightVolume :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USECUSTOMLIGHTVOLUME));
			break;

		//projector

		case g_uHash_DynamicLight_BlendMode :
			pxParam->SetInt(m_xParams.m_iBlendMode);
			break;

		case g_uHash_DynamicLight_TextureID :
			pxParam->SetHash(m_xParams.m_uTextureHash);
			break;

		case g_uHash_DynamicLight_TextureName :
			pxParam->SetString(m_xParams.m_xTextureName);
			break;

		case g_uHash_DynamicLight_DetailTextureID :
			pxParam->SetHash(m_xParams.m_uDetailTextureHash);
			break;

		case g_uHash_DynamicLight_DetailTextureName :
			pxParam->SetString(m_xParams.m_xDetailTextureName);
			break;


		case g_uHash_DynamicLight_UScrollSpeed :
			pxParam->SetFloat(m_xParams.m_fUScrollSpeed);
			break;

		case g_uHash_DynamicLight_UScrollRange :
			pxParam->SetFloat(m_xParams.m_fUScrollRange);
			break;

		case g_uHash_DynamicLight_VScrollSpeed :
			pxParam->SetFloat(m_xParams.m_fVScrollSpeed);
			break;

		case g_uHash_DynamicLight_VScrollRange :
			pxParam->SetFloat(m_xParams.m_fVScrollRange);
			break;

		case g_uHash_DynamicLight_DetailTextureScale :
			pxParam->SetFloat(m_xParams.m_fDetailTextureScale);
			break;

		case g_uHash_DynamicLight_FoV :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_fFoV));
			break;

		case g_uHash_DynamicLight_InnerFoV :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_fInnerFoV));
			break;


		case g_uHash_DynamicLight_UVAspectRatio :
			pxParam->SetFloat(m_xParams.m_fAspect);
			break;

		case g_uHash_DynamicLight_NearPlaneDist :
			pxParam->SetFloat(m_xParams.m_fMinRange);
			break;

		case g_uHash_DynamicLight_FarPlaneDist :
			pxParam->SetFloat(m_xParams.m_fMaxRange);
			break;

		case g_uHash_DynamicLight_ParallelProjScale :
			pxParam->SetFloat(m_xParams.m_fScale);
			break;
		case g_uHash_DynamicLight_CastEnvironmentShadows:
			pxParam->SetBool(GetShadowFlag(ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS));
			break;

		case g_uHash_DynamicLight_ParallelProjection :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_PARALLELPROJECTION));
			break;

		case g_uHash_DynamicLight_UseLimitingPlane :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USELIMITINGPLANE));
			break;

		//colour flicker
		case g_uHash_DynamicLight_BaseColourR :
			pxParam->SetFloat(m_xParams.m_fBaseR);
			break;

		case g_uHash_DynamicLight_BaseColourG :
			pxParam->SetFloat(m_xParams.m_fBaseG);
			break;

		case g_uHash_DynamicLight_BaseColourB :
			pxParam->SetFloat(m_xParams.m_fBaseB);
			break;

		case g_uHash_DynamicLight_BaseIntensity :
			pxParam->SetFloat(m_xParams.m_fBaseIntensity);
			break;

		case g_uHash_DynamicLight_GraphDuration :
			pxParam->SetFloat(m_xParams.m_fGraphDuration);
			break;

		case g_uHash_DynamicLight_GraphTimerOffset :
			pxParam->SetFloat(m_xParams.m_fGraphTimerOffset);
			break;

		case g_uHash_DynamicLight_Looping :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_GRAPHISLOOPED));
			break;

		//colour/range sub-tab
		case g_uHash_DynamicLight_UseGraphColour :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USEGRAPHCOLOUR));
			break;

		case g_uHash_DynamicLight_UseGraphRange :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USEGRAPHRANGE));
			break;

		//flicker sub-tab
		case g_uHash_DynamicLight_UseGraphFlicker :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USEGRAPHFLICKER));
			break;

		//strobe sub-tab
		case g_uHash_DynamicLight_UseStrobe :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_USESTROBE));
			break;

		case g_uHash_DynamicLight_StrobeMinTimeOff :
			pxParam->SetFloat(m_xParams.m_fStrobeMinTimeOff);
			break;

		case g_uHash_DynamicLight_StrobeMinTimeOn :
			pxParam->SetFloat(m_xParams.m_fStrobeMinTimeOn);
			break;

		case g_uHash_DynamicLight_StrobeMaxTimeOff :
			pxParam->SetFloat(m_xParams.m_fStrobeMaxTimeOff);
			break;

		case g_uHash_DynamicLight_StrobeMaxTimeOn :
			pxParam->SetFloat(m_xParams.m_fStrobeMaxTimeOn);
			break;

		//falloff tab
		case g_uHash_DynamicLight_FalloffInnerRatio :
			pxParam->SetFloat(m_xParams.m_fFalloffInnerRatio);
			break;

		case g_uHash_DynamicLight_FalloffPower :
			pxParam->SetFloat(m_xParams.m_fFalloffPower);
			break;

		case g_uHash_DynamicLight_FadeoutStart :
			pxParam->SetFloat(m_xParams.m_fFadeoutStart);
			break;

		case g_uHash_DynamicLight_FadeoutDist :
			pxParam->SetFloat(m_xParams.m_fFadeoutDist);
			break;

		//shadows tab
		case g_uHash_DynamicLight_ShadowMode :
			pxParam->SetInt(m_xParams.m_iShadowMode);
			break;

		case g_uHash_DynamicLight_ScaleShadowStrengthByBrightness :
			pxParam->SetBool(GetShadowFlag(ASURA_SHADOWFLAG_SCALE_STRENGTH_BY_BRIGHTNESS));
			break;

		case g_uHash_DynamicLight_ShadowDarkeningPass :
			pxParam->SetBool(GetShadowFlag(ASURA_SHADOWFLAG_IS_DARKENING_PASS));
			break;
		case g_uHash_DynamicLight_ShadowUseNoise:
			pxParam->SetBool(GetShadowFlag(ASURA_SHADOWFLAG_USE_NOISE));
			break;

		case g_uHash_DynamicLight_ShadowStrength :
			pxParam->SetFloat(m_xParams.m_xShadowParams.m_fShadowStrength);
			break;

		case g_uHash_DynamicLight_ShadowSoftness :
			pxParam->SetFloat(m_xParams.m_xShadowParams.m_fShadowSoftness);
			break;

		case g_uHash_DynamicLight_ShadowVerticalOffset :
			pxParam->SetFloat(m_xParams.m_xShadowParams.m_fVerticalOffset);
			break;

		//move tab
		case g_uHash_DynamicLight_EnableMovement :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_MOVING));
			break;

		case g_uHash_DynamicLight_RotSpeedX :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_xRotSpeed.x));
			break;

		case g_uHash_DynamicLight_RotSpeedY :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_xRotSpeed.y));
			break;

		case g_uHash_DynamicLight_RotSpeedZ :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_xRotSpeed.z));
			break;

		case g_uHash_DynamicLight_SwingX :
			pxParam->SetBool(m_xParams.m_abSwing[0]);
			break;

		case g_uHash_DynamicLight_SwingY :
			pxParam->SetBool(m_xParams.m_abSwing[1]);
			break;

		case g_uHash_DynamicLight_SwingZ :
			pxParam->SetBool(m_xParams.m_abSwing[2]);
			break;

		case g_uHash_DynamicLight_SwingDistX :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_xSwingDist.x));
			break;

		case g_uHash_DynamicLight_SwingDistY :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_xSwingDist.y));
			break;

		case g_uHash_DynamicLight_SwingDistZ :
			pxParam->SetFloat(Asura_Maths::RadToDeg(m_xParams.m_xSwingDist.z));
			break;

		case g_uHash_DynamicLight_RotationTimerOffset :
			pxParam->SetFloat(m_xParams.m_fRotationOffset);
			break;

		//special fx tab
		case g_uHash_DynamicLight_HasCorona :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_HASCORONA));
			break;

		case g_uHash_DynamicLight_DontFadeCoronaWithDistance :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_DONTFADECORONAWITHDIST));
			break;

		case g_uHash_DynamicLight_CoronaUsesModLinking :
			pxParam->SetBool(GetFlag(ASURA_DYNAMICLIGHTFLAG_CORONAUSESMODLINKING));
			break;

		case g_uHash_DynamicLight_CoronaRadius :
			pxParam->SetFloat(m_xParams.m_fCoronaRadius);
			break;

		case g_uHash_DynamicLight_CoronaVolumetricDepthScale :
			pxParam->SetFloat(m_xParams.m_fCoronaVolumetricDepthScale);
			break;

		case g_uHash_DynamicLight_CoronaTextureID :
			pxParam->SetHash(m_xParams.m_uCoronaTextureHash);
			break;

		case g_uHash_DynamicLight_CoronaTextureName :
			pxParam->SetString(m_xParams.m_xCoronaTextureName);
			break;

		
		default :
			return Tools_Entity_Object::GetParameter(uParamID, pxParam);
	}
	return true;
}

bool Tools_Entity_DynamicLight::SetParameter(Asura_Hash_ID uParamID,const class Tools_Parameter* pxParam)
{
	
	switch(uParamID)
	{
		case g_uHash_DynamicLight_Type :
			m_xParams.m_eType = (ASURA_DYNAMICLIGHT_TYPE)pxParam->GetInt();
			break;

		//photon light
		case g_uHash_DynamicLight_ProjCubeTextureID :
			m_xParams.m_xProjCubeTextureID = pxParam->GetHash();
			break;

		case g_uHash_DynamicLight_ProjCubeTextureName :
			m_xParams.m_xProjCubeTextureName = pxParam->GetString();
			break;

		case g_uHash_DynamicLight_ProjCubeBlurredTextureID :
			m_xParams.m_xProjCubeBlurredTextureID = pxParam->GetHash();
			break;

		case g_uHash_DynamicLight_ProjCubeBlurredTextureName :
			m_xParams.m_xProjCubeBlurredTextureName = pxParam->GetString();
			break;

		case g_uHash_DynamicLight_Range :
			m_xParams.m_fPhotonRange = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_SharpBlurredBlendDist :
			m_xParams.m_fProjCubeLerpDist = pxParam->GetFloat();
			break;
		case g_uHash_DynamicLight_CastEnvironmentShadows:
			SetShadowFlag(ASURA_SHADOWFLAG_CASTS_ENVIRONMENT_SHADOWS, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_UseLimitingAngles :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USEPHOTONLIMITINGANGLES, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_LimitingAngleInner :
			m_xParams.m_fLimitingAngleInner = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_LimitingAngleOuter :
			m_xParams.m_fLimitingAngleOuter = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_UseCustomLightVolume :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USECUSTOMLIGHTVOLUME, pxParam->GetBool());
			break;


		//projector

		case g_uHash_DynamicLight_BlendMode :
			m_xParams.m_iBlendMode = pxParam->GetInt();
			break;


		case g_uHash_DynamicLight_TextureID :
			m_xParams.m_uTextureHash = pxParam->GetHash();
			break;

		case g_uHash_DynamicLight_TextureName :
			m_xParams.m_xTextureName = pxParam->GetString();
			break;

		case g_uHash_DynamicLight_DetailTextureID :
			m_xParams.m_uDetailTextureHash = pxParam->GetHash();
			break;

		case g_uHash_DynamicLight_DetailTextureName :
			m_xParams.m_xDetailTextureName = pxParam->GetString();
			break;

		case g_uHash_DynamicLight_UScrollSpeed :
			m_xParams.m_fUScrollSpeed = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_UScrollRange :
			m_xParams.m_fUScrollRange = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_VScrollSpeed :
			m_xParams.m_fVScrollSpeed = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_VScrollRange :
			m_xParams.m_fVScrollRange = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_DetailTextureScale :
			m_xParams.m_fDetailTextureScale = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_FoV :
			m_xParams.m_fFoV = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_InnerFoV :
			m_xParams.m_fInnerFoV = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_UVAspectRatio :
			m_xParams.m_fAspect = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_NearPlaneDist :
			m_xParams.m_fMinRange = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_FarPlaneDist :
			m_xParams.m_fMaxRange = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_ParallelProjScale :
			m_xParams.m_fScale = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_ParallelProjection :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_PARALLELPROJECTION, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_UseLimitingPlane :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USELIMITINGPLANE, pxParam->GetBool());
			break;

		//colour flicker
		case g_uHash_DynamicLight_BaseColourR :
			m_xParams.m_fBaseR = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_BaseColourG :
			m_xParams.m_fBaseG = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_BaseColourB :
			m_xParams.m_fBaseB = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_BaseIntensity :
			m_xParams.m_fBaseIntensity = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_GraphDuration :
			m_xParams.m_fGraphDuration = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_GraphTimerOffset :
			m_xParams.m_fGraphTimerOffset = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_Looping :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_GRAPHISLOOPED, pxParam->GetBool());
			break;

		//colour/range sub-tab
		case g_uHash_DynamicLight_UseGraphColour :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USEGRAPHCOLOUR, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_UseGraphRange :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USEGRAPHRANGE, pxParam->GetBool());
			break;

		//flicker sub-tab
		case g_uHash_DynamicLight_UseGraphFlicker :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USEGRAPHFLICKER, pxParam->GetBool());
			break;

		//strobe sub-tab
		case g_uHash_DynamicLight_UseStrobe :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_USESTROBE, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_StrobeMinTimeOn :
			m_xParams.m_fStrobeMinTimeOn = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_StrobeMinTimeOff :
			m_xParams.m_fStrobeMinTimeOff = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_StrobeMaxTimeOn :
			m_xParams.m_fStrobeMaxTimeOn = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_StrobeMaxTimeOff :
			m_xParams.m_fStrobeMaxTimeOff = pxParam->GetFloat();
			break;

		//falloff tab
		case g_uHash_DynamicLight_FalloffInnerRatio :
			m_xParams.m_fFalloffInnerRatio = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_FalloffPower :
			m_xParams.m_fFalloffPower = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_FadeoutStart :
			m_xParams.m_fFadeoutStart = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_FadeoutDist :
			m_xParams.m_fFadeoutDist = pxParam->GetFloat();
			break;

		//shadows tab
		case g_uHash_DynamicLight_ShadowMode :
			m_xParams.m_iShadowMode = pxParam->GetInt();
			break;

		case g_uHash_DynamicLight_ScaleShadowStrengthByBrightness :
			SetShadowFlag(ASURA_SHADOWFLAG_SCALE_STRENGTH_BY_BRIGHTNESS, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_ShadowDarkeningPass :
			SetShadowFlag(ASURA_SHADOWFLAG_IS_DARKENING_PASS, pxParam->GetBool());
			break;
		case g_uHash_DynamicLight_ShadowUseNoise:
			SetShadowFlag(ASURA_SHADOWFLAG_USE_NOISE, pxParam->GetBool());
			break;
		case g_uHash_DynamicLight_ShadowStrength :
			m_xParams.m_xShadowParams.m_fShadowStrength = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_ShadowSoftness :
			m_xParams.m_xShadowParams.m_fShadowSoftness = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_ShadowVerticalOffset :
			m_xParams.m_xShadowParams.m_fVerticalOffset = pxParam->GetFloat();
			break;

		//move tab
		case g_uHash_DynamicLight_EnableMovement :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_MOVING, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_RotSpeedX :
			m_xParams.m_xRotSpeed.x = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_RotSpeedY :
			m_xParams.m_xRotSpeed.y = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_RotSpeedZ :
			m_xParams.m_xRotSpeed.z = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_SwingX :
			m_xParams.m_abSwing[0] = pxParam->GetBool();
			break;

		case g_uHash_DynamicLight_SwingY :
			m_xParams.m_abSwing[1] = pxParam->GetBool();
			break;

		case g_uHash_DynamicLight_SwingZ :
			m_xParams.m_abSwing[2] = pxParam->GetBool();
			break;

		case g_uHash_DynamicLight_SwingDistX :
			m_xParams.m_xSwingDist.x = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_SwingDistY :
			m_xParams.m_xSwingDist.y = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_SwingDistZ :
			m_xParams.m_xSwingDist.z = Asura_Maths::DegToRad(pxParam->GetFloat());
			break;

		case g_uHash_DynamicLight_RotationTimerOffset :
			m_xParams.m_fRotationOffset = pxParam->GetFloat();
			break;


 		//special fx tab
		case g_uHash_DynamicLight_HasCorona :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_HASCORONA, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_DontFadeCoronaWithDistance :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_DONTFADECORONAWITHDIST, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_CoronaUsesModLinking :
			SetFlag(ASURA_DYNAMICLIGHTFLAG_CORONAUSESMODLINKING, pxParam->GetBool());
			break;

		case g_uHash_DynamicLight_CoronaRadius :
			m_xParams.m_fCoronaRadius = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_CoronaVolumetricDepthScale :
			m_xParams.m_fCoronaVolumetricDepthScale = pxParam->GetFloat();
			break;

		case g_uHash_DynamicLight_CoronaTextureID :
			m_xParams.m_uCoronaTextureHash = pxParam->GetHash();
			break;

		case g_uHash_DynamicLight_CoronaTextureName :
			m_xParams.m_xCoronaTextureName = pxParam->GetString();
			break;



		default :
			return Tools_Entity_Object::SetParameter(uParamID, pxParam);
	}

	SetEntityModified();

	// Enforce constraints on any params that need it
	if (m_xParams.m_fInnerFoV>m_xParams.m_fFoV)
	{
		m_xParams.m_fInnerFoV = m_xParams.m_fFoV;
	}

	if (m_xParams.m_fLimitingAngleInner > m_xParams.m_fLimitingAngleOuter)
	{
		m_xParams.m_fLimitingAngleOuter = m_xParams.m_fLimitingAngleInner;
	}
    

    // Update entity with new parameters
	Asura_ServerEntity_DynamicLight* pxEntity = static_cast<Asura_ServerEntity_DynamicLight*>( Asura_Entity_Server::FindEntity(GetGuid()) );
	if(pxEntity)
	{
    	pxEntity->SetParams( m_xParams );
	}
	return true;
}

bool Tools_Entity_DynamicLight::GetFlag(u_int uFlag) const
{
	return  m_xParams.m_uFlags & uFlag ? true : false;
}

void Tools_Entity_DynamicLight::SetFlag(u_int uFlag, bool bSet)
{
	if(bSet)
	{
		m_xParams.m_uFlags |= uFlag;
	}
	else
	{
		m_xParams.m_uFlags &=~ uFlag;
	}
}

bool Tools_Entity_DynamicLight::GetShadowFlag(u_int uFlag) const
{
	return  m_xParams.m_xShadowParams.m_uFlags & uFlag ? true : false;
}

void Tools_Entity_DynamicLight::SetShadowFlag(u_int uFlag, bool bSet)
{
	if(bSet)
	{
		m_xParams.m_xShadowParams.m_uFlags |= uFlag;
	}
	else
	{
		m_xParams.m_xShadowParams.m_uFlags &=~ uFlag;
	}
}


void Tools_Entity_DynamicLight::UpdateEntities()
{
	for(Asura_Hashed_List_It<Asura_ClientEntity_DynamicLight> xIt(Asura_ClientEntity_DynamicLight::GetList()) ;!xIt.Done(); xIt.Next() )
	{
		Asura_ClientEntity_DynamicLight* pxEntity = xIt.GetCurrent();
		if(pxEntity->IsDestroyed()) continue;

		pxEntity->Update();
	}
}


#endif //#ifdef ASURA_TARGET_SM3
