///////////////////////////////////////////////////////////////////////////////
/// @file deSolidObject.cpp
///
/// @brief scenegraph additive object
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Oct 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deScene_priv.hpp"
#include "deMath.hpp"
#include "deCamera.hpp"
#include "dePortal.hpp"
#include "deRender.hpp"
#include "deRenderStates.hpp"
#include "deByteOrder.hpp"

//#include "deBrush.hpp"
#include "deAnimatedMesh.hpp"
//#include "deActor.hpp"
#include "deMesh.hpp"

WorldObjectClassImp(deSceneAnimatedMesh);
WorldObjectClassImp(deSceneActor);
WorldObjectClassImp(deSceneStaticMesh);
WorldObjectClassImp(deSceneLight);

IdeSceneLight* s_TestLight = NULL;
IdeSceneLight* s_TestLight2 = NULL;
IdeSceneLight* s_TestLightBright = NULL;

static long s_SceneObjectWOInterfaceID = -1;
deBoolean IdeSceneObject_Initialize()
{
	IdeWorldManager * manager = IdeWorld_GetManager();
	s_SceneObjectWOInterfaceID = manager->GetUniqueWOInterfaceID();
	
	if (!WorldObjectRegister(deSceneLight))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneLight) failed");
	if (!WorldObjectRegister(deSceneAnimatedMesh))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneAnimatedMesh) failed");
	if (!WorldObjectRegister(deSceneActor))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneActor) failed");
	if (!WorldObjectRegister(deSceneStaticMesh))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneStaticMesh) failed");
	if (!WorldObjectRegister(deSceneSky))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneSky) failed");
	if (!WorldObjectRegister(deSceneTerrain))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneTerrain) failed");
	if (!WorldObjectRegister(deSceneABT))
		DEBUGRETURN(deFALSE, "IdeSceneObject_Initialize: WorldObjectRegister(deSceneABT) failed");

#pragma todo (remove this test code)
#pragma warning (disable : 4127)
	if (1)
	{
		IdeRenderLight::LightData data;
		deVertex dir = {-.3f, -.5f, -.4f};
		deVertex pos = {0, 30, 0};
		if (0)
		{
			memset(&data, 0, sizeof(data));
			data.Status = deTRUE;
			data.ID = 0;
			data.Direction = dir;
			data.Position = pos;
			data.AmbientColor = deMake_Color(0xFF404040);
			data.DiffuseColor = deMake_Color(0xFF008000);
			data.SpecularColor = deMake_Color(0xFF600000);
			data.LightType = IdeRenderLight::LIGHTTYPE_DIRECTIONAL;
			data.MaxRange = 1000;
			data.FallOff = 10;
			s_TestLight = IdeSceneObject_CreateSceneLight();
			s_TestLight->SetLightData(data);
		}
		if (0)
		{
			memset(&data, 0, sizeof(data));
			data.Status = deTRUE;
			data.ID = 1;
			data.Direction = -dir;
			data.Position = -pos;
			data.AmbientColor = deMake_Color(0xFF000000);
			data.DiffuseColor = deMake_Color(0xFF800000);
			data.SpecularColor = deMake_Color(0xFF006000);
			data.LightType = IdeRenderLight::LIGHTTYPE_DIRECTIONAL;
			data.MaxRange = 1000;
			data.FallOff = 10;
			s_TestLight2 = IdeSceneObject_CreateSceneLight();
			s_TestLight2->SetLightData(data);
		}
		if (0)
		{
			memset(&data, 0, sizeof(data));
			data.Status = deTRUE;
			data.ID = 2;
			data.Direction = dir;
			data.Position = pos;
			data.AmbientColor = deMake_Color(0xFF808080);
			data.DiffuseColor = data.AmbientColor;
			data.SpecularColor = data.AmbientColor;
			data.LightType = IdeRenderLight::LIGHTTYPE_DIRECTIONAL;
			data.MaxRange = 1000;
			data.FallOff = 10;
			s_TestLightBright = IdeSceneObject_CreateSceneLight();
			s_TestLightBright->SetLightData(data);
		}
	}
#pragma warning (default : 4127)
	
	return deTRUE;
}

void IdeSceneObject_ShutDown()
{
	if (s_TestLight)
		s_TestLight->Release();
	if (s_TestLight2)
		s_TestLight2->Release();
	if (s_TestLightBright)
		s_TestLightBright->Release();
}

long IdeSceneObject_GetWOInterfaceID()
{
	return s_SceneObjectWOInterfaceID;
}

DESCENE_API IdeSceneAnimatedMesh* IdeSceneObject_CreateSceneAnimatedMesh()
{
	return new deSceneAnimatedMesh;
}

DESCENE_API IdeSceneActor*	IdeSceneObject_CreateSceneActor()
{
	return new deSceneActor;
}

DESCENE_API IdeSceneStaticMesh*	IdeSceneObject_CreateSceneStaticMesh()
{
	return new deSceneStaticMesh;
}

DESCENE_API IdeSceneLight* IdeSceneObject_CreateSceneLight()
{
	return new deSceneLight;
}

//=============================================================
// deSceneObject
//=============================================================

deSceneObject::deSceneObject(interface_t myType)
:	m_BoundCenter(deZeroVec),
	m_OccludeCenter(deZeroVec),
	m_Interface(myType)
{
	m_BoundRadius = 0;
	m_OccludeRadius = 0;
	m_Transformed = deFALSE;
	m_Transparent = deFALSE;
	int k;
	for (k = 0; k < activate_count; ++k)
	{
		m_Active[k] = deTRUE;
	}
#if SHOW_SPHERES
	m_BoundLine = new LineBrush;
#else
	m_BoundLine = NULL;
#endif
}

deSceneObject::~deSceneObject()
{
#if SHOW_SPHERES
	delete m_BoundLine;
#endif
	int i;
	for (i = m_RoomList.Length()-1; i >= 0; --i)
	{
		// we go backwards because this call causes deSceneObject::RemoveRoomReference to be called
		m_RoomList[i].Room->RemoveSceneObject(this);
	}
	IdeSceneGraph_GetSceneGraphPtr()->RemoveSceneObject(this);
}

IdeSceneObject::interface_t deSceneObject::GetSOIType()
{
	return m_Interface;
}

void* deSceneObject::GetSOInterface(interface_t i)
{
	if (i == iface_IdeSceneObject)
	{
		return (IdeSceneObject*)(this);
	}
	return NULL;
}

void* deSceneObject::GetWOInterface(long interface_id)
{
	if (interface_id == s_SceneObjectWOInterfaceID)
		return (IdeSceneObject*)(this);
	if (interface_id == 0)
		return (IdeWorldObject*)(this);
	return NULL;
}

deVec3d deSceneObject::GetBoundCenter()
{
	return m_BoundCenter;
}

deVec3d deSceneObject::GetOccludeCenter()
{
	return m_OccludeCenter;
}

deVec3d deSceneObject::GetWorldBoundCenter()
{
	if (!m_Transformed)
		SetWorldCenters();
	return m_WorldBound;
}

deVec3d deSceneObject::GetWorldOccludeCenter()
{
	if (!m_Transformed)
		SetWorldCenters();
	return m_WorldBound;
}

void deSceneObject::GetWorldTransform(deTransformInfo & mat)
{
	mat = m_WorldTransform;
}

const deTransformInfo & deSceneObject::GetWorldTransform()
{
	return m_WorldTransform;
}

void deSceneObject::GetWorldPos(deVec3d & PosVector)
{
	PosVector = m_WorldTransform.Translation;
}

deDouble deSceneObject::GetBoundRadius()
{
#pragma todo(fix this hack)
	deDouble scale, temp;
	scale = Vec3d_Length(m_WorldTransform.R);
	temp = Vec3d_Length(m_WorldTransform.R);
	scale = max(scale, temp);
	temp = Vec3d_Length(m_WorldTransform.R);
	scale = max(scale, temp);
	return m_BoundRadius * scale;
}

deDouble deSceneObject::GetOccludeRadius()
{
	return m_OccludeRadius;
}

void deSceneObject::GetLocalAABB(deAABB &bbox)
{
	bbox = m_AABB;
}

void deSceneObject::GetWorldOBB(deOBB &bbox)
{
	deTransform tform(m_WorldTransform);
	tform.Translation = deZeroVec;
	deGeometry::MakeOBBFromAABB(m_AABB, tform, bbox);
}

void deSceneObject::GetWorldAABB(deAABB &bbox)
{
	deOBB obb;
	GetWorldOBB(obb);
	deGeometry::MakeAABBFromOBB(obb, bbox);
}

void deSceneObject::SetActivation(activate_t a_type, deBoolean Active)
{
	if (a_type < activate_visible || a_type >= activate_count)
	{
		DE_ASSERT(a_type >= activate_visible && a_type < activate_count);
		return;
	}
	m_Active[a_type] = Active;
}

deBoolean deSceneObject::GetActivation(activate_t a_type) const
{
	if (a_type < activate_visible || a_type >= activate_count)
	{
		DEBUGRETURN(deFALSE, "deSceneObject::GetActivation - invalid value for a_type");
	}
	return m_Active[a_type];
}


long deSceneObject::Traverse(IdeSceneGraph::deSceneTraversal* Params)
{
	int PolysRendered = 0;
	if (m_CurrentPassNum == Params->CurrentPassNum)
		return PolysRendered;
	m_CurrentPassNum = Params->CurrentPassNum;

	if (!m_Active[activate_visible])
		return PolysRendered;

	PolysRendered += TraverseForRender(Params);

	IdeRender::deRenderObject obj;
	long DebugFlags;

	memset(&obj, 0, sizeof(IdeRender::deRenderObject));
	((deTransform&)obj.Transform).Reset();

	DebugFlags = IdeSceneGraph_GetSceneGraphPtr()->GetDebugFlags();
	if (DebugFlags & IdeSceneGraph::deDrawAABB)
	{
		{
			deAABB aabb;
			GetWorldAABB(aabb);
			aabb.Max -= aabb.Min;
			((deTransform&)obj.Transform).Reset();
			obj.Transform.Translation = aabb.Min;
			obj.Transform.Translation += GetWorldTransform().Translation;
			{
				deVec3d X,Y,Z;
				X = deVecX;
				X *= aabb.Max.x;
				Y = deVecY;
				Y *= aabb.Max.y;
				Z = deVecZ;
				Z *= aabb.Max.z;
				((deTransform&)obj.Transform).SetRightUpIn(X,Y,Z);
			}
		}
		memset(&obj.SurfaceDetail, 0, sizeof(obj.SurfaceDetail));
		obj.VB = s_AABB_VB;
		obj.IndexOffset = 0;
		obj.IndexOverride = NULL;
		PolysRendered += obj.VB->GetTriCount();
		obj.Lights[0] = s_TestLightBright;
		Params->render->RenderPrepBuffer(obj);
	}

	if (DebugFlags & IdeSceneGraph::deDrawOBB)
	{
		{
			deOBB obb;
			GetWorldOBB(obb);
			((deTransform&)obj.Transform).Reset();
			obj.Transform.Translation = obb.Corner;
			obj.Transform.Translation += GetWorldTransform().Translation;
			((deTransform&)obj.Transform).SetRightUpIn(obb.Offset[0], obb.Offset[1], obb.Offset[2]);
		}
		memset(&obj.SurfaceDetail, 0, sizeof(obj.SurfaceDetail));
		obj.VB = s_OBB_VB;
		obj.IndexOffset = 0;
		obj.IndexOverride = NULL;
		PolysRendered += obj.VB->GetTriCount();
		obj.Lights[0] = NULL;
		Params->render->RenderPrepBuffer(obj);
	}

	return PolysRendered;
}

deBoolean deSceneObject::OccludesObject(deVec3d &position, IdeSceneObject *so, deVec3d &NormalizedDisplacement)
{
	if (!so)
		return deFALSE;

	deVec3d b,c,d, e;
	deVec3d &a = NormalizedDisplacement;
	deDouble cc;

	// project 'b' onto 'a' to find 'c'
	b = so->GetBoundCenter() - position;
	cc = Vec3d_Dot(a, b);
	c = a * cc;
	// d is difference between b and c
	d = b - c;
	// make e = the radius of occluding sphere perpendicular to 'a'
	Vec3d_Normalize(d);
	e = d * m_OccludeRadius;

	deVec3d normal, displace;
	deDouble dist;

	// get displacement between point on sphere and view position
	displace = (m_OccludeCenter + e) - position;
	// find normal of "tangent" plane
	Vec3d_Normalize(displace);
	cc = Vec3d_Dot(displace, e);
	normal = e - displace * cc;
	Vec3d_Normalize(normal);
	// distance of "tangent" plane from origin
	d = m_OccludeCenter + e;
	dist = Vec3d_Dot(normal, d);

	deDouble radiussq = so->GetBoundRadius();
	radiussq = radiussq * radiussq;
	deDouble pointdistsq = Vec3d_Dot(normal, so->GetBoundCenter()) - dist;
	// if object on front of plane, not occluded
	if (pointdistsq > 0)
		return deFALSE;
	pointdistsq = pointdistsq * pointdistsq;

	// if object not wholly behind plane, not occluded
	if (pointdistsq < radiussq)
		return deFALSE;

	return deTRUE;
}

long deSceneObject::GetCurrentPassNum() const
{
	return m_CurrentPassNum;
}

deBoolean deSceneObject::AddRoomReference(IdeSceneRoom * ref, void * ptr)
{
	if (!ref || !ptr)
		return deFALSE;

	deTArray<RoomEntry>::iterator it = m_RoomList.IncrementSizeQuick();
	if (it == m_RoomList.end())
		return deFALSE;
	it->Room = ref;
	it->ptr = ptr;

	return deTRUE;
}

void * deSceneObject::GetRoomReference(IdeSceneRoom * ref)
{
	int i, len;
	len = m_RoomList.Length();
	for (i = 0; i < len; ++i)
	{
		if (m_RoomList[i].Room == ref)
			return m_RoomList[i].ptr;
	}
	return NULL;
}

deBoolean deSceneObject::RemoveRoomReference(IdeSceneRoom * ref)
{
	int i, len;
	len = m_RoomList.Length();
	for (i = 0; i < len; ++i)
	{
		if (m_RoomList[i].Room == ref)
		{
			if (!m_RoomList.RemoveElementAt(i))
				return deFALSE;
			return deTRUE;
		}
	}
	return deFALSE;
}

void deSceneObject::SetBoundCenter(deVec3d v)
{
	m_BoundCenter = v;
	m_Transformed = deFALSE;
}

void deSceneObject::SetOccludeCenter(deVec3d v)
{
	m_OccludeCenter = v;
	m_Transformed = deFALSE;
}

void deSceneObject::SetWorldCenters()
{
	m_WorldTransform.Transform(m_BoundCenter, m_WorldBound);
	m_WorldTransform.Transform(m_OccludeCenter, m_WorldOcclude);
	m_Transformed = deTRUE;
}

void deSceneObject::SetWorldTransform(const deTransformInfo & mat)
{
	m_WorldTransform = mat;
	m_Transformed = deFALSE;
	IdeSceneGraph* sg = IdeSceneGraph_GetSceneGraphPtr();
	sg->RepositionSceneObject(this);
}

void deSceneObject::SetBoundRadius(deDouble d)
{
	m_BoundRadius = d;
}

void deSceneObject::SetOccludeRadius(deDouble d)
{
	m_OccludeRadius = d;
}

void deSceneObject::SetLocalAABB(deVec3d Min, deVec3d Max)
{
	m_AABB.Min = Min;
	m_AABB.Max = Max;
}

deVec3d deSceneObject::CalcAverageCenter(deVec3d points[], long count)
{
	int i;
	deVec3d Center = deZeroVec;
	for (i = 0; i < count; i++)
	{
		Center = Center + points[i];
	}
	Center = Center * (1.0/count);
	return Center;
}

deDouble deSceneObject::CalcBoundRadius(deVec3d points[], long count)
{
	deDouble maxdistsq;
	int i;

	if (count <= 0)
		return 0;

	maxdistsq = Vec3d_DistSq(points[0], m_BoundCenter);

	for (i = 1; i < count; i++)
	{
		maxdistsq = max(maxdistsq, Vec3d_DistSq(points[i], m_BoundCenter));
	}

	return sqrt(maxdistsq);
}

deDouble deSceneObject::CalcOccludeRadius(deVec3d points[], long count)
{
	deDouble mindistsq;
	int i;

	if (count <= 0)
		return 0;

	mindistsq = Vec3d_DistSq(points[0], m_OccludeCenter);

	for (i = 1; i < count; i++)
	{
		mindistsq = min(mindistsq, Vec3d_DistSq(points[i], m_OccludeCenter));
	}

	return sqrt(mindistsq);
}

void deSceneObject::CalcLocalAABB(deVec3d points[], long count, deVec3d &mins, deVec3d &maxs)
{
	if (count <= 0)
		return;

	mins = maxs = points[0];

	int i;
	for (i = 1; i < count; i++)
	{
		mins.x = min(mins.x, points[i].x);
		maxs.x = min(maxs.x, points[i].x);
		mins.y = min(mins.y, points[i].y);
		maxs.y = min(maxs.y, points[i].y);
		mins.z = min(mins.z, points[i].z);
		maxs.z = min(maxs.z, points[i].z);
	}
}

void deSceneObject::CalcAllValues(deVec3d points[], long count)
{
	if (count <= 0)
		return;

	m_AABB.Min = m_AABB.Max = points[0];

	int i;
	for (i = 1; i < count; i++)
	{
		m_AABB.Min.x = min(m_AABB.Min.x, points[i].x);
		m_AABB.Max.x = max(m_AABB.Max.x, points[i].x);
		m_AABB.Min.y = min(m_AABB.Min.y, points[i].y);
		m_AABB.Max.y = max(m_AABB.Max.y, points[i].y);
		m_AABB.Min.z = min(m_AABB.Min.z, points[i].z);
		m_AABB.Max.z = max(m_AABB.Max.z, points[i].z);
	}

	m_BoundCenter = m_AABB.Min;
	m_BoundCenter += m_AABB.Max;
	m_BoundCenter *= 0.5;
	m_OccludeCenter = m_BoundCenter;
	m_BoundRadius = Vec3d_Dist(m_AABB.Min, m_AABB.Max) * 0.5;
	m_OccludeRadius = 0;

#if SHOW_SPHERES
	m_WorldTransform.Translation = AvgCenter;
	MakeSphere(m_BoundRadius, m_BoundLine, &m_WorldTransform);
#endif
}

deBoolean deSceneObject::IsTransparent()
{
	return m_Transparent;
}

void deSceneObject::SetTransparent(deBoolean Transparent)
{
	m_Transparent = Transparent;
}

IdeLineBrush* deSceneObject::GetBoundLine()
{
	return m_BoundLine;
}

struct deSceneObject_Header_t
{
	short VersionMajor;
	short VersionMinor;
};
static const deSceneObject_Header_t deSceneObject_Header = {0,2};
/*
Version 0.1:
October 15, 2002
struct Vec3d
{
double		x
double		y
double		z
}
struct TransformInfo
{
Vec3d		transform_trans
Vec3d		transform_left
Vec3d		transform_up
Vec3d		transform_in
Vec3d		transform_scale
uchar		transform_ident
}

short			Version_Major
short			Version_Minor
TransformInfo	worldtransform
double			boundradius
double			occluderadius
Vec3d			boundcenter
Vec3d			occludecenter
Vec3d			aabb_min
Vec3d			aabb_max
uchar			hastransparency

Version 0.2:
August 7, 2003
all the same, except following definition
struct TransformInfo
{
Vec3d		transform_trans
Vec3d		transform_left
Vec3d		transform_up
Vec3d		transform_in
}
*/
deBoolean deSceneObject::Serialize(IdeFile* FS)
{
	// header information
	DE3D_STREAM_PUT(FS, deSceneObject_Header.VersionMajor, u16);
	DE3D_STREAM_PUT(FS, deSceneObject_Header.VersionMinor, u16);

	// transformation
	deTransformInfo matrix = m_WorldTransform;
/*	{
		// 0.1
		DE3D_STREAM_PUT(FS, matrix, deTransformInfo);
	}*/
	{
		// 0.2
		DE3D_STREAM_PUT(FS, matrix.Translation.x, deDouble);
		DE3D_STREAM_PUT(FS, matrix.Translation.y, deDouble);
		DE3D_STREAM_PUT(FS, matrix.Translation.z, deDouble);
		DE3D_STREAM_PUT(FS, matrix.R.x, deDouble);
		DE3D_STREAM_PUT(FS, matrix.R.y, deDouble);
		DE3D_STREAM_PUT(FS, matrix.R.z, deDouble);
		DE3D_STREAM_PUT(FS, matrix.U.x, deDouble);
		DE3D_STREAM_PUT(FS, matrix.U.y, deDouble);
		DE3D_STREAM_PUT(FS, matrix.U.z, deDouble);
		DE3D_STREAM_PUT(FS, matrix.I.x, deDouble);
		DE3D_STREAM_PUT(FS, matrix.I.y, deDouble);
		DE3D_STREAM_PUT(FS, matrix.I.z, deDouble);
	}
	
	// radius
	DE3D_STREAM_PUT(FS, m_BoundRadius, deDouble);
	DE3D_STREAM_PUT(FS, m_OccludeRadius, deDouble);

	// center
	DE3D_STREAM_PUT(FS, m_BoundCenter.x, deDouble);
	DE3D_STREAM_PUT(FS, m_BoundCenter.y, deDouble);
	DE3D_STREAM_PUT(FS, m_BoundCenter.z, deDouble);
	DE3D_STREAM_PUT(FS, m_OccludeCenter.x, deDouble);
	DE3D_STREAM_PUT(FS, m_OccludeCenter.y, deDouble);
	DE3D_STREAM_PUT(FS, m_OccludeCenter.z, deDouble);
	
	// AABB
	DE3D_STREAM_PUT(FS, m_AABB.Min.x, deDouble);
	DE3D_STREAM_PUT(FS, m_AABB.Min.y, deDouble);
	DE3D_STREAM_PUT(FS, m_AABB.Min.z, deDouble);
	DE3D_STREAM_PUT(FS, m_AABB.Max.x, deDouble);
	DE3D_STREAM_PUT(FS, m_AABB.Max.y, deDouble);
	DE3D_STREAM_PUT(FS, m_AABB.Max.z, deDouble);

	// transparency
	DE3D_STREAM_PUT(FS, m_Transparent, deBoolean);

	return deTRUE;
}

deBoolean deSceneObject::DeSerialize(IdeFile* FS, long DataLength, u32 &AmtRead)
{
	short VersionMajor, VersionMinor;
	
	// header information
	DE3D_STREAM_GET(FS, VersionMajor, u16, AmtRead, DataLength);
	DE3D_STREAM_GET(FS, VersionMinor, u16, AmtRead, DataLength);
	
	// check the version
	if (VersionMajor > deSceneObject_Header.VersionMajor ||
		(VersionMajor == deSceneObject_Header.VersionMajor && VersionMinor > deSceneObject_Header.VersionMinor))
		return deFALSE;
	
	if (VersionMajor == 0 && (VersionMinor == 1 || VersionMinor == 2))
	{
		// transformation
		deTransformInfo matrix;
		DE3D_STREAM_GET(FS, matrix.Translation.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.Translation.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.Translation.z, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.R.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.R.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.R.z, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.U.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.U.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.U.z, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.I.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.I.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, matrix.I.z, deDouble, AmtRead, DataLength);
		if (VersionMinor == 1)
		{
			deDouble nulldouble;
			DE3D_STREAM_GET(FS, nulldouble, deDouble, AmtRead, DataLength);
			DE3D_STREAM_GET(FS, nulldouble, deDouble, AmtRead, DataLength);
			DE3D_STREAM_GET(FS, nulldouble, deDouble, AmtRead, DataLength);
			DE3D_STREAM_GET(FS, matrix.m_Identity, deBoolean, AmtRead, DataLength);
			m_WorldTransform = matrix;
		}
		else if (VersionMinor == 2)
		{
			m_WorldTransform = matrix;
		}
		m_WorldTransform.TestIdentity();
		
		// radius
		DE3D_STREAM_GET(FS, m_BoundRadius, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_OccludeRadius, deDouble, AmtRead, DataLength);
		
		// center
		DE3D_STREAM_GET(FS, m_BoundCenter.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_BoundCenter.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_BoundCenter.z, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_OccludeCenter.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_OccludeCenter.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_OccludeCenter.z, deDouble, AmtRead, DataLength);
		
		// AABB
		DE3D_STREAM_GET(FS, m_AABB.Min.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_AABB.Min.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_AABB.Min.z, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_AABB.Max.x, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_AABB.Max.y, deDouble, AmtRead, DataLength);
		DE3D_STREAM_GET(FS, m_AABB.Max.z, deDouble, AmtRead, DataLength);
		
		// transparency
		DE3D_STREAM_GET(FS, m_Transparent, u8, AmtRead, DataLength);
	}
	
	return deTRUE;
}

deBoolean deSceneObject::DeSerializeLoad()
{
	return deTRUE;
}


/*
//=============================================================
// deSceneBrush
//=============================================================

deSceneBrush::deSceneBrush()
{
	pExclusiveBrush = NULL;
}

deSceneBrush::~deSceneBrush()
{
	if (pExclusiveBrush)
		pExclusiveBrush->Release();
}

IdeSceneObject::interface_t deSceneBrush::GetSOIType()
{
	return iface_IdeSceneBrush;
}

void* deSceneBrush::GetSOInterface(interface_t i)
{
	if (i == iface_IdeSceneBrush)
	{
		return (IdeSceneBrush*)(this);
	}
	if (i == iface_IdeSceneObject)
	{
		return (IdeSceneObject*)(this);
	}
	return NULL;
}

void deSceneBrush::SetBrush(IdeBrush * brush)
{
	IdeVertexBuffer * VB, *VB2;
	deVertex *BufferList;
	deVec3d * Vec3dList;
	deTList <IdeVertexBuffer*> VBList;
	unsigned long TotalVerts, VertNum, i;
	void * ptr;

	if (pExclusiveBrush)
		pExclusiveBrush->Release();
	pExclusiveBrush = brush;
	if (!pExclusiveBrush)
		return;
	pExclusiveBrush->Claim();
	{
		i = brush->GetNumVBs();
		unsigned long k;
		for (k = 0; k < i; ++k)
		{
			VB = brush->GetVertexBuffer(k);
			if (k == 0 && VB && VB->GetParentBuffer())
			{
				VB = VB->GetParentBuffer();
				VB2 = VB->GetBoundDataBuffer();
				if (VB2)
					VB = VB2;
				VBList.AddElement(VB);
				break;
			}
			VBList.AddElement(VB);
		}
	}
	TotalVerts = 0;
	ptr = VBList.GetNext(NULL, VB);
	while (ptr)
	{
		TotalVerts += VB->GetVertexCount();

		ptr = VBList.GetNext(ptr, VB);
	}

	Vec3dList = new deVec3d[TotalVerts];
	VertNum = 0;
	ptr = VBList.GetNext(NULL, VB);
	while (ptr)
	{
		VB->Lock();
		BufferList = (deVertex*)VB->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
		if (!BufferList)
		{
			VB2 = VB->GetBoundDataBuffer();
			if (VB2)
			{
				VB->Unlock();
				VB2->Lock();
				BufferList = (deVertex*)VB2->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
				VB = VB2;
				VB2 = NULL;
			}
		}
		if (BufferList)
		{
			for (i = 0; i+VertNum < TotalVerts; i++)
			{
				Vec3dList[i+VertNum] = deMake_Vec3d(BufferList[i]);
			}
			VertNum += VB->GetVertexCount();
		}
		VB->Unlock();

		ptr = VBList.GetNext(ptr, VB);
	}

//	deVec3d Center = CalcAverageCenter(Vec3dList, TotalVerts);
//	pExclusiveBrush->Translate(Center * -1);
//	for (i = 0; i < TotalVerts; i++)
//		Vec3dList[i] = Vec3dList[i] - Center;

	CalcAllValues(Vec3dList, TotalVerts);
	SetOccludeRadius(0);

	deTransform M;
	pExclusiveBrush->GetWorldTransform(M);
//	M.Translation = M.Translation + Center;
	this->SetWorldTransform(M);

	delete[] Vec3dList;
}

long deSceneBrush::TraverseForRender(IdeSceneGraph::deSceneTraversal* Params)
{
	IdeVertexBuffer * VB;
	deTList <IdeVertexBuffer*> VBList;
	void * ptr;
	int i, PolysRendered = 0;
	IdeRender::deRenderObject obj;
	memset(&obj, 0, sizeof(IdeRender::deRenderObject));
	
	obj.VB = NULL;
	obj.Lights[0] = s_TestLight;
	obj.Lights[1] = s_TestLight2;
	obj.Lights[2] = NULL;

	obj.SurfaceDetail.TexBaseIndex = 0;
	obj.SurfaceDetail.BumpBaseIndex = 1;
	obj.SurfaceDetail.EmissiveIndex = 2;
	obj.SurfaceDetail.SpecMapIndex = 2;

	{
		i = pExclusiveBrush->GetNumVBs();
		for (int k = 0; k < i; ++k)
		{
			VBList.AddElement(pExclusiveBrush->GetVertexBuffer(k));
		}
	}
	i = 0;
	ptr = VBList.GetNext(NULL, VB);
	while (ptr)
	{
		obj.VB = VB;
		obj.SurfaceDetail.Transparency = IsTransparent();
		obj.SurfaceDetail.Texture = pExclusiveBrush->GetTexture(i);
		this->GetWorldTransform(obj.Transform);
		r->RenderPrepBuffer(obj);
		PolysRendered += VB->GetTriCount();
		i++;

		ptr = VBList.GetNext(ptr, VB);
	}


#if SHOW_SPHERES
	obj.Tex = NULL;
	VBList.EmptyList();
	m_LB->GetVertexBuffers(VBList);
	i = 0;
	ptr = VBList.GetNext(NULL, VB);
	while (ptr)
	{
		obj.VB = VB;
		this->GetWorldTransform(obj.Transform);
		r->RenderPrepBuffer(obj);
		PolysRendered += VB->GetTriCount();
		i++;

		ptr = VBList.GetNext(ptr, VB);
	}
#endif

	return PolysRendered;
}

deBoolean deSceneBrush::Serialize(IdeFile* FS)
{
	if (!deSceneObject::Serialize(FS))
		return deFALSE;

	IdeWorld*			pWorld;

	TempWID = 0;
	TempOID = 0;
	if (pExclusiveBrush)
	{
#pragma todo (decide if these Sceneobject references should only be saved if they are in the same world)
		TempWID = pExclusiveBrush->GetWorldID();
		pWorld = IdeWorld_GetManager()->GetWorld(((IdeWorldObject*)this)->GetWorldID());
		if (pWorld)
		{
			TempWID = pWorld->GetLocalIDFromLoadedWorld(TempWID);
			TempOID = pExclusiveBrush->GetObjectID();
		}
		else
			TempWID = 0;
	}
	
	DE3D_STREAM_PUT(FS, TempWID, u16); // deWorldID
	DE3D_STREAM_PUT(FS, TempOID, u32); // deObjectID

	return deTRUE;
}

deBoolean deSceneBrush::DeSerialize(IdeFile* FS, long DataLength)
{
	u32 AmtRead = 0;
	if (!deSceneObject::DeSerialize(FS, DataLength, AmtRead))
		return deFALSE;
	
	DE3D_STREAM_GET(FS, TempWID, u16, AmtRead, DataLength); // deWorldID
	DE3D_STREAM_GET(FS, TempOID, u32, AmtRead, DataLength); // deObjectID

	return deTRUE;
}

deBoolean deSceneBrush::DeSerializeLoad()
{
	IdeWorld*		pWorld;
	IdeWorldObject*	pObject;

	pExclusiveBrush = NULL;
	pWorld = IdeWorld_GetManager()->GetWorld(((IdeWorldObject*)this)->GetWorldID());
	if (pWorld)
	{
		TempWID = pWorld->GetWorldIDUsingLocalID(TempWID, deFALSE);
		pWorld = IdeWorld_GetManager()->GetWorld(TempWID);
		if (pWorld)
		{
			pObject = pWorld->GetWorldObject(TempOID);
			if (pObject)
			{
				pExclusiveBrush = (IdeBrush*)(pObject->GetWOInterface(IdeBrush_GetWOInterfaceID()));
			}
		}
	}

	return deTRUE;
}
*/

//=============================================================
// deSceneAnimatedMesh
//=============================================================

deSceneAnimatedMesh::deSceneAnimatedMesh()
:	deSceneObject(iface_IdeSceneAnimatedMesh)
{
	pMyMesh = NULL;
}

deSceneAnimatedMesh::~deSceneAnimatedMesh()
{
}

IdeSceneObject::interface_t deSceneAnimatedMesh::GetSOIType()
{
	return iface_IdeSceneAnimatedMesh;
}

void* deSceneAnimatedMesh::GetSOInterface(interface_t i)
{
	if (i == iface_IdeSceneAnimatedMesh)
	{
		return (IdeSceneAnimatedMesh*)(this);
	}
	if (i == iface_IdeSceneObject)
	{
		return (IdeSceneObject*)(this);
	}
	return NULL;
}

long deSceneAnimatedMesh::TraverseForRender(IdeSceneGraph::deSceneTraversal* Params)
{
	IdeVertexBuffer * VB;
	int PolysRendered = 0;
	IdeRender::deRenderObject obj;
	memset(&obj, 0, sizeof(IdeRender::deRenderObject));
	
	obj.VB = NULL;
	obj.Lights[0] = s_TestLight;
	obj.Lights[1] = NULL;
	
	VB = pMyMesh->GetVBuffer((float)Params->render->GetTimeStep());
	obj.VB = VB;
	((deTransform&)obj.Transform).Reset();
	obj.Transform.Translation.z = -20;
	obj.SurfaceDetail.Transparency = deFALSE;
	obj.SurfaceDetail.Texture = pMyMesh->GetTexture(Params->render->CurrentPassDriver());
	Params->render->RenderPrepBuffer(obj);
	PolysRendered += VB->GetTriCount();

	return PolysRendered;
}

void deSceneAnimatedMesh::SetMesh(IdeAnimatedMesh * mesh)
{
	pMyMesh = mesh;
}

deBoolean deSceneAnimatedMesh::Serialize(IdeFile* FS)
{
	if (!deSceneObject::Serialize(FS))
		return deFALSE;
	
	return deFALSE;
}

deBoolean deSceneAnimatedMesh::DeSerialize(IdeFile* FS, long DataLength)
{
	u32 AmtRead = 0;
	if (!deSceneObject::DeSerialize(FS, DataLength, AmtRead))
		return deFALSE;
	
	return deFALSE;
}

//=============================================================
// deSceneActor
//=============================================================

deSceneActor::deSceneActor()
:	deSceneObject(iface_IdeSceneActor),
	mActor(NULL)
{
}

deSceneActor::~deSceneActor()
{
}

IdeSceneObject::interface_t deSceneActor::GetSOIType()
{
	return iface_IdeSceneActor;
}

void* deSceneActor::GetSOInterface(interface_t i)
{
	if (i == iface_IdeSceneActor)
	{
		return (IdeSceneActor*)(this);
	}
	if (i == iface_IdeSceneObject)
	{
		return (IdeSceneObject*)(this);
	}
	return NULL;
}

long deSceneActor::TraverseForRender(IdeSceneGraph::deSceneTraversal* /*Params*/)
{
#pragma todo(implement deSceneActor::TraverseForRender)
	return 0;
	/*
	IdeVertexBuffer * VB;
	int PolysRendered = 0;
	IdeRender::deRenderObject obj;
	memset(&obj, 0, sizeof(IdeRender::deRenderObject));
	
	obj.VB = NULL;
	obj.Lights[0] = s_TestLight;
	obj.Lights[1] = NULL;
	
	VB = pMyMesh->GetVBuffer((float)r->GetTimeStep());
	obj.VB = VB;
	((deTransform&)obj.Transform).Reset();
	obj.Transform.Translation.z = -20;
	obj.SurfaceDetail.Transparency = deFALSE;
	obj.SurfaceDetail.Texture = pMyMesh->GetTexture(r->CurrentPassDriver());
	r->RenderPrepBuffer(obj);
	PolysRendered += VB->GetTriCount();
	
	return PolysRendered;
	*/
}

void deSceneActor::SetActor(IdeActor * actor)
{
//	if (actor)
//		actor->Claim();
//	if (mActor)
//		mActor->Release();
	mActor = actor;
}

deBoolean deSceneActor::Serialize(IdeFile* FS)
{
	if (!deSceneObject::Serialize(FS))
		return deFALSE;
	
	return deFALSE;
}

deBoolean deSceneActor::DeSerialize(IdeFile* FS, long DataLength)
{
	u32 AmtRead = 0;
	if (!deSceneObject::DeSerialize(FS, DataLength, AmtRead))
		return deFALSE;
	
	return deFALSE;
}



//=============================================================
// deSceneStaticMesh
//=============================================================

deSceneStaticMesh::deSceneStaticMesh()
:	deSceneObject(iface_IdeSceneStaticMesh),
	mMesh(NULL)
{
}

deSceneStaticMesh::~deSceneStaticMesh()
{
	if (mMesh)
		mMesh->Release();
}

IdeSceneObject::interface_t deSceneStaticMesh::GetSOIType()
{
	return iface_IdeSceneStaticMesh;
}

void* deSceneStaticMesh::GetSOInterface(interface_t i)
{
	if (i == iface_IdeSceneStaticMesh)
	{
		return (IdeSceneStaticMesh*)(this);
	}
	if (i == iface_IdeSceneObject)
	{
		return (IdeSceneObject*)(this);
	}
	return NULL;
}

long deSceneStaticMesh::TraverseForRender(IdeSceneGraph::deSceneTraversal* Params)
{
	IdeVertexBuffer * MainVB;
	int PolysRendered = 0;
	IdeRender::deRenderObject obj;
	memset(&obj, 0, sizeof(IdeRender::deRenderObject));

	obj.VB = NULL;
	Params->CurrentSceneGraph->LightExtents(m_WorldTransform.Translation, m_AABB, obj.Lights, 4);
	if (!obj.Lights[0])
		obj.Lights[0] = s_TestLightBright;
	//obj.Lights[1] = s_TestLight;

	int i, len;
	len = mMesh->GetNumSubMeshes();
	MainVB = mMesh->GetMainVertexBuffer();
	for (i = 0; i < len; ++i)
	{
		obj.VB = mMesh->GetSubMeshVertexBuffer(i);
		if (!obj.VB)
			obj.VB = MainVB;
		obj.IndexOverride = mMesh->GetSubMeshIndexBuffer(i);
		if (obj.IndexOverride)
			PolysRendered += obj.IndexOverride->GetTriCount();
		else if (obj.VB)
			PolysRendered += obj.VB->GetTriCount();
		obj.Transform = GetWorldTransform();
		obj.SurfaceDetail.Transparency = IsTransparent();
		obj.pSurface = mMesh->GetSubMeshSurface(i);
		Params->render->RenderPrepBuffer(obj);
	}
	
	return PolysRendered;
}

void deSceneStaticMesh::SetMesh(IdeMesh* mesh)
{
	if (mesh)
	{
		deAABB bbox;
		mesh->GetBBox(bbox);
		SetLocalAABB(bbox.Min, bbox.Max);
		SetBoundRadius(mesh->GetBoundRadius());
		mesh->Claim();
	}
	if (mMesh)
		mMesh->Release();
	mMesh = mesh;
}

void deSceneStaticMesh::SetTransparent(deBoolean Trans)
{
	deSceneObject::SetTransparent(Trans);
}

deBoolean deSceneStaticMesh::Serialize(IdeFile* FS)
{
	if (!deSceneObject::Serialize(FS))
		return deFALSE;

	if (mMesh)
	{
		mMeshID.wid = mMesh->GetWorldID();
		mMeshID.oid = mMesh->GetObjectID();
	}
	else
	{
		mMeshID.wid = 0;
		mMeshID.oid = 0;
	}
	deByteOrderLE16(mMeshID.wid);
	deByteOrderLE32(mMeshID.oid);
	if (2 != FS->Write(&mMeshID.wid, 2))	// 16 bit ID
		return deFALSE;
	if (4 != FS->Write(&mMeshID.oid, 4))	// 32 bit ID
		return deFALSE;
	
	return deTRUE;
}

deBoolean deSceneStaticMesh::DeSerialize(IdeFile* FS, long DataLength)
{
	u32 AmtRead = 0;
	if (!deSceneObject::DeSerialize(FS, DataLength, AmtRead))
		return deFALSE;
	
	if (2 != FS->Read(&mMeshID.wid, 2))	// 16 bit ID
		return deFALSE;
	if (4 != FS->Read(&mMeshID.oid, 4))	// 32 bit ID
		return deFALSE;

	deByteOrderLE16(mMeshID.wid);
	deByteOrderLE32(mMeshID.oid);

	return deTRUE;
}

deBoolean deSceneStaticMesh::DeSerializeLoad()
{
	IdeWorldManager* manager;
	manager = IdeWorld_GetManager();
	if (mMeshID.wid)
	{
		IdeWorldObject* pObject;
		pObject = manager->GetWorldObject(mMeshID.wid, mMeshID.oid);
		if (pObject)
			mMesh = (IdeMesh*)pObject->GetWOInterface(IdeMesh_GetWOInterfaceID());
		else
			DEBUGMSG2("deSceneStaticMesh::DeSerializeLoad - failure loading object with WID=%d and OID=%d", mMeshID.wid, mMeshID.oid);
	}
	if (mMesh)
	{
		mMesh->Claim();
	}
	return deTRUE;
}


//=============================================================
// deSceneStaticMesh
//=============================================================
deSceneLight::deSceneLight()
:	deSceneObject(iface_IdeSceneLight),
	m_RenderLight(NULL)
{
}

deSceneLight::~deSceneLight()
{
	if (m_RenderLight)
		m_RenderLight->Release();
}

IdeSceneObject::interface_t deSceneLight::GetSOIType()
{
	return iface_IdeSceneLight;
}

void* deSceneLight::GetSOInterface(interface_t i)
{
	if (i == iface_IdeSceneLight)
	{
		return (IdeSceneLight*)(this);
	}
	if (i == iface_IdeSceneObject)
	{
		return (IdeSceneObject*)(this);
	}
	return NULL;
}

long deSceneLight::TraverseForRender(IdeSceneGraph::deSceneTraversal* /*Params*/)
{
	// doesn't render anything
	return 0;
}

void deSceneLight::SetLightData(const IdeRenderLight::LightData& lightdata)
{
	if (!m_RenderLight)
		m_RenderLight = IdeRenderState_CreateRenderLight();

	deTransform mat(deMake_Vec3d(lightdata.Position));
	deAABB bbox = {
		{-lightdata.MaxRange,-lightdata.MaxRange,-lightdata.MaxRange},
		{lightdata.MaxRange,lightdata.MaxRange,lightdata.MaxRange}};
	SetBoundCenter(deZeroVec);
	SetBoundRadius(lightdata.MaxRange);
	SetLocalAABB(bbox.Min, bbox.Max);
	SetWorldTransform(mat);

	m_RenderLight->Lock();
	m_RenderLight->SetAllData(&lightdata);
	m_RenderLight->Unlock();
}

const IdeRenderLight::LightData* deSceneLight::GetLightData()
{
	if (!m_RenderLight)
		m_RenderLight = IdeRenderState_CreateRenderLight();
	return m_RenderLight->GetAllData();
}

const IdeRenderLight* deSceneLight::GetRenderLight()
{
	if (!m_RenderLight)
		m_RenderLight = IdeRenderState_CreateRenderLight();
	return m_RenderLight;
}

void deSceneLight::SetWorldTransform(const deTransformInfo& mat)
{
	deVertex currentpos = deMake_Vertex(m_WorldTransform.Translation);
	deSceneObject::SetWorldTransform(mat);
	if (!m_RenderLight)
		m_RenderLight = IdeRenderState_CreateRenderLight();
	deVertex pos;
	m_RenderLight->GetPosition(&pos);
	pos -= currentpos;
	pos += deMake_Vertex(m_WorldTransform.Translation);
	m_RenderLight->Lock();
	m_RenderLight->SetPosition(&pos);
	m_RenderLight->Unlock();
}

deBoolean deSceneLight::Serialize(IdeFile* FS)
{
#pragma todo(deSceneLight::Serialize)
	if (!deSceneObject::Serialize(FS))
		return deFALSE;
	
	return deFALSE;
}

deBoolean deSceneLight::DeSerialize(IdeFile* FS, long DataLength)
{
#pragma todo(deSceneLight::DeSerialize)
	u32 AmtRead = 0;
	if (!deSceneObject::DeSerialize(FS, DataLength, AmtRead))
		return deFALSE;
	
	return deFALSE;
}

