Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/geometry_set_instances.cc
| Show All 30 Lines | |||||
| 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, | ||||
| const float4x4 &transform, | const float4x4 &transform, | ||||
| Vector<GeometryInstanceGroup> &r_sets); | Vector<GeometryInstanceGroup> &r_sets); | ||||
| static void add_final_mesh_as_geometry_component(const Object &object, GeometrySet &geometry_set) | |||||
| { | |||||
| Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(&const_cast<Object &>(object), | |||||
| false); | |||||
| if (mesh != nullptr) { | |||||
| BKE_mesh_wrapper_ensure_mdata(mesh); | |||||
| MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); | |||||
| mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); | |||||
| mesh_component.copy_vertex_group_names_from_object(object); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. | * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. | ||||
| */ | */ | ||||
| static GeometrySet object_get_geometry_set_for_read(const Object &object) | static GeometrySet object_get_geometry_set_for_read(const Object &object) | ||||
| { | { | ||||
| /* Objects evaluated with a nodes modifier will have a geometry set already. */ | if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) { | ||||
| GeometrySet geometry_set; | |||||
| if (object.runtime.geometry_set_eval != nullptr) { | |||||
| /* `geometry_set_eval` only contains non-mesh components, see `editbmesh_build_data`. */ | |||||
| geometry_set = *object.runtime.geometry_set_eval; | |||||
| } | |||||
| add_final_mesh_as_geometry_component(object, geometry_set); | |||||
| return geometry_set; | |||||
| } | |||||
| if (object.runtime.geometry_set_eval != nullptr) { | if (object.runtime.geometry_set_eval != nullptr) { | ||||
| return *object.runtime.geometry_set_eval; | return *object.runtime.geometry_set_eval; | ||||
| } | } | ||||
| /* Otherwise, construct a new geometry set with the component based on the object type. */ | /* Otherwise, construct a new geometry set with the component based on the object type. */ | ||||
| GeometrySet new_geometry_set; | GeometrySet geometry_set; | ||||
| if (object.type == OB_MESH) { | if (object.type == OB_MESH) { | ||||
| Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object( | add_final_mesh_as_geometry_component(object, geometry_set); | ||||
| &const_cast<Object &>(object), false); | |||||
| if (mesh != nullptr) { | |||||
| BKE_mesh_wrapper_ensure_mdata(mesh); | |||||
| MeshComponent &mesh_component = new_geometry_set.get_component_for_write<MeshComponent>(); | |||||
| mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); | |||||
| mesh_component.copy_vertex_group_names_from_object(object); | |||||
| } | |||||
| } | } | ||||
| /* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the | /* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the | ||||
| * #geometry_set_eval case above. */ | * #geometry_set_eval case above. */ | ||||
| /* TODO: Add volume support. */ | /* TODO: Add volume support. */ | ||||
| /* Return by value since there is not always an existing geometry set owned elsewhere to use. */ | /* Return by value since there is not always an existing geometry set owned elsewhere to use. */ | ||||
| return new_geometry_set; | return geometry_set; | ||||
| } | } | ||||
| static void geometry_set_collect_recursive_collection_instance( | static void geometry_set_collect_recursive_collection_instance( | ||||
| const Collection &collection, const float4x4 &transform, Vector<GeometryInstanceGroup> &r_sets) | const Collection &collection, const float4x4 &transform, Vector<GeometryInstanceGroup> &r_sets) | ||||
| { | { | ||||
| float4x4 offset_matrix; | float4x4 offset_matrix; | ||||
| unit_m4(offset_matrix.values); | unit_m4(offset_matrix.values); | ||||
| sub_v3_v3(offset_matrix.values[3], collection.instance_offset); | sub_v3_v3(offset_matrix.values[3], collection.instance_offset); | ||||
| ▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | void geometry_set_gather_instances(const GeometrySet &geometry_set, | ||||
| Vector<GeometryInstanceGroup> &r_instance_groups) | Vector<GeometryInstanceGroup> &r_instance_groups) | ||||
| { | { | ||||
| float4x4 unit_transform; | float4x4 unit_transform; | ||||
| unit_m4(unit_transform.values); | unit_m4(unit_transform.values); | ||||
| geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups); | geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups); | ||||
| } | } | ||||
| void gather_attribute_info(Map<std::string, AttributeKind> &attributes, | static bool collection_instance_attribute_foreach(const Collection &collection, | ||||
| const AttributeForeachCallback callback, | |||||
| const int limit, | |||||
| int &count); | |||||
| static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, | |||||
| const AttributeForeachCallback callback, | |||||
| const int limit, | |||||
| int &count); | |||||
| static bool object_instance_attribute_foreach(const Object &object, | |||||
| const AttributeForeachCallback callback, | |||||
| const int limit, | |||||
| int &count) | |||||
| { | |||||
| GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); | |||||
| if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) { | |||||
| return false; | |||||
| } | |||||
| if (object.type == OB_EMPTY) { | |||||
| const Collection *collection_instance = object.instance_collection; | |||||
| if (collection_instance != nullptr) { | |||||
| if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool collection_instance_attribute_foreach(const Collection &collection, | |||||
| const AttributeForeachCallback callback, | |||||
| const int limit, | |||||
| int &count) | |||||
| { | |||||
| LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) { | |||||
| BLI_assert(collection_object->ob != nullptr); | |||||
| const Object &object = *collection_object->ob; | |||||
| if (!object_instance_attribute_foreach(object, callback, limit, count)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) { | |||||
| BLI_assert(collection_child->collection != nullptr); | |||||
| const Collection &collection = *collection_child->collection; | |||||
| if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * \return True if the recursive iteration should continue, false if the limit is reached or the | |||||
| * callback has returned false indicating it should stop. | |||||
| */ | |||||
| static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, | |||||
| const AttributeForeachCallback callback, | |||||
| const int limit, | |||||
| int &count) | |||||
| { | |||||
| for (const GeometryComponent *component : geometry_set.get_components_for_read()) { | |||||
| if (!component->attribute_foreach(callback)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /* Now that this this geometry set is visited, increase the count and check with the limit. */ | |||||
| if (limit > 0 && count++ > limit) { | |||||
| return false; | |||||
| } | |||||
| const InstancesComponent *instances_component = | |||||
| geometry_set.get_component_for_read<InstancesComponent>(); | |||||
| if (instances_component == nullptr) { | |||||
| return true; | |||||
| } | |||||
| for (const InstancedData &data : instances_component->instanced_data()) { | |||||
| if (data.type == INSTANCE_DATA_TYPE_OBJECT) { | |||||
| BLI_assert(data.data.object != nullptr); | |||||
| const Object &object = *data.data.object; | |||||
| if (!object_instance_attribute_foreach(object, callback, limit, count)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { | |||||
| BLI_assert(data.data.collection != nullptr); | |||||
| const Collection &collection = *data.data.collection; | |||||
| if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Call the callback on all of this geometry set's components, including geometry sets from | |||||
| * instances and recursive instances. This is necessary to access available attributes without | |||||
| * making all of the set's geometry real. | |||||
| * | |||||
| * \param limit: The total number of geometry sets to visit before returning early. This is used | |||||
| * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor | |||||
| * of performance at the cost of visiting every unique attribute. | |||||
| */ | |||||
| void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, | |||||
| const AttributeForeachCallback callback, | |||||
| const int limit) | |||||
| { | |||||
| int count = 0; | |||||
| instances_attribute_foreach_recursive(geometry_set, callback, limit, count); | |||||
| } | |||||
| void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups, | |||||
| Span<GeometryComponentType> component_types, | Span<GeometryComponentType> component_types, | ||||
| Span<GeometryInstanceGroup> set_groups, | const Set<std::string> &ignored_attributes, | ||||
| const Set<std::string> &ignored_attributes) | Map<std::string, AttributeKind> &r_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) { | 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); | ||||
| component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { | component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { | ||||
| if (ignored_attributes.contains(name)) { | if (ignored_attributes.contains(name)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| auto add_info = [&](AttributeKind *attribute_kind) { | auto add_info = [&](AttributeKind *attribute_kind) { | ||||
| attribute_kind->domain = meta_data.domain; | attribute_kind->domain = meta_data.domain; | ||||
| attribute_kind->data_type = meta_data.data_type; | attribute_kind->data_type = meta_data.data_type; | ||||
| }; | }; | ||||
| auto modify_info = [&](AttributeKind *attribute_kind) { | auto modify_info = [&](AttributeKind *attribute_kind) { | ||||
| attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ | attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ | ||||
| attribute_kind->data_type = bke::attribute_data_type_highest_complexity( | attribute_kind->data_type = bke::attribute_data_type_highest_complexity( | ||||
| {attribute_kind->data_type, meta_data.data_type}); | {attribute_kind->data_type, meta_data.data_type}); | ||||
| }; | }; | ||||
| attributes.add_or_modify(name, add_info, modify_info); | r_attributes.add_or_modify(name, add_info, modify_info); | ||||
| return true; | return true; | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| 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) | const bool convert_points_to_vertices) | ||||
| ▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| for (Map<std::string, AttributeKind>::Item entry : attribute_info.items()) { | for (Map<std::string, AttributeKind>::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( | ||||
| WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name); | entry.key, domain_output, data_type_output, AttributeInitDefault()); | ||||
| if (!write_attribute || &write_attribute->cpp_type() != cpp_type || | WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); | ||||
| write_attribute->domain() != domain_output) { | if (!write_attribute || &write_attribute.varray->type() != cpp_type || | ||||
| write_attribute.domain != domain_output) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only(); | |||||
| fn::GVMutableArray_GSpan dst_span{*write_attribute.varray}; | |||||
| 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) { | 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( | GVArrayPtr 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::GVArray_GSpan src_span{*source_attribute}; | ||||
| 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(); | dst_span.save(); | ||||
| } | } | ||||
| } | } | ||||
| static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, | static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, | ||||
| bool convert_points_to_vertices, | bool convert_points_to_vertices, | ||||
| GeometrySet &result) | 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); | convert_points_to_vertices); | ||||
| if (new_mesh == nullptr) { | if (new_mesh == nullptr) { | ||||
| return; | return; | ||||
| } | } | ||||
| 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); | ||||
| Vector<GeometryComponentType> component_types; | Vector<GeometryComponentType> component_types; | ||||
| component_types.append(GEO_COMPONENT_TYPE_MESH); | component_types.append(GEO_COMPONENT_TYPE_MESH); | ||||
| if (convert_points_to_vertices) { | if (convert_points_to_vertices) { | ||||
| component_types.append(GEO_COMPONENT_TYPE_POINT_CLOUD); | component_types.append(GEO_COMPONENT_TYPE_POINT_CLOUD); | ||||
| } | } | ||||
| /* Don't copy attributes that are stored directly in the mesh data structs. */ | /* Don't copy attributes that are stored directly in the mesh data structs. */ | ||||
| Map<std::string, AttributeKind> attributes; | Map<std::string, AttributeKind> attributes; | ||||
| gather_attribute_info(attributes, | geometry_set_gather_instances_attribute_info( | ||||
| component_types, | |||||
| set_groups, | set_groups, | ||||
| {"position", "material_index", "normal", "shade_smooth", "crease"}); | component_types, | ||||
| {"position", "material_index", "normal", "shade_smooth", "crease"}, | |||||
| attributes); | |||||
| join_attributes( | join_attributes( | ||||
| set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component)); | set_groups, component_types, 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); | ||||
| } | } | ||||
| } | } | ||||
| if (totpoint == 0) { | if (totpoint == 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| 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, AttributeKind> attributes; | Map<std::string, AttributeKind> attributes; | ||||
| gather_attribute_info(attributes, {GEO_COMPONENT_TYPE_POINT_CLOUD}, set_groups, {}); | geometry_set_gather_instances_attribute_info( | ||||
| set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {}, attributes); | |||||
| join_attributes(set_groups, | join_attributes(set_groups, | ||||
| {GEO_COMPONENT_TYPE_POINT_CLOUD}, | {GEO_COMPONENT_TYPE_POINT_CLOUD}, | ||||
| 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) | ||||
| ▲ Show 20 Lines • Show All 43 Lines • Show Last 20 Lines | |||||