[Bf-blender-cvs] [f117ea26246] master: Geometry Nodes: Expose vertex normals as an attribute

Hans Goudey noreply at git.blender.org
Fri Mar 5 22:16:31 CET 2021


Commit: f117ea26246355f423fd78785a3b00a2490bd9a4
Author: Hans Goudey
Date:   Fri Mar 5 15:16:25 2021 -0600
Branches: master
https://developer.blender.org/rBf117ea26246355f423fd78785a3b00a2490bd9a4

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

===================================================================

M	source/blender/blenkernel/intern/attribute_access.cc
M	source/blender/blenkernel/intern/geometry_set_instances.cc
M	source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc

===================================================================

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)



More information about the Bf-blender-cvs mailing list