diff options
author | Hans Goudey <h.goudey@me.com> | 2021-03-06 00:16:25 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-03-06 00:16:25 +0300 |
commit | f117ea26246355f423fd78785a3b00a2490bd9a4 (patch) | |
tree | 7eece56e3299c2e2f90691c10f3699d1d05a93c9 /source/blender | |
parent | becc36cce5248417fe4f626dedb50804c0e0eb1d (diff) |
Geometry Nodes: Expose vertex normals as an attribute
This attribute exposes mesh vertex normals as a `vertex_normal`
attribute for use with nodes. Since the normal vector stored in
vertices is only a cache of data computable from the surrounding faces,
the attribute is read-only. A proper error message for attempting to
write this attribute is part of T85749. A write-only normal attribute
will likely come later, most likely called `corner_normal`.
The normals are recomputed before reading if they are marked dirty.
This involves const write-access to the mesh, protected by the mutex
stored in `Mesh_Runtime`. This is essential for correct behavior after
nodes like "Edge Split" or nodes that adjust the position attribute.
Ref T84297, T85880, T86206
Differential Revision: https://developer.blender.org/D10541
Diffstat (limited to 'source/blender')
3 files changed, 70 insertions, 4 deletions
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index aeb7fba47e8..8348851741f 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -31,6 +31,7 @@ #include "BLI_color.hh" #include "BLI_float2.hh" #include "BLI_span.hh" +#include "BLI_threads.h" #include "CLG_log.h" @@ -712,11 +713,13 @@ struct CustomDataAccessInfo { class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size); using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size); + using UpdateOnRead = void (*)(const GeometryComponent &component); using UpdateOnWrite = void (*)(GeometryComponent &component); const CustomDataType stored_type_; const CustomDataAccessInfo custom_data_access_; const AsReadAttribute as_read_attribute_; const AsWriteAttribute as_write_attribute_; + const UpdateOnRead update_on_read_; const UpdateOnWrite update_on_write_; public: @@ -730,6 +733,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const CustomDataAccessInfo custom_data_access, const AsReadAttribute as_read_attribute, const AsWriteAttribute as_write_attribute, + const UpdateOnRead update_on_read, const UpdateOnWrite update_on_write) : BuiltinAttributeProvider( std::move(attribute_name), domain, attribute_type, creatable, writable, deletable), @@ -737,6 +741,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { custom_data_access_(custom_data_access), as_read_attribute_(as_read_attribute), as_write_attribute_(as_write_attribute), + update_on_read_(update_on_read), update_on_write_(update_on_write) { } @@ -747,6 +752,11 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { if (custom_data == nullptr) { return {}; } + + if (update_on_read_ != nullptr) { + update_on_read_(component); + } + const int domain_size = component.attribute_domain_size(domain_); const void *data = CustomData_get_layer(custom_data, stored_type_); if (data == nullptr) { @@ -1350,6 +1360,43 @@ static WriteAttributePtr make_material_index_write_attribute(void *data, const i ATTR_DOMAIN_POLYGON, MutableSpan<MPoly>((MPoly *)data, domain_size)); } +static float3 get_vertex_normal(const MVert &vert) +{ + float3 result; + normal_short_to_float_v3(result, vert.no); + return result; +} + +static ReadAttributePtr make_vertex_normal_read_attribute(const void *data, const int domain_size) +{ + return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_normal>>( + ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size)); +} + +static void update_vertex_normals_when_dirty(const GeometryComponent &component) +{ + const Mesh *mesh = get_mesh_from_component_for_read(component); + if (mesh == nullptr) { + return; + } + + /* Since normals are derived data, const write access to them is okay. However, ensure that + * two threads don't use write normals to a mesh at the same time. Note that this relies on + * the idempotence of the operation; calculating the normals just fills the MVert struct + * rather than allocating new memory. */ + if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { + ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex; + BLI_mutex_lock(mesh_eval_mutex); + + /* Check again to avoid a second thread needlessly recalculating the same normals. */ + if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { + BKE_mesh_calc_normals(const_cast<Mesh *>(mesh)); + } + + BLI_mutex_unlock(mesh_eval_mutex); + } +} + static float2 get_loop_uv(const MLoopUV &uv) { return float2(uv.uv); @@ -1459,6 +1506,7 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() point_access, make_vertex_position_read_attribute, make_vertex_position_write_attribute, + nullptr, tag_normals_dirty_when_writing_position); static BuiltinCustomDataLayerProvider material_index("material_index", @@ -1471,8 +1519,22 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() polygon_access, make_material_index_read_attribute, make_material_index_write_attribute, + nullptr, nullptr); + static BuiltinCustomDataLayerProvider vertex_normal("vertex_normal", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_MVERT, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Readonly, + BuiltinAttributeProvider::NonDeletable, + point_access, + make_vertex_normal_read_attribute, + nullptr, + update_vertex_normals_when_dirty, + nullptr); + static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, CD_MLOOPUV, @@ -1493,7 +1555,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}, + return ComponentAttributeProviders({&position, &material_index, &vertex_normal}, {&uvs, &vertex_colors, &corner_custom_data, @@ -1541,6 +1603,7 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() point_access, make_array_read_attribute<float3, ATTR_DOMAIN_POINT>, make_array_write_attribute<float3, ATTR_DOMAIN_POINT>, + nullptr, nullptr); static BuiltinCustomDataLayerProvider radius( "radius", @@ -1553,6 +1616,7 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() point_access, make_array_read_attribute<float, ATTR_DOMAIN_POINT>, make_array_write_attribute<float, ATTR_DOMAIN_POINT>, + nullptr, nullptr); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); return ComponentAttributeProviders({&position, &radius}, {&point_custom_data}); diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 1a260c5d48e..6c4c3231667 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -378,7 +378,8 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, /* Don't copy attributes that are stored directly in the mesh data structs. */ Map<std::string, AttributeKind> attributes; - gather_attribute_info(attributes, component_types, set_groups, {"position", "material_index"}); + gather_attribute_info( + attributes, component_types, set_groups, {"position", "material_index", "vertex_normal"}); join_attributes( set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 4e15f232934..9dce52c072d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -222,8 +222,9 @@ static void join_components(Span<const MeshComponent *> src_components, Geometry dst_component.replace(new_mesh); /* 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"}); + join_attributes(to_base_components(src_components), + dst_component, + {"position", "material_index", "vertex_normal"}); } static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result) |