/*
								+---------------------------------+
								|                                 |
								|   ***   3ds file loader  ***    |
								|                                 |
								|  Copyright  © -tHE SWINe- 2006  |
								|                                 |
								|           Load3ds.cpp           |
								|                                 |
								+---------------------------------+
*/

/**
 *	@file Load3ds.cpp
 *	@author -tHE SWINe-
 *	@date 2006
 *	@brief 3D Studio .3ds file loader
 *
 *	@date 2006-06-05
 *
 *	rewritten based on the old "C" 3ds loader
 *
 *	@date 2007-06-04
 *
 *	fixed !fread typo in ReadTrackColor()
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 */

#if defined(_MSC_VER) && !defined(__MWERKS__) // msvc
#pragma warning(4:4786)
// switch-off warning 4786 (too long class name truncated to 255 chars in debug info)
#endif

#include "NewFix.h"

#include "CallStack.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <math.h>
#include <algorithm>
#include "Vector.h"
#include "Load_3ds.h"
#include "StlUtils.h"

#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(for) && _MSC_VER <= 1200
#define for if(0) {} else for
#endif // _MSC_VER && !__MWERKS__ && !for && _MSC_VER <= 1200
// msvc 'for' scoping hack

using namespace _3DS;

/*
 *								=== TObject ===
 */

void TObject::Calc_FaceNormals()
{
	for(TFace *p_face_it = p_face, *p_end = p_face + n_face_num; p_face_it < p_end; p_face_it ++) {
		const TVertex **p_vert = (const TVertex**)p_face_it->p_vertex;

		p_face_it->t_normal = Plane3f(p_vert[0]->v_pos - p_vert[2]->v_pos,
			p_vert[1]->v_pos - p_vert[2]->v_pos, p_vert[2]->v_pos);
	}
}

void TObject::Calc_VertexNormals()
{
	for(TVertex *p_vertex_it = p_vertex, *p_end = p_vertex + n_vertex_num; p_vertex_it < p_end;)
		(p_vertex_it ++)->v_normal = Vector3f(0, 0, 0);
	for(TFace *p_face_it = p_face, *p_end = p_face + n_face_num; p_face_it < p_end; p_face_it ++) {
		for(int n = 0; n < 3; n ++)
			p_face_it->p_vertex[n]->v_normal += p_face_it->t_normal.v_normal;
	}
	for(TVertex *p_vertex_it = p_vertex, *p_end = p_vertex + n_vertex_num; p_vertex_it < p_end;)
		(p_vertex_it ++)->v_normal.Normalize();
}

class CVertexHash {
protected:
	TVertex *m_p_vertex;
	int m_n_vertex_num;
	Vector3f m_v_box[2];
	std::vector<TVertex*> **m_p_vertex_hash;
	int m_n_table_size;
	float m_f_cell_size;
	float m_f_cell_overlap;

public:
	CVertexHash(TVertex *p_vertex, int n_vertex_num)
		:m_p_vertex(p_vertex), m_n_vertex_num(n_vertex_num), m_f_cell_overlap(.1f)
	{
		m_v_box[0] = m_v_box[1] = p_vertex->v_pos;
		for(TVertex *p_end = p_vertex + n_vertex_num; p_vertex < p_end; p_vertex ++) {
			for(int n = 0; n < 3; n ++) {
				if(m_v_box[0][n] > p_vertex->v_pos[n])
					m_v_box[0][n] = p_vertex->v_pos[n];
				if(m_v_box[1][n] < p_vertex->v_pos[n])
					m_v_box[1][n] = p_vertex->v_pos[n];
			}
		}
		// find bounding box ...

		m_n_table_size = (int)sqrt(sqrt((double)n_vertex_num)) + 4;
		m_f_cell_size = (m_v_box[0] - m_v_box[1]).f_Length() / 1.732f / m_n_table_size; // sqrt(3)
		// determine size of the table

		m_p_vertex_hash = p_HashVertices(m_n_table_size, m_f_cell_size, m_f_cell_overlap);
		// hash vertices
	}

	~CVertexHash()
	{
		if(m_p_vertex_hash) {
			for(std::vector<TVertex*> **p_it = m_p_vertex_hash, **p_end = m_p_vertex_hash +
			   m_n_table_size * m_n_table_size * m_n_table_size; p_it < p_end;)
				delete *p_it ++;
			delete[] m_p_vertex_hash;
			m_p_vertex_hash = 0;
		}
	}

	bool b_Status()
	{
		return m_p_vertex_hash != 0;
	}

	std::vector<std::vector<TVertex*>*> *p_VertexMergeTable(float f_epsilon_ex)
	{
		std::vector<std::vector<TVertex*>*> *p_table;
		if(!(p_table = new(std::nothrow) std::vector<std::vector<TVertex*>*>))
			return 0;

		for(int i = 0; i < m_n_vertex_num; i ++)
			m_p_vertex[i].n_flag = 0;
		// clear vertex flags

		for(int i = 0; i < m_n_vertex_num; i ++) {
			int n_found = -1;
			TVertex *p_vertex = m_p_vertex + i;
			if(p_vertex->n_flag)
				continue;
			// we've already added this one

			std::vector<TVertex*> *p_group;

			p_group = new(std::nothrow) std::vector<TVertex*>;
			if(!p_group || !stl_ut::Reserve_1More(*p_table)) {
				if(p_group)
					delete p_group;
				for(unsigned int j = 0; j < p_table->size(); j ++)
					delete (*p_table)[j];
				delete p_table;
				return 0;
			}
			p_table->push_back(p_group);
			// create a new vertex group & add it to group table

			std::vector<TVertex*> *p_other_vertices =
				m_p_vertex_hash[n_HashFunction(m_p_vertex[i].v_pos)];
			// other vertices include current vertex as well so we don't need to explicitly add it

			for(unsigned int j = 0; j < p_other_vertices->size(); j ++) {
				TVertex *p_other = (*p_other_vertices)[j];
				if((p_other->v_pos - p_vertex->v_pos).f_Length() < f_epsilon_ex) {
					p_other->n_flag = 1;
					// set flags so this vertex is not inserted again

					if(!stl_ut::Reserve_1More(*p_group)) {
						for(unsigned int j = 0; j < p_table->size(); j ++)
							delete (*p_table)[j];
						delete p_table;
					}
					p_group->push_back(p_other);
					// add to table
				}
			}
			// search for similar vertices in table

			if(!p_group->size()) {
				p_table->erase(p_table->end() - 1);
				delete p_group;
			}
			// when there's vertex with NAN / INF coordinate, it can't be compared with the other
			// ones, neither with itself so it's not included in table
		}

		return p_table;
	}

protected:
	int n_HashFunction(Vector3f v_pos) const
	{
		int n_index[3];

		for(int i = 0; i < 3; i ++) {
			n_index[i] = ((int)(v_pos[i] / m_f_cell_size)) % m_n_table_size;
			while(n_index[i] < 0)
				n_index[i] += m_n_table_size;
		}

		return n_index[0] + (n_index[1] + n_index[2] * m_n_table_size) * m_n_table_size;
	}

	std::vector<TVertex*> **p_HashVertices(int n_table_size,
		float f_cell_size, float f_cell_overlap) const
	{
		std::vector<TVertex*> **p_table;
		int n_table_size3 = n_table_size * n_table_size * n_table_size;
		if(!(p_table = new(std::nothrow) std::vector<TVertex*>*[n_table_size3]))
			return 0;
		for(int i = 0; i < n_table_size3; i ++) {
			if(!(p_table[i] = new(std::nothrow) std::vector<TVertex*>)) {
				for(i --; i >= 0; i --)
					delete p_table[i];
				delete[] p_table;
				return 0;
			}
		}
		// alloc table

		for(int k = 0; k < m_n_vertex_num; k ++) {
			int n_index[3][2];
			for(int j = 0; j < 2; j ++) {
				for(int i = 0; i < 3; i ++) {
					n_index[i][j] = ((int)((m_p_vertex[k].v_pos[i] +
						j * f_cell_overlap) / f_cell_size)) % n_table_size;
					while(n_index[i][j] < 0)
						n_index[i][j] += n_table_size;
				}
			}
			// calc indices ...

			for(int x = 0; x < 2; x ++) {
				for(int y = 0; y < 2; y ++) {
					for(int z = 0; z < 2; z ++) {
						int n_cell = n_index[0][x] + (n_index[1][y] +
							n_index[2][z] * n_table_size) * n_table_size;
						// calc cell index

						if(!stl_ut::Reserve_1More(*p_table[n_cell])) {
							for(int i = 0; i < n_table_size3; i ++)
								delete p_table[i];
							delete[] p_table;
							return 0;
						}
						p_table[n_cell]->push_back(m_p_vertex + k);
						// add reference into cell

						if(n_index[2][0] == n_index[2][1])
							break;
					}
					if(n_index[1][0] == n_index[1][1])
						break;
				}
				if(n_index[0][0] == n_index[0][1])
					break;
			}
			// add references to a vertex to all cells it hit
		}

		return p_table;
	}
};

bool TObject::WeldVertices(float f_epsilon)
{
	CVertexHash hash(p_vertex, n_vertex_num);
	if(!hash.b_Status())
		return false;

	std::vector<std::vector<TVertex*>*> *p_merge_table;
	if(!(p_merge_table = hash.p_VertexMergeTable(f_epsilon)))
		return false;

	int i = 0;
	for(std::vector<std::vector<TVertex*>*>::iterator p_it = p_merge_table->begin();
	   p_it < p_merge_table->end(); p_it ++, i ++) {
		for(std::vector<TVertex*>::iterator p_vertex_it = (*p_it)->begin(); p_vertex_it < (*p_it)->end(); p_vertex_it ++)
			(*p_vertex_it)->n_flag = i;
	}
	// for every vertex, store it's index in the table

	for(TFace *p_face_it = p_face, *p_end = p_face + n_face_num; p_face_it < p_end; p_face_it ++) {
		for(int n = 0; n < 3; n ++) {
			TVertex *p_cur_vertex = p_face_it->p_vertex[n];
			p_face_it->p_vertex[n] = (*(*p_merge_table)[p_cur_vertex->n_flag])[0];
		}
	}
	// for every face, assign the first vertex with the right coordinates

	for(std::vector<std::vector<TVertex*>*>::iterator p_it = p_merge_table->begin();
	   p_it < p_merge_table->end();)
		delete *p_it ++;
	delete p_merge_table;

	/*for(TVertex *p_vertex_it = p_vertex, *p_end = p_vertex + n_vertex_num; p_vertex_it < p_end;)
		(p_vertex_it ++)->n_flag = -1;
	for(TFace *p_face_it = p_face, *p_end = p_face + n_face_num; p_face_it < p_end; p_face_it ++) {
		for(int n = 0; n < 3; n ++)
			p_face_it->p_vertex[n]->n_flag = p_face_it->n_group;
	}
	for(TFace *p_face_it = p_face, *p_end = p_face + n_face_num; p_face_it < p_end; p_face_it ++) {
		for(int n = 0; n < 3; n ++) {
			TVertex *p_cur_vertex = p_face_it->p_vertex[n];
			for(TVertex *p_vertex_it = p_vertex, *p_end = p_vertex + n_vertex_num;
			   p_vertex_it < p_end; p_vertex_it ++) {
				/ *if(p_vertex_it->n_flag != p_face_it->n_group)
					continue;* /
				if(p_vertex_it->v_pos.x - p_cur_vertex->v_pos.x < f_epsilon &&
				   p_vertex_it->v_pos.x - p_cur_vertex->v_pos.x > -f_epsilon &&
				   p_vertex_it->v_pos.y - p_cur_vertex->v_pos.y < f_epsilon &&
				   p_vertex_it->v_pos.y - p_cur_vertex->v_pos.y > -f_epsilon &&
				   p_vertex_it->v_pos.z - p_cur_vertex->v_pos.z < f_epsilon &&
				   p_vertex_it->v_pos.z - p_cur_vertex->v_pos.z > -f_epsilon) {
					p_face_it->p_vertex[n] = p_vertex_it;
					break;
				}
			}
		}
	}*/

	return true;
}

/*
 *								=== ~TObject ===
 */

/*
 *								=== TCamera ===
 */

void TCamera::CalcMatrix()
{
	if(!p_position || !n_position_num) {
		t_matrix.Identity();
		return;
	}

	if(!p_target || !n_target_num) {
		t_matrix.Identity();

		t_matrix[0][3] = p_position[0].v_position.x;
		t_matrix[1][3] = p_position[0].v_position.y;
		t_matrix[2][3] = p_position[0].v_position.z;

		return;
	}

	Vector3f v_up(0, 1, 0);
	Vector3f v_direction(p_target[0].v_position - p_position[0].v_position);
	v_direction.Normalize();
	Vector3f v_right(v_direction.v_Cross(v_up));
	v_up = v_direction.v_Cross(v_right);
	v_right.Normalize();
	v_up.Normalize();

	t_matrix[0][0] = v_right.x;
	t_matrix[1][0] = v_right.y;
	t_matrix[2][0] = v_right.z;
	t_matrix[3][0] = .0f;
	t_matrix[0][1] = v_up.x;
	t_matrix[1][1] = v_up.y;
	t_matrix[2][1] = v_up.z;
	t_matrix[3][1] = .0f;
	t_matrix[0][2] = v_direction.x;
	t_matrix[1][2] = v_direction.y;
	t_matrix[2][2] = v_direction.z;
	t_matrix[3][2] = .0f;
	t_matrix[0][3] = p_position[0].v_position.x;
	t_matrix[1][3] = p_position[0].v_position.y;
	t_matrix[2][3] = p_position[0].v_position.z;
	t_matrix[3][3] = 1.0f;
}

/*
 *								=== ~TCamera ===
 */

/*
 *								=== C3DSLoader ===
 */

enum {
	chunk_Version           = 0x0002,
	chunk_Color_F           = 0x0010,
	chunk_Color_24          = 0x0011,
	chunk_Lin_Color24       = 0x0012,
	chunk_Lin_Color_F       = 0x0013,
	chunk_N_Percentage      = 0x0030,
	chunk_F_Percentage      = 0x0031,
	chunk_Master_Scale      = 0x0100,
	chunk_BitMap            = 0x1100,
	chunk_Use_BitMap        = 0x1101,
	chunk_Bkgnd_Color       = 0x1200,
	chunk_Ambient_Color     = 0x2100,
	chunk_Layer_Fog         = 0x2302,
	chunk_MeshData          = 0x3D3D,
	chunk_MLib_Magic        = 0x3DAA,
	chunk_Mesh_Version      = 0x3D3E,
	chunk_Named_Object      = 0x4000,
	chunk_Triangle_Object        = 0x4100,
	chunk_Point_List        = 0x4110,
	chunk_Point_Flags       = 0x4111,
	chunk_Face_List         = 0x4120,
	chunk_Material_Group    = 0x4130,
	chunk_Tex_VertexS         = 0x4140,
	chunk_Smooth_Group      = 0x4150,
	chunk_Mesh_Matrix       = 0x4160,
	chunk_Mesh_Color        = 0x4165,
	chunk_Texture_Info      = 0x4170,
	chunk_Mesh_BoxMap       = 0x4190,
	chunk_Light             = 0x4600,
	chunk_SpotLight         = 0x4610,
	chunk_Light_Off         = 0x4620,
	chunk_Light_Shadowed    = 0x4627,
	chunk_Light_Attent      = 0x4625,
	chunk_Light_Range       = 0x4655,
	chunk_Light_Ray_Bias    = 0x4658,
	chunk_Light_In_Range    = 0x4659,
	chunk_Light_Out_Range   = 0x465A,
	chunk_Light_Multiplier  = 0x465B,
	chunk_Camera            = 0x4700,
	chunk_Main_Magic        = 0x4D4D,
	chunk_Hierarchy         = 0x4F00,
	chunk_Viewport_Layout   = 0x7001,
	chunk_Viewport_Size     = 0x7020,
	chunk_Material          = 0xAFFF,
	chunk_Material_Name     = 0xA000,
	chunk_Material_Ambient  = 0xA010,
	chunk_Material_Diffuse  = 0xA020,
	chunk_Material_Specular = 0xA030,
	chunk_Material_Shininess    = 0xA040,
	chunk_Material_ShinStrength = 0xA041,
	chunk_Material_Transparency = 0xA050,
	chunk_Material_XpFall   = 0xA052,
	chunk_Material_RefBlur  = 0xA053,
	chunk_Material_SelfIllum    = 0xA084,
	chunk_Material_DoubleSided = 0xA081,
	chunk_Material_TransAdd = 0xA083,
	chunk_Material_Wire_On   = 0xA085,
	chunk_Material_WireSize = 0xA087,
	chunk_Material_Soften   = 0xA08C,
	chunk_Material_Shading  = 0xA100,
	chunk_Material_Texture  = 0xA200,
	chunk_Material_Reflect  = 0xA220,
	chunk_Material_BumpMap  = 0xA230,
	chunk_Material_BumpAmount   = 0xA252,
	chunk_Material_MapName  = 0xA300,
	chunk_Material_MapFlags = 0xA351,
	chunk_Material_MapBlur  = 0xA353,
	chunk_Material_MapUScale    = 0xA354,
	chunk_Material_MapVScale    = 0xA356,
	chunk_Material_MapUOffset   = 0xA358,
	chunk_Material_MapVOffset   = 0xA35A,
	chunk_Material_MapRotAngle  = 0xA35C,
	chunk_KF_Data           = 0xB000,
	chunk_Ambient_Key       = 0xB001,
	chunk_Track_Info        = 0xB002,
	chunk_Track_Camera      = 0xB003,
	chunk_Track_Cam_Target  = 0xB004,
	chunk_Track_Light       = 0xB005,
	chunk_Track_Light_Target    = 0xB006,
	chunk_Track_SpotLight   = 0xB007,
	chunk_KF_Seg            = 0xB008,
	chunk_KF_CurTime        = 0xB009,
	chunk_KF_HDR            = 0xB00A,
	chunk_Track_Node_Name   = 0xB010,
	chunk_Instance_Name     = 0xB011,
	chunk_PreScale          = 0xB012,
	chunk_Track_Pivot       = 0xB013,
	chunk_BoundBox          = 0xB014,
	chunk_Morph_Smooth      = 0xB015,
	chunk_Track_Pos         = 0xB020,
	chunk_Track_Rotate      = 0xB021,
	chunk_Track_Scale       = 0xB022,
	chunk_Track_FOV         = 0xB023,
	chunk_Track_Roll        = 0xB024,
	chunk_Track_Color       = 0xB025,
	chunk_Track_Morph       = 0xB026,
	chunk_Track_Hotspot     = 0xB027,
	chunk_Track_Falloff     = 0xB028,
	chunk_Track_Hide        = 0xB029,
	chunk_Node_Id           = 0xB030,
	chunk_C_Magic           = 0xC23D
};

enum {
	Unknown = 0,
	Camera_Misc,
	Camera_Tar,
	Camera_Pos,
	Object_Pos
};

const C3DSLoader::TChunkName C3DSLoader::p_chunk_name_list[] = {
	{chunk_Version, false, Skip_Word},
	{chunk_Color_F, false, Read_Color_RGBF},
	{chunk_Color_24, false, Read_Color_RGB},
	{chunk_Lin_Color24, false, Read_Color_RGB},
	{chunk_Lin_Color_F, false, Read_Color_RGBF},
	{chunk_N_Percentage, false, Read_NPercentage},
	{chunk_F_Percentage, false, Read_FPercentage},
	{chunk_Master_Scale, false, Skip_Float},
	{chunk_C_Magic, true, 0},
	{chunk_MLib_Magic, true, 0},
	{chunk_Mesh_Version, false, Skip_Word},
	{chunk_Main_Magic, true, 0},
	{chunk_MeshData, true, 0},
	{chunk_Bkgnd_Color, true, 0},
	{chunk_Ambient_Color, true, 0},
	{chunk_Named_Object, true, Read_MeshName},
	{chunk_Triangle_Object, true, Read_TriObject},
	{chunk_Point_List, false, Read_VertexList},
	{chunk_Point_Flags, false, Read_VertexFlags},
	{chunk_Face_List, true, Read_FaceList},
	{chunk_Material_Group, false, Read_FaceMaterial},
	{chunk_Tex_VertexS, false, Read_MapList},
	{chunk_Smooth_Group, false, Read_SmoothingList},
	{chunk_Mesh_Matrix, false, Read_MeshMatrix},
	{chunk_Mesh_Color, false, Read_Skip},
	{chunk_Texture_Info, false, Read_Skip},
	{chunk_Mesh_BoxMap, false, 0},
	{chunk_Light, true, Read_Light},
	{chunk_SpotLight, false, Read_SpotLight},
	{chunk_Camera, false, Read_Camera},
	{chunk_Hierarchy, true, 0},
	{chunk_Viewport_Layout, false, Read_Skip},
	{chunk_Viewport_Size, false, Read_Skip},
	{chunk_Material, true, 0},
	{chunk_Material_Name, false, Read_MaterialName},
	{chunk_Material_Ambient, false/*true*/, Read_MaterialAmbient},
	{chunk_Material_Diffuse, false/*true*/, Read_MaterialDiffuse},
	{chunk_Material_Specular, false/*true*/, Read_MaterialSpecular},
	{chunk_Material_Shininess, true, 0},
	{chunk_Material_ShinStrength, true, 0},
	{chunk_Material_Transparency, true, 0},
	{chunk_Material_XpFall, true, 0},
	{chunk_Material_RefBlur, true, 0},
	{chunk_Material_SelfIllum, false/*true*/, Read_MaterialSelfIllum},
	{chunk_Material_DoubleSided, false, Read_Skip},
	{chunk_Material_TransAdd, false, Read_Skip},
	{chunk_Material_Wire_On, false, Read_Skip},
	{chunk_Material_WireSize, false, Skip_Float},
	{chunk_Material_Soften, false, Read_Skip},
	{chunk_Material_Shading, false, Skip_Word},
	{chunk_Material_Texture, true, Read_Skip},
	{chunk_Material_Reflect, true, Read_Skip},
	{chunk_Material_BumpMap, true, Read_Skip},
	{chunk_Material_BumpAmount, false, Skip_Word},
	{chunk_Material_MapName, false, Read_MapFile},
	{chunk_Material_MapFlags, false, Skip_Word},
	{chunk_Material_MapBlur, false, Skip_Float},
	{chunk_Material_MapUScale, false, Skip_Float},
	{chunk_Material_MapVScale, false, Skip_Float},
	{chunk_Material_MapUOffset, false, Skip_Float},
	{chunk_Material_MapVOffset, false, Skip_Float},
	{chunk_Material_MapRotAngle, false, Skip_Float},
	{chunk_KF_Data, true, 0},
	{chunk_Ambient_Key, true, 0},
	{chunk_Track_Info, true, Read_TrackInfo},
	{chunk_KF_Seg, false, Read_Frames},
	{chunk_Track_Node_Name, false, Read_TrackNodeName},
	{chunk_Instance_Name, false, 0},
	{chunk_Track_Pivot, false, Read_PivotPoint},
	{chunk_Track_Pos, false, Read_TrackPos},
	{chunk_Track_Color, false, Read_TrackColor},
	{chunk_Track_Rotate, false, Read_TrackRot},
	{chunk_Track_Scale, false, Read_TrackScale},
	{chunk_Track_Morph, false, 0},
	{chunk_Track_Hide, false, 0},
	{chunk_Node_Id, false, Read_NodeId},
	{chunk_Track_Camera, true, Read_TrackCamera},
	{chunk_Track_Cam_Target, true, Read_TrackCamTgt},
	{chunk_Track_Light, true, 0},
	{chunk_Track_Light_Target, true, 0},
	{chunk_Track_SpotLight, true, 0},
	{chunk_Track_FOV, false, Read_TrackFOV},
	{chunk_Track_Roll, false, Read_TrackRoll},
	{chunk_Light_In_Range, false, Read_LightInnerRange},
	{chunk_Light_Out_Range, false, Read_LightOuterRange},
	{chunk_Light_Multiplier, false, Read_LightMultiplier},
	{chunk_Light_Off, false, Read_LightOff},
	{chunk_Light_Shadowed, false, Read_LightShadowed},
};

bool C3DSLoader::Set_Object_Id(int n_index, int n_id, int n_parent_id)
{
	_ASSERTE(m_p_scene);

	m_p_scene->p_object[n_index].n_id = n_id;
	m_p_scene->p_object[n_index].n_parent = n_parent_id;

	return true;
}

int C3DSLoader::n_Object(const char *p_s_name) const
{
	_ASSERTE(m_p_scene);

	for(int i = 0; i < m_p_scene->n_object_num; i ++) {
		if(!strcmp(m_p_scene->p_object[i].p_s_name, p_s_name))
			return i;
	}
	return -1;
}

int C3DSLoader::n_Camera(const char *p_s_name) const
{
	_ASSERTE(m_p_scene);

	for(int i = 0; i < m_p_scene->n_camera_num; i ++) {
		if(!strcmp(m_p_scene->p_camera[i].p_s_name, p_s_name))
			return i;
	}
	return -1;
}

int C3DSLoader::n_Light(const char *p_s_name) const
{
	_ASSERTE(m_p_scene);

	for(int i = 0; i < m_p_scene->n_light_num; i ++) {
		if(!strcmp(m_p_scene->p_light[i].p_s_name, p_s_name))
			return i;
	}
	return -1;
}

int C3DSLoader::n_Material(const char *p_s_name) const
{
	_ASSERTE(m_p_scene);

	for(int i = 0; i < m_p_scene->n_material_num; i ++) {
		if(!strcmp(m_p_scene->p_material[i].p_s_name, p_s_name))
			return i;
	}
	return -1;
}

bool C3DSLoader::Read_CStr(char *p_s_name, FILE *p_fr)
{
	int i = 0, c = fgetc(p_fr);
	for(; c != EOF && c && i < 64 - 1; c = fgetc(p_fr))
		p_s_name[i ++] = c;
	p_s_name[i] = 0;

	if(i == 63)
		return true;
	return c != EOF;
}

bool C3DSLoader::Read_Skip(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	return true;
}

bool C3DSLoader::Skip_Float(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_tmp;
	return fread(&f_tmp, sizeof(float), 1, p_fr) == 1;
}

bool C3DSLoader::Skip_Word(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	short n_tmp;
	return fread(&n_tmp, sizeof(short), 1, p_fr) == 1;
}

bool C3DSLoader::Skip_DWord(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	int n_tmp;
	return fread(&n_tmp, sizeof(int), 1, p_fr) == 1;
}

bool C3DSLoader::Read_Color_RGB(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned char n_color[3];
	if(fread(n_color, sizeof(n_color), 1, p_fr) != 1)
		return false;

	p_this->m_v_color = Vector3f(n_color[0] / 255.0f, n_color[1] / 255.0f, n_color[2] / 255.0f);

	return true;
}

bool C3DSLoader::Read_Color_RGBF(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_color[3];
	if(fread(f_color, sizeof(f_color), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites) {
		int n_index;
		if((n_index = p_this->n_Light(p_this->m_p_s_name)) >= 0) {
			p_this->m_p_scene->p_light[n_index].v_color.x = f_color[0];
			p_this->m_p_scene->p_light[n_index].v_color.y = f_color[1];
			p_this->m_p_scene->p_light[n_index].v_color.z = f_color[2];
		}
	}

	return true;
}

bool C3DSLoader::Read_FPercentage(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	return fread(&p_this->m_f_percentage, sizeof(float), 1, p_fr) == 1;
}

bool C3DSLoader::Read_NPercentage(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	short n_tmp;
	if(fread(&n_tmp, sizeof(short), 1, p_fr) != 1)
		return false;

	p_this->m_f_percentage = n_tmp/* / 32767.0f*/;

	return true;
}

bool C3DSLoader::Read_Light(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_position[3];
	if(fread(f_position, sizeof(f_position), 1, p_fr) != 1)
		return false;

	if(p_this->m_b_count_entites)
		p_this->m_p_scene->n_light_num ++;
	else {
		TLight *p_light = p_this->m_p_scene->p_light + ++ p_this->m_n_light_index;

		if(!(p_light->p_s_name = new(std::nothrow) char[strlen(p_this->m_p_s_name) + 1]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(p_light->p_s_name, (strlen(p_this->m_p_s_name) + 1) * sizeof(char), p_this->m_p_s_name);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(p_light->p_s_name, p_this->m_p_s_name);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400

		p_light->v_position.x = f_position[0];
		p_light->v_position.z = f_position[1];
		p_light->v_position.y = f_position[2];

		p_light->b_spot = false;
	}

	return true;
}

bool C3DSLoader::Read_SpotLight(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_spot[5];
	if(fread(f_spot, sizeof(f_spot), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites) {
		TLight *p_light = p_this->m_p_scene->p_light + p_this->m_n_light_index;

		p_light->b_spot = true;
		p_light->v_target.x = f_spot[0];
		p_light->v_target.z = f_spot[1];
		p_light->v_target.y = f_spot[2];
		p_light->f_hotspot = f_spot[3];
		p_light->f_faloff = f_spot[4];
	}

	return true;
}

bool C3DSLoader::Read_LightInnerRange(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_param;
	if(fread(&f_param, sizeof(float), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_light[p_this->m_n_light_index].f_inner_range = f_param;

	return true;
}

bool C3DSLoader::Read_LightOuterRange(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_param;
	if(fread(&f_param, sizeof(float), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_light[p_this->m_n_light_index].f_outer_range = f_param;

	return true;
}

bool C3DSLoader::Read_LightMultiplier(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_param;
	if(fread(&f_param, sizeof(float), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_light[p_this->m_n_light_index].f_multiplier = f_param;

	return true;
}

bool C3DSLoader::Read_LightOff(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_light[p_this->m_n_light_index].b_off = true;

	return true;
}

bool C3DSLoader::Read_LightShadowed(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_light[p_this->m_n_light_index].b_cast_shadows = true;

	return true;
}

bool C3DSLoader::Read_MeshName(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	return Read_CStr(p_this->m_p_s_name, p_fr);
}

bool C3DSLoader::Read_MapFile(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	char p_s_tmp[64];
	return Read_CStr(p_s_tmp, p_fr);
}

bool C3DSLoader::Read_NodeId(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_id;
	if(fread(&n_id, sizeof(short), 1, p_fr) != 1)
		return false;

	p_this->m_n_node_object_index = n_id;

	return true;
}

bool C3DSLoader::Read_TriObject(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	if(p_this->m_b_count_entites)
		p_this->m_p_scene->n_object_num ++;
	else {
		TObject *p_object = p_this->m_p_scene->p_object + ++ p_this->m_n_object_index;
		if(!(p_object->p_s_name = new(std::nothrow) char[strlen(p_this->m_p_s_name) + 1]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(p_object->p_s_name, (strlen(p_this->m_p_s_name) + 1) * sizeof(char), p_this->m_p_s_name);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(p_object->p_s_name, p_this->m_p_s_name);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	}

	return true;
}

bool C3DSLoader::Read_VertexList(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_vertex_num;
	if(fread(&n_vertex_num, sizeof(short), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites) {
		TObject *p_object = p_this->m_p_scene->p_object + p_this->m_n_object_index;
		p_object->n_vertex_num = n_vertex_num;
		if(!(p_object->p_vertex = new(std::nothrow) TVertex[n_vertex_num]))
			return false;
	}

	for(int i = 0; i < n_vertex_num; i ++) {
		float f_pos[3];
		if(fread(f_pos, sizeof(f_pos), 1, p_fr) != 1)
			return false;
		if(!p_this->m_b_count_entites) {
			TObject *p_object = p_this->m_p_scene->p_object + p_this->m_n_object_index;
			p_object->p_vertex[i].v_pos.x = f_pos[0];
			p_object->p_vertex[i].v_pos.y = f_pos[2];
			p_object->p_vertex[i].v_pos.z = f_pos[1];
		}
	}

	return true;
}

bool C3DSLoader::Read_VertexFlags(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_flag_num;
	if(fread(&n_flag_num, sizeof(short), 1, p_fr) != 1)
		return false;

	while(n_flag_num -- > 0) {
		unsigned short n_flag;
		if(fread(&n_flag, sizeof(short), 1, p_fr) != 1)
			return false;
	}

	return true;
}

bool C3DSLoader::Read_FaceList(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_face_num;
	if(fread(&n_face_num, sizeof(short), 1, p_fr) != 1)
		return false;

	p_this->m_n_face_num = n_face_num;
	if(!p_this->m_b_count_entites) {
		TObject *p_object = p_this->m_p_scene->p_object + p_this->m_n_object_index;
		p_object->n_face_num = n_face_num;
		if(!(p_object->p_face = new(std::nothrow) TFace[n_face_num]))
			return false;
	}

	for(int i = 0; i < n_face_num; i ++) {
		unsigned short n_index[3], n_flags;
		if(fread(n_index, sizeof(n_index), 1, p_fr) != 1 ||
		   fread(&n_flags, sizeof(short), 1, p_fr) != 1)
			return false;

		if(!p_this->m_b_count_entites) {
			TObject *p_object = p_this->m_p_scene->p_object + p_this->m_n_object_index;
			p_object->p_face[i].p_vertex[0] = &p_object->p_vertex[n_index[0]];
			p_object->p_face[i].p_vertex[1] = &p_object->p_vertex[n_index[2]];
			p_object->p_face[i].p_vertex[2] = &p_object->p_vertex[n_index[1]];
		}
	}

	return true;
}

bool C3DSLoader::Read_FaceMaterial(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	char p_s_material_name[64];
	int n_material_id;
	if(!Read_CStr(p_s_material_name, p_fr) ||
	   (!p_this->m_b_count_entites && (n_material_id = p_this->n_Material(p_s_material_name)) == -1))
		return false;

	unsigned short n_face_num;
	if(fread(&n_face_num, sizeof(short), 1, p_fr) != 1)
		return false;

	for(int i = 0; i < n_face_num; i ++) {
		unsigned short n_index;
		if(fread(&n_index, sizeof(short), 1, p_fr) != 1)
			return false;

		if(!p_this->m_b_count_entites)
			p_this->m_p_scene->p_object[p_this->m_n_object_index].p_face[n_index].n_material = n_material_id;
	}

	return true;
}

bool C3DSLoader::Read_MapList(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_vertex_num;
	if(fread(&n_vertex_num, sizeof(short), 1, p_fr) != 1)
		return false;

	for(int i = 0; i < n_vertex_num; i ++) {
		float f_texcoord[2];
		if(fread(f_texcoord, sizeof(f_texcoord), 1, p_fr) != 1)
			return false;

		if(!p_this->m_b_count_entites) {
			TObject *p_object = p_this->m_p_scene->p_object + p_this->m_n_object_index;
			p_object->p_vertex[i].v_texcoord.x = f_texcoord[0];
			p_object->p_vertex[i].v_texcoord.y = f_texcoord[1];
		}
	}

	return true;
}

bool C3DSLoader::Read_SmoothingList(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	for(int i = 0; i < p_this->m_n_face_num; i ++) {
		unsigned short n_group;
		if(fread(&n_group, sizeof(short), 1, p_fr) != 1)
			return false;

		if(!p_this->m_b_count_entites)
			p_this->m_p_scene->p_object[p_this->m_n_object_index].p_face[i].n_group = n_group;
	}

	return true;
}

bool C3DSLoader::Read_MeshMatrix(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_matrix[4][3];
	if(fread(f_matrix, sizeof(f_matrix), 1, p_fr) != 1)
		return false;

	if(!p_this->m_b_count_entites) {
		TObject *p_object = p_this->m_p_scene->p_object + p_this->m_n_object_index;
		for(int i = 0; i < 4; i ++) {
			for(int j = 0; j < 3; j ++)
				p_object->t_matrix[i][j] = f_matrix[i][j];
		}
		for(int i = 0; i < 4; i ++)
			p_object->t_matrix[i][3] = i == 3;
	}

	return true;
}

bool C3DSLoader::Read_Camera(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	float f_param[8];
	if(fread(f_param, sizeof(f_param), 1, p_fr) != 1)
		return false;

	if(p_this->m_b_count_entites) 
		p_this->m_p_scene->n_camera_num ++; 
	else {
		TCamera *p_camera = p_this->m_p_scene->p_camera + ++ p_this->m_n_camera_index;

		p_camera->v_position.x = f_param[0];
		p_camera->v_position.y = f_param[2];
		p_camera->v_position.z = f_param[1];
		p_camera->v_target.x = f_param[3];
		p_camera->v_target.y = f_param[5];
		p_camera->v_target.z = f_param[4];
		p_camera->f_bank_angle = f_param[6];
		p_camera->f_focus = f_param[7];

		if(!(p_camera->p_s_name = new(std::nothrow) char[strlen(p_this->m_p_s_name) + 1]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(p_camera->p_s_name, (strlen(p_this->m_p_s_name) + 1) * sizeof(char), p_this->m_p_s_name);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(p_camera->p_s_name, p_this->m_p_s_name);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	}

	return true;
}

bool C3DSLoader::Read_MaterialAmbient(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	p_this->m_v_color = Vector3f(.75f, .75f, .75f);
	if(!p_this->Chunk_Read(p_fr, n_pos))
		return false;

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_material[p_this->m_n_material_index].v_ambient = p_this->m_v_color;

	return true;
}

bool C3DSLoader::Read_MaterialDiffuse(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	p_this->m_v_color = Vector3f(.75f, .75f, .75f);
	if(!p_this->Chunk_Read(p_fr, n_pos))
		return false;

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_material[p_this->m_n_material_index].v_diffuse = p_this->m_v_color;

	return true;
}

bool C3DSLoader::Read_MaterialSpecular(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	p_this->m_v_color = Vector3f(1.0f, 1.0f, 1.0f);
	if(!p_this->Chunk_Read(p_fr, n_pos))
		return false;

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_material[p_this->m_n_material_index].v_specular = p_this->m_v_color;

	return true;
}

bool C3DSLoader::Read_MaterialSelfIllum(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	if(!p_this->Chunk_Read(p_fr, n_pos))
		return false;
	// don't read color, read percentage

	if(!p_this->m_b_count_entites)
		p_this->m_p_scene->p_material[p_this->m_n_material_index].f_self_illum = p_this->m_f_percentage;

	return true;
}

bool C3DSLoader::Read_MaterialName(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	if(!Read_CStr(p_this->m_p_s_name, p_fr))
		return false;

	if(p_this->m_b_count_entites) {
		p_this->m_p_scene->n_material_num ++;
	} else {
		TMaterial *p_material = p_this->m_p_scene->p_material + ++ p_this->m_n_material_index;

		p_material->n_id = p_this->m_n_material_index;

		if(!(p_material->p_s_name = new(std::nothrow) char[strlen(p_this->m_p_s_name) + 1]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(p_material->p_s_name, (strlen(p_this->m_p_s_name) + 1) * sizeof(char), p_this->m_p_s_name);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(p_material->p_s_name, p_this->m_p_s_name);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	}

	return true;
}

bool C3DSLoader::Read_Frames(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned long n_frame[2]; 
	if(fread(n_frame, sizeof(n_frame), 1, p_fr) != 1)
		return false;

	p_this->m_p_scene->n_start_frame = n_frame[0];
	p_this->m_p_scene->n_end_frame = n_frame[1];

	return true;
}

bool C3DSLoader::Read_TrackNodeName(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	if(!Read_CStr(p_this->m_p_s_track_name, p_fr))
		return false;

	unsigned short n_parent;
	unsigned short n_skip[2];
	if(fread(n_skip, sizeof(n_skip), 1, p_fr) != 1 ||
	   fread(&n_parent, sizeof(short), 1, p_fr) != 1)
		return false;

	if(p_this->m_b_count_entites) 
		return true;
	if(p_this->m_n_track_type != Unknown) 
		p_this->m_n_element_index = p_this->n_Camera(p_this->m_p_s_track_name);
	else { 
		p_this->m_n_element_index = p_this->n_Object(p_this->m_p_s_track_name);
		p_this->Set_Object_Id(p_this->m_n_element_index, p_this->m_n_node_object_index, n_parent);
		p_this->m_n_track_type = Object_Pos;
	}

	return true;
}

bool C3DSLoader::Read_PivotPoint(C3DSLoader *p_this, FILE *p_fr, long n_pos) 
{
	return Skip_Float(p_this, p_fr, n_pos) &&
		Skip_Float(p_this, p_fr, n_pos) && Skip_Float(p_this, p_fr, n_pos);
}

bool C3DSLoader::Read_SplineFlags(FILE *p_fr, unsigned short &r_n_time,
	float &r_f_tens, float &r_f_cont, float &r_f_bias)
{
	unsigned short n_unknown, n_time, n_flag;
	if(fread(&n_time, sizeof(short), 1, p_fr) != 1 ||
	   fread(&n_unknown, sizeof(short), 1, p_fr) != 1 ||
	   fread(&n_flag, sizeof(short), 1, p_fr) != 1)
		return false;

	float f_tension, f_continuity, f_bias; 
	if((n_flag & 1) && fread(&f_tension, sizeof(float), 1, p_fr) != 1)
		return false;
	if((n_flag & 2) && fread(&f_continuity, sizeof(float), 1, p_fr) != 1)
		return false;
	if((n_flag & 4) && fread(&f_bias, sizeof(float), 1, p_fr) != 1)
		return false;

	r_n_time = n_time;
	r_f_tens = f_tension;
	r_f_cont = f_continuity;
	r_f_bias = r_f_bias;

	return true;
}

bool C3DSLoader::Read_TrackFlags(FILE *p_fr, unsigned short &r_n_frame_num)
{
	unsigned short n_flags[7];
	if(fread(n_flags, sizeof(n_flags), 1, p_fr) != 1)
		return false;

	r_n_frame_num = n_flags[5];

	return true;
}

bool C3DSLoader::Read_TrackPos(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_frame_num;
	if(!Read_TrackFlags(p_fr, n_frame_num))
		return false;

	if(!p_this->m_b_count_entites && p_this->m_n_element_index >= 0) {
		switch(p_this->m_n_track_type) {
		case Object_Pos:
			p_this->m_p_scene->p_object[p_this->m_n_element_index].n_pos_num = n_frame_num;
			if(!(p_this->m_p_scene->p_object[p_this->m_n_element_index].p_pos = new(std::nothrow) TPosFrame[n_frame_num]))
				return false;
			break;

		case Camera_Tar:
			p_this->m_p_scene->p_camera[p_this->m_n_element_index].n_target_num = n_frame_num;
			if(!(p_this->m_p_scene->p_camera[p_this->m_n_element_index].p_target = new(std::nothrow) TPosFrame[n_frame_num]))
				return false;
			break;

		case Camera_Pos:
			p_this->m_p_scene->p_camera[p_this->m_n_element_index].n_position_num = n_frame_num;
			if(!(p_this->m_p_scene->p_camera[p_this->m_n_element_index].p_position = new(std::nothrow) TPosFrame[n_frame_num]))
				return false;
			break;
		}
	}

	TPosFrame *p_cur_frame = 0;
	if(!p_this->m_b_count_entites && p_this->m_n_element_index >= 0) {
		switch(p_this->m_n_track_type) {
		case Camera_Pos:
			p_cur_frame = p_this->m_p_scene->p_camera[p_this->m_n_element_index].p_position;
			break;
		case Camera_Tar:
			p_cur_frame = p_this->m_p_scene->p_camera[p_this->m_n_element_index].p_target;
			break;
		case Object_Pos:
			p_cur_frame = p_this->m_p_scene->p_object[p_this->m_n_element_index].p_pos;
			break;
		}
	}

	while(n_frame_num -- > 0) {
		unsigned short n_time;
		float f_tension, f_continuity, f_bias;
		if(!Read_SplineFlags(p_fr, n_time, f_tension, f_continuity, f_bias)) 
			return false;

		float f_pos[3];
		if(fread(f_pos, sizeof(f_pos), 1, p_fr) != 1)
			return false;

		if(p_cur_frame) {
			p_cur_frame->f_time = n_time;
			p_cur_frame->v_position.x = f_pos[0];
			p_cur_frame->v_position.y = f_pos[1];
			p_cur_frame->v_position.z = f_pos[2];
			p_cur_frame->f_tension = f_tension;
			p_cur_frame->f_continuity = f_continuity;
			p_cur_frame->f_bias = f_bias;
			p_cur_frame ++;
		}
	}

	return true;
}

bool C3DSLoader::Read_TrackColor(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_track_num;
	if(!Read_TrackFlags(p_fr, n_track_num))
		return false;

	while(n_track_num -- > 0) {
		unsigned short n_tmp;
		float f_tmp[3];
		if(!Read_SplineFlags(p_fr, n_tmp, f_tmp[0], f_tmp[1], f_tmp[2]) ||
		   fread(f_tmp, sizeof(f_tmp), 1, p_fr) != 1)
			return false;
	}

	return true;
}

bool C3DSLoader::Read_TrackRot(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_frame_num;
	if(!Read_TrackFlags(p_fr, n_frame_num)) 
		return false;

	TRotFrame *p_cur_frame = 0;
	if(!p_this->m_b_count_entites && p_this->m_n_element_index >= 0 && p_this->m_n_track_type == Object_Pos) {
		p_this->m_p_scene->p_object[p_this->m_n_element_index].n_rot_num = n_frame_num;
		if(!(p_this->m_p_scene->p_object[p_this->m_n_element_index].p_rot = new(std::nothrow) TRotFrame[n_frame_num]))
			return false;
	}

	while(n_frame_num -- > 0) {
		unsigned short n_time;
		float f_tension, f_continuity, f_bias;
		float f_pos[4];
		if(!Read_SplineFlags(p_fr, n_time, f_tension, f_continuity, f_bias) ||
		   fread(f_pos, sizeof(f_pos), 1, p_fr) != 1) 
			return false;

		if(p_cur_frame) {
			p_cur_frame->f_time = n_time;
			p_cur_frame->t_rotation.x = f_pos[1];
			p_cur_frame->t_rotation.y = f_pos[3];
			p_cur_frame->t_rotation.z = f_pos[2];
			p_cur_frame->t_rotation.w = f_pos[0];
			p_cur_frame->f_tension = f_tension;
			p_cur_frame->f_continuity = f_continuity;
			p_cur_frame->f_bias = f_bias;
			p_cur_frame ++;
		}
	}

	return true;
}

bool C3DSLoader::Read_TrackFOV(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_frame_num;
	if(!Read_TrackFlags(p_fr, n_frame_num))
		return false;

	while(n_frame_num -- > 0) {
		unsigned short n_tmp;
		float f_tmp[3];
		if(!Read_SplineFlags(p_fr, n_tmp, f_tmp[0], f_tmp[1], f_tmp[2]) ||
		   fread(&f_tmp, sizeof(float), 1, p_fr) != 1)
			return false;
	}

	return true;
}

bool C3DSLoader::Read_TrackRoll(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_frame_num;
	if(!Read_TrackFlags(p_fr, n_frame_num))
		return false;

	while(n_frame_num -- > 0) {
		unsigned short n_tmp;
		float f_tmp[3];
		if(!Read_SplineFlags(p_fr, n_tmp, f_tmp[0], f_tmp[1], f_tmp[2]) ||
		   fread(&f_tmp, sizeof(float), 1, p_fr) != 1)
			return false;
	}

	return true;
}

bool C3DSLoader::Read_TrackScale(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	unsigned short n_frame_num;
	if(!Read_TrackFlags(p_fr, n_frame_num))
		return false;

	while(n_frame_num -- > 0) {
		unsigned short n_tmp;
		float f_tmp[3];
		if(!Read_SplineFlags(p_fr, n_tmp, f_tmp[0], f_tmp[1], f_tmp[2]) ||
		   fread(&f_tmp, sizeof(float), 1, p_fr) != 1)
			return false;
	}

	return true;
}

bool C3DSLoader::Read_TrackCamera(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	p_this->m_n_track_type = Camera_Pos;

	return p_this->Chunk_Read(p_fr, n_pos);
}

bool C3DSLoader::Read_TrackCamTgt(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	p_this->m_n_track_type = Camera_Tar;

	return p_this->Chunk_Read(p_fr, n_pos);
}

bool C3DSLoader::Read_TrackInfo(C3DSLoader *p_this, FILE *p_fr, long n_pos)
{
	p_this->m_n_track_type = Unknown;

	return p_this->Chunk_Read(p_fr, n_pos);
}

int C3DSLoader::n_Chunk(unsigned short n_id) const
{
	for(unsigned int i = 0; i < sizeof(p_chunk_name_list) / sizeof(p_chunk_name_list[0]); i ++) {
		if(n_id == p_chunk_name_list[i].n_id) 
			return i;
	}
	return -1;
}

bool C3DSLoader::Chunk_Read(FILE *p_fr, long n_pos)
{
	while(ftell(p_fr) < n_pos) {
		long n_cur_pos = ftell(p_fr);
		TChunkHeader t_header;
		fread(&t_header.n_id, sizeof(short), 1, p_fr);
		fread(&t_header.n_len, sizeof(long), 1, p_fr);

		if(!t_header.n_len)
			return false;

		int n_chunk_index;
		if((n_chunk_index = n_Chunk(t_header.n_id)) == -1)
			fseek(p_fr, n_cur_pos + t_header.n_len, SEEK_SET);
		else {
			n_cur_pos += t_header.n_len;
			if(p_chunk_name_list[n_chunk_index].function != NULL) {
				if(!p_chunk_name_list[n_chunk_index].function(this, p_fr, n_cur_pos))
					return false;
			}
			if(p_chunk_name_list[n_chunk_index].b_subchunk) {
				if(!Chunk_Read(p_fr, n_cur_pos))
					return false;
			}
			fseek(p_fr, n_cur_pos, SEEK_SET);
		}
		if(ferror(p_fr))
			return false;
	}

	return true;
}

TScene *C3DSLoader::p_Load(const char *p_s_filename)
{
	_ASSERTE(!m_p_scene); // ...

	if(!(m_p_scene = new(std::nothrow) TScene()))
		return 0;
	// alloc a new scene object

	FILE *p_fr;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	if(fopen_s(&p_fr, p_s_filename, "rb")) {
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	if((p_fr = fopen(p_s_filename, "rb")) == NULL) {
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		delete m_p_scene;
		return 0;
	}
	// open file

	long n_file_size;
	fseek(p_fr, 0, SEEK_END);
	n_file_size = ftell(p_fr);
	fseek(p_fr, 0, SEEK_SET);
	// get file size

	m_b_count_entites = true;
	if(!Chunk_Read(p_fr, n_file_size)) {
		delete m_p_scene;
		fclose(p_fr);
		return 0;
	}
	// count entities in the first pass ...

	if(m_p_scene->n_material_num && !(m_p_scene->p_material = new(std::nothrow) TMaterial[m_p_scene->n_material_num]) ||
	   m_p_scene->n_object_num && !(m_p_scene->p_object = new(std::nothrow) TObject[m_p_scene->n_object_num]) ||
	   m_p_scene->n_camera_num && !(m_p_scene->p_camera = new(std::nothrow) TCamera[m_p_scene->n_camera_num]) ||
	   m_p_scene->n_light_num && !(m_p_scene->p_light = new(std::nothrow) TLight[m_p_scene->n_light_num])) {
		delete m_p_scene;
		fclose(p_fr);
		return 0;
	}
	// alloc entities

	m_n_object_index = -1;
	m_n_camera_index = -1;
	m_n_material_index = -1;
	m_n_light_index = -1;
	// reset sounters

	fseek(p_fr, 0, SEEK_SET);
	m_b_count_entites = false;
	if(!Chunk_Read(p_fr, n_file_size)) {
		delete m_p_scene;
		fclose(p_fr);
		return 0;
	}
	// read entities in the second pass

	fclose(p_fr);
	// close file

	for(int i = 0; i < m_p_scene->n_object_num; i ++) {
		m_p_scene->p_object[i].Calc_FaceNormals();
		m_p_scene->p_object[i].Calc_VertexNormals();
	}
	// calculate vertex and face normals

	for(int i = 0; i < m_p_scene->n_camera_num; i ++)
		m_p_scene->p_camera[i].CalcMatrix();
	// calculate camera matrices

	TScene *p_world = m_p_scene;
	m_p_scene = 0;
	return p_world;
	// returns loaded scene
}

/*
 *								=== ~C3DSLoader ===
 */

/*
 *								=== TScene ===
 */

TScene::TScene()
	:n_start_frame(0), n_end_frame(0), n_object_num(0), n_camera_num(0), n_light_num(0),
	n_material_num(0), p_object(0), p_camera(0), p_light(0), p_material(0)
{
}

TScene::~TScene()
{
	/*n_start_frame = 0;
	n_end_frame = 0;*/
	if(p_object) {
		for(TObject *p_it = p_object, *p_end = p_object + n_object_num; p_it < p_end; p_it ++) {
			delete[] p_it->p_face;
			if(p_it->p_pos)
				delete[] p_it->p_pos;
			if(p_it->p_rot)
				delete[] p_it->p_rot;
			delete[] p_it->p_s_name;
			delete[] p_it->p_vertex;
		}
		delete[] p_object;
		//p_object = 0;
	}
	//n_object_num = 0;
	if(p_material) {
		for(TMaterial *p_it = p_material, *p_end = p_material + n_material_num;
		   p_it < p_end; p_it ++)
			delete[] p_it->p_s_name;
		delete[] p_material;
		//p_material = 0;
	}
	//n_material_num = 0;
	if(p_camera) {
		for(TCamera *p_it = p_camera, *p_end = p_camera + n_camera_num; p_it < p_end; p_it ++) {
			delete[] p_it->p_s_name;
			if(p_it->p_position)
				delete[] p_it->p_position;
			if(p_it->p_target)
				delete[] p_it->p_target;
		}
		delete[] p_camera;
		//p_camera = 0;
	}
	//n_camera_num = 0;
	if(p_light) {
		for(TLight *p_it = p_light, *p_end = p_light + n_light_num; p_it < p_end; p_it ++)
			delete[] p_it->p_s_name;
		delete[] p_light;
		//p_light = 0;
	}
	//n_light_num = 0;
}

TScene *TScene::p_Load(const char *p_s_file_name)
{
	C3DSLoader L;
	return L.p_Load(p_s_file_name);
}

/*
 *								=== ~TScene ===
 */
