Skip to content

Commit

Permalink
Modify FBX2glTF to allow that several nodes share the same mesh. (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
davilovick authored and zellski committed Nov 28, 2017
1 parent f61814f commit fada9e4
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 54 deletions.
21 changes: 19 additions & 2 deletions src/Fbx2Raw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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;
Expand Down
113 changes: 67 additions & 46 deletions src/Raw2Gltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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];

Expand All @@ -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<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();
Expand All @@ -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));
Expand Down Expand Up @@ -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
//
Expand Down
20 changes: 16 additions & 4 deletions src/RawModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}

6 changes: 4 additions & 2 deletions src/RawModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -248,6 +248,7 @@ struct RawNode
Vec3f translation;
Quatf rotation;
Vec3f scale;
long surfaceId;
};

class RawModel
Expand All @@ -267,7 +268,7 @@ class RawModel
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,
Expand Down Expand Up @@ -307,6 +308,7 @@ class RawModel
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(); }
Expand Down

0 comments on commit fada9e4

Please sign in to comment.