Skip to content

Commit

Permalink
gltfpack: Minor mesh processing refactoring
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zeux committed Jul 10, 2024
1 parent 769e168 commit f16ee88
Showing 1 changed file with 63 additions and 66 deletions.
129 changes: 63 additions & 66 deletions gltf/gltfpack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -269,78 +331,15 @@ 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);

markScenes(data, nodes);
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);
Expand Down Expand Up @@ -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());
Expand Down

0 comments on commit f16ee88

Please sign in to comment.