From aced349e3dcad31c6cf9486997ba9f859913715f Mon Sep 17 00:00:00 2001 From: Arye Ramaty Date: Thu, 12 Jun 2025 16:41:17 +0200 Subject: [PATCH] Geometry Nodes: Add Shape Method parameter to Pack UV Islands node Adds a Shape Method parameter to the UV Pack Islands node, enabling artists to choose between faster packing and more efficient space utilization. Those are the three shape method options: * Bounding Box: Fastest, less efficient space usage * Convex Hull: Balanced performance and efficiency * Exact Shape: Optimal packing, higher computational cost This change consolidates arguments in `uv_parametrizer_pack()`. Now it accept ` UVPackIsland_Params` instead of many different separate options. This also makes it easier to expose more options in the future. Pull Request: https://projects.blender.org/blender/blender/pulls/139110 --- .../blender/geometry/GEO_uv_parametrizer.hh | 6 +- .../geometry/intern/uv_parametrizer.cc | 9 +- .../nodes/node_geo_uv_pack_islands.cc | 97 ++++++++++++++++++- .../geometry/nodes/node_geo_uv_unwrap.cc | 7 +- 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/source/blender/geometry/GEO_uv_parametrizer.hh b/source/blender/geometry/GEO_uv_parametrizer.hh index 92392ca42f3..7995d995cbf 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.hh +++ b/source/blender/geometry/GEO_uv_parametrizer.hh @@ -10,6 +10,10 @@ namespace slim { struct MatrixTransfer; } +namespace blender::geometry { +class UVPackIsland_Params; +} + /** \file * \ingroup geo */ @@ -166,7 +170,7 @@ void uv_parametrizer_stretch_end(ParamHandle *handle); /** \name Packing * \{ */ -void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, bool ignore_pinned); +void uv_parametrizer_pack(ParamHandle *handle, const UVPackIsland_Params ¶ms); /** \} */ diff --git a/source/blender/geometry/intern/uv_parametrizer.cc b/source/blender/geometry/intern/uv_parametrizer.cc index 258cb378f89..df3d2f9e79a 100644 --- a/source/blender/geometry/intern/uv_parametrizer.cc +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -4176,7 +4176,7 @@ void uv_parametrizer_stretch_end(ParamHandle *phandle) phandle->state = PHANDLE_STATE_CONSTRUCTED; } -void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, bool ignore_pinned) +void uv_parametrizer_pack(ParamHandle *handle, const UVPackIsland_Params ¶ms) { if (handle->ncharts == 0) { return; @@ -4186,14 +4186,9 @@ void uv_parametrizer_pack(ParamHandle *handle, float margin, bool do_rotate, boo Vector pack_island_vector; - UVPackIsland_Params params; - params.rotate_method = do_rotate ? ED_UVPACK_ROTATION_ANY : ED_UVPACK_ROTATION_NONE; - params.margin = margin; - params.margin_method = ED_UVPACK_MARGIN_SCALED; - for (int i = 0; i < handle->ncharts; i++) { PChart *chart = handle->charts[i]; - if (ignore_pinned && chart->has_pins) { + if (params.pin_method == ED_UVPACK_PIN_NONE && chart->has_pins) { continue; } 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 859a3eb9104..97fb76219a1 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 @@ -2,16 +2,45 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "GEO_uv_pack.hh" #include "GEO_uv_parametrizer.hh" #include "DNA_mesh_types.h" #include "node_geometry_util.hh" +#include "NOD_rna_define.hh" + +#include "UI_interface.hh" + namespace blender::nodes::node_geo_uv_pack_islands_cc { +/** Local node enum that maps to eUVPackIsland_ShapeMethod in GEO_uv_pack.hh. */ +enum class ShapeMethod : int16_t { + Aabb = 0, + Convex = 1, + Concave = 2, +}; + +static eUVPackIsland_ShapeMethod convert_shape_method(const ShapeMethod method) +{ + switch (method) { + case ShapeMethod::Aabb: + return ED_UVPACK_SHAPE_AABB; + case ShapeMethod::Convex: + return ED_UVPACK_SHAPE_CONVEX; + case ShapeMethod::Concave: + return ED_UVPACK_SHAPE_CONCAVE; + } + BLI_assert_unreachable(); + return ED_UVPACK_SHAPE_AABB; +} + static void node_declare(NodeDeclarationBuilder &b) { + b.use_custom_socket_order(); + b.allow_any_socket_order(); + b.add_default_layout(); b.add_input("UV").hide_value().supports_field(); b.add_input("Selection") .default_value(true) @@ -24,11 +53,22 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output("UV").field_source_reference_all(); } +static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) +{ + layout->prop(ptr, "shape_method", UI_ITEM_NONE, "", ICON_NONE); +} + +static void node_init(bNodeTree * /*tree*/, bNode *node) +{ + node->custom1 = int16_t(ShapeMethod::Aabb); +} + static VArray construct_uv_gvarray(const Mesh &mesh, const Field selection_field, const Field uv_field, const bool rotate, const float margin, + const eUVPackIsland_ShapeMethod shape_method, const AttrDomain domain) { const Span positions = mesh.vert_positions(); @@ -79,7 +119,12 @@ static VArray construct_uv_gvarray(const Mesh &mesh, }); geometry::uv_parametrizer_construct_end(handle, true, true, nullptr); - geometry::uv_parametrizer_pack(handle, margin, rotate, true); + blender::geometry::UVPackIsland_Params params; + params.shape_method = shape_method; + params.rotate_method = rotate ? ED_UVPACK_ROTATION_ANY : ED_UVPACK_ROTATION_NONE; + params.margin = margin; + + geometry::uv_parametrizer_pack(handle, params); geometry::uv_parametrizer_flush(handle); delete (handle); @@ -93,17 +138,20 @@ class PackIslandsFieldInput final : public bke::MeshFieldInput { const Field uv_field_; const bool rotate_; const float margin_; + const eUVPackIsland_ShapeMethod shape_method_; public: PackIslandsFieldInput(const Field selection_field, const Field uv_field, const bool rotate, - const float margin) + const float margin, + const eUVPackIsland_ShapeMethod shape_method) : bke::MeshFieldInput(CPPType::get(), "Pack UV Islands Field"), selection_field_(selection_field), uv_field_(uv_field), rotate_(rotate), - margin_(margin) + margin_(margin), + shape_method_(shape_method) { category_ = Category::Generated; } @@ -112,7 +160,8 @@ class PackIslandsFieldInput final : public bke::MeshFieldInput { const AttrDomain domain, const IndexMask & /*mask*/) const final { - return construct_uv_gvarray(mesh, selection_field_, uv_field_, rotate_, margin_, domain); + return construct_uv_gvarray( + mesh, selection_field_, uv_field_, rotate_, margin_, shape_method_, domain); } void for_each_field_input_recursive(FunctionRef fn) const override @@ -129,13 +178,47 @@ class PackIslandsFieldInput final : public bke::MeshFieldInput { static void node_geo_exec(GeoNodeExecParams params) { + const bNode &node = params.node(); + const ShapeMethod local_shape_method = ShapeMethod(node.custom1); + const eUVPackIsland_ShapeMethod shape_method = convert_shape_method(local_shape_method); + const Field selection_field = params.extract_input>("Selection"); const Field uv_field = params.extract_input>("UV"); const bool rotate = params.extract_input("Rotate"); const float margin = params.extract_input("Margin"); params.set_output("UV", Field(std::make_shared( - selection_field, uv_field, rotate, margin))); + selection_field, uv_field, rotate, margin, shape_method))); +} + +static void node_rna(StructRNA *srna) +{ + static const EnumPropertyItem shape_method_items[] = { + {int(ShapeMethod::Aabb), + "AABB", + 0, + "Bounding Box", + "Use axis-aligned bounding boxes for packing (fastest, least space efficient)"}, + {int(ShapeMethod::Convex), + "CONVEX", + 0, + "Convex Hull", + "Use convex hull approximation of islands (good balance of speed and space efficiency)"}, + {int(ShapeMethod::Concave), + "CONCAVE", + 0, + "Exact Shape", + "Use exact geometry for most efficient packing (slowest)"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + RNA_def_node_enum(srna, + "shape_method", + "Shape Method", + "Method used for packing UV islands", + shape_method_items, + NOD_inline_enum_accessors(custom1), + int(ShapeMethod::Aabb)); } static void node_register() @@ -148,9 +231,13 @@ static void node_register() "Scale islands of a UV map and move them so they fill the UV space as much as possible"; ntype.enum_name_legacy = "UV_PACK_ISLANDS"; ntype.nclass = NODE_CLASS_CONVERTER; + ntype.initfunc = node_init; ntype.declare = node_declare; ntype.geometry_node_execute = node_geo_exec; + ntype.draw_buttons = node_layout; blender::bke::node_register_type(ntype); + + node_rna(ntype.rna_ext.srna); } NOD_REGISTER_NODE(node_register) 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 3fd48623d13..3159c11b6f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -4,6 +4,7 @@ #include "DNA_mesh_types.h" +#include "GEO_uv_pack.hh" #include "GEO_uv_parametrizer.hh" #include "UI_interface.hh" @@ -114,6 +115,10 @@ static VArray construct_uv_gvarray(const Mesh &mesh, geometry::uv_parametrizer_edge_set_seam(handle, vkeys); }); + blender::geometry::UVPackIsland_Params params; + params.margin = margin; + params.rotate_method = ED_UVPACK_ROTATION_ANY; + /* TODO: once field input nodes are able to emit warnings (#94039), emit a * warning if we fail to solve an island. */ geometry::uv_parametrizer_construct_end(handle, fill_holes, false, nullptr); @@ -123,7 +128,7 @@ static VArray construct_uv_gvarray(const Mesh &mesh, geometry::uv_parametrizer_lscm_solve(handle, nullptr, nullptr); geometry::uv_parametrizer_lscm_end(handle); geometry::uv_parametrizer_average(handle, true, false, false); - geometry::uv_parametrizer_pack(handle, margin, true, true); + geometry::uv_parametrizer_pack(handle, params); geometry::uv_parametrizer_flush(handle); delete (handle);