Fix #136378: Curve custom normals incorrectly propagated
Usually we prefer to propogate attributes based on name, even for
conversion between geometry types. Builtin attributes are a common
exception though. The Curve to Points node and the Curve to Mesh node
didn't correctly handle the "custom_normal" attribute, which shouldn't
be propagated. This has only been a problem since custom normals were
converted to a generic attribute on meshes in f9b627d29c.
Pull Request: https://projects.blender.org/blender/blender/pulls/136452
This commit is contained in:
@@ -365,8 +365,13 @@ static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attribut
|
||||
const AttributeFilter &attribute_filter)
|
||||
{
|
||||
|
||||
/* The position attribute has special non-generic evaluation. */
|
||||
if (id == "position") {
|
||||
/* The position attribute has special non-generic evaluation. */
|
||||
return false;
|
||||
}
|
||||
if (id == "custom_normal") {
|
||||
/* The custom normal attribute is builtin on both meshes and curves, but has a different
|
||||
* meaning and shouldn't be directly propagated. */
|
||||
return false;
|
||||
}
|
||||
/* Don't propagate built-in curves attributes that are not built-in on meshes. */
|
||||
|
||||
@@ -85,6 +85,7 @@ static void fill_rotation_attribute(const Span<float3> tangents,
|
||||
}
|
||||
|
||||
static void copy_curve_domain_attributes(const AttributeAccessor curve_attributes,
|
||||
const AttributeFilter &attribute_filter,
|
||||
MutableAttributeAccessor point_attributes)
|
||||
{
|
||||
curve_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
|
||||
@@ -94,6 +95,9 @@ static void copy_curve_domain_attributes(const AttributeAccessor curve_attribute
|
||||
if (iter.domain != AttrDomain::Curve) {
|
||||
return;
|
||||
}
|
||||
if (attribute_filter.allow_skip(iter.name)) {
|
||||
return;
|
||||
}
|
||||
if (iter.data_type == CD_PROP_STRING) {
|
||||
return;
|
||||
}
|
||||
@@ -104,32 +108,47 @@ static void copy_curve_domain_attributes(const AttributeAccessor curve_attribute
|
||||
});
|
||||
}
|
||||
|
||||
static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves,
|
||||
static PointCloud *pointcloud_from_curves(const bke::CurvesGeometry &curves,
|
||||
const AttributeFilter &attribute_filter,
|
||||
const std::optional<StringRef> &tangent_id,
|
||||
const std::optional<StringRef> &normal_id,
|
||||
const std::optional<StringRef> &rotation_id)
|
||||
{
|
||||
const AttributeAccessor curve_attributes = curves.attributes();
|
||||
|
||||
PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
|
||||
CustomData_free(&pointcloud->pdata);
|
||||
pointcloud->totpoint = curves.points_num();
|
||||
MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
|
||||
|
||||
const bke::AttributeFilterFromFunc filter = [&](const StringRef name) {
|
||||
if (attribute_filter.allow_skip(name)) {
|
||||
return bke::AttributeFilter::Result::AllowSkip;
|
||||
}
|
||||
if (curve_attributes.is_builtin(name) && !point_attributes.is_builtin(name)) {
|
||||
return bke::AttributeFilter::Result::AllowSkip;
|
||||
}
|
||||
return bke::AttributeFilter::Result::Process;
|
||||
};
|
||||
|
||||
bke::copy_attributes(curves.attributes(),
|
||||
bke::AttrDomain::Point,
|
||||
bke::AttrDomain::Point,
|
||||
filter,
|
||||
pointcloud->attributes_for_write());
|
||||
|
||||
copy_curve_domain_attributes(curve_attributes, filter, point_attributes);
|
||||
|
||||
if (rotation_id) {
|
||||
MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
const VArraySpan tangents = *attributes.lookup<float3>(*tangent_id, AttrDomain::Point);
|
||||
const VArraySpan normals = *attributes.lookup<float3>(*normal_id, AttrDomain::Point);
|
||||
SpanAttributeWriter<math::Quaternion> rotations =
|
||||
attributes.lookup_or_add_for_write_only_span<math::Quaternion>(*rotation_id,
|
||||
AttrDomain::Point);
|
||||
const VArraySpan tangents = *curve_attributes.lookup<float3>(*tangent_id, AttrDomain::Point);
|
||||
const VArraySpan normals = *curve_attributes.lookup<float3>(*normal_id, AttrDomain::Point);
|
||||
SpanAttributeWriter rotations =
|
||||
point_attributes.lookup_or_add_for_write_only_span<math::Quaternion>(*rotation_id,
|
||||
AttrDomain::Point);
|
||||
fill_rotation_attribute(tangents, normals, rotations.span);
|
||||
rotations.finish();
|
||||
}
|
||||
|
||||
/* Move the curve point custom data to the pointcloud, to avoid any copying. */
|
||||
CustomData_free(&pointcloud->pdata);
|
||||
pointcloud->pdata = curves.point_data;
|
||||
CustomData_reset(&curves.point_data);
|
||||
|
||||
copy_curve_domain_attributes(curves.attributes(), pointcloud->attributes_for_write());
|
||||
|
||||
return pointcloud;
|
||||
}
|
||||
|
||||
@@ -137,7 +156,8 @@ static void curve_to_points(GeometrySet &geometry_set,
|
||||
GeoNodeExecParams params,
|
||||
const GeometryNodeCurveResampleMode mode,
|
||||
geometry::ResampleCurvesOutputAttributeIDs resample_attributes,
|
||||
const std::optional<StringRef> &rotation_anonymous_id)
|
||||
const std::optional<StringRef> &rotation_anonymous_id,
|
||||
const AttributeFilter &attribute_filter)
|
||||
{
|
||||
switch (mode) {
|
||||
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
|
||||
@@ -152,7 +172,8 @@ static void curve_to_points(GeometrySet &geometry_set,
|
||||
fn::make_constant_field<bool>(true),
|
||||
count,
|
||||
resample_attributes);
|
||||
PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
|
||||
PointCloud *pointcloud = pointcloud_from_curves(dst_curves,
|
||||
attribute_filter,
|
||||
resample_attributes.tangent_id,
|
||||
resample_attributes.normal_id,
|
||||
rotation_anonymous_id);
|
||||
@@ -174,7 +195,8 @@ static void curve_to_points(GeometrySet &geometry_set,
|
||||
fn::make_constant_field<bool>(true),
|
||||
length,
|
||||
resample_attributes);
|
||||
PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
|
||||
PointCloud *pointcloud = pointcloud_from_curves(dst_curves,
|
||||
attribute_filter,
|
||||
resample_attributes.tangent_id,
|
||||
resample_attributes.normal_id,
|
||||
rotation_anonymous_id);
|
||||
@@ -191,7 +213,8 @@ static void curve_to_points(GeometrySet &geometry_set,
|
||||
const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};
|
||||
bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(
|
||||
src_curves, field_context, fn::make_constant_field<bool>(true), resample_attributes);
|
||||
PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
|
||||
PointCloud *pointcloud = pointcloud_from_curves(dst_curves,
|
||||
attribute_filter,
|
||||
resample_attributes.tangent_id,
|
||||
resample_attributes.normal_id,
|
||||
rotation_anonymous_id);
|
||||
@@ -265,7 +288,8 @@ static void grease_pencil_to_points(GeometrySet &geometry_set,
|
||||
break;
|
||||
}
|
||||
}
|
||||
pointcloud_by_layer[layer_index] = pointcloud_from_curves(std::move(dst_curves),
|
||||
pointcloud_by_layer[layer_index] = pointcloud_from_curves(dst_curves,
|
||||
attribute_filter,
|
||||
resample_attributes.tangent_id,
|
||||
resample_attributes.normal_id,
|
||||
rotation_anonymous_id);
|
||||
@@ -326,7 +350,8 @@ static void node_geo_exec(GeoNodeExecParams params)
|
||||
const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Points");
|
||||
|
||||
if (geometry_set.has_curves()) {
|
||||
curve_to_points(geometry_set, params, mode, resample_attributes, rotation_anonymous_id);
|
||||
curve_to_points(
|
||||
geometry_set, params, mode, resample_attributes, rotation_anonymous_id, attribute_filter);
|
||||
}
|
||||
if (geometry_set.has_grease_pencil()) {
|
||||
grease_pencil_to_points(
|
||||
|
||||
Reference in New Issue
Block a user