gltfpack: Minor mesh processing refactoring

The logic for detaching mesh from nodes is fairly complex but it's a
little difficult to separate into individual components due to multiple
scenes handling, so for now just extract it into a separate function to
make the high level processing flow easier to read.
This commit is contained in:
Arseny Kapoulkine
2024-07-10 11:13:29 -07:00
parent cf035b62e7
commit d548aeb08d
+63 -66
View File
@@ -250,6 +250,68 @@ static bool canTransformMesh(const Mesh& mesh)
return true;
}
static void detachMesh(Mesh& mesh, cgltf_data* data, const std::vector<NodeInfo>& nodes, const Settings& settings)
{
// mesh is already instanced, skip
if (!mesh.instances.empty())
return;
// mesh is already world space, skip
if (mesh.nodes.empty())
return;
// note: when -kn is specified, we keep mesh-node attachment so that named nodes can be transformed
if (settings.keep_nodes)
return;
// we keep skinned meshes or meshes with morph targets as is
// in theory we could transform both, but in practice transforming morph target meshes is more involved,
// and reparenting skinned meshes leads to incorrect bounding box generated in three.js
if (mesh.skin || mesh.targets)
return;
bool any_animated = false;
for (size_t j = 0; j < mesh.nodes.size(); ++j)
any_animated |= nodes[mesh.nodes[j] - data->nodes].animated;
// animated meshes will be anchored to the same node that they used to be in to retain the animation
if (any_animated)
return;
int scene = nodes[mesh.nodes[0] - data->nodes].scene;
bool any_other_scene = false;
for (size_t j = 0; j < mesh.nodes.size(); ++j)
any_other_scene |= scene != nodes[mesh.nodes[j] - data->nodes].scene;
// we only merge instances when all nodes have a single consistent scene
if (scene < 0 || any_other_scene)
return;
// we only merge multiple instances together if requested
// this often makes the scenes faster to render by reducing the draw call count, but can result in larger files
if (mesh.nodes.size() > 1 && !settings.mesh_merge && !settings.mesh_instancing)
return;
// prefer instancing if possible, use merging otherwise
if (mesh.nodes.size() > 1 && settings.mesh_instancing)
{
mesh.instances.resize(mesh.nodes.size());
for (size_t j = 0; j < mesh.nodes.size(); ++j)
cgltf_node_transform_world(mesh.nodes[j], mesh.instances[j].data);
mesh.nodes.clear();
mesh.scene = scene;
}
else if (canTransformMesh(mesh))
{
mergeMeshInstances(mesh);
assert(mesh.nodes.empty());
mesh.scene = scene;
}
}
static bool isExtensionSupported(const ExtensionInfo* extensions, size_t count, const char* name)
{
for (size_t i = 0; i < count; ++i)
@@ -269,9 +331,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
}
for (size_t i = 0; i < animations.size(); ++i)
{
processAnimation(animations[i], settings);
}
std::vector<NodeInfo> nodes(data->nodes_count);
@@ -279,68 +339,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
markAnimated(data, nodes, animations);
for (size_t i = 0; i < meshes.size(); ++i)
{
Mesh& mesh = meshes[i];
// mesh is already instanced, skip
if (!mesh.instances.empty())
continue;
// mesh is already world space, skip
if (mesh.nodes.empty())
continue;
// note: when -kn is specified, we keep mesh-node attachment so that named nodes can be transformed
if (settings.keep_nodes)
continue;
// we keep skinned meshes or meshes with morph targets as is
// in theory we could transform both, but in practice transforming morph target meshes is more involved,
// and reparenting skinned meshes leads to incorrect bounding box generated in three.js
if (mesh.skin || mesh.targets)
continue;
bool any_animated = false;
for (size_t j = 0; j < mesh.nodes.size(); ++j)
any_animated |= nodes[mesh.nodes[j] - data->nodes].animated;
// animated meshes will be anchored to the same node that they used to be in to retain the animation
if (any_animated)
continue;
int scene = nodes[mesh.nodes[0] - data->nodes].scene;
bool any_other_scene = false;
for (size_t j = 0; j < mesh.nodes.size(); ++j)
any_other_scene |= scene != nodes[mesh.nodes[j] - data->nodes].scene;
// we only merge instances when all nodes have a single consistent scene
if (scene < 0 || any_other_scene)
continue;
// we only merge multiple instances together if requested
// this often makes the scenes faster to render by reducing the draw call count, but can result in larger files
if (mesh.nodes.size() > 1 && !settings.mesh_merge && !settings.mesh_instancing)
continue;
// prefer instancing if possible, use merging otherwise
if (mesh.nodes.size() > 1 && settings.mesh_instancing)
{
mesh.instances.resize(mesh.nodes.size());
for (size_t j = 0; j < mesh.nodes.size(); ++j)
cgltf_node_transform_world(mesh.nodes[j], mesh.instances[j].data);
mesh.nodes.clear();
mesh.scene = scene;
}
else if (canTransformMesh(mesh))
{
mergeMeshInstances(mesh);
assert(mesh.nodes.empty());
mesh.scene = scene;
}
}
detachMesh(meshes[i], data, nodes, settings);
// material information is required for mesh and image processing
std::vector<MaterialInfo> materials(data->materials_count);
@@ -406,9 +405,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
#endif
for (size_t i = 0; i < meshes.size(); ++i)
{
processMesh(meshes[i], settings);
}
#ifndef NDEBUG
meshes.insert(meshes.end(), debug_meshes.begin(), debug_meshes.end());