Geometry Nodes: Add "normal" attribute for face normals

This commit adds a `normal` attribute on the polygon domain. Since
normal data is derived data purely based off of the location of each
face's vertices, it is exposed as a read-only attribute. After
rB80f7f1070f17, this attribute can be interpolated to the other domains.

Since this attribute is a special case compared to the others, the
implementation subclasses `BuiltinAttributeProvider`. It's possible
there is a better way to abstract this. Something else might also
become apparent if we add similar read-only attributes.

See rB2966871a7a891bf36 for why this is preferred over the previous
implementation.

Differential Revision: https://developer.blender.org/D10677
This commit is contained in:
Hans Goudey 2021-03-13 14:13:16 -05:00
parent 2966871a7a
commit ba3a0dc9ba
3 changed files with 64 additions and 3 deletions

View File

@ -766,6 +766,65 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
};
/**
* This provider makes face normals available as a read-only float3 attribute.
*/
class NormalAttributeProvider final : public BuiltinAttributeProvider {
public:
NormalAttributeProvider()
: BuiltinAttributeProvider(
"normal", ATTR_DOMAIN_POLYGON, CD_PROP_FLOAT3, NonCreatable, Readonly, NonDeletable)
{
}
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
if (mesh == nullptr) {
return {};
}
/* Use existing normals if possible. */
if (!(mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL) &&
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
return std::make_unique<ArrayReadAttribute<float3>>(
ATTR_DOMAIN_POLYGON, Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
for (const int i : IndexRange(mesh->totpoly)) {
const MPoly *poly = &mesh->mpoly[i];
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_POLYGON,
std::move(normals));
}
WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const
{
return {};
}
bool try_delete(GeometryComponent &UNUSED(component)) const
{
return false;
}
bool try_create(GeometryComponent &UNUSED(component)) const
{
return false;
}
bool exists(const GeometryComponent &component) const
{
return component.attribute_domain_size(ATTR_DOMAIN_POLYGON) != 0;
}
};
/**
* In this function all the attribute providers for a mesh component are created. Most data in this
* function is statically allocated, because it does not change over time.
@ -818,6 +877,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
make_vertex_position_write_attribute,
tag_normals_dirty_when_writing_position);
static NormalAttributeProvider normal;
static BuiltinCustomDataLayerProvider material_index("material_index",
ATTR_DOMAIN_POLYGON,
CD_PROP_INT32,
@ -862,7 +923,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
static CustomDataAttributeProvider polygon_custom_data(ATTR_DOMAIN_POLYGON, polygon_access);
return ComponentAttributeProviders({&position, &material_index, &shade_smooth},
return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal},
{&uvs,
&vertex_colors,
&corner_custom_data,

View File

@ -381,7 +381,7 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
gather_attribute_info(attributes,
component_types,
set_groups,
{"position", "material_index", "vertex_normal", "shade_smooth"});
{"position", "material_index", "normal", "shade_smooth"});
join_attributes(
set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component));
}

View File

@ -224,7 +224,7 @@ static void join_components(Span<const MeshComponent *> src_components, Geometry
/* Don't copy attributes that are stored directly in the mesh data structs. */
join_attributes(to_base_components(src_components),
dst_component,
{"position", "material_index", "vertex_normal", "shade_smooth"});
{"position", "material_index", "normal", "shade_smooth"});
}
static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)