diff options
author | Lukas Stockner <lukas.stockner@freenet.de> | 2022-10-30 00:41:21 +0300 |
---|---|---|
committer | Lukas Stockner <lukas.stockner@freenet.de> | 2022-10-30 01:14:59 +0300 |
commit | bc37e8d8399eef686b71341aa90eced9bc117786 (patch) | |
tree | 92e4af388150209df9bc44e2cba6f2f303aa7baf /source/blender/nodes/geometry/nodes | |
parent | 552abb838c76d44a0d7d1226b59a1ab381e88386 (diff) | |
parent | d1d2f002c7caaf4ab457ec27bbc44666d7aac624 (diff) |
Merge remote-tracking branch 'origin/master' into principled-v2
Diffstat (limited to 'source/blender/nodes/geometry/nodes')
127 files changed, 6134 insertions, 3874 deletions
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index 58fbfb5a000..9af445090e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -70,13 +70,13 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_(total_out_description)); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeAccumulateField *data = MEM_cnew<NodeAccumulateField>(__func__); data->data_type = CD_PROP_FLOAT; @@ -87,13 +87,13 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeAccumulateField &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); - bNodeSocket *sock_in_vector = (bNodeSocket *)node->inputs.first; + bNodeSocket *sock_in_vector = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *sock_in_float = sock_in_vector->next; bNodeSocket *sock_in_int = sock_in_float->next; - bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *sock_out_vector = static_cast<bNodeSocket *>(node->outputs.first); bNodeSocket *sock_out_float = sock_out_vector->next; bNodeSocket *sock_out_int = sock_out_float->next; @@ -192,7 +192,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -template<typename T> class AccumulateFieldInput final : public GeometryFieldInput { +template<typename T> class AccumulateFieldInput final : public bke::GeometryFieldInput { private: Field<T> input_; Field<int> group_index_; @@ -204,7 +204,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu Field<T> input, Field<int> group_index, AccumulationMode accumulation_mode) - : GeometryFieldInput(CPPType::get<T>(), "Accumulation"), + : bke::GeometryFieldInput(CPPType::get<T>(), "Accumulation"), input_(input), group_index_(group_index), source_domain_(source_domain), @@ -212,18 +212,18 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask /*mask*/) const final { - const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const AttributeAccessor attributes = *context.attributes(); + const int domain_size = attributes.domain_size(source_domain_); if (domain_size == 0) { return {}; } - const AttributeAccessor attributes = *component.attributes(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const bke::GeometryFieldContext source_context{ + context.geometry(), context.type(), source_domain_}; + fn::FieldEvaluator evaluator{source_context, domain_size}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -266,7 +266,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu } return attributes.adapt_domain<T>( - VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain); + VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain()); } uint64_t hash() const override @@ -285,9 +285,15 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu } return false; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return source_domain_; + } }; -template<typename T> class TotalFieldInput final : public GeometryFieldInput { +template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput { private: Field<T> input_; Field<int> group_index_; @@ -295,25 +301,25 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { public: TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index) - : GeometryFieldInput(CPPType::get<T>(), "Total Value"), + : bke::GeometryFieldInput(CPPType::get<T>(), "Total Value"), input_(input), group_index_(group_index), source_domain_(source_domain) { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + IndexMask /*mask*/) const final { - const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const AttributeAccessor attributes = *context.attributes(); + const int domain_size = attributes.domain_size(source_domain_); if (domain_size == 0) { return {}; } - const AttributeAccessor attributes = *component.attributes(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const bke::GeometryFieldContext source_context{ + context.geometry(), context.type(), source_domain_}; + fn::FieldEvaluator evaluator{source_context, domain_size}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -339,7 +345,7 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { } return attributes.adapt_domain<T>( - VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain); + VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain()); } uint64_t hash() const override @@ -355,6 +361,12 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return source_domain_; + } }; template<typename T> std::string identifier_suffix() @@ -373,8 +385,8 @@ template<typename T> std::string identifier_suffix() static void node_geo_exec(GeoNodeExecParams params) { const NodeAccumulateField &storage = node_storage(params.node()); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const eAttrDomain source_domain = static_cast<eAttrDomain>(storage.domain); + const eCustomDataType data_type = eCustomDataType(storage.data_type); + const eAttrDomain source_domain = eAttrDomain(storage.domain); Field<int> group_index_field = params.extract_input<Field<int>>("Group Index"); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 9f317075bb5..1aea129bd53 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -30,7 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -38,7 +38,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryAttributeCapture *data = MEM_cnew<NodeGeometryAttributeCapture>(__func__); data->data_type = CD_PROP_FLOAT; @@ -50,9 +50,9 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryAttributeCapture &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); - bNodeSocket *socket_value_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_value_geometry = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *socket_value_vector = socket_value_geometry->next; bNodeSocket *socket_value_float = socket_value_vector->next; bNodeSocket *socket_value_color4f = socket_value_float->next; @@ -65,7 +65,7 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, socket_value_boolean, data_type == CD_PROP_BOOL); nodeSetSocketAvailability(ntree, socket_value_int32, data_type == CD_PROP_INT32); - bNodeSocket *out_socket_value_geometry = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_value_geometry = static_cast<bNodeSocket *>(node->outputs.first); bNodeSocket *out_socket_value_vector = out_socket_value_geometry->next; bNodeSocket *out_socket_value_float = out_socket_value_vector->next; bNodeSocket *out_socket_value_color4f = out_socket_value_float->next; @@ -106,33 +106,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -static void try_capture_field_on_geometry(GeometryComponent &component, - const AttributeIDRef &attribute_id, - const eAttrDomain domain, - const GField &field) -{ - const int domain_size = component.attribute_domain_size(domain); - if (domain_size == 0) { - return; - } - GeometryComponentFieldContext field_context{component, domain}; - MutableAttributeAccessor attributes = *component.attributes_for_write(); - const IndexMask mask{IndexMask(domain_size)}; - - const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); - GAttributeWriter output_attribute = attributes.lookup_or_add_for_write( - attribute_id, domain, data_type); - if (!output_attribute) { - return; - } - - fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, output_attribute.varray); - evaluator.evaluate(); - - output_attribute.finish(); -} - static StringRefNull identifier_suffix(eCustomDataType data_type) { switch (data_type) { @@ -165,8 +138,8 @@ static void node_geo_exec(GeoNodeExecParams params) } const NodeGeometryAttributeCapture &storage = node_storage(params.node()); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); + const eCustomDataType data_type = eCustomDataType(storage.data_type); + const eAttrDomain domain = eAttrDomain(storage.domain); const std::string output_identifier = "Attribute" + identifier_suffix(data_type); @@ -206,7 +179,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { GeometryComponent &component = geometry_set.get_component_for_write( GEO_COMPONENT_TYPE_INSTANCES); - try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); } } else { @@ -217,7 +190,7 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponentType type : types) { if (geometry_set.has(type)) { GeometryComponent &component = geometry_set.get_component_for_write(type); - try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + bke::try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index f6ea6073459..af55ef3f7ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -30,19 +30,19 @@ static void node_declare(NodeDeclarationBuilder &b) }); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "component", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = GEO_COMPONENT_TYPE_MESH; } static void node_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *point_socket = (bNodeSocket *)node->outputs.first; + bNodeSocket *point_socket = static_cast<bNodeSocket *>(node->outputs.first); bNodeSocket *edge_socket = point_socket->next; bNodeSocket *face_socket = edge_socket->next; bNodeSocket *face_corner_socket = face_socket->next; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 34e28e50c81..3023c7bd751 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -40,13 +40,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Variance"), "Variance_001"); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = CD_PROP_FLOAT; node->custom2 = ATTR_DOMAIN_POINT; @@ -54,12 +54,12 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *socket_geo = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_geo = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *socket_selection = socket_geo->next; bNodeSocket *socket_float_attr = socket_selection->next; bNodeSocket *socket_float3_attr = socket_float_attr->next; - bNodeSocket *socket_float_mean = (bNodeSocket *)node->outputs.first; + bNodeSocket *socket_float_mean = static_cast<bNodeSocket *>(node->outputs.first); bNodeSocket *socket_float_median = socket_float_mean->next; bNodeSocket *socket_float_sum = socket_float_median->next; bNodeSocket *socket_float_min = socket_float_sum->next; @@ -77,7 +77,7 @@ static void node_update(bNodeTree *ntree, bNode *node) bNodeSocket *socket_vector_std = socket_vector_range->next; bNodeSocket *socket_vector_variance = socket_vector_std->next; - const eCustomDataType data_type = static_cast<eCustomDataType>(node->custom1); + const eCustomDataType data_type = eCustomDataType(node->custom1); nodeSetSocketAvailability(ntree, socket_float_attr, data_type == CD_PROP_FLOAT); nodeSetSocketAvailability(ntree, socket_float_mean, data_type == CD_PROP_FLOAT); @@ -184,8 +184,8 @@ static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry"); const bNode &node = params.node(); - const eCustomDataType data_type = static_cast<eCustomDataType>(node.custom1); - const eAttrDomain domain = static_cast<eAttrDomain>(node.custom2); + const eCustomDataType data_type = eCustomDataType(node.custom1); + const eAttrDomain domain = eAttrDomain(node.custom2); Vector<const GeometryComponent *> components = geometry_set.get_components_for_read(); const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); @@ -200,7 +200,7 @@ static void node_geo_exec(GeoNodeExecParams params) continue; } if (attributes->domain_supported(domain)) { - GeometryComponentFieldContext field_context{*component, domain}; + bke::GeometryFieldContext field_context{*component, domain}; const int domain_num = attributes->domain_size(domain); fn::FieldEvaluator data_evaluator{field_context, domain_num}; @@ -282,7 +282,7 @@ static void node_geo_exec(GeoNodeExecParams params) continue; } if (attributes->domain_supported(domain)) { - GeometryComponentFieldContext field_context{*component, domain}; + bke::GeometryFieldContext field_context{*component, domain}; const int domain_num = attributes->domain_size(domain); fn::FieldEvaluator data_evaluator{field_context, domain_num}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 69938f3e447..094aab65653 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -24,7 +24,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Intersecting Edges")).field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } @@ -37,7 +37,7 @@ static void node_update(bNodeTree *ntree, bNode *node) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1; - bNodeSocket *geometry_1_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *geometry_1_socket = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *geometry_2_socket = geometry_1_socket->next; switch (operation) { @@ -55,7 +55,7 @@ static void node_update(bNodeTree *ntree, bNode *node) } } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE; } @@ -93,7 +93,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector<bke::GeometryInstanceGroup> set_groups; - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2"); + Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -148,13 +148,14 @@ static void node_geo_exec(GeoNodeExecParams params) } MEM_SAFE_FREE(result->mat); - result->mat = (Material **)MEM_malloc_arrayN(materials.size(), sizeof(Material *), __func__); + result->mat = static_cast<Material **>( + MEM_malloc_arrayN(materials.size(), sizeof(Material *), __func__)); result->totcol = materials.size(); MutableSpan(result->mat, result->totcol).copy_from(materials); /* Store intersecting edges in attribute. */ if (attribute_outputs.intersecting_edges_id) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result); + MutableAttributeAccessor attributes = result->attributes_for_write(); SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index 54a061993a3..df677e1c399 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -8,6 +8,7 @@ #include "UI_resources.h" #include "BKE_collection.h" +#include "BKE_instances.hh" #include "node_geometry_util.hh" @@ -30,12 +31,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCollectionInfo *data = MEM_cnew<NodeGeometryCollectionInfo>(__func__); data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; @@ -57,8 +58,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } const Object *self_object = params.self_object(); - const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection, - (Object *)self_object); + const bool is_recursive = BKE_collection_has_object_recursive_instanced( + collection, const_cast<Object *>(self_object)); if (is_recursive) { params.error_message_add(NodeWarningType::Error, "Collection contains current object"); params.set_default_remaining_outputs(); @@ -69,8 +70,7 @@ static void node_geo_exec(GeoNodeExecParams params) const bool use_relative_transform = (storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); const bool separate_children = params.get_input<bool>("Separate Children"); if (separate_children) { @@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params) children_objects.append(collection_object->ob); } - instances.reserve(children_collections.size() + children_objects.size()); + instances->reserve(children_collections.size() + children_objects.size()); Vector<InstanceListEntry> entries; entries.reserve(children_collections.size() + children_objects.size()); @@ -99,11 +99,11 @@ static void node_geo_exec(GeoNodeExecParams params) sub_v3_v3(transform.values[3], collection->instance_offset); } } - const int handle = instances.add_reference(*child_collection); + const int handle = instances->add_reference(*child_collection); entries.append({handle, &(child_collection->id.name[2]), transform}); } for (Object *child_object : children_objects) { - const int handle = instances.add_reference(*child_object); + const int handle = instances->add_reference(*child_object); float4x4 transform = float4x4::identity(); if (!reset_children) { if (use_relative_transform) { @@ -123,7 +123,7 @@ static void node_geo_exec(GeoNodeExecParams params) return BLI_strcasecmp_natural(a.name, b.name) < 0; }); for (const InstanceListEntry &entry : entries) { - instances.add_instance(entry.handle, entry.transform); + instances->add_instance(entry.handle, entry.transform); } } else { @@ -133,11 +133,11 @@ static void node_geo_exec(GeoNodeExecParams params) mul_m4_m4_pre(transform.values, self_object->imat); } - const int handle = instances.add_reference(*collection); - instances.add_instance(handle, transform); + const int handle = instances->add_reference(*collection); + instances->add_instance(handle, transform); } - params.set_output("Geometry", geometry_set_out); + params.set_output("Geometry", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_collection_info_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 03892501c89..038309785fb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -46,6 +46,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) } /* Copy vertices. */ + MutableSpan<MVert> dst_verts = result->verts_for_write(); for (const int i : IndexRange(verts_num)) { float co[3]; int original_index; @@ -55,11 +56,11 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) # if 0 /* Disabled because it only works for meshes, not predictable enough. */ /* Copy custom data on vertices, like vertex groups etc. */ if (mesh && original_index < mesh->totvert) { - CustomData_copy_data(&mesh->vdata, &result->vdata, (int)original_index, (int)i, 1); + CustomData_copy_data(&mesh->vdata, &result->vdata, int(original_index), int(i), 1); } # endif /* Copy the position of the original point. */ - copy_v3_v3(result->mvert[i].co, co); + copy_v3_v3(dst_verts[i].co, co); } else { BLI_assert_msg(0, "Unexpected new vertex in hull output"); @@ -73,15 +74,17 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) * to a loop and edges need to be created from that. */ Array<MLoop> mloop_src(loops_num); uint edge_index = 0; + MutableSpan<MEdge> edges = result->edges_for_write(); + for (const int i : IndexRange(loops_num)) { int v_from; int v_to; plConvexHullGetLoop(hull, i, &v_from, &v_to); - mloop_src[i].v = (uint)v_from; + mloop_src[i].v = uint(v_from); /* Add edges for ascending order loops only. */ if (v_from < v_to) { - MEdge &edge = result->medge[edge_index]; + MEdge &edge = edges[edge_index]; edge.v1 = v_from; edge.v2 = v_to; edge.flag = ME_EDGEDRAW | ME_EDGERENDER; @@ -95,7 +98,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) } if (edges_num == 1) { /* In this case there are no loops. */ - MEdge &edge = result->medge[0]; + MEdge &edge = edges[0]; edge.v1 = 0; edge.v2 = 1; edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE; @@ -106,7 +109,10 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) /* Copy faces. */ Array<int> loops; int j = 0; - MLoop *loop = result->mloop; + MutableSpan<MPoly> polys = result->polys_for_write(); + MutableSpan<MLoop> mesh_loops = result->loops_for_write(); + MLoop *loop = mesh_loops.data(); + for (const int i : IndexRange(faces_num)) { const int len = plConvexHullGetFaceSize(hull, i); @@ -116,7 +122,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) loops.reinitialize(len); plConvexHullGetFaceLoops(hull, i, loops.data()); - MPoly &face = result->mpoly[i]; + MPoly &face = polys[i]; face.loopstart = j; face.totloop = len; for (const int k : IndexRange(len)) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index db3f108aad5..4161ec7e7ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -27,39 +27,31 @@ static void node_declare(NodeDeclarationBuilder &b) N_("The selection from the start and end of the splines based on the input sizes")); } -class EndpointFieldInput final : public GeometryFieldInput { +class EndpointFieldInput final : public bke::CurvesFieldInput { Field<int> start_size_; Field<int> end_size_; public: EndpointFieldInput(Field<int> start_size, Field<int> end_size) - : GeometryFieldInput(CPPType::get<bool>(), "Endpoint Selection node"), + : bke::CurvesFieldInput(CPPType::get<bool>(), "Endpoint Selection node"), start_size_(start_size), end_size_(end_size) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { - return nullptr; + if (domain != ATTR_DOMAIN_POINT) { + return {}; } - - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (!curve_component.has_curves()) { - return nullptr; - } - - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); if (curves.points_num() == 0) { - return nullptr; + return {}; } - GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE}; + bke::CurvesFieldContext size_context{curves, ATTR_DOMAIN_CURVE}; fn::FieldEvaluator evaluator{size_context, curves.curves_num()}; evaluator.add(start_size_); evaluator.add(end_size_); @@ -72,12 +64,12 @@ class EndpointFieldInput final : public GeometryFieldInput { devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) { threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) { for (const int i : curves_range) { - const IndexRange range = curves.points_for_curve(i); + const IndexRange points = curves.points_for_curve(i); const int start = std::max(start_size[i], 0); const int end = std::max(end_size[i], 0); - selection_span.slice(range.take_front(start)).fill(true); - selection_span.slice(range.take_back(end)).fill(true); + selection_span.slice(points.take_front(start)).fill(true); + selection_span.slice(points.take_back(end)).fill(true); } }); }); @@ -98,6 +90,11 @@ class EndpointFieldInput final : public GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index c9a8dba55b2..6eaa40bff03 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -27,12 +27,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveFill *data = MEM_cnew<NodeGeometryCurveFill>(__func__); @@ -79,13 +79,13 @@ static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result) } Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); for (const int i : IndexRange(result.vert.size())) { - copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f)); + copy_v3_v3(verts[i].co, float3(float(result.vert[i].x), float(result.vert[i].y), 0.0f)); } for (const int i : IndexRange(result.edge.size())) { edges[i].v1 = result.edge[i].first; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index ab1f8269c39..2b24b6cbf42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -32,12 +32,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__); data->mode = GEO_NODE_CURVE_FILLET_BEZIER; @@ -48,7 +48,7 @@ static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryCurveFillet &storage = node_storage(*node); const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode; - bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *poly_socket = static_cast<bNodeSocket *>(node->inputs.first)->next; nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); } @@ -72,10 +72,9 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &curves_id = *component.get_for_read(); + const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext context{curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{context, curves.points_num()}; evaluator.add(radius_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 5ef20f03f28..252f66c308f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -16,13 +16,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Selection")).field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__); @@ -70,41 +70,34 @@ static void select_by_handle_type(const bke::CurvesGeometry &curves, } } -class HandleTypeFieldInput final : public GeometryFieldInput { +class HandleTypeFieldInput final : public bke::CurvesFieldInput { HandleType type_; GeometryNodeCurveHandleMode mode_; public: HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode) - : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), + : bke::CurvesFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { + if (domain != ATTR_DOMAIN_POINT) { return {}; } - - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const Curves *curves_id = curve_component.get_for_read(); - if (curves_id == nullptr) { - return {}; - } - Array<bool> selection(mask.min_array_size()); - select_by_handle_type(bke::CurvesGeometry::wrap(curves_id->geometry), type_, mode_, selection); + select_by_handle_type(curves, type_, mode_, selection); return VArray<bool>::ForContainer(std::move(selection)); } uint64_t hash() const override { - return get_default_hash_2((int)mode_, (int)type_); + return get_default_hash_2(int(mode_), int(type_)); } bool is_equal_to(const fn::FieldNode &other) const override @@ -115,6 +108,11 @@ class HandleTypeFieldInput final : public GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc index ba8c9a893c2..cc32c8f5efc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc @@ -88,12 +88,12 @@ static void node_declare(NodeDeclarationBuilder &b) .make_available(enable_points); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurvePrimitiveArc *data = MEM_cnew<NodeGeometryCurvePrimitiveArc>(__func__); @@ -106,7 +106,7 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryCurvePrimitiveArc &storage = node_storage(*node); const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode; - bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *start_socket = static_cast<bNodeSocket *>(node->inputs.first)->next; bNodeSocket *middle_socket = start_socket->next; bNodeSocket *end_socket = middle_socket->next; @@ -116,7 +116,7 @@ static void node_update(bNodeTree *ntree, bNode *node) bNodeSocket *offset_angle_socket = sweep_angle_socket->next; - bNodeSocket *center_out_socket = ((bNodeSocket *)node->outputs.first)->next; + bNodeSocket *center_out_socket = static_cast<bNodeSocket *>(node->outputs.first)->next; bNodeSocket *normal_out_socket = center_out_socket->next; bNodeSocket *radius_out_socket = normal_out_socket->next; @@ -151,7 +151,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) { const float3 a = math::normalize(p2 - p1); const float3 b = math::normalize(p3 - p1); - return (ELEM(a, b, b * -1.0f)); + return ELEM(a, b, b * -1.0f); } static Curves *create_arc_curve_from_points(const int resolution, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 875664c41fa..b407ac47dc9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -41,12 +41,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurvePrimitiveBezierSegment *data = MEM_cnew<NodeGeometryCurvePrimitiveBezierSegment>(__func__); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index c33ba3e2a4c..35fdd6754cc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -56,12 +56,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Center")).make_available(endable_points); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurvePrimitiveCircle *data = MEM_cnew<NodeGeometryCurvePrimitiveCircle>(__func__); @@ -75,12 +75,12 @@ static void node_update(bNodeTree *ntree, bNode *node) const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) storage.mode; - bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *start_socket = static_cast<bNodeSocket *>(node->inputs.first)->next; bNodeSocket *middle_socket = start_socket->next; bNodeSocket *end_socket = middle_socket->next; bNodeSocket *radius_socket = end_socket->next; - bNodeSocket *center_socket = ((bNodeSocket *)node->outputs.first)->next; + bNodeSocket *center_socket = static_cast<bNodeSocket *>(node->outputs.first)->next; nodeSetSocketAvailability( ntree, start_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); @@ -98,7 +98,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) { const float3 a = math::normalize(p2 - p1); const float3 b = math::normalize(p3 - p1); - return (ELEM(a, b, b * -1.0f)); + return ELEM(a, b, b * -1.0f); } static Curves *create_point_circle_curve( @@ -144,7 +144,7 @@ static Curves *create_point_circle_curve( /* Get the radius from the center-point to p1. */ const float r = math::distance(p1, center); - const float theta_step = ((2 * M_PI) / (float)resolution); + const float theta_step = ((2 * M_PI) / float(resolution)); for (const int i : IndexRange(resolution)) { /* Formula for a circle around a point and 2 unit vectors perpendicular diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index 4cfa606d8eb..6b402a67450 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -39,12 +39,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurvePrimitiveLine *data = MEM_cnew<NodeGeometryCurvePrimitiveLine>(__func__); @@ -57,7 +57,7 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryCurvePrimitiveLine &storage = node_storage(*node); const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode; - bNodeSocket *p2_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *p2_socket = static_cast<bNodeSocket *>(node->inputs.first)->next; bNodeSocket *direction_socket = p2_socket->next; bNodeSocket *length_socket = direction_socket->next; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index fec4e31701f..44c2a078cb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -68,12 +68,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurvePrimitiveQuad *data = MEM_cnew<NodeGeometryCurvePrimitiveQuad>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE; @@ -83,10 +83,9 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryCurvePrimitiveQuad &storage = node_storage(*node); - GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( - storage.mode); + GeometryNodeCurvePrimitiveQuadMode mode = GeometryNodeCurvePrimitiveQuadMode(storage.mode); - bNodeSocket *width = ((bNodeSocket *)node->inputs.first); + bNodeSocket *width = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *height = width->next; bNodeSocket *bottom = height->next; bNodeSocket *top = bottom->next; @@ -140,7 +139,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) search_link_ops_for_declarations(params, declaration.outputs()); } else if (params.node_tree().typeinfo->validate_link( - static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + eNodeSocketDatatype(params.other_socket().type), SOCK_FLOAT)) { params.add_item(IFACE_("Width"), SocketSearchOp{"Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE}); params.add_item(IFACE_("Height"), diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 4aaf57d57cb..66284fe77db 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -43,9 +43,9 @@ static Curves *create_spiral_curve(const float rotations, const bool direction) { const int totalpoints = std::max(int(resolution * rotations), 1); - const float delta_radius = (end_radius - start_radius) / (float)totalpoints; - const float delta_height = height / (float)totalpoints; - const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * + const float delta_radius = (end_radius - start_radius) / float(totalpoints); + const float delta_height = height / float(totalpoints); + const float delta_theta = (M_PI * 2 * rotations) / float(totalpoints) * (direction ? 1.0f : -1.0f); Curves *curves_id = bke::curves_new_nomain_single(totalpoints + 1, CURVE_TYPE_POLY); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 37fc6823b9a..8b6a7064362 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -26,12 +26,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveResample *data = MEM_cnew<NodeGeometryCurveResample>(__func__); @@ -44,7 +44,7 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryCurveResample &storage = node_storage(*node); const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next->next; + bNodeSocket *count_socket = static_cast<bNodeSocket *>(node->inputs.first)->next->next; bNodeSocket *length_socket = count_socket->next; nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); @@ -66,12 +66,14 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_RESAMPLE_COUNT: { Field<int> count = params.extract_input<Field<int>>("Count"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { - if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (const Curves *src_curves = component->get_for_read()) { - Curves *dst_curves = geometry::resample_to_count(*component, selection, count); - bke::curves_copy_parameters(*src_curves, *dst_curves); - geometry.replace_curves(dst_curves); - } + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_count( + src_curves, selection, count); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(*src_curves_id, *dst_curves_id); + geometry.replace_curves(dst_curves_id); } }); break; @@ -79,24 +81,27 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_RESAMPLE_LENGTH: { Field<float> length = params.extract_input<Field<float>>("Length"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { - if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (const Curves *src_curves = component->get_for_read()) { - Curves *dst_curves = geometry::resample_to_length(*component, selection, length); - bke::curves_copy_parameters(*src_curves, *dst_curves); - geometry.replace_curves(dst_curves); - } + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_length( + src_curves, selection, length); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(*src_curves_id, *dst_curves_id); + geometry.replace_curves(dst_curves_id); } }); break; } case GEO_NODE_CURVE_RESAMPLE_EVALUATED: geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { - if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (const Curves *src_curves = component->get_for_read()) { - Curves *dst_curves = geometry::resample_to_evaluated(*component, selection); - bke::curves_copy_parameters(*src_curves, *dst_curves); - geometry.replace_curves(dst_curves); - } + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(src_curves, selection); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(*src_curves_id, *dst_curves_id); + geometry.replace_curves(dst_curves_id); } }); break; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index de29735bd2d..0169ead5bd2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -23,14 +23,12 @@ static void node_geo_exec(GeoNodeExecParams params) if (!geometry_set.has_curves()) { return; } + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator selection_evaluator{field_context, src_curves.curves_num()}; + selection_evaluator.add(params.get_input<Field<bool>>("Selection")); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); if (selection.is_empty()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index e80b600a740..27e111822bf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -35,12 +35,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Normal")).dependent_field(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_type_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_type_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveSample *data = MEM_cnew<NodeGeometryCurveSample>(__func__); data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH; @@ -52,7 +52,7 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryCurveSample &storage = node_storage(*node); const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; - bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *factor = static_cast<bNodeSocket *>(node->inputs.first)->next; bNodeSocket *length = factor->next; nodeSetSocketAvailability(ntree, factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); @@ -134,7 +134,7 @@ class SampleFloatSegmentsFunction : public fn::MultiFunction { return signature.build(); } - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override { const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length"); MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index"); @@ -172,7 +172,7 @@ class SampleCurveFunction : public fn::MultiFunction { return signature.build(); } - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override { MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>( 2, "Position"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc index f7ba724c377..d37af6e5fe8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc @@ -20,13 +20,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__); @@ -51,15 +51,12 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) return BEZIER_HANDLE_AUTO; } -static void set_type_in_component(CurveComponent &component, - const GeometryNodeCurveHandleMode mode, - const HandleType new_handle_type, - const Field<bool> &selection_field) +static void set_handle_type(bke::CurvesGeometry &curves, + const GeometryNodeCurveHandleMode mode, + const HandleType new_handle_type, + const Field<bool> &selection_field) { - Curves &curves_id = *component.get_for_write(); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -93,21 +90,17 @@ static void node_geo_exec(GeoNodeExecParams params) std::atomic<bool> has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; - } - has_curves = true; - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const AttributeAccessor attributes = *component.attributes(); - if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) { - return; + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + has_curves = true; + const AttributeAccessor attributes = curves.attributes(); + if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) { + return; + } + has_bezier = true; + + set_handle_type(curves, mode, new_handle_type, selection_field); } - has_bezier = true; - - set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(), - mode, - new_handle_type, - selection_field); }); if (has_curves && !has_bezier) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index b98541e3446..3dc89a9058e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -106,7 +106,7 @@ static Array<float> curve_length_point_domain(const bke::CurvesGeometry &curves) } static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry &curves, - const IndexMask UNUSED(mask), + const IndexMask /*mask*/, const eAttrDomain domain) { VArray<bool> cyclic = curves.cyclic(); @@ -126,6 +126,10 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry value *= factor; } } + else if (curve_lengths.size() == 1) { + /* The curve is a single point. */ + curve_lengths[0] = 0.0f; + } else { /* It is arbitrary what to do in those rare cases when all the points are * in the same position. In this case we are just arbitrarily giving a valid @@ -143,8 +147,8 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry Array<float> lengths = accumulated_lengths_curve_domain(curves); const int last_index = curves.curves_num() - 1; - const int total_length = lengths.last() + curves.evaluated_length_total_for_curve( - last_index, cyclic[last_index]); + const float total_length = lengths.last() + curves.evaluated_length_total_for_curve( + last_index, cyclic[last_index]); if (total_length > 0.0f) { const float factor = 1.0f / total_length; for (float &value : lengths) { @@ -165,7 +169,7 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry } static VArray<float> construct_curve_length_parameter_varray(const bke::CurvesGeometry &curves, - const IndexMask UNUSED(mask), + const IndexMask /*mask*/, const eAttrDomain domain) { curves.ensure_evaluated_lengths(); @@ -184,7 +188,7 @@ static VArray<float> construct_curve_length_parameter_varray(const bke::CurvesGe } static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &curves, - const IndexMask UNUSED(mask), + const IndexMask /*mask*/, const eAttrDomain domain) { if (domain == ATTR_DOMAIN_POINT) { @@ -203,26 +207,18 @@ static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &c return {}; } -class CurveParameterFieldInput final : public GeometryFieldInput { +class CurveParameterFieldInput final : public bke::CurvesFieldInput { public: - CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node") + CurveParameterFieldInput() : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Parameter node") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (curve_component.has_curves()) { - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_parameter_varray(curves, mask, domain); - } - } - return {}; + return construct_curve_parameter_varray(curves, mask, domain); } uint64_t hash() const override @@ -237,26 +233,19 @@ class CurveParameterFieldInput final : public GeometryFieldInput { } }; -class CurveLengthParameterFieldInput final : public GeometryFieldInput { +class CurveLengthParameterFieldInput final : public bke::CurvesFieldInput { public: - CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") + CurveLengthParameterFieldInput() + : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Length node") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (curve_component.has_curves()) { - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_length_parameter_varray(curves, mask, domain); - } - } - return {}; + return construct_curve_length_parameter_varray(curves, mask, domain); } uint64_t hash() const override @@ -271,26 +260,18 @@ class CurveLengthParameterFieldInput final : public GeometryFieldInput { } }; -class IndexOnSplineFieldInput final : public GeometryFieldInput { +class IndexOnSplineFieldInput final : public bke::CurvesFieldInput { public: - IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index") + IndexOnSplineFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Index") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (curve_component.has_curves()) { - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_index_on_spline_varray(curves, mask, domain); - } - } - return {}; + return construct_index_on_spline_varray(curves, mask, domain); } uint64_t hash() const override @@ -303,6 +284,11 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { { return dynamic_cast<const IndexOnSplineFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index a92479bc5f1..9ac6516ee7b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -20,12 +20,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__); @@ -45,14 +45,13 @@ static void node_geo_exec(GeoNodeExecParams params) if (!geometry_set.has_curves()) { return; } - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &src_curves_id = *src_component.get_for_read(); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); if (src_curves.is_single_type(dst_type)) { return; } - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; evaluator.set_selection(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index bd44adb35a2..5a1d2461c72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -29,16 +29,17 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); Field<int> cuts_field = params.extract_input<Field<int>>("Cuts"); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_curves()) { return; } - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &src_curves_id = *component.get_for_read(); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(cuts_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index fd75855bddb..e9eaa00b02d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -4,8 +4,11 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" +#include "DNA_pointcloud_types.h" + #include "BKE_pointcloud.h" -#include "BKE_spline.hh" + +#include "GEO_resample_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -37,12 +40,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Rotation")).field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__); @@ -55,16 +58,16 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryCurveToPoints &storage = node_storage(*node); const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *count_socket = static_cast<bNodeSocket *>(node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -static void curve_create_default_rotation_attribute(Span<float3> tangents, - Span<float3> normals, - MutableSpan<float3> rotations) +static void fill_rotation_attribute(const Span<float3> tangents, + const Span<float3> normals, + MutableSpan<float3> rotations) { threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { for (const int i : range) { @@ -74,235 +77,30 @@ static void curve_create_default_rotation_attribute(Span<float3> tangents, }); } -static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveResampleMode mode, - const CurveEval &curve, - const Span<SplinePtr> splines) -{ - const int size = curve.splines().size(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: { - const int count = params.get_input<int>("Count"); - if (count < 1) { - return {0}; - } - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - if (splines[i]->evaluated_points_num() > 0) { - offset += count; - } - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.get_input<float>("Length"), 0.0001f); - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - if (splines[i]->evaluated_points_num() > 0) { - offset += splines[i]->length() / resolution + 1; - } - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { - return curve.evaluated_point_offsets(); - } - } - BLI_assert_unreachable(); - return {0}; -} - -/** - * \note Relies on the fact that all attributes on point clouds are stored contiguously. - */ -static GMutableSpan ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id, - const eCustomDataType data_type) -{ - return points.attributes_for_write() - ->lookup_or_add_for_write(attribute_id, ATTR_DOMAIN_POINT, data_type) - .varray.get_internal_span(); -} - -template<typename T> -static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id) -{ - return points.attributes_for_write() - ->lookup_or_add_for_write<T>(attribute_id, ATTR_DOMAIN_POINT) - .varray.get_internal_span(); -} - -namespace { -struct AnonymousAttributeIDs { - StrongAnonymousAttributeID tangent_id; - StrongAnonymousAttributeID normal_id; - StrongAnonymousAttributeID rotation_id; -}; - -struct ResultAttributes { - MutableSpan<float3> positions; - MutableSpan<float> radii; - - Map<AttributeIDRef, GMutableSpan> point_attributes; - - MutableSpan<float3> tangents; - MutableSpan<float3> normals; - MutableSpan<float3> rotations; -}; -} // namespace - -static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, - const CurveEval &curve, - const AnonymousAttributeIDs &attributes) +static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves, + const AttributeIDRef &tangent_id, + const AttributeIDRef &normal_id, + const AttributeIDRef &rotation_id) { - ResultAttributes outputs; - - outputs.positions = ensure_point_attribute<float3>(points, "position"); - outputs.radii = ensure_point_attribute<float>(points, "radius"); - - if (attributes.tangent_id) { - outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get()); - } - if (attributes.normal_id) { - outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get()); - } - if (attributes.rotation_id) { - outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get()); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(0); + pointcloud->totpoint = curves.points_num(); + + if (rotation_id) { + MutableAttributeAccessor attributes = curves.attributes_for_write(); + const VArraySpan<float3> tangents = attributes.lookup<float3>(tangent_id, ATTR_DOMAIN_POINT); + const VArraySpan<float3> normals = attributes.lookup<float3>(normal_id, ATTR_DOMAIN_POINT); + SpanAttributeWriter<float3> rotations = attributes.lookup_or_add_for_write_only_span<float3>( + rotation_id, ATTR_DOMAIN_POINT); + fill_rotation_attribute(tangents, normals, rotations.span); + rotations.finish(); } - /* Because of the invariants of the curve component, we use the attributes of the first spline - * as a representative for the attribute meta data all splines. Attributes from the spline domain - * are handled separately. */ - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (id.should_be_kept()) { - outputs.point_attributes.add_new( - id, ensure_point_attribute(points, id, meta_data.data_type)); - } - return true; - }, - ATTR_DOMAIN_POINT); - - return outputs; -} - -/** - * TODO: For non-poly splines, this has double copies that could be avoided as part - * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. - */ -static void copy_evaluated_point_attributes(const Span<SplinePtr> splines, - const Span<int> offsets, - ResultAttributes &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - - data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); - spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size)); + /* Move the curve point custom data to the pointcloud, to avoid any copying. */ + CustomData_free(&pointcloud->pdata, pointcloud->totpoint); + pointcloud->pdata = curves.point_data; + CustomData_reset(&curves.point_data); - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - const GMutableSpan dst = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data()); - } - - if (!data.tangents.is_empty()) { - data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); - } - if (!data.normals.is_empty()) { - data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); - } - } - }); -} - -static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, - const Span<int> offsets, - ResultAttributes &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int num = offsets[i + 1] - offsets[i]; - if (num == 0) { - continue; - } - - const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); - - spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); - spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), - uniform_samples, - data.radii.slice(offset, num)); - - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - const GMutableSpan dst = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.sample_with_index_factors( - spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); - } - - if (!data.tangents.is_empty()) { - Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); - spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); - for (float3 &vector : sampled_tangents) { - vector = math::normalize(vector); - } - } - - if (!data.normals.is_empty()) { - Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); - spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); - for (float3 &vector : sampled_normals) { - vector = math::normalize(vector); - } - } - } - }); -} - -static void copy_spline_domain_attributes(const CurveEval &curve, - const Span<int> offsets, - PointCloudComponent &points) -{ - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id); - const CPPType &type = curve_attribute.type(); - const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type); - - for (const int i : curve.splines().index_range()) { - const int offset = offsets[i]; - const int num = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], num); - } - - return true; - }, - ATTR_DOMAIN_CURVE); + return pointcloud; } static void node_geo_exec(GeoNodeExecParams params) @@ -311,73 +109,96 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - AnonymousAttributeIDs attribute_outputs; - attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent"); - attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); - attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); - GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - geometry_set.remove_geometry_during_modify(); - return; - } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - const Span<SplinePtr> splines = curve->splines(); - curve->assert_valid_point_attributes(); - - const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_num = offsets.last(); - if (total_num == 0) { - geometry_set.remove_geometry_during_modify(); - return; - } + StrongAnonymousAttributeID tangent_anonymous_id; + StrongAnonymousAttributeID normal_anonymous_id; + StrongAnonymousAttributeID rotation_anonymous_id; + const bool rotation_required = params.output_is_required("Rotation"); + if (params.output_is_required("Tangent") || rotation_required) { + tangent_anonymous_id = StrongAnonymousAttributeID("Tangent"); + } + if (params.output_is_required("Normal") || rotation_required) { + normal_anonymous_id = StrongAnonymousAttributeID("Normal"); + } + if (rotation_required) { + rotation_anonymous_id = StrongAnonymousAttributeID("Rotation"); + } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); - PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); - ResultAttributes point_attributes = create_attributes_for_transfer( - points, *curve, attribute_outputs); + geometry::ResampleCurvesOutputAttributeIDs resample_attributes; + resample_attributes.tangent_id = tangent_anonymous_id.get(); + resample_attributes.normal_id = normal_anonymous_id.get(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: - case GEO_NODE_CURVE_RESAMPLE_LENGTH: - copy_uniform_sample_point_attributes(splines, offsets, point_attributes); - break; - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - copy_evaluated_point_attributes(splines, offsets, point_attributes); - break; + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_count( + src_curves, fn::make_constant_field<bool>(true), count, resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; } - - copy_spline_domain_attributes(*curve, offsets, points); - - if (!point_attributes.rotations.is_empty()) { - curve_create_default_rotation_attribute( - point_attributes.tangents, point_attributes.normals, point_attributes.rotations); + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + Field<float> length = params.extract_input<Field<float>>("Length"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_length( + src_curves, fn::make_constant_field<bool>(true), length, resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; } - - geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); - }); + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated( + src_curves, fn::make_constant_field<bool>(true), resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; + } params.set_output("Points", std::move(geometry_set)); - if (attribute_outputs.tangent_id) { - params.set_output( - "Tangent", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id), - params.attribute_producer_name())); + if (tangent_anonymous_id) { + params.set_output("Tangent", + AnonymousAttributeFieldInput::Create<float3>( + std::move(tangent_anonymous_id), params.attribute_producer_name())); } - if (attribute_outputs.normal_id) { - params.set_output( - "Normal", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), - params.attribute_producer_name())); + if (normal_anonymous_id) { + params.set_output("Normal", + AnonymousAttributeFieldInput::Create<float3>( + std::move(normal_anonymous_id), params.attribute_producer_name())); } - if (attribute_outputs.rotation_id) { - params.set_output( - "Rotation", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), - params.attribute_producer_name())); + if (rotation_anonymous_id) { + params.set_output("Rotation", + AnonymousAttributeFieldInput::Create<float3>( + std::move(rotation_anonymous_id), params.attribute_producer_name())); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_topology_curve_of_point.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_curve_of_point.cc new file mode 100644 index 00000000000..4d60ab939ca --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_curve_of_point.cc @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_topology_curve_of_point_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Point Index")) + .implicit_field(implicit_field_inputs::index) + .description(N_("The control point to retrieve data from")); + b.add_output<decl::Int>(N_("Curve Index")) + .dependent_field() + .description(N_("The curve the control point is part of")); + b.add_output<decl::Int>(N_("Index in Curve")) + .dependent_field() + .description(N_("How far along the control point is along its curve")); +} + +class CurveOfPointInput final : public bke::CurvesFieldInput { + public: + CurveOfPointInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Point Curve Index") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_POINT) { + return {}; + } + return VArray<int>::ForContainer(curves.point_to_curve_map()); + } + + uint64_t hash() const override + { + return 413209687345908697; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (dynamic_cast<const CurveOfPointInput *>(&other)) { + return true; + } + return false; + } +}; + +class PointIndexInCurveInput final : public bke::CurvesFieldInput { + public: + PointIndexInCurveInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Point Index in Curve") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_POINT) { + return {}; + } + const Span<int> offsets = curves.offsets(); + Array<int> point_to_curve_map = curves.point_to_curve_map(); + return VArray<int>::ForFunc( + curves.points_num(), + [offsets, point_to_curve_map = std::move(point_to_curve_map)](const int point_i) { + const int curve_i = point_to_curve_map[point_i]; + return point_i - offsets[curve_i]; + }); + } + + uint64_t hash() const final + { + return 9834765987345677; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const PointIndexInCurveInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> point_index = params.extract_input<Field<int>>("Point Index"); + if (params.output_is_required("Curve Index")) { + params.set_output( + "Curve Index", + Field<int>(std::make_shared<FieldAtIndexInput>( + point_index, Field<int>(std::make_shared<CurveOfPointInput>()), ATTR_DOMAIN_POINT))); + } + if (params.output_is_required("Index in Curve")) { + params.set_output("Index in Curve", + Field<int>(std::make_shared<FieldAtIndexInput>( + point_index, + Field<int>(std::make_shared<PointIndexInCurveInput>()), + ATTR_DOMAIN_POINT))); + } +} + +} // namespace blender::nodes::node_geo_curve_topology_curve_of_point_cc + +void register_node_type_geo_curve_topology_curve_of_point() +{ + namespace file_ns = blender::nodes::node_geo_curve_topology_curve_of_point_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_TOPOLOGY_CURVE_OF_POINT, "Curve of Point", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc new file mode 100644 index 00000000000..9f3d3c2caf3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_topology_points_of_curve_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Curve Index")) + .implicit_field(implicit_field_inputs::index) + .description(N_("The curve to retrieve data from. Defaults to the curve from the context")); + b.add_input<decl::Float>(N_("Weights")) + .supports_field() + .hide_value() + .description(N_("Values used to sort the curve's points. Uses indices by default")); + b.add_input<decl::Int>(N_("Sort Index")) + .min(0) + .supports_field() + .description(N_("Which of the sorted points to output")); + b.add_output<decl::Int>(N_("Point Index")) + .dependent_field() + .description(N_("A point of the curve, chosen by the sort index")); + b.add_output<decl::Int>(N_("Total")) + .dependent_field() + .description(N_("The number of points in the curve")); +} + +class PointsOfCurveInput final : public bke::CurvesFieldInput { + const Field<int> curve_index_; + const Field<int> sort_index_; + const Field<float> sort_weight_; + + public: + PointsOfCurveInput(Field<int> curve_index, Field<int> sort_index, Field<float> sort_weight) + : bke::CurvesFieldInput(CPPType::get<int>(), "Point of Curve"), + curve_index_(std::move(curve_index)), + sort_index_(std::move(sort_index)), + sort_weight_(std::move(sort_weight)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, + const eAttrDomain domain, + const IndexMask mask) const final + { + const bke::CurvesFieldContext context{curves, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(curve_index_); + evaluator.add(sort_index_); + evaluator.evaluate(); + const VArray<int> curve_indices = evaluator.get_evaluated<int>(0); + const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1); + + const bke::CurvesFieldContext point_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, curves.points_num()}; + point_evaluator.add(sort_weight_); + point_evaluator.evaluate(); + const VArray<float> all_sort_weights = point_evaluator.get_evaluated<float>(0); + + Array<int> point_of_curve(mask.min_array_size()); + threading::parallel_for(mask.index_range(), 256, [&](const IndexRange range) { + /* Reuse arrays to avoid allocation. */ + Array<float> sort_weights; + Array<int> sort_indices; + + for (const int selection_i : mask.slice(range)) { + const int curve_i = curve_indices[selection_i]; + const int index_in_sort = indices_in_sort[selection_i]; + if (!curves.curves_range().contains(curve_i)) { + point_of_curve[selection_i] = 0; + continue; + } + + const IndexRange points = curves.points_for_curve(curve_i); + + /* Retrieve the weights for each point. */ + sort_weights.reinitialize(points.size()); + all_sort_weights.materialize_compressed(IndexMask(points), sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(points.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { + return sort_weights[a] < sort_weights[b]; + }); + + const int index_in_sort_wrapped = mod_i(index_in_sort, points.size()); + point_of_curve[selection_i] = points[sort_indices[index_in_sort_wrapped]]; + } + }); + + return VArray<int>::ForContainer(std::move(point_of_curve)); + } + + uint64_t hash() const override + { + return 26978695677882; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const auto *typed = dynamic_cast<const PointsOfCurveInput *>(&other)) { + return typed->curve_index_ == curve_index_ && typed->sort_index_ == sort_index_ && + typed->sort_weight_ == sort_weight_; + } + return false; + } +}; + +class CurvePointCountInput final : public bke::CurvesFieldInput { + public: + CurvePointCountInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Curve Point Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_CURVE) { + return {}; + } + return VArray<int>::ForFunc(curves.curves_num(), [&, curves](const int64_t curve_i) { + return curves.points_num_for_curve(curve_i); + }); + } + + uint64_t hash() const final + { + return 903847569873762; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CurvePointCountInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> curve_index = params.extract_input<Field<int>>("Curve Index"); + if (params.output_is_required("Total")) { + params.set_output("Total", + Field<int>(std::make_shared<FieldAtIndexInput>( + curve_index, + Field<int>(std::make_shared<CurvePointCountInput>()), + ATTR_DOMAIN_CURVE))); + } + if (params.output_is_required("Point Index")) { + params.set_output("Point Index", + Field<int>(std::make_shared<PointsOfCurveInput>( + curve_index, + params.extract_input<Field<int>>("Sort Index"), + params.extract_input<Field<float>>("Weights")))); + } +} + +} // namespace blender::nodes::node_geo_curve_topology_points_of_curve_cc + +void register_node_type_geo_curve_topology_points_of_curve() +{ + namespace file_ns = blender::nodes::node_geo_curve_topology_points_of_curve_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_TOPOLOGY_POINTS_OF_CURVE, "Points of Curve", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 0932de237a9..15c89d78276 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_curves.hh" -#include "BKE_spline.hh" #include "BLI_task.hh" #include "UI_interface.h" @@ -9,12 +8,12 @@ #include "NOD_socket_search_link.hh" +#include "GEO_trim_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_trim_cc { -using blender::attribute_math::mix2; - NODE_STORAGE_FUNCS(NodeGeometryCurveTrim) static void node_declare(NodeDeclarationBuilder &b) @@ -47,12 +46,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveTrim *data = MEM_cnew<NodeGeometryCurveTrim>(__func__); @@ -65,7 +64,7 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryCurveTrim &storage = node_storage(*node); const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; - bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *start_fac = static_cast<bNodeSocket *>(node->inputs.first)->next; bNodeSocket *end_fac = start_fac->next; bNodeSocket *start_len = end_fac->next; bNodeSocket *end_len = start_len->next; @@ -96,8 +95,8 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); if (params.in_out() == SOCK_IN) { - if (params.node_tree().typeinfo->validate_link( - static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + if (params.node_tree().typeinfo->validate_link(eNodeSocketDatatype(params.other_socket().type), + SOCK_FLOAT)) { params.add_item(IFACE_("Start (Factor)"), SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_FACTOR}); params.add_item(IFACE_("End (Factor)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_FACTOR}); @@ -108,394 +107,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -struct TrimLocation { - /* Control point index at the start side of the trim location. */ - int left_index; - /* Control point index at the end of the trim location's segment. */ - int right_index; - /* The factor between the left and right indices. */ - float factor; -}; - -template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) -{ - BLI_assert(start_index + num - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * num); -} - -/* Shift slice to start of span and modifies start and end data. */ -template<typename T> -static void linear_trim_data(const TrimLocation &start, - const TrimLocation &end, - MutableSpan<T> data) -{ - const int num = end.right_index - start.left_index + 1; - - if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, num); - } - - const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); - - data.first() = start_data; - data[num - 1] = end_data; -} - -/** - * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than - * modifying the original data. - */ -template<typename T> -static void linear_trim_to_output_data(const TrimLocation &start, - const TrimLocation &end, - Span<T> src, - MutableSpan<T> dst) -{ - const int num = end.right_index - start.left_index + 1; - - const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); - const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - - dst.copy_from(src.slice(start.left_index, num)); - dst.first() = start_data; - dst.last() = end_data; -} - -/* Look up the control points to the left and right of factor, and get the factor between them. */ -static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, - const BezierSpline &spline) -{ - Span<int> offsets = spline.control_point_offsets(); - - const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index); - const int index = offset - offsets.begin(); - - const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index; - const int right = left == (spline.size() - 1) ? 0 : left + 1; - - const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_num = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); - - return {left, right, factor}; -} - -static void trim_poly_spline(Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */ - const TrimLocation start = { - start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; - const TrimLocation end = { - end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - - const int num = end.right_index - start.left_index + 1; - - linear_trim_data<float3>(start, end, spline.positions()); - linear_trim_data<float>(start, end, spline.radii()); - linear_trim_data<float>(start, end, spline.tilts()); - - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); - BLI_assert(src); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - linear_trim_data<T>(start, end, src->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - spline.resize(num); -} - -/** - * Trim NURB splines by converting to a poly spline. - */ -static PolySpline trim_nurbs_spline(const Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ - const TrimLocation start = { - start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; - const TrimLocation end = { - end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - - const int num = end.right_index - start.left_index + 1; - - /* Create poly spline and copy trimmed data to it. */ - PolySpline new_spline; - new_spline.resize(num); - - /* Copy generic attribute data. */ - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); - BLI_assert(dst); - - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); - linear_trim_to_output_data<T>( - start, end, eval_data.get_internal_span(), dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - linear_trim_to_output_data<float3>( - start, end, spline.evaluated_positions(), new_spline.positions()); - - VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); - linear_trim_to_output_data<float>( - start, end, evaluated_radii.get_internal_span(), new_spline.radii()); - - VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); - linear_trim_to_output_data<float>( - start, end, evaluated_tilts.get_internal_span(), new_spline.tilts()); - - return new_spline; -} - -/** - * Trim Bezier splines by adjusting the first and last handles - * and control points to maintain the original shape. - */ -static void trim_bezier_spline(Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); - - const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline); - TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline); - - const Span<int> control_offsets = bezier_spline.control_point_offsets(); - - /* The number of control points in the resulting spline. */ - const int num = end.right_index - start.left_index + 1; - - /* Trim the spline attributes. Done before end.factor recalculation as it needs - * the original end.factor value. */ - linear_trim_data<float>(start, end, bezier_spline.radii()); - linear_trim_data<float>(start, end, bezier_spline.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); - BLI_assert(src); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - linear_trim_data<T>(start, end, src->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - /* Recalculate end.factor if the `num` is two, because the adjustment in the - * position of the control point of the spline to the left of the new end point will change the - * factor between them. */ - if (num == 2) { - if (start_lookup.factor == 1.0f) { - end.factor = 0.0f; - } - else { - end.factor = (end_lookup.evaluated_index + end_lookup.factor - - (start_lookup.evaluated_index + start_lookup.factor)) / - (control_offsets[end.right_index] - - (start_lookup.evaluated_index + start_lookup.factor)); - end.factor = std::clamp(end.factor, 0.0f, 1.0f); - } - } - - BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion( - start.left_index, start.right_index, start.factor); - - /* Update the start control point parameters so they are used calculating the new end point. */ - bezier_spline.positions()[start.left_index] = start_point.position; - bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle; - bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next; - - const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( - end.left_index, end.right_index, end.factor); - - /* If `num` is two, then the start point right handle needs to change to reflect the end point - * previous handle update. */ - if (num == 2) { - start_point.right_handle = end_point.handle_prev; - } - - /* Shift control point position data to start at beginning of array. */ - if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, num); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); - } - - bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[num - 1] = end_point.position; - - bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; - - bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; - - /* If there is at least one control point between the endpoints, update the control - * point handle to the right of the start point and to the left of the end point. */ - if (num > 2) { - bezier_spline.handle_positions_left()[start.right_index - start.left_index] = - start_point.handle_next; - bezier_spline.handle_positions_right()[end.left_index - start.left_index] = - end_point.handle_prev; - } - - bezier_spline.resize(num); -} - -static void trim_spline(SplinePtr &spline, - const Spline::LookupResult start, - const Spline::LookupResult end) -{ - switch (spline->type()) { - case CURVE_TYPE_BEZIER: - trim_bezier_spline(*spline, start, end); - break; - case CURVE_TYPE_POLY: - trim_poly_spline(*spline, start, end); - break; - case CURVE_TYPE_NURBS: - spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); - break; - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - spline = {}; - } - spline->mark_cache_invalid(); -} - -template<typename T> -static void to_single_point_data(const TrimLocation &trim, MutableSpan<T> data) -{ - data.first() = mix2<T>(trim.factor, data[trim.left_index], data[trim.right_index]); -} -template<typename T> -static void to_single_point_data(const TrimLocation &trim, Span<T> src, MutableSpan<T> dst) -{ - dst.first() = mix2<T>(trim.factor, src[trim.left_index], src[trim.right_index]); -} - -static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup) -{ - BezierSpline &bezier = static_cast<BezierSpline &>(spline); - - const TrimLocation trim = lookup_control_point_position(lookup, bezier); - - const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( - trim.left_index, trim.right_index, trim.factor); - bezier.positions().first() = new_point.position; - bezier.handle_types_left().first() = BEZIER_HANDLE_FREE; - bezier.handle_types_right().first() = BEZIER_HANDLE_FREE; - bezier.handle_positions_left().first() = new_point.left_handle; - bezier.handle_positions_right().first() = new_point.right_handle; - - to_single_point_data<float>(trim, bezier.radii()); - to_single_point_data<float>(trim, bezier.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { - using T = decltype(dummy); - to_single_point_data<T>(trim, data->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - spline.resize(1); -} - -static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup) -{ - const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; - - to_single_point_data<float3>(trim, spline.positions()); - to_single_point_data<float>(trim, spline.radii()); - to_single_point_data<float>(trim, spline.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { - using T = decltype(dummy); - to_single_point_data<T>(trim, data->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - spline.resize(1); -} - -static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup) -{ - /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ - const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; - - /* Create poly spline and copy trimmed data to it. */ - PolySpline new_spline; - new_spline.resize(1); - - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - new_spline.attributes.create(attribute_id, meta_data.data_type); - std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); - to_single_point_data<T>(trim, eval_data.get_internal_span(), dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - to_single_point_data<float3>(trim, spline.evaluated_positions(), new_spline.positions()); - - VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); - to_single_point_data<float>(trim, evaluated_radii.get_internal_span(), new_spline.radii()); - - VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); - to_single_point_data<float>(trim, evaluated_tilts.get_internal_span(), new_spline.tilts()); - - return new_spline; -} - -static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) -{ - switch (spline->type()) { - case CURVE_TYPE_BEZIER: - to_single_point_bezier(*spline, lookup); - break; - case CURVE_TYPE_POLY: - to_single_point_poly(*spline, lookup); - break; - case CURVE_TYPE_NURBS: - spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); - break; - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - spline = {}; - } -} - static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, Field<float> &start_field, @@ -504,71 +115,50 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, if (!geometry_set.has_curves()) { return; } + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + if (src_curves.curves_num() == 0) { + return; + } - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); const VArray<float> starts = evaluator.get_evaluated<float>(0); const VArray<float> ends = evaluator.get_evaluated<float>(1); - const Curves &src_curves_id = *geometry_set.get_curves_for_read(); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id); - MutableSpan<SplinePtr> splines = curve->splines(); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - SplinePtr &spline = splines[i]; - - /* Currently trimming cyclic splines is not supported. It could be in the future though. */ - if (spline->is_cyclic()) { - continue; - } - - if (spline->evaluated_edges_num() == 0) { - continue; - } - - const float length = spline->length(); - if (length == 0.0f) { - continue; - } - - const float start = starts[i]; - const float end = ends[i]; - - /* When the start and end samples are reversed, instead of implicitly reversing the spline - * or switching the parameters, create a single point spline with the end sample point. */ - if (end <= start) { - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - to_single_point_spline(spline, - spline->lookup_evaluated_length(std::clamp(start, 0.0f, length))); - } - else { - to_single_point_spline(spline, - spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f))); - } - continue; - } - - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - trim_spline(spline, - spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)), - spline->lookup_evaluated_length(std::clamp(end, 0.0f, length))); - } - else { - trim_spline(spline, - spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)), - spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f))); - } + const VArray<bool> cyclic = src_curves.cyclic(); + + /* If node length input is on form [0, 1] instead of [0, length]*/ + const bool normalized_length_lookup = mode == GEO_NODE_CURVE_SAMPLE_FACTOR; + + /* Stack start + end field. */ + Vector<float> length_factors(src_curves.curves_num() * 2); + Vector<int64_t> lookup_indices(src_curves.curves_num() * 2); + threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange curve_range) { + for (const int64_t curve_i : curve_range) { + const bool negative_trim = !cyclic[curve_i] && starts[curve_i] > ends[curve_i]; + length_factors[curve_i] = starts[curve_i]; + length_factors[curve_i + src_curves.curves_num()] = negative_trim ? starts[curve_i] : + ends[curve_i]; + lookup_indices[curve_i] = curve_i; + lookup_indices[curve_i + src_curves.curves_num()] = curve_i; } }); - Curves *dst_curves_id = curve_eval_to_curves(*curve); + /* Create curve trim lookup table. */ + Array<bke::curves::CurvePoint, 12> point_lookups = geometry::lookup_curve_points( + src_curves, length_factors, lookup_indices, normalized_length_lookup); + + bke::CurvesGeometry dst_curves = geometry::trim_curves( + src_curves, + src_curves.curves_range().as_span(), + point_lookups.as_span().slice(0, src_curves.curves_num()), + point_lookups.as_span().slice(src_curves.curves_num(), src_curves.curves_num())); + + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); bke::curves_copy_parameters(src_curves_id, *dst_curves_id); geometry_set.replace_curves(dst_curves_id); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index 8b653296e12..0932624bdc3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -60,11 +60,18 @@ static void deform_curves(const CurvesGeometry &curves, Array<ReverseUVSampler::Result> surface_samples_old(curves_num); Array<ReverseUVSampler::Result> surface_samples_new(curves_num); threading::parallel_invoke( + 1024 < curves_num, [&]() { reverse_uv_sampler_old.sample_many(curve_attachment_uvs, surface_samples_old); }, [&]() { reverse_uv_sampler_new.sample_many(curve_attachment_uvs, surface_samples_new); }); const float4x4 curves_to_surface = surface_to_curves.inverted(); + const Span<MVert> surface_verts_old = surface_mesh_old.verts(); + const Span<MLoop> surface_loops_old = surface_mesh_old.loops(); + + const Span<MVert> surface_verts_new = surface_mesh_new.verts(); + const Span<MLoop> surface_loops_new = surface_mesh_new.loops(); + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { for (const int curve_i : range) { const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i]; @@ -91,13 +98,13 @@ static void deform_curves(const CurvesGeometry &curves, const int corner_1_new = looptri_new.tri[1]; const int corner_2_new = looptri_new.tri[2]; - const int vert_0_old = surface_mesh_old.mloop[corner_0_old].v; - const int vert_1_old = surface_mesh_old.mloop[corner_1_old].v; - const int vert_2_old = surface_mesh_old.mloop[corner_2_old].v; + const int vert_0_old = surface_loops_old[corner_0_old].v; + const int vert_1_old = surface_loops_old[corner_1_old].v; + const int vert_2_old = surface_loops_old[corner_2_old].v; - const int vert_0_new = surface_mesh_new.mloop[corner_0_new].v; - const int vert_1_new = surface_mesh_new.mloop[corner_1_new].v; - const int vert_2_new = surface_mesh_new.mloop[corner_2_new].v; + const int vert_0_new = surface_loops_new[corner_0_new].v; + const int vert_1_new = surface_loops_new[corner_1_new].v; + const int vert_2_new = surface_loops_new[corner_2_new].v; const float3 &normal_0_old = corner_normals_old[corner_0_old]; const float3 &normal_1_old = corner_normals_old[corner_1_old]; @@ -111,14 +118,14 @@ static void deform_curves(const CurvesGeometry &curves, const float3 normal_new = math::normalize( mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new)); - const float3 &pos_0_old = surface_mesh_old.mvert[vert_0_old].co; - const float3 &pos_1_old = surface_mesh_old.mvert[vert_1_old].co; - const float3 &pos_2_old = surface_mesh_old.mvert[vert_2_old].co; + const float3 &pos_0_old = surface_verts_old[vert_0_old].co; + const float3 &pos_1_old = surface_verts_old[vert_1_old].co; + const float3 &pos_2_old = surface_verts_old[vert_2_old].co; const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old); - const float3 &pos_0_new = surface_mesh_new.mvert[vert_0_new].co; - const float3 &pos_1_new = surface_mesh_new.mvert[vert_1_new].co; - const float3 &pos_2_new = surface_mesh_new.mvert[vert_2_new].co; + const float3 &pos_0_new = surface_verts_new[vert_0_new].co; + const float3 &pos_1_new = surface_verts_new[vert_1_new].co; + const float3 &pos_2_new = surface_verts_new[vert_2_new].co; const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new); /* The translation is just the difference between the old and new position on the surface. */ @@ -221,13 +228,13 @@ static void node_geo_exec(GeoNodeExecParams params) const Object *self_ob_eval = params.self_object(); if (self_ob_eval == nullptr || self_ob_eval->type != OB_CURVES) { pass_through_input(); + params.error_message_add(NodeWarningType::Error, TIP_("Node only works for curves objects")); return; } const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data); if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') { pass_through_input(); - const char *message = TIP_("Surface UV map not defined"); - params.error_message_add(NodeWarningType::Error, message); + params.error_message_add(NodeWarningType::Error, TIP_("Surface UV map not defined")); return; } /* Take surface information from self-object. */ @@ -241,14 +248,14 @@ static void node_geo_exec(GeoNodeExecParams params) } if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface"); + params.error_message_add(NodeWarningType::Error, TIP_("Curves not attached to a surface")); return; } Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval); Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data); if (BMEditMesh *em = surface_object_data.edit_mesh) { - surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, &surface_object_data); + surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, &surface_object_data); free_suface_mesh_orig = true; } else { @@ -257,21 +264,21 @@ static void node_geo_exec(GeoNodeExecParams params) Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval); if (surface_mesh_eval == nullptr) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Surface has no mesh"); + params.error_message_add(NodeWarningType::Error, TIP_("Surface has no mesh")); return; } BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval); - const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval); - const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig); + const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes(); + const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes(); Curves &curves_id = *curves_geometry.get_curves_for_write(); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); if (!mesh_attributes_eval.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s"), + char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: \"%s\""), uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); @@ -279,7 +286,8 @@ static void node_geo_exec(GeoNodeExecParams params) } if (!mesh_attributes_orig.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s"), uv_map_name.c_str()); + char *message = BLI_sprintfN(TIP_("Original surface missing UV map: \"%s\""), + uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); return; @@ -287,7 +295,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (!mesh_attributes_eval.contains(rest_position_name)) { pass_through_input(); params.error_message_add(NodeWarningType::Error, - TIP_("Evaluated surface missing attribute: rest_position")); + TIP_("Evaluated surface missing attribute: \"rest_position\"")); return; } if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) { @@ -304,10 +312,8 @@ static void node_geo_exec(GeoNodeExecParams params) ATTR_DOMAIN_POINT); const Span<float2> surface_uv_coords = curves.surface_uv_coords(); - const Span<MLoopTri> looptris_orig{BKE_mesh_runtime_looptri_ensure(surface_mesh_orig), - BKE_mesh_runtime_looptri_len(surface_mesh_orig)}; - const Span<MLoopTri> looptris_eval{BKE_mesh_runtime_looptri_ensure(surface_mesh_eval), - BKE_mesh_runtime_looptri_len(surface_mesh_eval)}; + const Span<MLoopTri> looptris_orig = surface_mesh_orig->looptris(); + const Span<MLoopTri> looptris_eval = surface_mesh_eval->looptris(); const ReverseUVSampler reverse_uv_sampler_orig{uv_map_orig, looptris_orig}; const ReverseUVSampler reverse_uv_sampler_eval{uv_map_eval, looptris_eval}; @@ -373,19 +379,22 @@ static void node_geo_exec(GeoNodeExecParams params) invalid_uv_count); /* Then also deform edit curve information for use in sculpt mode. */ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(edit_hints->curves_id_orig.geometry); - deform_curves(curves_orig, - *surface_mesh_orig, - *surface_mesh_eval, - surface_uv_coords, - reverse_uv_sampler_orig, - reverse_uv_sampler_eval, - corner_normals_orig, - corner_normals_eval, - rest_positions, - transforms.surface_to_curves, - edit_hint_positions, - edit_hint_rotations, - invalid_uv_count); + const Span<float2> surface_uv_coords_orig = curves_orig.surface_uv_coords(); + if (!surface_uv_coords_orig.is_empty()) { + deform_curves(curves_orig, + *surface_mesh_orig, + *surface_mesh_eval, + surface_uv_coords_orig, + reverse_uv_sampler_orig, + reverse_uv_sampler_eval, + corner_normals_orig, + corner_normals_eval, + rest_positions, + transforms.surface_to_curves, + edit_hint_positions, + edit_hint_rotations, + invalid_uv_count); + } } curves.tag_positions_changed(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index b74b4e45199..3e48a9fd923 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -4,13 +4,16 @@ #include "UI_resources.h" #include "BLI_array.hh" +#include "BLI_array_utils.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_customdata.h" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -21,15 +24,9 @@ namespace blender::nodes::node_geo_delete_geometry_cc { using blender::bke::CustomDataAttributes; template<typename T> -static void copy_data_based_on_mask(Span<T> data, MutableSpan<T> r_data, IndexMask mask) -{ - for (const int i_out : mask.index_range()) { - r_data[i_out] = data[mask[i_out]]; - } -} - -template<typename T> -static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> index_map) +static void copy_data_based_on_map(const Span<T> src, + const Span<int> index_map, + MutableSpan<T> dst) { for (const int i_src : index_map.index_range()) { const int i_dst = index_map[i_src]; @@ -53,26 +50,17 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes if (!attribute) { continue; } - /* Only copy if it is on a domain we want. */ if (!domains.contains(attribute.domain)) { continue; } const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, attribute.domain, data_type); - if (!result_attribute) { continue; } - - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArraySpan<T> span{attribute.varray.typed<T>()}; - MutableSpan<T> out_span = result_attribute.span.typed<T>(); - out_span.copy_from(span); - }); + attribute.varray.materialize(result_attribute.span.data()); result_attribute.finish(); } } @@ -93,26 +81,19 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin if (!attribute) { continue; } - /* Only copy if it is on a domain we want. */ if (domain != attribute.domain) { continue; } const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, attribute.domain, data_type); - if (!result_attribute) { continue; } - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArraySpan<T> span{attribute.varray.typed<T>()}; - MutableSpan<T> out_span = result_attribute.span.typed<T>(); - copy_data_based_on_mask(span, out_span, mask); - }); + array_utils::gather(attribute.varray, mask, result_attribute.span); + result_attribute.finish(); } } @@ -129,16 +110,13 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind if (!attribute) { continue; } - /* Only copy if it is on a domain we want. */ if (domain != attribute.domain) { continue; } const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); - GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, attribute.domain, data_type); - if (!result_attribute) { continue; } @@ -147,7 +125,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind using T = decltype(dummy); VArraySpan<T> span{attribute.varray.typed<T>()}; MutableSpan<T> out_span = result_attribute.span.typed<T>(); - copy_data_based_on_map(span, out_span, index_map); + copy_data_based_on_map(span, index_map, out_span); }); result_attribute.finish(); } @@ -160,10 +138,11 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> const Span<int> selected_poly_indices, const Mesh &mesh_in) { + const Span<MPoly> polys = mesh_in.polys(); Vector<int64_t> indices; indices.reserve(selected_loops_num); for (const int src_poly_index : selected_poly_indices) { - const MPoly &src_poly = mesh_in.mpoly[src_poly_index]; + const MPoly &src_poly = polys[src_poly_index]; const int src_loop_start = src_poly.loopstart; const int tot_loop = src_poly.totloop; for (const int i : IndexRange(tot_loop)) { @@ -174,39 +153,35 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices)); } -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map) +static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); + const Span<MVert> src_verts = src_mesh.verts(); + MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write(); + for (const int i_src : vertex_map.index_range()) { const int i_dst = vertex_map[i_src]; if (i_dst == -1) { continue; } - - const MVert &v_src = src_mesh.mvert[i_src]; - MVert &v_dst = dst_mesh.mvert[i_dst]; - - v_dst = v_src; + dst_verts[i_dst] = src_verts[i_src]; } } static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map) { BLI_assert(src_mesh.totedge == edge_map.size()); + const Span<MEdge> src_edges = src_mesh.edges(); + MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write(); + for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; if (ELEM(i_dst, -1, -2)) { continue; } - - const MEdge &e_src = src_mesh.medge[i_src]; - MEdge &e_dst = dst_mesh.medge[i_dst]; - - e_dst = e_src; - e_dst.v1 = e_src.v1; - e_dst.v2 = e_src.v2; + dst_edges[i_dst] = src_edges[i_src]; } } @@ -217,14 +192,16 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, { BLI_assert(src_mesh.totvert == vertex_map.size()); BLI_assert(src_mesh.totedge == edge_map.size()); + const Span<MEdge> src_edges = src_mesh.edges(); + MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write(); + for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; if (i_dst == -1) { continue; } - - const MEdge &e_src = src_mesh.medge[i_src]; - MEdge &e_dst = dst_mesh.medge[i_dst]; + const MEdge &e_src = src_edges[i_src]; + MEdge &e_dst = dst_edges[i_dst]; e_dst = e_src; e_dst.v1 = vertex_map[e_src.v1]; @@ -239,16 +216,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { + const Span<MPoly> src_polys = src_mesh.polys(); + const Span<MLoop> src_loops = src_mesh.loops(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); + MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); + for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + const MLoop *ml_src = &src_loops[i_ml_src]; + MLoop *ml_dst = &dst_loops[i_ml_dst]; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; @@ -265,16 +247,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { + const Span<MPoly> src_polys = src_mesh.polys(); + const Span<MLoop> src_loops = src_mesh.loops(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); + MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); + for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + const MLoop *ml_src = &src_loops[i_ml_src]; + MLoop *ml_dst = &dst_loops[i_ml_dst]; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; @@ -292,16 +279,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { + const Span<MPoly> src_polys = src_mesh.polys(); + const Span<MLoop> src_loops = src_mesh.loops(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); + MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); + for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + const MLoop *ml_src = &src_loops[i_ml_src]; + MLoop *ml_dst = &dst_loops[i_ml_dst]; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; @@ -316,18 +308,19 @@ static void delete_curves_selection(GeometrySet &geometry_set, const Field<bool> &selection_field, const eAttrDomain selection_domain) { - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - GeometryComponentFieldContext field_context{src_component, selection_domain}; + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - const int domain_num = src_component.attribute_domain_size(selection_domain); - fn::FieldEvaluator evaluator{field_context, domain_num}; + const int domain_size = src_curves.attributes().domain_size(selection_domain); + bke::CurvesFieldContext field_context{src_curves, selection_domain}; + fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); if (selection.is_empty()) { return; } - if (selection.size() == domain_num) { + if (selection.size() == domain_size) { geometry_set.remove<CurveComponent>(); return; } @@ -347,11 +340,10 @@ static void delete_curves_selection(GeometrySet &geometry_set, static void separate_point_cloud_selection(GeometrySet &geometry_set, const Field<bool> &selection_field) { - const PointCloudComponent &src_points = - *geometry_set.get_component_for_read<PointCloudComponent>(); - GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; + const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read(); - fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + bke::PointCloudFieldContext field_context{src_pointcloud}; + fn::FieldEvaluator evaluator{field_context, src_pointcloud.totpoint}; evaluator.set_selection(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); @@ -367,8 +359,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); copy_attributes_based_on_mask(attributes, - bke::pointcloud_attributes(*src_points.get_for_read()), - bke::pointcloud_attributes_for_write(*pointcloud), + src_pointcloud.attributes(), + pointcloud->attributes_for_write(), ATTR_DOMAIN_POINT, selection); geometry_set.replace_pointcloud(pointcloud); @@ -377,8 +369,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, static void delete_selected_instances(GeometrySet &geometry_set, const Field<bool> &selection_field) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; + bke::Instances &instances = *geometry_set.get_instances_for_write(); + bke::InstancesFieldContext field_context{instances}; fn::FieldEvaluator evaluator{field_context, instances.instances_num()}; evaluator.set_selection(selection_field); @@ -389,12 +381,12 @@ static void delete_selected_instances(GeometrySet &geometry_set, return; } - instances.remove_instances(selection); + instances.remove(selection); } -static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection, - MutableSpan<int> r_vertex_map, - int *r_selected_vertices_num) +static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection, + MutableSpan<int> r_vertex_map, + int *r_selected_verts_num) { BLI_assert(vertex_selection.size() == r_vertex_map.size()); @@ -409,7 +401,7 @@ static void compute_selected_vertices_from_vertex_selection(const Span<bool> ver } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; } static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, @@ -418,10 +410,11 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, int *r_selected_edges_num) { BLI_assert(mesh.totedge == r_edge_map.size()); + const Span<MEdge> edges = mesh.edges(); int selected_edges_num = 0; for (const int i : IndexRange(mesh.totedge)) { - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; /* Only add the edge if both vertices will be in the new mesh. */ if (vertex_selection[edge.v1] && vertex_selection[edge.v2]) { @@ -436,25 +429,27 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, *r_selected_edges_num = selected_edges_num; } -static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, - const Span<bool> vertex_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_vertex_selection(const Mesh &mesh, + const Span<bool> vertex_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totvert == vertex_selection.size()); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int selected_loops_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; bool all_verts_in_selection = true; - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { if (!vertex_selection[loop.v]) { all_verts_in_selection = false; break; @@ -476,20 +471,20 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the * edge are kept along with the edge. */ -static void compute_selected_vertices_and_edges_from_edge_selection( - const Mesh &mesh, - const Span<bool> edge_selection, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - int *r_selected_vertices_num, - int *r_selected_edges_num) +static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + int *r_selected_verts_num, + int *r_selected_edges_num) { BLI_assert(mesh.totedge == edge_selection.size()); + const Span<MEdge> edges = mesh.edges(); int selected_edges_num = 0; int selected_verts_num = 0; for (const int i : IndexRange(mesh.totedge)) { - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; if (edge_selection[i]) { r_edge_map[i] = selected_edges_num; selected_edges_num++; @@ -507,7 +502,7 @@ static void compute_selected_vertices_and_edges_from_edge_selection( } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; *r_selected_edges_num = selected_edges_num; } @@ -539,23 +534,26 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh, * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that * polygon is kept. */ -static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, - const Span<bool> edge_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int selected_loops_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; bool all_edges_in_selection = true; - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { if (!edge_selection[loop.e]) { all_edges_in_selection = false; break; @@ -590,12 +588,12 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face( compute_selected_edges_from_vertex_selection( mesh, vertex_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh, + vertex_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -608,23 +606,23 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { - compute_selected_vertices_from_vertex_selection( - vertex_selection, r_vertex_map, r_selected_vertices_num); + compute_selected_verts_from_vertex_selection( + vertex_selection, r_vertex_map, r_selected_verts_num); compute_selected_edges_from_vertex_selection( mesh, vertex_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh, + vertex_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -643,17 +641,17 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face( { compute_selected_edges_from_edge_selection( mesh, edge_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_edge_selection(mesh, + edge_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to - * that edge are kept as well. The polygons are kept if all edges are in the selection. + * that edge are kept as well. The polys are kept if all edges are in the selection. */ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, const Span<bool> edge_selection, @@ -661,44 +659,41 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { r_vertex_map.fill(-1); - compute_selected_vertices_and_edges_from_edge_selection(mesh, - edge_selection, - r_vertex_map, - r_edge_map, - r_selected_vertices_num, - r_selected_edges_num); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_verts_and_edges_from_edge_selection( + mesh, edge_selection, r_vertex_map, r_edge_map, r_selected_verts_num, r_selected_edges_num); + compute_selected_polys_from_edge_selection(mesh, + edge_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** * Checks for every polygon if it is in `poly_selection`. */ -static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, - const Span<bool> poly_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_poly_selection(const Mesh &mesh, + const Span<bool> poly_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); + const Span<MPoly> polys = mesh.polys(); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int selected_loops_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; /* We keep this one. */ if (poly_selection[i]) { r_selected_poly_indices.append_unchecked(i); @@ -725,6 +720,9 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_edge_map.fill(-1); r_selected_poly_indices.reserve(mesh.totpoly); @@ -732,8 +730,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( int selected_loops_num = 0; int selected_edges_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; /* We keep this one. */ if (poly_selection[i]) { r_selected_poly_indices.append_unchecked(i); @@ -741,8 +739,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( selected_loops_num += poly_src.totloop; /* Add the vertices and the edges. */ - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { /* Check first if it has not yet been added. */ if (r_edge_map[loop.e] == -1) { r_edge_map[loop.e] = selected_edges_num; @@ -766,13 +764,16 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_vertex_map.fill(-1); r_edge_map.fill(-1); @@ -782,8 +783,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, int selected_loops_num = 0; int selected_verts_num = 0; int selected_edges_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; /* We keep this one. */ if (poly_selection[i]) { r_selected_poly_indices.append_unchecked(i); @@ -791,8 +792,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, selected_loops_num += poly_src.totloop; /* Add the vertices and the edges. */ - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { /* Check first if it has not yet been added. */ if (r_vertex_map[loop.v] == -1) { r_vertex_map[loop.v] = selected_verts_num; @@ -805,7 +806,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, } } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; *r_selected_edges_num = selected_edges_num; *r_selected_polys_num = r_selected_poly_indices.size(); *r_selected_loops_num = selected_loops_num; @@ -890,30 +891,30 @@ static void do_mesh_separation(GeometrySet &geometry_set, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); + copy_masked_verts_to_new_mesh(mesh_in, *mesh_out, vertex_map); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_POINT, vertex_map); copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -967,29 +968,27 @@ static void do_mesh_separation(GeometrySet &geometry_set, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + mesh_out->verts_for_write().copy_from(mesh_in.verts()); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ - copy_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), - {ATTR_DOMAIN_POINT}); + copy_attributes( + attributes, mesh_in.attributes(), mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT}); copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -999,28 +998,28 @@ static void do_mesh_separation(GeometrySet &geometry_set, /* Fill all the maps based on the selection. */ switch (domain) { case ATTR_DOMAIN_POINT: - compute_selected_polygons_from_vertex_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_EDGE: - compute_selected_polygons_from_edge_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_edge_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_FACE: - compute_selected_polygons_from_poly_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_poly_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; default: BLI_assert_unreachable(); @@ -1030,23 +1029,23 @@ static void do_mesh_separation(GeometrySet &geometry_set, &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); - memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge)); + mesh_out->verts_for_write().copy_from(mesh_in.verts()); + mesh_out->edges_for_write().copy_from(mesh_in.edges()); copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -1063,11 +1062,9 @@ static void separate_mesh_selection(GeometrySet &geometry_set, const eAttrDomain selection_domain, const GeometryNodeDeleteGeometryMode mode) { - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext field_context{src_component, selection_domain}; - - fn::FieldEvaluator evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + const Mesh &src_mesh = *geometry_set.get_mesh_for_read(); + bke::MeshFieldContext field_context{src_mesh, selection_domain}; + fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)}; evaluator.add(selection_field); evaluator.evaluate(); const VArray<bool> selection = evaluator.get_evaluated<bool>(0); @@ -1078,8 +1075,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, const VArraySpan<bool> selection_span{selection}; - do_mesh_separation( - geometry_set, *src_component.get_for_read(), selection_span, selection_domain, mode); + do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode); } } // namespace blender::nodes::node_geo_delete_geometry_cc @@ -1140,11 +1136,11 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { const bNode *node = static_cast<bNode *>(ptr->data); const NodeGeometryDeleteGeometry &storage = node_storage(*node); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); + const eAttrDomain domain = eAttrDomain(storage.domain); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); /* Only show the mode when it is relevant. */ @@ -1153,7 +1149,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) } } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryDeleteGeometry *data = MEM_cnew<NodeGeometryDeleteGeometry>(__func__); data->domain = ATTR_DOMAIN_POINT; @@ -1173,7 +1169,7 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<Field<bool>>("Selection")); const NodeGeometryDeleteGeometry &storage = node_storage(params.node()); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); + const eAttrDomain domain = eAttrDomain(storage.domain); const GeometryNodeDeleteGeometryMode mode = (GeometryNodeDeleteGeometryMode)storage.mode; if (domain == ATTR_DOMAIN_INSTANCE) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc new file mode 100644 index 00000000000..95173bd23a5 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/Interpolation.h> +# include <openvdb/tools/PointScatter.h> +#endif + +#include "DNA_node_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_pointcloud.h" +#include "BKE_volume.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DEG_depsgraph_query.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeGeometryDistributePointsInVolume) + +static void geo_node_distribute_points_in_volume_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); + b.add_input<decl::Float>(N_("Density")) + .default_value(1.0f) + .min(0.0f) + .max(100000.0f) + .subtype(PROP_NONE) + .description(N_("Number of points to sample per unit volume")); + b.add_input<decl::Int>(N_("Seed")) + .min(-10000) + .max(10000) + .description(N_("Seed used by the random number generator to generate random points")); + b.add_input<decl::Vector>(N_("Spacing")) + .default_value({0.3, 0.3, 0.3}) + .min(0.0001f) + .subtype(PROP_XYZ) + .description(N_("Spacing between grid points")); + b.add_input<decl::Float>(N_("Threshold")) + .default_value(0.1f) + .min(0.0f) + .max(FLT_MAX) + .description(N_("Minimum density of a volume cell to contain a grid point")); + b.add_output<decl::Geometry>(N_("Points")); +} + +static void geo_node_distribute_points_in_volume_layout(uiLayout *layout, + bContext * /*C*/, + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void node_distribute_points_in_volume_init(bNodeTree * /*tree*/, bNode *node) +{ + NodeGeometryDistributePointsInVolume *data = MEM_cnew<NodeGeometryDistributePointsInVolume>( + __func__); + data->mode = GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM; + node->storage = data; +} + +static void node_distribute_points_in_volume_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryDistributePointsInVolume &storage = node_storage(*node); + GeometryNodeDistributePointsInVolumeMode mode = GeometryNodeDistributePointsInVolumeMode( + storage.mode); + + bNodeSocket *sock_density = static_cast<bNodeSocket *>(node->inputs.first)->next; + bNodeSocket *sock_seed = sock_density->next; + bNodeSocket *sock_spacing = sock_seed->next; + bNodeSocket *sock_threshold = sock_spacing->next; + + nodeSetSocketAvailability( + ntree, sock_density, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM); + nodeSetSocketAvailability( + ntree, sock_seed, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM); + nodeSetSocketAvailability( + ntree, sock_spacing, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID); + nodeSetSocketAvailability( + ntree, sock_threshold, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID); +} + +#ifdef WITH_OPENVDB +/* Implements the interface required by #openvdb::tools::NonUniformPointScatter. */ +class PositionsVDBWrapper { + private: + float3 offset_fix_; + Vector<float3> &vector_; + + public: + PositionsVDBWrapper(Vector<float3> &vector, const float3 offset_fix) + : offset_fix_(offset_fix), vector_(vector) + { + } + PositionsVDBWrapper(const PositionsVDBWrapper &wrapper) = default; + + void add(const openvdb::Vec3R &pos) + { + vector_.append(float3(float(pos[0]), float(pos[1]), float(pos[2])) + offset_fix_); + } +}; + +/* Use #std::mt19937 as a random number generator, + * it has a very long period and thus there should be no visible patterns in the generated points. + */ +using RNGType = std::mt19937; +/* Non-uniform scatter allows the amount of points to be scaled with the volume's density. */ +using NonUniformPointScatterVDB = + openvdb::tools::NonUniformPointScatter<PositionsVDBWrapper, RNGType>; + +static void point_scatter_density_random(const openvdb::FloatGrid &grid, + const float density, + const int seed, + Vector<float3> &r_positions) +{ + /* Offset points by half a voxel so that grid points are aligned with world grid points. */ + const float3 offset_fix = {0.5f * float(grid.voxelSize().x()), + 0.5f * float(grid.voxelSize().y()), + 0.5f * float(grid.voxelSize().z())}; + /* Setup and call into OpenVDB's point scatter API. */ + PositionsVDBWrapper vdb_position_wrapper = PositionsVDBWrapper(r_positions, offset_fix); + RNGType random_generator(seed); + NonUniformPointScatterVDB point_scatter(vdb_position_wrapper, density, random_generator); + point_scatter(grid); +} + +static void point_scatter_density_grid(const openvdb::FloatGrid &grid, + const float3 spacing, + const float threshold, + Vector<float3> &r_positions) +{ + const openvdb::Vec3d half_voxel(0.5, 0.5, 0.5); + const openvdb::Vec3d voxel_spacing(double(spacing.x) / grid.voxelSize().x(), + double(spacing.y) / grid.voxelSize().y(), + double(spacing.z) / grid.voxelSize().z()); + + /* Abort if spacing is zero. */ + const double min_spacing = std::min(voxel_spacing.x(), + std::min(voxel_spacing.y(), voxel_spacing.z())); + if (std::abs(min_spacing) < 0.0001) { + return; + } + + /* Iterate through tiles and voxels on the grid. */ + for (openvdb::FloatGrid::ValueOnCIter cell = grid.cbeginValueOn(); cell; ++cell) { + /* Check if the cell's value meets the minimum threshold. */ + if (cell.getValue() < threshold) { + continue; + } + /* Compute the bounding box of each tile/voxel. */ + const openvdb::CoordBBox bbox = cell.getBoundingBox(); + const openvdb::Vec3d box_min = bbox.min().asVec3d() - half_voxel; + const openvdb::Vec3d box_max = bbox.max().asVec3d() + half_voxel; + + /* Pick a starting point rounded up to the nearest possible point. */ + double abs_spacing_x = std::abs(voxel_spacing.x()); + double abs_spacing_y = std::abs(voxel_spacing.y()); + double abs_spacing_z = std::abs(voxel_spacing.z()); + const openvdb::Vec3d start(ceil(box_min.x() / abs_spacing_x) * abs_spacing_x, + ceil(box_min.y() / abs_spacing_y) * abs_spacing_y, + ceil(box_min.z() / abs_spacing_z) * abs_spacing_z); + + /* Iterate through all possible points in box. */ + for (double x = start.x(); x < box_max.x(); x += abs_spacing_x) { + for (double y = start.y(); y < box_max.y(); y += abs_spacing_y) { + for (double z = start.z(); z < box_max.z(); z += abs_spacing_z) { + /* Transform with grid matrix and add point. */ + const openvdb::Vec3d idx_pos(x, y, z); + const openvdb::Vec3d local_pos = grid.indexToWorld(idx_pos + half_voxel); + r_positions.append({float(local_pos.x()), float(local_pos.y()), float(local_pos.z())}); + } + } + } + } +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_distribute_points_in_volume_exec(GeoNodeExecParams params) +{ +#ifdef WITH_OPENVDB + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); + + const NodeGeometryDistributePointsInVolume &storage = node_storage(params.node()); + const GeometryNodeDistributePointsInVolumeMode mode = GeometryNodeDistributePointsInVolumeMode( + storage.mode); + + float density; + int seed; + float3 spacing{0, 0, 0}; + float threshold; + if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) { + density = params.extract_input<float>("Density"); + seed = params.extract_input<int>("Seed"); + } + else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) { + spacing = params.extract_input<float3>("Spacing"); + threshold = params.extract_input<float>("Threshold"); + } + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_volume()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + const VolumeComponent *component = geometry_set.get_component_for_read<VolumeComponent>(); + const Volume *volume = component->get_for_read(); + BKE_volume_load(volume, DEG_get_bmain(params.depsgraph())); + + Vector<float3> positions; + + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + if (volume_grid == nullptr) { + continue; + } + + openvdb::GridBase::ConstPtr base_grid = BKE_volume_grid_openvdb_for_read(volume, + volume_grid); + if (!base_grid) { + continue; + } + + if (!base_grid->isType<openvdb::FloatGrid>()) { + continue; + } + + const openvdb::FloatGrid::ConstPtr grid = openvdb::gridConstPtrCast<openvdb::FloatGrid>( + base_grid); + + if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) { + point_scatter_density_random(*grid, density, seed, positions); + } + else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) { + point_scatter_density_grid(*grid, spacing, threshold, positions); + } + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); + bke::SpanAttributeWriter<float3> point_positions = + point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); + bke::SpanAttributeWriter<float> point_radii = + point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT); + + point_positions.span.copy_from(positions); + point_radii.span.fill(0.05f); + point_positions.finish(); + point_radii.finish(); + + geometry_set.replace_pointcloud(pointcloud); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); + }); + + params.set_output("Points", std::move(geometry_set)); + +#else + params.set_default_remaining_outputs(); + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif +} +} // namespace blender::nodes + +void register_node_type_geo_distribute_points_in_volume() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, + GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME, + "Distribute Points in Volume", + NODE_CLASS_GEOMETRY); + node_type_storage(&ntype, + "NodeGeometryDistributePointsInVolume", + node_free_standard_storage, + node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::node_distribute_points_in_volume_init); + node_type_update(&ntype, blender::nodes::node_distribute_points_in_volume_update); + node_type_size(&ntype, 170, 100, 320); + ntype.declare = blender::nodes::geo_node_distribute_points_in_volume_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_distribute_points_in_volume_exec; + ntype.draw_buttons = blender::nodes::geo_node_distribute_points_in_volume_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index cfb9cbf7e24..c2cc70296ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -62,15 +62,15 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE); } static void node_point_distribute_points_on_faces_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 2); - bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next; + bNodeSocket *sock_distance_min = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 2)); + bNodeSocket *sock_density_max = static_cast<bNodeSocket *>(sock_distance_min->next); bNodeSocket *sock_density = sock_density_max->next; bNodeSocket *sock_density_factor = sock_density->next; nodeSetSocketAvailability(ntree, @@ -105,20 +105,21 @@ static void sample_mesh_surface(const Mesh &mesh, Vector<float3> &r_bary_coords, Vector<int> &r_looptri_indices) { - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MVert> verts = mesh.verts(); + const Span<MLoop> loops = mesh.loops(); + const Span<MLoopTri> looptris = mesh.looptris(); for (const int looptri_index : looptris.index_range()) { const MLoopTri &looptri = looptris[looptri_index]; const int v0_loop = looptri.tri[0]; const int v1_loop = looptri.tri[1]; const int v2_loop = looptri.tri[2]; - const int v0_index = mesh.mloop[v0_loop].v; - const int v1_index = mesh.mloop[v1_loop].v; - const int v2_index = mesh.mloop[v2_loop].v; - const float3 v0_pos = float3(mesh.mvert[v0_index].co); - const float3 v1_pos = float3(mesh.mvert[v1_index].co); - const float3 v2_pos = float3(mesh.mvert[v2_index].co); + const int v0_index = loops[v0_loop].v; + const int v1_index = loops[v1_loop].v; + const int v2_index = loops[v2_loop].v; + const float3 v0_pos = verts[v0_index].co; + const float3 v1_pos = verts[v1_index].co; + const float3 v2_pos = verts[v2_index].co; float looptri_density_factor = 1.0f; if (!density_factors.is_empty()) { @@ -184,7 +185,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( kdtree, positions[i], minimum_distance, - [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { + [](void *user_data, int index, const float * /*co*/, float /*dist_sq*/) { CallbackData &callback_data = *static_cast<CallbackData *>(user_data); if (index != callback_data.index) { callback_data.elimination_mask[index] = true; @@ -202,8 +203,7 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const Span<int> looptri_indices, const MutableSpan<bool> elimination_mask) { - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MLoopTri> looptris = mesh.looptris(); for (const int i : bary_coords.index_range()) { if (elimination_mask[i]) { continue; @@ -220,11 +220,11 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); - const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + - v2_density_factor * bary_coord.z; + const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + + v2_density_factor * bary_coord.z; const float hash = noise::hash_float_to_float(bary_coord); - if (hash > probablity) { + if (hash > probability) { elimination_mask[i] = true; } } @@ -283,15 +283,14 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, } BLI_NOINLINE static void propagate_existing_attributes( - const MeshComponent &mesh_component, + const Mesh &mesh, const Map<AttributeIDRef, AttributeKind> &attributes, - GeometryComponent &point_component, + PointCloud &points, const Span<float3> bary_coords, const Span<int> looptri_indices) { - const Mesh &mesh = *mesh_component.get_for_read(); - const AttributeAccessor mesh_attributes = *mesh_component.attributes(); - MutableAttributeAccessor point_attributes = *point_component.attributes_for_write(); + const AttributeAccessor mesh_attributes = mesh.attributes(); + MutableAttributeAccessor point_attributes = points.attributes_for_write(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -326,44 +325,44 @@ struct AttributeOutputs { }; } // namespace -BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component, - PointCloudComponent &point_component, +BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh, + PointCloud &points, const Span<float3> bary_coords, const Span<int> looptri_indices, const AttributeOutputs &attribute_outputs) { - MutableAttributeAccessor pointcloud_attributes = *point_component.attributes_for_write(); + MutableAttributeAccessor point_attributes = points.attributes_for_write(); - SpanAttributeWriter<int> ids = pointcloud_attributes.lookup_or_add_for_write_only_span<int>( + SpanAttributeWriter<int> ids = point_attributes.lookup_or_add_for_write_only_span<int>( "id", ATTR_DOMAIN_POINT); SpanAttributeWriter<float3> normals; SpanAttributeWriter<float3> rotations; if (attribute_outputs.normal_id) { - normals = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>( + normals = point_attributes.lookup_or_add_for_write_only_span<float3>( attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT); } if (attribute_outputs.rotation_id) { - rotations = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>( + rotations = point_attributes.lookup_or_add_for_write_only_span<float3>( attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT); } - const Mesh &mesh = *mesh_component.get_for_read(); - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; + const Span<MVert> verts = mesh.verts(); + const Span<MLoop> loops = mesh.loops(); + const Span<MLoopTri> looptris = mesh.looptris(); for (const int i : bary_coords.index_range()) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const float3 &bary_coord = bary_coords[i]; - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - const float3 v0_pos = float3(mesh.mvert[v0_index].co); - const float3 v1_pos = float3(mesh.mvert[v1_index].co); - const float3 v2_pos = float3(mesh.mvert[v2_index].co); + const int v0_index = loops[looptri.tri[0]].v; + const int v1_index = loops[looptri.tri[1]].v; + const int v2_index = loops[looptri.tri[2]].v; + const float3 v0_pos = verts[v0_index].co; + const float3 v1_pos = verts[v1_index].co; + const float3 v2_pos = verts[v2_index].co; ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); @@ -380,25 +379,19 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com } ids.finish(); - - if (normals) { - normals.finish(); - } - if (rotations) { - rotations.finish(); - } + normals.finish(); + rotations.finish(); } -static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component, +static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh, const Field<float> &density_field, const Field<bool> &selection_field) { - const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER; - GeometryComponentFieldContext field_context{component, attribute_domain}; - const int domain_size = component.attribute_domain_size(attribute_domain); - + const eAttrDomain domain = ATTR_DOMAIN_CORNER; + const int domain_size = mesh.attributes().domain_size(domain); Array<float> densities(domain_size, 0.0f); + bke::MeshFieldContext field_context{mesh, domain}; fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); evaluator.add_with_destination(density_field, densities.as_mutable_span()); @@ -406,7 +399,7 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent return densities; } -static void distribute_points_random(const MeshComponent &component, +static void distribute_points_random(const Mesh &mesh, const Field<float> &density_field, const Field<bool> &selection_field, const int seed, @@ -415,12 +408,11 @@ static void distribute_points_random(const MeshComponent &component, Vector<int> &looptri_indices) { const Array<float> densities = calc_full_density_factors_with_selection( - component, density_field, selection_field); - const Mesh &mesh = *component.get_for_read(); + mesh, density_field, selection_field); sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices); } -static void distribute_points_poisson_disk(const MeshComponent &mesh_component, +static void distribute_points_poisson_disk(const Mesh &mesh, const float minimum_distance, const float max_density, const Field<float> &density_factor_field, @@ -430,14 +422,13 @@ static void distribute_points_poisson_disk(const MeshComponent &mesh_component, Vector<float3> &bary_coords, Vector<int> &looptri_indices) { - const Mesh &mesh = *mesh_component.get_for_read(); sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices); Array<bool> elimination_mask(positions.size(), false); update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask); const Array<float> density_factors = calc_full_density_factors_with_selection( - mesh_component, density_factor_field, selection_field); + mesh, density_factor_field, selection_field); update_elimination_mask_based_on_density_factors( mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span()); @@ -457,7 +448,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, return; } - const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); Vector<float3> positions; Vector<float3> bary_coords; @@ -466,20 +457,15 @@ static void point_distribution_calculate(GeometrySet &geometry_set, switch (method) { case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: { const Field<float> density_field = params.get_input<Field<float>>("Density"); - distribute_points_random(mesh_component, - density_field, - selection_field, - seed, - positions, - bary_coords, - looptri_indices); + distribute_points_random( + mesh, density_field, selection_field, seed, positions, bary_coords, looptri_indices); break; } case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: { const float minimum_distance = params.get_input<float>("Distance Min"); const float density_max = params.get_input<float>("Density Max"); const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor"); - distribute_points_poisson_disk(mesh_component, + distribute_points_poisson_disk(mesh, minimum_distance, density_max, density_factors_field, @@ -497,8 +483,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); bke::SpanAttributeWriter<float3> point_positions = point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); bke::SpanAttributeWriter<float> point_radii = @@ -510,9 +495,6 @@ static void point_distribution_calculate(GeometrySet &geometry_set, geometry_set.replace_pointcloud(pointcloud); - PointCloudComponent &point_component = - geometry_set.get_component_for_write<PointCloudComponent>(); - Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); @@ -520,19 +502,17 @@ static void point_distribution_calculate(GeometrySet &geometry_set, /* Position is set separately. */ attributes.remove("position"); - propagate_existing_attributes( - mesh_component, attributes, point_component, bary_coords, looptri_indices); + propagate_existing_attributes(mesh, attributes, *pointcloud, bary_coords, looptri_indices); - compute_attribute_outputs( - mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs); + compute_attribute_outputs(mesh, *pointcloud, bary_coords, looptri_indices, attribute_outputs); } static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); - const GeometryNodeDistributePointsOnFacesMode method = - static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1); + const GeometryNodeDistributePointsOnFacesMode method = GeometryNodeDistributePointsOnFacesMode( + params.node().custom1); const int seed = params.get_input<int>("Seed") * 5383843; const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); @@ -545,6 +525,8 @@ static void node_geo_exec(GeoNodeExecParams params) attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); } + lazy_threading::send_hint(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { point_distribution_calculate( geometry_set, selection_field, method, seed, attribute_outputs, params); diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index 76eeee95239..9b1c13bf563 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_task.hh" #include "DNA_mesh_types.h" @@ -105,18 +106,6 @@ static void copy_data_based_on_pairs(Span<T> data, } } -/* Copy using the map. */ -template<typename T> -static void copy_data_based_on_new_to_old_map(Span<T> data, - MutableSpan<T> r_data, - const Span<int> new_to_old_map) -{ - for (const int i : r_data.index_range()) { - const int old_i = new_to_old_map[i]; - r_data[i] = data[old_i]; - } -} - /** * Transfers the attributes from the original mesh to the new mesh using the following logic: * - If the attribute was on the face domain it is now on the point domain, and this is true @@ -168,7 +157,6 @@ static void transfer_attributes( src_attribute.varray.type()); GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, out_domain, data_type); - if (!dst_attribute) { continue; } @@ -177,20 +165,24 @@ static void transfer_attributes( using T = decltype(dummy); VArraySpan<T> span{src_attribute.varray.typed<T>()}; MutableSpan<T> dst_span = dst_attribute.span.typed<T>(); - if (src_attribute.domain == ATTR_DOMAIN_FACE) { - dst_span.take_front(span.size()).copy_from(span); - if (keep_boundaries) { - copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map); - } - } - else if (src_attribute.domain == ATTR_DOMAIN_POINT) { - copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries); - } - else if (src_attribute.domain == ATTR_DOMAIN_EDGE) { - copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_edges_map); - } - else { - copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map); + switch (src_attribute.domain) { + case ATTR_DOMAIN_POINT: + copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries); + break; + case ATTR_DOMAIN_EDGE: + array_utils::gather(span, new_to_old_edges_map, dst_span); + break; + case ATTR_DOMAIN_FACE: + dst_span.take_front(span.size()).copy_from(span); + if (keep_boundaries) { + copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map); + } + break; + case ATTR_DOMAIN_CORNER: + array_utils::gather(span, new_to_old_face_corners_map, dst_span); + break; + default: + BLI_assert_unreachable(); } }); dst_attribute.finish(); @@ -209,13 +201,18 @@ static void calc_boundaries(const Mesh &mesh, { BLI_assert(r_vertex_types.size() == mesh.totvert); BLI_assert(r_edge_types.size() == mesh.totedge); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_vertex_types.fill(VertexType::Loose); r_edge_types.fill(EdgeType::Loose); /* Add up the number of polys connected to each edge. */ for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly = mesh.mpoly[i]; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]); } } @@ -226,7 +223,7 @@ static void calc_boundaries(const Mesh &mesh, if (edge_type == EdgeType::Loose) { continue; } - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; if (edge_type == EdgeType::Boundary) { r_vertex_types[edge.v1] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v1]); r_vertex_types[edge.v2] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v2]); @@ -241,7 +238,7 @@ static void calc_boundaries(const Mesh &mesh, for (const int i : IndexRange(mesh.totedge)) { const EdgeType edge_type = r_edge_types[i]; if (edge_type == EdgeType::Normal) { - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; if (r_vertex_types[edge.v1] == VertexType::Loose) { r_vertex_types[edge.v1] = VertexType::Normal; } @@ -258,9 +255,12 @@ static void calc_boundaries(const Mesh &mesh, static void create_vertex_poly_map(const Mesh &mesh, MutableSpan<Vector<int>> r_vertex_poly_indices) { - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly = mesh.mpoly[i]; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { r_vertex_poly_indices[loop.v].append(i); } } @@ -321,26 +321,28 @@ static void create_vertex_poly_map(const Mesh &mesh, * - Finally if we are in the normal case we also need to add the last "shared edge" to close the * loop. */ -static bool sort_vertex_polys(const Mesh &mesh, +static bool sort_vertex_polys(const Span<MEdge> edges, + const Span<MPoly> polys, + const Span<MLoop> loops, const int vertex_index, const bool boundary_vertex, const Span<EdgeType> edge_types, - MutableSpan<int> connected_polygons, + MutableSpan<int> connected_polys, MutableSpan<int> r_shared_edges, MutableSpan<int> r_sorted_corners) { - if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { + if (connected_polys.size() <= 2 && (!boundary_vertex || connected_polys.size() == 0)) { return true; } /* For each polygon store the two corners whose edge contains the vertex. */ - Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size()); - for (const int i : connected_polygons.index_range()) { - const MPoly &poly = mesh.mpoly[connected_polygons[i]]; + Array<std::pair<int, int>> poly_vertex_corners(connected_polys.size()); + for (const int i : connected_polys.index_range()) { + const MPoly &poly = polys[connected_polys[i]]; bool first_edge_done = false; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const MLoop &loop = mesh.mloop[loop_index]; - if (mesh.medge[loop.e].v1 == vertex_index || mesh.medge[loop.e].v2 == vertex_index) { + const MLoop &loop = loops[loop_index]; + if (edges[loop.e].v1 == vertex_index || edges[loop.e].v2 == vertex_index) { if (!first_edge_done) { poly_vertex_corners[i].first = loop_index; first_edge_done = true; @@ -359,20 +361,20 @@ static bool sort_vertex_polys(const Mesh &mesh, * the loop to determine the 'average' orientation. */ if (boundary_vertex) { /* Our first polygon needs to be one which has a boundary edge. */ - for (const int i : connected_polygons.index_range()) { - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second]; + for (const int i : connected_polys.index_range()) { + const MLoop &first_loop = loops[poly_vertex_corners[i].first]; + const MLoop &second_loop = loops[poly_vertex_corners[i].second]; if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].first; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) { shared_edge_i = first_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].second; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } @@ -380,20 +382,20 @@ static bool sort_vertex_polys(const Mesh &mesh, if (shared_edge_i == -1) { /* The rotation is inconsistent between the two polygons on the boundary. Just choose one * of the polygon's orientation. */ - for (const int i : connected_polygons.index_range()) { - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second]; + for (const int i : connected_polys.index_range()) { + const MLoop &first_loop = loops[poly_vertex_corners[i].first]; + const MLoop &second_loop = loops[poly_vertex_corners[i].second]; if (edge_types[first_loop.e] == EdgeType::Boundary) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].first; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } if (edge_types[second_loop.e] == EdgeType::Boundary) { shared_edge_i = first_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].second; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } @@ -402,8 +404,8 @@ static bool sort_vertex_polys(const Mesh &mesh, } else { /* Any polygon can be the first. Just need to check the orientation. */ - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[0].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[0].second]; + const MLoop &first_loop = loops[poly_vertex_corners[0].first]; + const MLoop &second_loop = loops[poly_vertex_corners[0].second]; if (first_loop.v == vertex_index) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[0].first; @@ -415,14 +417,14 @@ static bool sort_vertex_polys(const Mesh &mesh, } BLI_assert(shared_edge_i != -1); - for (const int i : IndexRange(connected_polygons.size() - 1)) { + for (const int i : IndexRange(connected_polys.size() - 1)) { r_shared_edges[i] = shared_edge_i; /* Look at the other polys to see if it has this shared edge. */ int j = i + 1; - for (; j < connected_polygons.size(); ++j) { - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[j].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[j].second]; + for (; j < connected_polys.size(); ++j) { + const MLoop &first_loop = loops[poly_vertex_corners[j].first]; + const MLoop &second_loop = loops[poly_vertex_corners[j].second]; if (first_loop.e == shared_edge_i) { r_sorted_corners[i + 1] = poly_vertex_corners[j].first; shared_edge_i = second_loop.e; @@ -434,13 +436,13 @@ static bool sort_vertex_polys(const Mesh &mesh, break; } } - if (j == connected_polygons.size()) { + if (j == connected_polys.size()) { /* The vertex is not manifold because the polygons around the vertex don't form a loop, and * hence can't be sorted. */ return false; } - std::swap(connected_polygons[i + 1], connected_polygons[j]); + std::swap(connected_polys[i + 1], connected_polys[j]); std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); } @@ -455,14 +457,16 @@ static bool sort_vertex_polys(const Mesh &mesh, * Get the edge on the poly that contains the given vertex and is a boundary edge. */ static void boundary_edge_on_poly(const MPoly &poly, - const Mesh &mesh, + const Span<MEdge> edges, + const Span<MLoop> loops, const int vertex_index, const Span<EdgeType> edge_types, int &r_edge) { - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { if (edge_types[loop.e] == EdgeType::Boundary) { - const MEdge &edge = mesh.medge[loop.e]; + const MEdge &edge = edges[loop.e]; if (edge.v1 == vertex_index || edge.v2 == vertex_index) { r_edge = loop.e; return; @@ -476,7 +480,8 @@ static void boundary_edge_on_poly(const MPoly &poly, * orientation of the poly is taken into account. */ static void boundary_edges_on_poly(const MPoly &poly, - const Mesh &mesh, + const Span<MEdge> edges, + const Span<MLoop> loops, const int vertex_index, const Span<EdgeType> edge_types, int &r_edge1, @@ -486,9 +491,10 @@ static void boundary_edges_on_poly(const MPoly &poly, /* This is set to true if the order in which we encounter the two edges is inconsistent with the * orientation of the polygon. */ bool needs_swap = false; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { if (edge_types[loop.e] == EdgeType::Boundary) { - const MEdge &edge = mesh.medge[loop.e]; + const MEdge &edge = edges[loop.e]; if (edge.v1 == vertex_index || edge.v2 == vertex_index) { if (edge1_done) { if (needs_swap) { @@ -510,7 +516,7 @@ static void boundary_edges_on_poly(const MPoly &poly, } } -static void add_edge(const Mesh &mesh, +static void add_edge(const Span<MEdge> src_edges, const int old_edge_i, const int v1, const int v2, @@ -518,7 +524,7 @@ static void add_edge(const Mesh &mesh, Vector<MEdge> &new_edges, Vector<int> &loop_edges) { - MEdge new_edge = MEdge(mesh.medge[old_edge_i]); + MEdge new_edge = src_edges[old_edge_i]; new_edge.v1 = v1; new_edge.v2 = v2; const int new_edge_i = new_edges.size(); @@ -549,14 +555,17 @@ static bool vertex_needs_dissolving(const int vertex, * edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate * edges being created. (See T94144) */ -static void dissolve_redundant_verts(const Mesh &mesh, +static void dissolve_redundant_verts(const Span<MEdge> edges, + const Span<MPoly> polys, + const Span<MLoop> loops, const Span<Vector<int>> vertex_poly_indices, MutableSpan<VertexType> vertex_types, MutableSpan<int> old_to_new_edges_map, Vector<MEdge> &new_edges, Vector<int> &new_to_old_edges_map) { - for (const int vert_i : IndexRange(mesh.totvert)) { + const int vertex_num = vertex_types.size(); + for (const int vert_i : IndexRange(vertex_num)) { if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) { continue; } @@ -564,9 +573,10 @@ static void dissolve_redundant_verts(const Mesh &mesh, const int second_poly_index = vertex_poly_indices[vert_i][1]; const int new_edge_index = new_edges.size(); bool edge_created = false; - const MPoly &poly = mesh.mpoly[first_poly_index]; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { - const MEdge &edge = mesh.medge[loop.e]; + const MPoly &poly = polys[first_poly_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { + const MEdge &edge = edges[loop.e]; const int v1 = edge.v1; const int v2 = edge.v2; bool mark_edge = false; @@ -617,6 +627,10 @@ static void calc_dual_mesh(GeometrySet &geometry_set, const bool keep_boundaries) { const Mesh &mesh_in = *in_component.get_for_read(); + const Span<MVert> src_verts = mesh_in.verts(); + const Span<MEdge> src_edges = mesh_in.edges(); + const Span<MPoly> src_polys = mesh_in.polys(); + const Span<MLoop> src_loops = mesh_in.loops(); Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( @@ -644,14 +658,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set, bool vertex_ok = true; if (vertex_types[i] == VertexType::Normal) { Array<int> shared_edges(loop_indices.size()); - vertex_ok = sort_vertex_polys( - mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners); + vertex_ok = sort_vertex_polys(src_edges, + src_polys, + src_loops, + i, + false, + edge_types, + loop_indices, + shared_edges, + sorted_corners); vertex_shared_edges[i] = std::move(shared_edges); } else { Array<int> shared_edges(loop_indices.size() - 1); - vertex_ok = sort_vertex_polys( - mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners); + vertex_ok = sort_vertex_polys(src_edges, + src_polys, + src_loops, + i, + true, + edge_types, + loop_indices, + shared_edges, + sorted_corners); vertex_shared_edges[i] = std::move(shared_edges); } if (!vertex_ok) { @@ -666,9 +694,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, Vector<float3> vertex_positions(mesh_in.totpoly); for (const int i : IndexRange(mesh_in.totpoly)) { - const MPoly poly = mesh_in.mpoly[i]; + const MPoly &poly = src_polys[i]; BKE_mesh_calc_poly_center( - &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]); + &poly, &src_loops[poly.loopstart], src_verts.data(), vertex_positions[i]); } Array<int> boundary_edge_midpoint_index; @@ -679,8 +707,8 @@ static void calc_dual_mesh(GeometrySet &geometry_set, for (const int i : IndexRange(mesh_in.totedge)) { if (edge_types[i] == EdgeType::Boundary) { float3 mid; - const MEdge &edge = mesh_in.medge[i]; - mid_v3_v3v3(mid, mesh_in.mvert[edge.v1].co, mesh_in.mvert[edge.v2].co); + const MEdge &edge = src_edges[i]; + mid_v3_v3v3(mid, src_verts[edge.v1].co, src_verts[edge.v2].co); boundary_edge_midpoint_index[i] = vertex_positions.size(); vertex_positions.append(mid); } @@ -706,7 +734,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, /* This is necessary to prevent duplicate edges from being created, but will likely not do * anything for most meshes. */ - dissolve_redundant_verts(mesh_in, + dissolve_redundant_verts(src_edges, + src_polys, + src_loops, vertex_poly_indices, vertex_types, old_to_new_edges_map, @@ -734,7 +764,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, const int old_edge_i = shared_edges[i]; if (old_to_new_edges_map[old_edge_i] == -1) { /* This edge has not been created yet. */ - MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]); + MEdge new_edge = src_edges[old_edge_i]; new_edge.v1 = loop_indices[i]; new_edge.v2 = loop_indices[(i + 1) % loop_indices.size()]; new_to_old_edges_map.append(old_edge_i); @@ -776,7 +806,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, const int old_edge_i = shared_edges[i]; if (old_to_new_edges_map[old_edge_i] == -1) { /* This edge has not been created yet. */ - MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]); + MEdge new_edge = src_edges[old_edge_i]; new_edge.v1 = loop_indices[i]; new_edge.v2 = loop_indices[i + 1]; new_to_old_edges_map.append(old_edge_i); @@ -795,13 +825,15 @@ static void calc_dual_mesh(GeometrySet &geometry_set, int edge2; if (loop_indices.size() >= 2) { /* The first boundary edge is at the end of the chain of polygons. */ - boundary_edge_on_poly(mesh_in.mpoly[loop_indices.last()], mesh_in, i, edge_types, edge1); - boundary_edge_on_poly(mesh_in.mpoly[loop_indices.first()], mesh_in, i, edge_types, edge2); + boundary_edge_on_poly( + src_polys[loop_indices.last()], src_edges, src_loops, i, edge_types, edge1); + boundary_edge_on_poly( + src_polys[loop_indices.first()], src_edges, src_loops, i, edge_types, edge2); } else { /* If there is only one polygon both edges are in that polygon. */ boundary_edges_on_poly( - mesh_in.mpoly[loop_indices[0]], mesh_in, i, edge_types, edge1, edge2); + src_polys[loop_indices[0]], src_edges, src_loops, i, edge_types, edge1, edge2); } const int last_face_center = loop_indices.last(); @@ -809,7 +841,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_face_corners_map.append(sorted_corners.last()); const int first_midpoint = loop_indices.last(); if (old_to_new_edges_map[edge1] == -1) { - add_edge(mesh_in, + add_edge(src_edges, edge1, last_face_center, first_midpoint, @@ -827,9 +859,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_face_corners_map.append(sorted_corners.first()); boundary_vertex_to_relevant_face_map.append( std::pair(loop_indices.last(), last_face_center)); - vertex_positions.append(mesh_in.mvert[i].co); + vertex_positions.append(src_verts[i].co); const int boundary_vertex = loop_indices.last(); - add_edge(mesh_in, + add_edge(src_edges, edge1, first_midpoint, boundary_vertex, @@ -840,7 +872,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, loop_indices.append(boundary_edge_midpoint_index[edge2]); new_to_old_face_corners_map.append(sorted_corners.first()); const int second_midpoint = loop_indices.last(); - add_edge(mesh_in, + add_edge(src_edges, edge2, boundary_vertex, second_midpoint, @@ -850,7 +882,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, if (old_to_new_edges_map[edge2] == -1) { const int first_face_center = loop_indices.first(); - add_edge(mesh_in, + add_edge(src_edges, edge2, second_midpoint, first_face_center, @@ -878,23 +910,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_edges_map, new_to_old_face_corners_map, boundary_vertex_to_relevant_face_map, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out)); + mesh_in.attributes(), + mesh_out->attributes_for_write()); + + MutableSpan<MVert> dst_verts = mesh_out->verts_for_write(); + MutableSpan<MEdge> dst_edges = mesh_out->edges_for_write(); + MutableSpan<MPoly> dst_polys = mesh_out->polys_for_write(); + MutableSpan<MLoop> dst_loops = mesh_out->loops_for_write(); int loop_start = 0; for (const int i : IndexRange(mesh_out->totpoly)) { - mesh_out->mpoly[i].loopstart = loop_start; - mesh_out->mpoly[i].totloop = loop_lengths[i]; + dst_polys[i].loopstart = loop_start; + dst_polys[i].totloop = loop_lengths[i]; loop_start += loop_lengths[i]; } for (const int i : IndexRange(mesh_out->totloop)) { - mesh_out->mloop[i].v = loops[i]; - mesh_out->mloop[i].e = loop_edges[i]; + dst_loops[i].v = loops[i]; + dst_loops[i].e = loop_edges[i]; } for (const int i : IndexRange(mesh_out->totvert)) { - copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]); + copy_v3_v3(dst_verts[i].co, vertex_positions[i]); } - memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size()); + dst_edges.copy_from(new_edges); geometry_set.replace_mesh(mesh_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index c6b0fb4c068..486f900aca5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_map.hh" #include "BLI_noise.hh" #include "BLI_span.hh" @@ -11,6 +12,7 @@ #include "BKE_attribute_math.hh" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -40,14 +42,14 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The indices of the duplicates for each element")); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__); data->domain = ATTR_DOMAIN_POINT; node->storage = data; } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } @@ -104,16 +106,6 @@ static void threaded_slice_fill(Span<int> offsets, }); } -template<typename T> -static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst) -{ - threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - dst[i] = src[mapping[i]]; - } - }); -} - static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst) { for (const int i : src.index_range()) { @@ -334,11 +326,10 @@ static void duplicate_curves(GeometrySet &geometry_set, geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE}); GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &curves_id = *src_component.get_for_read(); + const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; FieldEvaluator evaluator{field_context, curves.curves_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -440,17 +431,17 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set, MutableSpan<T> dst = dst_attribute.span.typed<T>(); switch (out_domain) { - case ATTR_DOMAIN_FACE: - threaded_slice_fill<T>(offsets, selection, src, dst); + case ATTR_DOMAIN_POINT: + array_utils::gather(src, vert_mapping, dst); break; case ATTR_DOMAIN_EDGE: - threaded_mapped_copy<T>(edge_mapping, src, dst); + array_utils::gather(src, edge_mapping, dst); break; - case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(vert_mapping, src, dst); + case ATTR_DOMAIN_FACE: + threaded_slice_fill<T>(offsets, selection, src, dst); break; case ATTR_DOMAIN_CORNER: - threaded_mapped_copy<T>(loop_mapping, src, dst); + array_utils::gather(src, loop_mapping, dst); break; default: break; @@ -487,7 +478,7 @@ static void copy_stable_id_faces(const Mesh &mesh, VArraySpan<int> src{src_attribute.varray.typed<int>()}; MutableSpan<int> dst = dst_attribute.span.typed<int>(); - Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + const Span<MPoly> polys = mesh.polys(); int loop_index = 0; for (const int i_poly : selection.index_range()) { const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); @@ -522,14 +513,13 @@ static void duplicate_faces(GeometrySet &geometry_set, } geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *src_component.get_for_read(); - Span<MVert> verts(mesh.mvert, mesh.totvert); - Span<MEdge> edges(mesh.medge, mesh.totedge); - Span<MPoly> polys(mesh.mpoly, mesh.totpoly); - Span<MLoop> loops(mesh.mloop, mesh.totloop); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator evaluator(field_context, polys.size()); evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -549,10 +539,10 @@ static void duplicate_faces(GeometrySet &geometry_set, offsets[selection.size()] = total_polys; Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys); - MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); - MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); - MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop); - MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly); + MutableSpan<MVert> new_verts = new_mesh->verts_for_write(); + MutableSpan<MEdge> new_edges = new_mesh->edges_for_write(); + MutableSpan<MPoly> new_polys = new_mesh->polys_for_write(); + MutableSpan<MLoop> new_loops = new_mesh->loops_for_write(); Array<int> vert_mapping(new_verts.size()); Array<int> edge_mapping(new_edges.size()); @@ -565,8 +555,8 @@ static void duplicate_faces(GeometrySet &geometry_set, const MPoly &source = polys[selection[i_selection]]; for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) { - new_poly[poly_index] = source; - new_poly[poly_index].loopstart = loop_index; + new_polys[poly_index] = source; + new_polys[poly_index].loopstart = loop_index; for (const int i_loops : IndexRange(source.totloop)) { const MLoop ¤t_loop = loops[source.loopstart + i_loops]; loop_mapping[loop_index] = source.loopstart + i_loops; @@ -579,7 +569,7 @@ static void duplicate_faces(GeometrySet &geometry_set, new_edges[loop_index].v2 = loop_index + 1; } else { - new_edges[loop_index].v2 = new_poly[poly_index].loopstart; + new_edges[loop_index].v2 = new_polys[poly_index].loopstart; } new_loops[loop_index].v = loop_index; new_loops[loop_index].e = loop_index; @@ -595,22 +585,15 @@ static void duplicate_faces(GeometrySet &geometry_set, loop_mapping, offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_faces(mesh, - selection, - offsets, - vert_mapping, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_faces( + mesh, selection, offsets, vert_mapping, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), - ATTR_DOMAIN_FACE, - selection, - attribute_outputs, - offsets); + create_duplicate_index_attribute( + new_mesh->attributes_for_write(), ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets); } geometry_set.replace_mesh(new_mesh); @@ -661,7 +644,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set, threaded_slice_fill<T>(offsets, selection, src, dst); break; case ATTR_DOMAIN_POINT: - threaded_mapped_copy<T>(point_mapping, src, dst); + array_utils::gather(src, point_mapping, dst); break; default: break; @@ -691,7 +674,7 @@ static void copy_stable_id_edges(const Mesh &mesh, return; } - Span<MEdge> edges(mesh.medge, mesh.totedge); + const Span<MEdge> edges = mesh.edges(); VArraySpan<int> src{src_attribute.varray.typed<int>()}; MutableSpan<int> dst = dst_attribute.span.typed<int>(); @@ -724,12 +707,10 @@ static void duplicate_edges(GeometrySet &geometry_set, geometry_set.remove_geometry_during_modify(); return; }; - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *src_component.get_for_read(); - Span<MVert> verts(mesh.mvert, mesh.totvert); - Span<MEdge> edges(mesh.medge, mesh.totedge); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + const Span<MEdge> edges = mesh.edges(); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator evaluator{field_context, edges.size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -740,8 +721,7 @@ static void duplicate_edges(GeometrySet &geometry_set, Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts); Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0); - MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); - MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + MutableSpan<MEdge> new_edges = new_mesh->edges_for_write(); Array<int> vert_orig_indices(edge_offsets.last() * 2); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { @@ -774,17 +754,14 @@ static void duplicate_edges(GeometrySet &geometry_set, vert_orig_indices, edge_offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_edges(mesh, - selection, - edge_offsets, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_edges( + mesh, selection, edge_offsets, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), + create_duplicate_index_attribute(new_mesh->attributes_for_write(), ATTR_DOMAIN_EDGE, selection, attribute_outputs, @@ -805,14 +782,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const Field<bool> &selection_field, const IndexAttributes &attribute_outputs) { - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &src_curves_id = *src_component.get_for_read(); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); if (src_curves.points_num() == 0) { return; } - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT}; FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -845,7 +821,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; - GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id); + GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id); if (!src_attribute) { continue; } @@ -909,11 +885,10 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, const Field<bool> &selection_field, const IndexAttributes &attribute_outputs) { - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *geometry_set.get_mesh_for_read(); - Span<MVert> src_verts(mesh.mvert, mesh.totvert); + const Span<MVert> src_verts = mesh.verts(); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_POINT}; FieldEvaluator evaluator{field_context, src_verts.size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -924,7 +899,7 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, Array<int> offsets = accumulate_counts_to_offsets(selection, counts); Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0); - MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MVert> dst_verts = new_mesh->verts_for_write(); threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts); @@ -933,14 +908,13 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_point( - offsets, bke::mesh_attributes(mesh), bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_point(offsets, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), + create_duplicate_index_attribute(new_mesh->attributes_for_write(), ATTR_DOMAIN_POINT, selection, attribute_outputs, @@ -961,12 +935,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, const Field<bool> &selection_field, const IndexAttributes &attribute_outputs) { - const PointCloudComponent &src_points = - *geometry_set.get_component_for_read<PointCloudComponent>(); - const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const PointCloud &src_points = *geometry_set.get_pointcloud_for_read(); - GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, point_num}; + bke::PointCloudFieldContext field_context{src_points}; + FieldEvaluator evaluator{field_context, src_points.totpoint}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -982,14 +954,13 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, - *src_points.attributes(), - bke::pointcloud_attributes_for_write(*pointcloud)); + src_points.attributes(), + pointcloud->attributes_for_write()); - copy_stable_id_point( - offsets, *src_points.attributes(), bke::pointcloud_attributes_for_write(*pointcloud)); + copy_stable_id_point(offsets, src_points.attributes(), pointcloud->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::pointcloud_attributes_for_write(*pointcloud), + create_duplicate_index_attribute(pointcloud->attributes_for_write(), ATTR_DOMAIN_POINT, selection, attribute_outputs, @@ -1052,10 +1023,9 @@ static void duplicate_instances(GeometrySet &geometry_set, return; } - const InstancesComponent &src_instances = - *geometry_set.get_component_for_read<InstancesComponent>(); + const bke::Instances &src_instances = *geometry_set.get_instances_for_read(); - GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + bke::InstancesFieldContext field_context{src_instances}; FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -1069,20 +1039,20 @@ static void duplicate_instances(GeometrySet &geometry_set, return; } - GeometrySet dst_geometry; - InstancesComponent &dst_instances = dst_geometry.get_component_for_write<InstancesComponent>(); - dst_instances.resize(offsets.last()); + std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>(); + + dst_instances->resize(offsets.last()); for (const int i_selection : selection.index_range()) { const IndexRange range = range_for_offsets_index(offsets, i_selection); if (range.size() == 0) { continue; } - const int old_handle = src_instances.instance_reference_handles()[i_selection]; - const InstanceReference reference = src_instances.references()[old_handle]; - const int new_handle = dst_instances.add_reference(reference); - const float4x4 transform = src_instances.instance_transforms()[i_selection]; - dst_instances.instance_transforms().slice(range).fill(transform); - dst_instances.instance_reference_handles().slice(range).fill(new_handle); + const int old_handle = src_instances.reference_handles()[i_selection]; + const bke::InstanceReference reference = src_instances.references()[old_handle]; + const int new_handle = dst_instances->add_reference(reference); + const float4x4 transform = src_instances.transforms()[i_selection]; + dst_instances->transforms().slice(range).fill(transform); + dst_instances->reference_handles().slice(range).fill(new_handle); } copy_attributes_without_id(geometry_set, @@ -1090,18 +1060,18 @@ static void duplicate_instances(GeometrySet &geometry_set, ATTR_DOMAIN_INSTANCE, offsets, selection, - *src_instances.attributes(), - *dst_instances.attributes_for_write()); + src_instances.attributes(), + dst_instances->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(*dst_instances.attributes_for_write(), + create_duplicate_index_attribute(dst_instances->attributes_for_write(), ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets); } - geometry_set = std::move(dst_geometry); + geometry_set = GeometrySet::create_with_instances(dst_instances.release()); } /** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc new file mode 100644 index 00000000000..ba09acf0bf0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" + +#include "DNA_mesh_types.h" + +#include "GEO_mesh_to_curve.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_edge_paths_to_curves_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curves")); +} + +static Curves *edge_paths_to_curves_convert(const Mesh &mesh, + const IndexMask start_verts_mask, + const Span<int> next_indices) +{ + Vector<int> vert_indices; + Vector<int> curve_offsets; + Array<bool> visited(mesh.totvert, false); + for (const int first_vert : start_verts_mask) { + const int second_vert = next_indices[first_vert]; + if (first_vert == second_vert) { + continue; + } + if (second_vert < 0 || second_vert >= mesh.totvert) { + continue; + } + + curve_offsets.append(vert_indices.size()); + + /* Iterate through path defined by #next_indices. */ + int current_vert = first_vert; + while (!visited[current_vert]) { + visited[current_vert] = true; + vert_indices.append(current_vert); + const int next_vert = next_indices[current_vert]; + if (next_vert < 0 || next_vert >= mesh.totvert) { + break; + } + current_vert = next_vert; + } + + /* Reset visited status. */ + const int points_in_curve_num = vert_indices.size() - curve_offsets.last(); + for (const int vert_in_curve : vert_indices.as_span().take_back(points_in_curve_num)) { + visited[vert_in_curve] = false; + } + } + + if (vert_indices.is_empty()) { + return nullptr; + } + Curves *curves_id = bke::curves_new_nomain( + geometry::create_curve_from_vert_indices(mesh, vert_indices, curve_offsets, IndexRange(0))); + return curves_id; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh == nullptr) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, mesh->totvert}; + evaluator.add(params.get_input<Field<int>>("Next Vertex Index")); + evaluator.add(params.get_input<Field<bool>>("Start Vertices")); + evaluator.evaluate(); + const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0); + IndexMask start_verts = evaluator.get_evaluated_as_mask(1); + + if (start_verts.is_empty()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + geometry_set.replace_curves(edge_paths_to_curves_convert(*mesh, start_verts, next_vert)); + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + }); + + params.set_output("Curves", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_edge_paths_to_curves_cc + +void register_node_type_geo_edge_paths_to_curves() +{ + namespace file_ns = blender::nodes::node_geo_edge_paths_to_curves_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_EDGE_PATHS_TO_CURVES, "Edge Paths to Curves", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc new file mode 100644 index 00000000000..f0bd01a012b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +#include <set> + +namespace blender::nodes::node_geo_edge_paths_to_selection_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field(); + b.add_output<decl::Bool>(N_("Selection")).field_source(); +} + +static void edge_paths_to_selection(const Mesh &src_mesh, + const IndexMask start_selection, + const Span<int> next_indices, + MutableSpan<bool> r_selection) +{ + const Span<MEdge> edges = src_mesh.edges(); + + Array<bool> selection(src_mesh.totvert, false); + + for (const int start_vert : start_selection) { + selection[start_vert] = true; + } + + for (const int start_i : start_selection) { + int iter = start_i; + while (iter != next_indices[iter] && !selection[next_indices[iter]]) { + if (next_indices[iter] < 0 || next_indices[iter] >= src_mesh.totvert) { + break; + } + selection[next_indices[iter]] = true; + iter = next_indices[iter]; + } + } + + for (const int i : edges.index_range()) { + const MEdge &edge = edges[i]; + if ((selection[edge.v1] && selection[edge.v2]) && + (edge.v1 == next_indices[edge.v2] || edge.v2 == next_indices[edge.v1])) { + r_selection[i] = true; + } + } +} + +class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { + private: + Field<bool> start_vertices_; + Field<int> next_vertex_; + + public: + PathToEdgeSelectionFieldInput(Field<bool> start_verts, Field<int> next_vertex) + : bke::MeshFieldInput(CPPType::get<bool>(), "Edge Selection"), + start_vertices_(start_verts), + next_vertex_(next_vertex) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, mesh.totvert}; + evaluator.add(next_vertex_); + evaluator.add(start_vertices_); + evaluator.evaluate(); + const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0); + const IndexMask start_verts = evaluator.get_evaluated_as_mask(1); + + if (start_verts.is_empty()) { + return {}; + } + + Array<bool> selection(mesh.totedge, false); + MutableSpan<bool> selection_span = selection.as_mutable_span(); + + edge_paths_to_selection(mesh, start_verts, next_vert, selection_span); + + return mesh.attributes().adapt_domain<bool>( + VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain); + } + + uint64_t hash() const override + { + return get_default_hash_2(start_vertices_, next_vertex_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const PathToEdgeSelectionFieldInput *other_field = + dynamic_cast<const PathToEdgeSelectionFieldInput *>(&other)) { + return other_field->start_vertices_ == start_vertices_ && + other_field->next_vertex_ == next_vertex_; + } + return false; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> start_verts = params.extract_input<Field<bool>>("Start Vertices"); + Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index"); + Field<bool> selection_field{ + std::make_shared<PathToEdgeSelectionFieldInput>(start_verts, next_vertex)}; + params.set_output("Selection", std::move(selection_field)); +} + +} // namespace blender::nodes::node_geo_edge_paths_to_selection_cc + +void register_node_type_geo_edge_paths_to_selection() +{ + namespace file_ns = blender::nodes::node_geo_edge_paths_to_selection_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_EDGE_PATHS_TO_SELECTION, "Edge Paths to Selection", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_size(&ntype, 150, 100, 300); + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 84acab47661..0b4d5bd53f3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" + #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -51,19 +53,18 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { - return; - } + if (const Mesh *mesh = geometry_set.get_mesh_for_write()) { - const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; - const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + bke::MeshFieldContext field_context{*mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator selection_evaluator{field_context, mesh->totedge}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection)); + Mesh *result = mesh_edge_split(*mesh, selection); + + geometry_set.replace_mesh(result); + } }); params.set_output("Mesh", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index acf85e74353..151ba3e59cc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_disjoint_set.hh" #include "BLI_task.hh" #include "BLI_vector_set.hh" @@ -24,7 +25,10 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); - b.add_input<decl::Vector>(N_("Offset")).subtype(PROP_TRANSLATION).implicit_field().hide_value(); + b.add_input<decl::Vector>(N_("Offset")) + .subtype(PROP_TRANSLATION) + .implicit_field(implicit_field_inputs::normal) + .hide_value(); b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).supports_field(); b.add_input<decl::Bool>(N_("Individual")).default_value(true); b.add_output<decl::Geometry>("Mesh"); @@ -32,14 +36,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Side")).field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryExtrudeMesh *data = MEM_cnew<NodeGeometryExtrudeMesh>(__func__); data->mode = GEO_NODE_EXTRUDE_MESH_FACES; @@ -49,9 +53,9 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryExtrudeMesh &storage = node_storage(*node); - const GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode); + const GeometryNodeExtrudeMeshMode mode = GeometryNodeExtrudeMeshMode(storage.mode); - bNodeSocket *individual_socket = (bNodeSocket *)node->inputs.last; + bNodeSocket *individual_socket = static_cast<bNodeSocket *>(node->inputs.last); nodeSetSocketAvailability(ntree, individual_socket, mode == GEO_NODE_EXTRUDE_MESH_FACES); } @@ -61,15 +65,15 @@ struct AttributeOutputs { StrongAnonymousAttributeID side_id; }; -static void save_selection_as_attribute(MeshComponent &component, +static void save_selection_as_attribute(Mesh &mesh, const AnonymousAttributeID *id, const eAttrDomain domain, const IndexMask selection) { - BLI_assert(!component.attributes()->contains(id)); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + BLI_assert(!attributes.contains(id)); - SpanAttributeWriter<bool> attribute = - component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain); + SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain); /* Rely on the new attribute being zeroed by default. */ BLI_assert(!attribute.span.as_span().contains(true)); @@ -83,31 +87,6 @@ static void save_selection_as_attribute(MeshComponent &component, attribute.finish(); } -static MutableSpan<MVert> mesh_verts(Mesh &mesh) -{ - return {mesh.mvert, mesh.totvert}; -} -static MutableSpan<MEdge> mesh_edges(Mesh &mesh) -{ - return {mesh.medge, mesh.totedge}; -} -static Span<MPoly> mesh_polys(const Mesh &mesh) -{ - return {mesh.mpoly, mesh.totpoly}; -} -static MutableSpan<MPoly> mesh_polys(Mesh &mesh) -{ - return {mesh.mpoly, mesh.totpoly}; -} -static Span<MLoop> mesh_loops(const Mesh &mesh) -{ - return {mesh.mloop, mesh.totloop}; -} -static MutableSpan<MLoop> mesh_loops(Mesh &mesh) -{ - return {mesh.mloop, mesh.totloop}; -} - /** * \note Some areas in this file rely on the new sections of attributes from #CustomData_realloc * to be zeroed. @@ -119,30 +98,25 @@ static void expand_mesh(Mesh &mesh, const int loop_expand) { if (vert_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert); + const int old_verts_num = mesh.totvert; mesh.totvert += vert_expand; - CustomData_realloc(&mesh.vdata, mesh.totvert); - } - else { - /* Even when the number of vertices is not changed, the mesh can still be deformed. */ - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert); + CustomData_realloc(&mesh.vdata, old_verts_num, mesh.totvert); } if (edge_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge); + const int old_edges_num = mesh.totedge; mesh.totedge += edge_expand; - CustomData_realloc(&mesh.edata, mesh.totedge); + CustomData_realloc(&mesh.edata, old_edges_num, mesh.totedge); } if (poly_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly); + const int old_polys_num = mesh.totpoly; mesh.totpoly += poly_expand; - CustomData_realloc(&mesh.pdata, mesh.totpoly); + CustomData_realloc(&mesh.pdata, old_polys_num, mesh.totpoly); } if (loop_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop); + const int old_loops_num = mesh.totloop; mesh.totloop += loop_expand; - CustomData_realloc(&mesh.ldata, mesh.totloop); + CustomData_realloc(&mesh.ldata, old_loops_num, mesh.totloop); } - BKE_mesh_update_customdata_pointers(&mesh, false); } static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain) @@ -162,9 +136,12 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain) } } +/** + * \note The result may be an empty span. + */ static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain) { - const bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); + const bke::AttributeAccessor attributes = mesh.attributes(); CustomData &custom_data = get_customdata(mesh, domain); if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) { return {orig_indices, attributes.domain_size(domain)}; @@ -199,24 +176,6 @@ static MPoly new_poly(const int loopstart, const int totloop) return poly; } -template<typename T> void copy_with_indices(MutableSpan<T> dst, Span<T> src, Span<int> indices) -{ - BLI_assert(dst.size() == indices.size()); - for (const int i : dst.index_range()) { - dst[i] = src[indices[i]]; - } -} - -template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask) -{ - BLI_assert(dst.size() == mask.size()); - threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) { - for (const int i : range) { - dst[i] = src[mask[i]]; - } - }); -} - /** * \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every * result point. @@ -225,7 +184,7 @@ template<typename T, typename GetMixIndicesFn> void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn) { threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) { - attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)}; + attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)}; for (const int i_dst : IndexRange(range.size())) { for (const int i_src : get_mix_indices_fn(range[i_dst])) { mixer.mix_in(i_dst, src[i_src]); @@ -247,16 +206,15 @@ static Array<Vector<int>> create_vert_to_edge_map(const int vert_size, return vert_to_edge_map; } -static void extrude_mesh_vertices(MeshComponent &component, +static void extrude_mesh_vertices(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; const int orig_edge_size = mesh.totedge; - GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add(offset_field); evaluator.set_selection(selection_field); @@ -265,48 +223,49 @@ static void extrude_mesh_vertices(MeshComponent &component, const VArray<float3> offsets = evaluator.get_evaluated<float3>(0); /* This allows parallelizing attribute mixing for new edges. */ - Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh)); + Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges()); expand_mesh(mesh, selection.size(), selection.size(), 0, 0); const IndexRange new_vert_range{orig_vert_size, selection.size()}; const IndexRange new_edge_range{orig_edge_size, selection.size()}; - MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); - MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range); + MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range); + MutableSpan<MEdge> new_edges = mesh.edges_for_write().slice(new_edge_range); for (const int i_selection : selection.index_range()) { new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]); } - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); - attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - MutableSpan<T> data = attribute.span.typed<T>(); - switch (attribute.domain) { - case ATTR_DOMAIN_POINT: { - /* New vertices copy the attribute values from their source vertex. */ - copy_with_mask(data.slice(new_vert_range), data.as_span(), selection); - break; - } - case ATTR_DOMAIN_EDGE: { + switch (attribute.domain) { + case ATTR_DOMAIN_POINT: + /* New vertices copy the attribute values from their source vertex. */ + array_utils::gather(attribute.span, selection, attribute.span.slice(new_vert_range)); + break; + case ATTR_DOMAIN_EDGE: + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> data = attribute.span.typed<T>(); /* New edge values are mixed from of all the edges connected to the source vertex. */ copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) { return vert_to_edge_map[selection[i]].as_span(); }); - break; - } - default: - BLI_assert_unreachable(); - } - }); + }); + break; + default: + BLI_assert_unreachable(); + } attribute.finish(); return true; @@ -324,13 +283,16 @@ static void extrude_mesh_vertices(MeshComponent &component, MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT); vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE); + MutableSpan<int> new_edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE); + new_edge_orig_indices.slice(new_edge_range).fill(ORIGINDEX_NONE); + if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -338,8 +300,8 @@ static void extrude_mesh_vertices(MeshComponent &component, static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh) { - Span<MPoly> polys = mesh_polys(mesh); - Span<MLoop> loops = mesh_loops(mesh); + Span<MPoly> polys = mesh.polys(); + Span<MLoop> loops = mesh.loops(); Array<Vector<int, 2>> polys_of_edge(mesh.totedge); for (const int i_poly : polys.index_range()) { @@ -397,29 +359,29 @@ template<typename T> static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices) { static_assert(is_same_any_v<T, int, int64_t>); + const Span<MEdge> edges = mesh.edges(); VectorSet<int> vert_indices; vert_indices.reserve(edge_indices.size()); for (const T i_edge : edge_indices) { - const MEdge &edge = mesh.medge[i_edge]; + const MEdge &edge = edges[i_edge]; vert_indices.add(edge.v1); vert_indices.add(edge.v2); } return vert_indices; } -static void extrude_mesh_edges(MeshComponent &component, +static void extrude_mesh_edges(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; - Span<MEdge> orig_edges = mesh_edges(mesh); - Span<MPoly> orig_polys = mesh_polys(mesh); + Span<MEdge> orig_edges = mesh.edges(); + Span<MPoly> orig_polys = mesh.polys(); const int orig_loop_size = mesh.totloop; - GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; edge_evaluator.set_selection(selection_field); edge_evaluator.add(offset_field); @@ -437,7 +399,7 @@ static void extrude_mesh_edges(MeshComponent &component, Array<float3> vert_offsets; if (!edge_offsets.is_single()) { vert_offsets.reinitialize(orig_vert_size); - attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets); for (const int i_edge : edge_selection) { const MEdge &edge = orig_edges[i_edge]; const float3 offset = edge_offsets[i_edge]; @@ -465,12 +427,12 @@ static void extrude_mesh_edges(MeshComponent &component, new_poly_range.size(), new_loop_range.size()); - MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); - MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range); - MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range); - MutableSpan<MPoly> polys = mesh_polys(mesh); + MutableSpan<MEdge> edges = mesh.edges_for_write(); + MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); + MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range); + MutableSpan<MPoly> polys = mesh.polys_for_write(); MutableSpan<MPoly> new_polys = polys.slice(new_poly_range); - MutableSpan<MLoop> loops = mesh_loops(mesh); + MutableSpan<MLoop> loops = mesh.loops_for_write(); MutableSpan<MLoop> new_loops = loops.slice(new_loop_range); for (const int i : connect_edges.index_range()) { @@ -478,7 +440,7 @@ static void extrude_mesh_edges(MeshComponent &component, } for (const int i : duplicate_edges.index_range()) { - const MEdge &orig_edge = mesh.medge[edge_selection[i]]; + const MEdge &orig_edge = edges[edge_selection[i]]; const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1); const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2); duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]); @@ -525,9 +487,12 @@ static void extrude_mesh_edges(MeshComponent &component, const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( new_vert_range.size(), duplicate_edges, orig_vert_size); - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); if (!attribute) { @@ -540,13 +505,14 @@ static void extrude_mesh_edges(MeshComponent &component, switch (attribute.domain) { case ATTR_DOMAIN_POINT: { /* New vertices copy the attribute values from their source vertex. */ - copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices); + array_utils::gather( + data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range)); break; } case ATTR_DOMAIN_EDGE: { /* Edges parallel to original edges copy the edge attributes from the original edges. */ MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range); - copy_with_mask(duplicate_data, data.as_span(), edge_selection); + array_utils::gather(data.as_span(), edge_selection, duplicate_data); /* Edges connected to original vertices mix values of selected connected edges. */ MutableSpan<T> connect_data = data.slice(connect_edge_range); @@ -583,7 +549,7 @@ static void extrude_mesh_edges(MeshComponent &component, /* Both corners on each vertical edge of the side polygon get the same value, * so there are only two unique values to mix. */ Array<T> side_poly_corner_data(2); - attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data}; + attribute_math::DefaultPropagationMixer<T> mixer{side_poly_corner_data}; const MEdge &duplicate_edge = duplicate_edges[i_edge_selection]; const int new_vert_1 = duplicate_edge.v1; @@ -633,6 +599,7 @@ static void extrude_mesh_edges(MeshComponent &component, return true; }); + MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range); if (edge_offsets.is_single()) { const float3 offset = edge_offsets.get_internal_single(); threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { @@ -656,13 +623,16 @@ static void extrude_mesh_edges(MeshComponent &component, edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE); edge_orig_indices.slice(duplicate_edge_range).fill(ORIGINDEX_NONE); + MutableSpan<int> poly_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_FACE); + poly_orig_indices.slice(new_poly_range).fill(ORIGINDEX_NONE); + if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -672,18 +642,17 @@ static void extrude_mesh_edges(MeshComponent &component, * Edges connected to one selected face are on the boundary of a region and will be duplicated into * a "side face". Edges inside a region will be duplicated to leave any original faces unchanged. */ -static void extrude_mesh_face_regions(MeshComponent &component, +static void extrude_mesh_face_regions(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; - Span<MEdge> orig_edges = mesh_edges(mesh); - Span<MPoly> orig_polys = mesh_polys(mesh); - Span<MLoop> orig_loops = mesh_loops(mesh); + Span<MEdge> orig_edges = mesh.edges(); + Span<MPoly> orig_polys = mesh.polys(); + Span<MLoop> orig_loops = mesh.loops(); - GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator poly_evaluator{poly_context, mesh.totpoly}; poly_evaluator.set_selection(selection_field); poly_evaluator.add(offset_field); @@ -705,7 +674,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, Array<float3> vert_offsets; if (!poly_offsets.is_single()) { vert_offsets.reinitialize(orig_vert_size); - attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets); for (const int i_poly : poly_selection) { const MPoly &poly = orig_polys[i_poly]; const float3 offset = poly_offsets[i_poly]; @@ -784,7 +753,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, /* The vertices attached to duplicate inner edges also have to be duplicated. */ for (const int i_edge : new_inner_edge_indices) { - const MEdge &edge = mesh.medge[i_edge]; + const MEdge &edge = orig_edges[i_edge]; new_vert_indices.add(edge.v1); new_vert_indices.add(edge.v2); } @@ -808,13 +777,13 @@ static void extrude_mesh_face_regions(MeshComponent &component, side_poly_range.size(), side_loop_range.size()); - MutableSpan<MEdge> edges = mesh_edges(mesh); + MutableSpan<MEdge> edges = mesh.edges_for_write(); MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range); MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range); - MutableSpan<MPoly> polys = mesh_polys(mesh); + MutableSpan<MPoly> polys = mesh.polys_for_write(); MutableSpan<MPoly> new_polys = polys.slice(side_poly_range); - MutableSpan<MLoop> loops = mesh_loops(mesh); + MutableSpan<MLoop> loops = mesh.loops_for_write(); MutableSpan<MLoop> new_loops = loops.slice(side_loop_range); /* Initialize the edges that form the sides of the extrusion. */ @@ -905,9 +874,12 @@ static void extrude_mesh_face_regions(MeshComponent &component, const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( new_vert_range.size(), boundary_edges, orig_vert_size); - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); if (!attribute) { @@ -920,17 +892,18 @@ static void extrude_mesh_face_regions(MeshComponent &component, switch (attribute.domain) { case ATTR_DOMAIN_POINT: { /* New vertices copy the attributes from their original vertices. */ - copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices); + array_utils::gather( + data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range)); break; } case ATTR_DOMAIN_EDGE: { /* Edges parallel to original edges copy the edge attributes from the original edges. */ MutableSpan<T> boundary_data = data.slice(boundary_edge_range); - copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices); + array_utils::gather(data.as_span(), boundary_edge_indices.as_span(), boundary_data); /* Edges inside of face regions also just duplicate their source data. */ MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range); - copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices); + array_utils::gather(data.as_span(), new_inner_edge_indices.as_span(), new_inner_data); /* Edges connected to original vertices mix values of selected connected edges. */ MutableSpan<T> connect_data = data.slice(connect_edge_range); @@ -942,8 +915,8 @@ static void extrude_mesh_face_regions(MeshComponent &component, case ATTR_DOMAIN_FACE: { /* New faces on the side of extrusions get the values from the corresponding selected * face. */ - copy_with_indices( - data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices); + array_utils::gather( + data.as_span(), edge_extruded_face_indices.as_span(), data.slice(side_poly_range)); break; } case ATTR_DOMAIN_CORNER: { @@ -1003,13 +976,14 @@ static void extrude_mesh_face_regions(MeshComponent &component, /* Translate vertices based on the offset. If the vertex is used by a selected edge, it will * have been duplicated and only the new vertex should use the offset. Otherwise the vertex might * still need an offset, but it was reused on the inside of a region of extruded faces. */ + MutableSpan<MVert> verts = mesh.verts_for_write(); if (poly_offsets.is_single()) { const float3 offset = poly_offsets.get_internal_single(); threading::parallel_for( IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) { for (const int i_orig : all_selected_verts.as_span().slice(range)) { const int i_new = new_vert_indices.index_of_try(i_orig); - MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]]; + MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]]; add_v3_v3(vert.co, offset); } }); @@ -1020,7 +994,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, for (const int i_orig : all_selected_verts.as_span().slice(range)) { const int i_new = new_vert_indices.index_of_try(i_orig); const float3 offset = vert_offsets[i_orig]; - MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]]; + MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]]; add_v3_v3(vert.co, offset); } }); @@ -1039,11 +1013,11 @@ static void extrude_mesh_face_regions(MeshComponent &component, if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -1057,21 +1031,20 @@ static IndexRange selected_corner_range(Span<int> offsets, const int index) return IndexRange(offset, next_offset - offset); } -static void extrude_individual_mesh_faces(MeshComponent &component, +static void extrude_individual_mesh_faces(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; const int orig_edge_size = mesh.totedge; - Span<MPoly> orig_polys = mesh_polys(mesh); - Span<MLoop> orig_loops = mesh_loops(mesh); + Span<MPoly> orig_polys = mesh.polys(); + Span<MLoop> orig_loops = mesh.loops(); /* Use a mesh for the result of the evaluation because the mesh is reallocated before * the vertices are moved, and the evaluated result might reference an attribute. */ Array<float3> poly_offset(orig_polys.size()); - GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator poly_evaluator{poly_context, mesh.totpoly}; poly_evaluator.set_selection(selection_field); poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span()); @@ -1105,13 +1078,13 @@ static void extrude_individual_mesh_faces(MeshComponent &component, side_poly_range.size(), side_loop_range.size()); - MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); - MutableSpan<MEdge> edges{mesh.medge, mesh.totedge}; + MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range); + MutableSpan<MEdge> edges = mesh.edges_for_write(); MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range); - MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly}; + MutableSpan<MPoly> polys = mesh.polys_for_write(); MutableSpan<MPoly> new_polys = polys.slice(side_poly_range); - MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop}; + MutableSpan<MLoop> loops = mesh.loops_for_write(); /* For every selected polygon, build the faces that form the sides of the extrusion. Filling some * of this data like the new edges or polygons could be easily split into separate loops, which @@ -1159,9 +1132,12 @@ static void extrude_individual_mesh_faces(MeshComponent &component, } }); - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( id, meta_data.domain, meta_data.data_type); if (!attribute) { @@ -1318,11 +1294,11 @@ static void extrude_individual_mesh_faces(MeshComponent &component, if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -1335,7 +1311,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float3> offset_field = params.extract_input<Field<float3>>("Offset"); Field<float> scale_field = params.extract_input<Field<float>>("Offset Scale"); const NodeGeometryExtrudeMesh &storage = node_storage(params.node()); - GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode); + GeometryNodeExtrudeMeshMode mode = GeometryNodeExtrudeMeshMode(storage.mode); /* Create a combined field from the offset and the scale so the field evaluator * can take care of the multiplication and to simplify each extrude function. */ @@ -1359,27 +1335,26 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<bool>("Individual"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_mesh()) { - MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>(); + if (Mesh *mesh = geometry_set.get_mesh_for_write()) { switch (mode) { case GEO_NODE_EXTRUDE_MESH_VERTICES: - extrude_mesh_vertices(component, selection, final_offset, attribute_outputs); + extrude_mesh_vertices(*mesh, selection, final_offset, attribute_outputs); break; case GEO_NODE_EXTRUDE_MESH_EDGES: - extrude_mesh_edges(component, selection, final_offset, attribute_outputs); + extrude_mesh_edges(*mesh, selection, final_offset, attribute_outputs); break; case GEO_NODE_EXTRUDE_MESH_FACES: { if (extrude_individual) { - extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs); + extrude_individual_mesh_faces(*mesh, selection, final_offset, attribute_outputs); } else { - extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs); + extrude_mesh_face_regions(*mesh, selection, final_offset, attribute_outputs); } break; } } - BLI_assert(BKE_mesh_is_valid(component.get_for_write())); + BLI_assert(BKE_mesh_is_valid(mesh)); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index 64861e529bc..fc1e2cb2503 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -9,17 +9,76 @@ #include "BLI_task.hh" +#include "NOD_socket_search_link.hh" + +namespace blender::nodes { + +FieldAtIndexInput::FieldAtIndexInput(Field<int> index_field, + GField value_field, + eAttrDomain value_field_domain) + : bke::GeometryFieldInput(value_field.cpp_type(), "Field at Index"), + index_field_(std::move(index_field)), + value_field_(std::move(value_field)), + value_field_domain_(value_field_domain) +{ +} + +GVArray FieldAtIndexInput::get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask mask) const +{ + const std::optional<AttributeAccessor> attributes = context.attributes(); + if (!attributes) { + return {}; + } + + const bke::GeometryFieldContext value_field_context{ + context.geometry(), context.type(), value_field_domain_}; + FieldEvaluator value_evaluator{value_field_context, + attributes->domain_size(value_field_domain_)}; + value_evaluator.add(value_field_); + value_evaluator.evaluate(); + const GVArray &values = value_evaluator.get_evaluated(0); + + FieldEvaluator index_evaluator{context, &mask}; + index_evaluator.add(index_field_); + index_evaluator.evaluate(); + const VArray<int> indices = index_evaluator.get_evaluated<int>(0); + + GVArray output_array; + attribute_math::convert_to_static_type(*type_, [&](auto dummy) { + using T = decltype(dummy); + Array<T> dst_array(mask.min_array_size()); + VArray<T> src_values = values.typed<T>(); + threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { + for (const int i : mask.slice(range)) { + const int index = indices[i]; + if (src_values.index_range().contains(index)) { + dst_array[i] = src_values[index]; + } + else { + dst_array[i] = {}; + } + } + }); + output_array = VArray<T>::ForContainer(std::move(dst_array)); + }); + + return output_array; +} + +} // namespace blender::nodes + namespace blender::nodes::node_geo_field_at_index_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Int>(N_("Index")).min(0).supports_field(); - b.add_input<decl::Float>(N_("Value"), "Value_Float").supports_field(); - b.add_input<decl::Int>(N_("Value"), "Value_Int").supports_field(); - b.add_input<decl::Vector>(N_("Value"), "Value_Vector").supports_field(); - b.add_input<decl::Color>(N_("Value"), "Value_Color").supports_field(); - b.add_input<decl::Bool>(N_("Value"), "Value_Bool").supports_field(); + b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field(); b.add_output<decl::Float>(N_("Value"), "Value_Float").field_source(); b.add_output<decl::Int>(N_("Value"), "Value_Int").field_source(); @@ -28,13 +87,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Value"), "Value_Bool").field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = ATTR_DOMAIN_POINT; node->custom2 = CD_PROP_FLOAT; @@ -42,7 +101,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { - const eCustomDataType data_type = static_cast<eCustomDataType>(node->custom2); + const eCustomDataType data_type = eCustomDataType(node->custom2); bNodeSocket *sock_index = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *sock_in_float = sock_index->next; @@ -70,60 +129,22 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); } -class FieldAtIndex final : public GeometryFieldInput { - private: - Field<int> index_field_; - GField value_field_; - eAttrDomain value_field_domain_; - - public: - FieldAtIndex(Field<int> index_field, GField value_field, eAttrDomain value_field_domain) - : GeometryFieldInput(value_field.cpp_type(), "Field at Index"), - index_field_(std::move(index_field)), - value_field_(std::move(value_field)), - value_field_domain_(value_field_domain) - { - } - - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask mask) const final - { - const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; - FieldEvaluator value_evaluator{value_field_context, - component.attribute_domain_size(value_field_domain_)}; - value_evaluator.add(value_field_); - value_evaluator.evaluate(); - const GVArray &values = value_evaluator.get_evaluated(0); - - const GeometryComponentFieldContext index_field_context{component, domain}; - FieldEvaluator index_evaluator{index_field_context, &mask}; - index_evaluator.add(index_field_); - index_evaluator.evaluate(); - const VArray<int> indices = index_evaluator.get_evaluated<int>(0); - - GVArray output_array; - attribute_math::convert_to_static_type(*type_, [&](auto dummy) { - using T = decltype(dummy); - Array<T> dst_array(mask.min_array_size()); - VArray<T> src_values = values.typed<T>(); - threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { - for (const int i : mask.slice(range)) { - const int index = indices[i]; - if (index >= 0 && index < src_values.size()) { - dst_array[i] = src_values[index]; - } - else { - dst_array[i] = {}; - } - } - }); - output_array = VArray<T>::ForContainer(std::move(dst_array)); +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const bNodeType &node_type = params.node_type(); + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom2 = *type; + params.update_and_connect_available_socket(node, "Value"); }); - - return output_array; } -}; +} static StringRefNull identifier_suffix(eCustomDataType data_type) { @@ -147,16 +168,16 @@ static StringRefNull identifier_suffix(eCustomDataType data_type) static void node_geo_exec(GeoNodeExecParams params) { const bNode &node = params.node(); - const eAttrDomain domain = static_cast<eAttrDomain>(node.custom1); - const eCustomDataType data_type = static_cast<eCustomDataType>(node.custom2); + const eAttrDomain domain = eAttrDomain(node.custom1); + const eCustomDataType data_type = eCustomDataType(node.custom2); Field<int> index_field = params.extract_input<Field<int>>("Index"); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); static const std::string identifier = "Value_" + identifier_suffix(data_type); Field<T> value_field = params.extract_input<Field<T>>(identifier); - Field<T> output_field{ - std::make_shared<FieldAtIndex>(std::move(index_field), std::move(value_field), domain)}; + Field<T> output_field{std::make_shared<FieldAtIndexInput>( + std::move(index_field), std::move(value_field), domain)}; params.set_output(identifier, std::move(output_field)); }); } @@ -175,5 +196,6 @@ void register_node_type_geo_field_at_index() ntype.draw_buttons = file_ns::node_layout; ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 15b2822805a..95a0013a9e1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -19,24 +19,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) +static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field) { - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + if (mesh.totpoly == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{field_context, mesh.totpoly}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); - Mesh *mesh = component.get_for_write(); - - mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer( - &mesh->ldata, CD_MLOOP, mesh->totloop); - Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + const Span<MPoly> polys = mesh.polys(); + MutableSpan<MLoop> loops = mesh.loops_for_write(); for (const int i : selection.index_range()) { const MPoly &poly = polys[selection[i]]; @@ -49,9 +44,12 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti } } - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } if (meta_data.domain == ATTR_DOMAIN_CORNER) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type); @@ -76,11 +74,9 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { - return; + if (Mesh *mesh = geometry_set.get_mesh_for_write()) { + mesh_flip_faces(*mesh, selection_field); } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_flip_faces(mesh_component, selection_field); }); params.set_output("Mesh", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc index 1f84f8f288d..45808ff9996 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_geometry_to_instance_cc { @@ -12,16 +14,14 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { - Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry"); - GeometrySet instances_geometry; - InstancesComponent &instances_component = - instances_geometry.get_component_for_write<InstancesComponent>(); + Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry"); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); for (GeometrySet &geometry : geometries) { geometry.ensure_owns_direct_data(); - const int handle = instances_component.add_reference(std::move(geometry)); - instances_component.add_instance(handle, float4x4::identity()); + const int handle = instances->add_reference(std::move(geometry)); + instances->add_instance(handle, float4x4::identity()); } - params.set_output("Instances", std::move(instances_geometry)); + params.set_output("Instances", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_geometry_to_instance_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc index 33802d00d2b..f39337d3fc3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -24,20 +24,20 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Image>(N_("Image")).hide_label(); b.add_input<decl::Vector>(N_("Vector")) - .implicit_field() - .description(("Texture coordinates from 0 to 1")); + .implicit_field(implicit_field_inputs::position) + .description("Texture coordinates from 0 to 1"); b.add_input<decl::Int>(N_("Frame")).min(0).max(MAXFRAMEF); b.add_output<decl::Color>(N_("Color")).no_muted_links().dependent_field(); b.add_output<decl::Float>(N_("Alpha")).no_muted_links().dependent_field(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryImageTexture *tex = MEM_cnew<NodeGeometryImageTexture>(__func__); node->storage = tex; @@ -122,9 +122,9 @@ class ImageFieldsFunction : public fn::MultiFunction { static float frac(const float x, int *ix) { - const int i = (int)x - ((x < 0.0f) ? 1 : 0); + const int i = int(x) - ((x < 0.0f) ? 1 : 0); *ix = i; - return x - (float)i; + return x - float(i); } static float4 image_cubic_texture_lookup(const ImBuf *ibuf, @@ -135,8 +135,8 @@ class ImageFieldsFunction : public fn::MultiFunction { const int width = ibuf->x; const int height = ibuf->y; int pix, piy, nix, niy; - const float tx = frac(px * (float)width - 0.5f, &pix); - const float ty = frac(py * (float)height - 0.5f, &piy); + const float tx = frac(px * float(width) - 0.5f, &pix); + const float ty = frac(py * float(height) - 0.5f, &piy); int ppix, ppiy, nnix, nniy; switch (extension) { @@ -189,22 +189,22 @@ class ImageFieldsFunction : public fn::MultiFunction { v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f); v[3] = (1.0f / 6.0f) * ty * ty * ty; - return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) + - u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) + - u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) + - u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) + - (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) + - u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) + - u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) + - u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) + - (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) + - u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) + - u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) + - u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) + - (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) + - u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) + - u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) + - u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3])))); + return (v[0] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[0]) + + u[1] * image_pixel_lookup(ibuf, xc[1], yc[0]) + + u[2] * image_pixel_lookup(ibuf, xc[2], yc[0]) + + u[3] * image_pixel_lookup(ibuf, xc[3], yc[0]))) + + (v[1] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[1]) + + u[1] * image_pixel_lookup(ibuf, xc[1], yc[1]) + + u[2] * image_pixel_lookup(ibuf, xc[2], yc[1]) + + u[3] * image_pixel_lookup(ibuf, xc[3], yc[1]))) + + (v[2] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[2]) + + u[1] * image_pixel_lookup(ibuf, xc[1], yc[2]) + + u[2] * image_pixel_lookup(ibuf, xc[2], yc[2]) + + u[3] * image_pixel_lookup(ibuf, xc[3], yc[2]))) + + (v[3] * (u[0] * image_pixel_lookup(ibuf, xc[0], yc[3]) + + u[1] * image_pixel_lookup(ibuf, xc[1], yc[3]) + + u[2] * image_pixel_lookup(ibuf, xc[2], yc[3]) + + u[3] * image_pixel_lookup(ibuf, xc[3], yc[3]))); } static float4 image_linear_texture_lookup(const ImBuf *ibuf, @@ -215,8 +215,8 @@ class ImageFieldsFunction : public fn::MultiFunction { const int width = ibuf->x; const int height = ibuf->y; int pix, piy, nix, niy; - const float nfx = frac(px * (float)width - 0.5f, &pix); - const float nfy = frac(py * (float)height - 0.5f, &piy); + const float nfx = frac(px * float(width) - 0.5f, &pix); + const float nfy = frac(py * float(height) - 0.5f, &piy); switch (extension) { case SHD_IMAGE_EXTENSION_CLIP: { @@ -257,8 +257,8 @@ class ImageFieldsFunction : public fn::MultiFunction { const int width = ibuf->x; const int height = ibuf->y; int ix, iy; - const float tx = frac(px * (float)width, &ix); - const float ty = frac(py * (float)height, &iy); + const float tx = frac(px * float(width), &ix); + const float ty = frac(py * float(height), &iy); switch (extension) { case SHD_IMAGE_EXTENSION_REPEAT: { @@ -285,14 +285,14 @@ class ImageFieldsFunction : public fn::MultiFunction { } } - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override { const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector"); MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>( 1, "Color"); MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha"); - MutableSpan<float4> color_data{(float4 *)r_color.data(), r_color.size()}; + MutableSpan<float4> color_data{reinterpret_cast<float4 *>(r_color.data()), r_color.size()}; /* Sample image texture. */ switch (interpolation_) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index bc1b9e940a1..2979d0e4639 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_input_curve_handles_cc { @@ -15,31 +17,27 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Right")).field_source(); } -class HandlePositionFieldInput final : public GeometryFieldInput { +class HandlePositionFieldInput final : public bke::CurvesFieldInput { Field<bool> relative_; bool left_; public: HandlePositionFieldInput(Field<bool> relative, bool left) - : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left) + : bke::CurvesFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left) { } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask mask) const final + const IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE) { - return {}; - } - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator(field_context, &mask); evaluator.add(relative_); evaluator.evaluate(); const VArray<bool> relative = evaluator.get_evaluated<bool>(0); - const AttributeAccessor attributes = *component.attributes(); + const AttributeAccessor attributes = curves.attributes(); VArray<float3> positions = attributes.lookup_or_default<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); @@ -69,7 +67,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput { output[i] = handles[i]; } } - return component.attributes()->adapt_domain<float3>( + return attributes.adapt_domain<float3>( VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); } @@ -86,6 +84,11 @@ class HandlePositionFieldInput final : public GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc index 4c7a148a797..f78815ebe74 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc @@ -2,6 +2,8 @@ #include "node_geometry_util.hh" +#include "BKE_instances.hh" + namespace blender::nodes::node_geo_input_instance_rotation_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -9,28 +11,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Rotation")).field_source(); } -class VectorFieldInput final : public GeometryFieldInput { +class InstanceRotationFieldInput final : public bke::InstancesFieldInput { public: - VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Rotation") + InstanceRotationFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Rotation") { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain UNUSED(domain), - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) { - return {}; - } - - const InstancesComponent &instance_component = static_cast<const InstancesComponent &>( - component); - - auto rotation_fn = [&](const int i) -> float3 { - return instance_component.instance_transforms()[i].to_euler(); - }; + auto rotation_fn = [&](const int i) -> float3 { return instances.transforms()[i].to_euler(); }; - return VArray<float3>::ForFunc(instance_component.instances_num(), rotation_fn); + return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn); } uint64_t hash() const override @@ -40,13 +31,13 @@ class VectorFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const VectorFieldInput *>(&other) != nullptr; + return dynamic_cast<const InstanceRotationFieldInput *>(&other) != nullptr; } }; static void node_geo_exec(GeoNodeExecParams params) { - Field<float3> rotation{std::make_shared<VectorFieldInput>()}; + Field<float3> rotation{std::make_shared<InstanceRotationFieldInput>()}; params.set_output("Rotation", std::move(rotation)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc index b3a362fbf3e..12ac48f8f11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc @@ -2,6 +2,8 @@ #include "node_geometry_util.hh" +#include "BKE_instances.hh" + namespace blender::nodes::node_geo_input_instance_scale_cc { static void node_declare(NodeDeclarationBuilder &b) @@ -9,28 +11,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Scale")).field_source(); } -class VectorFieldInput final : public GeometryFieldInput { +class InstanceScaleFieldInput final : public bke::InstancesFieldInput { public: - VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Scale") + InstanceScaleFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Scale") { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain UNUSED(domain), - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) { - return {}; - } - - const InstancesComponent &instance_component = static_cast<const InstancesComponent &>( - component); - - auto scale_fn = [&](const int i) -> float3 { - return instance_component.instance_transforms()[i].scale(); - }; + auto scale_fn = [&](const int i) -> float3 { return instances.transforms()[i].scale(); }; - return VArray<float3>::ForFunc(instance_component.instances_num(), scale_fn); + return VArray<float3>::ForFunc(instances.instances_num(), scale_fn); } uint64_t hash() const override @@ -40,13 +31,13 @@ class VectorFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const VectorFieldInput *>(&other) != nullptr; + return dynamic_cast<const InstanceScaleFieldInput *>(&other) != nullptr; } }; static void node_geo_exec(GeoNodeExecParams params) { - Field<float3> scale{std::make_shared<VectorFieldInput>()}; + Field<float3> scale{std::make_shared<InstanceScaleFieldInput>()}; params.set_output("Scale", std::move(scale)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc index 19882c4966d..943193a0d82 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -12,14 +12,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Material>(N_("Material")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "material", 0, "", ICON_NONE); } static void node_geo_exec(GeoNodeExecParams params) { - Material *material = (Material *)params.node().id; + Material *material = reinterpret_cast<Material *>(params.node().id); params.set_output("Material", material); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc index b009aaa5291..29730ab8dc4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc @@ -53,46 +53,36 @@ static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys, return edge_map; } -class AngleFieldInput final : public GeometryFieldInput { +class AngleFieldInput final : public bke::MeshFieldInput { public: - AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field") + AngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Unsigned Angle Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; - Span<MLoop> loops{mesh->mloop, mesh->totloop}; - Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge); + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge); - auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float { + auto angle_fn = [edge_map = std::move(edge_map), verts, polys, loops](const int i) -> float { if (edge_map[i].face_count != 2) { return 0.0f; } const MPoly &mpoly_1 = polys[edge_map[i].face_index_1]; const MPoly &mpoly_2 = polys[edge_map[i].face_index_2]; float3 normal_1, normal_2; - BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1); - BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2); + BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), normal_1); + BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), normal_2); return angle_normalized_v3v3(normal_1, normal_2); }; - VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn); - return component.attributes()->adapt_domain<float>( - std::move(angles), ATTR_DOMAIN_EDGE, domain); + VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn); + return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override @@ -105,34 +95,32 @@ class AngleFieldInput final : public GeometryFieldInput { { return dynamic_cast<const AngleFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; -class SignedAngleFieldInput final : public GeometryFieldInput { +class SignedAngleFieldInput final : public bke::MeshFieldInput { public: - SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field") + SignedAngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Signed Angle Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; - Span<MLoop> loops{mesh->mloop, mesh->totloop}; - Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge); - - auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float { + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge); + + auto angle_fn = + [edge_map = std::move(edge_map), verts, edges, polys, loops](const int i) -> float { if (edge_map[i].face_count != 2) { return 0.0f; } @@ -141,18 +129,18 @@ class SignedAngleFieldInput final : public GeometryFieldInput { /* Find the normals of the 2 polys. */ float3 poly_1_normal, poly_2_normal; - BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal); - BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal); + BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), poly_1_normal); + BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_2_normal); /* Find the centerpoint of the axis edge */ - const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) + - float3(mesh->mvert[mesh->medge[i].v2].co)) * + const float3 edge_centerpoint = (float3(verts[edges[i].v1].co) + + float3(verts[edges[i].v2].co)) * 0.5f; /* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent * normal for poly 2. */ float3 poly_center_2; - BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2); + BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_center_2); const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint); const float concavity = math::dot(poly_1_normal, poly_2_tangent); @@ -165,9 +153,8 @@ class SignedAngleFieldInput final : public GeometryFieldInput { return -angle; }; - VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn); - return component.attributes()->adapt_domain<float>( - std::move(angles), ATTR_DOMAIN_EDGE, domain); + VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn); + return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override @@ -180,6 +167,11 @@ class SignedAngleFieldInput final : public GeometryFieldInput { { return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc index 50d6998bb27..97c950988e7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc @@ -16,34 +16,26 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The number of faces that use each edge as one of their sides")); } -class EdgeNeighborCountFieldInput final : public GeometryFieldInput { +class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput { public: EdgeNeighborCountFieldInput() - : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field") + : bke::MeshFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - 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 {}; - } - - Array<int> face_count(mesh->totedge, 0); - for (const int i : IndexRange(mesh->totloop)) { - face_count[mesh->mloop[i].e]++; - } - - return mesh_component.attributes()->adapt_domain<int>( - VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain); + const Span<MLoop> loops = mesh.loops(); + Array<int> face_count(mesh.totedge, 0); + for (const MLoop &loop : loops) { + face_count[loop.e]++; } - return {}; + + return mesh.attributes().adapt_domain<int>( + VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override @@ -56,6 +48,11 @@ class EdgeNeighborCountFieldInput final : public GeometryFieldInput { { return dynamic_cast<const EdgeNeighborCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc index 83e511f45c2..513ddd76055 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc @@ -25,114 +25,102 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The position of the second vertex in the edge")); } -enum VertexNumber { VERTEX_ONE, VERTEX_TWO }; +enum class VertNumber { V1, V2 }; -static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component, - const VertexNumber vertex, - const eAttrDomain domain) +static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh, + const VertNumber vertex, + const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MEdge> edges = mesh.edges(); if (domain == ATTR_DOMAIN_EDGE) { - if (vertex == VERTEX_ONE) { - return VArray<int>::ForFunc(mesh->totedge, - [mesh](const int i) -> int { return mesh->medge[i].v1; }); + if (vertex == VertNumber::V1) { + return VArray<int>::ForFunc(edges.size(), + [edges](const int i) -> int { return edges[i].v1; }); } - return VArray<int>::ForFunc(mesh->totedge, - [mesh](const int i) -> int { return mesh->medge[i].v2; }); + return VArray<int>::ForFunc(edges.size(), [edges](const int i) -> int { return edges[i].v2; }); } return {}; } -class EdgeVerticesFieldInput final : public GeometryFieldInput { +class EdgeVertsInput final : public bke::MeshFieldInput { private: - VertexNumber vertex_; + VertNumber vertex_; public: - EdgeVerticesFieldInput(VertexNumber vertex) - : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex) + EdgeVertsInput(VertNumber vertex) + : bke::MeshFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_edge_vertices_gvarray(mesh_component, vertex_, domain); - } - return {}; + return construct_edge_verts_gvarray(mesh, vertex_, domain); } uint64_t hash() const override { - return vertex_ == VERTEX_ONE ? 23847562893465 : 92384598734567; + return vertex_ == VertNumber::V1 ? 23847562893465 : 92384598734567; } bool is_equal_to(const fn::FieldNode &other) const override { - if (const EdgeVerticesFieldInput *other_field = dynamic_cast<const EdgeVerticesFieldInput *>( - &other)) { + if (const EdgeVertsInput *other_field = dynamic_cast<const EdgeVertsInput *>(&other)) { return vertex_ == other_field->vertex_; } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; -static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component, - const VertexNumber vertex, +static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh, + const VertNumber vertex, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); - if (vertex == VERTEX_ONE) { - return component.attributes()->adapt_domain<float3>( - VArray<float3>::ForFunc( - mesh->totedge, - [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }), + if (vertex == VertNumber::V1) { + return mesh.attributes().adapt_domain<float3>( + VArray<float3>::ForFunc(edges.size(), + [verts, edges](const int i) { return verts[edges[i].v1].co; }), ATTR_DOMAIN_EDGE, domain); } - return component.attributes()->adapt_domain<float3>( - VArray<float3>::ForFunc( - mesh->totedge, - [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }), + return mesh.attributes().adapt_domain<float3>( + VArray<float3>::ForFunc(edges.size(), + [verts, edges](const int i) { return verts[edges[i].v2].co; }), ATTR_DOMAIN_EDGE, domain); } -class EdgePositionFieldInput final : public GeometryFieldInput { +class EdgePositionFieldInput final : public bke::MeshFieldInput { private: - VertexNumber vertex_; + VertNumber vertex_; public: - EdgePositionFieldInput(VertexNumber vertex) - : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex) + EdgePositionFieldInput(VertNumber vertex) + : bke::MeshFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_edge_positions_gvarray(mesh_component, vertex_, domain); - } - return {}; + return construct_edge_positions_gvarray(mesh, vertex_, domain); } uint64_t hash() const override { - return vertex_ == VERTEX_ONE ? 987456978362 : 374587679866; + return vertex_ == VertNumber::V1 ? 987456978362 : 374587679866; } bool is_equal_to(const fn::FieldNode &other) const override @@ -143,14 +131,19 @@ class EdgePositionFieldInput final : public GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } }; static void node_geo_exec(GeoNodeExecParams params) { - Field<int> vertex_field_1{std::make_shared<EdgeVerticesFieldInput>(VERTEX_ONE)}; - Field<int> vertex_field_2{std::make_shared<EdgeVerticesFieldInput>(VERTEX_TWO)}; - Field<float3> position_field_1{std::make_shared<EdgePositionFieldInput>(VERTEX_ONE)}; - Field<float3> position_field_2{std::make_shared<EdgePositionFieldInput>(VERTEX_TWO)}; + Field<int> vertex_field_1{std::make_shared<EdgeVertsInput>(VertNumber::V1)}; + Field<int> vertex_field_2{std::make_shared<EdgeVertsInput>(VertNumber::V2)}; + Field<float3> position_field_1{std::make_shared<EdgePositionFieldInput>(VertNumber::V1)}; + Field<float3> position_field_2{std::make_shared<EdgePositionFieldInput>(VertNumber::V2)}; params.set_output("Vertex Index 1", std::move(vertex_field_1)); params.set_output("Vertex Index 2", std::move(vertex_field_2)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc index 4d21bf9443a..aec1c27a4fc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc @@ -16,39 +16,33 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The surface area of each of the mesh's faces")); } -static VArray<float> construct_face_area_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<float> construct_face_area_varray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - auto area_fn = [mesh](const int i) -> float { - const MPoly *mp = &mesh->mpoly[i]; - return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert); + auto area_fn = [verts, polys, loops](const int i) -> float { + const MPoly &poly = polys[i]; + return BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data()); }; - return component.attributes()->adapt_domain<float>( - VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain); + return mesh.attributes().adapt_domain<float>( + VArray<float>::ForFunc(polys.size(), area_fn), ATTR_DOMAIN_FACE, domain); } -class FaceAreaFieldInput final : public GeometryFieldInput { +class FaceAreaFieldInput final : public bke::MeshFieldInput { public: - FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field") + FaceAreaFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Face Area Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_face_area_gvarray(mesh_component, domain); - } - return {}; + return construct_face_area_varray(mesh, domain); } uint64_t hash() const override @@ -61,6 +55,11 @@ class FaceAreaFieldInput final : public GeometryFieldInput { { return dynamic_cast<const FaceAreaFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc index 6b04ff08d9e..7b084995fc3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -22,53 +22,46 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>("Planar").field_source(); } -class PlanarFieldInput final : public GeometryFieldInput { +class PlanarFieldInput final : public bke::MeshFieldInput { private: Field<float> threshold_; public: PlanarFieldInput(Field<float> threshold) - : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) + : bke::MeshFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - [[maybe_unused]] IndexMask mask) const final + IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE}; - fn::FieldEvaluator evaluator{context, mesh->totpoly}; + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + const Span<float3> poly_normals{ + reinterpret_cast<const float3 *>(BKE_mesh_poly_normals_ensure(&mesh)), mesh.totpoly}; + + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{context, polys.size()}; evaluator.add(threshold_); evaluator.evaluate(); const VArray<float> thresholds = evaluator.get_evaluated<float>(0); - Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}; - - auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool { - if (mesh->mpoly[i_poly].totloop <= 3) { + auto planar_fn = [verts, polys, loops, thresholds, poly_normals](const int i) -> bool { + const MPoly &poly = polys[i]; + if (poly.totloop <= 3) { return true; } - const int loopstart = mesh->mpoly[i_poly].loopstart; - const int loops = mesh->mpoly[i_poly].totloop; - Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops); - float3 reference_normal = poly_normals[i_poly]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + const float3 &reference_normal = poly_normals[i]; float min = FLT_MAX; float max = -FLT_MAX; for (const int i_loop : poly_loops.index_range()) { - const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; + const float3 vert = verts[poly_loops[i_loop].v].co; float dot = math::dot(reference_normal, vert); if (dot > max) { max = dot; @@ -77,11 +70,11 @@ class PlanarFieldInput final : public GeometryFieldInput { min = dot; } } - return max - min < thresholds[i_poly] / 2.0f; + return max - min < thresholds[i] / 2.0f; }; - return component.attributes()->adapt_domain<bool>( - VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain); + return mesh.attributes().adapt_domain<bool>( + VArray<bool>::ForFunc(polys.size(), planar_fn), ATTR_DOMAIN_FACE, domain); } uint64_t hash() const override @@ -94,6 +87,11 @@ class PlanarFieldInput final : public GeometryFieldInput { { return dynamic_cast<const PlanarFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void geo_node_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc index a225ce61b14..f1724ef4a41 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc @@ -19,48 +19,41 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("Number of faces which share an edge with the face")); } -static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - Array<int> edge_count(mesh->totedge, 0); - for (const int i : IndexRange(mesh->totloop)) { - edge_count[mesh->mloop[i].e]++; + Array<int> edge_count(mesh.totedge, 0); + for (const MLoop &loop : loops) { + edge_count[loop.e]++; } - Array<int> poly_count(mesh->totpoly, 0); - for (const int poly_num : IndexRange(mesh->totpoly)) { - MPoly &poly = mesh->mpoly[poly_num]; - for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) { - poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1; + Array<int> poly_count(polys.size(), 0); + for (const int poly_index : polys.index_range()) { + const MPoly &poly = polys[poly_index]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + poly_count[poly_index] += edge_count[loop.e] - 1; } } - return component.attributes()->adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain); } -class FaceNeighborCountFieldInput final : public GeometryFieldInput { +class FaceNeighborCountFieldInput final : public bke::MeshFieldInput { public: FaceNeighborCountFieldInput() - : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field") + : bke::MeshFieldInput(CPPType::get<int>(), "Face Neighbor Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_neighbor_count_gvarray(mesh_component, domain); - } - return {}; + return construct_neighbor_count_varray(mesh, domain); } uint64_t hash() const override @@ -73,39 +66,35 @@ class FaceNeighborCountFieldInput final : public GeometryFieldInput { { return dynamic_cast<const FaceNeighborCountFieldInput *>(&other) != nullptr; } -}; -static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) -{ - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; } +}; - return component.attributes()->adapt_domain<int>( - VArray<int>::ForFunc(mesh->totpoly, - [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }), +static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain) +{ + const Span<MPoly> polys = mesh.polys(); + return mesh.attributes().adapt_domain<int>( + VArray<int>::ForFunc(polys.size(), + [polys](const int i) -> float { return polys[i].totloop; }), ATTR_DOMAIN_FACE, domain); } -class FaceVertexCountFieldInput final : public GeometryFieldInput { +class FaceVertexCountFieldInput final : public bke::MeshFieldInput { public: - FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field") + FaceVertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_vertex_count_gvarray(mesh_component, domain); - } - return {}; + return construct_vertex_count_varray(mesh, domain); } uint64_t hash() const override @@ -118,6 +107,11 @@ class FaceVertexCountFieldInput final : public GeometryFieldInput { { return dynamic_cast<const FaceVertexCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index 2c7eef5665f..6b54828b042 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -22,39 +22,32 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The total number of mesh islands")); } -class IslandFieldInput final : public GeometryFieldInput { +class IslandFieldInput final : public bke::MeshFieldInput { public: - IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index") + IslandFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Index") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MEdge> edges = mesh.edges(); - DisjointSet islands(mesh->totvert); - for (const int i : IndexRange(mesh->totedge)) { - islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + DisjointSet islands(mesh.totvert); + for (const int i : edges.index_range()) { + islands.join(edges[i].v1, edges[i].v2); } - Array<int> output(mesh->totvert); + Array<int> output(mesh.totvert); VectorSet<int> ordered_roots; - for (const int i : IndexRange(mesh->totvert)) { + for (const int i : IndexRange(mesh.totvert)) { const int64_t root = islands.find_root(i); output[i] = ordered_roots.index_of_or_add(root); } - return mesh_component.attributes()->adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); } @@ -68,41 +61,38 @@ class IslandFieldInput final : public GeometryFieldInput { { return dynamic_cast<const IslandFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; -class IslandCountFieldInput final : public GeometryFieldInput { +class IslandCountFieldInput final : public bke::MeshFieldInput { public: - IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count") + IslandCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Count") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MEdge> edges = mesh.edges(); - DisjointSet islands(mesh->totvert); - for (const int i : IndexRange(mesh->totedge)) { - islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + DisjointSet islands(mesh.totvert); + for (const int i : edges.index_range()) { + islands.join(edges[i].v1, edges[i].v2); } Set<int> island_list; - for (const int i_vert : IndexRange(mesh->totvert)) { + for (const int i_vert : IndexRange(mesh.totvert)) { const int64_t root = islands.find_root(i_vert); island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - mesh_component.attribute_domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh.attributes().domain_size(domain)); } uint64_t hash() const override @@ -115,6 +105,11 @@ class IslandCountFieldInput final : public GeometryFieldInput { { return dynamic_cast<const IslandCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc index 62b3f9d0e92..5b1b32c7b9c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc @@ -20,41 +20,32 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("Number of faces that contain the vertex")); } -static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<int> construct_vertex_count_gvarray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - + const Span<MEdge> edges = mesh.edges(); if (domain == ATTR_DOMAIN_POINT) { - Array<int> vertices(mesh->totvert, 0); - for (const int i : IndexRange(mesh->totedge)) { - vertices[mesh->medge[i].v1]++; - vertices[mesh->medge[i].v2]++; + Array<int> counts(mesh.totvert, 0); + for (const int i : edges.index_range()) { + counts[edges[i].v1]++; + counts[edges[i].v2]++; } - return VArray<int>::ForContainer(std::move(vertices)); + return VArray<int>::ForContainer(std::move(counts)); } return {}; } -class VertexCountFieldInput final : public GeometryFieldInput { +class VertexCountFieldInput final : public bke::MeshFieldInput { public: - VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field") + VertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_vertex_count_gvarray(mesh_component, domain); - } - return {}; + return construct_vertex_count_gvarray(mesh, domain); } uint64_t hash() const override @@ -67,20 +58,20 @@ class VertexCountFieldInput final : public GeometryFieldInput { { return dynamic_cast<const VertexCountFieldInput *>(&other) != nullptr; } -}; -static VArray<int> construct_face_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) -{ - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; } +}; +static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain) +{ + const Span<MLoop> loops = mesh.loops(); if (domain == ATTR_DOMAIN_POINT) { - Array<int> vertices(mesh->totvert, 0); - for (const int i : IndexRange(mesh->totloop)) { - int vertex = mesh->mloop[i].v; + Array<int> vertices(mesh.totvert, 0); + for (const int i : loops.index_range()) { + int vertex = loops[i].v; vertices[vertex]++; } return VArray<int>::ForContainer(std::move(vertices)); @@ -88,22 +79,18 @@ static VArray<int> construct_face_count_gvarray(const MeshComponent &component, return {}; } -class VertexFaceCountFieldInput final : public GeometryFieldInput { +class VertexFaceCountFieldInput final : public bke::MeshFieldInput { public: - VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field") + VertexFaceCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Face Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_face_count_gvarray(mesh_component, domain); - } - return {}; + return construct_face_count_gvarray(mesh, domain); } uint64_t hash() const override @@ -116,6 +103,11 @@ class VertexFaceCountFieldInput final : public GeometryFieldInput { { return dynamic_cast<const VertexFaceCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc index 122c7b352c7..1063a022e9d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc @@ -22,12 +22,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Int>(N_("Attribute"), "Attribute_Int").field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryInputNamedAttribute *data = MEM_cnew<NodeGeometryInputNamedAttribute>(__func__); data->data_type = CD_PROP_FLOAT; @@ -37,9 +37,9 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryInputNamedAttribute &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); - bNodeSocket *socket_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *socket_vector = static_cast<bNodeSocket *>(node->outputs.first); bNodeSocket *socket_float = socket_vector->next; bNodeSocket *socket_color4f = socket_float->next; bNodeSocket *socket_boolean = socket_color4f->next; @@ -59,7 +59,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) if (params.in_out() == SOCK_OUT) { const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( - static_cast<eNodeSocketDatatype>(params.other_socket().type)); + eNodeSocketDatatype(params.other_socket().type)); if (type && *type != CD_PROP_STRING) { /* The input and output sockets have the same name. */ params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { @@ -74,7 +74,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryInputNamedAttribute &storage = node_storage(params.node()); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); const std::string name = params.extract_input<std::string>("Name"); @@ -88,7 +88,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - params.used_named_attribute(name, eNamedAttrUsage::Read); + params.used_named_attribute(name, NamedAttributeUsage::Read); switch (data_type) { case CD_PROP_FLOAT: diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc b/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc index 0222ccbbd02..74d10c286a0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc @@ -18,7 +18,7 @@ static void node_exec(GeoNodeExecParams params) { const Scene *scene = DEG_get_input_scene(params.depsgraph()); const float scene_ctime = BKE_scene_ctime_get(scene); - const double frame_rate = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base); + const double frame_rate = (double(scene->r.frs_sec) / double(scene->r.frs_sec_base)); params.set_output("Seconds", float(scene_ctime / frame_rate)); params.set_output("Frame", scene_ctime); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc new file mode 100644 index 00000000000..00c92e30443 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <queue> + +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_shortest_edge_paths_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>(N_("End Vertex")).default_value(false).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Edge Cost")).default_value(1.0f).hide_value().supports_field(); + b.add_output<decl::Int>(N_("Next Vertex Index")).field_source(); + b.add_output<decl::Float>(N_("Total Cost")).field_source(); +} + +typedef std::pair<float, int> VertPriority; + +struct EdgeVertMap { + Array<Vector<int>> edges_by_vertex_map; + + EdgeVertMap(const Mesh &mesh) + { + const Span<MEdge> edges = mesh.edges(); + edges_by_vertex_map.reinitialize(mesh.totvert); + for (const int edge_i : edges.index_range()) { + const MEdge &edge = edges[edge_i]; + edges_by_vertex_map[edge.v1].append(edge_i); + edges_by_vertex_map[edge.v2].append(edge_i); + } + } +}; + +static void shortest_paths(const Mesh &mesh, + EdgeVertMap &maps, + const IndexMask end_selection, + const VArray<float> &input_cost, + MutableSpan<int> r_next_index, + MutableSpan<float> r_cost) +{ + const Span<MEdge> edges = mesh.edges(); + Array<bool> visited(mesh.totvert, false); + + std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue; + + for (const int start_vert_i : end_selection) { + r_cost[start_vert_i] = 0.0f; + queue.emplace(0.0f, start_vert_i); + } + + while (!queue.empty()) { + const float cost_i = queue.top().first; + const int vert_i = queue.top().second; + queue.pop(); + if (visited[vert_i]) { + continue; + } + visited[vert_i] = true; + const Span<int> incident_edge_indices = maps.edges_by_vertex_map[vert_i]; + for (const int edge_i : incident_edge_indices) { + const MEdge &edge = edges[edge_i]; + const int neighbor_vert_i = edge.v1 + edge.v2 - vert_i; + if (visited[neighbor_vert_i]) { + continue; + } + const float edge_cost = std::max(0.0f, input_cost[edge_i]); + const float new_neighbour_cost = cost_i + edge_cost; + if (new_neighbour_cost < r_cost[neighbor_vert_i]) { + r_cost[neighbor_vert_i] = new_neighbour_cost; + r_next_index[neighbor_vert_i] = vert_i; + queue.emplace(new_neighbour_cost, neighbor_vert_i); + } + } + } +} + +class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput { + private: + Field<bool> end_selection_; + Field<float> cost_; + + public: + ShortestEdgePathsNextVertFieldInput(Field<bool> end_selection, Field<float> cost) + : bke::MeshFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"), + end_selection_(end_selection), + cost_(cost) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; + edge_evaluator.add(cost_); + edge_evaluator.evaluate(); + const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0); + + bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, mesh.totvert}; + point_evaluator.add(end_selection_); + point_evaluator.evaluate(); + const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0); + + Array<int> next_index(mesh.totvert, -1); + Array<float> cost(mesh.totvert, FLT_MAX); + + if (!end_selection.is_empty()) { + EdgeVertMap maps(mesh); + shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost); + } + threading::parallel_for(next_index.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + if (next_index[i] == -1) { + next_index[i] = i; + } + } + }); + return mesh.attributes().adapt_domain<int>( + VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 8466507837; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const ShortestEdgePathsNextVertFieldInput *other_field = + dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other)) { + return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_; + } + return false; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } +}; + +class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { + private: + Field<bool> end_selection_; + Field<float> cost_; + + public: + ShortestEdgePathsCostFieldInput(Field<bool> end_selection, Field<float> cost) + : bke::MeshFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"), + end_selection_(end_selection), + cost_(cost) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; + edge_evaluator.add(cost_); + edge_evaluator.evaluate(); + const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0); + + bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, mesh.totvert}; + point_evaluator.add(end_selection_); + point_evaluator.evaluate(); + const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0); + + Array<int> next_index(mesh.totvert, -1); + Array<float> cost(mesh.totvert, FLT_MAX); + + if (!end_selection.is_empty()) { + EdgeVertMap maps(mesh); + shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost); + } + threading::parallel_for(cost.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + if (cost[i] == FLT_MAX) { + cost[i] = 0; + } + } + }); + return mesh.attributes().adapt_domain<float>( + VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + return get_default_hash_2(end_selection_, cost_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const ShortestEdgePathsCostFieldInput *other_field = + dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other)) { + return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_; + } + return false; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> end_selection = params.extract_input<Field<bool>>("End Vertex"); + Field<float> cost = params.extract_input<Field<float>>("Edge Cost"); + + Field<int> next_vert_field{ + std::make_shared<ShortestEdgePathsNextVertFieldInput>(end_selection, cost)}; + Field<float> cost_field{std::make_shared<ShortestEdgePathsCostFieldInput>(end_selection, cost)}; + params.set_output("Next Vertex Index", std::move(next_vert_field)); + params.set_output("Total Cost", std::move(cost_field)); +} + +} // namespace blender::nodes::node_geo_input_shortest_edge_paths_cc + +void register_node_type_geo_input_shortest_edge_paths() +{ + namespace file_ns = blender::nodes::node_geo_input_shortest_edge_paths_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, "Shortest Edge Paths", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 267ba44cc00..4bb4618588b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -16,15 +16,9 @@ static void node_declare(NodeDeclarationBuilder &b) * Spline Count */ -static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component, +static VArray<int> construct_curve_point_count_gvarray(const bke::CurvesGeometry &curves, const eAttrDomain domain) { - if (!component.has_curves()) { - return {}; - } - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); }; if (domain == ATTR_DOMAIN_CURVE) { @@ -32,29 +26,24 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com } if (domain == ATTR_DOMAIN_POINT) { VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn); - return component.attributes()->adapt_domain<int>( - std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + return curves.adapt_domain<int>(std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } return {}; } -class SplineCountFieldInput final : public GeometryFieldInput { +class SplineCountFieldInput final : public bke::CurvesFieldInput { public: - SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count") + SplineCountFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Point Count") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_curve_point_count_gvarray(curve_component, domain); - } - return {}; + return construct_curve_point_count_gvarray(curves, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index a2aab5464aa..7e7b0eb215f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -63,19 +63,12 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve return results; } -static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component, +static VArray<float3> construct_curve_tangent_gvarray(const bke::CurvesGeometry &curves, const eAttrDomain domain) { - if (!component.has_curves()) { - return {}; - } - - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - const VArray<int8_t> types = curves.curve_types(); if (curves.is_single_type(CURVE_TYPE_POLY)) { - return component.attributes()->adapt_domain<float3>( + return curves.adapt_domain<float3>( VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain); } @@ -86,29 +79,25 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp } if (domain == ATTR_DOMAIN_CURVE) { - return component.attributes()->adapt_domain<float3>( + return curves.adapt_domain<float3>( VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); } return nullptr; } -class TangentFieldInput final : public GeometryFieldInput { +class TangentFieldInput final : public bke::CurvesFieldInput { public: - TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node") + TangentFieldInput() : bke::CurvesFieldInput(CPPType::get<float3>(), "Tangent node") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_curve_tangent_gvarray(curve_component, domain); - } - return {}; + return construct_curve_tangent_gvarray(curves, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 119d895fead..64546684186 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -2,6 +2,7 @@ #include "DNA_collection_types.h" +#include "BLI_array_utils.hh" #include "BLI_hash.h" #include "BLI_task.hh" @@ -9,6 +10,7 @@ #include "UI_resources.h" #include "BKE_attribute_math.hh" +#include "BKE_instances.hh" #include "node_geometry_util.hh" @@ -25,7 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("Choose instances from the \"Instance\" input at each point instead of " "instancing the entire geometry")); b.add_input<decl::Int>(N_("Instance Index")) - .implicit_field() + .implicit_field(implicit_field_inputs::id_or_index) .description(N_( "Index of the instance that used for each point. This is only used when Pick Instances " "is on. By default the point index is used")); @@ -43,7 +45,7 @@ static void node_declare(NodeDeclarationBuilder &b) } static void add_instances_from_component( - InstancesComponent &dst_component, + bke::Instances &dst_component, const GeometryComponent &src_component, const GeometrySet &instance, const GeoNodeExecParams ¶ms, @@ -57,7 +59,7 @@ static void add_instances_from_component( VArray<float3> rotations; VArray<float3> scales; - GeometryComponentFieldContext field_context{src_component, domain}; + bke::GeometryFieldContext field_context{src_component, domain}; const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); @@ -70,6 +72,9 @@ static void add_instances_from_component( evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + if (selection.is_empty()) { + return; + } /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ @@ -77,25 +82,23 @@ static void add_instances_from_component( const int select_len = selection.index_range().size(); dst_component.resize(start_len + select_len); - MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len, - select_len); - MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len, - select_len); + MutableSpan<int> dst_handles = dst_component.reference_handles().slice(start_len, select_len); + MutableSpan<float4x4> dst_transforms = dst_component.transforms().slice(start_len, select_len); VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>( "position", domain, {0, 0, 0}); - const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>(); + const bke::Instances *src_instances = instance.get_instances_for_read(); /* Maps handles from the source instances to handles on the new instance. */ Array<int> handle_mapping; /* Only fill #handle_mapping when it may be used below. */ if (src_instances != nullptr && (!pick_instance.is_single() || pick_instance.get_internal_single())) { - Span<InstanceReference> src_references = src_instances->references(); + Span<bke::InstanceReference> src_references = src_instances->references(); handle_mapping.reinitialize(src_references.size()); for (const int src_instance_handle : src_references.index_range()) { - const InstanceReference &reference = src_references[src_instance_handle]; + const bke::InstanceReference &reference = src_references[src_instance_handle]; const int dst_instance_handle = dst_component.add_reference(reference); handle_mapping[src_instance_handle] = dst_instance_handle; } @@ -103,7 +106,7 @@ static void add_instances_from_component( const int full_instance_handle = dst_component.add_reference(instance); /* Add this reference last, because it is the most likely one to be removed later on. */ - const int empty_reference_handle = dst_component.add_reference(InstanceReference()); + const int empty_reference_handle = dst_component.add_reference(bke::InstanceReference()); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { for (const int range_i : selection_range) { @@ -126,12 +129,11 @@ static void add_instances_from_component( const int index = mod_i(original_index, std::max(src_instances_num, 1)); if (index < src_instances_num) { /* Get the reference to the source instance. */ - const int src_handle = src_instances->instance_reference_handles()[index]; + const int src_handle = src_instances->reference_handles()[index]; dst_handle = handle_mapping[src_handle]; /* Take transforms of the source instance into account. */ - mul_m4_m4_post(dst_transform.values, - src_instances->instance_transforms()[index].values); + mul_m4_m4_post(dst_transform.values, src_instances->transforms()[index].values); } } } @@ -154,7 +156,7 @@ static void add_instances_from_component( } } - bke::CustomDataAttributes &instance_attributes = dst_component.instance_attributes(); + bke::CustomDataAttributes &instance_attributes = dst_component.custom_data_attributes(); for (const auto item : attributes_to_propagate.items()) { const AttributeIDRef &attribute_id = item.key; const AttributeKind attribute_kind = item.value; @@ -171,18 +173,7 @@ static void add_instances_from_component( dst_attribute_opt = instance_attributes.get_for_write(attribute_id); } BLI_assert(dst_attribute_opt); - const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len); - threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { - attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray<T> src = src_attribute.typed<T>(); - MutableSpan<T> dst = dst_attribute.typed<T>(); - for (const int range_i : selection_range) { - const int i = selection[range_i]; - dst[range_i] = src[i]; - } - }); - }); + array_utils::gather(src_attribute, selection, dst_attribute_opt->slice(start_len, select_len)); } } @@ -193,7 +184,15 @@ static void node_geo_exec(GeoNodeExecParams params) instance.ensure_owns_direct_data(); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + /* It's important not to invalidate the existing #InstancesComponent because it owns references + * to other geometry sets that are processed by this node. */ + InstancesComponent &instances_component = + geometry_set.get_component_for_write<InstancesComponent>(); + bke::Instances *dst_instances = instances_component.get_for_write(); + if (dst_instances == nullptr) { + dst_instances = new bke::Instances(); + instances_component.replace(dst_instances); + } const Array<GeometryComponentType> types{ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; @@ -205,14 +204,13 @@ static void node_geo_exec(GeoNodeExecParams params) for (const GeometryComponentType type : types) { if (geometry_set.has(type)) { - add_instances_from_component(instances, + add_instances_from_component(*dst_instances, *geometry_set.get_component_for_read(type), instance, params, attributes_to_propagate); } } - geometry_set.remove_geometry_during_modify(); }); @@ -220,8 +218,9 @@ static void node_geo_exec(GeoNodeExecParams params) * process them needlessly. * This should eventually be moved into the loop above, but currently this is quite tricky * because it might remove references that the loop still wants to iterate over. */ - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - instances.remove_unused_references(); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + instances->remove_unused_references(); + } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 5e0789e557b..acd00d119ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -3,6 +3,7 @@ #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" +#include "BKE_instances.hh" #include "BKE_pointcloud.h" #include "node_geometry_util.hh" @@ -13,7 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Instances")).only_instances(); b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(implicit_field_inputs::position); b.add_input<decl::Float>(N_("Radius")) .default_value(0.05f) .min(0.0f) @@ -27,12 +28,10 @@ static void convert_instances_to_points(GeometrySet &geometry_set, Field<float> radius_field, const Field<bool> selection_field) { - const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); + const bke::Instances &instances = *geometry_set.get_instances_for_read(); - GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.instances_num(); - - fn::FieldEvaluator evaluator{field_context, domain_size}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(std::move(selection_field)); evaluator.add(std::move(position_field)); evaluator.add(std::move(radius_field)); @@ -47,8 +46,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); bke::SpanAttributeWriter<float3> point_positions = point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); @@ -73,7 +71,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const AttributeIDRef &attribute_id = item.key; const AttributeKind attribute_kind = item.value; - const GVArray src = instances.attributes()->lookup_or_default( + const GVArray src = instances.attributes().lookup_or_default( attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type); BLI_assert(src); GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span( diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc index 59e243db4a2..d4e18321665 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc @@ -9,7 +9,9 @@ #include "BLI_task.hh" -namespace blender::nodes::node_geo_field_on_domain_cc { +#include "NOD_socket_search_link.hh" + +namespace blender::nodes::node_geo_interpolate_domain_cc { static void node_declare(NodeDeclarationBuilder &b) { @@ -26,13 +28,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Value"), "Value_Bool").field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = ATTR_DOMAIN_POINT; node->custom2 = CD_PROP_FLOAT; @@ -40,7 +42,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { - const eCustomDataType data_type = static_cast<eCustomDataType>(node->custom2); + const eCustomDataType data_type = eCustomDataType(node->custom2); bNodeSocket *sock_in_float = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *sock_in_int = sock_in_float->next; @@ -67,31 +69,53 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); } -class FieldOnDomain final : public GeometryFieldInput { +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom2 = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +class InterpolateDomain final : public bke::GeometryFieldInput { private: GField src_field_; eAttrDomain src_domain_; public: - FieldOnDomain(GField field, eAttrDomain domain) - : GeometryFieldInput(field.cpp_type(), "Field on Domain"), + InterpolateDomain(GField field, eAttrDomain domain) + : bke::GeometryFieldInput(field.cpp_type(), "Interpolate Domain"), src_field_(std::move(field)), src_domain_(domain) { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask /* mask */) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + IndexMask /*mask*/) const final { - const GeometryComponentFieldContext context{component, src_domain_}; - const int64_t src_domain_size = component.attribute_domain_size(src_domain_); + const bke::AttributeAccessor attributes = *context.attributes(); + + const bke::GeometryFieldContext other_domain_context{ + context.geometry(), context.type(), src_domain_}; + const int64_t src_domain_size = attributes.domain_size(src_domain_); GArray values(src_field_.cpp_type(), src_domain_size); - FieldEvaluator value_evaluator{context, src_domain_size}; + FieldEvaluator value_evaluator{other_domain_context, src_domain_size}; value_evaluator.add_with_destination(src_field_, values.as_mutable_span()); value_evaluator.evaluate(); - return component.attributes()->adapt_domain( - GVArray::ForGArray(std::move(values)), src_domain_, domain); + return attributes.adapt_domain( + GVArray::ForGArray(std::move(values)), src_domain_, context.domain()); + } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return src_domain_; } }; @@ -117,31 +141,33 @@ static StringRefNull identifier_suffix(eCustomDataType data_type) static void node_geo_exec(GeoNodeExecParams params) { const bNode &node = params.node(); - const eAttrDomain domain = static_cast<eAttrDomain>(node.custom1); - const eCustomDataType data_type = static_cast<eCustomDataType>(node.custom2); + const eAttrDomain domain = eAttrDomain(node.custom1); + const eCustomDataType data_type = eCustomDataType(node.custom2); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); static const std::string identifier = "Value_" + identifier_suffix(data_type); Field<T> src_field = params.extract_input<Field<T>>(identifier); - Field<T> dst_field{std::make_shared<FieldOnDomain>(std::move(src_field), domain)}; + Field<T> dst_field{std::make_shared<InterpolateDomain>(std::move(src_field), domain)}; params.set_output(identifier, std::move(dst_field)); }); } -} // namespace blender::nodes::node_geo_field_on_domain_cc +} // namespace blender::nodes::node_geo_interpolate_domain_cc -void register_node_type_geo_field_on_domain() +void register_node_type_geo_interpolate_domain() { - namespace file_ns = blender::nodes::node_geo_field_on_domain_cc; + namespace file_ns = blender::nodes::node_geo_interpolate_domain_cc; static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_FIELD_ON_DOMAIN, "Field on Domain", NODE_CLASS_CONVERTER); + geo_node_type_base( + &ntype, GEO_NODE_INTERPOLATE_DOMAIN, "Interpolate Domain", NODE_CLASS_CONVERTER); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; ntype.draw_buttons = file_ns::node_layout; ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 083a505539a..ea2646a9786 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -2,6 +2,8 @@ #include "GEO_realize_instances.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_join_geometry_cc { @@ -29,6 +31,9 @@ static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info( if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } info.add_or_modify( attribute_id, [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, @@ -97,40 +102,44 @@ static void join_attributes(Span<const GeometryComponent *> src_components, static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result) { - InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); + std::unique_ptr<bke::Instances> dst_instances = std::make_unique<bke::Instances>(); int tot_instances = 0; for (const InstancesComponent *src_component : src_components) { - tot_instances += src_component->instances_num(); + tot_instances += src_component->get_for_read()->instances_num(); } - dst_component.reserve(tot_instances); + dst_instances->reserve(tot_instances); for (const InstancesComponent *src_component : src_components) { - Span<InstanceReference> src_references = src_component->references(); + const bke::Instances &src_instances = *src_component->get_for_read(); + + Span<bke::InstanceReference> src_references = src_instances.references(); Array<int> handle_map(src_references.size()); for (const int src_handle : src_references.index_range()) { - handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]); + handle_map[src_handle] = dst_instances->add_reference(src_references[src_handle]); } - Span<float4x4> src_transforms = src_component->instance_transforms(); - Span<int> src_reference_handles = src_component->instance_reference_handles(); + Span<float4x4> src_transforms = src_instances.transforms(); + Span<int> src_reference_handles = src_instances.reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - dst_component.add_instance(dst_handle, transform); + dst_instances->add_instance(dst_handle, transform); } } + + result.replace_instances(dst_instances.release()); + InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); join_attributes(to_base_components(src_components), dst_component, {"position"}); } -static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result) +static void join_components(Span<const VolumeComponent *> /*src_components*/, + 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<VolumeComponent>(); - UNUSED_VARS(src_components, dst_component); } template<typename Component> @@ -152,32 +161,30 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet return; } - GeometrySet instances_geometry_set; - InstancesComponent &instances = - instances_geometry_set.get_component_for_write<InstancesComponent>(); - if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) { join_components(components, result); } else { + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); for (const Component *component : components) { GeometrySet tmp_geo; tmp_geo.add(*component); - const int handle = instances.add_reference(InstanceReference{tmp_geo}); - instances.add_instance(handle, float4x4::identity()); + const int handle = instances->add_reference(bke::InstanceReference{tmp_geo}); + instances->add_instance(handle, float4x4::identity()); } geometry::RealizeInstancesOptions options; options.keep_original_ids = true; options.realize_instance_attributes = false; - GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options); + GeometrySet joined_components = geometry::realize_instances( + GeometrySet::create_with_instances(instances.release()), options); result.add(joined_components.get_component_for_write<Component>()); } } static void node_geo_exec(GeoNodeExecParams params) { - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry"); + Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry"); GeometrySet geometry_set_result; join_component_type<MeshComponent>(geometry_sets, geometry_set_result); @@ -185,6 +192,7 @@ static void node_geo_exec(GeoNodeExecParams params) join_component_type<InstancesComponent>(geometry_sets, geometry_set_result); join_component_type<VolumeComponent>(geometry_sets, geometry_set_result); join_component_type<CurveComponent>(geometry_sets, geometry_set_result); + join_component_type<GeometryComponentEditData>(geometry_sets, geometry_set_result); params.set_output("Geometry", std::move(geometry_set_result)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index ca613ae009b..dfb4181926e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -23,46 +23,56 @@ static void node_declare(NodeDeclarationBuilder &b) static void select_mesh_by_material(const Mesh &mesh, const Material *material, const IndexMask mask, - const MutableSpan<bool> r_selection) + MutableSpan<bool> r_selection) { BLI_assert(mesh.totpoly >= r_selection.size()); - Vector<int> material_indices; + Vector<int> slots; for (const int i : IndexRange(mesh.totcol)) { if (mesh.mat[i] == material) { - material_indices.append(i); + slots.append(i); } } + const AttributeAccessor attributes = mesh.attributes(); + const VArray<int> material_indices = attributes.lookup_or_default<int>( + "material_index", ATTR_DOMAIN_FACE, 0); + if (material != nullptr && material_indices.is_single() && + material_indices.get_internal_single() == 0) { + r_selection.fill_indices(mask, false); + return; + } + + const VArraySpan<int> material_indices_span(material_indices); + threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { const int face_index = mask[i]; - r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr); + r_selection[i] = slots.contains(material_indices_span[face_index]); } }); } -class MaterialSelectionFieldInput final : public GeometryFieldInput { +class MaterialSelectionFieldInput final : public bke::GeometryFieldInput { Material *material_; public: MaterialSelectionFieldInput(Material *material) - : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material) + : bke::GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), + material_(material) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask mask) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { + if (context.type() != GEO_COMPONENT_TYPE_MESH) { return {}; } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = context.mesh(); if (mesh == nullptr) { return {}; } - + const eAttrDomain domain = context.domain(); if (domain == ATTR_DOMAIN_FACE) { Array<bool> selection(mask.min_array_size()); select_mesh_by_material(*mesh, material_, mask, selection); @@ -71,7 +81,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput { Array<bool> selection(mesh->totpoly); select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); - return mesh_component.attributes()->adapt_domain<bool>( + return mesh->attributes().adapt_domain<bool>( VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain); return nullptr; @@ -90,6 +100,12 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index a4fb79bef7a..ce8b078f195 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" +#include "DNA_pointcloud_types.h" + #include "GEO_mesh_merge_by_distance.hh" #include "GEO_point_merge_by_distance.hh" @@ -21,27 +24,26 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMergeByDistance *data = MEM_cnew<NodeGeometryMergeByDistance>(__func__); data->mode = GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL; node->storage = data; } -static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points, +static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points, const float merge_distance, const Field<bool> &selection_field) { - const int src_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); - GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_num}; + bke::PointCloudFieldContext context{src_points}; + FieldEvaluator evaluator{context, src_points.totpoint}; evaluator.add(selection_field); evaluator.evaluate(); @@ -50,31 +52,28 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p return nullptr; } - return geometry::point_merge_by_distance(*src_points.get_for_read(), merge_distance, selection); + return geometry::point_merge_by_distance(src_points, merge_distance, selection); } -static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component, +static std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh, const float merge_distance, const Field<bool> &selection_field) { - const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - Array<bool> selection(src_num); - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_num}; + Array<bool> selection(mesh.totvert); + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add_with_destination(selection_field, selection.as_mutable_span()); evaluator.evaluate(); - const Mesh &mesh = *mesh_component.get_for_read(); return geometry::mesh_merge_by_distance_connected(mesh, selection, merge_distance, false); } -static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mesh_component, +static std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh, const float merge_distance, const Field<bool> &selection_field) { - const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_num}; + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add(selection_field); evaluator.evaluate(); @@ -83,7 +82,6 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes return std::nullopt; } - const Mesh &mesh = *mesh_component.get_for_read(); return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance); } @@ -98,22 +96,20 @@ static void node_geo_exec(GeoNodeExecParams params) const float merge_distance = params.extract_input<float>("Distance"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_pointcloud()) { - PointCloud *result = pointcloud_merge_by_distance( - *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection); + if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) { + PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection); if (result) { geometry_set.replace_pointcloud(result); } } - if (geometry_set.has_mesh()) { - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { std::optional<Mesh *> result; switch (mode) { case GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL: - result = mesh_merge_by_distance_all(component, merge_distance, selection); + result = mesh_merge_by_distance_all(*mesh, merge_distance, selection); break; case GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED: - result = mesh_merge_by_distance_connected(component, merge_distance, selection); + result = mesh_merge_by_distance_connected(*mesh, merge_distance, selection); break; default: BLI_assert_unreachable(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc new file mode 100644 index 00000000000..1b9852cf7b9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Face Set")) + .default_value(0) + .hide_value() + .supports_field() + .description(N_("An identifier for the group of each face. All contiguous faces with the " + "same value are in the same region")); + b.add_output<decl::Bool>(N_("Boundary Edges")) + .field_source() + .description(N_("The edges that lie on the boundaries between the different face sets")); +} + +class BoundaryFieldInput final : public bke::MeshFieldInput { + private: + const Field<int> face_set; + + public: + BoundaryFieldInput(const Field<int> face_set) + : bke::MeshFieldInput(CPPType::get<bool>(), "Boundary Field"), face_set(face_set) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + const bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; + FieldEvaluator face_evaluator{face_context, mesh.totpoly}; + face_evaluator.add(face_set); + face_evaluator.evaluate(); + const VArray<int> face_set = face_evaluator.get_evaluated<int>(0); + + Array<bool> boundary(mesh.totedge, false); + Array<bool> edge_visited(mesh.totedge, false); + Array<int> edge_face_set(mesh.totedge, 0); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + const int edge = loop.e; + if (edge_visited[edge]) { + if (edge_face_set[edge] != face_set[i]) { + /* This edge is connected to two faces on different face sets. */ + boundary[edge] = true; + } + } + edge_visited[edge] = true; + edge_face_set[edge] = face_set[i]; + } + } + return mesh.attributes().adapt_domain<bool>( + VArray<bool>::ForContainer(std::move(boundary)), ATTR_DOMAIN_EDGE, domain); + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_EDGE; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> face_set_field = params.extract_input<Field<int>>("Face Set"); + Field<bool> face_set_boundaries{std::make_shared<BoundaryFieldInput>(face_set_field)}; + params.set_output("Boundary Edges", std::move(face_set_boundaries)); +} + +} // namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc + +void register_node_type_geo_mesh_face_set_boundaries() +{ + namespace file_ns = blender::nodes::node_geo_mesh_face_set_boundaries_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_FACE_SET_BOUNDARIES, "Face Set Boundaries", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 9e85547315c..31a3e967905 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -29,14 +29,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMeshCircle *node_storage = MEM_cnew<NodeGeometryMeshCircle>(__func__); @@ -109,13 +109,13 @@ static Mesh *create_circle_mesh(const float radius, circle_corner_total(fill_type, verts_num), circle_face_total(fill_type, verts_num)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); /* Assign vertex coordinates. */ - const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num)); + const float angle_delta = 2.0f * (M_PI / float(verts_num)); for (const int i : IndexRange(verts_num)) { const float angle = i * angle_delta; copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index cb79ef93de9..4a9264b8464 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -255,10 +255,10 @@ int ConeConfig::calculate_total_corners() return corner_total; } -static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config) +static void calculate_cone_verts(const MutableSpan<MVert> &verts, const ConeConfig &config) { Array<float2> circle(config.circle_segments); - const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments)); + const float angle_delta = 2.0f * (M_PI / float(config.circle_segments)); float angle = 0.0f; for (const int i : IndexRange(config.circle_segments)) { circle[i].x = std::cos(angle); @@ -275,8 +275,7 @@ static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeC /* Top fill including the outer edge of the fill. */ if (!config.top_is_point) { - const float top_fill_radius_delta = config.radius_top / - static_cast<float>(config.fill_segments); + const float top_fill_radius_delta = config.radius_top / float(config.fill_segments); for (const int i : IndexRange(config.fill_segments)) { const float top_fill_radius = top_fill_radius_delta * (i + 1); for (const int j : IndexRange(config.circle_segments)) { @@ -289,8 +288,8 @@ static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeC /* Rings along the side. */ const float side_radius_delta = (config.radius_bottom - config.radius_top) / - static_cast<float>(config.side_segments); - const float height_delta = 2.0f * config.height / static_cast<float>(config.side_segments); + float(config.side_segments); + const float height_delta = 2.0f * config.height / float(config.side_segments); for (const int i : IndexRange(config.side_segments - 1)) { const float ring_radius = config.radius_top + (side_radius_delta * (i + 1)); const float ring_height = config.height - (height_delta * (i + 1)); @@ -303,8 +302,7 @@ static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeC /* Bottom fill including the outer edge of the fill. */ if (!config.bottom_is_point) { - const float bottom_fill_radius_delta = config.radius_bottom / - static_cast<float>(config.fill_segments); + const float bottom_fill_radius_delta = config.radius_bottom / float(config.fill_segments); for (const int i : IndexRange(config.fill_segments)) { const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta); for (const int j : IndexRange(config.circle_segments)) { @@ -480,12 +478,12 @@ static void calculate_selection_outputs(Mesh *mesh, const ConeConfig &config, ConeAttributeOutputs &attribute_outputs) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); /* Populate "Top" selection output. */ if (attribute_outputs.top_id) { const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; - SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( + SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>( attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); if (config.top_is_point) { @@ -501,7 +499,7 @@ static void calculate_selection_outputs(Mesh *mesh, if (attribute_outputs.bottom_id) { const bool face = !config.bottom_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; - SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( + SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>( attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); if (config.bottom_is_point) { @@ -518,7 +516,7 @@ static void calculate_selection_outputs(Mesh *mesh, /* Populate "Side" selection output. */ if (attribute_outputs.side_id) { - SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( + SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>( attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE); selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true); @@ -536,7 +534,7 @@ static void calculate_selection_outputs(Mesh *mesh, */ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -544,7 +542,7 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) Array<float2> circle(config.circle_segments); float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast<float>(config.circle_segments); + const float angle_delta = 2.0f * M_PI / float(config.circle_segments); for (const int i : IndexRange(config.circle_segments)) { circle[i].x = std::cos(angle) * 0.225f; circle[i].y = std::sin(angle) * 0.225f; @@ -556,9 +554,8 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) /* Left circle of the UV representing the top fill or top cone tip. */ if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { const float2 center_left(0.25f, 0.25f); - const float radius_factor_delta = 1.0f / (config.top_is_point ? - static_cast<float>(config.side_segments) : - static_cast<float>(config.fill_segments)); + const float radius_factor_delta = 1.0f / (config.top_is_point ? float(config.side_segments) : + float(config.fill_segments)); const int left_circle_segment_count = config.top_is_point ? config.side_segments : config.fill_segments; @@ -595,8 +592,8 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) if (!config.top_is_point && !config.bottom_is_point) { /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */ const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; - const float x_delta = 1.0f / static_cast<float>(config.circle_segments); - const float y_delta = (1.0f - bottom) / static_cast<float>(config.side_segments); + const float x_delta = 1.0f / float(config.circle_segments); + const float y_delta = (1.0f - bottom) / float(config.side_segments); for (const int i : IndexRange(config.side_segments)) { for (const int j : IndexRange(config.circle_segments)) { @@ -612,8 +609,8 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { const float2 center_right(0.75f, 0.25f); const float radius_factor_delta = 1.0f / (config.bottom_is_point ? - static_cast<float>(config.side_segments) : - static_cast<float>(config.fill_segments)); + float(config.side_segments) : + float(config.fill_segments)); const int right_circle_segment_count = config.bottom_is_point ? config.side_segments : config.fill_segments; @@ -657,7 +654,7 @@ static Mesh *create_vertex_mesh() { /* Returns a mesh with a single vertex at the origin. */ Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0); - copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f); + copy_v3_fl3(mesh->verts_for_write().first().co, 0.0f, 0.0f, 0.0f); return mesh; } @@ -679,7 +676,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, if (config.height == 0.0f) { return create_vertex_mesh(); } - const float z_delta = -2.0f * config.height / static_cast<float>(config.side_segments); + const float z_delta = -2.0f * config.height / float(config.side_segments); const float3 start(0.0f, 0.0f, config.height); const float3 delta(0.0f, 0.0f, z_delta); return create_line_mesh(start, delta, config.tot_verts); @@ -689,12 +686,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); - calculate_cone_vertices(verts, config); + calculate_cone_verts(verts, config); calculate_cone_edges(edges, config); calculate_cone_faces(loops, polys, config); calculate_cone_uvs(mesh, config); @@ -746,7 +743,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Side")).field_source(); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMeshCone *node_storage = MEM_cnew<NodeGeometryMeshCone>(__func__); @@ -757,7 +754,7 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *vertices_socket = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *rings_socket = vertices_socket->next; bNodeSocket *fill_subdiv_socket = rings_socket->next; @@ -767,7 +764,7 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 301d46e586f..f192b385b1b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -48,14 +48,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>(N_("Bottom")).field_source(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMeshCylinder *node_storage = MEM_cnew<NodeGeometryMeshCylinder>(__func__); @@ -66,7 +66,7 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *vertices_socket = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *rings_socket = vertices_socket->next; bNodeSocket *fill_subdiv_socket = rings_socket->next; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 9baf0b3171e..6f0b8283b72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -18,7 +18,7 @@ namespace blender::nodes { static void calculate_uvs( Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -49,10 +49,10 @@ Mesh *create_grid_mesh(const int verts_x, 0, edges_x * edges_y * 4, edges_x * edges_y); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); { const float dx = edges_x == 0 ? 0.0f : size_x / edges_x; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index aa9a2e9013f..8287c6a6714 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -47,7 +47,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) BMeshToMeshParams params{}; params.calc_object_remap = false; - Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); + Mesh *mesh = reinterpret_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr)); BKE_id_material_eval_ensure_default_slot(&mesh->id); BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); BM_mesh_free(bm); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 2af86b4b1d2..7faa7e23274 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -43,7 +43,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -53,7 +53,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) } } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMeshLine *node_storage = MEM_cnew<NodeGeometryMeshLine>(__func__); @@ -65,7 +65,7 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *count_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *count_socket = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *resolution_socket = count_socket->next; bNodeSocket *start_socket = resolution_socket->next; bNodeSocket *end_and_offset_socket = start_socket->next; @@ -97,7 +97,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) return; } else if (params.node_tree().typeinfo->validate_link( - static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + eNodeSocketDatatype(params.other_socket().type), SOCK_FLOAT)) { params.add_item(IFACE_("Count"), [](LinkSearchOpParams ¶ms) { bNode &node = params.add_node("GeometryNodeMeshLine"); node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET; @@ -153,7 +153,7 @@ static void node_geo_exec(GeoNodeExecParams params) mesh = create_line_mesh(start, float3(0), count); } else { - const float3 delta = total_delta / (float)(count - 1); + const float3 delta = total_delta / float(count - 1); mesh = create_line_mesh(start, delta, count); } } @@ -179,10 +179,11 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); threading::parallel_invoke( + 1024 < count, [&]() { threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { for (const int i : range) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index f78752387c6..4b43693f0f6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -61,15 +63,26 @@ static int sphere_face_total(const int segments, const int rings) * Also calculate vertex normals here, since the calculation is trivial, and it allows avoiding the * calculation later, if it's necessary. The vertex normals are just the normalized positions. */ -static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, - MutableSpan<float3> vert_normals, - const float radius, - const int segments, - const int rings) +BLI_NOINLINE static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, + MutableSpan<float3> vert_normals, + const float radius, + const int segments, + const int rings) { const float delta_theta = M_PI / rings; const float delta_phi = (2.0f * M_PI) / segments; + Array<float, 64> segment_cosines(segments + 1); + for (const int segment : IndexRange(1, segments)) { + const float phi = segment * delta_phi; + segment_cosines[segment] = std::cos(phi); + } + Array<float, 64> segment_sines(segments + 1); + for (const int segment : IndexRange(1, segments)) { + const float phi = segment * delta_phi; + segment_sines[segment] = std::sin(phi); + } + copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius)); vert_normals.first() = float3(0.0f, 0.0f, 1.0f); @@ -79,9 +92,8 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, const float sin_theta = std::sin(theta); const float z = std::cos(theta); for (const int segment : IndexRange(1, segments)) { - const float phi = segment * delta_phi; - const float x = sin_theta * std::cos(phi); - const float y = sin_theta * std::sin(phi); + const float x = sin_theta * segment_cosines[segment]; + const float y = sin_theta * segment_sines[segment]; copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); vert_normals[vert_index] = float3(x, y, z); vert_index++; @@ -92,9 +104,9 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, vert_normals.last() = float3(0.0f, 0.0f, -1.0f); } -static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, - const int segments, - const int rings) +BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, + const int segments, + const int rings) { int edge_index = 0; @@ -142,20 +154,46 @@ static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, } } -static void calculate_sphere_faces(MutableSpan<MLoop> loops, - MutableSpan<MPoly> polys, - const int segments, - const int rings) +BLI_NOINLINE static void calculate_sphere_faces(MutableSpan<MPoly> polys, const int segments) { int loop_index = 0; - int poly_index = 0; /* Add the triangles connected to the top vertex. */ - const int first_vert_ring_index_start = 1; - for (const int segment : IndexRange(segments)) { - MPoly &poly = polys[poly_index++]; + for (MPoly &poly : polys.take_front(segments)) { + poly.loopstart = loop_index; + poly.totloop = 3; + loop_index += 3; + } + + /* Add the middle quads. */ + for (MPoly &poly : polys.drop_front(segments).drop_back(segments)) { + poly.loopstart = loop_index; + poly.totloop = 4; + loop_index += 4; + } + + /* Add the triangles connected to the bottom vertex. */ + for (MPoly &poly : polys.take_back(segments)) { poly.loopstart = loop_index; poly.totloop = 3; + loop_index += 3; + } +} + +BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops, + const int segments, + const int rings) +{ + int loop_index = 0; + auto segment_next_or_first = [&](const int segment) { + return segment == segments - 1 ? 0 : segment + 1; + }; + + /* Add the triangles connected to the top vertex. */ + const int first_vert_ring_index_start = 1; + for (const int segment : IndexRange(segments)) { + const int segment_next = segment_next_or_first(segment); + MLoop &loop_a = loops[loop_index++]; loop_a.v = 0; loop_a.e = segment; @@ -163,8 +201,8 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops, loop_b.v = first_vert_ring_index_start + segment; loop_b.e = segments + segment; MLoop &loop_c = loops[loop_index++]; - loop_c.v = first_vert_ring_index_start + (segment + 1) % segments; - loop_c.e = (segment + 1) % segments; + loop_c.v = first_vert_ring_index_start + segment_next; + loop_c.e = segment_next; } int ring_vert_index_start = 1; @@ -175,9 +213,7 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops, const int ring_vertical_edge_index_start = ring_edge_index_start + segments; for (const int segment : IndexRange(segments)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 4; + const int segment_next = segment_next_or_first(segment); MLoop &loop_a = loops[loop_index++]; loop_a.v = ring_vert_index_start + segment; @@ -186,10 +222,10 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops, loop_b.v = next_ring_vert_index_start + segment; loop_b.e = next_ring_edge_index_start + segment; MLoop &loop_c = loops[loop_index++]; - loop_c.v = next_ring_vert_index_start + (segment + 1) % segments; - loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments; + loop_c.v = next_ring_vert_index_start + segment_next; + loop_c.e = ring_vertical_edge_index_start + segment_next; MLoop &loop_d = loops[loop_index++]; - loop_d.v = ring_vert_index_start + (segment + 1) % segments; + loop_d.v = ring_vert_index_start + segment_next; loop_d.e = ring_edge_index_start + segment; } ring_vert_index_start += segments; @@ -202,15 +238,13 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops, const int last_vert_index = sphere_vert_total(segments, rings) - 1; const int last_vert_ring_start = last_vert_index - segments; for (const int segment : IndexRange(segments)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; + const int segment_next = segment_next_or_first(segment); MLoop &loop_a = loops[loop_index++]; loop_a.v = last_vert_index; - loop_a.e = bottom_edge_fan_start + (segment + 1) % segments; + loop_a.e = bottom_edge_fan_start + segment_next; MLoop &loop_b = loops[loop_index++]; - loop_b.v = last_vert_ring_start + (segment + 1) % segments; + loop_b.v = last_vert_ring_start + segment_next; loop_b.e = last_edge_ring_start + segment; MLoop &loop_c = loops[loop_index++]; loop_c.v = last_vert_ring_start + segment; @@ -218,9 +252,9 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops, } } -static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings) +BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -229,29 +263,31 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r int loop_index = 0; const float dy = 1.0f / rings; + const float segments_inv = 1.0f / segments; + for (const int i_segment : IndexRange(segments)) { - const float segment = static_cast<float>(i_segment); - uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f); - uvs[loop_index++] = float2(segment / segments, dy); - uvs[loop_index++] = float2((segment + 1.0f) / segments, dy); + const float segment = float(i_segment); + uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 0.0f); + uvs[loop_index++] = float2(segment * segments_inv, dy); + uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, dy); } for (const int i_ring : IndexRange(1, rings - 2)) { - const float ring = static_cast<float>(i_ring); + const float ring = float(i_ring); for (const int i_segment : IndexRange(segments)) { - const float segment = static_cast<float>(i_segment); - uvs[loop_index++] = float2(segment / segments, ring / rings); - uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings); - uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings); - uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings); + const float segment = float(i_segment); + uvs[loop_index++] = float2(segment * segments_inv, ring / rings); + uvs[loop_index++] = float2(segment * segments_inv, (ring + 1.0f) / rings); + uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, (ring + 1.0f) / rings); + uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, ring / rings); } } for (const int i_segment : IndexRange(segments)) { - const float segment = static_cast<float>(i_segment); - uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f); - uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy); - uvs[loop_index++] = float2(segment / segments, 1.0f - dy); + const float segment = float(i_segment); + uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 1.0f); + uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, 1.0f - dy); + uvs[loop_index++] = float2(segment * segments_inv, 1.0f - dy); } uv_attribute.finish(); @@ -265,20 +301,23 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const sphere_corner_total(segments, rings), sphere_face_total(segments, rings)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; - - MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert}; - calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings); - BKE_mesh_vertex_normals_clear_dirty(mesh); - - calculate_sphere_edge_indices(edges, segments, rings); - - calculate_sphere_faces(loops, polys, segments, rings); - - calculate_sphere_uvs(mesh, segments, rings); + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); + + threading::parallel_invoke( + 1024 < segments * rings, + [&]() { + MutableSpan vert_normals{ + reinterpret_cast<float3 *>(BKE_mesh_vertex_normals_for_write(mesh)), mesh->totvert}; + calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings); + BKE_mesh_vertex_normals_clear_dirty(mesh); + }, + [&]() { calculate_sphere_edge_indices(edges, segments, rings); }, + [&]() { calculate_sphere_faces(polys, segments); }, + [&]() { calculate_sphere_corners(loops, segments, rings); }, + [&]() { calculate_sphere_uvs(mesh, segments, rings); }); return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 40169def51e..4d08fa40a29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" + #include "GEO_mesh_to_curve.hh" #include "node_geometry_util.hh" @@ -24,9 +26,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator evaluator{context, mesh->totedge}; evaluator.add(params.get_input<Field<bool>>("Selection")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index d3d1312be6d..d97a0e72060 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_task.hh" +#include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" @@ -22,7 +24,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); - b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(implicit_field_inputs::position); b.add_input<decl::Float>(N_("Radius")) .default_value(0.05f) .min(0.0f) @@ -31,47 +33,36 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Points")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryMeshToPoints *data = MEM_cnew<NodeGeometryMeshToPoints>(__func__); data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES; node->storage = data; } -static void materialize_compressed_to_uninitialized_threaded(const GVArray &src, - const IndexMask mask, - GMutableSpan dst) -{ - BLI_assert(src.type() == dst.type()); - BLI_assert(mask.size() == dst.size()); - threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { - src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data()); - }); -} - static void geometry_set_mesh_to_points(GeometrySet &geometry_set, Field<float3> &position_field, Field<float> &radius_field, Field<bool> &selection_field, const eAttrDomain domain) { - const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>(); - if (mesh_component == nullptr) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh == nullptr) { geometry_set.remove_geometry_during_modify(); return; } - GeometryComponentFieldContext field_context{*mesh_component, domain}; - const int domain_num = mesh_component->attribute_domain_size(domain); - if (domain_num == 0) { + const int domain_size = mesh->attributes().domain_size(domain); + if (domain_size == 0) { geometry_set.remove_geometry_during_modify(); return; } - fn::FieldEvaluator evaluator{field_context, domain_num}; + bke::MeshFieldContext field_context{*mesh, domain}; + fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); /* Evaluating directly into the point cloud doesn't work because we are not using the full * "min_array_size" array but compressing the selected elements into the final array with no @@ -83,19 +74,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - MutableAttributeAccessor pointcloud_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write(); - GSpanAttributeWriter position = pointcloud_attributes.lookup_or_add_for_write_only_span( + GSpanAttributeWriter position = dst_attributes.lookup_or_add_for_write_only_span( "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - materialize_compressed_to_uninitialized_threaded( - evaluator.get_evaluated(0), selection, position.span); + array_utils::gather(evaluator.get_evaluated(0), selection, position.span); position.finish(); - GSpanAttributeWriter radius = pointcloud_attributes.lookup_or_add_for_write_only_span( + GSpanAttributeWriter radius = dst_attributes.lookup_or_add_for_write_only_span( "radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT); - materialize_compressed_to_uninitialized_threaded( - evaluator.get_evaluated(1), selection, radius.span); + array_utils::gather(evaluator.get_evaluated(1), selection, radius.span); radius.finish(); Map<AttributeIDRef, AttributeKind> attributes; @@ -103,14 +91,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); attributes.remove("position"); + const AttributeAccessor src_attributes = mesh->attributes(); + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const eCustomDataType data_type = entry.value.data_type; - GVArray src = mesh_component->attributes()->lookup_or_default(attribute_id, domain, data_type); - GSpanAttributeWriter dst = pointcloud_attributes.lookup_or_add_for_write_only_span( + GVArray src = src_attributes.lookup_or_default(attribute_id, domain, data_type); + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - materialize_compressed_to_uninitialized_threaded(src, selection, dst.span); + array_utils::gather(src, selection, dst.span); dst.finish(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc index 92814a8bc5e..8885903f828 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc @@ -50,33 +50,31 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Volume")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { - NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)MEM_callocN( - sizeof(NodeGeometryMeshToVolume), __func__); + NodeGeometryMeshToVolume *data = MEM_cnew<NodeGeometryMeshToVolume>(__func__); data->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT; node->storage = data; } static void node_update(bNodeTree *ntree, bNode *node) { - NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)node->storage; + NodeGeometryMeshToVolume &data = node_storage(*node); bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); nodeSetSocketAvailability(ntree, voxel_amount_socket, - data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT); - nodeSetSocketAvailability(ntree, - voxel_size_socket, - data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE); + data.resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability( + ntree, voxel_size_socket, data.resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE); } #ifdef WITH_OPENVDB @@ -126,7 +124,7 @@ static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams ¶ exterior_band_width, mesh_to_volume_space_transform); - Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr)); BKE_volume_init_grids(volume); /* Convert mesh to grid and add to volume. */ @@ -149,7 +147,6 @@ static void node_geo_exec(GeoNodeExecParams params) { #ifdef WITH_OPENVDB GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh")); - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_mesh()) { Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params); @@ -159,9 +156,9 @@ static void node_geo_exec(GeoNodeExecParams params) }); params.set_output("Volume", std::move(geometry_set)); #else + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_default_remaining_outputs(); return; #endif } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc new file mode 100644 index 00000000000..94bca02640b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_corners_of_face_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Face Index")) + .implicit_field(implicit_field_inputs::index) + .description(N_("The face to retrieve data from. Defaults to the face from the context")); + b.add_input<decl::Float>(N_("Weights")) + .supports_field() + .hide_value() + .description(N_("Values used to sort the face's corners. Uses indices by default")); + b.add_input<decl::Int>(N_("Sort Index")) + .min(0) + .supports_field() + .description(N_("Which of the sorted corners to output")); + b.add_output<decl::Int>(N_("Corner Index")) + .dependent_field() + .description(N_("A corner of the face, chosen by the sort index")); + b.add_output<decl::Int>(N_("Total")) + .dependent_field() + .description(N_("The number of corners in the face")); +} + +class CornersOfFaceInput final : public bke::MeshFieldInput { + const Field<int> face_index_; + const Field<int> sort_index_; + const Field<float> sort_weight_; + + public: + CornersOfFaceInput(Field<int> face_index, Field<int> sort_index, Field<float> sort_weight) + : bke::MeshFieldInput(CPPType::get<int>(), "Corner of Face"), + face_index_(std::move(face_index)), + sort_index_(std::move(sort_index)), + sort_weight_(std::move(sort_weight)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask mask) const final + { + const Span<MPoly> polys = mesh.polys(); + + const bke::MeshFieldContext context{mesh, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(face_index_); + evaluator.add(sort_index_); + evaluator.evaluate(); + const VArray<int> face_indices = evaluator.get_evaluated<int>(0); + const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1); + + const bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER}; + fn::FieldEvaluator corner_evaluator{corner_context, mesh.totloop}; + corner_evaluator.add(sort_weight_); + corner_evaluator.evaluate(); + const VArray<float> all_sort_weights = corner_evaluator.get_evaluated<float>(0); + + Array<int> corner_of_face(mask.min_array_size()); + threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { + /* Reuse arrays to avoid allocation. */ + Array<float> sort_weights; + Array<int> sort_indices; + + for (const int selection_i : mask.slice(range)) { + const int poly_i = face_indices[selection_i]; + const int index_in_sort = indices_in_sort[selection_i]; + if (!polys.index_range().contains(poly_i)) { + corner_of_face[selection_i] = 0; + continue; + } + + const MPoly &poly = polys[poly_i]; + const IndexRange corners(poly.loopstart, poly.totloop); + + /* Retrieve the weights for each corner. */ + sort_weights.reinitialize(corners.size()); + all_sort_weights.materialize_compressed(IndexMask(corners), + sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(corners.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { + return sort_weights[a] < sort_weights[b]; + }); + + const int index_in_sort_wrapped = mod_i(index_in_sort, corners.size()); + corner_of_face[selection_i] = corners[sort_indices[index_in_sort_wrapped]]; + } + }); + + return VArray<int>::ForContainer(std::move(corner_of_face)); + } + + uint64_t hash() const final + { + return 6927982716657; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (const auto *typed = dynamic_cast<const CornersOfFaceInput *>(&other)) { + return typed->face_index_ == face_index_ && typed->sort_index_ == sort_index_ && + typed->sort_weight_ == sort_weight_; + } + return false; + } +}; + +static int get_poly_totloop(const MPoly &poly) +{ + return poly.totloop; +} + +class CornersOfFaceCountInput final : public bke::MeshFieldInput { + public: + CornersOfFaceCountInput() : bke::MeshFieldInput(CPPType::get<int>(), "Face Corner Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_FACE) { + return {}; + } + return VArray<int>::ForDerivedSpan<MPoly, get_poly_totloop>(mesh.polys()); + } + + uint64_t hash() const final + { + return 8345908765432698; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CornersOfFaceCountInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> face_index = params.extract_input<Field<int>>("Face Index"); + if (params.output_is_required("Total")) { + params.set_output("Total", + Field<int>(std::make_shared<FieldAtIndexInput>( + face_index, + Field<int>(std::make_shared<CornersOfFaceCountInput>()), + ATTR_DOMAIN_FACE))); + } + if (params.output_is_required("Corner Index")) { + params.set_output("Corner Index", + Field<int>(std::make_shared<CornersOfFaceInput>( + face_index, + params.extract_input<Field<int>>("Sort Index"), + params.extract_input<Field<float>>("Weights")))); + } +} + +} // namespace blender::nodes::node_geo_mesh_topology_corners_of_face_cc + +void register_node_type_geo_mesh_topology_corners_of_face() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_corners_of_face_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_FACE, "Corners of Face", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc new file mode 100644 index 00000000000..036af2d3b93 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_corners_of_vertex_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Vertex Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The vertex to retrieve data from. Defaults to the vertex from the context")); + b.add_input<decl::Float>(N_("Weights")) + .supports_field() + .hide_value() + .description( + N_("Values used to sort corners attached to the vertex. Uses indices by default")); + b.add_input<decl::Int>(N_("Sort Index")) + .min(0) + .supports_field() + .description(N_("Which of the sorted corners to output")); + b.add_output<decl::Int>(N_("Corner Index")) + .dependent_field() + .description(N_("A corner connected to the face, chosen by the sort index")); + b.add_output<decl::Int>(N_("Total")) + .dependent_field() + .description(N_("The number of faces or corners connected to each vertex")); +} + +static void convert_span(const Span<int> src, MutableSpan<int64_t> dst) +{ + for (const int i : src.index_range()) { + dst[i] = src[i]; + } +} + +class CornersOfVertInput final : public bke::MeshFieldInput { + const Field<int> vert_index_; + const Field<int> sort_index_; + const Field<float> sort_weight_; + + public: + CornersOfVertInput(Field<int> vert_index, Field<int> sort_index, Field<float> sort_weight) + : bke::MeshFieldInput(CPPType::get<int>(), "Corner of Vertex"), + vert_index_(std::move(vert_index)), + sort_index_(std::move(sort_index)), + sort_weight_(std::move(sort_weight)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask mask) const final + { + const IndexRange vert_range(mesh.totvert); + const Span<MLoop> loops = mesh.loops(); + Array<Vector<int>> vert_to_loop_map = bke::mesh_topology::build_vert_to_loop_map(loops, + mesh.totvert); + + const bke::MeshFieldContext context{mesh, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(vert_index_); + evaluator.add(sort_index_); + evaluator.evaluate(); + const VArray<int> vert_indices = evaluator.get_evaluated<int>(0); + const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1); + + const bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER}; + fn::FieldEvaluator corner_evaluator{corner_context, loops.size()}; + corner_evaluator.add(sort_weight_); + corner_evaluator.evaluate(); + const VArray<float> all_sort_weights = corner_evaluator.get_evaluated<float>(0); + + Array<int> corner_of_vertex(mask.min_array_size()); + threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { + /* Reuse arrays to avoid allocation. */ + Array<int64_t> corner_indices; + Array<float> sort_weights; + Array<int> sort_indices; + + for (const int selection_i : mask.slice(range)) { + const int vert_i = vert_indices[selection_i]; + const int index_in_sort = indices_in_sort[selection_i]; + if (!vert_range.contains(vert_i)) { + corner_of_vertex[selection_i] = 0; + continue; + } + + const Span<int> corners = vert_to_loop_map[vert_i]; + if (corners.is_empty()) { + corner_of_vertex[selection_i] = 0; + continue; + } + + /* Retrieve the connected edge indices as 64 bit integers for #materialize_compressed. */ + corner_indices.reinitialize(corners.size()); + convert_span(corners, corner_indices); + + /* Retrieve a compressed array of weights for each edge. */ + sort_weights.reinitialize(corners.size()); + all_sort_weights.materialize_compressed(IndexMask(corner_indices), + sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(corners.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { + return sort_weights[a] < sort_weights[b]; + }); + + const int index_in_sort_wrapped = mod_i(index_in_sort, corners.size()); + corner_of_vertex[selection_i] = corner_indices[sort_indices[index_in_sort_wrapped]]; + } + }); + + return VArray<int>::ForContainer(std::move(corner_of_vertex)); + } + + uint64_t hash() const final + { + return 3541871368173645; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (const auto *typed = dynamic_cast<const CornersOfVertInput *>(&other)) { + return typed->vert_index_ == vert_index_ && typed->sort_index_ == sort_index_ && + typed->sort_weight_ == sort_weight_; + } + return false; + } +}; + +class CornersOfVertCountInput final : public bke::MeshFieldInput { + public: + CornersOfVertCountInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Corner Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_POINT) { + return {}; + } + const Span<MLoop> loops = mesh.loops(); + Array<int> counts(mesh.totvert, 0); + for (const int i : loops.index_range()) { + counts[loops[i].v]++; + } + return VArray<int>::ForContainer(std::move(counts)); + } + + uint64_t hash() const final + { + return 253098745374645; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CornersOfVertCountInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> vert_index = params.extract_input<Field<int>>("Vertex Index"); + if (params.output_is_required("Total")) { + params.set_output("Total", + Field<int>(std::make_shared<FieldAtIndexInput>( + vert_index, + Field<int>(std::make_shared<CornersOfVertCountInput>()), + ATTR_DOMAIN_POINT))); + } + if (params.output_is_required("Corner Index")) { + params.set_output("Corner Index", + Field<int>(std::make_shared<CornersOfVertInput>( + vert_index, + params.extract_input<Field<int>>("Sort Index"), + params.extract_input<Field<float>>("Weights")))); + } +} +} // namespace blender::nodes::node_geo_mesh_topology_corners_of_vertex_cc + +void register_node_type_geo_mesh_topology_corners_of_vertex() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_corners_of_vertex_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_TOPOLOGY_CORNERS_OF_VERTEX, "Corners of Vertex", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc new file mode 100644 index 00000000000..84b560cb48a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_edges_of_corner_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Corner Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The corner to retrieve data from. Defaults to the corner from the context")); + b.add_output<decl::Int>(N_("Next Edge Index")) + .dependent_field() + .description( + N_("The edge after the corner in the face, in the direction of increasing indices")); + b.add_output<decl::Int>(N_("Previous Edge Index")) + .dependent_field() + .description( + N_("The edge before the corner in the face, in the direction of decreasing indices")); +} + +static int get_loop_edge(const MLoop &loop) +{ + return loop.e; +} + +class CornerNextEdgeFieldInput final : public bke::MeshFieldInput { + public: + CornerNextEdgeFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Next Edge") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_CORNER) { + return {}; + } + return VArray<int>::ForDerivedSpan<MLoop, get_loop_edge>(mesh.loops()); + } + + uint64_t hash() const final + { + return 1892753404495; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CornerNextEdgeFieldInput *>(&other)) { + return true; + } + return false; + } +}; + +class CornerPreviousEdgeFieldInput final : public bke::MeshFieldInput { + public: + CornerPreviousEdgeFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Previous Edge") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_CORNER) { + return {}; + } + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); + return VArray<int>::ForFunc( + mesh.totloop, + [polys, loops, loop_to_poly_map = std::move(loop_to_poly_map)](const int corner_i) { + const int poly_i = loop_to_poly_map[corner_i]; + const MPoly &poly = polys[poly_i]; + const int corner_i_prev = bke::mesh_topology::previous_poly_loop(poly, corner_i); + return loops[corner_i_prev].e; + }); + } + + uint64_t hash() const final + { + return 987298345762465; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CornerPreviousEdgeFieldInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> corner_index = params.extract_input<Field<int>>("Corner Index"); + if (params.output_is_required("Next Edge Index")) { + params.set_output("Next Edge Index", + Field<int>(std::make_shared<FieldAtIndexInput>( + corner_index, + Field<int>(std::make_shared<CornerNextEdgeFieldInput>()), + ATTR_DOMAIN_CORNER))); + } + if (params.output_is_required("Previous Edge Index")) { + params.set_output("Previous Edge Index", + Field<int>(std::make_shared<FieldAtIndexInput>( + corner_index, + Field<int>(std::make_shared<CornerPreviousEdgeFieldInput>()), + ATTR_DOMAIN_CORNER))); + } +} + +} // namespace blender::nodes::node_geo_mesh_topology_edges_of_corner_cc + +void register_node_type_geo_mesh_topology_edges_of_corner() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_edges_of_corner_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_TOPOLOGY_EDGES_OF_CORNER, "Edges of Corner", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc new file mode 100644 index 00000000000..f0cc191e217 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_edges_of_vertex_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Vertex Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The vertex to retrieve data from. Defaults to the vertex from the context")); + b.add_input<decl::Float>(N_("Weights")) + .supports_field() + .hide_value() + .description( + N_("Values used to sort the edges connected to the vertex. Uses indices by default")); + b.add_input<decl::Int>(N_("Sort Index")) + .min(0) + .supports_field() + .description(N_("Which of the sorted edges to output")); + b.add_output<decl::Int>(N_("Edge Index")) + .dependent_field() + .description(N_("An edge connected to the face, chosen by the sort index")); + b.add_output<decl::Int>(N_("Total")) + .dependent_field() + .description(N_("The number of edges connected to each vertex")); +} + +static void convert_span(const Span<int> src, MutableSpan<int64_t> dst) +{ + for (const int i : src.index_range()) { + dst[i] = src[i]; + } +} + +class EdgesOfVertInput final : public bke::MeshFieldInput { + const Field<int> vert_index_; + const Field<int> sort_index_; + const Field<float> sort_weight_; + + public: + EdgesOfVertInput(Field<int> vert_index, Field<int> sort_index, Field<float> sort_weight) + : bke::MeshFieldInput(CPPType::get<int>(), "Edge of Vertex"), + vert_index_(std::move(vert_index)), + sort_index_(std::move(sort_index)), + sort_weight_(std::move(sort_weight)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask mask) const final + { + const IndexRange vert_range(mesh.totvert); + const Span<MEdge> edges = mesh.edges(); + Array<Vector<int>> vert_to_edge_map = bke::mesh_topology::build_vert_to_edge_map(edges, + mesh.totvert); + + const bke::MeshFieldContext context{mesh, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(vert_index_); + evaluator.add(sort_index_); + evaluator.evaluate(); + const VArray<int> vert_indices = evaluator.get_evaluated<int>(0); + const VArray<int> indices_in_sort = evaluator.get_evaluated<int>(1); + + const bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; + edge_evaluator.add(sort_weight_); + edge_evaluator.evaluate(); + const VArray<float> all_sort_weights = edge_evaluator.get_evaluated<float>(0); + + Array<int> edge_of_vertex(mask.min_array_size()); + threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { + /* Reuse arrays to avoid allocation. */ + Array<int64_t> edge_indices; + Array<float> sort_weights; + Array<int> sort_indices; + + for (const int selection_i : mask.slice(range)) { + const int vert_i = vert_indices[selection_i]; + const int index_in_sort = indices_in_sort[selection_i]; + if (!vert_range.contains(vert_i)) { + edge_of_vertex[selection_i] = 0; + continue; + } + + const Span<int> edges = vert_to_edge_map[vert_i]; + if (edges.is_empty()) { + edge_of_vertex[selection_i] = 0; + continue; + } + + /* Retrieve the connected edge indices as 64 bit integers for #materialize_compressed. */ + edge_indices.reinitialize(edges.size()); + convert_span(edges, edge_indices); + + /* Retrieve a compressed array of weights for each edge. */ + sort_weights.reinitialize(edges.size()); + all_sort_weights.materialize_compressed(IndexMask(edge_indices), + sort_weights.as_mutable_span()); + + /* Sort a separate array of compressed indices corresponding to the compressed weights. + * This allows using `materialize_compressed` to avoid virtual function call overhead + * when accessing values in the sort weights. However, it means a separate array of + * indices within the compressed array is necessary for sorting. */ + sort_indices.reinitialize(edges.size()); + std::iota(sort_indices.begin(), sort_indices.end(), 0); + std::stable_sort(sort_indices.begin(), sort_indices.end(), [&](int a, int b) { + return sort_weights[a] < sort_weights[b]; + }); + + const int index_in_sort_wrapped = mod_i(index_in_sort, edges.size()); + edge_of_vertex[selection_i] = edge_indices[sort_indices[index_in_sort_wrapped]]; + } + }); + + return VArray<int>::ForContainer(std::move(edge_of_vertex)); + } + + uint64_t hash() const final + { + return 98762349875636; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (const auto *typed = dynamic_cast<const EdgesOfVertInput *>(&other)) { + return typed->vert_index_ == vert_index_ && typed->sort_index_ == sort_index_ && + typed->sort_weight_ == sort_weight_; + } + return false; + } +}; + +class EdgesOfVertCountInput final : public bke::MeshFieldInput { + public: + EdgesOfVertCountInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Face Index") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_POINT) { + return {}; + } + const Span<MEdge> edges = mesh.edges(); + Array<int> counts(mesh.totvert, 0); + for (const int i : edges.index_range()) { + counts[edges[i].v1]++; + counts[edges[i].v2]++; + } + return VArray<int>::ForContainer(std::move(counts)); + } + + uint64_t hash() const final + { + return 436758278618374; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const EdgesOfVertCountInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> vert_index = params.extract_input<Field<int>>("Vertex Index"); + if (params.output_is_required("Total")) { + params.set_output("Total", + Field<int>(std::make_shared<FieldAtIndexInput>( + vert_index, + Field<int>(std::make_shared<EdgesOfVertCountInput>()), + ATTR_DOMAIN_POINT))); + } + if (params.output_is_required("Edge Index")) { + params.set_output("Edge Index", + Field<int>(std::make_shared<EdgesOfVertInput>( + vert_index, + params.extract_input<Field<int>>("Sort Index"), + params.extract_input<Field<float>>("Weights")))); + } +} + +} // namespace blender::nodes::node_geo_mesh_topology_edges_of_vertex_cc + +void register_node_type_geo_mesh_topology_edges_of_vertex() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_edges_of_vertex_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_TOPOLOGY_EDGES_OF_VERTEX, "Edges of Vertex", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc new file mode 100644 index 00000000000..d9f944ca11e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_face_of_corner_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Corner Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The corner to retrieve data from. Defaults to the corner from the context")); + b.add_output<decl::Int>(N_("Face Index")) + .dependent_field() + .description(N_("The index of the face the corner is a part of")); + b.add_output<decl::Int>(N_("Index in Face")) + .dependent_field() + .description(N_("The index of the corner starting from the first corner in the face")); +} + +class CornerFaceIndexInput final : public bke::MeshFieldInput { + public: + CornerFaceIndexInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Face Index") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_CORNER) { + return {}; + } + return VArray<int>::ForContainer( + bke::mesh_topology::build_loop_to_poly_map(mesh.polys(), mesh.totloop)); + } + + uint64_t hash() const final + { + return 2348712958475728; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + return dynamic_cast<const CornerFaceIndexInput *>(&other) != nullptr; + } +}; + +class CornerIndexInFaceInput final : public bke::MeshFieldInput { + public: + CornerIndexInFaceInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Index In Face") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_CORNER) { + return {}; + } + const Span<MPoly> polys = mesh.polys(); + Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); + return VArray<int>::ForFunc( + mesh.totloop, [polys, loop_to_poly_map = std::move(loop_to_poly_map)](const int corner_i) { + const int poly_i = loop_to_poly_map[corner_i]; + return corner_i - polys[poly_i].loopstart; + }); + } + + uint64_t hash() const final + { + return 97837176448; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CornerIndexInFaceInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> corner_index = params.extract_input<Field<int>>("Corner Index"); + if (params.output_is_required("Face Index")) { + params.set_output("Face Index", + Field<int>(std::make_shared<FieldAtIndexInput>( + corner_index, + Field<int>(std::make_shared<CornerFaceIndexInput>()), + ATTR_DOMAIN_CORNER))); + } + if (params.output_is_required("Index in Face")) { + params.set_output("Index in Face", + Field<int>(std::make_shared<FieldAtIndexInput>( + corner_index, + Field<int>(std::make_shared<CornerIndexInFaceInput>()), + ATTR_DOMAIN_CORNER))); + } +} + +} // namespace blender::nodes::node_geo_mesh_topology_face_of_corner_cc + +void register_node_type_geo_mesh_topology_face_of_corner() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_face_of_corner_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_TOPOLOGY_FACE_OF_CORNER, "Face of Corner", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc new file mode 100644 index 00000000000..2cb9ae82fa1 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_offset_corner_in_face_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Corner Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The corner to retrieve data from. Defaults to the corner from the context")); + b.add_input<decl::Int>(N_("Offset")) + .supports_field() + .description(N_("The number of corners to move around the face before finding the result, " + "circling around the start of the face if necessary")); + b.add_output<decl::Int>(N_("Corner Index")) + .dependent_field() + .description(N_("The index of the offset corner")); +} + +class OffsetCornerInFaceFieldInput final : public bke::MeshFieldInput { + const Field<int> corner_index_; + const Field<int> offset_; + + public: + OffsetCornerInFaceFieldInput(Field<int> corner_index, Field<int> offset) + : bke::MeshFieldInput(CPPType::get<int>(), "Offset Corner in Face"), + corner_index_(std::move(corner_index)), + offset_(std::move(offset)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask mask) const final + { + const IndexRange corner_range(mesh.totloop); + const Span<MPoly> polys = mesh.polys(); + + const bke::MeshFieldContext context{mesh, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(corner_index_); + evaluator.add(offset_); + evaluator.evaluate(); + const VArray<int> corner_indices = evaluator.get_evaluated<int>(0); + const VArray<int> offsets = evaluator.get_evaluated<int>(1); + + Array<int> loop_to_poly_map = bke::mesh_topology::build_loop_to_poly_map(polys, mesh.totloop); + + Array<int> offset_corners(mask.min_array_size()); + threading::parallel_for(mask.index_range(), 2048, [&](const IndexRange range) { + for (const int selection_i : range) { + const int corner_i = corner_indices[selection_i]; + const int offset = offsets[selection_i]; + if (!corner_range.contains(corner_i)) { + offset_corners[selection_i] = 0; + continue; + } + + const int poly_i = loop_to_poly_map[corner_i]; + const IndexRange poly_range(polys[poly_i].loopstart, polys[poly_i].totloop); + offset_corners[selection_i] = apply_offset_in_cyclic_range(poly_range, corner_i, offset); + } + }); + + return VArray<int>::ForContainer(std::move(offset_corners)); + } + + uint64_t hash() const final + { + return get_default_hash(offset_); + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (const OffsetCornerInFaceFieldInput *other_field = + dynamic_cast<const OffsetCornerInFaceFieldInput *>(&other)) { + return other_field->corner_index_ == corner_index_ && other_field->offset_ == offset_; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + params.set_output("Corner Index", + Field<int>(std::make_shared<OffsetCornerInFaceFieldInput>( + params.extract_input<Field<int>>("Corner Index"), + params.extract_input<Field<int>>("Offset")))); +} + +} // namespace blender::nodes::node_geo_mesh_topology_offset_corner_in_face_cc + +void register_node_type_geo_mesh_topology_offset_corner_in_face() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_offset_corner_in_face_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, + GEO_NODE_MESH_TOPOLOGY_OFFSET_CORNER_IN_FACE, + "Offset Corner in Face", + NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_vertex_of_corner.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_vertex_of_corner.cc new file mode 100644 index 00000000000..f0163fa553a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_vertex_of_corner.cc @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_mesh.h" + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_topology_vertex_of_corner_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Corner Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The corner to retrieve data from. Defaults to the corner from the context")); + b.add_output<decl::Int>(N_("Vertex Index")) + .dependent_field() + .description(N_("The vertex the corner is attached to")); +} + +static int get_loop_vert(const MLoop &loop) +{ + return loop.v; +} + +class CornerVertFieldInput final : public bke::MeshFieldInput { + public: + CornerVertFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Corner Vertex") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + if (domain != ATTR_DOMAIN_CORNER) { + return {}; + } + return VArray<int>::ForDerivedSpan<MLoop, get_loop_vert>(mesh.loops()); + } + + uint64_t hash() const final + { + return 30495867093876; + } + + bool is_equal_to(const fn::FieldNode &other) const final + { + if (dynamic_cast<const CornerVertFieldInput *>(&other)) { + return true; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + params.set_output("Vertex Index", + Field<int>(std::make_shared<FieldAtIndexInput>( + params.extract_input<Field<int>>("Corner Index"), + Field<int>(std::make_shared<CornerVertFieldInput>()), + ATTR_DOMAIN_CORNER))); +} + +} // namespace blender::nodes::node_geo_mesh_topology_vertex_of_corner_cc + +void register_node_type_geo_mesh_topology_vertex_of_corner() +{ + namespace file_ns = blender::nodes::node_geo_mesh_topology_vertex_of_corner_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_TOPOLOGY_VERTEX_OF_CORNER, "Vertex of Corner", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 0b2159364f1..bf064c6fcbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -3,6 +3,7 @@ #include "BLI_math_matrix.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -25,7 +26,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } @@ -68,19 +69,20 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set; if (params.get_input<bool>("As Instance")) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - const int handle = instances.add_reference(*object); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); + const int handle = instances->add_reference(*object); if (transform_space_relative) { - instances.add_instance(handle, transform); + instances->add_instance(handle, transform); } else { - instances.add_instance(handle, float4x4::identity()); + instances->add_instance(handle, float4x4::identity()); } + geometry_set = GeometrySet::create_with_instances(instances.release()); } else { geometry_set = bke::object_get_evaluated_geometry_set(*object); if (transform_space_relative) { - transform_geometry_set(geometry_set, transform, *params.depsgraph()); + transform_geometry_set(params, geometry_set, transform, *params.depsgraph()); } } @@ -88,7 +90,7 @@ static void node_geo_exec(GeoNodeExecParams params) } } -static void node_node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryObjectInfo *data = MEM_cnew<NodeGeometryObjectInfo>(__func__); data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; diff --git a/source/blender/nodes/geometry/nodes/node_geo_offset_point_in_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_offset_point_in_curve.cc new file mode 100644 index 00000000000..d71e27e0385 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_offset_point_in_curve.cc @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "BKE_curves.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +int apply_offset_in_cyclic_range(const IndexRange range, const int start_index, const int offset) +{ + BLI_assert(range.contains(start_index)); + const int start_in_range = start_index - range.first(); + const int offset_in_range = start_in_range + offset; + const int mod_offset = offset_in_range % range.size(); + if (mod_offset >= 0) { + return range[mod_offset]; + } + return range.last(-(mod_offset + 1)); +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_offset_point_in_curve_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Point Index")) + .implicit_field(implicit_field_inputs::index) + .description( + N_("The index of the control point to evaluate. Defaults to the current index")); + b.add_input<decl::Int>(N_("Offset")) + .supports_field() + .description(N_("The number of control points along the curve to traverse")); + b.add_output<decl::Bool>(N_("Is Valid Offset")) + .dependent_field() + .description(N_("Outputs true if the evaluated control point plus the offset " + "is a valid index of the original curve")); + b.add_output<decl::Int>(N_("Point Index")) + .dependent_field() + .description(N_("The index of the control point plus the offset within the entire " + "curves data-block")); +} + +class ControlPointNeighborFieldInput final : public bke::CurvesFieldInput { + private: + const Field<int> index_; + const Field<int> offset_; + + public: + ControlPointNeighborFieldInput(Field<int> index, Field<int> offset) + : CurvesFieldInput(CPPType::get<int>(), "Offset Point in Curve"), + index_(std::move(index)), + offset_(std::move(offset)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, + const eAttrDomain domain, + const IndexMask mask) const final + { + const VArray<bool> cyclic = curves.cyclic(); + const Array<int> parent_curves = curves.point_to_curve_map(); + + const bke::CurvesFieldContext context{curves, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(index_); + evaluator.add(offset_); + evaluator.evaluate(); + const VArray<int> indices = evaluator.get_evaluated<int>(0); + const VArray<int> offsets = evaluator.get_evaluated<int>(1); + + Array<int> output(mask.min_array_size()); + for (const int i_selection : mask) { + const int i_point = std::clamp(indices[i_selection], 0, curves.points_num() - 1); + const int i_curve = parent_curves[i_point]; + const IndexRange curve_points = curves.points_for_curve(i_curve); + const int offset_point = i_point + offsets[i_point]; + + if (cyclic[i_curve]) { + output[i_selection] = apply_offset_in_cyclic_range( + curve_points, i_point, offsets[i_selection]); + continue; + } + output[i_selection] = std::clamp(offset_point, 0, curves.points_num() - 1); + } + + return VArray<int>::ForContainer(std::move(output)); + } +}; + +class OffsetValidFieldInput final : public bke::CurvesFieldInput { + private: + const Field<int> index_; + const Field<int> offset_; + + public: + OffsetValidFieldInput(Field<int> index, Field<int> offset) + : CurvesFieldInput(CPPType::get<bool>(), "Offset Valid"), + index_(std::move(index)), + offset_(std::move(offset)) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, + const eAttrDomain domain, + const IndexMask mask) const final + { + const VArray<bool> cyclic = curves.cyclic(); + const Array<int> parent_curves = curves.point_to_curve_map(); + + const bke::CurvesFieldContext context{curves, domain}; + fn::FieldEvaluator evaluator{context, &mask}; + evaluator.add(index_); + evaluator.add(offset_); + evaluator.evaluate(); + const VArray<int> indices = evaluator.get_evaluated<int>(0); + const VArray<int> offsets = evaluator.get_evaluated<int>(1); + + Array<bool> output(mask.min_array_size()); + for (const int i_selection : mask) { + const int i_point = indices[i_selection]; + if (!curves.points_range().contains(i_point)) { + output[i_selection] = false; + continue; + } + + const int i_curve = parent_curves[i_point]; + const IndexRange curve_points = curves.points_for_curve(i_curve); + if (cyclic[i_curve]) { + output[i_selection] = true; + continue; + } + output[i_selection] = curve_points.contains(i_point + offsets[i_selection]); + }; + return VArray<bool>::ForContainer(std::move(output)); + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> index = params.extract_input<Field<int>>("Point Index"); + Field<int> offset = params.extract_input<Field<int>>("Offset"); + + if (params.output_is_required("Point Index")) { + Field<int> curve_point_field{std::make_shared<ControlPointNeighborFieldInput>(index, offset)}; + params.set_output("Point Index", std::move(curve_point_field)); + } + if (params.output_is_required("Is Valid Offset")) { + Field<bool> valid_field{std::make_shared<OffsetValidFieldInput>(index, offset)}; + params.set_output("Is Valid Offset", std::move(valid_field)); + } +} + +} // namespace blender::nodes::node_geo_offset_point_in_curve_cc + +void register_node_type_geo_offset_point_in_curve() +{ + namespace file_ns = blender::nodes::node_geo_offset_point_in_curve_cc; + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_OFFSET_POINT_IN_CURVE, "Offset Point in Curve", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc index dd32e6714f4..dcbe176b384 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_pointcloud.h" +#include "DNA_pointcloud_types.h" #include "BLI_task.hh" @@ -19,9 +20,10 @@ static void node_declare(NodeDeclarationBuilder &b) .default_value(float3(0.0f)) .description(N_("The positions of the new points")); b.add_input<decl::Float>(N_("Radius")) + .min(0.0f) + .default_value(0.1f) .supports_field() .subtype(PROP_DISTANCE) - .default_value(float(0.1f)) .description(N_("The radii of the new points")); b.add_output<decl::Geometry>(N_("Geometry")); } @@ -42,7 +44,7 @@ class PointsFieldContext : public FieldContext { GVArray get_varray_for_input(const FieldInput &field_input, const IndexMask mask, - ResourceScope &UNUSED(scope)) const + ResourceScope & /*scope*/) const { const bke::IDAttributeFieldInput *id_field_input = dynamic_cast<const bke::IDAttributeFieldInput *>(&field_input); @@ -69,10 +71,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float3> position_field = params.extract_input<Field<float3>>("Position"); Field<float> radius_field = params.extract_input<Field<float>>("Radius"); - PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count); - GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud); - PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); - MutableAttributeAccessor attributes = *points.attributes_for_write(); + PointCloud *points = BKE_pointcloud_new_nomain(count); + MutableAttributeAccessor attributes = points->attributes_for_write(); AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>( "position", ATTR_DOMAIN_POINT); AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>( @@ -86,7 +86,7 @@ static void node_geo_exec(GeoNodeExecParams params) output_position.finish(); output_radii.finish(); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Geometry", GeometrySet::create_with_pointcloud(points)); } } // namespace blender::nodes::node_geo_points_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index ed7ef9b7c71..4ac3bf712f7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "DNA_pointcloud_types.h" + #include "BKE_attribute_math.hh" #include "BKE_mesh.h" @@ -22,21 +24,18 @@ static void node_declare(NodeDeclarationBuilder &b) static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Field<bool> &selection_field) { - const PointCloudComponent *point_component = - geometry_set.get_component_for_read<PointCloudComponent>(); - if (point_component == nullptr) { + const PointCloud *points = geometry_set.get_pointcloud_for_read(); + if (points == nullptr) { geometry_set.remove_geometry_during_modify(); return; } - - GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; - const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_num == 0) { + if (points->totpoint == 0) { geometry_set.remove_geometry_during_modify(); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_num}; + bke::PointCloudFieldContext field_context{*points}; + fn::FieldEvaluator selection_evaluator{field_context, points->totpoint}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); @@ -47,16 +46,16 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0); geometry_set.replace_mesh(mesh); - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + + const AttributeAccessor src_attributes = points->attributes(); + MutableAttributeAccessor dst_attributes = mesh->attributes_for_write(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const eCustomDataType data_type = entry.value.data_type; - GVArray src = point_component->attributes()->lookup_or_default( + GVArray src = src_attributes.lookup_or_default(attribute_id, ATTR_DOMAIN_POINT, data_type); + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, data_type); - GSpanAttributeWriter dst = - mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span( - attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { src.materialize_compressed_to_uninitialized(selection, dst.span.data()); dst.finish(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 4a3048e5f4a..45f6820f2e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -43,14 +43,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Volume")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__); data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; @@ -83,7 +83,7 @@ struct ParticleList { size_t size() const { - return (size_t)positions.size(); + return size_t(positions.size()); } void getPos(size_t n, openvdb::Vec3R &xyz) const @@ -170,7 +170,7 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms, "position", ATTR_DOMAIN_POINT, {0, 0, 0}); Field<float> radius_field = params.get_input<Field<float>>("Radius"); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::GeometryFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT); r_positions.resize(r_positions.size() + domain_num); @@ -215,7 +215,7 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, return; } - Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr)); BKE_volume_init_grids(volume); const float density = params.get_input<float>("Density"); @@ -231,17 +231,16 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); - #ifdef WITH_OPENVDB + GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { initialize_volume_component_from_points(params, geometry_set); }); params.set_output("Volume", std::move(geometry_set)); #else + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_default_remaining_outputs(); #endif } diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index d5233ee35a4..21f4449baee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -22,17 +22,17 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Geometry>(N_("Target")) .only_realized_data() .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); - b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(implicit_field_inputs::position); b.add_output<decl::Vector>(N_("Position")).dependent_field(); b.add_output<decl::Float>(N_("Distance")).dependent_field(); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE); } -static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) +static void geo_proximity_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryProximity *node_storage = MEM_cnew<NodeGeometryProximity>(__func__); node_storage->target_element = GEO_NODE_PROX_TARGET_FACES; @@ -151,7 +151,7 @@ class ProximityFunction : public fn::MultiFunction { return signature.build(); } - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override { const VArray<float3> &src_positions = params.readonly_single_input<float3>(0, "Source Position"); @@ -211,8 +211,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float3> position_field = params.extract_input<Field<float3>>("Source Position"); auto proximity_fn = std::make_unique<ProximityFunction>( - std::move(geometry_set_target), - static_cast<GeometryNodeProximityTargetType>(storage.target_element)); + std::move(geometry_set_target), GeometryNodeProximityTargetType(storage.target_element)); auto proximity_op = std::make_shared<FieldOperation>( FieldOperation(std::move(proximity_fn), {std::move(position_field)})); diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index f81748da587..d248bc539b1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -31,7 +31,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); - b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(implicit_field_inputs::position); b.add_input<decl::Vector>(N_("Ray Direction")) .default_value({0.0f, 0.0f, -1.0f}) .supports_field(); @@ -53,13 +53,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__); data->mapping = GEO_NODE_RAYCAST_INTERPOLATED; @@ -70,9 +70,9 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryRaycast &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); - bNodeSocket *socket_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *socket_vector = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 1)); bNodeSocket *socket_float = socket_vector->next; bNodeSocket *socket_color4f = socket_float->next; bNodeSocket *socket_boolean = socket_color4f->next; @@ -84,7 +84,7 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL); nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32); - bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4); + bNodeSocket *out_socket_vector = static_cast<bNodeSocket *>(BLI_findlink(&node->outputs, 4)); bNodeSocket *out_socket_float = out_socket_vector->next; bNodeSocket *out_socket_color4f = out_socket_float->next; bNodeSocket *out_socket_boolean = out_socket_color4f->next; @@ -208,7 +208,7 @@ class RaycastFunction : public fn::MultiFunction { GeometryNodeRaycastMapMode mapping_; /** The field for data evaluated on the target geometry. */ - std::optional<GeometryComponentFieldContext> target_context_; + std::optional<bke::MeshFieldContext> target_context_; std::unique_ptr<FieldEvaluator> target_evaluator_; const GVArray *target_data_ = nullptr; @@ -245,7 +245,7 @@ class RaycastFunction : public fn::MultiFunction { return signature.build(); } - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override { /* Hit positions are always necessary for retrieving the attribute from the target if that * output is required, so always retrieve a span from the evaluator in that case (it's @@ -310,9 +310,9 @@ class RaycastFunction : public fn::MultiFunction { if (!src_field) { return; } - const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); - target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); + const Mesh &mesh = *target_.get_mesh_for_read(); + target_context_.emplace(bke::MeshFieldContext{mesh, domain_}); + const int domain_size = mesh.attributes().domain_size(domain_); target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); @@ -386,8 +386,8 @@ static void node_geo_exec(GeoNodeExecParams params) { GeometrySet target = params.extract_input<GeometrySet>("Target Geometry"); const NodeGeometryRaycast &storage = node_storage(params.node()); - const GeometryNodeRaycastMapMode mapping = (GeometryNodeRaycastMapMode)storage.mapping; - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const GeometryNodeRaycastMapMode mapping = GeometryNodeRaycastMapMode(storage.mapping); + const eCustomDataType data_type = eCustomDataType(storage.data_type); if (target.is_empty()) { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc index 1c72d73d151..3ccc8afb0a7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -15,7 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "legacy_behavior", 0, nullptr, ICON_NONE); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc index ee279ba58f9..1b398f63691 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc @@ -55,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) }); if (attribute_exists && !cannot_delete) { - params.used_named_attribute(name, eNamedAttrUsage::Remove); + params.used_named_attribute(name, NamedAttributeUsage::Remove); } if (!attribute_exists) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index d414bb1fa1d..fac92a7500c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_rotate_instances_cc { @@ -16,12 +18,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Instances")); } -static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void rotate_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - const int domain_num = instances_component.instances_num(); - - fn::FieldEvaluator evaluator{field_context, domain_num}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); @@ -33,14 +33,14 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst const VArray<float3> pivots = evaluator.get_evaluated<float3>(1); const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2); - MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; const float3 euler = rotations[i]; - float4x4 &instance_transform = instance_transforms[i]; + float4x4 &instance_transform = transforms[i]; float4x4 rotation_matrix; float3 used_pivot; @@ -83,9 +83,8 @@ static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &inst static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - rotate_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + rotate_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc new file mode 100644 index 00000000000..4d2db059798 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_sample_index_cc { + +NODE_STORAGE_FUNCS(NodeGeometrySampleIndex); + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}); + + b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Index")) + .supports_field() + .description(N_("Which element to retrieve a value from on the geometry")); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({6}); + b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({6}); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({6}); + b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({6}); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({6}); +} + +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "clamp", 0, nullptr, ICON_NONE); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + NodeGeometrySampleIndex *data = MEM_cnew<NodeGeometrySampleIndex>(__func__); + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_POINT; + data->clamp = 0; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type); + + bNodeSocket *in_socket_geometry = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *in_socket_float = in_socket_geometry->next; + bNodeSocket *in_socket_int32 = in_socket_float->next; + bNodeSocket *in_socket_vector = in_socket_int32->next; + bNodeSocket *in_socket_color4f = in_socket_vector->next; + bNodeSocket *in_socket_bool = in_socket_color4f->next; + + nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first); + bNodeSocket *out_socket_int32 = out_socket_float->next; + bNodeSocket *out_socket_vector = out_socket_int32->next; + bNodeSocket *out_socket_color4f = out_socket_vector->next; + bNodeSocket *out_socket_bool = out_socket_color4f->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(1)); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSampleIndex"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const eAttrDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +static const GeometryComponent *find_source_component(const GeometrySet &geometry, + const eAttrDomain domain) +{ + /* Choose the other component based on a consistent order, rather than some more complicated + * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ + static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +template<typename T> +void copy_with_indices(const VArray<T> &src, + const VArray<int> &indices, + const IndexMask mask, + MutableSpan<T> dst) +{ + const IndexRange src_range = src.index_range(); + devirtualize_varray2(src, indices, [&](const auto src, const auto indices) { + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : mask.slice(range)) { + const int index = indices[i]; + if (src_range.contains(index)) { + dst[i] = src[index]; + } + else { + dst[i] = {}; + } + } + }); + }); +} + +template<typename T> +void copy_with_clamped_indices(const VArray<T> &src, + const VArray<int> &indices, + const IndexMask mask, + MutableSpan<T> dst) +{ + const int last_index = src.index_range().last(); + devirtualize_varray2(src, indices, [&](const auto src, const auto indices) { + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : mask.slice(range)) { + const int index = indices[i]; + dst[i] = src[std::clamp(index, 0, last_index)]; + } + }); + }); +} + +/** + * The index-based transfer theoretically does not need realized data when there is only one + * instance geometry set in the source. A future optimization could be removing that limitation + * internally. + */ +class SampleIndexFunction : public fn::MultiFunction { + GeometrySet src_geometry_; + GField src_field_; + eAttrDomain domain_; + bool clamp_; + + fn::MFSignature signature_; + + std::optional<bke::GeometryFieldContext> geometry_context_; + std::unique_ptr<FieldEvaluator> evaluator_; + const GVArray *src_data_ = nullptr; + + public: + SampleIndexFunction(GeometrySet geometry, + GField src_field, + const eAttrDomain domain, + const bool clamp) + : src_geometry_(std::move(geometry)), + src_field_(std::move(src_field)), + domain_(domain), + clamp_(clamp) + { + src_geometry_.ensure_owns_direct_data(); + + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->evaluate_field(); + } + + fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Sample Index"}; + signature.single_input<int>("Index"); + signature.single_output("Value", src_field_.cpp_type()); + return signature.build(); + } + + void evaluate_field() + { + const GeometryComponent *component = find_source_component(src_geometry_, domain_); + if (component == nullptr) { + return; + } + const int domain_num = component->attribute_domain_size(domain_); + geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_)); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); + evaluator_->add(src_field_); + evaluator_->evaluate(); + src_data_ = &evaluator_->get_evaluated(0); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override + { + const VArray<int> &indices = params.readonly_single_input<int>(0, "Index"); + GMutableSpan dst = params.uninitialized_single_output(1, "Value"); + + const CPPType &type = dst.type(); + if (src_data_ == nullptr) { + type.value_initialize_indices(dst.data(), mask); + return; + } + + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + if (clamp_) { + copy_with_clamped_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>()); + } + else { + copy_with_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>()); + } + }); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input<Field<float>>("Value_Float"); + case CD_PROP_FLOAT3: + return params.extract_input<Field<float3>>("Value_Vector"); + case CD_PROP_COLOR: + return params.extract_input<Field<ColorGeometry4f>>("Value_Color"); + case CD_PROP_BOOL: + return params.extract_input<Field<bool>>("Value_Bool"); + case CD_PROP_INT32: + return params.extract_input<Field<int>>("Value_Int"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Value_Float", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Value_Vector", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Value_Color", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Value_Bool", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Value_Int", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); + const NodeGeometrySampleIndex &storage = node_storage(params.node()); + const eCustomDataType data_type = eCustomDataType(storage.data_type); + const eAttrDomain domain = eAttrDomain(storage.domain); + + auto fn = std::make_shared<SampleIndexFunction>(std::move(geometry), + get_input_attribute_field(params, data_type), + domain, + bool(storage.clamp)); + auto op = FieldOperation::Create(std::move(fn), {params.extract_input<Field<int>>("Index")}); + output_attribute_field(params, GField(std::move(op))); +} + +} // namespace blender::nodes::node_geo_sample_index_cc + +void register_node_type_geo_sample_index() +{ + namespace file_ns = blender::nodes::node_geo_sample_index_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_INDEX, "Sample Index", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + node_type_storage( + &ntype, "NodeGeometrySampleIndex", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc new file mode 100644 index 00000000000..8c5dad3a1c5 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_pointcloud_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(positions.size() >= r_indices.size()); + BLI_assert(positions.size() >= r_distances_sq.size()); + BLI_assert(positions.size() >= r_positions.size()); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_sample_nearest_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input<decl::Vector>(N_("Sample Position")).implicit_field(implicit_field_inputs::position); + b.add_output<decl::Int>(N_("Index")).dependent_field({1}); +} + +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; + node->custom2 = ATTR_DOMAIN_POINT; +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq) +{ + BLI_assert(positions.size() >= r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_point_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_edge_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polys(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_poly_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array<int> looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); + + const Span<MLoopTri> looptris = mesh.looptris(); + + for (const int i : mask) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_corner_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + + BLI_assert(mesh.totloop > 0); + Array<int> poly_indices(positions.size()); + get_closest_mesh_polys(mesh, positions, mask, poly_indices, {}, {}); + + for (const int i : mask) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = polys[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = loops[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = verts[vertex_index]; + const float distance_sq = math::distance_squared(position, float3(mvert.co)); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const eAttrDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +static const GeometryComponent *find_source_component(const GeometrySet &geometry, + const eAttrDomain domain) +{ + /* Choose the other component based on a consistent order, rather than some more complicated + * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ + static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +class SampleNearestFunction : public fn::MultiFunction { + GeometrySet source_; + eAttrDomain domain_; + + const GeometryComponent *src_component_; + + fn::MFSignature signature_; + + public: + SampleNearestFunction(GeometrySet geometry, eAttrDomain domain) + : source_(std::move(geometry)), domain_(domain) + { + source_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->src_component_ = find_source_component(source_, domain_); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Sample Nearest"}; + signature.single_input<float3>("Position"); + signature.single_output<int>("Index"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override + { + const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); + MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Index"); + if (!src_component_) { + indices.fill_indices(mask, 0); + return; + } + + switch (src_component_->type()) { + case GEO_COMPONENT_TYPE_MESH: { + const MeshComponent &component = *static_cast<const MeshComponent *>(src_component_); + const Mesh &mesh = *component.get_for_read(); + Array<float> distances(mask.min_array_size()); + switch (domain_) { + case ATTR_DOMAIN_POINT: + get_closest_mesh_points(mesh, positions, mask, indices, distances, {}); + break; + case ATTR_DOMAIN_EDGE: + get_closest_mesh_edges(mesh, positions, mask, indices, distances, {}); + break; + case ATTR_DOMAIN_FACE: + get_closest_mesh_polys(mesh, positions, mask, indices, distances, {}); + break; + case ATTR_DOMAIN_CORNER: + get_closest_mesh_corners(mesh, positions, mask, indices, distances, {}); + break; + default: + break; + } + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const PointCloudComponent &component = *static_cast<const PointCloudComponent *>( + src_component_); + const PointCloud &points = *component.get_for_read(); + Array<float> distances(mask.min_array_size()); + get_closest_pointcloud_points(points, positions, mask, indices, distances); + break; + } + default: + break; + } + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); + const eAttrDomain domain = eAttrDomain(params.node().custom2); + if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The source geometry must contain a mesh or a point cloud")); + params.set_default_remaining_outputs(); + return; + } + + Field<float3> positions = params.extract_input<Field<float3>>("Sample Position"); + auto fn = std::make_shared<SampleNearestFunction>(std::move(geometry), domain); + auto op = FieldOperation::Create(std::move(fn), {std::move(positions)}); + params.set_output<Field<int>>("Index", Field<int>(std::move(op))); +} + +} // namespace blender::nodes::node_geo_sample_nearest_cc + +void register_node_type_geo_sample_nearest() +{ + namespace file_ns = blender::nodes::node_geo_sample_nearest_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_NEAREST, "Sample Nearest", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc new file mode 100644 index 00000000000..95bf7199d63 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_sample_nearest_surface_cc { + +using namespace blender::bke::mesh_surface_sample; + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + + b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Sample Position")).implicit_field(implicit_field_inputs::position); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({6}); + b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({6}); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({6}); + b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({6}); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({6}); +} + +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const eCustomDataType data_type = eCustomDataType(node->custom1); + + bNodeSocket *in_socket_mesh = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *in_socket_float = in_socket_mesh->next; + bNodeSocket *in_socket_int32 = in_socket_float->next; + bNodeSocket *in_socket_vector = in_socket_int32->next; + bNodeSocket *in_socket_color4f = in_socket_vector->next; + bNodeSocket *in_socket_bool = in_socket_color4f->next; + + nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first); + bNodeSocket *out_socket_int32 = out_socket_float->next; + bNodeSocket *out_socket_vector = out_socket_int32->next; + bNodeSocket *out_socket_color4f = out_socket_vector->next; + bNodeSocket *out_socket_bool = out_socket_color4f->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSampleNearestSurface"); + node.custom1 = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the source geometry once and store + * it for later. + */ +class SampleNearestSurfaceFunction : public fn::MultiFunction { + GeometrySet source_; + GField src_field_; + + /** + * This function is meant to sample the surface of a mesh rather than take the value from + * individual elements, so use the most complex domain, ensuring no information is lost. In the + * future, it should be possible to use the most complex domain required by the field inputs, to + * simplify sampling and avoid domain conversions. + */ + eAttrDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + std::optional<bke::MeshFieldContext> source_context_; + std::unique_ptr<FieldEvaluator> source_evaluator_; + const GVArray *source_data_; + + public: + SampleNearestSurfaceFunction(GeometrySet geometry, GField src_field) + : source_(std::move(geometry)), src_field_(std::move(src_field)) + { + source_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + this->evaluate_source_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Sample Nearest Surface"}; + signature.single_input<float3>("Position"); + signature.single_output("Value", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override + { + const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Value"); + + const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); + BLI_assert(mesh_component.has_mesh()); + const Mesh &mesh = *mesh_component.get_for_read(); + BLI_assert(mesh.totpoly > 0); + + /* Find closest points on the mesh surface. */ + Array<int> looptri_indices(mask.min_array_size()); + Array<float3> sampled_positions(mask.min_array_size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); + + MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); + interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); + } + + private: + void evaluate_source_field() + { + const Mesh &mesh = *source_.get_mesh_for_read(); + source_context_.emplace(bke::MeshFieldContext{mesh, domain_}); + const int domain_size = mesh.attributes().domain_size(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + source_evaluator_->add(src_field_); + source_evaluator_->evaluate(); + source_data_ = &source_evaluator_->get_evaluated(0); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input<Field<float>>("Value_Float"); + case CD_PROP_FLOAT3: + return params.extract_input<Field<float3>>("Value_Vector"); + case CD_PROP_COLOR: + return params.extract_input<Field<ColorGeometry4f>>("Value_Color"); + case CD_PROP_BOOL: + return params.extract_input<Field<bool>>("Value_Bool"); + case CD_PROP_INT32: + return params.extract_input<Field<int>>("Value_Int"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Value_Float", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Value_Vector", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Value_Color", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Value_Bool", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Value_Int", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Mesh"); + const eCustomDataType data_type = eCustomDataType(params.node().custom1); + const Mesh *mesh = geometry.get_mesh_for_read(); + if (mesh == nullptr) { + params.set_default_remaining_outputs(); + return; + } + if (mesh->totvert == 0) { + params.set_default_remaining_outputs(); + return; + } + if (mesh->totpoly == 0) { + params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces")); + params.set_default_remaining_outputs(); + return; + } + + Field<float3> positions = params.extract_input<Field<float3>>("Sample Position"); + GField field = get_input_attribute_field(params, data_type); + auto fn = std::make_shared<SampleNearestSurfaceFunction>(std::move(geometry), std::move(field)); + auto op = FieldOperation::Create(std::move(fn), {std::move(positions)}); + output_attribute_field(params, GField(std::move(op))); +} + +} // namespace blender::nodes::node_geo_sample_nearest_surface_cc + +void register_node_type_geo_sample_nearest_surface() +{ + namespace file_ns = blender::nodes::node_geo_sample_nearest_surface_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SAMPLE_NEAREST_SURFACE, "Sample Nearest Surface", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_uv_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_uv_surface.cc new file mode 100644 index 00000000000..2e8446ba559 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_uv_surface.cc @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" +#include "BKE_type_conversions.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "GEO_reverse_uv_sampler.hh" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_sample_uv_surface_cc { + +using geometry::ReverseUVSampler; + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + + b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Source UV Map")) + .hide_value() + .supports_field() + .description(N_("The mesh UV map to sample. Should not have overlapping faces")); + b.add_input<decl::Vector>(N_("Sample UV")) + .supports_field() + .description(N_("The coordinates to sample within the UV map")); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({7}); + b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({7}); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({7}); + b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({7}); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({7}); + + b.add_output<decl::Bool>(N_("Is Valid")) + .dependent_field({7}) + .description(N_("Whether the node could find a single face to sample at the UV coordinate")); +} + +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const eCustomDataType data_type = eCustomDataType(node->custom1); + + bNodeSocket *in_socket_mesh = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *in_socket_float = in_socket_mesh->next; + bNodeSocket *in_socket_int32 = in_socket_float->next; + bNodeSocket *in_socket_vector = in_socket_int32->next; + bNodeSocket *in_socket_color4f = in_socket_vector->next; + bNodeSocket *in_socket_bool = in_socket_color4f->next; + + nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first); + bNodeSocket *out_socket_int32 = out_socket_float->next; + bNodeSocket *out_socket_vector = out_socket_int32->next; + bNodeSocket *out_socket_color4f = out_socket_vector->next; + bNodeSocket *out_socket_bool = out_socket_color4f->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + search_link_ops_for_declarations(params, declaration.outputs().take_back(1)); + + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + eNodeSocketDatatype(params.other_socket().type)); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSampleUVSurface"); + node.custom1 = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +class SampleUVSurfaceFunction : public fn::MultiFunction { + GeometrySet source_; + Field<float2> src_uv_map_field_; + GField src_field_; + + /** + * Use the most complex domain for now ensuring no information is lost. In the future, it should + * be possible to use the most complex domain required by the field inputs, to simplify sampling + * and avoid domain conversions. + */ + eAttrDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + std::optional<bke::MeshFieldContext> source_context_; + std::unique_ptr<FieldEvaluator> source_evaluator_; + const GVArray *source_data_; + VArraySpan<float2> source_uv_map_; + + std::optional<ReverseUVSampler> reverse_uv_sampler_; + + public: + SampleUVSurfaceFunction(GeometrySet geometry, Field<float2> src_uv_map_field, GField src_field) + : source_(std::move(geometry)), + src_uv_map_field_(std::move(src_uv_map_field)), + src_field_(std::move(src_field)) + { + source_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + this->evaluate_source(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Sample UV Surface"}; + signature.single_input<float2>("Sample UV"); + signature.single_output("Value", src_field_.cpp_type()); + signature.single_output<bool>("Is Valid"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext /*context*/) const override + { + const VArray<float2> &sample_uvs = params.readonly_single_input<float2>(0, "Sample UV"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Value"); + MutableSpan<bool> valid_dst = params.uninitialized_single_output_if_required<bool>(2, + "Is Valid"); + + const CPPType &type = src_field_.cpp_type(); + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + const VArray<T> src_typed = source_data_->typed<T>(); + MutableSpan<T> dst_typed = dst.typed<T>(); + for (const int i : mask) { + const float2 sample_uv = sample_uvs[i]; + const ReverseUVSampler::Result result = reverse_uv_sampler_->sample(sample_uv); + const bool valid = result.type == ReverseUVSampler::ResultType::Ok; + if (!dst_typed.is_empty()) { + if (valid) { + dst_typed[i] = attribute_math::mix3(result.bary_weights, + src_typed[result.looptri->tri[0]], + src_typed[result.looptri->tri[1]], + src_typed[result.looptri->tri[2]]); + } + else { + dst_typed[i] = {}; + } + } + if (!valid_dst.is_empty()) { + valid_dst[i] = valid; + } + } + }); + } + + private: + void evaluate_source() + { + const Mesh &mesh = *source_.get_mesh_for_read(); + source_context_.emplace(bke::MeshFieldContext{mesh, domain_}); + const int domain_size = mesh.attributes().domain_size(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + source_evaluator_->add(src_uv_map_field_); + source_evaluator_->add(src_field_); + source_evaluator_->evaluate(); + source_uv_map_ = source_evaluator_->get_evaluated<float2>(0); + source_data_ = &source_evaluator_->get_evaluated(1); + + reverse_uv_sampler_.emplace(source_uv_map_, mesh.looptris()); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input<Field<float>>("Value_Float"); + case CD_PROP_FLOAT3: + return params.extract_input<Field<float3>>("Value_Vector"); + case CD_PROP_COLOR: + return params.extract_input<Field<ColorGeometry4f>>("Value_Color"); + case CD_PROP_BOOL: + return params.extract_input<Field<bool>>("Value_Bool"); + case CD_PROP_INT32: + return params.extract_input<Field<int>>("Value_Int"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Value_Float", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Value_Vector", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Value_Color", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Value_Bool", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Value_Int", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Mesh"); + const eCustomDataType data_type = eCustomDataType(params.node().custom1); + const Mesh *mesh = geometry.get_mesh_for_read(); + if (mesh == nullptr) { + params.set_default_remaining_outputs(); + return; + } + if (mesh->totpoly == 0 && mesh->totvert != 0) { + params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces")); + params.set_default_remaining_outputs(); + return; + } + + const CPPType &float2_type = CPPType::get<float2>(); + + const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); + Field<float2> source_uv_map = conversions.try_convert( + params.extract_input<Field<float3>>("Source UV Map"), float2_type); + GField field = get_input_attribute_field(params, data_type); + Field<float2> sample_uvs = conversions.try_convert( + params.extract_input<Field<float3>>("Sample UV"), float2_type); + auto fn = std::make_shared<SampleUVSurfaceFunction>( + std::move(geometry), std::move(source_uv_map), std::move(field)); + auto op = FieldOperation::Create(std::move(fn), {std::move(sample_uvs)}); + output_attribute_field(params, GField(op, 0)); + params.set_output("Is Valid", Field<bool>(op, 1)); +} + +} // namespace blender::nodes::node_geo_sample_uv_surface_cc + +void register_node_type_geo_sample_uv_surface() +{ + namespace file_ns = blender::nodes::node_geo_sample_uv_surface_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_UV_SURFACE, "Sample UV Surface", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc index d674f611c9f..5f1baa23511 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc @@ -25,7 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Float>(N_("Scale"), "Scale").default_value(1.0f).min(0.0f).supports_field(); b.add_input<decl::Vector>(N_("Center")) .subtype(PROP_TRANSLATION) - .implicit_field() + .implicit_field(implicit_field_inputs::position) .description(N_("Origin of the scaling for each element. If multiple elements are " "connected, their center is averaged")); b.add_input<decl::Vector>(N_("Axis")) @@ -36,13 +36,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); }; -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); uiItemR(layout, ptr, "scale_mode", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = ATTR_DOMAIN_FACE; node->custom2 = GEO_NODE_SCALE_ELEMENTS_UNIFORM; @@ -56,8 +56,7 @@ static void node_update(bNodeTree *ntree, bNode *node) bNodeSocket *center_socket = scale_float_socket->next; bNodeSocket *axis_socket = center_socket->next; - const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>( - node->custom2); + const GeometryNodeScaleElementsMode mode = GeometryNodeScaleElementsMode(node->custom2); const bool use_single_axis = mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS; nodeSetSocketAvailability(ntree, axis_socket, use_single_axis); @@ -147,14 +146,22 @@ static float4x4 create_single_axis_transform(const float3 ¢er, return transform; } -using GetVertexIndicesFn = - FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>; +using GetVertexIndicesFn = FunctionRef<void(Span<MEdge> edges, + Span<MPoly> polys, + Span<MLoop> loops, + int element_index, + VectorSet<int> &r_vertex_indices)>; static void scale_vertex_islands_uniformly(Mesh &mesh, const Span<ElementIsland> islands, const UniformScaleParams ¶ms, const GetVertexIndicesFn get_vertex_indices) { + MutableSpan<MVert> verts = mesh.verts_for_write(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) { for (const int island_index : range) { const ElementIsland &island = islands[island_index]; @@ -164,7 +171,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh, VectorSet<int> vertex_indices; for (const int poly_index : island.element_indices) { - get_vertex_indices(mesh, poly_index, vertex_indices); + get_vertex_indices(edges, polys, loops, poly_index, vertex_indices); center += params.centers[poly_index]; scale += params.scales[poly_index]; } @@ -175,7 +182,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh, center *= f; for (const int vert_index : vertex_indices) { - MVert &vert = mesh.mvert[vert_index]; + MVert &vert = verts[vert_index]; const float3 old_position = vert.co; const float3 new_position = transform_with_uniform_scale(old_position, center, scale); copy_v3_v3(vert.co, new_position); @@ -191,6 +198,11 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, const AxisScaleParams ¶ms, const GetVertexIndicesFn get_vertex_indices) { + MutableSpan<MVert> verts = mesh.verts_for_write(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) { for (const int island_index : range) { const ElementIsland &island = islands[island_index]; @@ -201,7 +213,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, VectorSet<int> vertex_indices; for (const int poly_index : island.element_indices) { - get_vertex_indices(mesh, poly_index, vertex_indices); + get_vertex_indices(edges, polys, loops, poly_index, vertex_indices); center += params.centers[poly_index]; scale += params.scales[poly_index]; axis += params.axis_vectors[poly_index]; @@ -219,7 +231,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, const float4x4 transform = create_single_axis_transform(center, axis, scale); for (const int vert_index : vertex_indices) { - MVert &vert = mesh.mvert[vert_index]; + MVert &vert = verts[vert_index]; const float3 old_position = vert.co; const float3 new_position = transform * old_position; copy_v3_v3(vert.co, new_position); @@ -232,11 +244,14 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection) { + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + /* Use the disjoint set data structure to determine which vertices have to be scaled together. */ DisjointSet disjoint_set(mesh.totvert); for (const int poly_index : face_selection) { - const MPoly &poly = mesh.mpoly[poly_index]; - const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const MPoly &poly = polys[poly_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); for (const int loop_index : IndexRange(poly.totloop - 1)) { const int v1 = poly_loops[loop_index].v; const int v2 = poly_loops[loop_index + 1].v; @@ -252,8 +267,8 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM /* Gather all of the face indices in each island into separate vectors. */ for (const int poly_index : face_selection) { - const MPoly &poly = mesh.mpoly[poly_index]; - const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const MPoly &poly = polys[poly_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); const int island_id = disjoint_set.find_root(poly_loops[0].v); const int island_index = island_ids.index_of_or_add(island_id); if (island_index == islands.size()) { @@ -266,10 +281,14 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM return islands; } -static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices) +static void get_face_verts(const Span<MEdge> /*edges*/, + const Span<MPoly> polys, + const Span<MLoop> loops, + int face_index, + VectorSet<int> &r_vertex_indices) { - const MPoly &poly = mesh.mpoly[face_index]; - const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const MPoly &poly = polys[face_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); for (const MLoop &loop : poly_loops) { r_vertex_indices.add(loop.v); } @@ -288,18 +307,14 @@ static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator, return out; } -static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields) +static void scale_faces_on_axis(Mesh &mesh, const AxisScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator evaluator{field_context, mesh.totpoly}; AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); - scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices); + scale_vertex_islands_on_axis(mesh, island, params, get_face_verts); } static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator, @@ -314,26 +329,24 @@ static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluato return out; } -static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields) +static void scale_faces_uniformly(Mesh &mesh, const UniformScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator evaluator{field_context, mesh.totpoly}; UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); - scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices); + scale_vertex_islands_uniformly(mesh, island, params, get_face_verts); } static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection) { + const Span<MEdge> edges = mesh.edges(); + /* Use the disjoint set data structure to determine which vertices have to be scaled together. */ DisjointSet disjoint_set(mesh.totvert); for (const int edge_index : edge_selection) { - const MEdge &edge = mesh.medge[edge_index]; + const MEdge &edge = edges[edge_index]; disjoint_set.join(edge.v1, edge.v2); } @@ -344,7 +357,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM /* Gather all of the edge indices in each island into separate vectors. */ for (const int edge_index : edge_selection) { - const MEdge &edge = mesh.medge[edge_index]; + const MEdge &edge = edges[edge_index]; const int island_id = disjoint_set.find_root(edge.v1); const int island_index = island_ids.index_of_or_add(island_id); if (island_index == islands.size()) { @@ -357,47 +370,42 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM return islands; } -static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices) +static void get_edge_verts(const Span<MEdge> edges, + const Span<MPoly> /*polys*/, + const Span<MLoop> /*loops*/, + int edge_index, + VectorSet<int> &r_vertex_indices) { - const MEdge &edge = mesh.medge[edge_index]; + const MEdge &edge = edges[edge_index]; r_vertex_indices.add(edge.v1); r_vertex_indices.add(edge.v2); } -static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields) +static void scale_edges_uniformly(Mesh &mesh, const UniformScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator evaluator{field_context, mesh.totedge}; UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); - scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices); + scale_vertex_islands_uniformly(mesh, island, params, get_edge_verts); } -static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields) +static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator evaluator{field_context, mesh.totedge}; AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); - scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices); + scale_vertex_islands_on_axis(mesh, island, params, get_edge_verts); } static void node_geo_exec(GeoNodeExecParams params) { const bNode &node = params.node(); - const eAttrDomain domain = static_cast<eAttrDomain>(node.custom1); - const GeometryNodeScaleElementsMode scale_mode = static_cast<GeometryNodeScaleElementsMode>( - node.custom2); + const eAttrDomain domain = eAttrDomain(node.custom1); + const GeometryNodeScaleElementsMode scale_mode = GeometryNodeScaleElementsMode(node.custom2); GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); @@ -410,42 +418,38 @@ static void node_geo_exec(GeoNodeExecParams params) } geometry.modify_geometry_sets([&](GeometrySet &geometry) { - if (!geometry.has_mesh()) { - return; - } - MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>(); - switch (domain) { - case ATTR_DOMAIN_FACE: { - switch (scale_mode) { - case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { - scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field}); - break; - } - case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { - scale_faces_on_axis(mesh_component, - {selection_field, scale_field, center_field, axis_field}); - break; + if (Mesh *mesh = geometry.get_mesh_for_write()) { + switch (domain) { + case ATTR_DOMAIN_FACE: { + switch (scale_mode) { + case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { + scale_faces_uniformly(*mesh, {selection_field, scale_field, center_field}); + break; + } + case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { + scale_faces_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field}); + break; + } } + break; } - break; - } - case ATTR_DOMAIN_EDGE: { - switch (scale_mode) { - case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { - scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field}); - break; - } - case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { - scale_edges_on_axis(mesh_component, - {selection_field, scale_field, center_field, axis_field}); - break; + case ATTR_DOMAIN_EDGE: { + switch (scale_mode) { + case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { + scale_edges_uniformly(*mesh, {selection_field, scale_field, center_field}); + break; + } + case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { + scale_edges_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field}); + break; + } } + break; } - break; + default: + BLI_assert_unreachable(); + break; } - default: - BLI_assert_unreachable(); - break; } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 7156feb37d7..dacb130337f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_scale_instances_cc { @@ -19,11 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Instances")); } -static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void scale_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - - fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); @@ -35,13 +36,13 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta const VArray<float3> pivots = evaluator.get_evaluated<float3>(1); const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2); - MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; const float3 pivot = pivots[i]; - float4x4 &instance_transform = instance_transforms[i]; + float4x4 &instance_transform = transforms[i]; if (local_spaces[i]) { instance_transform *= float4x4::from_location(pivot); @@ -62,9 +63,8 @@ static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &insta static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - scale_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + scale_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_self_object.cc b/source/blender/nodes/geometry/nodes/node_geo_self_object.cc new file mode 100644 index 00000000000..7b33afdb4a0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_self_object.cc @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_self_object_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Object>(N_("Self Object")); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + params.set_output("Self Object", const_cast<Object *>(params.self_object())); +} + +} // namespace blender::nodes::node_geo_self_object_cc + +void register_node_type_geo_self_object() +{ + namespace file_ns = blender::nodes::node_geo_self_object_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SELF_OBJECT, "Self Object", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc index d785694f253..44d12466d9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -23,12 +23,12 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The parts of the geometry not in the selection")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometrySeparateGeometry *data = MEM_cnew<NodeGeometrySeparateGeometry>(__func__); data->domain = ATTR_DOMAIN_POINT; @@ -43,7 +43,7 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); const NodeGeometrySeparateGeometry &storage = node_storage(params.node()); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); + const eAttrDomain domain = eAttrDomain(storage.domain); auto separate_geometry_maybe_recursively = [&](GeometrySet &geometry_set, const Field<bool> &selection) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index fc3cb7006bb..c143203337a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -17,17 +17,21 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field([](const bNode &node, void *r_value) { + const StringRef side = node_storage(node).mode == GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : + "handle_right"; + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side)); + }); b.add_input<decl::Vector>(N_("Offset")).default_value(float3(0.0f, 0.0f, 0.0f)).supports_field(); b.add_output<decl::Geometry>(N_("Curve")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometrySetCurveHandlePositions *data = MEM_cnew<NodeGeometrySetCurveHandlePositions>( __func__); @@ -68,19 +72,18 @@ static void update_handle_types_for_movement(int8_t &type, int8_t &other) } } -static void set_position_in_component(CurveComponent &component, +static void set_position_in_component(bke::CurvesGeometry &curves, const GeometryNodeCurveHandleMode mode, const Field<bool> &selection_field, const Field<float3> &position_field, const Field<float3> &offset_field) { - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (curves.points_num() == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); @@ -89,9 +92,6 @@ static void set_position_in_component(CurveComponent &component, const VArray<float3> new_positions = evaluator.get_evaluated<float3>(0); const VArray<float3> new_offsets = evaluator.get_evaluated<float3>(1); - Curves &curves_id = *component.get_for_write(); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - Span<float3> positions = curves.positions(); const bool use_left = mode == GEO_NODE_CURVE_HANDLE_LEFT; @@ -141,22 +141,17 @@ static void node_geo_exec(GeoNodeExecParams params) std::atomic<bool> has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; - } - has_curves = true; - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const AttributeAccessor attributes = *component.attributes(); - if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) { - return; - } - has_bezier = true; + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + has_curves = true; + const AttributeAccessor attributes = curves.attributes(); + if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) { + return; + } + has_bezier = true; - set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(), - mode, - selection_field, - position_field, - offset_field); + set_position_in_component(curves, mode, selection_field, position_field, offset_field); + } }); if (has_curves && !has_bezier) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_normal.cc new file mode 100644 index 00000000000..e2169966f5a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_normal.cc @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_curve_normal_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + node->custom1 = NORMAL_MODE_MINIMUM_TWIST; +} + +static void set_normal_mode(bke::CurvesGeometry &curves, + const NormalMode mode, + const Field<bool> &selection_field) +{ + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + curves.normal_mode_for_write().fill_indices(selection, mode); + curves.tag_normals_changed(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NormalMode mode = static_cast<NormalMode>(params.node().custom1); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + set_normal_mode(curves, mode, selection_field); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_curve_normal_cc + +void register_node_type_geo_set_curve_normal() +{ + namespace file_ns = blender::nodes::node_geo_set_curve_normal_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_NORMAL, "Set Curve Normal", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + ntype.draw_buttons = file_ns::node_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index e4fae95b5a5..0d361090068 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_curve_radius_cc { @@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void set_radius_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<float> &radius_field) +static void set_radius(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<float> &radius_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (curves.points_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray); evaluator.evaluate(); @@ -45,9 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - set_radius_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_radius(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, radii_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 2211ac62727..8c1fb883f46 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_curve_tilt_cc { @@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void set_tilt_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<float> &tilt_field) +static void set_tilt(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<float> &tilt_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (curves.points_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(tilt_field, tilts.varray); evaluator.evaluate(); @@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - set_tilt_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_tilt(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, tilt_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index fbb2ecbb799..e308371b1c2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -8,7 +8,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Geometry")); b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); - b.add_input<decl::Int>(N_("ID")).implicit_field(); + b.add_input<decl::Int>(N_("ID")).implicit_field(implicit_field_inputs::index); b.add_output<decl::Geometry>(N_("Geometry")); } @@ -24,7 +24,7 @@ static void set_id_in_component(GeometryComponent &component, return; } MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, domain}; + bke::GeometryFieldContext field_context{component, domain}; fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 507c6e81b1f..8d00d82664b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -12,6 +12,7 @@ #include "DNA_volume_types.h" #include "BKE_material.h" +#include "BKE_mesh.h" namespace blender::nodes::node_geo_set_material_cc { @@ -49,11 +50,11 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); } - mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); - for (const int i : selection) { - MPoly &poly = mesh.mpoly[i]; - poly.mat_nr = new_material_index; - } + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>( + "material_index", ATTR_DOMAIN_FACE); + material_indices.span.fill_indices(selection, new_material_index); + material_indices.finish(); } static void node_geo_exec(GeoNodeExecParams params) @@ -72,8 +73,8 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_mesh()) { MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); Mesh &mesh = *mesh_component.get_for_write(); - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index 0dc89bb7ef4..bb9ac9b5d4c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -14,21 +14,23 @@ static void node_declare(NodeDeclarationBuilder &b) static void set_material_index_in_component(GeometryComponent &component, const Field<bool> &selection_field, - const Field<int> &index_field) + const Field<int> &index_field, + const eAttrDomain domain) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + const int domain_size = component.attribute_domain_size(domain); if (domain_size == 0) { return; } MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + bke::GeometryFieldContext field_context{component, domain}; - AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", - ATTR_DOMAIN_FACE); + const bke::AttributeValidator validator = attributes.lookup_validator("material_index"); + AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", domain); fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); - evaluator.add_with_destination(index_field, indices.varray); + evaluator.add_with_destination(validator.validate_field_if_necessary(index_field), + indices.varray); evaluator.evaluate(); indices.finish(); } @@ -41,8 +43,10 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_mesh()) { - set_material_index_in_component( - geometry_set.get_component_for_write<MeshComponent>(), selection_field, index_field); + set_material_index_in_component(geometry_set.get_component_for_write<MeshComponent>(), + selection_field, + index_field, + ATTR_DOMAIN_FACE); } }); params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index da7977a4fb4..28d07b31218 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_pointcloud_types.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_point_radius_cc { @@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Points")); } -static void set_radius_in_component(GeometryComponent &component, +static void set_radius_in_component(PointCloud &pointcloud, const Field<bool> &selection_field, const Field<float> &radius_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (pointcloud.totpoint == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::PointCloudFieldContext field_context{pointcloud}; + fn::FieldEvaluator evaluator{field_context, pointcloud.totpoint}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray); evaluator.evaluate(); @@ -45,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_pointcloud()) { - set_radius_in_component(geometry_set.get_component_for_write<PointCloudComponent>(), - selection_field, - radii_field); + if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) { + set_radius_in_component(*pointcloud, selection_field, radii_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 880252de4fa..e243fe3614c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -8,6 +8,7 @@ #include "DNA_meshdata_types.h" #include "BKE_curves.hh" +#include "BKE_mesh.h" #include "node_geometry_util.hh" @@ -17,7 +18,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Geometry>(N_("Geometry")); b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); - b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(implicit_field_inputs::position); b.add_input<decl::Vector>(N_("Offset")).supports_field().subtype(PROP_TRANSLATION); b.add_output<decl::Geometry>(N_("Geometry")); } @@ -35,14 +36,14 @@ static void set_computed_position_and_offset(GeometryComponent &component, switch (component.type()) { case GEO_COMPONENT_TYPE_MESH: { Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write(); - MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert}; + MutableSpan<MVert> verts = mesh->verts_for_write(); if (in_positions.is_same(positions.varray)) { devirtualize_varray(in_offsets, [&](const auto in_offsets) { threading::parallel_for( selection.index_range(), grain_size, [&](const IndexRange range) { for (const int i : selection.slice(range)) { const float3 offset = in_offsets[i]; - add_v3_v3(mverts[i].co, offset); + add_v3_v3(verts[i].co, offset); } }); }); @@ -54,7 +55,7 @@ static void set_computed_position_and_offset(GeometryComponent &component, selection.index_range(), grain_size, [&](const IndexRange range) { for (const int i : selection.slice(range)) { const float3 new_position = in_positions[i] + in_offsets[i]; - copy_v3_v3(mverts[i].co, new_position); + copy_v3_v3(verts[i].co, new_position); } }); }); @@ -136,7 +137,7 @@ static void set_position_in_component(GeometryComponent &component, { eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; - GeometryComponentFieldContext field_context{component, domain}; + bke::GeometryFieldContext field_context{component, domain}; const int domain_size = component.attribute_domain_size(domain); if (domain_size == 0) { return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index e0cf0f98d58..0df51e49827 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_shade_smooth_cc { @@ -12,27 +14,25 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void set_smooth_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<bool> &shade_field) +static void set_smooth(Mesh &mesh, + const Field<bool> &selection_field, + const Field<bool> &shade_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + if (mesh.totpoly == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - AttributeWriter<bool> shades = attributes.lookup_or_add_for_write<bool>("shade_smooth", + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + AttributeWriter<bool> smooth = attributes.lookup_or_add_for_write<bool>("shade_smooth", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{field_context, mesh.totpoly}; evaluator.set_selection(selection_field); - evaluator.add_with_destination(shade_field, shades.varray); + evaluator.add_with_destination(shade_field, smooth.varray); evaluator.evaluate(); - shades.finish(); + smooth.finish(); } static void node_geo_exec(GeoNodeExecParams params) @@ -42,9 +42,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> shade_field = params.extract_input<Field<bool>>("Shade Smooth"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_mesh()) { - set_smooth_in_component( - geometry_set.get_component_for_write<MeshComponent>(), selection_field, shade_field); + if (Mesh *mesh = geometry_set.get_mesh_for_write()) { + set_smooth(*mesh, selection_field, shade_field); } }); params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index a35d8d66558..d8faa154477 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_spline_cyclic_cc { @@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void set_cyclic_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<bool> &cyclic_field) +static void set_cyclic(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<bool> &cyclic_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + if (curves.curves_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(cyclic_field, cyclics.varray); evaluator.evaluate(); @@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - set_cyclic_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_cyclic(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, cyclic_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index fcebc1116d7..d46ceac92ba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_spline_resolution_cc { @@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void set_resolution_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<int> &resolution_field) +static void set_resolution(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<int> &resolution_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + if (curves.curves_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(resolution_field, resolutions.varray); evaluator.evaluate(); @@ -38,12 +37,13 @@ static void set_resolution_in_component(GeometryComponent &component, static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); - Field<int> resolution_field = params.extract_input<Field<int>>("Resolution"); + Field<bool> selection = params.extract_input<Field<bool>>("Selection"); + Field<int> resolution = params.extract_input<Field<int>>("Resolution"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - set_resolution_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, resolution_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_resolution(bke::CurvesGeometry::wrap(curves_id->geometry), selection, resolution); + } }); params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index dbd68f4c783..3c85fd459e1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -30,7 +30,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -38,7 +38,7 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryStoreNamedAttribute *data = MEM_cnew<NodeGeometryStoreNamedAttribute>(__func__); data->data_type = CD_PROP_FLOAT; @@ -49,9 +49,9 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryStoreNamedAttribute &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); - bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_geometry = static_cast<bNodeSocket *>(node->inputs.first); bNodeSocket *socket_name = socket_geometry->next; bNodeSocket *socket_vector = socket_name->next; bNodeSocket *socket_float = socket_vector->next; @@ -71,10 +71,11 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) { const NodeDeclaration &declaration = *params.node_type().fixed_declaration; search_link_ops_for_declarations(params, declaration.inputs().take_front(2)); + search_link_ops_for_declarations(params, declaration.outputs().take_front(1)); - if (params.in_out() == SOCK_OUT) { + if (params.in_out() == SOCK_IN) { const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( - static_cast<eNodeSocketDatatype>(params.other_socket().type)); + eNodeSocketDatatype(params.other_socket().type)); if (type && *type != CD_PROP_STRING) { /* The input and output sockets have the same name. */ params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { @@ -86,56 +87,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -static void try_capture_field_on_geometry(GeometryComponent &component, - const StringRef name, - const eAttrDomain domain, - const GField &field, - std::atomic<bool> &r_failure) -{ - MutableAttributeAccessor attributes = *component.attributes_for_write(); - const int domain_size = attributes.domain_size(domain); - if (domain_size == 0) { - return; - } - - GeometryComponentFieldContext field_context{component, domain}; - const IndexMask mask{IndexMask(domain_size)}; - - const CPPType &type = field.cpp_type(); - const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type); - - /* 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(field, GMutableSpan{type, buffer, domain_size}); - evaluator.evaluate(); - - if (GAttributeWriter attribute = attributes.lookup_for_write(name)) { - 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; - } - } - - if (attributes.remove(name)) { - if (attributes.add(name, domain, data_type, bke::AttributeInitMove{buffer})) { - return; - } - } - - /* 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); - r_failure = true; -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -151,11 +102,11 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - params.used_named_attribute(name, eNamedAttrUsage::Write); + params.used_named_attribute(name, NamedAttributeUsage::Write); const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node()); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); + const eCustomDataType data_type = eCustomDataType(storage.data_type); + const eAttrDomain domain = eAttrDomain(storage.domain); GField field; switch (data_type) { @@ -191,7 +142,9 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_instances()) { GeometryComponent &component = geometry_set.get_component_for_write( GEO_COMPONENT_TYPE_INSTANCES); - try_capture_field_on_geometry(component, name, domain, field, failure); + if (!bke::try_capture_field_on_geometry(component, name, domain, field)) { + failure.store(true); + } } } else { @@ -200,7 +153,9 @@ static void node_geo_exec(GeoNodeExecParams params) {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { if (geometry_set.has(type)) { GeometryComponent &component = geometry_set.get_component_for_write(type); - try_capture_field_on_geometry(component, name, domain, field, failure); + if (!bke::try_capture_field_on_geometry(component, name, domain, field)) { + failure.store(true); + } } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index bb33430a02f..09c01b8c627 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -13,12 +13,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { - Vector<std::string> strings = params.extract_multi_input<std::string>("Strings"); + Vector<fn::ValueOrField<std::string>> strings = + params.extract_input<Vector<fn::ValueOrField<std::string>>>("Strings"); const std::string delim = params.extract_input<std::string>("Delimiter"); std::string output; for (const int i : strings.index_range()) { - output += strings[i]; + output += strings[i].as_value(); if (i < (strings.size() - 1)) { output += delim; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index afd7db6604d..769a63f58cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -6,6 +6,7 @@ #include "BKE_curve.h" #include "BKE_curve_legacy_convert.hh" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_vfont.h" #include "BLI_hash.h" @@ -76,7 +77,7 @@ static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) uiItemR(layout, ptr, "pivot_mode", 0, IFACE_("Pivot Point"), ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryStringToCurves *data = MEM_cnew<NodeGeometryStringToCurves>(__func__); @@ -85,7 +86,7 @@ static void node_init(bNodeTree *UNUSED(ntree), bNode *node) data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE; data->pivot_mode = GEO_NODE_STRING_TO_CURVES_PIVOT_MODE_BOTTOM_LEFT; node->storage = data; - node->id = (ID *)BKE_vfont_builtin_get(); + node->id = reinterpret_cast<ID *>(BKE_vfont_builtin_get()); } static void node_update(bNodeTree *ntree, bNode *node) @@ -93,11 +94,11 @@ static void node_update(bNodeTree *ntree, bNode *node) const NodeGeometryStringToCurves &storage = node_storage(*node); const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) storage.overflow; - bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next; + bNodeSocket *socket_remainder = static_cast<bNodeSocket *>(node->outputs.first)->next; nodeSetSocketAvailability( ntree, socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); - bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last; + bNodeSocket *height_socket = static_cast<bNodeSocket *>(node->inputs.last); nodeSetSocketAvailability( ntree, height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); } @@ -203,7 +204,7 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams ¶ms) cu.linedist = line_spacing; cu.vfont = vfont; cu.overflow = overflow; - cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__); + cu.tb = static_cast<TextBox *>(MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__)); cu.tb->w = textbox_w; cu.tb->h = textbox_h; cu.totbox = 1; @@ -213,8 +214,8 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams ¶ms) cu.len = len_bytes; cu.pos = len_chars; /* The reason for the additional character here is unknown, but reflects other code elsewhere. */ - cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__); - cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__); + cu.str = static_cast<char *>(MEM_mallocN(len_bytes + sizeof(char32_t), __func__)); + cu.strinfo = static_cast<CharInfo *>(MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__)); BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1); struct CharTrans *chartransdata = nullptr; @@ -226,7 +227,7 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams ¶ms) nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata); if (text_free) { - MEM_freeN((void *)r_text); + MEM_freeN(const_cast<char32_t *>(r_text)); } Span<CharInfo> info{cu.strinfo, text_len}; @@ -270,9 +271,9 @@ static std::optional<TextLayout> get_text_layout(GeoNodeExecParams ¶ms) /* Returns a mapping of UTF-32 character code to instance handle. */ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, TextLayout &layout, - InstancesComponent &instances) + bke::Instances &instances) { - VFont *vfont = (VFont *)params.node().id; + VFont *vfont = reinterpret_cast<VFont *>(params.node().id); Map<int, int> handles; bool pivot_required = params.output_is_required("Pivot Point"); @@ -315,13 +316,13 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, return handles; } -static void add_instances_from_handles(InstancesComponent &instances, +static void add_instances_from_handles(bke::Instances &instances, const Map<int, int> &char_handles, const TextLayout &layout) { instances.resize(layout.positions.size()); - MutableSpan<int> handles = instances.instance_reference_handles(); - MutableSpan<float4x4> transforms = instances.instance_transforms(); + MutableSpan<int> handles = instances.reference_handles(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(IndexRange(layout.positions.size()), 256, [&](IndexRange range) { for (const int i : range) { @@ -333,9 +334,9 @@ static void add_instances_from_handles(InstancesComponent &instances, static void create_attributes(GeoNodeExecParams ¶ms, const TextLayout &layout, - InstancesComponent &instances) + bke::Instances &instances) { - MutableAttributeAccessor attributes = *instances.attributes_for_write(); + MutableAttributeAccessor attributes = instances.attributes_for_write(); if (params.output_is_required("Line")) { StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line"); @@ -385,13 +386,12 @@ static void node_geo_exec(GeoNodeExecParams params) } /* Create and add instances. */ - GeometrySet geometry_set_out; - InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - Map<int, int> char_handles = create_curve_instances(params, *layout, instances); - add_instances_from_handles(instances, char_handles, *layout); - create_attributes(params, *layout, instances); + std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>(); + Map<int, int> char_handles = create_curve_instances(params, *layout, *instances); + add_instances_from_handles(*instances, char_handles, *layout); + create_attributes(params, *layout, *instances); - params.set_output("Curve Instances", std::move(geometry_set_out)); + params.set_output("Curve Instances", GeometrySet::create_with_instances(instances.release())); } } // namespace blender::nodes::node_geo_string_to_curves_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index eda6a51d412..2e6ad02bfd5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -39,13 +39,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE); uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__); data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; @@ -73,7 +73,7 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray) } else { crease = static_cast<float *>( - CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert)); + CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, nullptr, mesh.totvert)); } materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert}); } @@ -119,21 +119,19 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); - const int verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - const int edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - if (verts_num == 0 || edges_num == 0) { + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + if (mesh.totvert == 0 || mesh.totedge == 0) { return; } - GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator point_evaluator(point_context, verts_num); + bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT}; + FieldEvaluator point_evaluator(point_context, mesh.totvert); point_evaluator.add(vertex_crease_field); point_evaluator.evaluate(); const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0); - GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE}; - FieldEvaluator edge_evaluator(edge_context, edges_num); + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator(edge_context, mesh.totedge); edge_evaluator.add(edge_crease_field); edge_evaluator.evaluate(); const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0); @@ -162,17 +160,15 @@ static void node_geo_exec(GeoNodeExecParams params) subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( uv_smooth); - const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in); + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh); /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { return; } - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in); + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh); geometry_set.replace_mesh(mesh_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index ddc87e3dac4..36be68f1a22 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -73,12 +73,12 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Image>(N_("Output"), "Output_011"); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeSwitch *data = MEM_cnew<NodeSwitch>(__func__); data->input_type = SOCK_GEOMETRY; @@ -89,8 +89,8 @@ static void node_update(bNodeTree *ntree, bNode *node) { const NodeSwitch &storage = node_storage(*node); int index = 0; - bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first; - bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next; + bNodeSocket *field_switch = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *non_field_switch = static_cast<bNodeSocket *>(field_switch->next); const bool fields_type = ELEM( storage.input_type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA, SOCK_STRING); @@ -222,7 +222,7 @@ template<typename T> void switch_no_fields(GeoNodeExecParams ¶ms, const Stri static void node_geo_exec(GeoNodeExecParams params) { const NodeSwitch &storage = node_storage(params.node()); - const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.input_type); + const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.input_type); switch (data_type) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc deleted file mode 100644 index cd75822f665..00000000000 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ /dev/null @@ -1,826 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_generic_array.hh" -#include "BLI_kdopbvh.h" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_pointcloud_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_bvhutils.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_sample.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "NOD_socket_search_link.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_transfer_attribute_cc { - -using namespace blender::bke::mesh_surface_sample; - -NODE_STORAGE_FUNCS(NodeGeometryTransferAttribute) - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Source")) - .supported_type({GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES}); - - b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field(); - b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); - b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field(); - b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); - b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); - - b.add_input<decl::Vector>(N_("Source Position")) - .implicit_field() - .make_available([](bNode &node) { - node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; - }); - b.add_input<decl::Int>(N_("Index")).implicit_field().make_available([](bNode &node) { - node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_INDEX; - }); - - b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7}); - b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); - b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7}); - b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7}); - b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7}); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - const bNode &node = *static_cast<const bNode *>(ptr->data); - const NodeGeometryTransferAttribute &storage = node_storage(node); - const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) - storage.mode; - - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); - if (mapping != GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED) { - uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); - } -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryTransferAttribute *data = MEM_cnew<NodeGeometryTransferAttribute>(__func__); - data->data_type = CD_PROP_FLOAT; - data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - const NodeGeometryTransferAttribute &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) - storage.mode; - - bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first; - bNodeSocket *socket_vector = socket_geometry->next; - bNodeSocket *socket_float = socket_vector->next; - bNodeSocket *socket_color4f = socket_float->next; - bNodeSocket *socket_boolean = socket_color4f->next; - bNodeSocket *socket_int32 = socket_boolean->next; - - bNodeSocket *socket_positions = socket_int32->next; - bNodeSocket *socket_indices = socket_positions->next; - - nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32); - - nodeSetSocketAvailability(ntree, socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); - nodeSetSocketAvailability(ntree, socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); - - bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first; - bNodeSocket *out_socket_float = out_socket_vector->next; - bNodeSocket *out_socket_color4f = out_socket_float->next; - bNodeSocket *out_socket_boolean = out_socket_color4f->next; - bNodeSocket *out_socket_int32 = out_socket_boolean->next; - - nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); -} - -static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) -{ - const NodeDeclaration &declaration = *params.node_type().fixed_declaration; - search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); - search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); - - const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( - (eNodeSocketDatatype)params.other_socket().type); - if (type && *type != CD_PROP_STRING) { - /* The input and output sockets have the same name. */ - params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { - bNode &node = params.add_node("GeometryNodeAttributeTransfer"); - node_storage(node).data_type = *type; - params.update_and_connect_available_socket(node, "Attribute"); - }); - } -} - -static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(positions.size() >= r_indices.size()); - BLI_assert(positions.size() >= r_distances_sq.size()); - BLI_assert(positions.size() >= r_positions.size()); - - for (const int i : mask) { - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - const float3 position = positions[i]; - BLI_bvhtree_find_nearest( - tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); - if (!r_indices.is_empty()) { - r_indices[i] = nearest.index; - } - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = nearest.dist_sq; - } - if (!r_positions.is_empty()) { - r_positions[i] = nearest.co; - } - } -} - -static void get_closest_pointcloud_points(const PointCloud &pointcloud, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_indices, - const MutableSpan<float> r_distances_sq) -{ - BLI_assert(positions.size() >= r_indices.size()); - BLI_assert(pointcloud.totpoint > 0); - - BVHTreeFromPointCloud tree_data; - BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); - - for (const int i : mask) { - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - const float3 position = positions[i]; - BLI_bvhtree_find_nearest( - tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); - r_indices[i] = nearest.index; - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = nearest.dist_sq; - } - } - - free_bvhtree_from_pointcloud(&tree_data); -} - -static void get_closest_mesh_points(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_point_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totvert > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); - get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_edges(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_edge_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totedge > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); - get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_looptris(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_looptri_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totpoly > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); - get_closest_in_bvhtree( - tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_polygons(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_poly_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totpoly > 0); - - Array<int> looptri_indices(positions.size()); - get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); - - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; - - for (const int i : mask) { - const MLoopTri &looptri = looptris[looptri_indices[i]]; - r_poly_indices[i] = looptri.poly; - } -} - -/* The closest corner is defined to be the closest corner on the closest face. */ -static void get_closest_mesh_corners(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_corner_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totloop > 0); - Array<int> poly_indices(positions.size()); - get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {}); - - for (const int i : mask) { - const float3 position = positions[i]; - const int poly_index = poly_indices[i]; - const MPoly &poly = mesh.mpoly[poly_index]; - - /* Find the closest vertex in the polygon. */ - float min_distance_sq = FLT_MAX; - const MVert *closest_mvert; - int closest_loop_index = 0; - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const MLoop &loop = mesh.mloop[loop_index]; - const int vertex_index = loop.v; - const MVert &mvert = mesh.mvert[vertex_index]; - const float distance_sq = math::distance_squared(position, float3(mvert.co)); - if (distance_sq < min_distance_sq) { - min_distance_sq = distance_sq; - closest_loop_index = loop_index; - closest_mvert = &mvert; - } - } - if (!r_corner_indices.is_empty()) { - r_corner_indices[i] = closest_loop_index; - } - if (!r_positions.is_empty()) { - r_positions[i] = closest_mvert->co; - } - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = min_distance_sq; - } - } -} - -template<typename T> -void copy_with_indices(const VArray<T> &src, - const IndexMask mask, - const Span<int> indices, - const MutableSpan<T> dst) -{ - if (src.is_empty()) { - return; - } - for (const int i : mask) { - dst[i] = src[indices[i]]; - } -} - -template<typename T> -void copy_with_indices_clamped(const VArray<T> &src, - const IndexMask mask, - const VArray<int> &indices, - const MutableSpan<T> dst) -{ - if (src.is_empty()) { - return; - } - const int max_index = src.size() - 1; - threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { - for (const int i : range) { - const int index = mask[i]; - dst[index] = src[std::clamp(indices[index], 0, max_index)]; - } - }); -} - -template<typename T> -void copy_with_indices_and_comparison(const VArray<T> &src_1, - const VArray<T> &src_2, - const Span<float> distances_1, - const Span<float> distances_2, - const IndexMask mask, - const Span<int> indices_1, - const Span<int> indices_2, - const MutableSpan<T> dst) -{ - if (src_1.is_empty() || src_2.is_empty()) { - return; - } - for (const int i : mask) { - if (distances_1[i] < distances_2[i]) { - dst[i] = src_1[indices_1[i]]; - } - else { - dst[i] = src_2[indices_2[i]]; - } - } -} - -static bool component_is_available(const GeometrySet &geometry, - const GeometryComponentType type, - const eAttrDomain domain) -{ - if (!geometry.has(type)) { - return false; - } - const GeometryComponent &component = *geometry.get_component_for_read(type); - if (component.is_empty()) { - return false; - } - return component.attribute_domain_size(domain) != 0; -} - -/** - * \note Multi-threading for this function is provided by the field evaluator. Since the #call - * function could be called many times, calculate the data from the source geometry once and store - * it for later. - */ -class NearestInterpolatedTransferFunction : public fn::MultiFunction { - GeometrySet source_; - GField src_field_; - - /** - * This function is meant to sample the surface of a mesh rather than take the value from - * individual elements, so use the most complex domain, ensuring no information is lost. In the - * future, it should be possible to use the most complex domain required by the field inputs, to - * simplify sampling and avoid domain conversions. - */ - eAttrDomain domain_ = ATTR_DOMAIN_CORNER; - - fn::MFSignature signature_; - - std::optional<GeometryComponentFieldContext> source_context_; - std::unique_ptr<FieldEvaluator> source_evaluator_; - const GVArray *source_data_; - - public: - NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field) - : source_(std::move(geometry)), src_field_(std::move(src_field)) - { - source_.ensure_owns_direct_data(); - signature_ = this->create_signature(); - this->set_signature(&signature_); - this->evaluate_source_field(); - } - - fn::MFSignature create_signature() - { - blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"}; - signature.single_input<float3>("Position"); - signature.single_output("Attribute", src_field_.cpp_type()); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); - GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); - - const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); - BLI_assert(mesh_component.has_mesh()); - const Mesh &mesh = *mesh_component.get_for_read(); - BLI_assert(mesh.totpoly > 0); - - /* Find closest points on the mesh surface. */ - Array<int> looptri_indices(mask.min_array_size()); - Array<float3> sampled_positions(mask.min_array_size()); - get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); - - MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); - interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); - } - - private: - void evaluate_source_field() - { - const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); - source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_num = mesh_component.attribute_domain_size(domain_); - source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num); - source_evaluator_->add(src_field_); - source_evaluator_->evaluate(); - source_data_ = &source_evaluator_->get_evaluated(0); - } -}; - -/** - * \note Multi-threading for this function is provided by the field evaluator. Since the #call - * function could be called many times, calculate the data from the source geometry once and store - * it for later. - */ -class NearestTransferFunction : public fn::MultiFunction { - GeometrySet source_; - GField src_field_; - eAttrDomain domain_; - - fn::MFSignature signature_; - - bool use_mesh_; - bool use_points_; - - /* Store data from the source as a virtual array, since we may only access a few indices. */ - std::optional<GeometryComponentFieldContext> mesh_context_; - std::unique_ptr<FieldEvaluator> mesh_evaluator_; - const GVArray *mesh_data_; - - std::optional<GeometryComponentFieldContext> point_context_; - std::unique_ptr<FieldEvaluator> point_evaluator_; - const GVArray *point_data_; - - public: - NearestTransferFunction(GeometrySet geometry, GField src_field, eAttrDomain domain) - : source_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) - { - source_.ensure_owns_direct_data(); - signature_ = this->create_signature(); - this->set_signature(&signature_); - - this->use_mesh_ = component_is_available(source_, GEO_COMPONENT_TYPE_MESH, domain_); - this->use_points_ = component_is_available(source_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); - - this->evaluate_source_field(); - } - - fn::MFSignature create_signature() - { - blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"}; - signature.single_input<float3>("Position"); - signature.single_output("Attribute", src_field_.cpp_type()); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); - GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); - - if (!use_mesh_ && !use_points_) { - dst.type().value_initialize_indices(dst.data(), mask); - return; - } - - const Mesh *mesh = use_mesh_ ? source_.get_mesh_for_read() : nullptr; - const PointCloud *pointcloud = use_points_ ? source_.get_pointcloud_for_read() : nullptr; - - const int tot_samples = mask.min_array_size(); - - Array<int> point_indices; - Array<float> point_distances; - - /* Depending on where what domain the source attribute lives, these indices are either vertex, - * corner, edge or polygon indices. */ - Array<int> mesh_indices; - Array<float> mesh_distances; - - /* If there is a point cloud, find the closest points. */ - if (use_points_) { - point_indices.reinitialize(tot_samples); - if (use_mesh_) { - point_distances.reinitialize(tot_samples); - } - get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances); - } - - /* If there is a mesh, find the closest mesh elements. */ - if (use_mesh_) { - mesh_indices.reinitialize(tot_samples); - if (use_points_) { - mesh_distances.reinitialize(tot_samples); - } - switch (domain_) { - case ATTR_DOMAIN_POINT: { - get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - case ATTR_DOMAIN_EDGE: { - get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - case ATTR_DOMAIN_FACE: { - get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - case ATTR_DOMAIN_CORNER: { - get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - default: { - break; - } - } - } - - attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { - using T = decltype(dummy); - if (use_mesh_ && use_points_) { - VArray<T> src_mesh = mesh_data_->typed<T>(); - VArray<T> src_point = point_data_->typed<T>(); - copy_with_indices_and_comparison(src_mesh, - src_point, - mesh_distances, - point_distances, - mask, - mesh_indices, - point_indices, - dst.typed<T>()); - } - else if (use_points_) { - VArray<T> src_point = point_data_->typed<T>(); - copy_with_indices(src_point, mask, point_indices, dst.typed<T>()); - } - else if (use_mesh_) { - VArray<T> src_mesh = mesh_data_->typed<T>(); - copy_with_indices(src_mesh, mask, mesh_indices, dst.typed<T>()); - } - }); - } - - private: - void evaluate_source_field() - { - if (use_mesh_) { - const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); - const int domain_num = mesh.attribute_domain_size(domain_); - mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num); - mesh_evaluator_->add(src_field_); - mesh_evaluator_->evaluate(); - mesh_data_ = &mesh_evaluator_->get_evaluated(0); - } - - if (use_points_) { - const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); - const int domain_num = points.attribute_domain_size(domain_); - point_context_.emplace(GeometryComponentFieldContext(points, domain_)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num); - point_evaluator_->add(src_field_); - point_evaluator_->evaluate(); - point_data_ = &point_evaluator_->get_evaluated(0); - } - } -}; - -static const GeometryComponent *find_source_component(const GeometrySet &geometry, - const eAttrDomain domain) -{ - /* Choose the other component based on a consistent order, rather than some more complicated - * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ - static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES}; - for (const GeometryComponentType src_type : supported_types) { - if (component_is_available(geometry, src_type, domain)) { - return geometry.get_component_for_read(src_type); - } - } - - return nullptr; -} - -/** - * The index-based transfer theoretically does not need realized data when there is only one - * instance geometry set in the source. A future optimization could be removing that limitation - * internally. - */ -class IndexTransferFunction : public fn::MultiFunction { - GeometrySet src_geometry_; - GField src_field_; - eAttrDomain domain_; - - fn::MFSignature signature_; - - std::optional<GeometryComponentFieldContext> geometry_context_; - std::unique_ptr<FieldEvaluator> evaluator_; - const GVArray *src_data_ = nullptr; - - public: - IndexTransferFunction(GeometrySet geometry, GField src_field, const eAttrDomain domain) - : src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) - { - src_geometry_.ensure_owns_direct_data(); - - signature_ = this->create_signature(); - this->set_signature(&signature_); - - this->evaluate_field(); - } - - fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Attribute Transfer Index"}; - signature.single_input<int>("Index"); - signature.single_output("Attribute", src_field_.cpp_type()); - return signature.build(); - } - - void evaluate_field() - { - const GeometryComponent *component = find_source_component(src_geometry_, domain_); - if (component == nullptr) { - return; - } - const int domain_num = component->attribute_domain_size(domain_); - geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); - evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); - evaluator_->add(src_field_); - evaluator_->evaluate(); - src_data_ = &evaluator_->get_evaluated(0); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<int> &indices = params.readonly_single_input<int>(0, "Index"); - GMutableSpan dst = params.uninitialized_single_output(1, "Attribute"); - - const CPPType &type = dst.type(); - if (src_data_ == nullptr) { - type.value_initialize_indices(dst.data(), mask); - return; - } - - attribute_math::convert_to_static_type(type, [&](auto dummy) { - using T = decltype(dummy); - copy_with_indices_clamped(src_data_->typed<T>(), mask, indices, dst.typed<T>()); - }); - } -}; - -static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) -{ - switch (data_type) { - case CD_PROP_FLOAT: - return params.extract_input<Field<float>>("Attribute_001"); - case CD_PROP_FLOAT3: - return params.extract_input<Field<float3>>("Attribute"); - case CD_PROP_COLOR: - return params.extract_input<Field<ColorGeometry4f>>("Attribute_002"); - case CD_PROP_BOOL: - return params.extract_input<Field<bool>>("Attribute_003"); - case CD_PROP_INT32: - return params.extract_input<Field<int>>("Attribute_004"); - default: - BLI_assert_unreachable(); - } - return {}; -} - -static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) -{ - switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { - case CD_PROP_FLOAT: { - params.set_output("Attribute_001", Field<float>(field)); - break; - } - case CD_PROP_FLOAT3: { - params.set_output("Attribute", Field<float3>(field)); - break; - } - case CD_PROP_COLOR: { - params.set_output("Attribute_002", Field<ColorGeometry4f>(field)); - break; - } - case CD_PROP_BOOL: { - params.set_output("Attribute_003", Field<bool>(field)); - break; - } - case CD_PROP_INT32: { - params.set_output("Attribute_004", Field<int>(field)); - break; - } - default: - break; - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry = params.extract_input<GeometrySet>("Source"); - const NodeGeometryTransferAttribute &storage = node_storage(params.node()); - const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) - storage.mode; - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); - - GField field = get_input_attribute_field(params, data_type); - - auto return_default = [&]() { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - output_attribute_field(params, fn::make_constant_field<T>(T())); - }); - }; - - GField output_field; - switch (mapping) { - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { - const Mesh *mesh = geometry.get_mesh_for_read(); - if (mesh == nullptr) { - if (!geometry.is_empty()) { - params.error_message_add(NodeWarningType::Error, - TIP_("The source geometry must contain a mesh")); - } - return return_default(); - } - if (mesh->totpoly == 0) { - /* Don't add a warning for empty meshes. */ - if (mesh->totvert != 0) { - params.error_message_add(NodeWarningType::Error, - TIP_("The source mesh must have faces")); - } - return return_default(); - } - auto fn = std::make_unique<NearestInterpolatedTransferFunction>(std::move(geometry), - std::move(field)); - auto op = std::make_shared<FieldOperation>( - FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")})); - output_field = GField(std::move(op)); - break; - } - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { - if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { - params.error_message_add(NodeWarningType::Error, - TIP_("The source geometry must contain a mesh or a point cloud")); - return return_default(); - } - auto fn = std::make_unique<NearestTransferFunction>( - std::move(geometry), std::move(field), domain); - auto op = std::make_shared<FieldOperation>( - FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")})); - output_field = GField(std::move(op)); - break; - } - case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: { - Field<int> indices = params.extract_input<Field<int>>("Index"); - auto fn = std::make_unique<IndexTransferFunction>( - std::move(geometry), std::move(field), domain); - auto op = std::make_shared<FieldOperation>( - FieldOperation(std::move(fn), {std::move(indices)})); - output_field = GField(std::move(op)); - break; - } - } - - output_attribute_field(params, std::move(output_field)); -} - -} // namespace blender::nodes::node_geo_transfer_attribute_cc - -void register_node_type_geo_transfer_attribute() -{ - namespace file_ns = blender::nodes::node_geo_transfer_attribute_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage(&ntype, - "NodeGeometryTransferAttribute", - node_free_standard_storage, - node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - ntype.gather_link_search_ops = file_ns::node_gather_link_searches; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 945d5fbdcac..3c8a3f3ca76 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -11,6 +11,7 @@ #include "DNA_volume_types.h" #include "BKE_curves.hh" +#include "BKE_instances.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_volume.h" @@ -47,7 +48,7 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform) static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( "position", ATTR_DOMAIN_POINT); for (const int i : position.span.index_range()) { @@ -58,7 +59,7 @@ static void translate_pointcloud(PointCloud &pointcloud, const float3 translatio static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) { - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( "position", ATTR_DOMAIN_POINT); for (const int i : position.span.index_range()) { @@ -67,60 +68,77 @@ static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transfo position.finish(); } -static void translate_instances(InstancesComponent &instances, const float3 translation) +static void translate_instances(bke::Instances &instances, const float3 translation) { - MutableSpan<float4x4> transforms = instances.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); for (float4x4 &transform : transforms) { add_v3_v3(transform.ptr()[3], translation); } } -static void transform_instances(InstancesComponent &instances, const float4x4 &transform) +static void transform_instances(bke::Instances &instances, const float4x4 &transform) { - MutableSpan<float4x4> instance_transforms = instances.instance_transforms(); - for (float4x4 &instance_transform : instance_transforms) { + MutableSpan<float4x4> transforms = instances.transforms(); + for (float4x4 &instance_transform : transforms) { instance_transform = transform * instance_transform; } } -static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph) +static void transform_volume(GeoNodeExecParams ¶ms, + Volume &volume, + const float4x4 &transform, + const Depsgraph &depsgraph) { #ifdef WITH_OPENVDB - /* Scaling an axis to zero is not supported for volumes. */ - const float3 translation = transform.translation(); - const float3 rotation = transform.to_euler(); - const float3 scale = transform.scale(); - const float3 limited_scale = { - (scale.x == 0.0f) ? FLT_EPSILON : scale.x, - (scale.y == 0.0f) ? FLT_EPSILON : scale.y, - (scale.z == 0.0f) ? FLT_EPSILON : scale.z, - }; - const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale( - translation, rotation, limited_scale); - const Main *bmain = DEG_get_bmain(&depsgraph); BKE_volume_load(&volume, bmain); openvdb::Mat4s vdb_matrix; - memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4])); + memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4])); openvdb::Mat4d vdb_matrix_d{vdb_matrix}; + bool found_too_small_scale = false; const int grids_num = BKE_volume_num_grids(&volume); for (const int i : IndexRange(grids_num)) { VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i); - - openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false); - openvdb::math::Transform &grid_transform = grid->transform(); - grid_transform.postMult(vdb_matrix_d); + float4x4 grid_matrix; + BKE_volume_grid_transform_matrix(volume_grid, grid_matrix.values); + mul_m4_m4_pre(grid_matrix.values, transform.values); + const float determinant = determinant_m4(grid_matrix.values); + if (!BKE_volume_grid_determinant_valid(determinant)) { + found_too_small_scale = true; + /* Clear the tree because it is too small. */ + BKE_volume_grid_clear_tree(volume, *volume_grid); + if (determinant == 0) { + /* Reset rotation and scale. */ + copy_v3_fl3(grid_matrix.values[0], 1, 0, 0); + copy_v3_fl3(grid_matrix.values[1], 0, 1, 0); + copy_v3_fl3(grid_matrix.values[2], 0, 0, 1); + } + else { + /* Keep rotation but reset scale. */ + normalize_v3(grid_matrix.values[0]); + normalize_v3(grid_matrix.values[1]); + normalize_v3(grid_matrix.values[2]); + } + } + BKE_volume_grid_transform_matrix_set(volume_grid, grid_matrix.values); + } + if (found_too_small_scale) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Volume scale is lower than permitted by OpenVDB")); } #else - UNUSED_VARS(volume, transform, depsgraph); + UNUSED_VARS(params, volume, transform, depsgraph); #endif } -static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph) +static void translate_volume(GeoNodeExecParams ¶ms, + Volume &volume, + const float3 translation, + const Depsgraph &depsgraph) { - transform_volume(volume, float4x4::from_location(translation), depsgraph); + transform_volume(params, volume, float4x4::from_location(translation), depsgraph); } static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform) @@ -151,7 +169,8 @@ static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const f } } -static void translate_geometry_set(GeometrySet &geometry, +static void translate_geometry_set(GeoNodeExecParams ¶ms, + GeometrySet &geometry, const float3 translation, const Depsgraph &depsgraph) { @@ -165,17 +184,18 @@ static void translate_geometry_set(GeometrySet &geometry, translate_pointcloud(*pointcloud, translation); } if (Volume *volume = geometry.get_volume_for_write()) { - translate_volume(*volume, translation, depsgraph); + translate_volume(params, *volume, translation, depsgraph); } - if (geometry.has_instances()) { - translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation); + if (bke::Instances *instances = geometry.get_instances_for_write()) { + translate_instances(*instances, translation); } if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { translate_curve_edit_hints(*curve_edit_hints, translation); } } -void transform_geometry_set(GeometrySet &geometry, +void transform_geometry_set(GeoNodeExecParams ¶ms, + GeometrySet &geometry, const float4x4 &transform, const Depsgraph &depsgraph) { @@ -189,10 +209,10 @@ void transform_geometry_set(GeometrySet &geometry, transform_pointcloud(*pointcloud, transform); } if (Volume *volume = geometry.get_volume_for_write()) { - transform_volume(*volume, transform, depsgraph); + transform_volume(params, *volume, transform, depsgraph); } - if (geometry.has_instances()) { - transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform); + if (bke::Instances *instances = geometry.get_instances_for_write()) { + transform_instances(*instances, transform); } if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { transform_curve_edit_hints(*curve_edit_hints, transform); @@ -230,10 +250,11 @@ static void node_geo_exec(GeoNodeExecParams params) /* Use only translation if rotation and scale don't apply. */ if (use_translate(rotation, scale)) { - translate_geometry_set(geometry_set, translation, *params.depsgraph()); + translate_geometry_set(params, geometry_set, translation, *params.depsgraph()); } else { - transform_geometry_set(geometry_set, + transform_geometry_set(params, + geometry_set, float4x4::from_loc_eul_scale(translation, rotation, scale), *params.depsgraph()); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index ae538072e65..23052abddc4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "BKE_instances.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_translate_instances_cc { @@ -15,11 +17,10 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Instances")); } -static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +static void translate_instances(GeoNodeExecParams ¶ms, bke::Instances &instances) { - GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - - fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); @@ -29,16 +30,16 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i const VArray<float3> translations = evaluator.get_evaluated<float3>(0); const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(1); - MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + MutableSpan<float4x4> transforms = instances.transforms(); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { for (const int i_selection : range) { const int i = selection[i_selection]; if (local_spaces[i]) { - instance_transforms[i] *= float4x4::from_location(translations[i]); + transforms[i] *= float4x4::from_location(translations[i]); } else { - add_v3_v3(instance_transforms[i].values[3], translations[i]); + add_v3_v3(transforms[i].values[3], translations[i]); } } }); @@ -47,9 +48,8 @@ static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &i static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - translate_instances(params, instances); + if (bke::Instances *instances = geometry_set.get_instances_for_write()) { + translate_instances(params, *instances); } params.set_output("Instances", std::move(geometry_set)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 992470e8279..cedc1ef845b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -23,13 +23,13 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "quad_method", 0, "", ICON_NONE); uiItemR(layout, ptr, "ngon_method", 0, "", ICON_NONE); } -static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) +static void geo_triangulate_init(bNodeTree * /*tree*/, bNode *node) { node->custom1 = GEO_NODE_TRIANGULATE_QUAD_SHORTEDGE; node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY; @@ -47,9 +47,6 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, BMeshFromMeshParams from_mesh_params{}; from_mesh_params.calc_face_normal = true; from_mesh_params.calc_vert_normal = true; - from_mesh_params.add_key_index = true; - from_mesh_params.use_shapekey = true; - from_mesh_params.active_shapekey = 1; from_mesh_params.cd_mask_extra = cd_mask_extra; BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); @@ -71,21 +68,17 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4); - GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>( - params.node().custom1); - GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>( - params.node().custom2); + GeometryNodeTriangulateQuads quad_method = GeometryNodeTriangulateQuads(params.node().custom1); + GeometryNodeTriangulateNGons ngon_method = GeometryNodeTriangulateNGons(params.node().custom2); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_mesh()) { return; } - GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator{context, domain_size}; + bke::MeshFieldContext context{mesh_in, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator{context, mesh_in.totpoly}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc index 17413e64f7d..c2d27cffa93 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc @@ -5,6 +5,8 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_mesh.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_uv_pack_islands_cc { @@ -28,21 +30,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("UV")).field_source(); } -static VArray<float3> construct_uv_gvarray(const MeshComponent &component, +static VArray<float3> construct_uv_gvarray(const Mesh &mesh, const Field<bool> selection_field, const Field<float3> uv_field, const bool rotate, const float margin, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE); - GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator face_evaluator{face_context, face_num}; + bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; + FieldEvaluator face_evaluator{face_context, polys.size()}; face_evaluator.add(selection_field); face_evaluator.evaluate(); const IndexMask selection = face_evaluator.get_evaluated_as_mask(0); @@ -50,25 +50,24 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, return {}; } - const int corner_num = component.attribute_domain_size(ATTR_DOMAIN_CORNER); - GeometryComponentFieldContext corner_context{component, ATTR_DOMAIN_CORNER}; - FieldEvaluator evaluator{corner_context, corner_num}; - Array<float3> uv(corner_num); + bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER}; + FieldEvaluator evaluator{corner_context, mesh.totloop}; + Array<float3> uv(mesh.totloop); evaluator.add_with_destination(uv_field, uv.as_mutable_span()); evaluator.evaluate(); ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); for (const int mp_index : selection) { - const MPoly &mp = mesh->mpoly[mp_index]; + const MPoly &mp = polys[mp_index]; Array<ParamKey, 16> mp_vkeys(mp.totloop); Array<bool, 16> mp_pin(mp.totloop); Array<bool, 16> mp_select(mp.totloop); Array<const float *, 16> mp_co(mp.totloop); Array<float *, 16> mp_uv(mp.totloop); for (const int i : IndexRange(mp.totloop)) { - const MLoop &ml = mesh->mloop[mp.loopstart + i]; + const MLoop &ml = loops[mp.loopstart + i]; mp_vkeys[i] = ml.v; - mp_co[i] = mesh->mvert[ml.v].co; + mp_co[i] = verts[ml.v].co; mp_uv[i] = uv[mp.loopstart + i]; mp_pin[i] = false; mp_select[i] = false; @@ -88,11 +87,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); - return component.attributes()->adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } -class PackIslandsFieldInput final : public GeometryFieldInput { +class PackIslandsFieldInput final : public bke::MeshFieldInput { private: const Field<bool> selection_field; const Field<float3> uv_field; @@ -104,7 +103,7 @@ class PackIslandsFieldInput final : public GeometryFieldInput { const Field<float3> uv_field, const bool rotate, const float margin) - : GeometryFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"), + : bke::MeshFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"), selection_field(selection_field), uv_field(uv_field), rotate(rotate), @@ -113,16 +112,16 @@ class PackIslandsFieldInput final : public GeometryFieldInput { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_uv_gvarray( - mesh_component, selection_field, uv_field, rotate, margin, domain); - } - return {}; + return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain); + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_CORNER; } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc index 03657f3e016..e45ce6b42b4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -5,6 +5,8 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_mesh.h" + #include "UI_interface.h" #include "UI_resources.h" @@ -38,21 +40,21 @@ static void node_declare(NodeDeclarationBuilder &b) N_("UV coordinates between 0 and 1 for each face corner in the selected faces")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "method", 0, "", ICON_NONE); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryUVUnwrap *data = MEM_cnew<NodeGeometryUVUnwrap>(__func__); data->method = GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED; node->storage = data; } -static VArray<float3> construct_uv_gvarray(const MeshComponent &component, +static VArray<float3> construct_uv_gvarray(const Mesh &mesh, const Field<bool> selection_field, const Field<bool> seam_field, const bool fill_holes, @@ -60,14 +62,13 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, const GeometryNodeUVUnwrapMethod method, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE); - GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator face_evaluator{face_context, face_num}; + bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; + FieldEvaluator face_evaluator{face_context, polys.size()}; face_evaluator.add(selection_field); face_evaluator.evaluate(); const IndexMask selection = face_evaluator.get_evaluated_as_mask(0); @@ -75,27 +76,26 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, return {}; } - const int edge_num = component.attribute_domain_size(ATTR_DOMAIN_EDGE); - GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; - FieldEvaluator edge_evaluator{edge_context, edge_num}; + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator{edge_context, edges.size()}; edge_evaluator.add(seam_field); edge_evaluator.evaluate(); const IndexMask seam = edge_evaluator.get_evaluated_as_mask(0); - Array<float3> uv(mesh->totloop, float3(0)); + Array<float3> uv(loops.size(), float3(0)); ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); for (const int mp_index : selection) { - const MPoly &mp = mesh->mpoly[mp_index]; + const MPoly &mp = polys[mp_index]; Array<ParamKey, 16> mp_vkeys(mp.totloop); Array<bool, 16> mp_pin(mp.totloop); Array<bool, 16> mp_select(mp.totloop); Array<const float *, 16> mp_co(mp.totloop); Array<float *, 16> mp_uv(mp.totloop); for (const int i : IndexRange(mp.totloop)) { - const MLoop &ml = mesh->mloop[mp.loopstart + i]; + const MLoop &ml = loops[mp.loopstart + i]; mp_vkeys[i] = ml.v; - mp_co[i] = mesh->mvert[ml.v].co; + mp_co[i] = verts[ml.v].co; mp_uv[i] = uv[mp.loopstart + i]; mp_pin[i] = false; mp_select[i] = false; @@ -110,7 +110,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, mp_select.data()); } for (const int i : seam) { - const MEdge &edge = mesh->medge[i]; + const MEdge &edge = edges[i]; ParamKey vkeys[2]{edge.v1, edge.v2}; GEO_uv_parametrizer_edge_set_seam(handle, vkeys); } @@ -126,11 +126,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); - return component.attributes()->adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } -class UnwrapFieldInput final : public GeometryFieldInput { +class UnwrapFieldInput final : public bke::MeshFieldInput { private: const Field<bool> selection; const Field<bool> seam; @@ -144,7 +144,7 @@ class UnwrapFieldInput final : public GeometryFieldInput { const bool fill_holes, const float margin, const GeometryNodeUVUnwrapMethod method) - : GeometryFieldInput(CPPType::get<float3>(), "UV Unwrap Field"), + : bke::MeshFieldInput(CPPType::get<float3>(), "UV Unwrap Field"), selection(selection), seam(seam), fill_holes(fill_holes), @@ -154,16 +154,16 @@ class UnwrapFieldInput final : public GeometryFieldInput { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_uv_gvarray( - mesh_component, selection, seam, fill_holes, margin, method, domain); - } - return {}; + return construct_uv_gvarray(mesh, selection, seam, fill_holes, margin, method, domain); + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_CORNER; } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 6979693e215..e9050f9e6a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -6,7 +6,7 @@ #include "UI_resources.h" #include "ED_node.h" -#include "ED_spreadsheet.h" +#include "ED_viewer_path.hh" #include "NOD_socket_search_link.hh" @@ -26,15 +26,21 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_input<decl::Bool>(N_("Value"), "Value_004").supports_field().hide_value(); } -static void node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryViewer *data = MEM_cnew<NodeGeometryViewer>(__func__); data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_AUTO; node->storage = data; } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_layout_ex(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); } @@ -61,7 +67,7 @@ static eNodeSocketDatatype custom_data_type_to_socket_type(const eCustomDataType static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryViewer &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); + const eCustomDataType data_type = eCustomDataType(storage.data_type); const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type); LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { @@ -79,7 +85,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) SpaceNode *snode = CTX_wm_space_node(¶ms.C); Main *bmain = CTX_data_main(¶ms.C); ED_node_set_active(bmain, snode, ¶ms.node_tree, &viewer_node, nullptr); - ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, &viewer_node); + ed::viewer_path::activate_geometry_node(*bmain, *snode, viewer_node); }; const std::optional<eCustomDataType> type = node_socket_to_custom_data_type( @@ -132,7 +138,9 @@ void register_node_type_geo_viewer() node_type_update(&ntype, file_ns::node_update); node_type_init(&ntype, file_ns::node_init); ntype.declare = file_ns::node_declare; - ntype.draw_buttons_ex = file_ns::node_layout; + ntype.draw_buttons = file_ns::node_layout; + ntype.draw_buttons_ex = file_ns::node_layout_ex; ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc index d7e9e38ea0d..7d439309380 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc @@ -75,13 +75,12 @@ class Grid3DFieldContext : public FieldContext { int64_t points_num() const { - return static_cast<int64_t>(resolution_.x) * static_cast<int64_t>(resolution_.y) * - static_cast<int64_t>(resolution_.z); + return int64_t(resolution_.x) * int64_t(resolution_.y) * int64_t(resolution_.z); } GVArray get_varray_for_input(const FieldInput &field_input, - const IndexMask UNUSED(mask), - ResourceScope &UNUSED(scope)) const + const IndexMask /*mask*/, + ResourceScope & /*scope*/) const { const bke::AttributeFieldInput *attribute_field_input = dynamic_cast<const bke::AttributeFieldInput *>(&field_input); @@ -113,9 +112,9 @@ class Grid3DFieldContext : public FieldContext { } }; -#ifdef WITH_OPENVDB static void node_geo_exec(GeoNodeExecParams params) { +#ifdef WITH_OPENVDB const float3 bounds_min = params.extract_input<float3>("Min"); const float3 bounds_max = params.extract_input<float3>("Max"); @@ -137,6 +136,14 @@ static void node_geo_exec(GeoNodeExecParams params) return; } + const double3 scale_fac = double3(bounds_max - bounds_min) / double3(resolution - 1); + if (!BKE_volume_grid_determinant_valid(scale_fac.x * scale_fac.y * scale_fac.z)) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Volume scale is lower than permitted by OpenVDB")); + params.set_default_remaining_outputs(); + return; + } + Field<float> input_field = params.extract_input<Field<float>>("Density"); /* Evaluate input field on a 3D grid. */ @@ -157,12 +164,11 @@ static void node_geo_exec(GeoNodeExecParams params) openvdb::tools::copyFromDense(dense_grid, *grid, 0.0f); grid->transform().preTranslate(openvdb::math::Vec3<float>(-0.5f)); - const float3 scale_fac = (bounds_max - bounds_min) / float3(resolution - 1); - grid->transform().postScale(openvdb::math::Vec3<float>(scale_fac.x, scale_fac.y, scale_fac.z)); + grid->transform().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z)); grid->transform().postTranslate( openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z)); - Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + Volume *volume = reinterpret_cast<Volume *>(BKE_id_new_nomain(ID_VO, nullptr)); BKE_volume_init_grids(volume); BKE_volume_grid_add_vdb(*volume, "density", std::move(grid)); @@ -170,16 +176,12 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet r_geometry_set; r_geometry_set.replace_volume(volume); params.set_output("Volume", r_geometry_set); -} - #else -static void node_geo_exec(GeoNodeExecParams params) -{ + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_default_remaining_outputs(); +#endif } -#endif /* WITH_OPENVDB */ } // namespace blender::nodes::node_geo_volume_cube_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 91429560ac8..88e7718ed3c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -48,14 +48,14 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); } -static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; @@ -123,9 +123,9 @@ static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> gri Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); for (const int i : grids.index_range()) { const bke::OpenVDBMeshData &data = mesh_data[i]; @@ -187,20 +187,19 @@ static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParam static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); - #ifdef WITH_OPENVDB + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { Mesh *mesh = create_mesh_from_volume(geometry_set, params); geometry_set.replace_mesh(mesh); geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); }); + params.set_output("Mesh", std::move(geometry_set)); #else + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - - params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes::node_geo_volume_to_mesh_cc |