Modify FBX2glTF to allow that several nodes share the same mesh. (#46)

This commit is contained in:
David Ávila 2017-11-28 10:16:01 +01:00 committed by Pär Winzell
parent f61814f7c9
commit fada9e45ee
4 changed files with 106 additions and 54 deletions

View File

@ -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<FbxVector4> normalLayer(pMesh->GetElementNormal(), pMesh->GetElementNormalCount());
@ -688,7 +705,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
const std::shared_ptr<FbxMaterialAccess> 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;

View File

@ -231,6 +231,15 @@ T &require(std::map<std::string, std::shared_ptr<T>> map, std::string key)
return result;
}
template<typename T>
T &require(std::map<long, std::shared_ptr<T>> map, long key)
{
auto iter = map.find(key);
assert(iter != map.end());
T &result = *iter->second;
return result;
}
static const std::vector<TriangleIndex> getIndexArray(const RawModel &raw)
{
std::vector<TriangleIndex> result;
@ -284,7 +293,7 @@ ModelData *Raw2Gltf(
std::map<std::string, std::shared_ptr<NodeData>> nodesByName;
std::map<std::string, std::shared_ptr<MaterialData>> materialsByName;
std::map<std::string, std::shared_ptr<MeshData>> meshByNodeName;
std::map<long, std::shared_ptr<MeshData>> 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()) {
auto meshIter = meshBySurfaceId.find(surfaceId);
if (meshIter != meshBySurfaceId.end()) {
mesh = meshIter->second.get();
} else {
}
else {
std::vector<float> 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<Mat4f> inverseBindMatrices;
for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) {
inverseBindMatrices.push_back(inverseBindMatrice.Transpose());
}
std::vector<uint32_t> 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<PrimitiveData> 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<Mat4f> inverseBindMatrices;
for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) {
inverseBindMatrices.push_back(inverseBindMatrice.Transpose());
}
std::vector<uint32_t> 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
//

View File

@ -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;
}

View File

@ -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<float, 3> bounds;
std::vector<std::string> 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(); }