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

Name:		Raytracer
Author:		Kamil Szatkowski "Netrix" (C)
Website:	http://www.netrix.org.pl/

*********************************************************************/
#pragma once
#include "NLib/nMath.hpp"
#include "NLib/nAlignedDynamicTable.hpp"
#include "NLib/nDynamicTable.hpp"

struct BoxAABB
{
	NIne::NMVector3f vMin;
	NIne::NMVector3f vMax;
};

struct RayTestOutput
{
	NIne::NMVector3f oPoint;	// 12
	float t;					// 4
	bool bFront;				// 1
	float u, v;					// 8
	float f;					// 4
	NIne::NUint32 : 24;			// 3
};	// 32

enum
{
	X_AXIS = 0,
	Y_AXIS,
	Z_AXIS
};

struct Vertex
{
	NIne::NMVector3f v3Position;
	NIne::NMVector3f v3Normal;
};	// 24

struct FaceData
{
	Vertex* pVertices[3];			// 12/24
	NIne::NUint32 uAttributeIndex;	// 4
	NIne::NMVector3f v3Normal;		// 12
	NIne::NUint8 uAxis;				// 4
	float ua, ub;					// 8
	float va, vb;					// 8
	float na, nb, nd;				// 12
	NIne::NMVector3f v3Min, v3Max;			// 24
	NIne::NMVector3f v3Tangent, v3Binormal; // 24
	NIne::NMVector3f v3TangentSize;			// 12
};	// 64/80


class Scene
{
	typedef NIne::NDynamicTable<Vertex> VertexArray;
	typedef NIne::NDynamicTable<NIne::NUint32> NUint32Array;
	typedef NIne::NAlignedDynamicTable<FaceData> FaceDataArray;

public:
	Scene();
	~Scene();

	bool loadScene(const char* szFile);
	void releaseScene();

	void preprocessScene();

	NIne::NSize_t getFaceCount() const { return m_aFaces.GetSize(); }
	const NIne::NMVector3f& getFaceNormal(NIne::NSize_t uFaceIndex) const { return m_aFaces[uFaceIndex].v3Normal; }
	NIne::NUint32 getFaceAttributeIndex(NIne::NSize_t uFaceIndex) const { return m_aFaces[uFaceIndex].uAttributeIndex; }

	const VertexArray& getVertices() const { return m_aVertices; }
	FaceDataArray& getFaceData() { return m_aFaces; }
	const NIne::NMVector3f& getDomainMin() const { return m_v3Min; }
	const NIne::NMVector3f& getDomainMax() const { return m_v3Max; }
	const NIne::NMVector3f& getDomainCenter() const { return m_v3Center; }

	bool testTriangle(NIne::NSize_t uFaceIndex, const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, RayTestOutput* pRayOut) const;

	bool testTriangle(const FaceData* pFace, const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, float fMaxDist, RayTestOutput* pRayOut) const;

	bool rayTriangleTestFast(NIne::NSize_t uFaceIndex, const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, float fMaxDist, RayTestOutput* pRayOut) const;

	bool rayTriangleTestFast(const FaceData* pFace, const NIne::NMVector3f& rayPos, const NIne::NMVector3f& rayDir, float fMaxDist, RayTestOutput* pRayOut) const;

	float getDomainFactor() const { return m_fDomainFactor; }
	float getMaxDomainLength() const { return m_fMaxDomainLength; }

	const char* getSceneName() const { return m_szName; }

	operator bool() { return m_bLoaded; }

private:
	void precalculateFaces();

	void findMinMax();

	void fixDomain();

private:
	bool m_bLoaded;
	char m_szName[255];
	VertexArray m_aVertices;
	FaceDataArray m_aFaces;

	NIne::NMVector3f m_v3Min,
				     m_v3Max,
					 m_v3Center;

	float m_fMaxDomainLength;
	float m_fDomainFactor;
};