Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/geometry_set_instances.cc
| Show All 18 Lines | |||||
| #include "BKE_mesh_wrapper.h" | #include "BKE_mesh_wrapper.h" | ||||
| #include "BKE_modifier.h" | #include "BKE_modifier.h" | ||||
| #include "BKE_pointcloud.h" | #include "BKE_pointcloud.h" | ||||
| #include "DNA_collection_types.h" | #include "DNA_collection_types.h" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_pointcloud_types.h" | |||||
| namespace blender::bke { | namespace blender::bke { | ||||
| static void geometry_set_collect_recursive(const GeometrySet &geometry_set, | static void geometry_set_collect_recursive(const GeometrySet &geometry_set, | ||||
| const float4x4 &transform, | const float4x4 &transform, | ||||
| Vector<GeometryInstanceGroup> &r_sets); | Vector<GeometryInstanceGroup> &r_sets); | ||||
| static void geometry_set_collect_recursive_collection(const Collection &collection, | static void geometry_set_collect_recursive_collection(const Collection &collection, | ||||
| ▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set) | ||||
| unit_m4(unit_transform.values); | unit_m4(unit_transform.values); | ||||
| geometry_set_collect_recursive(geometry_set, unit_transform, result_vector); | geometry_set_collect_recursive(geometry_set, unit_transform, result_vector); | ||||
| return result_vector; | return result_vector; | ||||
| } | } | ||||
| void gather_attribute_info(Map<std::string, AttributeInfo> &attributes, | void gather_attribute_info(Map<std::string, AttributeInfo> &attributes, | ||||
| const GeometryComponentType component_type, | Span<GeometryComponentType> component_types, | ||||
HooglyBoogly: Nice improvement here, I didn't think of that before. | |||||
| Span<GeometryInstanceGroup> set_groups, | Span<GeometryInstanceGroup> set_groups, | ||||
| const Set<std::string> &ignored_attributes) | const Set<std::string> &ignored_attributes) | ||||
| { | { | ||||
| for (const GeometryInstanceGroup &set_group : set_groups) { | for (const GeometryInstanceGroup &set_group : set_groups) { | ||||
| const GeometrySet &set = set_group.geometry_set; | const GeometrySet &set = set_group.geometry_set; | ||||
| for (const GeometryComponentType component_type : component_types) { | |||||
| if (!set.has(component_type)) { | if (!set.has(component_type)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const GeometryComponent &component = *set.get_component_for_read(component_type); | const GeometryComponent &component = *set.get_component_for_read(component_type); | ||||
| for (const std::string &name : component.attribute_names()) { | for (const std::string &name : component.attribute_names()) { | ||||
| if (ignored_attributes.contains(name)) { | if (ignored_attributes.contains(name)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name); | const ReadAttributePtr read_attribute = component.attribute_try_get_for_read(name); | ||||
| if (!read_attribute) { | if (!read_attribute) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| const AttributeDomain domain = read_attribute->domain(); | const AttributeDomain domain = read_attribute->domain(); | ||||
| const CustomDataType data_type = read_attribute->custom_data_type(); | const CustomDataType data_type = read_attribute->custom_data_type(); | ||||
| auto add_info = [&, data_type, domain](AttributeInfo *info) { | auto add_info = [&, data_type, domain](AttributeInfo *info) { | ||||
| info->domain = domain; | info->domain = domain; | ||||
| info->data_type = data_type; | info->data_type = data_type; | ||||
| }; | }; | ||||
| auto modify_info = [&, data_type, domain](AttributeInfo *info) { | auto modify_info = [&, data_type, domain](AttributeInfo *info) { | ||||
| info->domain = domain; /* TODO: Use highest priority domain. */ | info->domain = domain; /* TODO: Use highest priority domain. */ | ||||
| info->data_type = bke::attribute_data_type_highest_complexity( | info->data_type = bke::attribute_data_type_highest_complexity( | ||||
| {info->data_type, data_type}); | {info->data_type, data_type}); | ||||
| }; | }; | ||||
| attributes.add_or_modify(name, add_info, modify_info); | attributes.add_or_modify(name, add_info, modify_info); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups) | static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGroup> set_groups, | ||||
| const bool convert_points_to_vertices) | |||||
| { | { | ||||
| int totverts = 0; | int totverts = 0; | ||||
| int totloops = 0; | int totloops = 0; | ||||
| int totedges = 0; | int totedges = 0; | ||||
| int totpolys = 0; | int totpolys = 0; | ||||
| int64_t cd_dirty_vert = 0; | int64_t cd_dirty_vert = 0; | ||||
| int64_t cd_dirty_poly = 0; | int64_t cd_dirty_poly = 0; | ||||
| int64_t cd_dirty_edge = 0; | int64_t cd_dirty_edge = 0; | ||||
| int64_t cd_dirty_loop = 0; | int64_t cd_dirty_loop = 0; | ||||
| for (const GeometryInstanceGroup &set_group : set_groups) { | for (const GeometryInstanceGroup &set_group : set_groups) { | ||||
| const GeometrySet &set = set_group.geometry_set; | const GeometrySet &set = set_group.geometry_set; | ||||
| const int tot_transforms = set_group.transforms.size(); | |||||
| if (set.has_mesh()) { | if (set.has_mesh()) { | ||||
| const Mesh &mesh = *set.get_mesh_for_read(); | const Mesh &mesh = *set.get_mesh_for_read(); | ||||
| totverts += mesh.totvert * set_group.transforms.size(); | totverts += mesh.totvert * tot_transforms; | ||||
| totloops += mesh.totloop * set_group.transforms.size(); | totloops += mesh.totloop * tot_transforms; | ||||
| totedges += mesh.totedge * set_group.transforms.size(); | totedges += mesh.totedge * tot_transforms; | ||||
| totpolys += mesh.totpoly * set_group.transforms.size(); | totpolys += mesh.totpoly * tot_transforms; | ||||
| cd_dirty_vert |= mesh.runtime.cd_dirty_vert; | cd_dirty_vert |= mesh.runtime.cd_dirty_vert; | ||||
| cd_dirty_poly |= mesh.runtime.cd_dirty_poly; | cd_dirty_poly |= mesh.runtime.cd_dirty_poly; | ||||
| cd_dirty_edge |= mesh.runtime.cd_dirty_edge; | cd_dirty_edge |= mesh.runtime.cd_dirty_edge; | ||||
| cd_dirty_loop |= mesh.runtime.cd_dirty_loop; | cd_dirty_loop |= mesh.runtime.cd_dirty_loop; | ||||
| } | } | ||||
| if (convert_points_to_vertices && set.has_pointcloud()) { | |||||
| const PointCloud &pointcloud = *set.get_pointcloud_for_read(); | |||||
| totverts += pointcloud.totpoint * tot_transforms; | |||||
| } | |||||
| } | } | ||||
| Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); | Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); | ||||
| /* Copy settings from the first input geometry set with a mesh. */ | /* Copy settings from the first input geometry set with a mesh. */ | ||||
| for (const GeometryInstanceGroup &set_group : set_groups) { | for (const GeometryInstanceGroup &set_group : set_groups) { | ||||
| const GeometrySet &set = set_group.geometry_set; | const GeometrySet &set = set_group.geometry_set; | ||||
| if (set.has_mesh()) { | if (set.has_mesh()) { | ||||
| const Mesh &mesh = *set.get_mesh_for_read(); | const Mesh &mesh = *set.get_mesh_for_read(); | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (set.has_mesh()) { | ||||
| } | } | ||||
| vert_offset += mesh.totvert; | vert_offset += mesh.totvert; | ||||
| loop_offset += mesh.totloop; | loop_offset += mesh.totloop; | ||||
| edge_offset += mesh.totedge; | edge_offset += mesh.totedge; | ||||
| poly_offset += mesh.totpoly; | poly_offset += mesh.totpoly; | ||||
| } | } | ||||
| } | } | ||||
| if (convert_points_to_vertices && set.has_pointcloud()) { | |||||
| const PointCloud &pointcloud = *set.get_pointcloud_for_read(); | |||||
| for (const float4x4 &transform : set_group.transforms) { | |||||
| for (const int i : IndexRange(pointcloud.totpoint)) { | |||||
| MVert &new_vert = new_mesh->mvert[vert_offset + i]; | |||||
| const float3 old_position = pointcloud.co[i]; | |||||
| const float3 new_position = transform * old_position; | |||||
| copy_v3_v3(new_vert.co, new_position); | |||||
| } | |||||
| vert_offset += pointcloud.totpoint; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| return new_mesh; | return new_mesh; | ||||
| } | } | ||||
| static void join_attributes(Span<GeometryInstanceGroup> set_groups, | static void join_attributes(Span<GeometryInstanceGroup> set_groups, | ||||
| const GeometryComponentType component_type, | Span<GeometryComponentType> component_types, | ||||
| const Map<std::string, AttributeInfo> &attribute_info, | const Map<std::string, AttributeInfo> &attribute_info, | ||||
| GeometryComponent &result) | GeometryComponent &result) | ||||
| { | { | ||||
| for (Map<std::string, AttributeInfo>::Item entry : attribute_info.items()) { | for (Map<std::string, AttributeInfo>::Item entry : attribute_info.items()) { | ||||
| StringRef name = entry.key; | StringRef name = entry.key; | ||||
| const AttributeDomain domain_output = entry.value.domain; | const AttributeDomain domain_output = entry.value.domain; | ||||
| const CustomDataType data_type_output = entry.value.data_type; | const CustomDataType data_type_output = entry.value.data_type; | ||||
| const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); | const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); | ||||
| BLI_assert(cpp_type != nullptr); | BLI_assert(cpp_type != nullptr); | ||||
| result.attribute_try_create(entry.key, domain_output, data_type_output); | result.attribute_try_create(entry.key, domain_output, data_type_output); | ||||
| WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); | WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); | ||||
| if (!write_attribute || &write_attribute->cpp_type() != cpp_type || | if (!write_attribute || &write_attribute->cpp_type() != cpp_type || | ||||
| write_attribute->domain() != domain_output) { | write_attribute->domain() != domain_output) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); | fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); | ||||
| int offset = 0; | int offset = 0; | ||||
| for (const GeometryInstanceGroup &set_group : set_groups) { | for (const GeometryInstanceGroup &set_group : set_groups) { | ||||
| const GeometrySet &set = set_group.geometry_set; | const GeometrySet &set = set_group.geometry_set; | ||||
| for (const GeometryComponentType component_type : component_types) { | |||||
| if (set.has(component_type)) { | if (set.has(component_type)) { | ||||
| const GeometryComponent &component = *set.get_component_for_read(component_type); | const GeometryComponent &component = *set.get_component_for_read(component_type); | ||||
| const int domain_size = component.attribute_domain_size(domain_output); | const int domain_size = component.attribute_domain_size(domain_output); | ||||
| if (domain_size == 0) { | if (domain_size == 0) { | ||||
| continue; /* Domain size is 0, so no need to increment the offset. */ | continue; /* Domain size is 0, so no need to increment the offset. */ | ||||
| } | } | ||||
| ReadAttributePtr source_attribute = component.attribute_try_get_for_read( | ReadAttributePtr source_attribute = component.attribute_try_get_for_read( | ||||
| name, domain_output, data_type_output); | name, domain_output, data_type_output); | ||||
| if (source_attribute) { | if (source_attribute) { | ||||
| fn::GSpan src_span = source_attribute->get_span(); | fn::GSpan src_span = source_attribute->get_span(); | ||||
| const void *src_buffer = src_span.data(); | const void *src_buffer = src_span.data(); | ||||
| for (const int UNUSED(i) : set_group.transforms.index_range()) { | for (const int UNUSED(i) : set_group.transforms.index_range()) { | ||||
| void *dst_buffer = dst_span[offset]; | void *dst_buffer = dst_span[offset]; | ||||
| cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); | cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size); | ||||
| offset += domain_size; | offset += domain_size; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| offset += domain_size * set_group.transforms.size(); | offset += domain_size * set_group.transforms.size(); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| write_attribute->apply_span(); | write_attribute->apply_span(); | ||||
| } | } | ||||
| } | } | ||||
| static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) | static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, | ||||
| bool convert_points_to_vertices, | |||||
| GeometrySet &result) | |||||
| { | { | ||||
| Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups); | Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups, | ||||
| convert_points_to_vertices); | |||||
| MeshComponent &dst_component = result.get_component_for_write<MeshComponent>(); | MeshComponent &dst_component = result.get_component_for_write<MeshComponent>(); | ||||
| dst_component.replace(new_mesh); | dst_component.replace(new_mesh); | ||||
| /* Don't copy attributes that are stored directly in the mesh data structs. */ | Vector<GeometryComponentType> component_types; | ||||
| component_types.append(GeometryComponentType::Mesh); | |||||
| if (convert_points_to_vertices) { | |||||
| component_types.append(GeometryComponentType::PointCloud); | |||||
| } | |||||
| /* The position attribute is handled above already. */ | |||||
HooglyBooglyUnsubmitted Not Done Inline ActionsLooks like a merge might have reset the change to the comment I made this morning? HooglyBoogly: Looks like a merge might have reset the change to the comment I made this morning? | |||||
| Map<std::string, AttributeInfo> attributes; | Map<std::string, AttributeInfo> attributes; | ||||
| gather_attribute_info( | gather_attribute_info(attributes, component_types, set_groups, {"position", "material_index"}); | ||||
| attributes, GeometryComponentType::Mesh, set_groups, {"position", "material_index"}); | join_attributes( | ||||
| join_attributes(set_groups, | set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component)); | ||||
| GeometryComponentType::Mesh, | |||||
| attributes, | |||||
| static_cast<GeometryComponent &>(dst_component)); | |||||
| } | } | ||||
| static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups, | static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_groups, | ||||
| GeometrySet &result) | GeometrySet &result) | ||||
| { | { | ||||
| int totpoint = 0; | int totpoint = 0; | ||||
| for (const GeometryInstanceGroup &set_group : set_groups) { | for (const GeometryInstanceGroup &set_group : set_groups) { | ||||
| const GeometrySet &set = set_group.geometry_set; | const GeometrySet &set = set_group.geometry_set; | ||||
| if (set.has<PointCloudComponent>()) { | if (set.has<PointCloudComponent>()) { | ||||
| const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>(); | const PointCloudComponent &component = *set.get_component_for_read<PointCloudComponent>(); | ||||
| totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); | totpoint += component.attribute_domain_size(ATTR_DOMAIN_POINT); | ||||
| } | } | ||||
| } | } | ||||
| PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); | PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); | ||||
| PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint); | PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint); | ||||
| dst_component.replace(pointcloud); | dst_component.replace(pointcloud); | ||||
| Map<std::string, AttributeInfo> attributes; | Map<std::string, AttributeInfo> attributes; | ||||
| gather_attribute_info(attributes, GeometryComponentType::Mesh, set_groups, {}); | gather_attribute_info(attributes, {GeometryComponentType::PointCloud}, set_groups, {}); | ||||
HooglyBooglyUnsubmitted Done Inline ActionsOops! HooglyBoogly: Oops! | |||||
| join_attributes(set_groups, | join_attributes(set_groups, | ||||
| GeometryComponentType::PointCloud, | {GeometryComponentType::PointCloud}, | ||||
| attributes, | attributes, | ||||
| static_cast<GeometryComponent &>(dst_component)); | static_cast<GeometryComponent &>(dst_component)); | ||||
| } | } | ||||
| static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, | static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, | ||||
| GeometrySet &result) | GeometrySet &result) | ||||
| { | { | ||||
| /* Not yet supported. Joining volume grids with the same name requires resampling of at least | /* 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. */ | * one of the grids. The cell size of the resulting volume has to be determined somehow. */ | ||||
| VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); | VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); | ||||
| UNUSED_VARS(set_groups, dst_component); | UNUSED_VARS(set_groups, dst_component); | ||||
| } | } | ||||
| GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) | |||||
| { | |||||
| if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) { | |||||
| return geometry_set; | |||||
| } | |||||
| GeometrySet new_geometry_set = geometry_set; | |||||
| Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set); | |||||
| join_instance_groups_mesh(set_groups, true, new_geometry_set); | |||||
| /* Remove all instances, even though some might contain other non-mesh data. We can't really | |||||
| * keep only non-mesh instances in general. */ | |||||
| new_geometry_set.remove<InstancesComponent>(); | |||||
| /* If there was a point cloud, it is now part of the mesh. */ | |||||
| new_geometry_set.remove<PointCloudComponent>(); | |||||
| return new_geometry_set; | |||||
| } | |||||
| GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) | GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) | ||||
| { | { | ||||
| if (!geometry_set.has_instances()) { | if (!geometry_set.has_instances()) { | ||||
| return geometry_set; | return geometry_set; | ||||
| } | } | ||||
| GeometrySet new_geometry_set; | GeometrySet new_geometry_set; | ||||
| Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set); | Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set); | ||||
| join_instance_groups_mesh(set_groups, new_geometry_set); | join_instance_groups_mesh(set_groups, false, new_geometry_set); | ||||
| join_instance_groups_pointcloud(set_groups, new_geometry_set); | join_instance_groups_pointcloud(set_groups, new_geometry_set); | ||||
| join_instance_groups_volume(set_groups, new_geometry_set); | join_instance_groups_volume(set_groups, new_geometry_set); | ||||
| return new_geometry_set; | return new_geometry_set; | ||||
| } | } | ||||
| } // namespace blender::bke | } // namespace blender::bke | ||||
Nice improvement here, I didn't think of that before.