// Copyright (C) 2004-2009 Christoph Kubisch & Eike Decker
// This file is part of the "Luxinia Engine".
// For conditions of distribution and use, see copyright notice in luxiniapublicsdk.txt

#ifndef __EXMODEL_H__
#define __EXMODEL_H__

//////////////////////////////////////////////////////////////////////////
// EXModel
//
// Library for 3d model/animation asset exchange
// Author: Christoph Kubisch


#if defined(__cplusplus)
extern "C"
{
#endif

#define EXMODEL_NOID			0xffffffff
#define EXMODEL_NONAME			""
#define EXMODEL_MAXUVWS			4
#define EXMODEL_MAXWEIGHTS		4

struct EXModel;
struct EXMesh;
struct EXVertex;
struct EXIndex;
struct EXPrimitiveGroup;
struct EXSkin;
struct EXSkinWeight;
struct EXSkinVertex;
struct EXMaterial;
struct EXAnim;
struct EXTrack;
struct EXPRSKey;
struct EXOutBuffer;

// MAIN
typedef struct EXModel			*EXModelPTR;
typedef struct EXAnim			*EXAnimPTR;
typedef struct EXOutBuffer		*EXOutBufferPTR;

// GEOMETRY
typedef struct EXMesh			*EXMeshPTR;
typedef struct EXSkin			*EXSkinPTR;
typedef struct EXMaterial		*EXMaterialPTR;

	// only valid between provides/adds/gets
typedef struct EXVertex			*EXVertexPTR;
typedef struct EXIndex			*EXIndexPTR;
typedef struct EXPrimitiveGroup	*EXPrimitiveGroupPTR;
typedef struct EXSkinVertex		*EXSkinVertexPTR;
typedef struct EXSkinWeight		*EXSkinWeightPTR;


typedef unsigned int uint;
typedef unsigned int booln;


// ANIMATION
typedef struct EXTrack	*EXTrackPTR;

	// only valid between provides/adds/gets
typedef struct EXPRSKey	*EXPRSKeyPTR;

//////////////////////////////////////////////////////////////////////////
typedef enum EXPrimitiveType_e{
	EXPRIM_POINTS,
	EXPRIM_TRIANGLE_LIST,
	EXPRIM_TRIANGLE_STRIP,
	EXPRIM_QUAD_LIST,
	EXPRIM_QUAD_STRIP,
	EXPRIMS,
	EXPRIM_UNSET = 0xFFFF,
}EXPrimitiveType;

typedef enum EXMeshType_e{
		// vertices, indices, material and spatial info (may have skin)
	EXMESH_MESH,
		// vertices & indices shared, but own material/spatial
	EXMESH_INST_SHRD_VERT_TRIS,
		// no geometry, only spatial node
	EXMESH_HELPER,

	EXMESHS,
	EXMESH_DEPRECATED	= 0xDD,
	EXMESH_UNSET	= 0xFF,
}EXMeshType;

typedef enum EXTransformState_e{
	EXTRANS_POS = 1,
	EXTRANS_ROT = 2,
	EXTRANS_SCALE = 4,
	EXTRANS_MASK = 7,
}EXTransformState;

typedef enum EXModelOutType_e{
	EXMODEL_OUT_LUXF3D,
	EXMODEL_OUTS,
}EXModelOutType;

typedef enum EXAnimOutType_e{
	EXANIM_OUT_LUXMA,
	EXANIM_OUTS,
}EXAnimOutType;

//////////////////////////////////////////////////////////////////////////
// Content Generation
//
// NEVER MIX PTRS OF DIFFERENT MODELS/ANIMS!!!
// All allocations are performed within a model/anim and its
// free function will delete all its resources.


/*
	Animation
	contains multiple tracks, each track contains multiple 
	PRSKeys (position rot scale). Each key has a certain timevalue
	associated and a bitflag that defines which data (pos, rot , scale)
	is in use.

*/

// EXAnimPTR
EXAnimPTR		EXAnim_new				(const char *name);
void			EXAnim_delete			(EXAnimPTR imodel);
EXTrackPTR		EXAnim_provideTrack	 	(EXAnimPTR imodel, const char *name, uint id);
uint			EXAnim_getTrackCount	(EXAnimPTR imodel);
EXTrackPTR		EXAnim_getTrack			(EXAnimPTR imodel, uint index);

// EXTrackPTR
void			EXTrack_setName		(EXTrackPTR imesh, const char* name);
const char*		EXTrack_getName		(EXTrackPTR imesh);
uint			EXTrack_getID		(EXTrackPTR imesh);
void			EXTrack_setLength	(EXTrackPTR imesh, float time);
float			EXTrack_getLength	(EXTrackPTR imesh);

uint			EXTrack_getKeyCount	(EXTrackPTR imesh);
EXPRSKeyPTR		EXTrack_getKey		(EXTrackPTR imesh, uint index);

EXPRSKeyPTR		EXTrack_provideKey	(EXTrackPTR imesh, uint atindex); 
EXPRSKeyPTR		EXTrack_addKey		(EXTrackPTR imesh);

// EXPRSKeyPTR
void			EXPRSKey_setTime		(EXPRSKeyPTR imesh, float x);
float			EXPRSKey_getTime		(EXPRSKeyPTR imesh);
void			EXPRSKey_setUsedFlags	(EXPRSKeyPTR imesh, uint states);
uint			EXPRSKey_getUsedFlags	(EXPRSKeyPTR imesh);

void			EXPRSKey_setPosition	(EXPRSKeyPTR imesh, float x, float y, float z);
float*			EXPRSKey_getPosition	(EXPRSKeyPTR imesh);
void			EXPRSKey_setRotation	(EXPRSKeyPTR imesh, float x, float y, float z, float r);
float*			EXPRSKey_getRotation	(EXPRSKeyPTR imesh);
void			EXPRSKey_setScale		(EXPRSKeyPTR imesh, float x, float y, float z);
float*			EXPRSKey_getScale		(EXPRSKeyPTR imesh);

/*
	Model:
	Consists of Meshes (or nodes, can be helpers, ie without geometry)
	Materials (have a shadername) and Skins (contain data about
	skeletal deformation of vertices)

*/


// EXModelPTR
EXModelPTR		EXModel_new		(const char *name);
void			EXModel_delete	(EXModelPTR imodel);
EXMeshPTR		EXModel_provideMesh		(EXModelPTR model, const char *name, uint id);
EXSkinPTR		EXModel_provideSkin		(EXModelPTR model, const char *name, uint id);
EXMaterialPTR	EXModel_provideMaterial (EXModelPTR model, const char *name, uint id);

EXMeshPTR		EXModel_provideMeshParent	(EXModelPTR model, EXMeshPTR imesh, const char *name, uint id);
EXMaterialPTR	EXModel_provideMeshMaterial	(EXModelPTR model, EXMeshPTR imesh, const char *name, uint id);
EXMeshPTR		EXModel_provideMeshInstance	(EXModelPTR model, EXMeshPTR imesh, const char *name, uint id);
EXMaterialPTR	EXModel_provideMaterialSub	(EXModelPTR model, EXMaterialPTR imaterial, uint atindex, const char *name, uint id);
EXMeshPTR		EXModel_provideSkinMesh		(EXModelPTR model, EXSkinPTR iskin, const char *name, uint id);
EXMeshPTR		EXModel_provideSkinMeshParent(EXModelPTR model, EXSkinPTR iskin, uint atindex, const char *name, uint id);

uint			EXModel_getMeshCount	(EXModelPTR imodel);
uint			EXModel_getMeshTypeCount(EXModelPTR imodel, EXMeshType type);
uint			EXModel_getMaterialCount(EXModelPTR imodel);
uint			EXModel_getSkinCount	(EXModelPTR imodel);

EXMeshPTR		EXModel_getMesh			(EXModelPTR imodel, uint index);
EXMaterialPTR	EXModel_getMaterial		(EXModelPTR imodel, uint index);
EXSkinPTR		EXModel_getSkin			(EXModelPTR imodel, uint index);


/*
	Meshes can create a hierarchy and can instance each other's geometry.
	Every mesh can have multiple primitive groups (sets of indices).

	Vertices are allocated in a big chunk, their output order
	may change during processing and is represented by vertexOrder.
	VertexID is used to mark several vertices belonging togehter,
	mostly used by Skins, which dont already have same skinvertex count
	as mesh.

	Skins can either have a dedicated list of which meshes/bones are used,
	then each weight has a index into that list. Or you can link to the
	mesh/bone directly per weight. Then the parent list can be ignored empty.

	After the main "validation", you can use the "ordered" vertices to get data
	of the actual in-use vertex data. As certain operations might result
	into original vertices not being used (indexed), or additional
	to be added.
*/

// EXMeshPTR
void			EXMesh_setType			(EXMeshPTR imesh, EXMeshType type);
EXMeshType		EXMesh_getType			(EXMeshPTR imesh);
void			EXMesh_setName			(EXMeshPTR imesh, const char* name);
const char*		EXMesh_getName			(EXMeshPTR imesh);
uint			EXMesh_getID			(EXMeshPTR imesh);
void			EXMesh_setInstance		(EXMeshPTR imesh, EXMeshPTR instanceof);
EXMeshPTR		EXMesh_getInstance		(EXMeshPTR imesh);
void			EXMesh_setMaterial		(EXMeshPTR imesh, EXMaterialPTR imat);
EXMaterialPTR	EXMesh_getMaterial		(EXMeshPTR imesh);
void			EXMesh_setSkin			(EXMeshPTR imesh, EXSkinPTR iskin);
EXSkinPTR		EXMesh_getSkin			(EXMeshPTR imesh);
void			EXMesh_setParent		(EXMeshPTR imesh, EXMeshPTR parent);
EXMeshPTR		EXMesh_getParent		(EXMeshPTR imesh);
void			EXMesh_setNoTangentSpace(EXMeshPTR imesh, booln state);
booln			EXMesh_getNoTangentSpace(EXMeshPTR imesh);
void			EXMesh_setNoLocalMirrors(EXMeshPTR imesh, booln state);
booln			EXMesh_getNoLocalMirrors(EXMeshPTR imesh);

void			EXMesh_setPosition		(EXMeshPTR imesh, float x, float y, float z);
float*			EXMesh_getPosition		(EXMeshPTR imesh);
void			EXMesh_setRotation		(EXMeshPTR imesh, float x, float y, float z, float r);
float*			EXMesh_getRotation		(EXMeshPTR imesh);
void			EXMesh_setScale			(EXMeshPTR imesh, float x, float y, float z);
float*			EXMesh_getScale			(EXMeshPTR imesh);
void			EXMesh_setMatrix		(EXMeshPTR imesh, const float matrix44[16]);
float*			EXMesh_getMatrix		(EXMeshPTR imesh);

uint				EXMesh_getIndexTotalCount	(EXMeshPTR imesh);
void				EXMesh_setPrimitiveType		(EXMeshPTR imesh, EXPrimitiveType primType);
EXPrimitiveType		EXMesh_getPrimitiveType		(EXMeshPTR imesh);
uint				EXMesh_getPrimitiveGroupCount(EXMeshPTR imesh);
EXPrimitiveGroupPTR EXMesh_getPrimitiveGroup	(EXMeshPTR imesh, uint atindex);

EXPrimitiveGroupPTR	EXMesh_providePrimitiveGroup(EXMeshPTR imesh, uint atindex);
EXPrimitiveGroupPTR	EXMesh_addPrimitiveGroup	(EXMeshPTR imesh);

//EXPrimitiveGroupPTR
uint			EXPrimitiveGroup_getIndicesCount(EXPrimitiveGroupPTR iprimgrp);
EXIndexPTR		EXPrimitiveGroup_getIndex		(EXPrimitiveGroupPTR iprimgrp, uint atindex);
EXIndexPTR		EXPrimitiveGroup_provideIndex	(EXPrimitiveGroupPTR iprimgrp, uint atindex);
EXIndexPTR		EXPrimitiveGroup_addIndex		(EXPrimitiveGroupPTR iprimgrp); 


//EXIndexPTR
void			EXIndex_setIndexAttribs			(EXIndexPTR iprim, uint ID, uint vertexIDX);
uint			EXIndex_getID					(EXIndexPTR iprim);
uint			EXIndex_getVertexIDX			(EXIndexPTR iprim);
	// routed to vertex host if instanced, set after validate
EXVertexPTR		EXIndex_getVertex				(EXIndexPTR iprim);	
int				EXIndex_getVertexOrderIDX		(EXIndexPTR iprim);

//EXMeshPTR (vertex)

	// original order & count, as it was fed prior any validation and additonal ops
uint			EXMesh_getVertexCount		(EXMeshPTR imesh);
EXVertexPTR		EXMesh_getVertex			(EXMeshPTR imesh, uint index);
	// reoredered output vertices after ops/validation
EXVertexPTR		EXMesh_getVertexOrdered		(EXMeshPTR imesh, uint index);
uint			EXMesh_getVertexOrderCount	(EXMeshPTR imesh);
booln			EXMesh_hasValidVertexOrder	(EXMeshPTR imesh);
void			EXMesh_setUsedUVWCount		(EXMeshPTR imesh, uint uvcount);
uint			EXMesh_getUsedUVWCount		(EXMeshPTR imesh);
EXVertexPTR		EXMesh_provideVertex		(EXMeshPTR imesh, uint atindex); 
EXVertexPTR		EXMesh_addVertex			(EXMeshPTR imesh); 

//EXVertexPTR
int				EXVertex_getOrder		(EXVertexPTR ivert);
void			EXVertex_setPosition	(EXVertexPTR vertex, float x, float y, float z);
float*			EXVertex_getPosition	(EXVertexPTR ivert);
void			EXVertex_setNormal		(EXVertexPTR vertex, float x, float y, float z);
float*			EXVertex_getNormal		(EXVertexPTR ivert);
void			EXVertex_setColor		(EXVertexPTR vertex, float r, float g, float b, float a);
float*			EXVertex_getColor		(EXVertexPTR ivert);
void			EXVertex_setTangent		(EXVertexPTR vertex, float x, float y, float z);
float*			EXVertex_getTangent		(EXVertexPTR ivert);
void			EXVertex_setBinormal	(EXVertexPTR vertex, float x, float y, float z);
float*			EXVertex_getBinormal	(EXVertexPTR ivert);
void			EXVertex_setID			(EXVertexPTR vertex, int vertexID);
int				EXVertex_getID			(EXVertexPTR ivert);
void			EXVertex_setUVW			(EXVertexPTR vertex, uint uvIndex, float u, float v, float w, float r);
float*			EXVertex_getUVW			(EXVertexPTR ivert, uint uvIndex);


// EXMaterialPTR
uint			EXMaterial_getID			(EXMaterialPTR imat);
void			EXMaterial_setName			(EXMaterialPTR imat, const char *name);
const char*		EXMaterial_getName			(EXMaterialPTR imat);
uint			EXMaterial_getSubCount		(EXMaterialPTR imat);
EXMaterialPTR	EXMaterial_getSubMaterial	(EXMaterialPTR imat, uint index);

void			EXMaterial_setShaderName	(EXMaterialPTR imat,const char* name);
const char*		EXMaterial_getShaderName	(EXMaterialPTR imat);


// EXSkinPTR
uint			EXSkin_getID			(EXSkinPTR iskin);
const char*		EXSkin_getName			(EXSkinPTR iskin);
EXMeshPTR		EXSkin_getMesh			(EXSkinPTR iskin);
void			EXSkin_setMesh			(EXSkinPTR iskin, EXMeshPTR imesh);
booln			EXSkin_hasValidVertexOrder(EXSkinPTR iskin);

void			EXSkin_setUsedWeightCount(EXSkinPTR iskin, uint max);
uint			EXSkin_getUsedWeightCount(EXSkinPTR iskin);

uint			EXSkin_getParentsCount	(EXSkinPTR iskin);
void			EXSkin_setParentMesh	(EXSkinPTR iskin, uint subIDX, EXMeshPTR mesh);
EXMeshPTR		EXSkin_getParentMesh	(EXSkinPTR iskin, uint subIDX);
uint			EXSkin_getVertexCount	(EXSkinPTR iskin);
EXSkinVertexPTR	EXSkin_getVertex		(EXSkinPTR iskin, uint index);
	// using target mesh's order
uint			EXSkin_getVertexOrderCount(EXSkinPTR iskin);
EXSkinVertexPTR	EXSkin_getVertexOrdered	(EXSkinPTR iskin, uint index);

EXSkinVertexPTR	EXSkin_provideSkinVertex(EXSkinPTR iskin, uint atindex);
EXSkinVertexPTR	EXSkin_addSkinVertex	(EXSkinPTR iskin);

// EXSkinVertexPTR

	// must specify vertexID if (skinvertex count < mesh count)
	// otherwise we assume target vertexIDX to be same skinvertexIDX
	// after validation skinvertex count == mesh count
void			EXSkinVertex_setVertexID	(EXSkinVertexPTR isvert, int vertexID);
int				EXSkinVertex_getVertexID	(EXSkinVertexPTR isvert);
int				EXSkinVertex_getVertexIDX	(EXSkinVertexPTR isvert);

uint			EXSkinVertex_getWeightsCount(EXSkinVertexPTR isvert);
EXSkinWeightPTR	EXSkinVertex_getWeight		(EXSkinVertexPTR isvert, uint index);
EXSkinWeightPTR	EXSkinVertex_addWeight		(EXSkinVertexPTR isvert, float weight, 
												int parentIDX, EXMeshPTR parent);
// EXSkinWeightPTR
float			EXSkinWeight_getWeight		(EXSkinWeightPTR isw);
EXMeshPTR		EXSkinWeight_getParent		(EXSkinWeightPTR isw);
int				EXSkinWeight_getParentIDX	(EXSkinWeightPTR isw);



//////////////////////////////////////////////////////////////////////////
// functions to restructure model or report errors

//////////////////////////////////////////////////////////////////////////
// Generic Base Operations

		// returns error message if a fix fails
const char *EXModel_lastError(EXModelPTR imodel);
const char *EXAnim_lastError(EXAnimPTR imodel);


	// rebuild mat/mesh/skin IDs
	// ids start at 1
	// linearly fills up ids based on position in the list
	// meshes are filled sequentially by their type
void	EXModel_rebuildIDs(EXModelPTR imodel);
void	EXAnim_rebuldIDs(EXAnimPTR imodel);

	// basic error checking and linking
	// vertex order will be validated
	// all PTRs will be set, skin will have
	// per vertex weights and parent lists
	// multi-material meshes will be split to multiple meshes
	// unused vertices/skinvertices will be removed (via order)
booln	EXModel_validate(EXModelPTR imodel);	
	// sorts and packs tracks (potentially reduces keycount)
void	EXAnim_validate(EXAnimPTR imodel);


//////////////////////////////////////////////////////////////////////////
// Additional Operations (assume validation to be run once)

	// generates keys with all PRS states, used flag remains original
	// spline & loop ignored at the moment
void	EXAnim_allStatesPerKey		(EXAnimPTR imodel, booln spline, booln loop);

	// detects and removes identical vertices (via order)
void	EXModel_removeIdenticalVertices(EXModelPTR imodel);

	// changes orientation
void	EXModel_swizzle(EXModelPTR imodel,
					  uint axis[3],float dir[4], booln reversetris, booln flipnormals);
void	EXAnim_swizzle(EXAnimPTR imodel,
						uint axis[3],float dir[4], booln reversetris, booln flipnormals);

//////////////////////////////////////////////////////////////////////////
// these operations should be performed last

	// generates per-vertex mirror normals to detect mirrored geometry
	// only for UV charts outside 0-1 (shifts them into 0,1 afterwards)
	// stores result in tangent vector or in normal
booln	EXModel_localmirrorsLUX(EXModelPTR imodel, uint uvwchannel, booln asnormals);


//////////////////////////////////////////////////////////////////////////
// Mesh Optimizers
// generates vertexcache optimized triangle lists or triangle strips
// primID of triangle indices is lost

	// only reorders vertexdata based on triangle order
void EXModel_optimizeONLYREORDER(EXModel *imodel);

	// using tipsify algorithm (ati/amd)
	// supports only tri-lists, outputs tri-lists
void	EXModel_optimizeTIPSIFY(EXModelPTR imodel, uint cachesize);

	// using Tom Forsyth's algorithm (radgametools/intel)
	// supports only tri-lists, outputs tri-lists
void	EXModel_optimizeFORSYTH(EXModelPTR imodel, uint cachesize);


//////////////////////////////////////////////////////////////////////////
// Output Conversion
EXOutBufferPTR	EXOutBuffer_new();
void			EXOutBuffer_delete(EXOutBufferPTR buffer);
size_t			EXOutBuffer_getSize(EXOutBufferPTR buffer);
const char*		EXOutBuffer_getContent(EXOutBufferPTR buffer);

booln			EXModel_toOutBuffer(EXModelPTR imodel, EXModelOutType type, EXOutBufferPTR outbuffer);
booln			EXAnim_toOutBuffer(EXAnimPTR ianim, EXAnimOutType type, EXOutBufferPTR outbuffer);

booln			EXModel_toFile(EXModelPTR imodel, EXModelOutType type, const char *filename);
booln			EXAnim_toFile(EXAnimPTR ianim, EXAnimOutType type, const char *filename);


#if defined(__cplusplus)
}
#endif


#ifdef __cplusplus
#include <string>
std::string		EXOutBuffer_getContentString(EXOutBufferPTR buffer);

#endif


#endif

