commit 37cc0ff3d7ad496f7e56c02762ad197026ae0fd5
Author: myconid <christofi.cos@gmail.com>
Date:   Thu Jun 21 09:43:50 2012 +0300

    Multi-texcoord support, so multiple UV sets can be loaded

diff --git a/source/collada/GeomReindex.cpp b/source/collada/GeomReindex.cpp
index ceca97e..d924a2a 100644
--- a/source/collada/GeomReindex.cpp
+++ b/source/collada/GeomReindex.cpp
@@ -21,6 +21,7 @@
 
 #include "FCollada.h"
 #include "FCDocument/FCDEntity.h"
+#include "FCDocument/FCDGeometryMesh.h"
 #include "FCDocument/FCDGeometryPolygons.h"
 #include "FCDocument/FCDGeometryPolygonsInput.h"
 #include "FCDocument/FCDGeometrySource.h"
@@ -33,17 +34,17 @@
 
 struct VertexData
 {
-	VertexData(const float* pos, const float* norm, const float* tex, const std::vector<FCDJointWeightPair>& weights)
+	VertexData(const float* pos, const float* norm, const std::vector<float> &uvs, const std::vector<FCDJointWeightPair>& weights)
 		: x(pos[0]), y(pos[1]), z(pos[2]),
 		nx(norm[0]), ny(norm[1]), nz(norm[2]),
-		u(tex[0]), v(tex[1]),
+		uvs(uvs),
 		weights(weights)
 	{
 	}
 
 	float x, y, z;
 	float nx, ny, nz;
-	float u, v;
+	std::vector<float> uvs;
 	std::vector<FCDJointWeightPair> weights;
 };
 
@@ -75,7 +76,7 @@ bool operator==(const VertexData& a, const VertexData& b)
 {
 	return (similar(a.x,  b.x)  && similar(a.y,  b.y)  && similar(a.z,  b.z)
 		 && similar(a.nx, b.nx) && similar(a.ny, b.ny) && similar(a.nz, b.nz)
-		 && similar(a.u,  b.u)  && similar(a.v,  b.v)
+		 && (a.uvs == b.uvs)
 		 && (a.weights == b.weights));
 }
 
@@ -84,7 +85,7 @@ bool operator<(const VertexData& a, const VertexData& b)
 #define CMP(f) if (a.f < b.f) return true; if (a.f > b.f) return false
 	CMP(x);  CMP(y);  CMP(z);
 	CMP(nx); CMP(ny); CMP(nz);
-	CMP(u);  CMP(v);
+	CMP(uvs);
 	CMP(weights);
 #undef CMP
 	return false;
@@ -148,13 +149,14 @@ void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
 	assert(indicesNormal);
 	assert(indicesTexcoord); // TODO - should be optional, because textureless meshes aren't unreasonable
 
+	FCDGeometrySourceList texcoordSources;
+	polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
+
 	FCDGeometrySource* sourcePosition = inputPosition->GetSource();
 	FCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();
-	FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource();
 
 	const float* dataPosition = sourcePosition->GetData();
 	const float* dataNormal   = sourceNormal  ->GetData();
-	const float* dataTexcoord = sourceTexcoord->GetData();
 
 	if (skin)
 	{
@@ -166,7 +168,6 @@ void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
 
 	uint32 stridePosition = sourcePosition->GetStride();
 	uint32 strideNormal   = sourceNormal  ->GetStride();
-	uint32 strideTexcoord = sourceTexcoord->GetStride();
 
 	std::vector<uint32> indicesCombined;
 	std::vector<VertexData> vertexes;
@@ -188,10 +189,20 @@ void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
 			CanonicaliseWeights(weights);
 		}
 
+		std::vector<float> uvs;
+		for (size_t set = 0; set < texcoordSources.size(); ++set)
+		{
+			const float* dataTexcoord = texcoordSources[set]->GetData();
+			uint32 strideTexcoord = texcoordSources[set]->GetStride();
+			
+			uvs.push_back(dataTexcoord[indicesTexcoord[i]*strideTexcoord]);
+			uvs.push_back(dataTexcoord[indicesTexcoord[i]*strideTexcoord + 1]);
+		}
+
 		VertexData vtx (
 			&dataPosition[indicesPosition[i]*stridePosition],
 			&dataNormal  [indicesNormal  [i]*strideNormal],
-			&dataTexcoord[indicesTexcoord[i]*strideTexcoord],
+			uvs,
 			weights
 		);
 		size_t idx = inserter.add(vtx);
@@ -215,8 +226,6 @@ void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
 		newDataNormal  .push_back(vertexes[i].nx);
 		newDataNormal  .push_back(vertexes[i].ny);
 		newDataNormal  .push_back(vertexes[i].nz);
-		newDataTexcoord.push_back(vertexes[i].u);
-		newDataTexcoord.push_back(vertexes[i].v);
 		newWeightedMatches.push_back(vertexes[i].weights);
 	}
 
@@ -226,9 +235,19 @@ void ReindexGeometry(FCDGeometryPolygons* polys, FCDSkinController* skin)
 	inputNormal  ->SetIndices(&indicesCombined.front(), indicesCombined.size());
 	inputTexcoord->SetIndices(&indicesCombined.front(), indicesCombined.size());
 
+	for (size_t set = 0; set < texcoordSources.size(); ++set)
+	{
+		newDataTexcoord.clear();
+		for (size_t i = 0; i < vertexes.size(); ++i)
+		{
+			newDataTexcoord.push_back(vertexes[i].uvs[set * 2]);
+			newDataTexcoord.push_back(vertexes[i].uvs[set * 2 + 1]);
+		}
+		texcoordSources[set]->SetData(newDataTexcoord, 2);
+	}
+
 	sourcePosition->SetData(newDataPosition, 3);
 	sourceNormal  ->SetData(newDataNormal,   3);
-	sourceTexcoord->SetData(newDataTexcoord, 2);
 
 	if (skin)
 	{
diff --git a/source/collada/PMDConvert.cpp b/source/collada/PMDConvert.cpp
index 5cbcd88..a3b31b7 100644
--- a/source/collada/PMDConvert.cpp
+++ b/source/collada/PMDConvert.cpp
@@ -149,7 +149,6 @@ public:
 
 			FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
 			FCDGeometryPolygonsInput* inputNormal   = polys->FindInput(FUDaeGeometryInput::NORMAL);
-			FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
 
 			const uint32* indicesCombined = inputPosition->GetIndices();
 			size_t indicesCombinedCount = inputPosition->GetIndexCount();
@@ -157,15 +156,22 @@ public:
 
 			FCDGeometrySource* sourcePosition = inputPosition->GetSource();
 			FCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();
-			FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource();
+			
+			FCDGeometrySourceList texcoordSources;
+			polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
+			
+			std::vector<float*> dataTexcoords;
+			for (size_t i = 0; i < texcoordSources.size(); ++i)
+			{
+				dataTexcoords.push_back(texcoordSources[i]->GetData());
+				assert(texcoordSources[i]->GetDataCount() == vertexCount*2);
+			}
 
 			float* dataPosition = sourcePosition->GetData();
 			float* dataNormal   = sourceNormal  ->GetData();
-			float* dataTexcoord = sourceTexcoord->GetData();
 			size_t vertexCount = sourcePosition->GetDataCount() / 3;
 			assert(sourcePosition->GetDataCount() == vertexCount*3);
 			assert(sourceNormal  ->GetDataCount() == vertexCount*3);
-			assert(sourceTexcoord->GetDataCount() == vertexCount*2);
 
 			// Transform mesh coordinate system to game coordinates
 			// (doesn't modify prop points)
@@ -190,7 +196,7 @@ public:
 
 			AddStaticPropPoints(propPoints, upAxisTransform, converter.GetInstance().GetParent());
 
-			WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoord, vertexCount, boneWeights, boneTransforms, propPoints);
+			WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
 		}
 		else if (converter.GetInstance().GetType() == FCDEntityInstance::CONTROLLER)
 		{
@@ -417,8 +423,6 @@ public:
 
 			FCDGeometryPolygonsInput* inputPosition = polys->FindInput(FUDaeGeometryInput::POSITION);
 			FCDGeometryPolygonsInput* inputNormal   = polys->FindInput(FUDaeGeometryInput::NORMAL);
-			FCDGeometryPolygonsInput* inputTexcoord = polys->FindInput(FUDaeGeometryInput::TEXCOORD);
-
 
 			const uint32* indicesCombined = inputPosition->GetIndices();
 			size_t indicesCombinedCount = inputPosition->GetIndexCount();
@@ -426,15 +430,22 @@ public:
 
 			FCDGeometrySource* sourcePosition = inputPosition->GetSource();
 			FCDGeometrySource* sourceNormal   = inputNormal  ->GetSource();
-			FCDGeometrySource* sourceTexcoord = inputTexcoord->GetSource();
+
+			FCDGeometrySourceList texcoordSources;
+			polys->GetParent()->FindSourcesByType(FUDaeGeometryInput::TEXCOORD, texcoordSources);
+			
+			std::vector<float*> dataTexcoords;
+			for (size_t i = 0; i < texcoordSources.size(); ++i)
+			{
+				dataTexcoords.push_back(texcoordSources[i]->GetData());
+				assert(texcoordSources[i]->GetDataCount() == vertexCount*2);
+			}
 
 			float* dataPosition = sourcePosition->GetData();
 			float* dataNormal   = sourceNormal  ->GetData();
-			float* dataTexcoord = sourceTexcoord->GetData();
 			size_t vertexCount = sourcePosition->GetDataCount() / 3;
 			assert(sourcePosition->GetDataCount() == vertexCount*3);
 			assert(sourceNormal  ->GetDataCount() == vertexCount*3);
-			assert(sourceTexcoord->GetDataCount() == vertexCount*2);
 
 			// Transform model coordinate system to game coordinates
 
@@ -442,7 +453,7 @@ public:
 				converter.GetEntityTransform(), skin->GetBindShapeTransform(),
 				converter.IsYUp(), converter.IsXSI());
 
-			WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoord, vertexCount, boneWeights, boneTransforms, propPoints);
+			WritePMD(output, indicesCombined, indicesCombinedCount, dataPosition, dataNormal, dataTexcoords, vertexCount, boneWeights, boneTransforms, propPoints);
 		}
 		else
 		{
@@ -470,7 +481,9 @@ public:
 	 */
 	static void WritePMD(OutputCB& output,
 		const uint32* indices, size_t indexCount,
-		const float* position, const float* normal, const float* texcoord, size_t vertexCount,
+		const float* position, const float* normal,
+		const std::vector<float*>& texcoords,
+		size_t vertexCount,
 		const std::vector<VertexBlend>& boneWeights, const std::vector<BoneTransform>& boneTransforms,
 		const std::vector<PropPoint>& propPoints)
 	{
@@ -489,9 +502,9 @@ public:
 		}
 
 		output("PSMD", 4);  // magic number
-		write(output, (uint32)3); // version number
+		write(output, (uint32)4); // version number
 		write(output, (uint32)(
-			4 + 13*4*vertexCount + // vertices
+			4 + 11*4*vertexCount + 4 + 8*texcoords.size()*vertexCount + // vertices
 			4 + 6*faceCount + // faces
 			4 + 7*4*boneCount + // bones
 			4 + propPointsSize // props
@@ -499,11 +512,17 @@ public:
 
 		// Vertex data
 		write<uint32>(output, (uint32)vertexCount);
+		write<uint32>(output, (uint32)texcoords.size()); // UV pairs per vertex
 		for (size_t i = 0; i < vertexCount; ++i)
 		{
 			output((char*)&position[i*3], 12);
 			output((char*)&normal  [i*3], 12);
-			output((char*)&texcoord[i*2],  8);
+
+			for (size_t s = 0; s < texcoords.size(); ++s)
+			{
+				output((char*)&texcoords[s][i*2], 8);
+			}
+
 			if (boneCount)
 				write(output, boneWeights[i]);
 			else
diff --git a/source/graphics/ModelDef.cpp b/source/graphics/ModelDef.cpp
index d328d49..e6c0923 100644
--- a/source/graphics/ModelDef.cpp
+++ b/source/graphics/ModelDef.cpp
@@ -226,7 +226,7 @@ void CModelDef::BlendBoneMatrices(
 
 // CModelDef Constructor
 CModelDef::CModelDef() :
-	m_NumVertices(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0),
+	m_NumVertices(0), m_NumUVsPerVertex(0), m_pVertices(0), m_NumFaces(0), m_pFaces(0),
 	m_NumBones(0), m_Bones(0), m_InverseBindBoneMatrices(NULL),
 	m_NumBlends(0), m_pBlends(0), m_pBlendIndices(0),
 	m_Name(L"[not loaded]")
@@ -275,8 +275,34 @@ CModelDef* CModelDef::Load(const VfsPath& filename, const VfsPath& name)
 
 	// now unpack everything 
 	mdef->m_NumVertices = unpacker.UnpackSize();
+	
+	// versions prior to 4 only support 1 UV set, 4 and later store it here
+	if (unpacker.GetVersion() <= 3) 
+	{
+		mdef->m_NumUVsPerVertex = 1;
+	}
+	else
+	{
+		mdef->m_NumUVsPerVertex = unpacker.UnpackSize();
+	}
+
 	mdef->m_pVertices=new SModelVertex[mdef->m_NumVertices];
-	unpacker.UnpackRaw(mdef->m_pVertices,sizeof(SModelVertex)*mdef->m_NumVertices);
+	
+	for (size_t i = 0; i < mdef->m_NumVertices; ++i)
+	{
+		unpacker.UnpackRaw(&mdef->m_pVertices[i].m_Coords, 12);
+		unpacker.UnpackRaw(&mdef->m_pVertices[i].m_Norm, 12);
+		
+		for (size_t s = 0; s < mdef->m_NumUVsPerVertex; ++s)
+		{
+			float uv[2];
+			unpacker.UnpackRaw(&uv[0], 8);
+			mdef->m_pVertices[i].m_UVs.push_back(uv[0]);
+			mdef->m_pVertices[i].m_UVs.push_back(uv[1]);
+		}
+		
+		unpacker.UnpackRaw(&mdef->m_pVertices[i].m_Blend, sizeof(SVertexBlend));
+	}
 	
 	mdef->m_NumFaces = unpacker.UnpackSize();
 	mdef->m_pFaces=new SModelFace[mdef->m_NumFaces];
diff --git a/source/graphics/ModelDef.h b/source/graphics/ModelDef.h
index 7f67301..ea5f344 100644
--- a/source/graphics/ModelDef.h
+++ b/source/graphics/ModelDef.h
@@ -23,6 +23,7 @@
 #define INCLUDED_MODELDEF
 
 #include "ps/CStr.h"
+#include "maths/Vector2D.h"
 #include "maths/Vector3D.h"
 #include "maths/Quaternion.h"
 #include "lib/file/vfs/vfs_path.h"
@@ -104,7 +105,7 @@ struct SModelVertex
 	// vertex normal
 	CVector3D m_Norm;
 	// vertex UVs
-	float m_U, m_V;
+	std::vector<float> m_UVs;
 	// vertex blend data
 	SVertexBlend m_Blend;
 };
@@ -164,6 +165,9 @@ public:
 	// accessor: get vertex data
 	size_t GetNumVertices() const { return m_NumVertices; }
 	SModelVertex* GetVertices() const { return m_pVertices; }
+	
+	// accessor: get number of UV sets
+	size_t GetNumUVsPerVertex() const { return m_NumUVsPerVertex; }
 
 	// accessor: get face data
 	size_t GetNumFaces() const { return m_NumFaces; }
@@ -256,6 +260,7 @@ public:
 	// vertex data
 	size_t m_NumVertices;
 	SModelVertex* m_pVertices;
+	size_t m_NumUVsPerVertex; // number of UV pairs per vertex
 	// face data
 	size_t m_NumFaces;
 	SModelFace* m_pFaces;
diff --git a/source/renderer/HWLightingModelRenderer.cpp b/source/renderer/HWLightingModelRenderer.cpp
index c1fc61d..083a71a 100644
--- a/source/renderer/HWLightingModelRenderer.cpp
+++ b/source/renderer/HWLightingModelRenderer.cpp
@@ -62,7 +62,7 @@ ShaderModelDef::ShaderModelDef(const CModelDefPtr& mdef)
 
 	VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>();
 
-	ModelRenderer::BuildUV(mdef, UVit);
+	ModelRenderer::BuildUV(mdef, UVit, 0);
 
 	m_Array.Upload();
 	m_Array.FreeBackingStore();
@@ -232,9 +232,9 @@ void ShaderModelVertexRenderer::UpdateModelData(CModel* model, CModelRData* data
 void ShaderModelVertexRenderer::BeginPass(int streamflags)
 {
 	if (m->cpuLighting)
-		ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_COLOR)));
+		ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_COLOR)));
 	else
-		ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_NORMAL)));
+		ENSURE(streamflags == (streamflags & (STREAM_POS | STREAM_UV0 | STREAM_UV1 | STREAM_NORMAL)));
 }
 
 // Cleanup one rendering pass
diff --git a/source/renderer/InstancingModelRenderer.cpp b/source/renderer/InstancingModelRenderer.cpp
index 9932731..4d8b500 100644
--- a/source/renderer/InstancingModelRenderer.cpp
+++ b/source/renderer/InstancingModelRenderer.cpp
@@ -46,13 +46,15 @@ struct IModelDef : public CModelDefRPrivate
 	/// Static per-CModel vertex array
 	VertexArray m_Array;
 
-	/// Position, normals and UV are all static
+	/// Position and normals are static
 	VertexArray::Attribute m_Position;
 	VertexArray::Attribute m_Normal;
-	VertexArray::Attribute m_UV;
 	VertexArray::Attribute m_BlendJoints; // valid iff gpuSkinning == true
 	VertexArray::Attribute m_BlendWeights; // valid iff gpuSkinning == true
 
+	/// The number of UVs is determined by the model
+	VertexArray::Attribute m_UVs[5];
+
 	/// Indices are the same for all models, so share them
 	VertexIndexArray m_IndexArray;
 
@@ -73,9 +75,12 @@ IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning)
 	m_Normal.elems = 3;
 	m_Array.AddAttribute(&m_Normal);
 
-	m_UV.type = GL_FLOAT;
-	m_UV.elems = 2;
-	m_Array.AddAttribute(&m_UV);
+	for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
+	{
+		m_UVs[i].type = GL_FLOAT;
+		m_UVs[i].elems = 2;
+		m_Array.AddAttribute(&m_UVs[i]);
+	}
 
 	if (gpuSkinning)
 	{
@@ -93,10 +98,14 @@ IModelDef::IModelDef(const CModelDefPtr& mdef, bool gpuSkinning)
 
 	VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>();
 	VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>();
-	VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>();
 
 	ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal);
-	ModelRenderer::BuildUV(mdef, UVit);
+
+	for (size_t i = 0; i < mdef->GetNumUVsPerVertex(); i++)
+	{
+		VertexArrayIterator<float[2]> UVit = m_UVs[i].GetIterator<float[2]>();
+		ModelRenderer::BuildUV(mdef, UVit, i);
+	}
 
 	if (gpuSkinning)
 	{
@@ -180,7 +189,7 @@ void InstancingModelRenderer::UpdateModelData(CModel* UNUSED(model), CModelRData
 // Setup one rendering pass.
 void InstancingModelRenderer::BeginPass(int streamflags)
 {
-	ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0)));
+	ENSURE(streamflags == (streamflags & (STREAM_POS|STREAM_NORMAL|STREAM_UV0|STREAM_UV1)));
 }
 
 // Cleanup rendering pass.
@@ -209,7 +218,10 @@ void InstancingModelRenderer::PrepareModelDef(const CShaderProgramPtr& shader, i
 		shader->NormalPointer(GL_FLOAT, stride, base + m->imodeldef->m_Normal.offset);
 
 	if (streamflags & STREAM_UV0)
-		shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->imodeldef->m_UV.offset);
+		shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->imodeldef->m_UVs[0].offset);
+	
+	if ((streamflags & STREAM_UV1) && def.GetNumUVsPerVertex() >= 2)
+		shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, base + m->imodeldef->m_UVs[1].offset);
 
 	// GPU skinning requires extra attributes to compute positions/normals
 	if (m->gpuSkinning)
diff --git a/source/renderer/ModelRenderer.cpp b/source/renderer/ModelRenderer.cpp
index b020071..65a2baa 100644
--- a/source/renderer/ModelRenderer.cpp
+++ b/source/renderer/ModelRenderer.cpp
@@ -148,15 +148,16 @@ void ModelRenderer::BuildColor4ub(
 // Copy UV coordinates
 void ModelRenderer::BuildUV(
 		const CModelDefPtr& mdef,
-		const VertexArrayIterator<float[2]>& UV)
+		const VertexArrayIterator<float[2]>& UV,
+		int UVset)
 {
 	size_t numVertices = mdef->GetNumVertices();
 	SModelVertex* vertices = mdef->GetVertices();
 
 	for (size_t j=0; j < numVertices; ++j)
 	{
-		UV[j][0] = vertices[j].m_U;
-		UV[j][1] = 1.0-vertices[j].m_V;
+		UV[j][0] = vertices[j].m_UVs[UVset * 2];
+		UV[j][1] = 1.0-vertices[j].m_UVs[UVset * 2 + 1];
 	}
 }
 
diff --git a/source/renderer/ModelRenderer.h b/source/renderer/ModelRenderer.h
index e27a0c6..7019524 100644
--- a/source/renderer/ModelRenderer.h
+++ b/source/renderer/ModelRenderer.h
@@ -241,7 +241,8 @@ public:
 	 */
 	static void BuildUV(
 			const CModelDefPtr& mdef,
-			const VertexArrayIterator<float[2]>& UV);
+			const VertexArrayIterator<float[2]>& UV,
+			int UVset);
 
 	/**
 	 * BuildIndices: Create the indices array for the given CModelDef.
