#include "gosFXHeaders.hpp"

//==========================================================================//
// File:	 gosFX_Singleton.cpp										//
// Project:  gosFX					                                        //
// Contents: Base gosFX::Singleton Component									//
//--------------------------------------------------------------------------//
// Date      Who    Modification                                            //
// 09/28/98  JTR    Created													//
//																			//
//--------------------------------------------------------------------------//
// Copyright (C) 1997-1998, Virtual World Entertainment, Inc.               //
// All Rights reserved worldwide                                            //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL              //
//==========================================================================//
//
//############################################################################
//########################  gosFX::Singleton__Specification  #############################
//############################################################################

//------------------------------------------------------------------------------
//
gosFX::Singleton__Specification::Singleton__Specification(
	Stuff::RegisteredClass::ClassID class_id,
	Stuff::MemoryStream *stream,
	int gfx_version
):
	Effect__Specification(class_id, stream, gfx_version)
{
	Check_Pointer(this);
	Check_Object(stream);

	//
	//-------------------
	// Load in the curves
	//-------------------
	//
	m_red.Load(stream, gfx_version);
	m_green.Load(stream, gfx_version);
	m_blue.Load(stream, gfx_version);
	m_alpha.Load(stream, gfx_version);
	if (gfx_version >= 22)
		m_spin.Load(stream, gfx_version);
	else
	{
		m_spin.m_ageCurve.SetCurve(0.0f);
		m_spin.m_seeded = false;
		m_spin.m_seedCurve.SetCurve(1.0f);
	}
	m_scale.Load(stream, gfx_version);
	if (gfx_version >= 22)
		*stream >> m_randomStartingRotation;
	else
		m_randomStartingRotation = false;
	*stream >> m_alignZUsingX >> m_alignZUsingY;
}

//------------------------------------------------------------------------------
//
gosFX::Singleton__Specification::Singleton__Specification(
	Stuff::RegisteredClass::ClassID class_id
):
	Effect__Specification(class_id)
{
	Check_Pointer(this);
	m_randomStartingRotation = false;
	m_alignZUsingX = false;
	m_alignZUsingY = false;
}

//------------------------------------------------------------------------------
//
gosFX::Singleton__Specification::~Singleton__Specification()
{
	Check_Object(this);
}

//------------------------------------------------------------------------------
//
void
	gosFX::Singleton__Specification::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	BaseClass::Save(stream);

	//
	//----------------
	// Save our curves
	//----------------
	//
	m_red.Save(stream);
	m_green.Save(stream);
	m_blue.Save(stream);
	m_alpha.Save(stream);
	m_spin.Save(stream);
	m_scale.Save(stream);
	*stream << m_randomStartingRotation << m_alignZUsingX << m_alignZUsingY;
}

//------------------------------------------------------------------------------
//
void 
	gosFX::Singleton__Specification::BuildDefaults()
{

	Check_Object(this);
	BaseClass::BuildDefaults();

	m_red.m_ageCurve.SetCurve(1.0f);
	m_red.m_seeded = false;
	m_red.m_seedCurve.SetCurve(1.0f);

	m_green.m_ageCurve.SetCurve(1.0f);
	m_green.m_seeded = false;
	m_green.m_seedCurve.SetCurve(1.0f);

	m_blue.m_ageCurve.SetCurve(1.0f);
	m_blue.m_seeded = false;
	m_blue.m_seedCurve.SetCurve(1.0f);

	m_alpha.m_ageCurve.SetCurve(1.0f);
	m_alpha.m_seeded = false;
	m_alpha.m_seedCurve.SetCurve(1.0f);

	m_spin.m_ageCurve.SetCurve(0.0f);
	m_spin.m_seeded = false;
	m_spin.m_seedCurve.SetCurve(1.0f);

	m_scale.m_ageCurve.SetCurve(1.0f);
	m_scale.m_seeded = false;
	m_scale.m_seedCurve.SetCurve(1.0f);

	m_randomStartingRotation = false;
	m_alignZUsingX = false;
	m_alignZUsingY = false;
}

//------------------------------------------------------------------------------
//
bool 
	gosFX::Singleton__Specification::IsDataValid(bool fix_data)
{

	Check_Object(this);
	Stuff::Scalar min,max;
	m_scale.ExpensiveComputeRange(&min,&max);
	if (min<5.0f*Stuff::SMALL) 
	{
		if (fix_data)
			m_scale.SetMinimum(10.0f*Stuff::SMALL);
		else
			return false;
	}
	return BaseClass::IsDataValid(fix_data);
}
//------------------------------------------------------------------------------
//
void
	gosFX::Singleton__Specification::Copy(Singleton__Specification *spec)
{
	Check_Object(this);
	Check_Object(spec);

	BaseClass::Copy(spec);

	//
	//----------------
	// Copy the curves
	//----------------
	//
	gos_PushCurrentHeap(Heap);
	m_red = spec->m_red;
	m_green = spec->m_green;
	m_blue = spec->m_blue;
	m_alpha = spec->m_alpha;
	m_spin = spec->m_spin;
	m_scale = spec->m_scale;
	m_randomStartingRotation = spec->m_randomStartingRotation;
	m_alignZUsingX = spec->m_alignZUsingX;
	m_alignZUsingY = spec->m_alignZUsingY;
	gos_PopCurrentHeap();
}

//############################################################################
//############################  gosFX::Singleton  ###############################
//############################################################################

gosFX::Singleton::ClassData*
	gosFX::Singleton::DefaultData = NULL;

//------------------------------------------------------------------------------
//
void
	gosFX::Singleton::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			SingletonClassID,
			"gosFX::Singleton",
			BaseClass::DefaultData,
			NULL,
			NULL
		);
	Check_Object(DefaultData);
}

//------------------------------------------------------------------------------
//
void
	gosFX::Singleton::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//------------------------------------------------------------------------------
//
gosFX::Singleton::Singleton(
	ClassData *class_data,
	Specification *spec,
	unsigned flags
):
	Effect(class_data, spec, flags)
{
	Check_Pointer(this);
	Check_Object(spec);
}

//------------------------------------------------------------------------------
//
void
	gosFX::Singleton::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//------------------------------------------------------------------------------
//
void
	gosFX::Singleton::Start(ExecuteInfo *info)
{
	Check_Object(this);
	Check_Pointer(info);

	BaseClass::Start(info);

	//
	//---------------------------------
	// Figure out the particle rotation
	//---------------------------------
	//
	Specification *spec = GetSpecification();
	Check_Object(spec);
	m_angularVelocity.x = spec->m_spin.ComputeValue(m_age, m_seed);
	m_angularVelocity.y = spec->m_spin.ComputeValue(m_age, m_seed);
	m_angularVelocity.z = spec->m_spin.ComputeValue(m_age, m_seed);

	//
	//-----------------------------------------------------
	// If we are aligning with velocity, deal with that now
	//-----------------------------------------------------
	//
	if (spec->m_randomStartingRotation)
	{
		Stuff::EulerAngles
			rotation(
				Stuff::Two_Pi*Stuff::Random::GetFraction(),
				Stuff::Pi*Stuff::Random::GetFraction(),
				Stuff::Two_Pi*Stuff::Random::GetFraction()
			);
		m_localRotation = rotation;
	}
	else
		m_localRotation = Stuff::UnitQuaternion::Identity;

	//
	//-------------------------
	// Set up the initial scale
	//-------------------------
	//
	m_scale = spec->m_scale.ComputeValue(m_age, m_seed);
}

//------------------------------------------------------------------------------
//
bool
	gosFX::Singleton::Execute(ExecuteInfo *info)
{
	Check_Object(this);
	Check_Object(info);
	GFX_LOGIC("Execute::Singleton");

	if (!IsExecuted())
		return false;

	//
	//---------------------
	// Animate the rotation
	//---------------------
	//
	Stuff::Scalar time_slice =
		static_cast<Stuff::Scalar>(info->m_time - m_lastRan);
	Stuff::Vector3D omega(m_angularVelocity);
	omega *= time_slice;
	Stuff::UnitQuaternion omega_q;
	omega_q = omega;
	m_localRotation.Multiply(omega_q, Stuff::UnitQuaternion(m_localRotation));
	m_localRotation.Normalize();
	m_localToParent.BuildRotation(m_localRotation);

	//
	//------------------------
	// Call the base class now
	//------------------------
	//
	if (!BaseClass::Execute(info))
		return false;

	//
	//-----------------------------------------
	// Animate the parent then get our pointers
	//-----------------------------------------
	//
	Specification *spec = GetSpecification();
	Check_Object(spec);

	m_scale = spec->m_scale.ComputeValue(m_age, m_seed);

	//
	//------------------------------
	// Calculate the bounding volume
	//------------------------------
	//
	Stuff::OBB bounds;
	bounds.sphereRadius = m_radius*m_scale;
	if (bounds.sphereRadius < Stuff::SMALL)
		bounds.sphereRadius = 0.01f;
	bounds.localToParent = m_localToParent;
	bounds.axisExtents = Stuff::Vector3D::Identity;
	info->m_bounds->Union(*info->m_bounds, bounds);
	return true;
}
