[FBX] Allow export multi materials per node (#5888)

* [FBX] Allow export multi materials per node

Previously it was assumed that each node would export a single material. This removes that
assumption, and also allows for exporting multiple meshes with a single node. Previously they
would each be seprated into their own node.

* Support for animations with multiple meshes too

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
This commit is contained in:
Julian Knodt
2025-01-14 02:37:16 -08:00
committed by GitHub
parent 8e44b40df4
commit e722420907
2 changed files with 249 additions and 256 deletions

View File

@@ -1065,21 +1065,28 @@ void FBXExporter::WriteObjects () {
object_node.BeginChildren(outstream, binary, indent);
bool bJoinIdenticalVertices = mProperties->GetPropertyBool("bJoinIdenticalVertices", true);
std::vector<std::vector<int32_t>> vVertexIndice;//save vertex_indices as it is needed later
// save vertex_indices as it is needed later
std::vector<std::vector<int32_t>> vVertexIndice(mScene->mNumMeshes);
std::vector<uint32_t> uniq_v_before_mi;
const auto bTransparencyFactorReferencedToOpacity = mProperties->GetPropertyBool(AI_CONFIG_EXPORT_FBX_TRANSPARENCY_FACTOR_REFER_TO_OPACITY, false);
// geometry (aiMesh)
mesh_uids.clear();
indent = 1;
for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
// it's all about this mesh
aiMesh* m = mScene->mMeshes[mi];
std::function<void(const aiNode*)> visit_node_geo = [&](const aiNode *node) {
if (node->mNumMeshes == 0) {
for (uint32_t ni = 0; ni < node->mNumChildren; ni++) {
visit_node_geo(node->mChildren[ni]);
}
return;
}
// start the node record
FBX::Node n("Geometry");
int64_t uid = generate_uid();
mesh_uids.push_back(uid);
mesh_uids[node] = uid;
n.AddProperty(uid);
n.AddProperty(FBX::SEPARATOR + "Geometry");
n.AddProperty("Mesh");
@@ -1087,158 +1094,112 @@ void FBXExporter::WriteObjects () {
n.DumpProperties(outstream, binary, indent);
n.EndProperties(outstream, binary, indent);
n.BeginChildren(outstream, binary, indent);
indent = 2;
// output vertex data - each vertex should be unique (probably)
std::vector<double> flattened_vertices;
// index of original vertex in vertex data vector
std::vector<int32_t> vertex_indices;
// map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value;
if(bJoinIdenticalVertices){
int32_t index = 0;
for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
aiVector3D vtx = m->mVertices[vi];
auto elem = index_by_vertex_value.find(vtx);
if (elem == index_by_vertex_value.end()) {
vertex_indices.push_back(index);
index_by_vertex_value[vtx] = index;
flattened_vertices.push_back(vtx[0]);
flattened_vertices.push_back(vtx[1]);
flattened_vertices.push_back(vtx[2]);
++index;
} else {
vertex_indices.push_back(int32_t(elem->second));
}
}
}
else { // do not join vertex, respect the export flag
vertex_indices.resize(m->mNumVertices);
std::iota(vertex_indices.begin(), vertex_indices.end(), 0);
for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
aiVector3D vtx = m->mVertices[v];
flattened_vertices.push_back(vtx.x);
flattened_vertices.push_back(vtx.y);
flattened_vertices.push_back(vtx.z);
}
}
vVertexIndice.push_back(vertex_indices);
FBX::Node::WritePropertyNode(
"Vertices", flattened_vertices, outstream, binary, indent
);
std::vector<double> normal_data;
std::vector<double> color_data;
std::vector<int32_t> polygon_data;
std::vector<std::vector<double>> uv_data;
std::vector<std::vector<int32_t>> uv_indices;
std::map<aiVector3D, int32_t> index_by_uv;
std::vector<int32_t> offsets = { 0 };
indent = 2;
for (uint32_t n_mi = 0; n_mi < node->mNumMeshes; n_mi++) {
const auto mi = node->mMeshes[n_mi];
const aiMesh *m = mScene->mMeshes[mi];
size_t v_offset = vertex_indices.size();
size_t uniq_v_before = flattened_vertices.size() / 3;
// map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value;
if(bJoinIdenticalVertices){
int32_t index = 0;
for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
aiVector3D vtx = m->mVertices[vi];
auto elem = index_by_vertex_value.find(vtx);
if (elem == index_by_vertex_value.end()) {
vertex_indices.push_back(index);
index_by_vertex_value[vtx] = index;
flattened_vertices.insert(flattened_vertices.end(), { vtx.x, vtx.y, vtx.z });
++index;
} else {
vertex_indices.push_back(int32_t(elem->second));
}
}
} else { // do not join vertex, respect the export flag
vertex_indices.resize(v_offset + m->mNumVertices);
std::iota(vertex_indices.begin() + v_offset, vertex_indices.end(), (int)v_offset);
for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
aiVector3D vtx = m->mVertices[v];
flattened_vertices.insert(flattened_vertices.end(), {vtx.x, vtx.y, vtx.z});
}
}
vVertexIndice[mi].insert(
// TODO test whether this can be end or not
vVertexIndice[mi].begin(),
vertex_indices.begin(),
vertex_indices.end()
);
// here could be edges but they're insane.
// it's optional anyway, so let's ignore it.
// output polygon data as a flattened array of vertex indices.
// the last vertex index of each polygon is negated and - 1
std::vector<int32_t> polygon_data;
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) {
polygon_data.push_back(vertex_indices[f.mIndices[pvi]]);
size_t pvi = 0;
for (; pvi < f.mNumIndices - 1; pvi++) {
polygon_data.push_back(
static_cast<int32_t>(uniq_v_before + vertex_indices[v_offset + f.mIndices[pvi]])
);
}
polygon_data.push_back(
-1 - vertex_indices[f.mIndices[f.mNumIndices-1]]
static_cast<int32_t>(-1 - (uniq_v_before + vertex_indices[v_offset+f.mIndices[pvi]]))
);
}
FBX::Node::WritePropertyNode(
"PolygonVertexIndex", polygon_data, outstream, binary, indent
);
}
// here could be edges but they're insane.
// it's optional anyway, so let's ignore it.
uniq_v_before_mi.push_back(static_cast<uint32_t>(uniq_v_before));
FBX::Node::WritePropertyNode(
"GeometryVersion", int32_t(124), outstream, binary, indent
);
// normals, if any
if (m->HasNormals()) {
FBX::Node normals("LayerElementNormal", int32_t(0));
normals.Begin(outstream, binary, indent);
normals.DumpProperties(outstream, binary, indent);
normals.EndProperties(outstream, binary, indent);
normals.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode(
"Version", int32_t(101), outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"Name", "", outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"MappingInformationType", "ByPolygonVertex",
outstream, binary, indent
);
// TODO: vertex-normals or indexed normals when appropriate
FBX::Node::WritePropertyNode(
"ReferenceInformationType", "Direct",
outstream, binary, indent
);
std::vector<double> normal_data;
if (m->HasNormals()) {
normal_data.reserve(3 * polygon_data.size());
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
const aiVector3D &curN = m->mNormals[f.mIndices[pvi]];
normal_data.push_back(curN.x);
normal_data.push_back(curN.y);
normal_data.push_back(curN.z);
}
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace & f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; pvi++) {
const aiVector3D &curN = m->mNormals[f.mIndices[pvi]];
normal_data.insert(normal_data.end(), { curN.x, curN.y, curN.z });
}
}
FBX::Node::WritePropertyNode(
"Normals", normal_data, outstream, binary, indent
);
// note: version 102 has a NormalsW also... not sure what it is,
// so we can stick with version 101 for now.
indent = 2;
normals.End(outstream, binary, indent, true);
}
}
// colors, if any
for (size_t ci = 0; ci < m->GetNumColorChannels(); ++ci) {
FBX::Node vertexcolors("LayerElementColor", int32_t(ci));
vertexcolors.Begin(outstream, binary, indent);
vertexcolors.DumpProperties(outstream, binary, indent);
vertexcolors.EndProperties(outstream, binary, indent);
vertexcolors.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode(
"Version", int32_t(101), outstream, binary, indent
);
char layerName[8];
snprintf(layerName, sizeof(layerName), "COLOR_%d", int32_t(ci));
FBX::Node::WritePropertyNode(
"Name", (const char*)layerName, outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"MappingInformationType", "ByPolygonVertex",
outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"ReferenceInformationType", "Direct",
outstream, binary, indent
);
std::vector<double> color_data;
const int32_t colorChannelIndex = 0;
if (m->HasVertexColors(colorChannelIndex)) {
color_data.reserve(4 * polygon_data.size());
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
const aiColor4D &c = m->mColors[ci][f.mIndices[pvi]];
color_data.push_back(c.r);
color_data.push_back(c.g);
color_data.push_back(c.b);
color_data.push_back(c.a);
}
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; pvi++) {
const aiColor4D &c = m->mColors[colorChannelIndex][f.mIndices[pvi]];
color_data.insert(color_data.end(), { c.r, c.g, c.b, c.a });
}
}
FBX::Node::WritePropertyNode(
"Colors", color_data, outstream, binary, indent
);
indent = 2;
vertexcolors.End(outstream, binary, indent, true);
}
}
// uvs, if any
for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) {
const auto num_uv = static_cast<size_t>(m->GetNumUVChannels());
uv_indices.resize(std::max(num_uv, uv_indices.size()));
uv_data.resize(std::max(num_uv, uv_data.size()));
// uvs, if any
for (size_t uvi = 0; uvi < m->GetNumUVChannels(); uvi++) {
if (m->mNumUVComponents[uvi] > 2) {
// FBX only supports 2-channel UV maps...
// or at least i'm not sure how to indicate a different number
@@ -1254,71 +1215,111 @@ void FBXExporter::WriteObjects () {
err << " but may be incorrectly interpreted on load.";
ASSIMP_LOG_WARN(err.str());
}
FBX::Node uv("LayerElementUV", int32_t(uvi));
uv.Begin(outstream, binary, indent);
uv.DumpProperties(outstream, binary, indent);
uv.EndProperties(outstream, binary, indent);
uv.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode(
"Version", int32_t(101), outstream, binary, indent
);
// it doesn't seem like assimp keeps the uv map name,
// so just leave it blank.
FBX::Node::WritePropertyNode(
"Name", "", outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"MappingInformationType", "ByPolygonVertex",
outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"ReferenceInformationType", "IndexToDirect",
outstream, binary, indent
);
std::vector<double> uv_data;
std::vector<int32_t> uv_indices;
std::map<aiVector3D,int32_t> index_by_uv;
int32_t index = 0;
for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
const aiVector3D &curUv =
m->mTextureCoords[uvi][f.mIndices[pvi]];
auto elem = index_by_uv.find(curUv);
if (elem == index_by_uv.end()) {
index_by_uv[curUv] = index;
uv_indices.push_back(index);
for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) {
uv_data.push_back(curUv[x]);
}
++index;
} else {
uv_indices.push_back(elem->second);
}
for (size_t fi = 0; fi < m->mNumFaces; fi++) {
const aiFace &f = m->mFaces[fi];
for (size_t pvi = 0; pvi < f.mNumIndices; pvi++) {
const aiVector3D &curUv = m->mTextureCoords[uvi][f.mIndices[pvi]];
auto elem = index_by_uv.find(curUv);
if (elem == index_by_uv.end()) {
index_by_uv[curUv] = index;
uv_indices[uvi].push_back(index);
for (uint32_t x = 0; x < m->mNumUVComponents[uvi]; ++x) {
uv_data[uvi].push_back(curUv[x]);
}
++index;
} else {
uv_indices[uvi].push_back(elem->second);
}
}
}
FBX::Node::WritePropertyNode(
"UV", uv_data, outstream, binary, indent
);
FBX::Node::WritePropertyNode(
"UVIndex", uv_indices, outstream, binary, indent
);
indent = 2;
uv.End(outstream, binary, indent, true);
}
offsets.push_back((int32_t)polygon_data.size());
}
// i'm not really sure why this material section exists,
// as the material is linked via "Connections".
// it seems to always have the same "0" value.
FBX::Node::WritePropertyNode("Vertices", flattened_vertices, outstream, binary, indent);
FBX::Node::WritePropertyNode("PolygonVertexIndex", polygon_data, outstream, binary, indent);
FBX::Node::WritePropertyNode("GeometryVersion", int32_t(124), outstream, binary, indent);
FBX::Node normals("LayerElementNormal", int32_t(0));
normals.Begin(outstream, binary, indent);
normals.DumpProperties(outstream, binary, indent);
normals.EndProperties(outstream, binary, indent);
normals.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode("Version", int32_t(101),outstream,binary,indent);
FBX::Node::WritePropertyNode("Name", "",outstream,binary,indent);
FBX::Node::WritePropertyNode("MappingInformationType", "ByPolygonVertex",outstream,binary,indent);
FBX::Node::WritePropertyNode("ReferenceInformationType", "Direct",outstream,binary,indent);
FBX::Node::WritePropertyNode("Normals", normal_data,outstream,binary,indent);
// note: version 102 has a NormalsW also... not sure what it is,
// so stick with version 101 for now.
indent = 2;
normals.End(outstream,binary,indent,true);
const auto colorChannelIndex = 0;
FBX::Node vertexcolors("LayerElementColor", int32_t(colorChannelIndex));
vertexcolors.Begin(outstream, binary, indent);
vertexcolors.DumpProperties(outstream, binary, indent);
vertexcolors.EndProperties(outstream, binary, indent);
vertexcolors.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream, binary, indent);
char layerName[8];
snprintf(layerName, sizeof(layerName), "COLOR_%d", colorChannelIndex);
FBX::Node::WritePropertyNode("Name", (const char *)layerName, outstream, binary, indent);
FBX::Node::WritePropertyNode("MappingInformationType", "ByPolygonVertex", outstream, binary, indent);
FBX::Node::WritePropertyNode("ReferenceInformationType", "Direct", outstream, binary, indent);
FBX::Node::WritePropertyNode("Colors", color_data, outstream, binary, indent);
indent = 2;
vertexcolors.End(outstream, binary, indent, true);
for (uint32_t uvi = 0; uvi < uv_data.size(); uvi++) {
FBX::Node uv("LayerElementUV", int32_t(uvi));
uv.Begin(outstream, binary, indent);
uv.DumpProperties(outstream, binary, indent);
uv.EndProperties(outstream, binary, indent);
uv.BeginChildren(outstream, binary, indent);
indent = 3;
FBX::Node::WritePropertyNode("Version", int32_t(101), outstream, binary, indent);
FBX::Node::WritePropertyNode("Name", "", outstream, binary, indent);
FBX::Node::WritePropertyNode("MappingInformationType", "ByPolgonVertex", outstream, binary, indent);
FBX::Node::WritePropertyNode("ReferenceInformationType", "IndexToDirect", outstream, binary, indent);
FBX::Node::WritePropertyNode("UV", uv_data[uvi], outstream, binary, indent);
FBX::Node::WritePropertyNode("UVIndex", uv_indices[uvi], outstream, binary, indent);
indent = 2;
uv.End(outstream, binary, indent, true);
}
// When merging multiple meshes, we instead use by polygon so the correct material is
// assigned to each face. Previously, this LayerElementMaterial always had 0 since it
// assumed there was 1 material for each node for all meshes.
FBX::Node mat("LayerElementMaterial", int32_t(0));
mat.AddChild("Version", int32_t(101));
mat.AddChild("Name", "");
mat.AddChild("MappingInformationType", "AllSame");
mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices = {0};
mat.AddChild("Materials", mat_indices);
if (node->mNumMeshes == 1) {
mat.AddChild("MappingInformationType", "AllSame");
mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices = {0};
mat.AddChild("Materials", mat_indices);
} else {
mat.AddChild("MappingInformationType", "ByPolygon");
mat.AddChild("ReferenceInformationType", "IndexToDirect");
std::vector<int32_t> mat_indices(polygon_data.size());
uint32_t curr_offset = 0;
for (uint32_t mi = 0; mi < node->mNumMeshes; mi++) {
uint32_t num_faces = mScene->mMeshes[node->mMeshes[mi]]->mNumFaces;
for (uint32_t fi = 0; fi < num_faces; fi++) {
mat_indices[curr_offset + fi] = mi;
}
curr_offset += num_faces;
}
mat.AddChild("Materials", mat_indices);
}
mat.Dump(outstream, binary, indent);
// finally we have the layer specifications,
@@ -1331,12 +1332,10 @@ void FBXExporter::WriteObjects () {
le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le);
for (size_t ci = 0; ci < m->GetNumColorChannels(); ++ci) {
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementColor");
le.AddChild("TypedIndex", int32_t(ci));
layer.AddChild(le);
}
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementColor");
le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le);
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementMaterial");
@@ -1348,8 +1347,7 @@ void FBXExporter::WriteObjects () {
layer.AddChild(le);
layer.Dump(outstream, binary, indent);
for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr)
{
for(unsigned int lr = 1; lr < uv_data.size(); ++ lr) {
FBX::Node layerExtra("Layer", int32_t(lr));
layerExtra.AddChild("Version", int32_t(100));
FBX::Node leExtra("LayerElement");
@@ -1361,7 +1359,14 @@ void FBXExporter::WriteObjects () {
// finish the node record
indent = 1;
n.End(outstream, binary, indent, true);
}
for (uint32_t ni = 0; ni < node->mNumChildren; ni++) {
visit_node_geo(node->mChildren[ni]);
}
return;
};
visit_node_geo(mScene->mRootNode);
// aiMaterial
@@ -1726,7 +1731,8 @@ void FBXExporter::WriteObjects () {
dnode.AddChild("Version", int32_t(101));
dnode.Dump(outstream, binary, indent);
// connect it
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
const auto node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode);
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[node]);
std::vector<int32_t> vertex_indices = vVertexIndice[mi];
for (unsigned int am = 0; am < m->mNumAnimMeshes; ++am) {
@@ -1736,7 +1742,7 @@ void FBXExporter::WriteObjects () {
// start the node record
FBX::Node bsnode("Geometry");
int64_t blendshape_uid = generate_uid();
mesh_uids.push_back(blendshape_uid);
blendshape_uids.push_back(blendshape_uid);
bsnode.AddProperty(blendshape_uid);
bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Geometry");
bsnode.AddProperty("Shape");
@@ -1923,22 +1929,15 @@ void FBXExporter::WriteObjects () {
// otherwise check if this is the root of the skeleton
bool end = false;
// is the mesh part of this node?
for (size_t i = 0; i < parent->mNumMeshes; ++i) {
if (parent->mMeshes[i] == mi) {
end = true;
break;
}
for (size_t i = 0; i < parent->mNumMeshes && !end; ++i) {
end |= parent->mMeshes[i] == mi;
}
// is the mesh in one of the children of this node?
for (size_t j = 0; j < parent->mNumChildren; ++j) {
for (size_t j = 0; j < parent->mNumChildren && !end; ++j) {
aiNode* child = parent->mChildren[j];
for (size_t i = 0; i < child->mNumMeshes; ++i) {
if (child->mMeshes[i] == mi) {
end = true;
break;
}
for (size_t i = 0; i < child->mNumMeshes && !end; ++i) {
end |= child->mMeshes[i] == mi;
}
if (end) { break; }
}
// if it was the skeleton root we can finish here
@@ -1952,8 +1951,7 @@ void FBXExporter::WriteObjects () {
for (size_t i = 0; i < mScene->mNumMeshes; ++i) {
auto &s = skeleton_by_mesh[i];
for (const aiNode* n : s) {
auto elem = node_uids.find(n);
if (elem == node_uids.end()) {
if (node_uids.find(n) == node_uids.end()) {
node_uids[n] = generate_uid();
}
}
@@ -1969,6 +1967,8 @@ void FBXExporter::WriteObjects () {
if (!m->HasBones()) {
continue;
}
const aiNode *mesh_node = get_node_for_mesh((uint32_t)mi, mScene->mRootNode);
// make a deformer for this mesh
int64_t deformer_uid = generate_uid();
FBX::Node dnode("Deformer");
@@ -1980,10 +1980,7 @@ void FBXExporter::WriteObjects () {
dnode.Dump(outstream, binary, indent);
// connect it
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
//computed before
std::vector<int32_t>& vertex_indices = vVertexIndice[mi];
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mesh_node]);
// TODO, FIXME: this won't work if anything is not in the bind pose.
// for now if such a situation is detected, we throw an exception.
@@ -1997,7 +1994,6 @@ void FBXExporter::WriteObjects () {
// as it can be instanced to many nodes.
// All we can do is assume no instancing,
// and take the first node we find that contains the mesh.
aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode);
aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
// now make a subdeformer for each bone in the skeleton
@@ -2026,14 +2022,15 @@ void FBXExporter::WriteObjects () {
sdnode.AddChild("Version", int32_t(100));
sdnode.AddChild("UserData", "", "");
std::set<int32_t> setWeightedVertex;
// add indices and weights, if any
if (b) {
std::set<int32_t> setWeightedVertex;
std::vector<int32_t> subdef_indices;
std::vector<double> subdef_weights;
int32_t last_index = -1;
for (size_t wi = 0; wi < b->mNumWeights; ++wi) {
int32_t vi = vertex_indices[b->mWeights[wi].mVertexId];
int32_t vi = vVertexIndice[mi][b->mWeights[wi].mVertexId] \
+ uniq_v_before_mi[mi];
bool bIsWeightedAlready = (setWeightedVertex.find(vi) != setWeightedVertex.end());
if (vi == last_index || bIsWeightedAlready) {
// only for vertices we exported to fbx
@@ -2680,9 +2677,8 @@ void FBXExporter::WriteModelNodes(
// handled later
} else if (node->mNumMeshes == 1) {
// connect to child mesh, which should have been written previously
connections.emplace_back(
"C", "OO", mesh_uids[node->mMeshes[0]], node_uid
);
// TODO double check this line
connections.emplace_back("C", "OO", mesh_uids[node], node_uid);
// also connect to the material for the child mesh
connections.emplace_back(
"C", "OO",
@@ -2707,6 +2703,16 @@ void FBXExporter::WriteModelNodes(
na.Dump(outstream, binary, 1);
// and connect them
connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
} else if (node->mNumMeshes >= 1) {
connections.emplace_back("C", "OO", mesh_uids[node], node_uid);
for (size_t i = 0; i < node->mNumMeshes; i++) {
connections.emplace_back(
"C", "OO",
material_uids[mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex],
node_uid
);
}
WriteModelNode(outstream, binary, node, node_uid, "Mesh", transform_chain);
} else {
const auto& lightIt = lights_uids.find(node->mName.C_Str());
if(lightIt != lights_uids.end()) {
@@ -2723,34 +2729,20 @@ void FBXExporter::WriteModelNodes(
}
}
// if more than one child mesh, make nodes for each mesh
if (node->mNumMeshes > 1 || node == mScene->mRootNode) {
for (size_t i = 0; i < node->mNumMeshes; ++i) {
// make a new model node
int64_t new_node_uid = generate_uid();
// connect to parent node
connections.emplace_back("C", "OO", new_node_uid, node_uid);
// connect to child mesh, which should have been written previously
connections.emplace_back(
"C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid
);
// also connect to the material for the child mesh
connections.emplace_back(
"C", "OO",
material_uids[
mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex
],
new_node_uid
);
aiNode new_node;
// take name from mesh name, if it exists
new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName;
// write model node
WriteModelNode(
outstream, binary, &new_node, new_node_uid, "Mesh", std::vector<std::pair<std::string,aiVector3D>>()
);
}
if (node == mScene->mRootNode && node->mNumMeshes > 0) {
int64_t new_node_uid = generate_uid();
connections.emplace_back("C", "OO", new_node_uid, node_uid);
connections.emplace_back("C", "OO", mesh_uids[node], new_node_uid);
for (size_t i = 0; i < node->mNumMeshes; ++i) {
connections.emplace_back(
"C", "OO",
material_uids[mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex],
new_node_uid
);
}
aiNode new_node;
new_node.mName = mScene->mMeshes[0]->mName;
WriteModelNode(outstream, binary, &new_node, new_node_uid, "Mesh", {});
}
// now recurse into children

View File

@@ -90,7 +90,8 @@ namespace Assimp {
std::vector<FBX::Node> connections; // connection storage
std::vector<int64_t> mesh_uids;
std::map<const aiNode*, int64_t> mesh_uids;
std::vector<int64_t> blendshape_uids;
std::vector<int64_t> material_uids;
std::map<const aiNode*,int64_t> node_uids;
std::map<std::string,int64_t> lights_uids;