diff options
Diffstat (limited to 'source/blender/nodes/geometry/nodes')
116 files changed, 4680 insertions, 2537 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 13a9cdc8600..33a58cada1b 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; @@ -285,6 +285,12 @@ template<typename T> class AccumulateFieldInput final : public bke::GeometryFiel } return false; } + + std::optional<eAttrDomain> preferred_domain( + const GeometryComponent & /*component*/) const override + { + return source_domain_; + } }; template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput { @@ -355,6 +361,12 @@ template<typename T> class TotalFieldInput final : public bke::GeometryFieldInpu } 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) { @@ -413,8 +425,8 @@ void register_node_type_geo_accumulate_field() geo_node_type_base(&ntype, GEO_NODE_ACCUMULATE_FIELD, "Accumulate Field", NODE_CLASS_CONVERTER); ntype.geometry_node_execute = file_ns::node_geo_exec; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; ntype.draw_buttons = file_ns::node_layout; ntype.declare = file_ns::node_declare; ntype.gather_link_search_ops = file_ns::node_gather_link_searches; 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 8c11288efdd..1a0cb14f451 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; - } - bke::GeometryFieldContext 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); } } }); @@ -268,8 +241,8 @@ void register_node_type_geo_attribute_capture() "NodeGeometryAttributeCapture", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = 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; 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..d31366f9c93 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; @@ -132,7 +132,7 @@ void register_node_type_geo_attribute_domain_size() ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; ntype.draw_buttons = file_ns::node_layout; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; nodeRegisterType(&ntype); 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 af0007c2fa4..e381133af30 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"); @@ -396,8 +396,8 @@ void register_node_type_geo_attribute_statistic() &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; 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; diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index c8c58945bce..61780ee25bb 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; } @@ -148,7 +148,8 @@ 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); @@ -190,7 +191,7 @@ void register_node_type_geo_boolean() ntype.declare = file_ns::node_declare; ntype.draw_buttons = file_ns::node_layout; ntype.updatefunc = file_ns::node_update; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } 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..6c41dbbe34c 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()); @@ -93,26 +93,26 @@ static void node_geo_exec(GeoNodeExecParams params) if (!reset_children) { add_v3_v3(transform.values[3], child_collection->instance_offset); if (use_relative_transform) { - mul_m4_m4_pre(transform.values, self_object->imat); + mul_m4_m4_pre(transform.values, self_object->world_to_object); } else { 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) { - transform = self_object->imat; + transform = self_object->world_to_object; } else { sub_v3_v3(transform.values[3], collection->instance_offset); } - mul_m4_m4_post(transform.values, child_object->obmat); + mul_m4_m4_post(transform.values, child_object->object_to_world); } entries.append({handle, &(child_object->id.name[2]), transform}); } @@ -123,21 +123,21 @@ 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 { float4x4 transform = float4x4::identity(); if (use_relative_transform) { copy_v3_v3(transform.values[3], collection->instance_offset); - mul_m4_m4_pre(transform.values, self_object->imat); + mul_m4_m4_pre(transform.values, self_object->world_to_object); } - 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 @@ -150,7 +150,7 @@ void register_node_type_geo_collection_info() geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_node_init); + ntype.initfunc = file_ns::node_node_init; node_type_storage(&ntype, "NodeGeometryCollectionInfo", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc index 531d37c3a89..90fb7e10570 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_common.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc @@ -23,7 +23,7 @@ void register_node_type_geo_group() node_type_size(&ntype, 140, 60, 400); ntype.labelfunc = node_group_label; - node_type_group_update(&ntype, node_group_update); + ntype.group_update_func = node_group_update; nodeRegisterType(&ntype); } 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 c8b692651e9..278d7c4bd24 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -56,7 +56,7 @@ 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. */ @@ -81,13 +81,13 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) 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 = edges[edge_index]; edge.v1 = v_from; edge.v2 = v_to; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; /* Write edge index into both loops that have it. */ int reverse_index = plConvexHullGetReversedLoopIndex(hull, i); @@ -101,7 +101,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) MEdge &edge = edges[0]; edge.v1 = 0; edge.v2 = 1; - edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE; + edge.flag |= ME_EDGEDRAW | ME_LOOSEEDGE; edge_index++; } BLI_assert(edge_index == edges_num); 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 28d979facac..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 @@ -42,7 +42,7 @@ class EndpointFieldInput final : public bke::CurvesFieldInput { GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { if (domain != ATTR_DOMAIN_POINT) { return {}; @@ -90,6 +90,11 @@ class EndpointFieldInput final : public bke::CurvesFieldInput { } 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 c675ee472cd..a14661b4a50 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__); @@ -85,12 +85,12 @@ static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result) 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; edges[i].v2 = result.edge[i].second; - edges[i].flag = ME_EDGEDRAW | ME_EDGERENDER; + edges[i].flag = ME_EDGEDRAW; } int i_loop = 0; for (const int i : IndexRange(result.face.size())) { @@ -155,7 +155,7 @@ void register_node_type_geo_curve_fill() geo_node_type_base(&ntype, GEO_NODE_FILL_CURVE, "Fill Curve", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage( &ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; 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 4586bb24464..7a6fa799013 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); } @@ -121,8 +121,8 @@ void register_node_type_geo_curve_fillet() node_type_storage( &ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } 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 b34b22e995d..9f0d40bb0d7 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__); @@ -97,7 +97,7 @@ class HandleTypeFieldInput final : public bke::CurvesFieldInput { 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 @@ -108,6 +108,11 @@ class HandleTypeFieldInput final : public bke::CurvesFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const CurvesGeometry & /*curves*/) const + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) @@ -133,7 +138,7 @@ void register_node_type_geo_curve_handle_type_selection() &ntype, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, "Handle Type Selection", NODE_CLASS_INPUT); ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryCurveSelectHandles", node_free_standard_storage, 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..e0148730710 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, @@ -359,8 +359,8 @@ void register_node_type_geo_curve_primitive_arc() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_ARC, "Arc", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage(&ntype, "NodeGeometryCurvePrimitiveArc", node_free_standard_storage, 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..59c17365261 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__); @@ -119,7 +119,7 @@ void register_node_type_geo_curve_primitive_bezier_segment() static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, "Bezier Segment", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryCurvePrimitiveBezierSegment", node_free_standard_storage, 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..75f2116b237 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 @@ -217,8 +217,8 @@ void register_node_type_geo_curve_primitive_circle() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage(&ntype, "NodeGeometryCurvePrimitiveCircle", node_free_standard_storage, 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..d4ce1923178 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; @@ -119,8 +119,8 @@ void register_node_type_geo_curve_primitive_line() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage(&ntype, "NodeGeometryCurvePrimitiveLine", node_free_standard_storage, 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..d4e37a98372 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"), @@ -275,8 +274,8 @@ void register_node_type_geo_curve_primitive_quadrilateral() ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; - node_type_update(&ntype, file_ns::node_update); - node_type_init(&ntype, file_ns::node_init); + ntype.updatefunc = file_ns::node_update; + ntype.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryCurvePrimitiveQuad", node_free_standard_storage, 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 41eafe2a741..23a71af448d 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); @@ -123,8 +123,8 @@ void register_node_type_geo_curve_resample() ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } 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 0169ead5bd2..040ebf55ec5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -19,6 +19,8 @@ static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_curves()) { return; 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..c1f631a86fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_devirtualize_parameters.hh" +#include "BLI_generic_array.hh" #include "BLI_length_parameterize.hh" #include "BKE_curves.hh" @@ -8,6 +9,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_sample_cc { @@ -16,9 +19,16 @@ NODE_STORAGE_FUNCS(NodeGeometryCurveSample) static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>(N_("Curve")) + b.add_input<decl::Geometry>(N_("Curves")) .only_realized_data() .supported_type(GEO_COMPONENT_TYPE_CURVE); + + 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::Float>(N_("Factor")) .min(0.0f) .max(1.0f) @@ -30,20 +40,34 @@ static void node_declare(NodeDeclarationBuilder &b) .subtype(PROP_DISTANCE) .supports_field() .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }); + b.add_input<decl::Int>(N_("Curve Index")).supports_field().make_available([](bNode &node) { + node_storage(node).use_all_curves = false; + }); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field(); + b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field(); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field(); + b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field(); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field(); + b.add_output<decl::Vector>(N_("Position")).dependent_field(); b.add_output<decl::Vector>(N_("Tangent")).dependent_field(); 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, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_all_curves", 0, nullptr, ICON_NONE); } -static void node_type_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree * /*tree*/, bNode *node) { NodeGeometryCurveSample *data = MEM_cnew<NodeGeometryCurveSample>(__func__); - data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH; + data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR; + data->use_all_curves = false; + data->data_type = CD_PROP_FLOAT; node->storage = data; } @@ -51,16 +75,62 @@ static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryCurveSample &storage = node_storage(*node); const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; + const eCustomDataType data_type = eCustomDataType(storage.data_type); + + bNodeSocket *in_socket_float = static_cast<bNodeSocket *>(node->inputs.first)->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; - bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *factor = in_socket_bool->next; bNodeSocket *length = factor->next; + bNodeSocket *curve_index = length->next; nodeSetSocketAvailability(ntree, factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(ntree, curve_index, !storage.use_all_curves); + + 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_front(4)); + search_link_ops_for_declarations(params, declaration.outputs().take_front(3)); + + 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("GeometryNodeSampleCurve"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } } static void sample_indices_and_lengths(const Span<float> accumulated_lengths, const Span<float> sample_lengths, + const GeometryNodeCurveSampleMode length_mode, const IndexMask mask, MutableSpan<int> r_segment_indices, MutableSpan<float> r_length_in_segment) @@ -70,10 +140,13 @@ static void sample_indices_and_lengths(const Span<float> accumulated_lengths, mask.to_best_mask_type([&](const auto mask) { for (const int64_t i : mask) { + const float sample_length = length_mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? + sample_lengths[i] * total_length : + sample_lengths[i]; int segment_i; float factor_in_segment; length_parameterize::sample_at_length(accumulated_lengths, - std::clamp(sample_lengths[i], 0.0f, total_length), + std::clamp(sample_length, 0.0f, total_length), segment_i, factor_in_segment, &hint); @@ -89,6 +162,7 @@ static void sample_indices_and_lengths(const Span<float> accumulated_lengths, static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths, const Span<float> sample_lengths, + const GeometryNodeCurveSampleMode length_mode, const IndexMask mask, MutableSpan<int> r_segment_indices, MutableSpan<float> r_factor_in_segment) @@ -96,16 +170,32 @@ static void sample_indices_and_factors_to_compressed(const Span<float> accumulat const float total_length = accumulated_lengths.last(); length_parameterize::SampleSegmentHint hint; - mask.to_best_mask_type([&](const auto mask) { - for (const int64_t i : IndexRange(mask.size())) { - const float length = sample_lengths[mask[i]]; - length_parameterize::sample_at_length(accumulated_lengths, - std::clamp(length, 0.0f, total_length), - r_segment_indices[i], - r_factor_in_segment[i], - &hint); - } - }); + switch (length_mode) { + case GEO_NODE_CURVE_SAMPLE_FACTOR: + mask.to_best_mask_type([&](const auto mask) { + for (const int64_t i : IndexRange(mask.size())) { + const float length = sample_lengths[mask[i]] * total_length; + length_parameterize::sample_at_length(accumulated_lengths, + std::clamp(length, 0.0f, total_length), + r_segment_indices[i], + r_factor_in_segment[i], + &hint); + } + }); + break; + case GEO_NODE_CURVE_SAMPLE_LENGTH: + mask.to_best_mask_type([&](const auto mask) { + for (const int64_t i : IndexRange(mask.size())) { + const float length = sample_lengths[mask[i]]; + length_parameterize::sample_at_length(accumulated_lengths, + std::clamp(length, 0.0f, total_length), + r_segment_indices[i], + r_factor_in_segment[i], + &hint); + } + }); + break; + } } /** @@ -115,10 +205,12 @@ static void sample_indices_and_factors_to_compressed(const Span<float> accumulat class SampleFloatSegmentsFunction : public fn::MultiFunction { private: Array<float> accumulated_lengths_; + GeometryNodeCurveSampleMode length_mode_; public: - SampleFloatSegmentsFunction(Array<float> accumulated_lengths) - : accumulated_lengths_(std::move(accumulated_lengths)) + SampleFloatSegmentsFunction(Array<float> accumulated_lengths, + const GeometryNodeCurveSampleMode length_mode) + : accumulated_lengths_(std::move(accumulated_lengths)), length_mode_(length_mode) { static fn::MFSignature signature = create_signature(); this->set_signature(&signature); @@ -134,14 +226,15 @@ 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"); MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>( 2, "Length in Curve"); - sample_indices_and_lengths(accumulated_lengths_, lengths, mask, indices, lengths_in_segments); + sample_indices_and_lengths( + accumulated_lengths_, lengths, length_mode_, mask, indices, lengths_in_segments); } }; @@ -153,15 +246,27 @@ class SampleCurveFunction : public fn::MultiFunction { * that the curve is not freed before the function can execute. */ GeometrySet geometry_set_; + GField src_field_; + GeometryNodeCurveSampleMode length_mode_; + + fn::MFSignature signature_; + + std::optional<bke::CurvesFieldContext> source_context_; + std::unique_ptr<FieldEvaluator> source_evaluator_; + const GVArray *source_data_; public: - SampleCurveFunction(GeometrySet geometry_set) : geometry_set_(std::move(geometry_set)) + SampleCurveFunction(GeometrySet geometry_set, + const GeometryNodeCurveSampleMode length_mode, + const GField &src_field) + : geometry_set_(std::move(geometry_set)), src_field_(src_field), length_mode_(length_mode) { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); + signature_ = create_signature(); + this->set_signature(&signature_); + this->evaluate_source(); } - static fn::MFSignature create_signature() + fn::MFSignature create_signature() { blender::fn::MFSignatureBuilder signature{"Sample Curve"}; signature.single_input<int>("Curve Index"); @@ -169,10 +274,11 @@ class SampleCurveFunction : public fn::MultiFunction { signature.single_output<float3>("Position"); signature.single_output<float3>("Tangent"); signature.single_output<float3>("Normal"); + signature.single_output("Value", src_field_.cpp_type()); 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"); @@ -180,6 +286,7 @@ class SampleCurveFunction : public fn::MultiFunction { 3, "Tangent"); MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>( 4, "Normal"); + GMutableSpan sampled_values = params.uninitialized_single_output_if_required(5, "Value"); auto return_default = [&]() { if (!sampled_positions.is_empty()) { @@ -202,6 +309,7 @@ class SampleCurveFunction : public fn::MultiFunction { if (curves.points_num() == 0) { return return_default(); } + curves.ensure_can_interpolate_to_evaluated(); Span<float3> evaluated_positions = curves.evaluated_positions(); Span<float3> evaluated_tangents; Span<float3> evaluated_normals; @@ -218,18 +326,40 @@ class SampleCurveFunction : public fn::MultiFunction { Array<int> indices; Array<float> factors; + GArray<> src_original_values(source_data_->type()); + GArray<> src_evaluated_values(source_data_->type()); + + auto fill_invalid = [&](const IndexMask mask) { + if (!sampled_positions.is_empty()) { + sampled_positions.fill_indices(mask, float3(0)); + } + if (!sampled_tangents.is_empty()) { + sampled_tangents.fill_indices(mask, float3(0)); + } + if (!sampled_normals.is_empty()) { + sampled_normals.fill_indices(mask, float3(0)); + } + if (!sampled_values.is_empty()) { + attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) { + using T = decltype(dummy); + sampled_values.typed<T>().fill_indices(mask, {}); + }); + } + }; auto sample_curve = [&](const int curve_i, const IndexMask mask) { + const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i, + cyclic[curve_i]); + if (accumulated_lengths.is_empty()) { + fill_invalid(mask); + return; + } /* Store the sampled indices and factors in arrays the size of the mask. * Then, during interpolation, move the results back to the masked indices. */ indices.reinitialize(mask.size()); factors.reinitialize(mask.size()); sample_indices_and_factors_to_compressed( - curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]), - lengths, - mask, - indices, - factors); + accumulated_lengths, lengths, length_mode_, mask, indices, factors); const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i); if (!sampled_positions.is_empty()) { @@ -254,54 +384,67 @@ class SampleCurveFunction : public fn::MultiFunction { sampled_normals[i] = math::normalize(sampled_normals[i]); } } + if (!sampled_values.is_empty()) { + const IndexRange points = curves.points_for_curve(curve_i); + src_original_values.reinitialize(points.size()); + source_data_->materialize_compressed_to_uninitialized(points, src_original_values.data()); + src_evaluated_values.reinitialize(curves.evaluated_points_for_curve(curve_i).size()); + curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values); + attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) { + using T = decltype(dummy); + const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>(); + MutableSpan<T> sampled_values_typed = sampled_values.typed<T>(); + length_parameterize::interpolate_to_masked<T>( + src_evaluated_values_typed, indices, factors, mask, sampled_values_typed); + }); + } }; - if (curve_indices.is_single()) { - sample_curve(curve_indices.get_internal_single(), mask); + if (const std::optional<int> curve_i = curve_indices.get_if_single()) { + if (curves.curves_range().contains(*curve_i)) { + sample_curve(*curve_i, mask); + } + else { + fill_invalid(mask); + } } else { + Vector<int64_t> invalid_indices; MultiValueMap<int, int64_t> indices_per_curve; devirtualize_varray(curve_indices, [&](const auto curve_indices) { for (const int64_t i : mask) { - indices_per_curve.add(curve_indices[i], i); + const int curve_i = curve_indices[i]; + if (curves.curves_range().contains(curve_i)) { + indices_per_curve.add(curve_i, i); + } + else { + invalid_indices.append(i); + } } }); for (const int curve_i : indices_per_curve.keys()) { sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i))); } + fill_invalid(IndexMask(invalid_indices)); } } -}; -/** - * Pre-process the lengths or factors used for the sampling, turning factors into lengths, and - * clamping between zero and the total length of the curves. Do this as a separate operation in the - * field tree to make the sampling simpler, and to let the evaluator optimize better. - * - * \todo Use a mutable single input instead when they are supported. - */ -static Field<float> get_length_input_field(GeoNodeExecParams params, - const GeometryNodeCurveSampleMode mode, - const float curves_total_length) -{ - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - return params.extract_input<Field<float>>("Length"); + private: + void evaluate_source() + { + const Curves &curves_id = *geometry_set_.get_curves_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + source_context_.emplace(bke::CurvesFieldContext{curves, ATTR_DOMAIN_POINT}); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num()); + source_evaluator_->add(src_field_); + source_evaluator_->evaluate(); + source_data_ = &source_evaluator_->get_evaluated(0); } - - /* Convert the factor to a length. */ - Field<float> factor_field = params.get_input<Field<float>>("Factor"); - auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>( - __func__, - [curves_total_length](float factor) { return factor * curves_total_length; }, - fn::CustomMF_presets::AllSpanOrSingle()); - - return Field<float>(FieldOperation::Create(std::move(clamp_fn), {std::move(factor_field)}), 0); -} +}; static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves) { - curves.ensure_evaluated_lengths(); Array<float> curve_lengths(curves.curves_num()); const VArray<bool> cyclic = curves.cyclic(); @@ -313,9 +456,56 @@ static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves) return curve_lengths; } +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_set = params.extract_input<GeometrySet>("Curve"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curves"); if (!geometry_set.has_curves()) { params.set_default_remaining_outputs(); return; @@ -328,34 +518,49 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - Array<float> curve_lengths = curve_accumulated_lengths(curves); - const float total_length = curve_lengths.last(); - if (total_length == 0.0f) { - params.set_default_remaining_outputs(); - return; - } + curves.ensure_evaluated_lengths(); const NodeGeometryCurveSample &storage = node_storage(params.node()); const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; - Field<float> length_field = get_length_input_field(params, mode, total_length); + const eCustomDataType data_type = eCustomDataType(storage.data_type); - auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set)); + Field<float> length_field = params.extract_input<Field<float>>( + mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? "Factor" : "Length"); + GField src_values_field = get_input_attribute_field(params, data_type); std::shared_ptr<FieldOperation> sample_op; if (curves.curves_num() == 1) { - sample_op = FieldOperation::Create(std::move(sample_fn), - {fn::make_constant_field<int>(0), std::move(length_field)}); + sample_op = FieldOperation::Create( + std::make_unique<SampleCurveFunction>( + std::move(geometry_set), mode, std::move(src_values_field)), + {fn::make_constant_field<int>(0), std::move(length_field)}); } else { - auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(std::move(curve_lengths)); - auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)}); - sample_op = FieldOperation::Create(std::move(sample_fn), - {Field<int>(index_op, 0), Field<float>(index_op, 1)}); + if (storage.use_all_curves) { + auto index_fn = std::make_unique<SampleFloatSegmentsFunction>( + curve_accumulated_lengths(curves), mode); + auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)}); + Field<int> curve_index = Field<int>(index_op, 0); + Field<float> length_in_curve = Field<float>(index_op, 1); + sample_op = FieldOperation::Create( + std::make_unique<SampleCurveFunction>( + std::move(geometry_set), GEO_NODE_CURVE_SAMPLE_LENGTH, std::move(src_values_field)), + {std::move(curve_index), std::move(length_in_curve)}); + } + else { + Field<int> curve_index = params.extract_input<Field<int>>("Curve Index"); + Field<float> length_in_curve = std::move(length_field); + sample_op = FieldOperation::Create( + std::make_unique<SampleCurveFunction>( + std::move(geometry_set), mode, std::move(src_values_field)), + {std::move(curve_index), std::move(length_in_curve)}); + } } params.set_output("Position", Field<float3>(sample_op, 0)); params.set_output("Tangent", Field<float3>(sample_op, 1)); params.set_output("Normal", Field<float3>(sample_op, 2)); + output_attribute_field(params, GField(sample_op, 3)); } } // namespace blender::nodes::node_geo_curve_sample_cc @@ -369,11 +574,11 @@ void register_node_type_geo_curve_sample() geo_node_type_base(&ntype, GEO_NODE_SAMPLE_CURVE, "Sample Curve", NODE_CLASS_GEOMETRY); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_type_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage( &ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage); 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_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc index 8bb24821064..46377329fb4 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__); @@ -120,7 +120,7 @@ void register_node_type_geo_curve_set_handle_type() &ntype, GEO_NODE_CURVE_SET_HANDLE_TYPE, "Set Handle Type", 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.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryCurveSetHandles", node_free_standard_storage, 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 b5d8d1f020a..159a4661df0 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(); @@ -117,15 +117,18 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange range) { for (const int i_curve : range) { - const float total_length = curves.evaluated_length_total_for_curve(i_curve, - cyclic[i_curve]); MutableSpan<float> curve_lengths = lengths.slice(curves.points_for_curve(i_curve)); + const float total_length = curve_lengths.last(); if (total_length > 0.0f) { const float factor = 1.0f / total_length; for (float &value : curve_lengths) { 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 @@ -165,7 +168,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 +187,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) { @@ -280,6 +283,11 @@ class IndexOnSplineFieldInput final : public bke::CurvesFieldInput { { 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 4d7e5f13969..4b8251aadd3 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__); @@ -87,7 +87,7 @@ void register_node_type_geo_curve_spline_type() geo_node_type_base(&ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", 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.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryCurveSplineType", node_free_standard_storage, 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 919d0056bca..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,6 +29,8 @@ 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; 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 0ddf06dc8c7..268c927f782 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,239 +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) -{ - GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write( - attribute_id, ATTR_DOMAIN_POINT, data_type); - GMutableSpan span = attribute.varray.get_internal_span(); - attribute.finish(); - return span; -} - -template<typename T> -static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id) -{ - AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>( - attribute_id, ATTR_DOMAIN_POINT); - MutableSpan<T> span = attribute.varray.get_internal_span(); - attribute.finish(); - return 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) @@ -315,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())); } } @@ -399,7 +216,7 @@ void register_node_type_geo_curve_to_points() ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; nodeRegisterType(&ntype); } 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..459f45ef8fb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_curve_of_point.cc @@ -0,0 +1,131 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) const final + { + return ATTR_DOMAIN_POINT; + } +}; + +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; + } + + std::optional<eAttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) + { + return ATTR_DOMAIN_POINT; + } +}; + +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..7f69503831f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_topology_points_of_curve.cc @@ -0,0 +1,192 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) const final + { + return ATTR_DOMAIN_CURVE; + } +}; + +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; + } + + std::optional<eAttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) const final + { + return ATTR_DOMAIN_CURVE; + } +}; + +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 443f67be421..b0c2f3117fa 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,17 +8,18 @@ #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) { 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::Float>(N_("Start")) .min(0.0f) .max(1.0f) @@ -47,12 +47,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 +65,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->next; bNodeSocket *end_fac = start_fac->next; bNodeSocket *start_len = end_fac->next; bNodeSocket *end_len = start_len->next; @@ -96,8 +96,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,396 +108,9 @@ 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<bool> &selection_field, Field<float> &start_field, Field<float> &end_field) { @@ -505,68 +118,29 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, return; } const Curves &src_curves_id = *geometry_set.get_curves_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + if (src_curves.curves_num() == 0) { + return; + } - bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; - fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; + evaluator.add(selection_field); 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); - - 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 IndexMask selection = evaluator.get_evaluated_as_mask(0); + const VArray<float> starts = evaluator.get_evaluated<float>(1); + const VArray<float> ends = evaluator.get_evaluated<float>(2); - 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))); - } - } - }); + if (selection.is_empty()) { + return; + } - Curves *dst_curves_id = curve_eval_to_curves(*curve); + bke::CurvesGeometry dst_curves = geometry::trim_curves( + src_curves, selection, starts, ends, mode); + 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); } @@ -579,18 +153,19 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { Field<float> start_field = params.extract_input<Field<float>>("Start"); Field<float> end_field = params.extract_input<Field<float>>("End"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, start_field, end_field); + geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field); }); } else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { Field<float> start_field = params.extract_input<Field<float>>("Start_001"); Field<float> end_field = params.extract_input<Field<float>>("End_001"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, start_field, end_field); + geometry_set_curve_trim(geometry_set, mode, selection_field, start_field, end_field); }); } @@ -610,8 +185,8 @@ void register_node_type_geo_curve_trim() ntype.declare = file_ns::node_declare; node_type_storage( &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + 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_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index 3f6155460ed..dabd2a1a9f2 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 @@ -310,12 +310,11 @@ static void node_geo_exec(GeoNodeExecParams params) ATTR_DOMAIN_CORNER); const VArraySpan<float3> rest_positions = mesh_attributes_eval.lookup<float3>(rest_position_name, ATTR_DOMAIN_POINT); - const Span<float2> surface_uv_coords = curves.surface_uv_coords(); + const VArraySpan<float2> surface_uv_coords = curves.attributes().lookup_or_default( + "surface_uv_coordinate", ATTR_DOMAIN_CURVE, float2(0)); - 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}; @@ -381,19 +380,23 @@ 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 VArraySpan<float2> surface_uv_coords_orig = curves_orig.attributes().lookup_or_default( + "surface_uv_coordinate", ATTR_DOMAIN_CURVE, float2(0)); + 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 851ca622d6b..8ed97f2019f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -4,6 +4,7 @@ #include "UI_resources.h" #include "BLI_array.hh" +#include "BLI_array_utils.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -12,6 +13,7 @@ #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" @@ -22,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]; @@ -54,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(); } } @@ -94,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(); } } @@ -130,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; } @@ -148,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(); } @@ -392,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>(); - bke::GeometryFieldContext 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); @@ -404,7 +381,7 @@ static void delete_selected_instances(GeometrySet &geometry_set, return; } - instances.remove_instances(selection); + instances.remove(selection); } static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection, @@ -1159,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. */ @@ -1172,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; @@ -1192,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) { @@ -1225,7 +1202,7 @@ void register_node_type_geo_delete_geometry() node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; 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..f2e66e03d26 --- /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); + ntype.initfunc = blender::nodes::node_distribute_points_in_volume_init; + ntype.updatefunc = 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 b84ee33e26f..7c9501608a3 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, @@ -107,8 +107,7 @@ static void sample_mesh_surface(const Mesh &mesh, { const Span<MVert> verts = mesh.verts(); const Span<MLoop> loops = mesh.loops(); - 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 looptri_index : looptris.index_range()) { const MLoopTri &looptri = looptris[looptri_index]; @@ -186,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; @@ -204,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; @@ -352,8 +350,7 @@ BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh, const Span<MVert> verts = mesh.verts(); const Span<MLoop> loops = mesh.loops(); - 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()) { const int looptri_index = looptri_indices[i]; @@ -382,13 +379,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh, } ids.finish(); - - if (normals) { - normals.finish(); - } - if (rotations) { - rotations.finish(); - } + normals.finish(); + rotations.finish(); } static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh, @@ -519,8 +511,8 @@ 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"); @@ -533,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); @@ -569,7 +563,7 @@ void register_node_type_geo_distribute_points_on_faces() GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Distribute Points on Faces", NODE_CLASS_GEOMETRY); - node_type_update(&ntype, file_ns::node_point_distribute_points_on_faces_update); + ntype.updatefunc = file_ns::node_point_distribute_points_on_faces_update; node_type_size(&ntype, 170, 100, 320); ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; 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 84e63845b84..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(); 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 d2a3c339301..f048ec11f77 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()) { @@ -439,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; @@ -652,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; @@ -1031,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(); - bke::GeometryFieldContext 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); @@ -1048,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, @@ -1069,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()); } /** \} */ @@ -1158,7 +1149,7 @@ void register_node_type_geo_duplicate_elements() node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; ntype.draw_buttons = file_ns::node_layout; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; 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 index 9ef9ee8ad6e..f0bd01a012b 100644 --- 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 @@ -106,6 +106,11 @@ class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { } return false; } + + 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_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index c7f4b78946d..0062abba909 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); } @@ -132,6 +136,9 @@ 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 = mesh.attributes(); @@ -147,8 +154,7 @@ static MEdge new_edge(const int v1, const int v2) MEdge edge; edge.v1 = v1; edge.v2 = v2; - edge.crease = 0; - edge.flag = (ME_EDGEDRAW | ME_EDGERENDER); + edge.flag = ME_EDGEDRAW; return edge; } @@ -157,7 +163,6 @@ static MEdge new_loose_edge(const int v1, const int v2) MEdge edge; edge.v1 = v1; edge.v2 = v2; - edge.crease = 0; edge.flag = ME_LOOSEEDGE; return edge; } @@ -171,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. @@ -256,28 +243,29 @@ static void extrude_mesh_vertices(Mesh &mesh, 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; @@ -288,7 +276,6 @@ static void extrude_mesh_vertices(Mesh &mesh, for (const int i : range) { const float3 offset = offsets[selection[i]]; add_v3_v3(new_verts[i].co, offset); - new_verts[i].flag = 0; } }); }); @@ -296,6 +283,9 @@ static void extrude_mesh_vertices(Mesh &mesh, 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( mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range); @@ -500,6 +490,9 @@ static void extrude_mesh_edges(Mesh &mesh, 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) { @@ -512,13 +505,14 @@ static void extrude_mesh_edges(Mesh &mesh, 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); @@ -611,7 +605,6 @@ static void extrude_mesh_edges(Mesh &mesh, threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { for (const int i : range) { add_v3_v3(new_verts[i].co, offset); - new_verts[i].flag = 0; } }); } @@ -619,7 +612,6 @@ static void extrude_mesh_edges(Mesh &mesh, threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { for (const int i : range) { add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]); - new_verts[i].flag = 0; } }); } @@ -631,6 +623,9 @@ static void extrude_mesh_edges(Mesh &mesh, 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( mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range); @@ -882,6 +877,9 @@ static void extrude_mesh_face_regions(Mesh &mesh, 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) { @@ -894,17 +892,18 @@ static void extrude_mesh_face_regions(Mesh &mesh, 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); @@ -916,8 +915,8 @@ static void extrude_mesh_face_regions(Mesh &mesh, 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: { @@ -1001,10 +1000,6 @@ static void extrude_mesh_face_regions(Mesh &mesh, }); } - for (MVert &vert : verts.slice(new_vert_range)) { - vert.flag = 0; - } - MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT); vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE); @@ -1140,6 +1135,9 @@ static void extrude_individual_mesh_faces(Mesh &mesh, 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) { @@ -1262,7 +1260,6 @@ static void extrude_individual_mesh_faces(Mesh &mesh, const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection); for (MVert &vert : new_verts.slice(poly_corner_range)) { add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]); - vert.flag = 0; } } }); @@ -1314,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. */ @@ -1383,8 +1380,8 @@ void register_node_type_geo_extrude_mesh() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_EXTRUDE_MESH, "Extrude Mesh", NODE_CLASS_GEOMETRY); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; ntype.geometry_node_execute = file_ns::node_geo_exec; node_type_storage( &ntype, "NodeGeometryExtrudeMesh", node_free_standard_storage, node_copy_standard_storage); 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 c8df5785fed..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 @@ -11,6 +11,63 @@ #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) @@ -30,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; @@ -44,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; @@ -89,60 +146,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -class FieldAtIndex final : public bke::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) - : 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 get_varray_for_context(const bke::GeometryFieldContext &context, - const IndexMask mask) const final - { - const bke::GeometryFieldContext value_field_context{ - context.geometry(), context.type(), value_field_domain_}; - FieldEvaluator value_evaluator{value_field_context, - 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 (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)); - }); - - return output_array; - } -}; - static StringRefNull identifier_suffix(eCustomDataType data_type) { switch (data_type) { @@ -165,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)); }); } 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 613425716d4..95a0013a9e1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -47,6 +47,9 @@ static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field) 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); 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 8e64209a418..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 { @@ -13,15 +15,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry"); - GeometrySet instances_geometry; - InstancesComponent &instances_component = - instances_geometry.get_component_for_write<InstancesComponent>(); + 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..0d339620047 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_) { @@ -402,7 +402,7 @@ void register_node_type_geo_image_texture() geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE); ntype.declare = file_ns::node_declare; ntype.draw_buttons = file_ns::node_layout; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage( &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage); node_type_size_preset(&ntype, NODE_SIZE_LARGE); 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 bff2e7831c6..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 @@ -84,6 +84,11 @@ class HandlePositionFieldInput final : public bke::CurvesFieldInput { } 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 8c5a92904ab..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) @@ -15,12 +17,9 @@ class InstanceRotationFieldInput final : public bke::InstancesFieldInput { { } - GVArray get_varray_for_context(const InstancesComponent &instances, - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - auto rotation_fn = [&](const int i) -> float3 { - return instances.instance_transforms()[i].to_euler(); - }; + auto rotation_fn = [&](const int i) -> float3 { return instances.transforms()[i].to_euler(); }; return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn); } 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 b79e73915b7..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) @@ -15,12 +17,9 @@ class InstanceScaleFieldInput final : public bke::InstancesFieldInput { { } - GVArray get_varray_for_context(const InstancesComponent &instances, - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::Instances &instances, IndexMask /*mask*/) const final { - auto scale_fn = [&](const int i) -> float3 { - return instances.instance_transforms()[i].scale(); - }; + auto scale_fn = [&](const int i) -> float3 { return instances.transforms()[i].scale(); }; return VArray<float3>::ForFunc(instances.instances_num(), scale_fn); } 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 f2e7379b3a2..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 @@ -62,7 +62,7 @@ class AngleFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { const Span<MVert> verts = mesh.verts(); const Span<MPoly> polys = mesh.polys(); @@ -95,6 +95,11 @@ class AngleFieldInput final : public bke::MeshFieldInput { { 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 bke::MeshFieldInput { @@ -106,7 +111,7 @@ class SignedAngleFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { const Span<MVert> verts = mesh.verts(); const Span<MEdge> edges = mesh.edges(); @@ -162,6 +167,11 @@ class SignedAngleFieldInput final : public bke::MeshFieldInput { { 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 bfe8753c039..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 @@ -26,7 +26,7 @@ class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { const Span<MLoop> loops = mesh.loops(); Array<int> face_count(mesh.totedge, 0); @@ -48,6 +48,11 @@ class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput { { 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 c8ceae239a4..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,15 +25,15 @@ 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_verts_gvarray(const Mesh &mesh, - const VertexNumber vertex, + const VertNumber vertex, const eAttrDomain domain) { const Span<MEdge> edges = mesh.edges(); if (domain == ATTR_DOMAIN_EDGE) { - if (vertex == VERTEX_ONE) { + if (vertex == VertNumber::V1) { return VArray<int>::ForFunc(edges.size(), [edges](const int i) -> int { return edges[i].v1; }); } @@ -42,12 +42,12 @@ static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh, return {}; } -class EdgeVerticesFieldInput final : public bke::MeshFieldInput { +class EdgeVertsInput final : public bke::MeshFieldInput { private: - VertexNumber vertex_; + VertNumber vertex_; public: - EdgeVerticesFieldInput(VertexNumber vertex) + EdgeVertsInput(VertNumber vertex) : bke::MeshFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex) { category_ = Category::Generated; @@ -55,34 +55,38 @@ class EdgeVerticesFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { 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 Mesh &mesh, - const VertexNumber vertex, + const VertNumber vertex, const eAttrDomain domain) { const Span<MVert> verts = mesh.verts(); const Span<MEdge> edges = mesh.edges(); - if (vertex == VERTEX_ONE) { + 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; }), @@ -98,10 +102,10 @@ static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh, class EdgePositionFieldInput final : public bke::MeshFieldInput { private: - VertexNumber vertex_; + VertNumber vertex_; public: - EdgePositionFieldInput(VertexNumber vertex) + EdgePositionFieldInput(VertNumber vertex) : bke::MeshFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex) { category_ = Category::Generated; @@ -109,14 +113,14 @@ class EdgePositionFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + IndexMask /*mask*/) const final { 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 @@ -127,14 +131,19 @@ class EdgePositionFieldInput final : public bke::MeshFieldInput { } 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 be921c1f1c5..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 @@ -40,7 +40,7 @@ class FaceAreaFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_face_area_varray(mesh, domain); } @@ -55,6 +55,11 @@ class FaceAreaFieldInput final : public bke::MeshFieldInput { { 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 72c45de7b0f..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 @@ -40,7 +40,8 @@ class PlanarFieldInput final : public bke::MeshFieldInput { const Span<MVert> verts = mesh.verts(); const Span<MPoly> polys = mesh.polys(); const Span<MLoop> loops = mesh.loops(); - const Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}; + 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()}; @@ -86,6 +87,11 @@ class PlanarFieldInput final : public bke::MeshFieldInput { { 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 9e85eae3a31..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 @@ -51,7 +51,7 @@ class FaceNeighborCountFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_neighbor_count_varray(mesh, domain); } @@ -66,6 +66,11 @@ class FaceNeighborCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const FaceNeighborCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_FACE; + } }; static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain) @@ -87,7 +92,7 @@ class FaceVertexCountFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_vertex_count_varray(mesh, domain); } @@ -102,6 +107,11 @@ class FaceVertexCountFieldInput final : public bke::MeshFieldInput { { 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 9d7735e707d..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 @@ -31,7 +31,7 @@ class IslandFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { const Span<MEdge> edges = mesh.edges(); @@ -61,6 +61,11 @@ class IslandFieldInput final : public bke::MeshFieldInput { { 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 bke::MeshFieldInput { @@ -72,7 +77,7 @@ class IslandCountFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { const Span<MEdge> edges = mesh.edges(); @@ -100,6 +105,11 @@ class IslandCountFieldInput final : public bke::MeshFieldInput { { 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 ab44a6c8515..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 @@ -43,7 +43,7 @@ class VertexCountFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_vertex_count_gvarray(mesh, domain); } @@ -58,6 +58,11 @@ class VertexCountFieldInput final : public bke::MeshFieldInput { { return dynamic_cast<const VertexCountFieldInput *>(&other) != nullptr; } + + 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) @@ -83,7 +88,7 @@ class VertexFaceCountFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_face_count_gvarray(mesh, domain); } @@ -98,6 +103,11 @@ class VertexFaceCountFieldInput final : public bke::MeshFieldInput { { 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 da09d3650e3..9d1f90ba0f3 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"); @@ -126,7 +126,7 @@ void register_node_type_geo_input_named_attribute() ntype.draw_buttons = file_ns::node_layout; ntype.gather_link_search_ops = file_ns::node_gather_link_searches; ntype.updatefunc = file_ns::node_update; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryInputNamedAttribute", node_free_standard_storage, 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 index a54daabde3b..00c92e30443 100644 --- 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 @@ -143,6 +143,11 @@ class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput { } return false; } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const override + { + return ATTR_DOMAIN_POINT; + } }; class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { @@ -206,6 +211,11 @@ class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { } return false; } + + 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_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 07dc158ff48..5a42949d4c8 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 @@ -41,7 +41,7 @@ class SplineCountFieldInput final : public bke::CurvesFieldInput { GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_curve_point_count_gvarray(curves, domain); } @@ -56,6 +56,11 @@ class SplineCountFieldInput final : public bke::CurvesFieldInput { { return dynamic_cast<const SplineCountFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) const final + { + return ATTR_DOMAIN_CURVE; + } }; static void node_geo_exec(GeoNodeExecParams params) 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 ea3d060f03c..aa27fa70e64 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -95,7 +95,7 @@ class TangentFieldInput final : public bke::CurvesFieldInput { GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { return construct_curve_tangent_gvarray(curves, domain); } @@ -110,6 +110,11 @@ class TangentFieldInput final : public bke::CurvesFieldInput { { return dynamic_cast<const TangentFieldInput *>(&other) != nullptr; } + + std::optional<eAttrDomain> preferred_domain(const bke::CurvesGeometry & /*curves*/) const final + { + return ATTR_DOMAIN_POINT; + } }; static void node_geo_exec(GeoNodeExecParams params) 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 d54d082311f..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, @@ -80,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; } @@ -106,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) { @@ -129,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); } } } @@ -157,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; @@ -174,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)); } } @@ -196,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}; @@ -208,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(); }); @@ -223,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 ec2f1b00e6c..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,7 +28,7 @@ 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(); const bke::InstancesFieldContext context{instances}; fn::FieldEvaluator evaluator{context, instances.instances_num()}; @@ -70,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_interpolate_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc index 8e38ef14aba..d4e18321665 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc @@ -28,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; @@ -42,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; @@ -111,6 +111,12 @@ class InterpolateDomain final : public bke::GeometryFieldInput { 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_; + } }; static StringRefNull identifier_suffix(eCustomDataType data_type) @@ -135,8 +141,8 @@ 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); 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 9fdf7fe7d31..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,25 +161,23 @@ 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>()); } } 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 628688f3b47..dfb4181926e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -100,6 +100,12 @@ class MaterialSelectionFieldInput final : public bke::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 f64f997810e..8ab8c4afaa9 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 @@ -24,14 +24,14 @@ 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; @@ -132,7 +132,7 @@ void register_node_type_geo_merge_by_distance() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge by Distance", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometryMergeByDistance", node_free_standard_storage, 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 801b3c78060..14f38efbd42 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__); @@ -115,7 +115,7 @@ static Mesh *create_circle_mesh(const float radius, 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)); @@ -127,7 +127,7 @@ static Mesh *create_circle_mesh(const float radius, /* Create outer edges. */ const short edge_flag = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? ME_LOOSEEDGE : - (ME_EDGEDRAW | ME_EDGERENDER); /* NGON or TRIANGLE_FAN */ + ME_EDGEDRAW; /* NGON or TRIANGLE_FAN */ for (const int i : IndexRange(verts_num)) { MEdge &edge = edges[i]; edge.v1 = i; @@ -141,7 +141,7 @@ static Mesh *create_circle_mesh(const float radius, MEdge &edge = edges[verts_num + i]; edge.v1 = verts_num; edge.v2 = i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } } @@ -205,7 +205,7 @@ void register_node_type_geo_mesh_primitive_circle() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage( &ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; 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 edf14f664c5..dca91d2dc61 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 @@ -258,7 +258,7 @@ int ConeConfig::calculate_total_corners() 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_verts(const MutableSpan<MVert> &verts, const ConeConf /* 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_verts(const MutableSpan<MVert> &verts, const ConeConf /* 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_verts(const MutableSpan<MVert> &verts, const ConeConf /* 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)) { @@ -331,7 +329,7 @@ static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConf MEdge &edge = edges[edge_index++]; edge.v1 = config.first_vert; edge.v2 = config.first_ring_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } } @@ -344,7 +342,7 @@ static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConf MEdge &edge = edges[edge_index++]; edge.v1 = this_ring_vert_start + j; edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments); - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } if (i == config.tot_edge_rings - 1) { /* There is one fewer ring of connecting edges. */ @@ -355,7 +353,7 @@ static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConf MEdge &edge = edges[edge_index++]; edge.v1 = this_ring_vert_start + j; edge.v2 = next_ring_vert_start + j; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } } @@ -365,7 +363,7 @@ static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConf MEdge &edge = edges[edge_index++]; edge.v1 = config.last_ring_verts_start + i; edge.v2 = config.last_vert; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } } } @@ -485,7 +483,7 @@ static void calculate_selection_outputs(Mesh *mesh, /* 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); @@ -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; @@ -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); @@ -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); @@ -857,8 +854,8 @@ void register_node_type_geo_mesh_primitive_cone() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage( &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; 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..b02b1fbae27 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; @@ -154,8 +154,8 @@ void register_node_type_geo_mesh_primitive_cylinder() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage( &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; 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 6f0b8283b72..e8ee057ee5c 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 @@ -76,8 +76,7 @@ Mesh *create_grid_mesh(const int verts_x, const int y_edges_start = 0; const int x_edges_start = verts_x * edges_y; - const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE : - ME_EDGEDRAW | ME_EDGERENDER; + const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE : ME_EDGEDRAW; /* Build the horizontal edges in the X direction. */ threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) { 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 4fd6399f4eb..51a4f36507e 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); } } @@ -214,8 +214,8 @@ void register_node_type_geo_mesh_primitive_line() geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage( &ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; 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 d39e72b7f0a..cfa1c477b28 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 @@ -116,7 +116,7 @@ BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, MEdge &edge = edges[edge_index++]; edge.v1 = 0; edge.v2 = first_vert_ring_index_start + segment; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } int ring_vert_index_start = 1; @@ -128,7 +128,7 @@ BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, MEdge &edge = edges[edge_index++]; edge.v1 = ring_vert_index_start + segment; edge.v2 = ring_vert_index_start + ((segment + 1) % segments); - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } /* Add the edges connecting to the next ring. */ @@ -137,7 +137,7 @@ BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, MEdge &edge = edges[edge_index++]; edge.v1 = ring_vert_index_start + segment; edge.v2 = next_ring_vert_index_start + segment; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } } ring_vert_index_start += segments; @@ -150,7 +150,7 @@ BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, MEdge &edge = edges[edge_index++]; edge.v1 = last_vert_index; edge.v2 = last_vert_ring_start + segment; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge.flag = ME_EDGEDRAW; } } @@ -266,16 +266,16 @@ BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float segments_inv = 1.0f / segments; for (const int i_segment : IndexRange(segments)) { - const float segment = static_cast<float>(i_segment); + 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); + 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); @@ -284,7 +284,7 @@ BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, } for (const int i_segment : IndexRange(segments)) { - const float segment = static_cast<float>(i_segment); + 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); @@ -309,7 +309,8 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const threading::parallel_invoke( 1024 < segments * rings, [&]() { - MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert}; + 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); }, 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 a1d6695b33b..d47e0c0f101 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,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" @@ -23,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) @@ -32,29 +33,18 @@ 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, @@ -88,14 +78,12 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, 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 = 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; @@ -112,7 +100,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, 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(); } } @@ -175,7 +163,7 @@ void register_node_type_geo_mesh_to_points() geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", 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.initfunc = file_ns::node_init; ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage); 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..cf4d058cd31 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 } @@ -177,8 +174,8 @@ void register_node_type_geo_mesh_to_volume() geo_node_type_base(&ntype, GEO_NODE_MESH_TO_VOLUME, "Mesh to Volume", NODE_CLASS_GEOMETRY); ntype.declare = file_ns::node_declare; node_type_size(&ntype, 200, 120, 700); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; node_type_storage( 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..b464832409c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_face.cc @@ -0,0 +1,199 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_FACE; + } +}; + +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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_FACE; + } +}; + +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..c01c4149864 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_corners_of_vertex.cc @@ -0,0 +1,219 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_POINT; + } +}; + +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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_POINT; + } +}; + +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..e46061e0d65 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_corner.cc @@ -0,0 +1,146 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_CORNER; + } +}; + +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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_CORNER; + } +}; + +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..7aadc15f7f8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_edges_of_vertex.cc @@ -0,0 +1,221 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_POINT; + } +}; + +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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_POINT; + } +}; + +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..2cf7ed2c687 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_face_of_corner.cc @@ -0,0 +1,126 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_CORNER; + } +}; + +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..ef5c9a445f2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_offset_corner_in_face.cc @@ -0,0 +1,118 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_CORNER; + } +}; + +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..9f730367931 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_topology_vertex_of_corner.cc @@ -0,0 +1,84 @@ +/* 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; + } + + std::optional<eAttrDomain> preferred_domain(const Mesh & /*mesh*/) const final + { + return ATTR_DOMAIN_CORNER; + } +}; + +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 691988249df..f1a9987e721 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); } @@ -44,8 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const float4x4 &object_matrix = object->obmat; - const float4x4 transform = float4x4(self_object->imat) * object_matrix; + const float4x4 &object_matrix = object->object_to_world; + const float4x4 transform = float4x4(self_object->world_to_object) * object_matrix; if (transform_space_relative) { params.set_output("Location", transform.translation()); @@ -68,14 +69,15 @@ 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); @@ -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; @@ -104,7 +106,7 @@ void register_node_type_geo_object_info() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT); - node_type_init(&ntype, file_ns::node_node_init); + ntype.initfunc = file_ns::node_node_init; node_type_storage( &ntype, "NodeGeometryObjectInfo", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; 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 4a294076834..dcbe176b384 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc @@ -20,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")); } @@ -43,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); 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 ba6bd40a6b6..d0b7f6a455f 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 @@ -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 } @@ -259,8 +258,8 @@ void register_node_type_geo_points_to_volume() node_free_standard_storage, node_copy_standard_storage); node_type_size(&ntype, 170, 120, 700); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = 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; diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index d5233ee35a4..92a409f9db6 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)})); @@ -229,7 +228,7 @@ void register_node_type_geo_proximity() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, file_ns::geo_proximity_init); + ntype.initfunc = file_ns::geo_proximity_init; node_type_storage( &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index f657b128c51..b3dbc9ee866 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; @@ -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 @@ -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(); @@ -435,8 +435,8 @@ void register_node_type_geo_raycast() geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage( &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; 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_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index 4ed94e67e74..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,10 +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) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{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>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); @@ -31,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; @@ -81,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..22c18504985 --- /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); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = 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..7028b013dc6 --- /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); + ntype.initfunc = 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..503f6264191 --- /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); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = 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..0a6dd569395 --- /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); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = 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 2ebbf88b8ad..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); @@ -405,9 +404,8 @@ static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields) 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"); 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 21fe724e194..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,10 +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) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{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")); @@ -34,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); @@ -61,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..d4da5dda647 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) { @@ -87,7 +87,7 @@ void register_node_type_geo_separate_geometry() node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; 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 e529ddddabe..48be899bec9 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__); @@ -170,7 +174,7 @@ void register_node_type_geo_set_curve_handles() ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.declare = file_ns::node_declare; ntype.minwidth = 100.0f; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage(&ntype, "NodeGeometrySetCurveHandlePositions", node_free_standard_storage, 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..287c5920134 --- /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; + ntype.initfunc = file_ns::node_init; + ntype.draw_buttons = file_ns::node_layout; + + nodeRegisterType(&ntype); +} 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 5864401223b..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")); } 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 f6dded56315..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 @@ -24,11 +24,13 @@ static void set_material_index_in_component(GeometryComponent &component, MutableAttributeAccessor attributes = *component.attributes_for_write(); bke::GeometryFieldContext field_context{component, domain}; + 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(); } 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 613f140ff0a..e243fe3614c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -18,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")); } 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 d46ceac92ba..dcd910b8ad2 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 @@ -24,11 +24,13 @@ static void set_resolution(bke::CurvesGeometry &curves, MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution", ATTR_DOMAIN_CURVE); + bke::AttributeValidator validator = attributes.lookup_validator("resolution"); 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.add_with_destination(validator.validate_field_if_necessary(resolution_field), + resolutions.varray); evaluator.evaluate(); resolutions.finish(); 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 2a590f5bf4a..b71dba98daa 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_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,54 +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; - } - - bke::GeometryFieldContext 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; - } - } - attributes.remove(name); - if (attributes.add(name, domain, data_type, bke::AttributeInitMoveArray{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"); @@ -152,8 +105,8 @@ static void node_geo_exec(GeoNodeExecParams params) 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) { @@ -189,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 { @@ -198,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); + } } } }); @@ -235,7 +192,7 @@ void register_node_type_geo_store_named_attribute() node_free_standard_storage, node_copy_standard_storage); node_type_size(&ntype, 140, 100, 700); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; ntype.updatefunc = file_ns::node_update; ntype.declare = file_ns::node_declare; ntype.gather_link_search_ops = file_ns::node_gather_link_searches; 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..405c2761711 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 @@ -405,8 +405,8 @@ void register_node_type_geo_string_to_curves() geo_node_type_base(&ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", 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); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_size(&ntype, 190, 120, 700); node_type_storage(&ntype, "NodeGeometryStringToCurves", 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 60c8a89a6bf..7b4608d32a5 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; @@ -191,7 +191,7 @@ void register_node_type_geo_subdivision_surface() ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_storage(&ntype, "NodeGeometrySubdivisionSurface", diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index ddc87e3dac4..bd3ac271817 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) { @@ -290,8 +290,8 @@ void register_node_type_geo_switch() geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.geometry_node_execute_supports_laziness = true; 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 afc492c40e4..00000000000 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ /dev/null @@ -1,830 +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.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_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{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) -{ - 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; - } - } -} - -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<bke::MeshFieldContext> 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 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); - } -}; - -/** - * \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<bke::MeshFieldContext> mesh_context_; - std::unique_ptr<FieldEvaluator> mesh_evaluator_; - const GVArray *mesh_data_; - - std::optional<bke::PointCloudFieldContext> 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_polys(*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 Mesh &mesh = *source_.get_mesh_for_read(); - const int domain_size = mesh.attributes().domain_size(domain_); - mesh_context_.emplace(bke::MeshFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); - mesh_evaluator_->add(src_field_); - mesh_evaluator_->evaluate(); - mesh_data_ = &mesh_evaluator_->get_evaluated(0); - } - - if (use_points_) { - const PointCloud &points = *source_.get_pointcloud_for_read(); - point_context_.emplace(bke::PointCloudFieldContext(points)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, points.totpoint); - 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<bke::GeometryFieldContext> 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(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 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 4130cad3bda..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" @@ -67,18 +68,18 @@ 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; } } @@ -185,8 +186,8 @@ static void translate_geometry_set(GeoNodeExecParams ¶ms, if (Volume *volume = geometry.get_volume_for_write()) { 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); @@ -210,8 +211,8 @@ void transform_geometry_set(GeoNodeExecParams ¶ms, if (Volume *volume = geometry.get_volume_for_write()) { 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); 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 3e9fe99adb0..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,10 +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) { - const bke::InstancesFieldContext context{instances_component}; - fn::FieldEvaluator evaluator{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")); @@ -28,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]); } } }); @@ -46,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 57487059437..5cb78b3abe8 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; @@ -68,10 +68,8 @@ 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()) { @@ -102,7 +100,7 @@ void register_node_type_geo_triangulate() geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", NODE_CLASS_GEOMETRY); ntype.declare = file_ns::node_declare; - node_type_init(&ntype, file_ns::geo_triangulate_init); + ntype.initfunc = file_ns::geo_triangulate_init; 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_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc index ccb489f6e29..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 @@ -114,10 +114,15 @@ class PackIslandsFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { 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; + } }; static void node_geo_exec(GeoNodeExecParams params) 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 801bc3f4642..eff3b969250 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -40,14 +40,14 @@ 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; @@ -156,10 +156,15 @@ class UnwrapFieldInput final : public bke::MeshFieldInput { GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + const IndexMask /*mask*/) const final { 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; + } }; static void node_geo_exec(GeoNodeExecParams params) @@ -184,7 +189,7 @@ void register_node_type_geo_uv_unwrap() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_UV_UNWRAP, "UV Unwrap", NODE_CLASS_CONVERTER); - node_type_init(&ntype, file_ns::node_init); + ntype.initfunc = file_ns::node_init; node_type_storage( &ntype, "NodeGeometryUVUnwrap", node_free_standard_storage, node_copy_standard_storage); ntype.declare = file_ns::node_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 6979693e215..53a8cd79f52 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( @@ -129,10 +135,12 @@ void register_node_type_geo_viewer() geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT); node_type_storage( &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage); - node_type_update(&ntype, file_ns::node_update); - node_type_init(&ntype, file_ns::node_init); + ntype.updatefunc = file_ns::node_update; + ntype.initfunc = 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 e964bf03ed2..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"); @@ -169,7 +168,7 @@ static void node_geo_exec(GeoNodeExecParams params) 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)); @@ -177,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 46708f53087..c076a6c08f3 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; @@ -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 @@ -216,8 +215,8 @@ void register_node_type_geo_volume_to_mesh() node_type_storage( &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); node_type_size(&ntype, 170, 120, 700); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); |