/* SPDX-FileCopyrightText: 2023 Blender Authors * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_bounds.hh" #include "BLI_map.hh" #include "BLI_memory_counter.hh" #include "BLI_task.hh" #include "BLT_translation.hh" #include "BKE_attribute.hh" #include "BKE_curves.hh" #include "BKE_geometry_set.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_grease_pencil.hh" #include "BKE_instances.hh" #include "BKE_lib_id.hh" #include "BKE_mesh.hh" #include "BKE_mesh_wrapper.hh" #include "BKE_modifier.hh" #include "BKE_object_types.hh" #include "BKE_pointcloud.hh" #include "BKE_volume.hh" #include "DNA_collection_types.h" #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" #include "BLI_rand.hh" #include "MEM_guardedalloc.h" namespace blender::bke { /* -------------------------------------------------------------------- */ /** \name Geometry Component * \{ */ GeometryComponent::GeometryComponent(Type type) : type_(type) {} GeometryComponentPtr GeometryComponent::create(Type component_type) { switch (component_type) { case Type::Mesh: return GeometryComponentPtr(new MeshComponent()); case Type::PointCloud: return GeometryComponentPtr(new PointCloudComponent()); case Type::Instance: return GeometryComponentPtr(new InstancesComponent()); case Type::Volume: return GeometryComponentPtr(new VolumeComponent()); case Type::Curve: return GeometryComponentPtr(new CurveComponent()); case Type::Edit: return GeometryComponentPtr(new GeometryComponentEditData()); case Type::GreasePencil: return GeometryComponentPtr(new GreasePencilComponent()); } BLI_assert_unreachable(); return {}; } int GeometryComponent::attribute_domain_size(const AttrDomain domain) const { if (this->is_empty()) { return 0; } const std::optional attributes = this->attributes(); if (attributes.has_value()) { return attributes->domain_size(domain); } return 0; } std::optional GeometryComponent::attributes() const { return std::nullopt; }; std::optional GeometryComponent::attributes_for_write() { return std::nullopt; } void GeometryComponent::count_memory(MemoryCounter & /*memory*/) const {} GeometryComponent::Type GeometryComponent::type() const { return type_; } bool GeometryComponent::is_empty() const { return false; } void GeometryComponent::delete_self() { delete this; } void GeometryComponent::delete_data_only() { this->clear(); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Geometry Set * \{ */ GeometrySet::GeometrySet() = default; GeometrySet::GeometrySet(const GeometrySet &other) = default; GeometrySet::GeometrySet(GeometrySet &&other) = default; GeometrySet::~GeometrySet() = default; GeometrySet &GeometrySet::operator=(const GeometrySet &other) = default; GeometrySet &GeometrySet::operator=(GeometrySet &&other) = default; GeometryComponent &GeometrySet::get_component_for_write(GeometryComponent::Type component_type) { GeometryComponentPtr &component_ptr = components_[size_t(component_type)]; if (!component_ptr) { /* If the component did not exist before, create a new one. */ component_ptr = GeometryComponent::create(component_type); } else if (component_ptr->is_mutable()) { /* If the referenced component is already mutable, return it directly. */ component_ptr->tag_ensured_mutable(); } else { /* If the referenced component is shared, make a copy. The copy is not shared and is * therefore mutable. */ component_ptr = component_ptr->copy(); } return const_cast(*component_ptr); } GeometryComponent *GeometrySet::get_component_ptr(GeometryComponent::Type type) { if (this->has(type)) { return &this->get_component_for_write(type); } return nullptr; } const GeometryComponent *GeometrySet::get_component(GeometryComponent::Type component_type) const { return components_[size_t(component_type)].get(); } bool GeometrySet::has(const GeometryComponent::Type component_type) const { const GeometryComponentPtr &component = components_[size_t(component_type)]; return component.has_value() && !component->is_empty(); } void GeometrySet::remove(const GeometryComponent::Type component_type) { components_[size_t(component_type)].reset(); } void GeometrySet::keep_only(const Span component_types) { for (GeometryComponentPtr &component_ptr : components_) { if (component_ptr) { if (!component_types.contains(component_ptr->type())) { component_ptr.reset(); } } } } void GeometrySet::keep_only_during_modify(const Span component_types) { Vector extended_types = component_types; extended_types.append_non_duplicates(GeometryComponent::Type::Instance); extended_types.append_non_duplicates(GeometryComponent::Type::Edit); this->keep_only(extended_types); } void GeometrySet::remove_geometry_during_modify() { this->keep_only_during_modify({}); } void GeometrySet::add(const GeometryComponent &component) { BLI_assert(!components_[size_t(component.type())]); component.add_user(); components_[size_t(component.type())] = GeometryComponentPtr( const_cast(&component)); } Vector GeometrySet::get_components() const { Vector components; for (const GeometryComponentPtr &component_ptr : components_) { if (component_ptr) { components.append(component_ptr.get()); } } return components; } std::optional> GeometrySet::compute_boundbox_without_instances() const { std::optional> bounds; if (const PointCloud *pointcloud = this->get_pointcloud()) { bounds = bounds::merge(bounds, pointcloud->bounds_min_max()); } if (const Mesh *mesh = this->get_mesh()) { bounds = bounds::merge(bounds, mesh->bounds_min_max()); } if (const Volume *volume = this->get_volume()) { bounds = bounds::merge(bounds, BKE_volume_min_max(volume)); } if (const Curves *curves_id = this->get_curves()) { bounds = bounds::merge(bounds, curves_id->geometry.wrap().bounds_min_max()); } if (const GreasePencil *grease_pencil = this->get_grease_pencil()) { bounds = bounds::merge(bounds, grease_pencil->bounds_min_max_eval()); } return bounds; } std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set) { Vector parts; if (const Mesh *mesh = geometry_set.get_mesh()) { parts.append(std::to_string(mesh->verts_num) + " verts"); parts.append(std::to_string(mesh->edges_num) + " edges"); parts.append(std::to_string(mesh->faces_num) + " faces"); parts.append(std::to_string(mesh->corners_num) + " corners"); } if (const Curves *curves = geometry_set.get_curves()) { parts.append(std::to_string(curves->geometry.point_num) + " control points"); parts.append(std::to_string(curves->geometry.curve_num) + " curves"); } if (const GreasePencil *grease_pencil = geometry_set.get_grease_pencil()) { parts.append(std::to_string(grease_pencil->layers().size()) + " Grease Pencil layers"); } if (const PointCloud *point_cloud = geometry_set.get_pointcloud()) { parts.append(std::to_string(point_cloud->totpoint) + " points"); } if (const Volume *volume = geometry_set.get_volume()) { parts.append(std::to_string(BKE_volume_num_grids(volume)) + " volume grids"); } if (geometry_set.has_instances()) { parts.append(std::to_string(geometry_set.get_instances()->instances_num()) + " instances"); } if (geometry_set.get_curve_edit_hints()) { parts.append("curve edit hints"); } stream << ""; return stream; } void GeometrySet::clear() { for (GeometryComponentPtr &component_ptr : components_) { component_ptr.reset(); } } void GeometrySet::ensure_owns_direct_data() { for (GeometryComponentPtr &component_ptr : components_) { if (!component_ptr) { continue; } if (component_ptr->owns_direct_data()) { continue; } GeometryComponent &component_for_write = this->get_component_for_write(component_ptr->type()); component_for_write.ensure_owns_direct_data(); } } void GeometrySet::ensure_owns_all_data() { if (Instances *instances = this->get_instances_for_write()) { instances->ensure_geometry_instances(); } this->ensure_owns_direct_data(); } bool GeometrySet::owns_direct_data() const { for (const GeometryComponentPtr &component_ptr : components_) { if (component_ptr) { if (!component_ptr->owns_direct_data()) { return false; } } } return true; } const Mesh *GeometrySet::get_mesh() const { const MeshComponent *component = this->get_component(); return (component == nullptr) ? nullptr : component->get(); } bool GeometrySet::has_mesh() const { const MeshComponent *component = this->get_component(); return component != nullptr && component->has_mesh(); } const PointCloud *GeometrySet::get_pointcloud() const { const PointCloudComponent *component = this->get_component(); return (component == nullptr) ? nullptr : component->get(); } const Volume *GeometrySet::get_volume() const { const VolumeComponent *component = this->get_component(); return (component == nullptr) ? nullptr : component->get(); } const Curves *GeometrySet::get_curves() const { const CurveComponent *component = this->get_component(); return (component == nullptr) ? nullptr : component->get(); } const Instances *GeometrySet::get_instances() const { const InstancesComponent *component = this->get_component(); return (component == nullptr) ? nullptr : component->get(); } const CurvesEditHints *GeometrySet::get_curve_edit_hints() const { const GeometryComponentEditData *component = this->get_component(); return (component == nullptr) ? nullptr : component->curves_edit_hints_.get(); } const GizmoEditHints *GeometrySet::get_gizmo_edit_hints() const { const GeometryComponentEditData *component = this->get_component(); return (component == nullptr) ? nullptr : component->gizmo_edit_hints_.get(); } const GreasePencil *GeometrySet::get_grease_pencil() const { const GreasePencilComponent *component = this->get_component(); return (component == nullptr) ? nullptr : component->get(); } bool GeometrySet::has_pointcloud() const { const PointCloudComponent *component = this->get_component(); return component != nullptr && component->has_pointcloud(); } bool GeometrySet::has_instances() const { const InstancesComponent *component = this->get_component(); return component != nullptr && component->get() != nullptr && component->get()->instances_num() >= 1; } bool GeometrySet::has_volume() const { const VolumeComponent *component = this->get_component(); return component != nullptr && component->has_volume(); } bool GeometrySet::has_curves() const { const CurveComponent *component = this->get_component(); return component != nullptr && component->has_curves(); } bool GeometrySet::has_realized_data() const { for (const GeometryComponentPtr &component_ptr : components_) { if (component_ptr) { if (component_ptr->type() != GeometryComponent::Type::Instance) { return true; } } } return false; } bool GeometrySet::has_grease_pencil() const { const GreasePencilComponent *component = this->get_component(); return component != nullptr && component->has_grease_pencil(); } bool GeometrySet::is_empty() const { return !(this->has_mesh() || this->has_curves() || this->has_pointcloud() || this->has_volume() || this->has_instances() || this->has_grease_pencil()); } GeometrySet GeometrySet::from_mesh(Mesh *mesh, GeometryOwnershipType ownership) { GeometrySet geometry_set; geometry_set.replace_mesh(mesh, ownership); return geometry_set; } GeometrySet GeometrySet::from_volume(Volume *volume, GeometryOwnershipType ownership) { GeometrySet geometry_set; geometry_set.replace_volume(volume, ownership); return geometry_set; } GeometrySet GeometrySet::from_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { GeometrySet geometry_set; geometry_set.replace_pointcloud(pointcloud, ownership); return geometry_set; } GeometrySet GeometrySet::from_curves(Curves *curves, GeometryOwnershipType ownership) { GeometrySet geometry_set; geometry_set.replace_curves(curves, ownership); return geometry_set; } GeometrySet GeometrySet::from_instances(Instances *instances, GeometryOwnershipType ownership) { GeometrySet geometry_set; geometry_set.replace_instances(instances, ownership); return geometry_set; } GeometrySet GeometrySet::from_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership) { GeometrySet geometry_set; geometry_set.replace_grease_pencil(grease_pencil, ownership); return geometry_set; } void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { if (mesh == nullptr) { this->remove(); return; } if (mesh == this->get_mesh()) { return; } this->remove(); MeshComponent &component = this->get_component_for_write(); component.replace(mesh, ownership); } void GeometrySet::replace_curves(Curves *curves, GeometryOwnershipType ownership) { if (curves == nullptr) { this->remove(); return; } if (curves == this->get_curves()) { return; } this->remove(); CurveComponent &component = this->get_component_for_write(); component.replace(curves, ownership); } void GeometrySet::replace_instances(Instances *instances, GeometryOwnershipType ownership) { if (instances == nullptr) { this->remove(); return; } if (instances == this->get_instances()) { return; } this->remove(); InstancesComponent &component = this->get_component_for_write(); component.replace(instances, ownership); } void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { if (pointcloud == nullptr) { this->remove(); return; } if (pointcloud == this->get_pointcloud()) { return; } this->remove(); PointCloudComponent &component = this->get_component_for_write(); component.replace(pointcloud, ownership); } void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership) { if (volume == nullptr) { this->remove(); return; } if (volume == this->get_volume()) { return; } this->remove(); VolumeComponent &component = this->get_component_for_write(); component.replace(volume, ownership); } void GeometrySet::replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership) { if (grease_pencil == nullptr) { this->remove(); return; } if (grease_pencil == this->get_grease_pencil()) { return; } this->remove(); GreasePencilComponent &component = this->get_component_for_write(); component.replace(grease_pencil, ownership); } Mesh *GeometrySet::get_mesh_for_write() { MeshComponent *component = this->get_component_ptr(); return component == nullptr ? nullptr : component->get_for_write(); } PointCloud *GeometrySet::get_pointcloud_for_write() { PointCloudComponent *component = this->get_component_ptr(); return component == nullptr ? nullptr : component->get_for_write(); } Volume *GeometrySet::get_volume_for_write() { VolumeComponent *component = this->get_component_ptr(); return component == nullptr ? nullptr : component->get_for_write(); } Curves *GeometrySet::get_curves_for_write() { CurveComponent *component = this->get_component_ptr(); return component == nullptr ? nullptr : component->get_for_write(); } Instances *GeometrySet::get_instances_for_write() { InstancesComponent *component = this->get_component_ptr(); return component == nullptr ? nullptr : component->get_for_write(); } CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write() { if (!this->has()) { return nullptr; } GeometryComponentEditData &component = this->get_component_for_write(); return component.curves_edit_hints_.get(); } GizmoEditHints *GeometrySet::get_gizmo_edit_hints_for_write() { if (!this->has()) { return nullptr; } GeometryComponentEditData &component = this->get_component_for_write(); return component.gizmo_edit_hints_.get(); } GreasePencil *GeometrySet::get_grease_pencil_for_write() { GreasePencilComponent *component = this->get_component_ptr(); return component == nullptr ? nullptr : component->get_for_write(); } void GeometrySet::count_memory(MemoryCounter &memory) const { for (const GeometryComponentPtr &component : components_) { if (component) { memory.add_shared(component.get(), [&](MemoryCounter &shared_memory) { component->count_memory(shared_memory); }); } } } void GeometrySet::attribute_foreach(const Span component_types, const bool include_instances, const AttributeForeachCallback callback) const { for (const GeometryComponent::Type component_type : component_types) { if (!this->has(component_type)) { continue; } const GeometryComponent &component = *this->get_component(component_type); const std::optional attributes = component.attributes(); if (attributes.has_value()) { attributes->foreach_attribute([&](const AttributeIter &iter) { callback(iter.name, {iter.domain, iter.data_type}, component); }); } } if (include_instances && this->has_instances()) { const Instances &instances = *this->get_instances(); instances.foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { instance_geometry_set.attribute_foreach(component_types, include_instances, callback); }); } } void GeometrySet::propagate_attributes_from_layer_to_instances( const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, const AttributeFilter &attribute_filter) { src_attributes.foreach_attribute([&](const AttributeIter &iter) { if (attribute_filter.allow_skip(iter.name)) { return; } const GAttributeReader src = iter.get(AttrDomain::Layer); if (src.sharing_info && src.varray.is_span()) { const AttributeInitShared init(src.varray.get_internal_span().data(), *src.sharing_info); if (dst_attributes.add(iter.name, AttrDomain::Instance, iter.data_type, init)) { return; } } GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( iter.name, AttrDomain::Instance, iter.data_type); if (!dst) { return; } array_utils::copy(src.varray, dst.span); dst.finish(); }); } bool attribute_is_builtin_on_component_type(const GeometryComponent::Type type, const StringRef name) { switch (type) { case GeometryComponent::Type::Mesh: { static auto component = GeometryComponent::create(type); return component->attributes()->is_builtin(name); } case GeometryComponent::Type::PointCloud: { static auto component = GeometryComponent::create(type); return component->attributes()->is_builtin(name); } case GeometryComponent::Type::Instance: { static auto component = GeometryComponent::create(type); return component->attributes()->is_builtin(name); } case GeometryComponent::Type::Curve: { static auto component = GeometryComponent::create(type); return component->attributes()->is_builtin(name); } case GeometryComponent::Type::GreasePencil: { static auto grease_pencil_component = GeometryComponent::create( GeometryComponent::Type::GreasePencil); static auto curves_component = GeometryComponent::create(GeometryComponent::Type::Curve); return grease_pencil_component->attributes()->is_builtin(name) || curves_component->attributes()->is_builtin(name); } case GeometryComponent::Type::Volume: case GeometryComponent::Type::Edit: { return false; } } BLI_assert_unreachable(); return false; } void GeometrySet::gather_attributes_for_propagation( const Span component_types, const GeometryComponent::Type dst_component_type, bool include_instances, const AttributeFilter &attribute_filter, Map &r_attributes) const { this->attribute_foreach( component_types, include_instances, [&](const StringRef attribute_id, const AttributeMetaData &meta_data, const GeometryComponent &component) { if (component.attributes()->is_builtin(attribute_id)) { if (!attribute_is_builtin_on_component_type(dst_component_type, attribute_id)) { /* Don't propagate built-in attributes that are not built-in on the destination * component. */ return; } } if (meta_data.data_type == CD_PROP_STRING) { /* Propagating string attributes is not supported yet. */ return; } if (attribute_filter.allow_skip(attribute_id)) { return; } AttrDomain domain = meta_data.domain; if (dst_component_type != GeometryComponent::Type::Instance && domain == AttrDomain::Instance) { domain = AttrDomain::Point; } auto add_info = [&](AttributeKind *attribute_kind) { attribute_kind->domain = domain; attribute_kind->data_type = meta_data.data_type; }; auto modify_info = [&](AttributeKind *attribute_kind) { attribute_kind->domain = bke::attribute_domain_highest_priority( {attribute_kind->domain, domain}); attribute_kind->data_type = bke::attribute_data_type_highest_complexity( {attribute_kind->data_type, meta_data.data_type}); }; r_attributes.add_or_modify(attribute_id, add_info, modify_info); }); } static void gather_component_types_recursive(const GeometrySet &geometry_set, const bool include_instances, const bool ignore_empty, Vector &r_types) { for (const GeometryComponent *component : geometry_set.get_components()) { if (ignore_empty) { if (component->is_empty()) { continue; } } r_types.append_non_duplicates(component->type()); } if (!include_instances) { return; } const Instances *instances = geometry_set.get_instances(); if (instances == nullptr) { return; } instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_component_types_recursive( instance_geometry_set, include_instances, ignore_empty, r_types); }); } Vector GeometrySet::gather_component_types(const bool include_instances, bool ignore_empty) const { Vector types; gather_component_types_recursive(*this, include_instances, ignore_empty, types); return types; } static void gather_mutable_geometry_sets(GeometrySet &geometry_set, Vector &r_geometry_sets) { r_geometry_sets.append(&geometry_set); if (!geometry_set.has_instances()) { return; } /* In the future this can be improved by deduplicating instance references across different * instances. */ Instances &instances = *geometry_set.get_instances_for_write(); instances.ensure_geometry_instances(); for (const int handle : instances.references().index_range()) { if (instances.references()[handle].type() == InstanceReference::Type::GeometrySet) { GeometrySet &instance_geometry = instances.geometry_set_from_reference(handle); gather_mutable_geometry_sets(instance_geometry, r_geometry_sets); } } } void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback) { Vector geometry_sets; gather_mutable_geometry_sets(*this, geometry_sets); if (geometry_sets.size() == 1) { /* Avoid possible overhead and a large call stack when multithreading is pointless. */ callback(*geometry_sets.first()); } else { threading::parallel_for_each(geometry_sets, [&](GeometrySet *geometry_set) { callback(*geometry_set); }); } } bool object_has_geometry_set_instances(const Object &object) { const GeometrySet *geometry_set = object.runtime->geometry_set_eval; if (geometry_set == nullptr) { return false; } if (geometry_set->has_component()) { return true; } if (object.type != OB_MESH && geometry_set->has_component()) { return true; } if (object.type != OB_POINTCLOUD && geometry_set->has_component()) { return true; } if (object.type != OB_VOLUME && geometry_set->has_component()) { return true; } if (!ELEM(object.type, OB_CURVES_LEGACY, OB_FONT) && geometry_set->has_component()) { return true; } if (object.type != OB_GREASE_PENCIL && geometry_set->has_component()) { return true; } return false; } /** \} */ } // namespace blender::bke