///////////////////////////////////////////////////////////////////////////////
/// @file deScene_priv.hpp
///
/// @brief private stuff for deScene
///
/// @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 Mar 2002
/// @author Assassin
/// @remarks Creation
///
/// @date Mar 2003
/// @author Masterg
/// @remarks Terrain addition
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DESCENE_PRIV_HPP
#define DESCENE_PRIV_HPP

#include "deScene.hpp"
#include "deOctree.hpp"
#include "deFileSystem.hpp"
#include "deBST.hpp"
#include "dePolyABT.hpp"
#include "deWorld_Helper.hpp"
#include "deObjectTree.hpp"

#pragma warning (disable : 4512) // assignment operator could not be generated

deBoolean IdeSceneRoom_Initialize();
deBoolean IdeSceneObject_Initialize();

void IdeSceneRoom_ShutDown();
void IdeSceneObject_ShutDown();

extern IdeVertexBuffer * s_AABB_VB;
extern IdeVertexBuffer * s_OBB_VB;

//=============================================================
class deSceneGraph : public IdeSceneGraph
{
public:
	deSceneGraph();
	~deSceneGraph();
	
	long Traverse(deSceneTraversal* Params);
	deBoolean	SetCullFlags(long CullFlags);
	long		GetCullFlags();

	deBoolean	SetDebugFlags(long DebugFlags);
	long		GetDebugFlags();
	
	IdeSceneRoom * FindRoomWithID(deWorldID WorldID, deObjectID ObjectID);
	deBoolean AddRoom(IdeSceneRoom * Room);
	deBoolean RemoveRoom(IdeSceneRoom* Room);
	deBoolean AddSceneObject(IdeSceneObject* Object);
	void RemoveSceneObject(IdeSceneObject* Object);
	void RepositionSceneObject(IdeSceneObject* Object);
	
	void AddObjectList(deTList <IdeSceneObject*> &list);	// this will destroy the list fed into it
	deBoolean PointsOccluded(deVec3d points[], long num);
	deBoolean PortalOccluded(IdePortal * portal);

	DWORD     TestIntersection(const deVec3d &Position, const deVec3d *Mins, const deVec3d *Maxs);
	deBoolean TestCollision(const IdeCollision::deCollideParams & Params, IdeCollision * Collision);
	void ObjectExtents(IdeBoundingVolume* volume, IdeCollision * Results);
	long LightExtents(const deVec3d& pos, const deAABB& bbox, IdeSceneLight** outBuffer, long bufferSize);

private:
	void BuildSphereCullList(deTList <IdeSceneObject*> &list);
	int DetailCullObjects(deDouble MinPixels);
	int OcclusionSphereCullObjects();
	void CleanUpSphereCullList();

private:
	long m_CurrentPassNum;
	long m_CullFlags;
	long m_DebugFlags;
	long m_MinPixelCull;
	deTList <IdeSceneRoom*> m_Rooms;
//	deSceneOctree * m_Octree; REMOVED
	// below items used on a per-pass basis
	deTList <IdeSceneObject*> m_Objects;
	deObjectTree m_ObjectTree;
	deObjectTree m_LightTree;
	struct cull_sphere_t
	{
	public:
		deVec3d boundcenter, occludecenter;
		deDouble boundradius, occluderadius;
		deDouble zval;
		IdeSceneObject * object;
		void * listptr;
		cull_sphere_t * next;
	};
	int m_NumOccluders;
	cull_sphere_t *m_SphereCullFront, *m_SphereCullBack;
	IdeCamera * m_CurrentCamera;
	deVec3d m_CurrentCamFwd;
};

class deCollision : public IdeCollision
{
public:
	deCollision();
	~deCollision();

	long Release();
	
	void		Reset();
	
	long		GetNumCollisions();
	void		GetCollision(long ColNum, deCollideInfo & collision);
	
	deBoolean	AddCollision(const deCollideInfo & collision);

private:
	deTArray <deCollideInfo> m_Collisions; // sorted by Percent
};


//=============================================================
class deSceneRoom : public deWorldObject, virtual public IdeSceneRoom
{
public:
	deSceneRoom();
	virtual ~deSceneRoom();
	
	WorldObjectClassDef;
	void* GetWOInterface(long interface_id);
	deBoolean DeSerializeLoad();
	
	void SetRoomListPtr(void * ptr);
	void*GetRoomListPtr();

	long Traverse(IdeSceneGraph::deSceneTraversal* Params);
	
	deBoolean TestCollision(const IdeCollision::deCollideParams & Params, IdeCollision * Collision);
	
	long GetPassNum();
	void GetAABB(deVec3d &Min, deVec3d &Max);
	
	void  SetContents(DWORD contents);
	DWORD GetContents();
	
	deBoolean AddSceneObject(IdeSceneObject * obj);
	deBoolean AddPortal(IdePortal * portal);
	
	deBoolean RemoveSceneObject(IdeSceneObject * obj);

protected:
	int ViewCullObjectList(IdeCamera * c, const IdePlaneFrustum* f, deTList <IdeSceneObject*> &inout);
	
	deTList <IdePortal*>		m_PortalList;
	deTList <IdeSceneObject*>	m_Objects;
	deVec3d					m_Min, m_Max;
	DWORD					m_Contents;
	void*					m_RoomListPtr;
	
private:
	virtual int TraverseRoom(IdeSceneGraph::deSceneTraversal* /*Params*/ ) { return 0; }
	virtual deBoolean TestRoomCollision(const IdeCollision::deCollideParams & /*Params*/, IdeCollision * /*Collision*/) { return deFALSE; }
	int m_CurrentPassNum;

	deTArray<deIDPair> m_CachedObjects;
};

//=============================================================
class deSceneObject : public deWorldObject, virtual public IdeSceneObject
{
protected:
	virtual long		TraverseForRender(IdeSceneGraph::deSceneTraversal* Params) = 0;
	
public:
	deSceneObject(interface_t myType);
	virtual ~deSceneObject();
	
	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	void* GetWOInterface(long interface_id);
	deBoolean Serialize(IdeFile* FS);
	deBoolean DeSerialize(IdeFile* FS, long DataLength, u32 &AmtRead);
	deBoolean DeSerializeLoad();

	void		PreCache(IdeDriver*) {}
	
	long		Traverse(IdeSceneGraph::deSceneTraversal* Params);
	void		SetActivation(activate_t a_type, deBoolean Active);
	deBoolean	GetActivation(activate_t a_type) const;
	
	void		SetWorldTransform(const deTransformInfo & mat);
	
	deVec3d		GetBoundCenter();
	deVec3d		GetOccludeCenter();
	void		GetWorldTransform(deTransformInfo & mat);
	void		GetWorldPos(deVec3d & PosVector);
	
	deVec3d		GetWorldBoundCenter();
	deVec3d		GetWorldOccludeCenter();
	deDouble	GetBoundRadius();
	deDouble	GetOccludeRadius();
	
	void		GetLocalAABB(deAABB &bbox);
	void		GetWorldOBB(deOBB &bbox);
	void		GetWorldAABB(deAABB &bbox);
	
	deBoolean	IsTransparent();
	
	deBoolean	OccludesObject(deVec3d &position, IdeSceneObject *so, deVec3d &NormalizedDisplacement);
	
	long		GetCurrentPassNum() const;
	deBoolean	AddRoomReference(IdeSceneRoom * ref, void * ptr);
	void*		GetRoomReference(IdeSceneRoom * ref);
	deBoolean	RemoveRoomReference(IdeSceneRoom * ref);
	
protected:
	void		SetBoundCenter(deVec3d);
	void		SetOccludeCenter(deVec3d);
	void		SetBoundRadius(deDouble);
	void		SetOccludeRadius(deDouble);
	
	void		SetWorldCenters();
	
	void		SetLocalAABB(deVec3d min, deVec3d max);
	
	deVec3d		CalcAverageCenter(deVec3d points[], long count);
	deDouble	CalcBoundRadius(deVec3d points[], long count);
	deDouble	CalcOccludeRadius(deVec3d points[], long count);
	void		CalcLocalAABB(deVec3d points[], long count, deVec3d &min, deVec3d &max);
	
	void		CalcAllValues(deVec3d points[], long count);
	
	void		SetTransparent(deBoolean Transparent);
	
	IdeLineBrush*	GetBoundLine();
	
	const deTransformInfo &	GetWorldTransform();
	
private:
protected:
	deTransform	m_WorldTransform;
	deDouble	m_BoundRadius;
	deDouble	m_OccludeRadius;
	deVec3d		m_BoundCenter;
	deVec3d		m_OccludeCenter;
	deAABB		m_AABB;
	deVec3d		m_WorldBound, m_WorldOcclude;
	deBoolean	m_Transformed;
	deBoolean	m_Transparent;
	deBoolean	m_Active[activate_count];
	long		m_CurrentPassNum;
	const interface_t m_Interface;
private:
	IdeLineBrush*	m_BoundLine;
	struct RoomEntry
	{
		IdeSceneRoom* Room;
		void * ptr;
	};
	deTArray<RoomEntry> m_RoomList;
};

//=============================================================
class deSceneABT : public deSceneObject, public IdeSceneABT
{
protected:
	virtual ~deSceneABT();
public:
	deSceneABT();

	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;
	deBoolean DeSerializeLoad();
	
	deBoolean AddGeometry(IdeMesh* pMesh, const deTransformInfo & pos);

private:
	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	deBoolean TestRoomCollision(const IdeCollision::deCollideParams & Params, IdeCollision * Collision);

	dePolyABT m_ABT;
	deBoolean m_Compiled;
};

//=============================================================
class deSceneTerrain : public deSceneObject, public IdeSceneTerrain
{
protected:
	virtual ~deSceneTerrain();
public:
	deSceneTerrain();
	
	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;
	deBoolean DeSerializeLoad();

	void PreCache(IdeDriver* pDriver);

	deBoolean SetTerrainHeightMap(IdeBitmapProxy * bmp);
	deBoolean SetTerrainColorMap(IdeBitmapProxy * bmp);
	deBoolean SetTerrainDetailMap(IdeBitmapProxy * bmp);

	deVec3d GetTerrainScale();
	void SetTerrainScale(deVec3d vScale);
	void SetTerrainScale(deFloat x, deFloat y, deFloat z);
	
	deTexCoord GetDetailRepeat();
	void SetDetailRepeat(deTexCoord Repeat);
	void SetDetailRepeat(deFloat u, deFloat v);

	void SetHeightAt(unsigned char ucHeight, long x, long y);
	unsigned char GetTrueHeightAt(long x, long y);
	deFloat GetScaledHeightAt(long x, long y);

	deDouble GetLODBias();
	void SetLODBias(deDouble Bias);
	
	deBoolean BuildRenderable();

	// quadtree routines
	long GetGMMChunkSize();
	void SetGMMChunkSize(long ChunkSize);
	deBoolean BuildGeoMipMap(IdeDriver* pDriver);

private:
	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	deBoolean TestRoomCollision(const IdeCollision::deCollideParams & Params, IdeCollision * Collision);
	
	struct deTerrainGMMChunk
	{
		deAABB				BBox;
		deVec3d				ChunkCenterPos;
		deDouble			Diagonal;
		deDouble			UnitError[8];
		IdeVertexBuffer*	Vertices;
		long				XCoord;
		long				ZCoord;
		char				CurrentLOD;
	};
	struct deTerrainGMMQuadrant
	{
		deBoundSphere			BSphere;
		deAABB					BBox;
		deTerrainGMMQuadrant*	Children[4];
		deTerrainGMMChunk*		Geometry;
	};
	
//	deTransform			m_WorldTransform;
	deVec3d				m_Scale;
	deTexCoord			m_DetailRepeat;

	IdeBitmapProxy*		m_HeightMap;
	IdeBitmapProxy*		m_ColorMap;
	IdeBitmapProxy*		m_DetailMap;

	IdeRenderTexture*	m_RenderTexture;

	deTArray<IdeVertexBuffer*>	m_VBuffers;
	deTArray<IdeVertexBuffer*>	m_IndexBuffers;

	// quadtree related variables
	deTerrainGMMChunk*		m_GMMChunks;
	deTerrainGMMQuadrant*	m_GMMRoot;
	long m_GMMChunkSize;
	long m_GMMChunksXSide;
	long m_GMMChunksZSide;
	deDouble m_LODBias;

	// texture stuff
	deBoolean CreateTerrainTexture(IdeDriver * pDriver);

	// quadtree related private routines
	int GetGMMChunkNumber(long x, long z);
	void ResetGMMChunks();
	deBoolean BuildGMMQuadtree();
	deBoolean BuildGMMQuadrant(deTerrainGMMQuadrant* Parent, long StartX, long StartZ, long StopX, long StopZ);
	void DestroyGMMQuadrant(deTerrainGMMQuadrant* &Quadrant);

	// individual node routines
	deBoolean RenderGMMChunk(const deTerrainGMMChunk* Chunk, IdeSceneGraph::deSceneTraversal* Params, const deTransform& WorldTransform);
	deBoolean BuildGMMChunk(long x, long z, IdeDriver* pDriver);
	void DestroyGMMChunk(deTerrainGMMChunk* Chunk);

	// entire collection of node, qtree routines
	deBoolean RenderVisibleGMMChunks(deTerrainGMMQuadrant* Quadrant, IdeSceneGraph::deSceneTraversal* Params, const deTransform& WorldTransform, deBoolean AllVisible);
};

//=============================================================
class deSceneSky : public deSceneObject, public IdeSceneSky
{
protected:
	virtual ~deSceneSky();
public:
	deSceneSky();
	
	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;
	deBoolean DeSerializeLoad();
	void OnWorldIDSet();

	deBoolean	IsLockedToCam() const;
	void		LockToCam(deBoolean Lock);
	deBoolean	GetInfo(SkyInfo * info) const;
	deBoolean	SetInfo(const SkyInfo * info);

private:
	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	deBoolean TestRoomCollision(const IdeCollision::deCollideParams & Params, IdeCollision * Collision);

	deBoolean CreateVBuffers();
	deBoolean CreateTextures();

	static long					s_NumSkyRooms;
	static IdeVertexBuffer*		s_VBuffer;
	static IdeVertexBuffer*		s_SubVBs[6];
	IdeRenderTexture*			m_Textures[6];
	IdeSceneSky::SkyInfo		m_Info;
	deBoolean					m_LockedToCam;
};

//=============================================================
class deSceneAnimatedMesh : public deSceneObject, public IdeSceneAnimatedMesh
{
public:
	deSceneAnimatedMesh();
	virtual ~deSceneAnimatedMesh();
	
	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;
	
	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	void SetMesh(IdeAnimatedMesh * mesh);
	
private:
	IdeAnimatedMesh * pMyMesh;
};

//=============================================================
class deSceneStaticMesh : public deSceneObject, public IdeSceneStaticMesh
{
public:
	deSceneStaticMesh();
	virtual ~deSceneStaticMesh();

	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;
	deBoolean DeSerializeLoad();

	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	void SetMesh(IdeMesh* mesh);
	void SetTransparent(deBoolean Trans);
	
private:
	IdeMesh * mMesh;
	deIDPair mMeshID;
};

//=============================================================
class deSceneActor : public deSceneObject, public IdeSceneActor
{
public:
	deSceneActor();
	virtual ~deSceneActor();
	
	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;
	
	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	void SetActor(IdeActor* actor);
	
private:
	IdeActor * mActor;
};

//=============================================================
class deSceneLight : public deSceneObject, public IdeSceneLight
{
public:
	deSceneLight();
	virtual ~deSceneLight();
	
	IdeSceneObject::interface_t GetSOIType();
	void* GetSOInterface(IdeSceneObject::interface_t i);
	WorldObjectClassDef;

	void SetWorldTransform(const deTransformInfo& mat);

	long TraverseForRender(IdeSceneGraph::deSceneTraversal* Params);
	void SetLightData(const IdeRenderLight::LightData& lightdata);
	const IdeRenderLight::LightData* GetLightData();
	const IdeRenderLight* GetRenderLight();

private:
	IdeRenderLight* m_RenderLight;
};

#endif