Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/geometry_fields.cc
| Show First 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context, | ||||
| } | } | ||||
| if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>( | if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>( | ||||
| &context)) { | &context)) { | ||||
| return this->get_varray_for_context({instances_context->instances()}, mask); | return this->get_varray_for_context({instances_context->instances()}, mask); | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| std::optional<eAttrDomain> GeometryFieldInput::preferred_domain( | |||||
| const GeometryComponent & /*component*/) const | |||||
| { | |||||
| return std::nullopt; | |||||
| } | |||||
| GVArray MeshFieldInput::get_varray_for_context(const fn::FieldContext &context, | GVArray MeshFieldInput::get_varray_for_context(const fn::FieldContext &context, | ||||
| const IndexMask mask, | const IndexMask mask, | ||||
| ResourceScope & /*scope*/) const | ResourceScope & /*scope*/) const | ||||
| { | { | ||||
| if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( | if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( | ||||
| &context)) { | &context)) { | ||||
| if (const Mesh *mesh = geometry_context->mesh()) { | if (const Mesh *mesh = geometry_context->mesh()) { | ||||
| return this->get_varray_for_context(*mesh, geometry_context->domain(), mask); | return this->get_varray_for_context(*mesh, geometry_context->domain(), mask); | ||||
| } | } | ||||
| } | } | ||||
| if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) { | if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) { | ||||
| return this->get_varray_for_context(mesh_context->mesh(), mesh_context->domain(), mask); | return this->get_varray_for_context(mesh_context->mesh(), mesh_context->domain(), mask); | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| std::optional<eAttrDomain> MeshFieldInput::preferred_domain(const Mesh & /*mesh*/) const | |||||
| { | |||||
| return std::nullopt; | |||||
| } | |||||
| GVArray CurvesFieldInput::get_varray_for_context(const fn::FieldContext &context, | GVArray CurvesFieldInput::get_varray_for_context(const fn::FieldContext &context, | ||||
| IndexMask mask, | IndexMask mask, | ||||
| ResourceScope & /*scope*/) const | ResourceScope & /*scope*/) const | ||||
| { | { | ||||
| if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( | if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( | ||||
| &context)) { | &context)) { | ||||
| if (const CurvesGeometry *curves = geometry_context->curves()) { | if (const CurvesGeometry *curves = geometry_context->curves()) { | ||||
| return this->get_varray_for_context(*curves, geometry_context->domain(), mask); | return this->get_varray_for_context(*curves, geometry_context->domain(), mask); | ||||
| } | } | ||||
| } | } | ||||
| if (const CurvesFieldContext *curves_context = dynamic_cast<const CurvesFieldContext *>( | if (const CurvesFieldContext *curves_context = dynamic_cast<const CurvesFieldContext *>( | ||||
| &context)) { | &context)) { | ||||
| return this->get_varray_for_context(curves_context->curves(), curves_context->domain(), mask); | return this->get_varray_for_context(curves_context->curves(), curves_context->domain(), mask); | ||||
| } | } | ||||
| return {}; | return {}; | ||||
| } | } | ||||
| std::optional<eAttrDomain> CurvesFieldInput::preferred_domain( | |||||
| const CurvesGeometry & /*curves*/) const | |||||
| { | |||||
| return std::nullopt; | |||||
| } | |||||
| GVArray PointCloudFieldInput::get_varray_for_context(const fn::FieldContext &context, | GVArray PointCloudFieldInput::get_varray_for_context(const fn::FieldContext &context, | ||||
| IndexMask mask, | IndexMask mask, | ||||
| ResourceScope & /*scope*/) const | ResourceScope & /*scope*/) const | ||||
| { | { | ||||
| if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( | if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>( | ||||
| &context)) { | &context)) { | ||||
| if (const PointCloud *pointcloud = geometry_context->pointcloud()) { | if (const PointCloud *pointcloud = geometry_context->pointcloud()) { | ||||
| return this->get_varray_for_context(*pointcloud, mask); | return this->get_varray_for_context(*pointcloud, mask); | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
| bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const | bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const | ||||
| { | { | ||||
| if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) { | if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) { | ||||
| return name_ == other_typed->name_ && type_ == other_typed->type_; | return name_ == other_typed->name_ && type_ == other_typed->type_; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| std::optional<eAttrDomain> AttributeFieldInput::preferred_domain( | |||||
| const GeometryComponent &component) const | |||||
| { | |||||
| const std::optional<AttributeAccessor> attributes = component.attributes(); | |||||
| if (!attributes.has_value()) { | |||||
| return std::nullopt; | |||||
| } | |||||
| const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data(name_); | |||||
| if (!meta_data.has_value()) { | |||||
| return std::nullopt; | |||||
| } | |||||
| return meta_data->domain; | |||||
| } | |||||
| static StringRef get_random_id_attribute_name(const eAttrDomain domain) | static StringRef get_random_id_attribute_name(const eAttrDomain domain) | ||||
| { | { | ||||
| switch (domain) { | switch (domain) { | ||||
| case ATTR_DOMAIN_POINT: | case ATTR_DOMAIN_POINT: | ||||
| case ATTR_DOMAIN_INSTANCE: | case ATTR_DOMAIN_INSTANCE: | ||||
| return "id"; | return "id"; | ||||
| default: | default: | ||||
| return ""; | return ""; | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| if (const AnonymousAttributeFieldInput *other_typed = | if (const AnonymousAttributeFieldInput *other_typed = | ||||
| dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) { | dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) { | ||||
| return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_; | return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| std::optional<eAttrDomain> AnonymousAttributeFieldInput::preferred_domain( | |||||
| const GeometryComponent &component) const | |||||
| { | |||||
| const std::optional<AttributeAccessor> attributes = component.attributes(); | |||||
| if (!attributes.has_value()) { | |||||
| return std::nullopt; | |||||
| } | |||||
| const std::optional<AttributeMetaData> meta_data = attributes->lookup_meta_data( | |||||
| anonymous_id_.get()); | |||||
| if (!meta_data.has_value()) { | |||||
| return std::nullopt; | |||||
| } | |||||
| return meta_data->domain; | |||||
| } | |||||
| } // namespace blender::bke | } // namespace blender::bke | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Mesh and Curve Normals Field Input | /** \name Mesh and Curve Normals Field Input | ||||
| * \{ */ | * \{ */ | ||||
| namespace blender::bke { | namespace blender::bke { | ||||
| Show All 19 Lines | uint64_t NormalFieldInput::hash() const | ||||
| return 213980475983; | return 213980475983; | ||||
| } | } | ||||
| bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const | bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const | ||||
| { | { | ||||
| return dynamic_cast<const NormalFieldInput *>(&other) != nullptr; | return dynamic_cast<const NormalFieldInput *>(&other) != nullptr; | ||||
| } | } | ||||
| bool try_capture_field_on_geometry(GeometryComponent &component, | |||||
| const AttributeIDRef &attribute_id, | |||||
| const eAttrDomain domain, | |||||
| const fn::GField &field) | |||||
| { | |||||
| MutableAttributeAccessor attributes = *component.attributes_for_write(); | |||||
| const int domain_size = attributes.domain_size(domain); | |||||
| const CPPType &type = field.cpp_type(); | |||||
| const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type); | |||||
| if (domain_size == 0) { | |||||
| return attributes.add(attribute_id, domain, data_type, AttributeInitConstruct{}); | |||||
| } | |||||
| bke::GeometryFieldContext field_context{component, domain}; | |||||
| const IndexMask mask{IndexMask(domain_size)}; | |||||
| const bke::AttributeValidator validator = attributes.lookup_validator(attribute_id); | |||||
| /* Could avoid allocating a new buffer if: | |||||
| * - We are writing to an attribute that exists already with the correct domain and type. | |||||
| * - The field does not depend on that attribute (we can't easily check for that yet). */ | |||||
| void *buffer = MEM_mallocN(type.size() * domain_size, __func__); | |||||
| fn::FieldEvaluator evaluator{field_context, &mask}; | |||||
| evaluator.add_with_destination(validator.validate_field_if_necessary(field), | |||||
| GMutableSpan{type, buffer, domain_size}); | |||||
| evaluator.evaluate(); | |||||
| if (GAttributeWriter attribute = attributes.lookup_for_write(attribute_id)) { | |||||
| if (attribute.domain == domain && attribute.varray.type() == type) { | |||||
| attribute.varray.set_all(buffer); | |||||
| attribute.finish(); | |||||
| type.destruct_n(buffer, domain_size); | |||||
| MEM_freeN(buffer); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| attributes.remove(attribute_id); | |||||
| if (attributes.add(attribute_id, domain, data_type, bke::AttributeInitMoveArray{buffer})) { | |||||
| return true; | |||||
| } | |||||
| /* If the name corresponds to a builtin attribute, removing the attribute might fail if | |||||
| * it's required, and adding the attribute might fail if the domain or type is incorrect. */ | |||||
| type.destruct_n(buffer, domain_size); | |||||
| MEM_freeN(buffer); | |||||
| return false; | |||||
| } | |||||
| std::optional<eAttrDomain> try_detect_field_domain(const GeometryComponent &component, | |||||
| const fn::GField &field) | |||||
| { | |||||
| const GeometryComponentType component_type = component.type(); | |||||
| if (component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { | |||||
| return ATTR_DOMAIN_POINT; | |||||
| } | |||||
| if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { | |||||
| return ATTR_DOMAIN_INSTANCE; | |||||
| } | |||||
| const std::shared_ptr<const fn::FieldInputs> &field_inputs = field.node().field_inputs(); | |||||
| if (!field_inputs) { | |||||
| return std::nullopt; | |||||
| } | |||||
| std::optional<eAttrDomain> output_domain; | |||||
| auto handle_domain = [&](const std::optional<eAttrDomain> domain) { | |||||
| if (!domain.has_value()) { | |||||
| return false; | |||||
| } | |||||
| if (output_domain.has_value()) { | |||||
| if (*output_domain != *domain) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| output_domain = domain; | |||||
| return true; | |||||
| }; | |||||
| if (component_type == GEO_COMPONENT_TYPE_MESH) { | |||||
| const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); | |||||
| const Mesh *mesh = mesh_component.get_for_read(); | |||||
| if (mesh == nullptr) { | |||||
| return std::nullopt; | |||||
| } | |||||
| for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) { | |||||
| if (auto geometry_field_input = dynamic_cast<const GeometryFieldInput *>(&field_input)) { | |||||
| if (!handle_domain(geometry_field_input->preferred_domain(component))) { | |||||
| return std::nullopt; | |||||
| } | |||||
| } | |||||
| else if (auto mesh_field_input = dynamic_cast<const MeshFieldInput *>(&field_input)) { | |||||
| if (!handle_domain(mesh_field_input->preferred_domain(*mesh))) { | |||||
| return std::nullopt; | |||||
| } | |||||
| } | |||||
| else { | |||||
| return std::nullopt; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (component_type == GEO_COMPONENT_TYPE_CURVE) { | |||||
| const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); | |||||
| const Curves *curves = curve_component.get_for_read(); | |||||
| if (curves == nullptr) { | |||||
| return std::nullopt; | |||||
| } | |||||
| for (const fn::FieldInput &field_input : field_inputs->deduplicated_nodes) { | |||||
| if (auto geometry_field_input = dynamic_cast<const GeometryFieldInput *>(&field_input)) { | |||||
| if (!handle_domain(geometry_field_input->preferred_domain(component))) { | |||||
| return std::nullopt; | |||||
| } | |||||
| } | |||||
| else if (auto curves_field_input = dynamic_cast<const CurvesFieldInput *>(&field_input)) { | |||||
| if (!handle_domain( | |||||
| curves_field_input->preferred_domain(CurvesGeometry::wrap(curves->geometry)))) { | |||||
| return std::nullopt; | |||||
| } | |||||
| } | |||||
| else { | |||||
| return std::nullopt; | |||||
| } | |||||
| } | |||||
| } | |||||
| return output_domain; | |||||
| } | |||||
| } // namespace blender::bke | } // namespace blender::bke | ||||
| /** \} */ | /** \} */ | ||||