From 415592b6dea51f0023878a4db0b6b74ece4be255 Mon Sep 17 00:00:00 2001 From: "PLAINCONCEPTS\\davila" Date: Thu, 23 Nov 2017 11:06:35 +0100 Subject: [PATCH] Modify FBX2glTF to allow that several nodes share the same mesh. --- src/Fbx2Raw.cpp | 21 ++++++++- src/Raw2Gltf.cpp | 113 ++++++++++++++++++++++++++++------------------- src/RawModel.cpp | 20 +++++++-- src/RawModel.h | 6 ++- 4 files changed, 106 insertions(+), 54 deletions(-) diff --git a/src/Fbx2Raw.cpp b/src/Fbx2Raw.cpp index 62463d8..d0c7243 100644 --- a/src/Fbx2Raw.cpp +++ b/src/Fbx2Raw.cpp @@ -607,8 +607,25 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std: meshConverter.Triangulate(pNode->GetNodeAttribute(), true); FbxMesh *pMesh = pNode->GetMesh(); + // Obtains the surface Id + const long surfaceId = pMesh->GetUniqueID(); + + // Associate the node to this surface + int nodeId = raw.GetNodeByName(pNode->GetName()); + if (nodeId >= 0) + { + RawNode& node = raw.GetNode(nodeId); + node.surfaceId = surfaceId; + } + + if (raw.GetSurfaceById(surfaceId) >= 0) + { + // This surface is already loaded + return; + } + const char *meshName = (pNode->GetName()[0] != '\0') ? pNode->GetName() : pMesh->GetName(); - const int rawSurfaceIndex = raw.AddSurface(meshName, pNode->GetName()); + const int rawSurfaceIndex = raw.AddSurface(meshName, surfaceId); const FbxVector4 *controlPoints = pMesh->GetControlPoints(); const FbxLayerElementAccess normalLayer(pMesh->GetElementNormal(), pMesh->GetElementNormalCount()); @@ -688,7 +705,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std: const std::shared_ptr fbxMaterial = materials.GetMaterial(polygonIndex); int textures[RAW_TEXTURE_USAGE_MAX]; - std::fill_n(textures, RAW_TEXTURE_USAGE_MAX, -1); + std::fill_n(textures, (int)RAW_TEXTURE_USAGE_MAX, -1); FbxString shadingModel, materialName; FbxVector4 ambient, specular, diffuse, emissive; diff --git a/src/Raw2Gltf.cpp b/src/Raw2Gltf.cpp index 45c98e6..76d94b1 100644 --- a/src/Raw2Gltf.cpp +++ b/src/Raw2Gltf.cpp @@ -231,6 +231,15 @@ T &require(std::map> map, std::string key) return result; } +template +T &require(std::map> map, long key) +{ + auto iter = map.find(key); + assert(iter != map.end()); + T &result = *iter->second; + return result; +} + static const std::vector getIndexArray(const RawModel &raw) { std::vector result; @@ -284,7 +293,7 @@ ModelData *Raw2Gltf( std::map> nodesByName; std::map> materialsByName; - std::map> meshByNodeName; + std::map> meshBySurfaceId; // for now, we only have one buffer; data->binary points to the same vector as that BufferData does. BufferData &buffer = *gltf->buffers.hold( @@ -461,17 +470,6 @@ ModelData *Raw2Gltf( materialsByName[materialHash(material)] = mData; } - // - // surfaces - // - - // in GLTF 2.0, the structural limitation is that a node can - // only belong to a single mesh. A mesh can however contain any - // number of primitives, which are essentially little meshes. - // - // so each RawSurface turns into a primitive, and we sort them - // by root node using this map; one mesh per node. - for (size_t surfaceIndex = 0; surfaceIndex < materialModels.size(); surfaceIndex++) { const RawModel &surfaceModel = materialModels[surfaceIndex]; @@ -481,50 +479,25 @@ ModelData *Raw2Gltf( const RawMaterial &rawMaterial = surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex); const MaterialData &mData = require(materialsByName, materialHash(rawMaterial)); - std::string nodeName = rawSurface.nodeName; - NodeData &meshNode = require(nodesByName, nodeName); + int surfaceId = rawSurface.id; + //NodeData &meshNode = require(nodesByName, nodeName); - MeshData *mesh = nullptr; - auto meshIter = meshByNodeName.find(nodeName); - if (meshIter != meshByNodeName.end()) { + MeshData *mesh = nullptr; + auto meshIter = meshBySurfaceId.find(surfaceId); + if (meshIter != meshBySurfaceId.end()) { mesh = meshIter->second.get(); - } else { + } + else { std::vector defaultDeforms; for (const auto &channel : rawSurface.blendChannels) { defaultDeforms.push_back(channel.defaultDeform); } auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms)); - meshByNodeName[nodeName] = meshPtr; - meshNode.SetMesh(meshPtr->ix); + meshBySurfaceId[surfaceId] = meshPtr; mesh = meshPtr.get(); } - // - // surface skin - // - if (!rawSurface.jointNames.empty()) { - if (meshNode.skin == -1) { - // glTF uses column-major matrices - std::vector inverseBindMatrices; - for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) { - inverseBindMatrices.push_back(inverseBindMatrice.Transpose()); - } - - std::vector jointIndexes; - for (const auto &jointName : rawSurface.jointNames) { - jointIndexes.push_back(require(nodesByName, jointName).ix); - } - - // Write out inverseBindMatrices - auto accIBM = gltf->AddAccessorAndView(buffer, GLT_MAT4F, inverseBindMatrices); - - auto skeletonRoot = require(nodesByName, rawSurface.skeletonRootName); - auto skin = *gltf->skins.hold(new SkinData(jointIndexes, *accIBM, skeletonRoot)); - meshNode.SetSkin(skin.ix); - } - } - std::shared_ptr primitive; if (options.useDraco) { int triangleCount = surfaceModel.GetTriangleCount(); @@ -544,7 +517,8 @@ ModelData *Raw2Gltf( AccessorData &indexes = *gltf->accessors.hold(new AccessorData(GLT_USHORT)); indexes.count = 3 * triangleCount; primitive.reset(new PrimitiveData(indexes, mData, dracoMesh)); - } else { + } + else { const AccessorData &indexes = *gltf->AddAccessorWithView( *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER), GLT_USHORT, getIndexArray(surfaceModel)); @@ -665,6 +639,53 @@ ModelData *Raw2Gltf( mesh->AddPrimitive(primitive); } + // + // Assign meshes to node + // + + for (int i = 0; i < raw.GetNodeCount(); i++) { + + const RawNode &node = raw.GetNode(i); + auto nodeData = gltf->nodes.ptrs[i]; + + // + // Assign mesh to node + // + if (node.surfaceId > 0) + { + int surfaceIndex = raw.GetSurfaceById(node.surfaceId); + const RawSurface &rawSurface = raw.GetSurface(surfaceIndex); + + MeshData &meshData = require(meshBySurfaceId, rawSurface.id); + nodeData->SetMesh(meshData.ix); + + // + // surface skin + // + if (!rawSurface.jointNames.empty()) { + if (nodeData->skin == -1) { + // glTF uses column-major matrices + std::vector inverseBindMatrices; + for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) { + inverseBindMatrices.push_back(inverseBindMatrice.Transpose()); + } + + std::vector jointIndexes; + for (const auto &jointName : rawSurface.jointNames) { + jointIndexes.push_back(require(nodesByName, jointName).ix); + } + + // Write out inverseBindMatrices + auto accIBM = gltf->AddAccessorAndView(buffer, GLT_MAT4F, inverseBindMatrices); + + auto skeletonRoot = require(nodesByName, rawSurface.skeletonRootName); + auto skin = *gltf->skins.hold(new SkinData(jointIndexes, *accIBM, skeletonRoot)); + nodeData->SetSkin(skin.ix); + } + } + } + } + // // cameras // diff --git a/src/RawModel.cpp b/src/RawModel.cpp index 22084c2..d6ea545 100644 --- a/src/RawModel.cpp +++ b/src/RawModel.cpp @@ -180,18 +180,18 @@ int RawModel::AddSurface(const RawSurface &surface) return (int) (surfaces.size() - 1); } -int RawModel::AddSurface(const char *name, const char *nodeName) +int RawModel::AddSurface(const char *name, const long surfaceId) { assert(name[0] != '\0'); for (size_t i = 0; i < surfaces.size(); i++) { - if (Gltf::StringUtils::CompareNoCase(surfaces[i].name, name) == 0) { + if (surfaces[i].id == surfaceId) { return (int) i; } } RawSurface surface; + surface.id = surfaceId; surface.name = name; - surface.nodeName = nodeName; surface.bounds.Clear(); surface.discrete = false; @@ -263,6 +263,7 @@ int RawModel::AddNode(const char *name, const char *parentName) joint.isJoint = false; joint.name = name; joint.parentName = parentName; + joint.surfaceId = 0; joint.translation = Vec3f(0, 0, 0); joint.rotation = Quatf(0, 0, 0, 1); joint.scale = Vec3f(1, 1, 1); @@ -281,7 +282,7 @@ void RawModel::Condense() for (auto &triangle : triangles) { const RawSurface &surface = oldSurfaces[triangle.surfaceIndex]; - const int surfaceIndex = AddSurface(surface.name.c_str(), surface.nodeName.c_str()); + const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id); surfaces[surfaceIndex] = surface; triangle.surfaceIndex = surfaceIndex; } @@ -538,3 +539,14 @@ int RawModel::GetNodeByName(const char *name) const } return -1; } + +int RawModel::GetSurfaceById(const long surfaceId) const +{ + for (size_t i = 0; i < surfaces.size(); i++) { + if (surfaces[i].id == surfaceId) { + return (int)i; + } + } + return -1; +} + diff --git a/src/RawModel.h b/src/RawModel.h index 1d0961f..3077bbb 100644 --- a/src/RawModel.h +++ b/src/RawModel.h @@ -182,8 +182,8 @@ struct RawBlendChannel struct RawSurface { + long id; std::string name; // The name of this surface - std::string nodeName; // The node that links to this surface. std::string skeletonRootName; // The name of the root of the skeleton. Bounds bounds; std::vector jointNames; @@ -248,6 +248,7 @@ struct RawNode Vec3f translation; Quatf rotation; Vec3f scale; + long surfaceId; }; class RawModel @@ -267,7 +268,7 @@ public: Vec4f diffuseFactor, Vec3f specularFactor, Vec3f emissiveFactor, float shinineness); int AddSurface(const RawSurface &suface); - int AddSurface(const char *name, const char *nodeName); + int AddSurface(const char *name, long surfaceId); int AddAnimation(const RawAnimation &animation); int AddCameraPerspective( const char *name, const char *nodeName, const float aspectRatio, const float fovDegreesX, const float fovDegreesY, @@ -307,6 +308,7 @@ public: int GetSurfaceCount() const { return (int) surfaces.size(); } const RawSurface &GetSurface(const int index) const { return surfaces[index]; } RawSurface &GetSurface(const int index) { return surfaces[index]; } + int GetSurfaceById(const long id) const; // Iterate over the animations. int GetAnimationCount() const { return (int) animations.size(); }