Browse Source

- fbx: implement mesh splitting for meshes with multiple materials.

Alexander Gessler 13 years ago
parent
commit
61c6c37e30
1 changed files with 226 additions and 23 deletions
  1. 226 23
      code/FBXConverter.cpp

+ 226 - 23
code/FBXConverter.cpp

@@ -182,12 +182,12 @@ private:
 
 
 			const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
 			const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
 			if(mesh) {
 			if(mesh) {
-				const unsigned int index = ConvertMesh(*mesh, model);
-				if(index == 0) {
-					continue;
-				}
+				std::vector<unsigned int>& indices = ConvertMesh(*mesh, model);
 
 
-				meshes.push_back(index-1);
+				// mesh indices are shifted by 1 and 0 entries are failed conversions -
+				// XXX maybe log how many conversions went wrong?
+				std::remove(indices.begin(),indices.end(),0);
+				std::transform(indices.begin(),indices.end(),std::back_inserter(meshes), std::bind2nd(std::minus<unsigned int>(),1) );
 			}
 			}
 			else {
 			else {
 				FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
 				FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
@@ -205,18 +205,21 @@ private:
 
 
 	// ------------------------------------------------------------------------------------------------
 	// ------------------------------------------------------------------------------------------------
 	// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
 	// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
-	unsigned int ConvertMesh(const MeshGeometry& mesh, const Model& model)
+	std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model)
 	{
 	{
+		std::vector<unsigned int> temp; 
+
 		MeshMap::const_iterator it = meshes_converted.find(&mesh);
 		MeshMap::const_iterator it = meshes_converted.find(&mesh);
 		if (it != meshes_converted.end()) {
 		if (it != meshes_converted.end()) {
-			return (*it).second + 1;
+			temp.push_back((*it).second + 1);
+			return temp;
 		}
 		}
 
 
 		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
 		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
 		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
 		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
 		if(vertices.empty() || faces.empty()) {
 		if(vertices.empty() || faces.empty()) {
 			FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
 			FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
-			return 0;
+			return temp;
 		}
 		}
 
 
 		aiMesh* out_mesh = new aiMesh();
 		aiMesh* out_mesh = new aiMesh();
@@ -224,13 +227,37 @@ private:
 
 
 		meshes_converted[&mesh] = static_cast<unsigned int>(meshes.size()-1);
 		meshes_converted[&mesh] = static_cast<unsigned int>(meshes.size()-1);
 
 
+		// one material per mesh maps easily to aiMesh. Multiple material 
+		// meshes need to be split.
+		const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices();
+		if (!mindices.empty()) {
+			const unsigned int base = mindices[0];
+			BOOST_FOREACH(unsigned int index, mindices) {
+				if(index != base) {
+					return ConvertMeshMultiMaterial(out_mesh, mesh, model);
+				}
+			}
+		}
+
+		// faster codepath, just copy the data
+		temp.push_back(ConvertMeshSingleMaterial(out_mesh, mesh, model));
+		return temp;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	unsigned int ConvertMeshSingleMaterial(aiMesh* out_mesh, const MeshGeometry& mesh, const Model& model)	
+	{
+		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+
 		// copy vertices
 		// copy vertices
-		out_mesh->mNumVertices = static_cast<size_t>(vertices.size());
+		out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
 		out_mesh->mVertices = new aiVector3D[vertices.size()];
 		out_mesh->mVertices = new aiVector3D[vertices.size()];
 		std::copy(vertices.begin(),vertices.end(),out_mesh->mVertices);
 		std::copy(vertices.begin(),vertices.end(),out_mesh->mVertices);
 
 
 		// generate dummy faces
 		// generate dummy faces
-		out_mesh->mNumFaces = static_cast<size_t>(faces.size());
+		out_mesh->mNumFaces = static_cast<unsigned int>(faces.size());
 		aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()]();
 		aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()]();
 
 
 		unsigned int cursor = 0;
 		unsigned int cursor = 0;
@@ -240,18 +267,18 @@ private:
 			f.mIndices = new unsigned int[pcount];
 			f.mIndices = new unsigned int[pcount];
 			switch(pcount) 
 			switch(pcount) 
 			{
 			{
-				case 1:
-					out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
-					break;
-				case 2:
-					out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
-					break;
-				case 3:
-					out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
-					break;
-				default:
-					out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
-					break;
+			case 1:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+				break;
+			case 2:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+				break;
+			case 3:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+				break;
+			default:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+				break;
 			}
 			}
 			for (unsigned int i = 0; i < pcount; ++i) {
 			for (unsigned int i = 0; i < pcount; ++i) {
 				f.mIndices[i] = cursor++;
 				f.mIndices[i] = cursor++;
@@ -328,7 +355,183 @@ private:
 
 
 		const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices();
 		const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices();
 		ConvertMaterialForMesh(out_mesh,model,mesh,mindices.size() ? mindices[0] : 0);
 		ConvertMaterialForMesh(out_mesh,model,mesh,mindices.size() ? mindices[0] : 0);
-		
+
+		return static_cast<unsigned int>(meshes.size());
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	std::vector<unsigned int> ConvertMeshMultiMaterial(aiMesh* out_mesh, const MeshGeometry& mesh, const Model& model)	
+	{
+		const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices();
+		ai_assert(mindices.size());
+	
+		std::set<unsigned int> had;
+		std::vector<unsigned int> indices;
+
+		BOOST_FOREACH(unsigned int index, mindices) {
+			if(had.find(index) != had.end()) {
+
+				indices.push_back(ConvertMeshMultiMaterial(out_mesh, mesh, model, index));
+				had.insert(index);
+			}
+		}
+
+		return indices;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	unsigned int ConvertMeshMultiMaterial(aiMesh* out_mesh, const MeshGeometry& mesh, const Model& model, unsigned int index)	
+	{
+		const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices();
+		ai_assert(mindices.size());
+
+		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+
+		unsigned int count_faces = 0;
+		unsigned int count_vertices = 0;
+
+		// count faces
+		for(std::vector<unsigned int>::const_iterator it = mindices.begin(), 
+			end = mindices.end(), itf = faces.begin(); it != end; ++it, ++itf) 
+		{	
+			if ((*it) != index) {
+				continue;
+			}
+			++count_faces;
+			count_vertices += *itf;
+		}
+
+		ai_assert(count_faces);
+
+
+		// allocate output data arrays, but don't fill them yet
+		out_mesh->mNumVertices = count_vertices;
+		out_mesh->mVertices = new aiVector3D[count_vertices];
+
+		out_mesh->mNumFaces = count_faces;
+		aiFace* fac = out_mesh->mFaces = new aiFace[count_faces]();
+
+
+		// allocate normals
+		const std::vector<aiVector3D>& normals = mesh.GetNormals();
+		if(normals.size()) {
+			ai_assert(normals.size() == vertices.size());
+			out_mesh->mNormals = new aiVector3D[vertices.size()];
+		}
+
+		// allocate tangents, binormals. 
+		const std::vector<aiVector3D>& tangents = mesh.GetTangents();
+		const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
+
+		if(tangents.size()) {
+			std::vector<aiVector3D> tempBinormals;
+			if (!binormals->size()) {
+				if (normals.size()) {
+					// XXX this computes the binormals for the entire mesh, not only 
+					// the part for which we need them.
+					tempBinormals.resize(normals.size());
+					for (unsigned int i = 0; i < tangents.size(); ++i) {
+						tempBinormals[i] = normals[i] ^ tangents[i];
+					}
+
+					binormals = &tempBinormals;
+				}
+				else {
+					binormals = NULL;	
+				}
+			}
+
+			if(binormals) {
+				ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
+
+				out_mesh->mTangents = new aiVector3D[vertices.size()];
+				out_mesh->mBitangents = new aiVector3D[vertices.size()];
+			}
+		}
+
+		// allocate texture coords
+		unsigned int num_uvs = 0;
+		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) {
+			const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+			if(uvs.empty()) {
+				break;
+			}
+
+			out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+			out_mesh->mNumUVComponents[i] = 2;
+		}
+
+		// allocate vertex colors
+		unsigned int num_vcs = 0;
+		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) {
+			const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
+			if(colors.empty()) {
+				break;
+			}
+
+			out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+		}
+
+		unsigned int cursor = 0, in_cursor = 0;
+
+		for(std::vector<unsigned int>::const_iterator it = mindices.begin(), 
+			end = mindices.end(), itf = faces.begin(); it != end; ++it, ++itf) 
+		{	
+			const unsigned int pcount = *itf;
+			if ((*it) != index) {
+				in_cursor += pcount;
+				continue;
+			}
+
+			aiFace& f = *fac++;
+
+			f.mNumIndices = pcount;
+			f.mIndices = new unsigned int[pcount];
+			switch(pcount) 
+			{
+			case 1:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+				break;
+			case 2:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+				break;
+			case 3:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+				break;
+			default:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+				break;
+			}
+			for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
+				f.mIndices[i] = cursor;
+
+				out_mesh->mVertices[cursor] = vertices[in_cursor];
+
+				if(out_mesh->mNormals) {
+					out_mesh->mNormals[cursor] = normals[in_cursor];
+				}
+
+				if(out_mesh->mTangents) {
+					out_mesh->mTangents[cursor] = tangents[in_cursor];
+					out_mesh->mBitangents[cursor] = (*binormals)[in_cursor];
+				}
+
+				for (unsigned int i = 0; i < num_uvs; ++i) {
+					const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+					out_mesh->mTextureCoords[i][cursor] = aiVector3D(uvs[in_cursor].x,uvs[in_cursor].y, 0.0f);
+				}
+
+				for (unsigned int i = 0; i < num_vcs; ++i) {
+					const std::vector<aiColor4D>& cols = mesh.GetVertexColors(i);
+					out_mesh->mColors[i][cursor] = cols[in_cursor];
+				}
+			}
+		}
+	
+		ConvertMaterialForMesh(out_mesh,model,mesh,index);
 		return static_cast<unsigned int>(meshes.size());
 		return static_cast<unsigned int>(meshes.size());
 	}
 	}