/* SPDX-License-Identifier: GPL-2.0-or-later */ #include "GEO_uv_parametrizer.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_mesh.h" #include "node_geometry_util.hh" namespace blender::nodes::node_geo_uv_pack_islands_cc { static void node_declare(NodeDeclarationBuilder &b) { b.add_input(N_("UV")).hide_value().supports_field(); b.add_input(N_("Selection")) .default_value(true) .hide_value() .supports_field() .description(N_("Faces to consider when packing islands")); b.add_input(N_("Margin")) .default_value(0.001f) .min(0.0f) .max(1.0f) .description(N_("Space between islands")); b.add_input(N_("Rotate")) .default_value(true) .description(N_("Rotate islands for best fit")); b.add_output(N_("UV")).field_source(); } static VArray construct_uv_gvarray(const Mesh &mesh, const Field selection_field, const Field uv_field, const bool rotate, const float margin, const eAttrDomain domain) { const Span verts = mesh.verts(); const Span polys = mesh.polys(); const Span loops = mesh.loops(); bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator face_evaluator{face_context, polys.size()}; face_evaluator.add(selection_field); face_evaluator.evaluate(); const IndexMask selection = face_evaluator.get_evaluated_as_mask(0); if (selection.is_empty()) { return {}; } bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER}; FieldEvaluator evaluator{corner_context, mesh.totloop}; Array uv(mesh.totloop); evaluator.add_with_destination(uv_field, uv.as_mutable_span()); evaluator.evaluate(); ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); for (const int mp_index : selection) { const MPoly &mp = polys[mp_index]; Array mp_vkeys(mp.totloop); Array mp_pin(mp.totloop); Array mp_select(mp.totloop); Array mp_co(mp.totloop); Array mp_uv(mp.totloop); for (const int i : IndexRange(mp.totloop)) { const MLoop &ml = loops[mp.loopstart + i]; mp_vkeys[i] = ml.v; mp_co[i] = verts[ml.v].co; mp_uv[i] = uv[mp.loopstart + i]; mp_pin[i] = false; mp_select[i] = false; } GEO_uv_parametrizer_face_add(handle, mp_index, mp.totloop, mp_vkeys.data(), mp_co.data(), mp_uv.data(), mp_pin.data(), mp_select.data()); } GEO_uv_parametrizer_construct_end(handle, true, true, nullptr); GEO_uv_parametrizer_pack(handle, margin, rotate, true); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); return mesh.attributes().adapt_domain( VArray::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } class PackIslandsFieldInput final : public bke::MeshFieldInput { private: const Field selection_field; const Field uv_field; const bool rotate; const float margin; public: PackIslandsFieldInput(const Field selection_field, const Field uv_field, const bool rotate, const float margin) : bke::MeshFieldInput(CPPType::get(), "Pack UV Islands Field"), selection_field(selection_field), uv_field(uv_field), rotate(rotate), margin(margin) { category_ = Category::Generated; } GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, const IndexMask /*mask*/) const final { return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain); } std::optional preferred_domain(const Mesh & /*mesh*/) const override { return ATTR_DOMAIN_CORNER; } }; static void node_geo_exec(GeoNodeExecParams params) { 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))); } } // namespace blender::nodes::node_geo_uv_pack_islands_cc void register_node_type_geo_uv_pack_islands() { namespace file_ns = blender::nodes::node_geo_uv_pack_islands_cc; static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_UV_PACK_ISLANDS, "Pack UV Islands", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare; ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); }