From 61c6c37e30ad90a12491a948bb9670d03a9073ea Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Thu, 5 Jul 2012 16:35:08 +0200 Subject: [PATCH] - fbx: implement mesh splitting for meshes with multiple materials. --- code/FBXConverter.cpp | 249 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 226 insertions(+), 23 deletions(-) diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 290170add..74d611d9b 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -182,12 +182,12 @@ private: const MeshGeometry* const mesh = dynamic_cast(geo); if(mesh) { - const unsigned int index = ConvertMesh(*mesh, model); - if(index == 0) { - continue; - } + std::vector& 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(),1) ); } else { FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name()); @@ -205,18 +205,21 @@ private: // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed - unsigned int ConvertMesh(const MeshGeometry& mesh, const Model& model) + std::vector ConvertMesh(const MeshGeometry& mesh, const Model& model) { + std::vector temp; + MeshMap::const_iterator it = meshes_converted.find(&mesh); if (it != meshes_converted.end()) { - return (*it).second + 1; + temp.push_back((*it).second + 1); + return temp; } const std::vector& vertices = mesh.GetVertices(); const std::vector& faces = mesh.GetFaceIndexCounts(); if(vertices.empty() || faces.empty()) { FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name()); - return 0; + return temp; } aiMesh* out_mesh = new aiMesh(); @@ -224,13 +227,37 @@ private: meshes_converted[&mesh] = static_cast(meshes.size()-1); + // one material per mesh maps easily to aiMesh. Multiple material + // meshes need to be split. + const std::vector& 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& vertices = mesh.GetVertices(); + const std::vector& faces = mesh.GetFaceIndexCounts(); + // copy vertices - out_mesh->mNumVertices = static_cast(vertices.size()); + out_mesh->mNumVertices = static_cast(vertices.size()); out_mesh->mVertices = new aiVector3D[vertices.size()]; std::copy(vertices.begin(),vertices.end(),out_mesh->mVertices); // generate dummy faces - out_mesh->mNumFaces = static_cast(faces.size()); + out_mesh->mNumFaces = static_cast(faces.size()); aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()](); unsigned int cursor = 0; @@ -240,18 +267,18 @@ private: 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; + 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) { f.mIndices[i] = cursor++; @@ -328,7 +355,183 @@ private: const std::vector& mindices = mesh.GetMaterialIndices(); ConvertMaterialForMesh(out_mesh,model,mesh,mindices.size() ? mindices[0] : 0); - + + return static_cast(meshes.size()); + } + + + // ------------------------------------------------------------------------------------------------ + std::vector ConvertMeshMultiMaterial(aiMesh* out_mesh, const MeshGeometry& mesh, const Model& model) + { + const std::vector& mindices = mesh.GetMaterialIndices(); + ai_assert(mindices.size()); + + std::set had; + std::vector 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& mindices = mesh.GetMaterialIndices(); + ai_assert(mindices.size()); + + const std::vector& vertices = mesh.GetVertices(); + const std::vector& faces = mesh.GetFaceIndexCounts(); + + unsigned int count_faces = 0; + unsigned int count_vertices = 0; + + // count faces + for(std::vector::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& 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& tangents = mesh.GetTangents(); + const std::vector* binormals = &mesh.GetBinormals(); + + if(tangents.size()) { + std::vector 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& 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& 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::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& 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& cols = mesh.GetVertexColors(i); + out_mesh->mColors[i][cursor] = cols[in_cursor]; + } + } + } + + ConvertMaterialForMesh(out_mesh,model,mesh,index); return static_cast(meshes.size()); }