#include "dexibuild.hpp"
#include "deBitmap.hpp"
#include "deSurface.hpp"
#include "deRenderStates.hpp"
#include "deMesh.hpp"

typedef std::map<deString2, IdeBitmapProxy*> bmpMap_t;
typedef std::map<deString2, IdeSurface*> sfcMap_t;
typedef std::map<deString2, dexiVertexSet*> vtxMap_t;
typedef std::map<deString2, dexiTriMesh*> meshMap_t;
typedef std::map<deString2, IdeSceneLight*> lightMap_t;
void loadBitmaps(bmpMap_t& bmpMap, TiXmlElement* bitmaps);
void loadSurfaces(const bmpMap_t& bmpMap, sfcMap_t& sfcMap, TiXmlElement* surfaces);
void loadVertices(vtxMap_t& vtxMap, TiXmlElement* vertices);
void loadTrimesh(const vtxMap_t& vtxMap, const sfcMap_t& sfcMap, meshMap_t& meshMap, TiXmlElement* meshdef);
void loadScene(sfcMap_t& sfcMap, vtxMap_t& vtxMap, meshMap_t& meshMap, dexiScene& scene, TiXmlElement* scenedef);

void cleanupContainers(bmpMap_t& bmpMap, sfcMap_t& sfcMap, vtxMap_t& vtxMap, meshMap_t& meshMap, dexiScene& scene);

// main elements needed:
// bitmaps, surfaces, vertices, trimeshdef, scenedef
void buildDWORLD(TiXmlElement* pRoot, deString2 inputFileName)
{
	TiXmlElement* elem = NULL;

	bmpMap_t bmpMap;
	sfcMap_t sfcMap;
	vtxMap_t vtxMap;
	meshMap_t meshMap;
	dexiScene scene;

	try
	{
		// only one bitmaps element
		elem = pRoot->FirstChildElement("bitmaps");
		loadBitmaps(bmpMap, elem);
		// only one surfaces element
		elem = pRoot->FirstChildElement("surfaces");
		loadSurfaces(bmpMap, sfcMap, elem);
		elem = pRoot->FirstChildElement("vertices");
		loadVertices(vtxMap, elem);
		elem = pRoot->FirstChildElement("meshes");
		// multiple trimeshdef elements
		for(elem = elem->FirstChildElement("trimeshdef");
			elem != NULL;
			elem = elem->NextSiblingElement("trimeshdef"))
		{
			loadTrimesh(vtxMap, sfcMap, meshMap, elem);
		}
		elem = pRoot->FirstChildElement("scenedef");
		loadScene(sfcMap, vtxMap, meshMap, scene, elem);
	}
	catch (dexiException e)
	{
		cleanupContainers(bmpMap, sfcMap, vtxMap, meshMap, scene);
		throw e;
	}

#if 1
	IdeFileSystem* pFS = NULL;
	IdeFile* pOutFile = NULL;
	IdeWorld* pWorld = NULL;
	IdeSceneRoom * pRoom = NULL;
	IdeSceneABT * pABT = NULL;
	IdeRenderTexture * pDefaultTex = NULL;
	try
	{
		deString2 outputFileName;
		int dot = inputFileName.Find('.', 0, deString2::search_backwards);
		if (dot >= 0)
			outputFileName = inputFileName.SubString(0, dot);
		else
			outputFileName = inputFileName;
		outputFileName += ".dworld";
		IdeWorldManager * manager = IdeWorld_GetManager();
		if (!manager)
		{
			throw dexiException("Error getting world manager\n");
		}
		deWorldID worldID = manager->CreateWorld();
		if (worldID == 0)
		{
			throw dexiException("Error creating world\n");
		}

		bmpMap_t::iterator bmpIt;
		sfcMap_t::iterator sfcIt;
		for (bmpIt = bmpMap.begin(); bmpIt != bmpMap.end(); ++bmpIt)
		{
			IdeBitmapProxy* proxy = bmpIt->second;
			if (proxy)
				proxy->SetWorldID(worldID);
		}
		for (sfcIt = sfcMap.begin(); sfcIt != sfcMap.end(); ++sfcIt)
		{
			IdeSurface* surface = sfcIt->second;
			if (surface)
			{
				surface->SetWorldID(worldID);
				IdeRenderTexture* tex;
				IdeRenderMaterial* mat;
				tex = surface->GetRenderTexture();
				if (tex)
				{
					tex->SetWorldID(worldID);
					int k;
					for (k = 0; k < TEXTURESTAGE_COUNT; ++k)
					{
						IdeBitmapProxy* proxy = tex->GetBitmap(k);
						if (proxy)
						{
							proxy->SettingSavePixels(deTRUE);
							proxy->SetWorldID(worldID);
						}
					}
				}
				mat = surface->GetRenderMaterial();
				if (mat)
					mat->SetWorldID(worldID);
			}
		}
		meshMap_t::iterator meshIt;
		for (meshIt = meshMap.begin(); meshIt != meshMap.end(); ++meshIt)
		{
			dexiTriMesh* mesh = meshIt->second;
			if (mesh->instance_owner && mesh->instance_template)
				mesh->instance_template->SetWorldID(worldID);
		}

		// create the scene-specific entries
		int k;
		IdeSceneGraph* pSG = IdeSceneGraph_GetSceneGraphPtr();
		pRoom = IdeSceneRoom_CreateRoom();
		pRoom->SetWorldID(worldID);
		pABT = IdeSceneObject_CreateSceneABT();
		pABT->SetWorldID(worldID);
		for (k = 0; k < scene.instance_meshes.size(); ++k)
		{
			scene.instance_meshes[k]->SetWorldID(worldID);
			pSG->AddSceneObject(scene.instance_meshes[k]);
			pRoom->AddSceneObject(scene.instance_meshes[k]); // remove me
		}
		for (k = 0; k < scene.fixed_meshes.size(); ++k)
		{
			pABT->AddGeometry(scene.fixed_meshes[k], scene.fixed_transforms[k]);
		}
		pSG->AddRoom(pRoom);
		pRoom->AddSceneObject(pABT); // remove me
		pSG->AddSceneObject(pABT);

#if 1
		LogExportMsg(Log_Message, "Setting up DWorld File\n");
		// save the stuff to a dworld file
		pWorld = manager->GetWorld(worldID);
		if (!pWorld)
		{
			throw dexiException("could not get world");
		}
		pFS = IdeFileSystem_CreateFSReal();
		if (!pFS)
		{
			throw dexiException("could not create filesystem");
		}
		pOutFile = pFS->Open(outputFileName.const_str(), (deFile_Open_CreateAlways | deFile_Open_Write));
		if (!pOutFile)
		{
			deString s;
			s.Format("Could not create file: %s\n", outputFileName.const_str());
			throw dexiException(s);
		}
		LogExportMsg(Log_Message, "Saving DWorld\n");
		if (!pWorld->Serialize(pOutFile))
		{
			throw dexiException("Error writing file");
		}
		pOutFile->Close();

		LogExportMsg(Log_Message, "Export Complete\n\n");
#endif
	}
	catch (dexiException e)
	{
		if (pRoom)
			pRoom->Release();
		if (pOutFile)
			pOutFile->Release();
		if (pFS)
			pFS->Release();
		if (pWorld)
			pWorld->Release();
		if (pDefaultTex)
			pDefaultTex->Release();
		cleanupContainers(bmpMap, sfcMap, vtxMap, meshMap, scene);
		throw e;
	}
#endif

	DE3D_SAFE_RELEASE(pABT);
	DE3D_SAFE_RELEASE(pRoom);
	DE3D_SAFE_RELEASE(pOutFile);
	DE3D_SAFE_RELEASE(pFS);
	DE3D_SAFE_RELEASE(pWorld);
	DE3D_SAFE_RELEASE(pDefaultTex);
	cleanupContainers(bmpMap, sfcMap, vtxMap, meshMap, scene);
}

void cleanupContainers(bmpMap_t& bmpMap, sfcMap_t& sfcMap, vtxMap_t& vtxMap, meshMap_t& meshMap, 
					   dexiScene& scene)
{
	int k;
	for (k = 0; k < scene.fixed_meshes.size(); ++k)
	{
		if (scene.fixed_meshes[k])
			scene.fixed_meshes[k]->Release();
	}
	for (k = 0; k < scene.instance_meshes.size(); ++k)
	{
		if (scene.instance_meshes[k])
			scene.instance_meshes[k]->Release();
	}
	meshMap_t::iterator meshIt;
	for (meshIt = meshMap.begin(); meshIt != meshMap.end(); ++meshIt)
	{
		dexiTriMesh* mesh = meshIt->second;
		if (mesh->instance_owner && mesh->instance_template)
			mesh->instance_template->Release();
		delete mesh;
	}
	sfcMap_t::iterator sfcIt;
	for (sfcIt = sfcMap.begin(); sfcIt != sfcMap.end(); ++sfcIt)
	{
		IdeSurface* sfc = sfcIt->second;
		if (sfc)
			sfc->Release();
	}
	bmpMap_t::iterator bmpIt;
	for (bmpIt = bmpMap.begin(); bmpIt != bmpMap.end(); ++bmpIt)
	{
		IdeBitmapProxy* proxy = bmpIt->second;
		if (proxy)
			proxy->Release();
	}
	vtxMap_t::iterator vtxIt;
	for (vtxIt = vtxMap.begin(); vtxIt != vtxMap.end(); ++vtxIt)
	{
		dexiVertexSet* vtx = vtxIt->second;
		delete vtx;
	}
}

bool readColorAtts(TiXmlElement* thisElem, deColor& outColor)
{
	if (!thisElem)
		return false;
	const char *sr, *sg, *sb, *sa, *sisfloat;
	sisfloat = thisElem->Attribute("float");
	sr = thisElem->Attribute("r");
	sg = thisElem->Attribute("g");
	sb = thisElem->Attribute("b");
	sa = thisElem->Attribute("a");
	
	double r=1,g=1,b=1,a=1;
	int isfloat = 0; // default to integer
	if (sisfloat)
	{
		isfloat = atoi(sisfloat);
	}
	if (sr)
	{
		r = atof(sr);
		if (!isfloat)
			r /= 255;
		outColor.r = (float)r;
	}
	if (sg)
	{
		g = atof(sg);
		if (!isfloat)
			g /= 255;
		outColor.g = (float)g;
	}
	if (sb)
	{
		b = atof(sb);
		if (!isfloat)
			b /= 255;
		outColor.b = (float)b;
	}
	if (sa)
	{
		a = atof(sa);
		if (!isfloat)
			a /= 255;
		outColor.a = (float)a;
	}
	return true;
}

// read a deColor child element named 'color'
bool readColor(TiXmlElement* parentElem, deColor& outColor, bool required)
{
	if (!parentElem)
		return false;
	TiXmlElement* colorElem = parentElem->FirstChildElement("color");
	if (!colorElem)
	{
		if (required)
		{
			deString error;
			error.Format("missing required color element at (%d,%d)", 
				parentElem->Row(), 
				parentElem->Column());
			throw dexiException(error);
		}
		return false;
	}

	return readColorAtts(colorElem, outColor);
}

static const char* posNames[] = {"x","y","z"};
static const char* normNames[] = {"nx","ny","nz"};
static const char* scaleNames[] = {"sx","sy","sz"};
static const char* texNames[] = {"u","v","w"};
bool readVecAtts(TiXmlElement* thisElem, deVec3d& outVec, const char** attNames)
{
	if (!thisElem)
		return false;
	const char *sx, *sy, *sz;
	sx = thisElem->Attribute(attNames[0]);
	sy = thisElem->Attribute(attNames[1]);
	sz = thisElem->Attribute(attNames[2]);
	
	if (!sx || !sy || !sz)
	{
		deString error;
		error.Format("Missing required attribute (%s,%s,%s) at (%d,%d)", 
			attNames[0], attNames[1], attNames[2],
			thisElem->Row(), thisElem->Column());
		throw dexiException(error);
	}
	
	outVec.x = atof(sx);
	outVec.y = atof(sy);
	outVec.z = atof(sz);
	
	return true;
}

// read a vec3d child element with specified name
bool readVec(TiXmlElement* parentElem, deVec3d& outVec, const char* valname, bool required,
			 const char** attNames)
{
	if (!parentElem)
		return false;
	TiXmlElement* posElem = parentElem->FirstChildElement(valname);
	if (!posElem)
	{
		if (required)
		{
			deString error;
			error.Format("missing required %s element at (%d,%d)", 
				valname,
				parentElem->Row(), parentElem->Column());
			throw dexiException(error);
		}
		return false;
	}

	return readVecAtts(posElem, outVec, attNames);
}

bool readQuatAtts(TiXmlElement* thisElem, deQuaternion& outQuat)
{
	if (!thisElem)
		return false;
	const char *sx, *sy, *sz, *sw;
	sx = thisElem->Attribute("qx");
	sy = thisElem->Attribute("qy");
	sz = thisElem->Attribute("qz");
	sw = thisElem->Attribute("qw");
	
	if (!sx || !sy || !sz || !sw)
	{
		deString error;
		error.Format("Missing required attribute (qx,qy,qz,qw) at (%d,%d)", 
			thisElem->Row(), thisElem->Column());
		throw dexiException(error);
	}
	
	outQuat.x = (float)atof(sx);
	outQuat.y = (float)atof(sy);
	outQuat.z = (float)atof(sz);
	outQuat.w = (float)atof(sw);
	
	return true;
}

// read a quaternion child element with specified name
bool readQuat(TiXmlElement* parentElem, deQuaternion& outQuat, const char* valname, bool required)
{
	if (!parentElem)
		return false;
	TiXmlElement* quatElem = parentElem->FirstChildElement(valname);
	if (!quatElem)
	{
		if (required)
		{
			deString error;
			error.Format("missing required %s element at (%d,%d)", 
				valname,
				parentElem->Row(), parentElem->Column());
			throw dexiException(error);
		}
		return false;
	}

	return readQuatAtts(quatElem, outQuat);
}

bool readTransformAtts(TiXmlElement* thisElem, deTransform& outTrans)
{
	deVec3d pos, scale;
	deQuaternion rot;
	if (readQuat(thisElem, rot, "rotation", false))
		rot.ToMatrix(outTrans);
	else
		outTrans.Reset();
	if (readVec(thisElem, scale, "scale", false, scaleNames))
		outTrans.ApplyScaling(scale);
	readVec(thisElem, pos, "position", true, posNames);
	outTrans.Translation = pos;

	return true;
}

bool readTransform(TiXmlElement* parentElem, deTransform& outTrans, bool required)
{
	if (!parentElem)
		return false;
	TiXmlElement* transElem = parentElem->FirstChildElement("transform");
	if (!transElem)
	{
		if (required)
		{
			deString error;
			error.Format("missing required transform element at (%d,%d)", 
				parentElem->Row(), parentElem->Column());
			throw dexiException(error);
		}
		return false;
	}

	return readTransformAtts(transElem, outTrans);
}

// load all the bitmaps inside a 'bitmaps' element
void loadBitmaps(bmpMap_t& bmpMap, TiXmlElement* bitmaps)
{
	LogExportMsg(Log_Message, "Processing bitmaps...\n");
	TiXmlElement* bmpElem;
	for(bmpElem = bitmaps->FirstChildElement("bitmap");
		bmpElem != NULL; 
		bmpElem = bmpElem->NextSiblingElement("bitmap"))
	{
		bool datafound = false;
		TiXmlElement* subelem;
		subelem = bmpElem->FirstChildElement();
		while (!datafound)
		{
			deString2 val = subelem->Value();
			if (val == "file")
			{
				const char* filename = subelem->Attribute("name");
				if (filename && filename[0] != '\0')
				{
					IdeBitmapProxy* bmpProxy = IdeBitmap_CreateProxy(NULL);
					bmpProxy->SetFilename(filename);
					if (!bmpProxy->GetBitmap())
						LogExportMsg(Log_Warning, "* warning: Unable to load bitmap '%s', keeping filename\n", filename);
					deString2 name2 = bmpElem->Attribute("name");
					bmpMap[name2] = bmpProxy;
					datafound = true;
				}
				else
				{
					LogExportMsg(Log_Warning, "Invalid filename on bitmap named '%s'\n", bmpElem->Attribute("name"));
				}
			}
			else if (val == "bitmap_data")
			{
				datafound = true;
				LogExportMsg(Log_Warning, "* warning: Cannot handle bitmap_data at (%d,%d)\n", 
					bmpElem->Row(), bmpElem->Column());
			}
			
			if (!datafound)
			{
				subelem = subelem->NextSiblingElement();
				if (!subelem)
					break;
			}
		}
		if (!datafound)
		{
			LogExportMsg(Log_Warning, "* warning: No bitmap data found in bitmap named '%s'\n", bmpElem->Attribute("name"));
		}
	}
}

// load all the surfaces stored inside a surfaces element
void loadSurfaces(const bmpMap_t& bmpMap, sfcMap_t& sfcMap, TiXmlElement* surfaces)
{
	LogExportMsg(Log_Message, "Processing surfaces...\n");
	TiXmlElement* sfcElem;
	for(sfcElem = surfaces->FirstChildElement("surface");
		sfcElem != NULL; 
		sfcElem = sfcElem->NextSiblingElement("surface"))
	{
		IdeSurface* newsurf = IdeSurface_CreateSurface();
		TiXmlElement* subelem;
		for(subelem = sfcElem->FirstChildElement();
			subelem != NULL; 
			subelem = subelem->NextSiblingElement())
		{
			deString2 val = subelem->Value();
			if (val == "material")
			{
				// default values set up to be matte (no reflectivity, no emission)
				deColor diff = {1,1,1,1};
				deColor spec = {0,0,0,0};
				deColor amb = {1,1,1,1};
				deColor emiss = {0,0,0,0};
				float power = 1;
				// read the elements and attributes, if they exist
				readColor(subelem->FirstChildElement("diffuse"),  diff, false);
				readColor(subelem->FirstChildElement("specular"), spec, false);
				readColor(subelem->FirstChildElement("ambient"),  amb, false);
				readColor(subelem->FirstChildElement("emissive"), emiss, false);
				const char* spower = subelem->Attribute("power");
				if (spower)
					power = (float)atof(spower);
				IdeRenderMaterial* mat = IdeRenderState_CreateRenderMaterial();
				mat->Lock();
				mat->SetDiffuseColor(&diff);
				mat->SetSpecularColor(&spec);
				mat->SetAmbientColor(&amb);
				mat->SetEmissiveColor(&emiss);
				mat->SetSpecularPower(power);
				mat->Unlock();
				newsurf->SetRenderMaterial(mat);
				mat->Release();
			}
			else if (val == "textures")
			{
				IdeRenderTexture* tex = IdeRenderState_CreateRenderTexture();
				tex->Lock();
				TiXmlElement* texElem;
				for(texElem = subelem->FirstChildElement("texture"); 
					texElem != NULL; 
					texElem = texElem->NextSiblingElement("texture"))
				{
					const char* stage = texElem->Attribute("stage");
					if (!stage)
					{
						deString error;
						error.Format("Missing required attribute (stage) at (%d,%d)", texElem->Row(), texElem->Column());
						throw dexiException(error);
					}
					int stagenum = atoi(stage);
					const char* textype = texElem->Attribute("textype");
					if (!textype)
					{
						deString error;
						error.Format("Missing required attribute (textype) at (%d,%d)", texElem->Row(), texElem->Column());
						throw dexiException(error);
					}
					TiXmlElement* bmpname;
					for(bmpname = texElem->FirstChildElement("bitmap_name");
						bmpname != NULL; 
						bmpname = bmpname->NextSiblingElement("bitmap_name"))
					{
						TiXmlHandle texthandle(bmpname);
						TiXmlText* text = texthandle.FirstChild().Text();
						if (text)
						{
							deString2 val = text->Value();
							bmpMap_t::const_iterator bmp = bmpMap.find(val);
							if (bmp != bmpMap.end())
							{
								IdeBitmapProxy* proxy = bmp->second;
								tex->SetBitmap(stagenum, proxy);
								tex->SetColorArg(stagenum, 1, IdeRenderTexture::TEXTUREARG_PREVIOUS);
								tex->SetColorArg(stagenum, 2, IdeRenderTexture::TEXTUREARG_TEXTURE);
								tex->SetColorOperation(stagenum, IdeRenderTexture::TEXTUREOP_MODULATE);
							}
							else
							{
								LogExportMsg(Log_Warning, "* warning: Could not find bitmap named '%s' at (%d,%d)\n",
									val.const_str(),
									text->Row(),
									text->Column());
							}
						}
					}
				}
				tex->Unlock();
				newsurf->SetRenderTexture(tex);
				tex->Release();
			}
			else if (val == "shader")
			{
				LogExportMsg(Log_Warning, "* warning: Encountered shader element, cannot handle currently\n");
			}
		}
		deString2 name = sfcElem->Attribute("name");
		sfcMap[name] = newsurf;
	}
}

// load a named vertex definition
void loadVertices(vtxMap_t& vtxMap, TiXmlElement* vertices)
{
	LogExportMsg(Log_Message, "Processing vertices...\n");
	TiXmlElement* vpoolElem;
	// multiple vertices elements
	for(vpoolElem = vertices->FirstChildElement("vertexpool");
		vpoolElem != NULL;
		vpoolElem = vpoolElem->NextSiblingElement("vertexpool"))
	{
		const char* str;
		// make sure this vertex pool has a name
		str = vpoolElem->Attribute("name");
		if (!str)
		{
			deString error;
			error.Format("Missing required name at (%d,%d)",
				vpoolElem->Row(), vpoolElem->Column());
			throw dexiException(error);
		}
		dexiVertexSet* vertexSet = new dexiVertexSet;
		// see if the file contains the number of positions & vertices to expect
		str = vpoolElem->Attribute("num_pos");
		if (str)
		{
			int num = atoi(str);
			vertexSet->unique_positions.Reserve(num);
		}
		str = vpoolElem->Attribute("num_vert");
		if (str)
		{
			int num = atoi(str);
			vertexSet->vert_positions.Reserve(num);
			vertexSet->pos_bindings.Reserve(num);
			vertexSet->normals.Reserve(num);
			vertexSet->colors.Reserve(num);
			vertexSet->texcoords.Reserve(num);
		}
		int pos_idx=0, vert_idx=0;
		TiXmlElement* vtxElem;
		// read all the child elements (position and vertex)
		for(vtxElem = vpoolElem->FirstChildElement();
			vtxElem != NULL;
			vtxElem = vtxElem->NextSiblingElement())
		{
			deString2 val = vtxElem->Value();
			if (val == "vert_pos" || val == "position") // old & bloated, just contains a position
			{
				// position element, store it into the position stream
				if (vertexSet->unique_positions.size() <= pos_idx)
					vertexSet->unique_positions.resize(pos_idx+1);
				deVec3d tempvec;
				if (val == "position")
					readVecAtts(vtxElem, tempvec, posNames);
				else
					readVec(vtxElem, tempvec, "position", true, posNames);
				vertexSet->unique_positions[pos_idx] = tempvec;
				++pos_idx;
			}
			else if (val == "vertex")
			{
				// vertex element, lots of stuff to read from it
				// first grab the position reference
				str = vtxElem->Attribute("posidx");
				if (!str)
				{
					deString error;
					error.Format("Missing required attribute (posidx) at (%d,%d)", 
						vtxElem->Row(), vtxElem->Column());
					throw dexiException(error);
				}
				int vposidx = atoi(str);
				if (vposidx < 0 || vposidx >= pos_idx)
				{
					deString error;
					error.Format("posidx outside valid range [0, %d) at (%d,%d)", 
						pos_idx,
						vtxElem->Row(), vtxElem->Column());
					throw dexiException(error);
				}
				if (vertexSet->pos_bindings.size() <= vert_idx)
				{
					vertexSet->pos_bindings.resize(vert_idx+1);
					vertexSet->vert_positions.resize(vert_idx+1);
				}
				vertexSet->pos_bindings[vert_idx] = vposidx;
				vertexSet->vert_positions[vert_idx] = deMake_Vertex(vertexSet->unique_positions[vposidx]);
				// next grab all the child elements (normals, colors, texcoords, etc)
				TiXmlElement* subelem;
				int tex_idx = 0;
				for(subelem = vtxElem->FirstChildElement();
					subelem != NULL;
					subelem = subelem->NextSiblingElement())
				{
					val = subelem->Value();
					if (val == "normal")
					{
						// vertex normal
						if (vertexSet->normals.size() <= vert_idx)
						{
							if (vertexSet->normals.size() < vert_idx)
							{
								LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - skipped a normal\n",
									subelem->Row(), subelem->Column());
							}
							vertexSet->normals.resize(vert_idx+1);
						}
						deVec3d tempvec;
						readVecAtts(subelem, tempvec, normNames);
						Vec3d_Normalize(tempvec);
						vertexSet->normals[vert_idx] = deMake_Vertex(tempvec);
					}
					else if (val == "diffuse")
					{
						// vertex color
						if (vertexSet->colors.size() <= vert_idx)
						{
							if (vertexSet->colors.size() < vert_idx)
							{
								LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - skipped a color\n",
									subelem->Row(), subelem->Column());
							}
							vertexSet->colors.resize(vert_idx+1);
						}
						readColor(subelem, vertexSet->colors[vert_idx], true);
					}
					else if (val == "texcoord")
					{
						// vertex texcoord - can have multiple texcoords per vertex
						if (vertexSet->texcoords.size() <= vert_idx)
						{
							if (vertexSet->texcoords.size() < vert_idx)
							{
								LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - skipped a texcoord\n",
									subelem->Row(), subelem->Column());
							}
							vertexSet->texcoords.resize(vert_idx+1);
						}
						if (vertexSet->texcoords[vert_idx].size() <= tex_idx)
							vertexSet->texcoords[vert_idx].resize(tex_idx+1);
						deVec3d tempvec;
						readVecAtts(subelem, tempvec, texNames);
						vertexSet->texcoords[vert_idx][tex_idx].u = (float)tempvec.x;
						vertexSet->texcoords[vert_idx][tex_idx].v = (float)tempvec.y;
						vertexSet->texcoords[vert_idx][tex_idx].w = (float)tempvec.z;
						++tex_idx;
					}
				}
				if (vert_idx > 0 && vertexSet->texcoords.size() > 0 &&
					vertexSet->texcoords[vert_idx].size() != vertexSet->texcoords[vert_idx-1].size())
				{
					LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - texcoord num mismatch\n",
						vtxElem->Row(), vtxElem->Column());
				}
				++vert_idx;
			}
		}
		// do an integrity check, make sure that if any stream was present that is was filled up
		if (vertexSet->normals.size() > 0 &&
			vertexSet->normals.size() != vertexSet->pos_bindings.size())
		{
			LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - normal num mismatch\n",
				vpoolElem->Row(), vpoolElem->Column());
		}
		if (vertexSet->colors.size() > 0 &&
			vertexSet->colors.size() != vertexSet->pos_bindings.size())
		{
			LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - color num mismatch\n",
				vpoolElem->Row(), vpoolElem->Column());
		}
		if (vertexSet->texcoords.size() > 0 &&
			vertexSet->texcoords.size() != vertexSet->pos_bindings.size())
		{
			LogExportMsg(Log_Warning, "* warning: Inconsistent vertex elements at (%d,%d) - texcoord num mismatch\n",
				vpoolElem->Row(), vpoolElem->Column());
		}
		// record this vertex set into the vertices map
		str = vpoolElem->Attribute("name");
		vtxMap[str] = vertexSet;
	}
}

void loadTrimesh(const vtxMap_t& vtxMap, const sfcMap_t& sfcMap, meshMap_t& meshMap, TiXmlElement* meshdef)
{
	LogExportMsg(Log_Message, "Processing trimeshdef...\n");
	const char* str;
	// make sure this mesh definition has a name
	str = meshdef->Attribute("name");
	if (!str)
	{
		deString error;
		error.Format("Missing required name at (%d,%d)",
			meshdef->Row(), meshdef->Column());
		throw dexiException(error);
	}
	// and a vertex pool
	str = meshdef->Attribute("vertexpool");
	if (!str)
	{
		deString error;
		error.Format("Missing required attribute (vertexpool) at (%d,%d)",
			meshdef->Row(), meshdef->Column());
		throw dexiException(error);
	}
	
	dexiTriMesh* newmesh = new dexiTriMesh;
	newmesh->vertexpool = str;
	vtxMap_t::const_iterator vtxIt = vtxMap.find(newmesh->vertexpool);
	if (vtxIt == vtxMap.end())
	{
		deString error;
		error.Format("Could not find vertexpool named '%s' at (%d,%d)",
			str,
			meshdef->Row(), meshdef->Column());
		throw dexiException(error);
	}
	u32 index_max = vtxIt->second->pos_bindings.size()-1;

	TiXmlElement* subelem;
	// read all the submeshes
	for(subelem = meshdef->FirstChildElement("trimeshsub");
		subelem != NULL;
		subelem = subelem->NextSiblingElement("trimeshsub"))
	{
		// add a submesh
		int subnum = newmesh->submeshes.size();
		newmesh->submeshes.resize(subnum+1);

		// read the surface name
		str = subelem->Attribute("surface");
		if (!str)
		{
			deString error;
			error.Format("Missing attribute (surface) at (%d,%d)",
				subelem->Row(), subelem->Column());
			throw dexiException(error);
		}
		deString2 surfname = str;
		newmesh->submeshes[subnum].surface_name = surfname;

		// verify the surface name
		sfcMap_t::const_iterator it = sfcMap.find(surfname);
		if (it == sfcMap.end())
		{
			LogExportMsg(Log_Warning, "* warning: Could not find surface named '%s' at (%d,%d), using NULL\n",
				surfname.const_str(),
				subelem->Row(), subelem->Column());
			newmesh->submeshes[subnum].surface_name = "";
		}

		// retrieve the intended facecount, if available
		int facecount = -1;
		str = subelem->Attribute("facecount");
		if (str)
		{
			facecount = atoi(str);
			newmesh->submeshes[subnum].faces.Reserve(facecount);
		}

		// get the winding value (default to counter-clockwise if none specified)
		bool ccw = true;
		str = subelem->Attribute("winding");
		if (str)
		{
			deString2 val = str;
			if (val == "counter")
				ccw = true;
			else if (val == "clockwise")
				ccw = false;
			else
			{
				deString error;
				error.Format("Invalid value for attribute (winding): %s", str);
				throw dexiException(error);
			}
		}

		TiXmlElement* triElem;
		// read all the trifaces for this submesh
		for(triElem = subelem->FirstChildElement("triface");
			triElem != NULL;
			triElem = triElem->NextSiblingElement("triface"))
		{
			// read the string values
			const char *s0, *s1, *s2;
			s0 = triElem->Attribute("v0");
			s1 = triElem->Attribute("v1");
			s2 = triElem->Attribute("v2");
			
			if (!s0 || !s1 || !s2)
			{
				deString error;
				error.Format("Missing required attribute (v0,v1,v2) at (%d,%d)", 
					triElem->Row(),
					triElem->Column());
				throw dexiException(error);
			}

			// convert the string vals into vertex indices
			int face_idx = newmesh->submeshes[subnum].faces.size();
			newmesh->submeshes[subnum].faces.IncrementSize();
			dexiTriMeshSub::triface_t* pFace = &newmesh->submeshes[subnum].faces[face_idx];
			pFace->v0 = atoi(s0);
			// account for face winding order
			if (ccw)
			{
				pFace->v1 = atoi(s1);
				pFace->v2 = atoi(s2);
			}
			else
			{
				pFace->v1 = atoi(s2);
				pFace->v2 = atoi(s1);
			}

			// check that the index values are in a valid range
			if (pFace->v0 > index_max || pFace->v0 < 0 ||
				pFace->v1 > index_max || pFace->v1 < 0 ||
				pFace->v2 > index_max || pFace->v2 < 0)
			{
				deString error;
				error.Format("One of (v0,v1,v2) outside valid range [0,%d] at (%d,%d)", 
					index_max,
					triElem->Row(), triElem->Column());
				throw dexiException(error);
			}
		}
		// this submesh is done reading faces

		// check integrity of facecount
		int face_num = newmesh->submeshes[subnum].faces.size();
		if (face_num != facecount)
		{
			LogExportMsg(Log_Warning, "* warning: Mismatched facecount (expected %d, found %d) at (%d, %d)\n",
				facecount, face_num,
				subelem->Row(), subelem->Column(),
				face_num);
		}
	}
	
	// record this mesh definition into the mesh map
	str = meshdef->Attribute("name");
	meshMap[str] = newmesh;
}

// handles a scenedef to have mesh instances compiled into an ABT
void loadScene(sfcMap_t& sfcMap, vtxMap_t& vtxMap, meshMap_t& meshMap, dexiScene& scene, TiXmlElement* scenedef)
{
	LogExportMsg(Log_Message, "Processing scenedef...\n");
	const char* str;
	TiXmlElement* meshElem;
	for(meshElem = scenedef->FirstChildElement("trimeshinstance");
		meshElem != NULL;
		meshElem = meshElem->NextSiblingElement("trimeshinstance"))
	{
		deTransform tform;
		readTransform(meshElem, tform, false);

		// grab the mesh definition being used
		str = meshElem->Attribute("meshdef");
		if (!str)
		{
			deString error;
			error.Format("Missing required attribute (meshdef) at (%d,%d)", 
				meshElem->Row(), meshElem->Column());
			throw dexiException(error);
		}
		deString2 meshdef = str;

		// find whether this will be an instanced mesh (ie, just a copy)
		bool instanced = false;
		str = meshElem->Attribute("instanced");
		if (str)
		{
			int x = atoi(str);
			instanced = !!x;
		}

		meshMap_t::const_iterator it = meshMap.find(meshdef);
		if (it == meshMap.end())
		{
			LogExportMsg(Log_Warning, "* warning: Could not find meshdef named '%s' at (%d,%d), skipping\n",
				meshdef.const_str(),
				meshElem->Row(), meshElem->Column());
			continue;
		}

		dexiTriMesh* mesh = it->second;
		if (!mesh)
		{
			deString error;
			error.Format("found mesh named '%s' but it was NULL", meshdef.const_str());
			throw dexiException(error.const_str());
		}

		if (mesh->instance_template == NULL)
		{
			// create a the mesh template
#pragma todo (create a the mesh template)
			int k;
			long indexoffset;
			mesh->instance_template = IdeMesh_CreateMesh();
			mesh->instance_owner = deTRUE;
			IdeMesh* instance_template = mesh->instance_template;

			// build the vertex data
			vtxMap_t::const_iterator vtxIt = vtxMap.find(mesh->vertexpool);
			if (vtxIt == vtxMap.end())
			{
				deString error;
				error.Format("Could not find vertexpool named '%s' at (%d,%d)",
					mesh->vertexpool.const_str(),
					meshElem->Row(), meshElem->Column());
				throw dexiException(error);
			}
			dexiVertexSet* vtxSet = vtxIt->second;
			deMeshVertexData vdata;
			memset(&vdata, 0, sizeof(deMeshVertexData));
			vdata.positions = &(vtxSet->vert_positions[0]);
			// normals are easy to grab
			if (vtxSet->normals.size() > 0)
				vdata.normals = &(vtxSet->normals[0]);
			// colors need conversion to integer format
			if (vtxSet->colors.size() > 0)
			{				
				vdata.colors = new deARGB[vtxSet->colors.size()];
				for (k = 0; k < vtxSet->colors.size(); ++k)
				{
					vdata.colors[k] = deMake_Color(vtxSet->colors[k]);
				}
			}
			// texture coords take a bit of work
			if (vtxSet->texcoords.size() > 0)
			{
				vdata.uvcoords = new deTexCoord2[vtxSet->texcoords.size() * vtxSet->texcoords[0].size()];
				int j, numverts, numstages;
				numverts = vtxSet->texcoords.size();
				numstages = vtxSet->texcoords[0].size();
				vdata.numtexstages = numstages;
				for (j = 0; j < numverts; ++j)
				{
					for (k = 0; k < numstages; ++k)
					{
						deTexCoord3 tempcoord3;
						deTexCoord2 tempcoord2;
						tempcoord3 = vtxSet->texcoords[j][k];
						tempcoord2.u = tempcoord3.u;
						tempcoord2.v = tempcoord3.v;
						vdata.uvcoords[k*numverts + j] = tempcoord2;
					}
				}
			}
			vdata.numpoints = vtxSet->vert_positions.size();
			instance_template->AddVertices(vdata, indexoffset);

			// process all the submeshes
			int numsubs = mesh->submeshes.size();
			deMeshIndexData* idata = new deMeshIndexData[numsubs];
			memset(idata, 0, numsubs*sizeof(deMeshIndexData));
			for (k = 0; k < numsubs; ++k)
			{
				sfcMap_t::const_iterator sfcIt = sfcMap.find(mesh->submeshes[k].surface_name);
				if (sfcIt == sfcMap.end())
				{
					LogExportMsg(Log_Warning, "* warning: Could not find surface named '%s' at (%d,%d), using NULL\n",
						mesh->submeshes[k].surface_name.const_str(),
						meshElem->Row(), meshElem->Column());
				}
				else
					idata[k].surface = sfcIt->second;
				dexiTriMeshSub* submesh = &mesh->submeshes[k];
				int numfaces = submesh->faces.size();
				idata[k].numindices = numfaces*3;
				if (vdata.numpoints <= 65535)
				{
					idata[k].indices16 = new u16[idata[k].numindices];
					int j;
					for (j = 0; j < numfaces; ++j)
					{
						u16 idx0 = (u16)submesh->faces[j].v0;
						idata[k].indices16[j*3+0] = idx0;
						u16 idx1 = (u16)submesh->faces[j].v1;
						idata[k].indices16[j*3+1] = idx1;
						u16 idx2 = (u16)submesh->faces[j].v2;
						idata[k].indices16[j*3+2] = idx2;
						DE_ASSERT((idx0 | idx1 | idx2) < 65536);
					}
				}
				else
				{
					idata[k].indices32 = new u32[idata[k].numindices];
					int j;
					for (j = 0; j < numfaces; ++j)
					{
						idata[k].indices32[j*3+0] = submesh->faces[j].v0;
						idata[k].indices32[j*3+1] = submesh->faces[j].v1;
						idata[k].indices32[j*3+2] = submesh->faces[j].v2;
					}
				}
				instance_template->AddIndices(idata[k]);
			}

			// build the mesh
			instance_template->BuildFromVertices();

			// clean up our mess
			delete[] vdata.colors;
			delete[] vdata.uvcoords;
			for (k = 0; k < numsubs; ++k)
			{
				delete[] idata[k].indices16;
				delete[] idata[k].indices32;
			}
			delete[] idata;
		}

		if (instanced)
		{
			// instance needs a scenegraph entry to go with it
			IdeSceneStaticMesh* staticmesh = IdeSceneObject_CreateSceneStaticMesh();
			staticmesh->SetMesh(mesh->instance_template);
			staticmesh->SetWorldTransform(tform);
			scene.instance_meshes.push_back(staticmesh);
		}
		else
		{
			// fixed meshes will all be compiled into ABT
			mesh->instance_template->Claim();
			scene.fixed_meshes.push_back(mesh->instance_template);
			scene.fixed_transforms.push_back(tform);
		}
	}
}

