/*!
	@file RuEntity_DecalShadow.cpp

	Copyright (c) 2004-2006 All rights reserved
*/

#include "../../Scene/FX/RuEntity_DecalShadow.h"

#pragma unmanaged

// ************************************************************************************************************************************************************

ruRTTI_DEFINE_SUBCLASS(CRuEntity_DecalShadow, IRuEntity_Renderable)
ruHEAP_DEFINE_SUBCLASS(CRuEntity_DecalShadow, "CRuEntity_DecalShadow", "IRuEntity_Renderable")
ruCLASSGUID_DEFINE(CRuEntity_DecalShadow, 0xDE74870E, 0xEC4A4731, 0xA0954E0C, 0xE30B225E)

// ************************************************************************************************************************************************************

CRuEntity_DecalShadow::CRuEntity_DecalShadow()
:	m_maximumDecalFaces(2000),
	m_mesh(NULL),
	m_material(NULL)
{
	m_mesh = ruNEW CRuDynamicMesh();
	m_mesh->Initialize(ruPRIMTYPE_TRIANGLELIST, m_maximumDecalFaces * 3, m_maximumDecalFaces, ruVERTEXFORMAT_POSITION | ruVERTEXFORMAT_DIFFUSE, 1);
	m_mesh->AddTextureCoordinate(ruTEXCHANNEL_DIFFUSEMAP);
	m_mesh->SetNumPrimitives(0);
	m_mesh->SetNumVertices(0);

	m_material = ruNEW CRuMaterial();
	m_material->SetShader(g_ruResourceManager->GetShader("ruSHADER_NULL"));

	// Register self as an event listener
	this->RegisterListener(this, NULL);
}

CRuEntity_DecalShadow::~CRuEntity_DecalShadow()
{
	ruSAFE_RELEASE(m_mesh);
	ruSAFE_RELEASE(m_material);
}

IRuObject *CRuEntity_DecalShadow::Clone(IRuObject *clonedObject)
{
	// Create a new cloned entity if necessary
	clonedObject = clonedObject ? clonedObject : ruNEW CRuEntity_DecalShadow();

	return clonedObject;
}

BOOL CRuEntity_DecalShadow::SerializeFrom(IRuStream *stream)
{
	CRuGUID classGUID;

	// Read class GUID
	if(RuReadGUID(stream, classGUID) == FALSE)
	{
		return FALSE;
	}

	// Read version
	INT32 version = 1;
	stream->Read(&version, sizeof(INT32));

	// Chain serialization to base class
	IRuEntity_Renderable::SerializeFrom(stream);

	return TRUE;
}

BOOL CRuEntity_DecalShadow::SerializeTo(IRuStream *stream)
{
	// Write class GUID
	stream->Write(GetClassGUID().Data(), GetClassGUID().Size());

	// Write version
	INT32 version = 1;
	stream->Write(&version, sizeof(INT32));

	// Chain serialization to base class
	IRuEntity_Renderable::SerializeTo(stream);

	return TRUE;
}

BOOL CRuEntity_DecalShadow::Update(REAL elapsedTime)
{
	// Chain update to base class
	IRuEntity_Renderable::Update(elapsedTime);

	if(m_caster && m_receiver)
	{
		// Determine decal enumeration bounds
		CRuVector3 decalCenterOffsetVector = m_shadowDirection * m_shadowZoneSize;
		const CRuSphere &casterBounds = m_caster->GetLocalBounds();
		CRuSphere curDecalEnumBounds(m_caster->GetTranslation(ruFRAMETYPE_WORLD) + decalCenterOffsetVector, casterBounds.Radius() + m_shadowZoneSize);

		// Check if it is necessary to re-enumerate decal triangles
		if(m_decalEnumerationBounds.Contains(curDecalEnumBounds) == FALSE)
		{
			// Set new enumeration bounds to include extra padding
			m_decalEnumerationBounds.Set(curDecalEnumBounds.Center(), curDecalEnumBounds.Radius() + m_enumerationPadding);

			// Build triangle enumeration query
			CRuCollisionQuery_Sphere_TriEnum triEnumQuery;
			triEnumQuery.m_exclusionMask = 0x00A00000;		// Exclude water and foliage surfaces
			triEnumQuery.m_colSphere = m_decalEnumerationBounds;

			// Perform enumeration on receiver
			m_receiver->QueryCollision(&triEnumQuery);

			// Iterate through colliding triangles and add them to the decal mesh
			CRuMorphTarget *morphTarget = static_cast<CRuMorphTarget *>(m_mesh->GetMorphTarget(0));
			CRuVector3 *position = morphTarget->GetPosition();
			DWORD *diffuse = morphTarget->GetDiffuse();
			UINT16 *indices = m_mesh->GetIndices();

			INT32 vIndex = 0;
			INT32 iIndex = 0;

			INT32 maxVertsToProcess = min(m_maximumDecalFaces * 3, triEnumQuery.m_colTriVerts->Count());

			for(vIndex = 0, iIndex = 0; vIndex < maxVertsToProcess; vIndex += 3, iIndex += 3)
			{
				position[vIndex] = triEnumQuery.m_colTriVerts->Get(vIndex);
				position[vIndex + 1] = triEnumQuery.m_colTriVerts->Get(vIndex + 1);
				position[vIndex + 2] = triEnumQuery.m_colTriVerts->Get(vIndex + 2);

				diffuse[vIndex] = 0xFFFFFFFF;
				diffuse[vIndex + 1] = 0xFFFFFFFF;
				diffuse[vIndex + 2] = 0xFFFFFFFF;

				indices[iIndex] = iIndex;
				indices[iIndex + 1] = iIndex + 1;
				indices[iIndex + 2] = iIndex + 2;
			}

			// Set primitive and vertex count for the mesh
			m_mesh->SetNumPrimitives(iIndex / 3);
			m_mesh->SetNumVertices(vIndex);

			// Set local entity bounds to be the enumeration sphere
			this->SetLocalEntityBounds(CRuSphere(CRuVector3(0.0f, 0.0f, 0.0f), m_decalEnumerationBounds.Radius()));

			// Enable world transform override
			this->SetOption(ruENTITYOPTION_WTOVERRIDE, TRUE);
		}
	}

	return TRUE;
}

INT32 CRuEntity_DecalShadow::GetNumMeshes() const
{
	return 1;
}

BOOL CRuEntity_DecalShadow::GetMesh(INT32 meshIndex, IRuMesh **meshOut, IRuMaterial **materialOut)
{
	if(meshIndex == 0)
	{
		*meshOut = m_mesh;
		*materialOut = m_material;

		return TRUE;
	}

	return FALSE;
}

BOOL CRuEntity_DecalShadow::HandleEvent(CRuEntity *entity, INT32 eventID, INT32 eventParameter0, INT32 eventParameter1, const char *eventString, REAL eventDuration, void *userData)
{
	return FALSE;
}

// ************************************************************************************************************************************************************

#pragma managed
