From 5688f791f51827d77f4479bee90c4e88add72ce6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 16 Feb 2021 12:30:42 +0100 Subject: Geometry Nodes: move realize-instances code to blenkernel I need to access this functionality from modifier code. Therefore it should not be in the nodes module. --- .../blenkernel/BKE_geometry_set_instances.hh | 19 ++ .../blenkernel/intern/geometry_set_instances.cc | 247 +++++++++++++++++++++ source/blender/nodes/NOD_geometry_exec.hh | 2 + .../blender/nodes/geometry/node_geometry_util.cc | 243 -------------------- .../blender/nodes/geometry/node_geometry_util.hh | 18 -- 5 files changed, 268 insertions(+), 261 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 11725d75df9..4f3c9c65203 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#pragma once + #include "BKE_geometry_set.hh" namespace blender::bke { @@ -39,4 +41,21 @@ struct GeometryInstanceGroup { Vector geometry_set_gather_instances(const GeometrySet &geometry_set); +GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set); + +struct AttributeInfo { + CustomDataType data_type; + AttributeDomain domain; +}; + +/** + * Add information about all the attributes on every component of the type. The resulting info + * will contain the highest complexity data type and the highest priority domain among every + * attribute with the given name on all of the input components. + */ +void gather_attribute_info(Map &attributes, + const GeometryComponentType component_type, + Span set_groups, + const Set &ignored_attributes); + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 6410aeb49fa..8e9edc86bca 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -15,10 +15,14 @@ */ #include "BKE_geometry_set_instances.hh" +#include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_pointcloud.h" #include "DNA_collection_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" namespace blender::bke { @@ -160,4 +164,247 @@ Vector geometry_set_gather_instances(const GeometrySet &g return result_vector; } +void gather_attribute_info(Map &attributes, + const GeometryComponentType component_type, + Span set_groups, + const Set &ignored_attributes) +{ + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (!set.has(component_type)) { + continue; + } + const GeometryComponent &component = *set.get_component_for_read(component_type); + + for (const std::string &name : component.attribute_names()) { + if (ignored_attributes.contains(name)) { + continue; + } + const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name); + if (!read_attribute) { + continue; + } + const AttributeDomain domain = read_attribute->domain(); + const CustomDataType data_type = read_attribute->custom_data_type(); + + auto add_info = [&, data_type, domain](AttributeInfo *info) { + info->domain = domain; + info->data_type = data_type; + }; + auto modify_info = [&, data_type, domain](AttributeInfo *info) { + info->domain = domain; /* TODO: Use highest priority domain. */ + info->data_type = bke::attribute_data_type_highest_complexity( + {info->data_type, data_type}); + }; + + attributes.add_or_modify(name, add_info, modify_info); + } + } +} + +static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups) +{ + int totverts = 0; + int totloops = 0; + int totedges = 0; + int totpolys = 0; + int64_t cd_dirty_vert = 0; + int64_t cd_dirty_poly = 0; + int64_t cd_dirty_edge = 0; + int64_t cd_dirty_loop = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has_mesh()) { + const Mesh &mesh = *set.get_mesh_for_read(); + totverts += mesh.totvert * set_group.transforms.size(); + totloops += mesh.totloop * set_group.transforms.size(); + totedges += mesh.totedge * set_group.transforms.size(); + totpolys += mesh.totpoly * set_group.transforms.size(); + cd_dirty_vert |= mesh.runtime.cd_dirty_vert; + cd_dirty_poly |= mesh.runtime.cd_dirty_poly; + cd_dirty_edge |= mesh.runtime.cd_dirty_edge; + cd_dirty_loop |= mesh.runtime.cd_dirty_loop; + } + } + + Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); + /* Copy settings from the first input geometry set with a mesh. */ + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has_mesh()) { + const Mesh &mesh = *set.get_mesh_for_read(); + BKE_mesh_copy_settings(new_mesh, &mesh); + break; + } + } + new_mesh->runtime.cd_dirty_vert = cd_dirty_vert; + new_mesh->runtime.cd_dirty_poly = cd_dirty_poly; + new_mesh->runtime.cd_dirty_edge = cd_dirty_edge; + new_mesh->runtime.cd_dirty_loop = cd_dirty_loop; + + int vert_offset = 0; + int loop_offset = 0; + int edge_offset = 0; + int poly_offset = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has_mesh()) { + const Mesh &mesh = *set.get_mesh_for_read(); + for (const float4x4 &transform : set_group.transforms) { + for (const int i : IndexRange(mesh.totvert)) { + const MVert &old_vert = mesh.mvert[i]; + MVert &new_vert = new_mesh->mvert[vert_offset + i]; + + new_vert = old_vert; + + const float3 new_position = transform * float3(old_vert.co); + copy_v3_v3(new_vert.co, new_position); + } + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &old_edge = mesh.medge[i]; + MEdge &new_edge = new_mesh->medge[edge_offset + i]; + new_edge = old_edge; + new_edge.v1 += vert_offset; + new_edge.v2 += vert_offset; + } + for (const int i : IndexRange(mesh.totloop)) { + const MLoop &old_loop = mesh.mloop[i]; + MLoop &new_loop = new_mesh->mloop[loop_offset + i]; + new_loop = old_loop; + new_loop.v += vert_offset; + new_loop.e += edge_offset; + } + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &old_poly = mesh.mpoly[i]; + MPoly &new_poly = new_mesh->mpoly[poly_offset + i]; + new_poly = old_poly; + new_poly.loopstart += loop_offset; + } + + vert_offset += mesh.totvert; + loop_offset += mesh.totloop; + edge_offset += mesh.totedge; + poly_offset += mesh.totpoly; + } + } + } + + return new_mesh; +} + +static void join_attributes(Span set_groups, + const GeometryComponentType component_type, + const Map &attribute_info, + GeometryComponent &result) +{ + for (Map::Item entry : attribute_info.items()) { + StringRef name = entry.key; + const AttributeDomain domain_output = entry.value.domain; + const CustomDataType data_type_output = entry.value.data_type; + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); + BLI_assert(cpp_type != nullptr); + + result.attribute_try_create(entry.key, domain_output, data_type_output); + WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); + if (!write_attribute || &write_attribute->cpp_type() != cpp_type || + write_attribute->domain() != domain_output) { + continue; + } + fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); + + int offset = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has(component_type)) { + const GeometryComponent &component = *set.get_component_for_read(component_type); + const int domain_size = component.attribute_domain_size(domain_output); + if (domain_size == 0) { + continue; /* Domain size is 0, so no need to increment the offset. */ + } + ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + name, domain_output, data_type_output); + + if (source_attribute) { + fn::GSpan src_span = source_attribute->get_span(); + const void *src_buffer = src_span.data(); + for (const int UNUSED(i) : set_group.transforms.index_range()) { + void *dst_buffer = dst_span[offset]; + cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); + offset += domain_size; + } + } + else { + offset += domain_size * set_group.transforms.size(); + } + } + } + + write_attribute->apply_span(); + } +} + +static void join_instance_groups_mesh(Span set_groups, GeometrySet &result) +{ + Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups); + + MeshComponent &dst_component = result.get_component_for_write(); + dst_component.replace(new_mesh); + + /* The position attribute is handled above already. */ + Map attributes; + gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {"position"}); + join_attributes(set_groups, + GeometryComponentType::Mesh, + attributes, + static_cast(dst_component)); +} + +static void join_instance_groups_pointcloud(Span set_groups, + GeometrySet &result) +{ + int totpoint = 0; + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + if (set.has()) { + const PointCloudComponent &component = *set.get_component_for_read(); + totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); + } + } + + PointCloudComponent &dst_component = result.get_component_for_write(); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint); + dst_component.replace(pointcloud); + Map attributes; + gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {}); + join_attributes(set_groups, + GeometryComponentType::PointCloud, + attributes, + static_cast(dst_component)); +} + +static void join_instance_groups_volume(Span set_groups, + GeometrySet &result) +{ + /* Not yet supported. Joining volume grids with the same name requires resampling of at least + * one of the grids. The cell size of the resulting volume has to be determined somehow. */ + VolumeComponent &dst_component = result.get_component_for_write(); + UNUSED_VARS(set_groups, dst_component); +} + +GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) +{ + if (!geometry_set.has_instances()) { + return geometry_set; + } + + GeometrySet new_geometry_set; + + Vector set_groups = bke::geometry_set_gather_instances(geometry_set); + join_instance_groups_mesh(set_groups, new_geometry_set); + join_instance_groups_pointcloud(set_groups, new_geometry_set); + join_instance_groups_volume(set_groups, new_geometry_set); + + return new_geometry_set; +} + } // namespace blender::bke diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 222aa559e8a..1149ab51ad0 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -20,6 +20,7 @@ #include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" +#include "BKE_geometry_set_instances.hh" #include "BKE_persistent_data_handle.hh" #include "DNA_node_types.h" @@ -38,6 +39,7 @@ using bke::Float3ReadAttribute; using bke::Float3WriteAttribute; using bke::FloatReadAttribute; using bke::FloatWriteAttribute; +using bke::geometry_set_realize_instances; using bke::Int32ReadAttribute; using bke::Int32WriteAttribute; using bke::PersistentDataHandleMap; diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index daee693c24f..0f725ecf211 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -28,249 +28,6 @@ namespace blender::nodes { using bke::GeometryInstanceGroup; -void gather_attribute_info(Map &attributes, - const GeometryComponentType component_type, - Span set_groups, - const Set &ignored_attributes) -{ - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (!set.has(component_type)) { - continue; - } - const GeometryComponent &component = *set.get_component_for_read(component_type); - - for (const std::string &name : component.attribute_names()) { - if (ignored_attributes.contains(name)) { - continue; - } - const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name); - if (!read_attribute) { - continue; - } - const AttributeDomain domain = read_attribute->domain(); - const CustomDataType data_type = read_attribute->custom_data_type(); - - auto add_info = [&, data_type, domain](AttributeInfo *info) { - info->domain = domain; - info->data_type = data_type; - }; - auto modify_info = [&, data_type, domain](AttributeInfo *info) { - info->domain = domain; /* TODO: Use highest priority domain. */ - info->data_type = bke::attribute_data_type_highest_complexity( - {info->data_type, data_type}); - }; - - attributes.add_or_modify(name, add_info, modify_info); - } - } -} - -static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups) -{ - int totverts = 0; - int totloops = 0; - int totedges = 0; - int totpolys = 0; - int64_t cd_dirty_vert = 0; - int64_t cd_dirty_poly = 0; - int64_t cd_dirty_edge = 0; - int64_t cd_dirty_loop = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (set.has_mesh()) { - const Mesh &mesh = *set.get_mesh_for_read(); - totverts += mesh.totvert * set_group.transforms.size(); - totloops += mesh.totloop * set_group.transforms.size(); - totedges += mesh.totedge * set_group.transforms.size(); - totpolys += mesh.totpoly * set_group.transforms.size(); - cd_dirty_vert |= mesh.runtime.cd_dirty_vert; - cd_dirty_poly |= mesh.runtime.cd_dirty_poly; - cd_dirty_edge |= mesh.runtime.cd_dirty_edge; - cd_dirty_loop |= mesh.runtime.cd_dirty_loop; - } - } - - Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); - /* Copy settings from the first input geometry set with a mesh. */ - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (set.has_mesh()) { - const Mesh &mesh = *set.get_mesh_for_read(); - BKE_mesh_copy_settings(new_mesh, &mesh); - break; - } - } - new_mesh->runtime.cd_dirty_vert = cd_dirty_vert; - new_mesh->runtime.cd_dirty_poly = cd_dirty_poly; - new_mesh->runtime.cd_dirty_edge = cd_dirty_edge; - new_mesh->runtime.cd_dirty_loop = cd_dirty_loop; - - int vert_offset = 0; - int loop_offset = 0; - int edge_offset = 0; - int poly_offset = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (set.has_mesh()) { - const Mesh &mesh = *set.get_mesh_for_read(); - for (const float4x4 &transform : set_group.transforms) { - for (const int i : IndexRange(mesh.totvert)) { - const MVert &old_vert = mesh.mvert[i]; - MVert &new_vert = new_mesh->mvert[vert_offset + i]; - - new_vert = old_vert; - - const float3 new_position = transform * float3(old_vert.co); - copy_v3_v3(new_vert.co, new_position); - } - for (const int i : IndexRange(mesh.totedge)) { - const MEdge &old_edge = mesh.medge[i]; - MEdge &new_edge = new_mesh->medge[edge_offset + i]; - new_edge = old_edge; - new_edge.v1 += vert_offset; - new_edge.v2 += vert_offset; - } - for (const int i : IndexRange(mesh.totloop)) { - const MLoop &old_loop = mesh.mloop[i]; - MLoop &new_loop = new_mesh->mloop[loop_offset + i]; - new_loop = old_loop; - new_loop.v += vert_offset; - new_loop.e += edge_offset; - } - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &old_poly = mesh.mpoly[i]; - MPoly &new_poly = new_mesh->mpoly[poly_offset + i]; - new_poly = old_poly; - new_poly.loopstart += loop_offset; - } - - vert_offset += mesh.totvert; - loop_offset += mesh.totloop; - edge_offset += mesh.totedge; - poly_offset += mesh.totpoly; - } - } - } - - return new_mesh; -} - -static void join_attributes(Span set_groups, - const GeometryComponentType component_type, - const Map &attribute_info, - GeometryComponent &result) -{ - for (Map::Item entry : attribute_info.items()) { - StringRef name = entry.key; - const AttributeDomain domain_output = entry.value.domain; - const CustomDataType data_type_output = entry.value.data_type; - const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); - BLI_assert(cpp_type != nullptr); - - result.attribute_try_create(entry.key, domain_output, data_type_output); - WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); - if (!write_attribute || &write_attribute->cpp_type() != cpp_type || - write_attribute->domain() != domain_output) { - continue; - } - fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); - - int offset = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (set.has(component_type)) { - const GeometryComponent &component = *set.get_component_for_read(component_type); - const int domain_size = component.attribute_domain_size(domain_output); - if (domain_size == 0) { - continue; /* Domain size is 0, so no need to increment the offset. */ - } - ReadAttributePtr source_attribute = component.attribute_try_get_for_read( - name, domain_output, data_type_output); - - if (source_attribute) { - fn::GSpan src_span = source_attribute->get_span(); - const void *src_buffer = src_span.data(); - for (const int UNUSED(i) : set_group.transforms.index_range()) { - void *dst_buffer = dst_span[offset]; - cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); - offset += domain_size; - } - } - else { - offset += domain_size * set_group.transforms.size(); - } - } - } - - write_attribute->apply_span(); - } -} - -static void join_instance_groups_mesh(Span set_groups, GeometrySet &result) -{ - Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups); - - MeshComponent &dst_component = result.get_component_for_write(); - dst_component.replace(new_mesh); - - /* The position attribute is handled above already. */ - Map attributes; - gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {"position"}); - join_attributes(set_groups, - GeometryComponentType::Mesh, - attributes, - static_cast(dst_component)); -} - -static void join_instance_groups_pointcloud(Span set_groups, - GeometrySet &result) -{ - int totpoint = 0; - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - if (set.has()) { - const PointCloudComponent &component = *set.get_component_for_read(); - totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); - } - } - - PointCloudComponent &dst_component = result.get_component_for_write(); - PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint); - dst_component.replace(pointcloud); - Map attributes; - gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {}); - join_attributes(set_groups, - GeometryComponentType::PointCloud, - attributes, - static_cast(dst_component)); -} - -static void join_instance_groups_volume(Span set_groups, - GeometrySet &result) -{ - /* Not yet supported. Joining volume grids with the same name requires resampling of at least - * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - VolumeComponent &dst_component = result.get_component_for_write(); - UNUSED_VARS(set_groups, dst_component); -} - -GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) -{ - if (!geometry_set.has_instances()) { - return geometry_set; - } - - GeometrySet new_geometry_set; - - Vector set_groups = bke::geometry_set_gather_instances(geometry_set); - join_instance_groups_mesh(set_groups, new_geometry_set); - join_instance_groups_pointcloud(set_groups, new_geometry_set); - join_instance_groups_volume(set_groups, new_geometry_set); - - return new_geometry_set; -} - /** * Update the availability of a group of input sockets with the same name, * used for switching between attribute inputs or single values. diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 78418b37011..271e3771006 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -25,7 +25,6 @@ #include "DNA_node_types.h" -#include "BKE_geometry_set_instances.hh" #include "BKE_node.h" #include "BLT_translation.h" @@ -48,21 +47,4 @@ void update_attribute_input_socket_availabilities(bNode &node, Array get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); -GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set); - -struct AttributeInfo { - CustomDataType data_type; - AttributeDomain domain; -}; - -/** - * Add information about all the attributes on every component of the type. The resulting info - * will contain the highest complexity data type and the highest priority domain among every - * attribute with the given name on all of the input components. - */ -void gather_attribute_info(Map &attributes, - const GeometryComponentType component_type, - Span set_groups, - const Set &ignored_attributes); - } // namespace blender::nodes -- cgit v1.2.3