Blender V4.3
node_geo_sample_uv_surface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6#include "BKE_mesh.hh"
7#include "BKE_mesh_sample.hh"
9
10#include "NOD_rna_define.hh"
12
13#include "UI_interface.hh"
14#include "UI_resources.hh"
15
17
18#include "RNA_enum_types.hh"
19
20#include "node_geometry_util.hh"
21
23
25
27{
28 const bNode *node = b.node_or_null();
29
30 b.add_input<decl::Geometry>("Mesh").supported_type(GeometryComponent::Type::Mesh);
31 if (node != nullptr) {
32 const eCustomDataType data_type = eCustomDataType(node->custom1);
33 b.add_input(data_type, "Value").hide_value().field_on_all();
34 }
35 b.add_input<decl::Vector>("UV Map", "Source UV Map")
36 .hide_value()
37 .field_on_all()
38 .description("The mesh UV map to sample. Should not have overlapping faces");
39 b.add_input<decl::Vector>("Sample UV")
40 .supports_field()
41 .description("The coordinates to sample within the UV map");
42
43 if (node != nullptr) {
44 const eCustomDataType data_type = eCustomDataType(node->custom1);
45 b.add_output(data_type, "Value").dependent_field({3});
46 }
47 b.add_output<decl::Bool>("Is Valid")
48 .dependent_field({3})
49 .description("Whether the node could find a single face to sample at the UV coordinate");
50}
51
52static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
53{
54 uiItemR(layout, ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
55}
56
57static void node_init(bNodeTree * /*tree*/, bNode *node)
58{
59 node->custom1 = CD_PROP_FLOAT;
60}
61
63{
64 const NodeDeclaration &declaration = *params.node_type().static_declaration;
67
68 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
69 eNodeSocketDatatype(params.other_socket().type));
70 if (type && *type != CD_PROP_STRING) {
71 /* The input and output sockets have the same name. */
72 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
73 bNode &node = params.add_node("GeometryNodeSampleUVSurface");
74 node.custom1 = *type;
75 params.update_and_connect_available_socket(node, "Value");
76 });
77 }
78}
79
81 GeometrySet source_;
82 Field<float2> src_uv_map_field_;
83
84 std::optional<bke::MeshFieldContext> source_context_;
85 std::unique_ptr<FieldEvaluator> source_evaluator_;
86 VArraySpan<float2> source_uv_map_;
87
88 std::optional<ReverseUVSampler> reverse_uv_sampler_;
89
90 public:
92 : source_(std::move(geometry)), src_uv_map_field_(std::move(src_uv_map_field))
93 {
95 this->evaluate_source();
96
97 static const mf::Signature signature = []() {
99 mf::SignatureBuilder builder{"Sample UV Surface", signature};
100 builder.single_input<float2>("Sample UV");
101 builder.single_output<bool>("Is Valid", mf::ParamFlag::SupportsUnusedOutput);
102 builder.single_output<int>("Triangle Index", mf::ParamFlag::SupportsUnusedOutput);
103 builder.single_output<float3>("Barycentric Weights", mf::ParamFlag::SupportsUnusedOutput);
104 return signature;
105 }();
106 this->set_signature(&signature);
107 }
108
109 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
110 {
111 const VArraySpan<float2> sample_uvs = params.readonly_single_input<float2>(0, "Sample UV");
112 MutableSpan<bool> is_valid = params.uninitialized_single_output_if_required<bool>(1,
113 "Is Valid");
114 MutableSpan<int> tri_index = params.uninitialized_single_output_if_required<int>(
115 2, "Triangle Index");
116 MutableSpan<float3> bary_weights = params.uninitialized_single_output_if_required<float3>(
117 3, "Barycentric Weights");
118
119 mask.foreach_index([&](const int i) {
120 const ReverseUVSampler::Result result = reverse_uv_sampler_->sample(sample_uvs[i]);
121 if (!is_valid.is_empty()) {
123 }
124 if (!tri_index.is_empty()) {
125 tri_index[i] = result.tri_index;
126 }
127 if (!bary_weights.is_empty()) {
128 bary_weights[i] = result.bary_weights;
129 }
130 });
131 }
132
133 private:
134 void evaluate_source()
135 {
136 const Mesh &mesh = *source_.get_mesh();
137 source_context_.emplace(bke::MeshFieldContext{mesh, AttrDomain::Corner});
138 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, mesh.corners_num);
139 source_evaluator_->add(src_uv_map_field_);
140 source_evaluator_->evaluate();
141 source_uv_map_ = source_evaluator_->get_evaluated<float2>(0);
142
143 reverse_uv_sampler_.emplace(source_uv_map_, mesh.corner_tris());
144 }
145};
146
148{
149 GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
150 const Mesh *mesh = geometry.get_mesh();
151 if (mesh == nullptr) {
152 params.set_default_remaining_outputs();
153 return;
154 }
155 if (mesh->faces_num == 0 && mesh->verts_num != 0) {
156 params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces"));
157 params.set_default_remaining_outputs();
158 return;
159 }
160
161 /* Do reverse sampling of the UV map first. */
163 const CPPType &float2_type = CPPType::get<float2>();
164 Field<float2> source_uv_map = conversions.try_convert(
165 params.extract_input<Field<float3>>("Source UV Map"), float2_type);
166 Field<float2> sample_uvs = conversions.try_convert(
167 params.extract_input<Field<float3>>("Sample UV"), float2_type);
168 auto uv_op = FieldOperation::Create(
169 std::make_shared<ReverseUVSampleFunction>(geometry, std::move(source_uv_map)),
170 {std::move(sample_uvs)});
171 params.set_output("Is Valid", Field<bool>(uv_op, 0));
172
173 /* Use the output of the UV sampling to interpolate the mesh attribute. */
174 GField field = params.extract_input<GField>("Value");
175
176 auto sample_op = FieldOperation::Create(
177 std::make_shared<bke::mesh_surface_sample::BaryWeightSampleFn>(std::move(geometry),
178 std::move(field)),
179 {Field<int>(uv_op, 1), Field<float3>(uv_op, 2)});
180 params.set_output("Value", GField(sample_op, 0));
181}
182
183static void node_rna(StructRNA *srna)
184{
186 "data_type",
187 "Data Type",
188 "",
193}
194
195static void node_register()
196{
197 static blender::bke::bNodeType ntype;
198
199 geo_node_type_base(&ntype, GEO_NODE_SAMPLE_UV_SURFACE, "Sample UV Surface", NODE_CLASS_GEOMETRY);
200 ntype.initfunc = node_init;
201 ntype.declare = node_declare;
206
207 node_rna(ntype.rna_ext.srna);
208}
210
211} // namespace blender::nodes::node_geo_sample_uv_surface_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define TIP_(msgid)
#define IFACE_(msgid)
@ CD_PROP_FLOAT
@ CD_PROP_STRING
eNodeSocketDatatype
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
static const CPPType & get()
GVArray try_convert(GVArray varray, const CPPType &to_type) const
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
void set_signature(const Signature *signature)
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
ReverseUVSampleFunction(GeometrySet geometry, Field< float2 > src_uv_map_field)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
const DataTypeConversions & get_implicit_type_conversions()
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:4379
const EnumPropertyItem * attribute_type_type_with_socket_fn(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
static void node_geo_exec(GeoNodeExecParams params)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_declare(NodeDeclarationBuilder &b)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params, Span< SocketDeclaration * > declarations)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
const EnumPropertyItem rna_enum_attribute_type_items[]
StructRNA * srna
Definition RNA_types.hh:780
const Mesh * get_mesh() const
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:363
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126