Fix T103304: Incorrect handling of edge crease in subdivision node

The crease custom data layer was added to a mutable version of the mesh,
but that wasn't used in the rest of the operation. Also the layer wasn't
retrieved properly with write access from the custom data API (fixed
separately as part of D14140). Also clean up a bit by retrieving
attributes from the mesh directly and by tweaking naming a bit.
This commit is contained in:
Hans Goudey 2023-01-13 15:34:01 -06:00
parent dc99c09daa
commit 0a21a554d5
Notes: blender-bot 2023-02-14 06:05:22 +01:00
Referenced by issue #103304, Regression: Two Subdivision surface node is acting weird
1 changed files with 26 additions and 23 deletions

View File

@ -69,7 +69,8 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
{
float *crease;
if (CustomData_has_layer(&mesh.vdata, CD_CREASE)) {
crease = static_cast<float *>(CustomData_get_layer(&mesh.vdata, CD_CREASE));
crease = static_cast<float *>(
CustomData_duplicate_referenced_layer(&mesh.vdata, CD_CREASE, mesh.totvert));
}
else {
crease = static_cast<float *>(
@ -78,11 +79,11 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert});
}
static void write_edge_creases(MeshComponent &mesh, const VArray<float> &crease_varray)
static void write_edge_creases(Mesh &mesh, const VArray<float> &crease_varray)
{
bke::SpanAttributeWriter<float> attribute =
mesh.attributes_for_write()->lookup_or_add_for_write_only_span<float>("crease",
ATTR_DOMAIN_EDGE);
mesh.attributes_for_write().lookup_or_add_for_write_only_span<float>("crease",
ATTR_DOMAIN_EDGE);
materialize_and_clamp_creases(crease_varray, attribute.span);
attribute.finish();
}
@ -115,23 +116,22 @@ static void node_geo_exec(GeoNodeExecParams params)
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_mesh()) {
const Mesh *mesh = geometry_set.get_mesh_for_read();
if (!mesh) {
return;
}
if (mesh->totvert == 0 || mesh->totedge == 0) {
return;
}
const Mesh &mesh = *geometry_set.get_mesh_for_read();
if (mesh.totvert == 0 || mesh.totedge == 0) {
return;
}
bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator point_evaluator(point_context, mesh.totvert);
bke::MeshFieldContext point_context{*mesh, ATTR_DOMAIN_POINT};
FieldEvaluator point_evaluator(point_context, mesh->totvert);
point_evaluator.add(vertex_crease_field);
point_evaluator.evaluate();
const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0);
bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator(edge_context, mesh.totedge);
bke::MeshFieldContext edge_context{*mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator(edge_context, mesh->totedge);
edge_evaluator.add(edge_crease_field);
edge_evaluator.evaluate();
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
@ -139,8 +139,13 @@ static void node_geo_exec(GeoNodeExecParams params)
const bool use_creases = varray_is_nonzero(vertex_creases) || varray_is_nonzero(edge_creases);
if (use_creases) {
write_vertex_creases(*geometry_set.get_mesh_for_write(), vertex_creases);
write_edge_creases(geometry_set.get_component_for_write<MeshComponent>(), edge_creases);
/* Due to the "BKE_subdiv" API, the crease layers must be on the input mesh. But in this node
* they are provided as separate inputs, not as custom data layers. When needed, retrieve the
* mesh with write access and store the new crease values there. */
Mesh &mesh_for_write = *geometry_set.get_mesh_for_write();
write_vertex_creases(mesh_for_write, vertex_creases);
write_edge_creases(mesh_for_write, edge_creases);
mesh = &mesh_for_write;
}
/* Initialize mesh settings. */
@ -161,25 +166,23 @@ static void node_geo_exec(GeoNodeExecParams params)
uv_smooth);
/* Apply subdivision to mesh. */
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh);
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh);
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
return;
}
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh);
Mesh *result = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh);
BKE_subdiv_free(subdiv);
if (use_creases) {
/* Remove the layer in case it was created by the node from the field input. The fact
* that this node uses #CD_CREASE to input creases to the subdivision code is meant to be
* an implementation detail ideally. */
CustomData_free_layers(&mesh_out->edata, CD_CREASE, mesh_out->totedge);
CustomData_free_layers(&result->edata, CD_CREASE, result->totedge);
}
geometry_set.replace_mesh(mesh_out);
BKE_subdiv_free(subdiv);
geometry_set.replace_mesh(result);
});
#endif
params.set_output("Mesh", std::move(geometry_set));