Blender V5.0
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
5#include "DNA_mesh_types.h"
6
7#include "BKE_mesh_sample.hh"
9
10#include "NOD_rna_define.hh"
12
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")
31 .supported_type(GeometryComponent::Type::Mesh)
32 .description("Mesh whose UV map is used");
33 if (node != nullptr) {
34 const eCustomDataType data_type = eCustomDataType(node->custom1);
35 b.add_input(data_type, "Value").hide_value().field_on_all();
36 }
37 b.add_input<decl::Vector>("UV Map", "Source UV Map")
38 .hide_value()
39 .field_on_all()
40 .description("The mesh UV map to sample. Should not have overlapping faces");
41 b.add_input<decl::Vector>("Sample UV")
42 .supports_field()
43 .description("The coordinates to sample within the UV map")
44 .structure_type(StructureType::Dynamic);
45
46 if (node != nullptr) {
47 const eCustomDataType data_type = eCustomDataType(node->custom1);
48 b.add_output(data_type, "Value").dependent_field({3});
49 }
50 b.add_output<decl::Bool>("Is Valid")
51 .dependent_field({3})
52 .description("Whether the node could find a single face to sample at the UV coordinate");
53}
54
55static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
56{
57 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
58}
59
60static void node_init(bNodeTree * /*tree*/, bNode *node)
61{
62 node->custom1 = CD_PROP_FLOAT;
63}
64
66{
67 const NodeDeclaration &declaration = *params.node_type().static_declaration;
70
71 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
72 eNodeSocketDatatype(params.other_socket().type));
73 if (type && *type != CD_PROP_STRING) {
74 /* The input and output sockets have the same name. */
75 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
76 bNode &node = params.add_node("GeometryNodeSampleUVSurface");
77 node.custom1 = *type;
78 params.update_and_connect_available_socket(node, "Value");
79 });
80 }
81}
82
83class ReverseUVSampleFunction : public mf::MultiFunction {
84 GeometrySet source_;
85 Field<float2> src_uv_map_field_;
86
87 std::optional<bke::MeshFieldContext> source_context_;
88 std::unique_ptr<FieldEvaluator> source_evaluator_;
89 VArraySpan<float2> source_uv_map_;
90
91 std::optional<ReverseUVSampler> reverse_uv_sampler_;
92
93 public:
95 : source_(std::move(geometry)), src_uv_map_field_(std::move(src_uv_map_field))
96 {
97 source_.ensure_owns_direct_data();
98 this->evaluate_source();
99
100 static const mf::Signature signature = []() {
101 mf::Signature signature;
102 mf::SignatureBuilder builder{"Sample UV Surface", signature};
103 builder.single_input<float2>("Sample UV");
104 builder.single_output<bool>("Is Valid", mf::ParamFlag::SupportsUnusedOutput);
105 builder.single_output<int>("Triangle Index", mf::ParamFlag::SupportsUnusedOutput);
106 builder.single_output<float3>("Barycentric Weights", mf::ParamFlag::SupportsUnusedOutput);
107 return signature;
108 }();
109 this->set_signature(&signature);
110 }
111
112 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
113 {
114 const VArraySpan<float2> sample_uvs = params.readonly_single_input<float2>(0, "Sample UV");
115 MutableSpan<bool> is_valid = params.uninitialized_single_output_if_required<bool>(1,
116 "Is Valid");
117 MutableSpan<int> tri_index = params.uninitialized_single_output_if_required<int>(
118 2, "Triangle Index");
119 MutableSpan<float3> bary_weights = params.uninitialized_single_output_if_required<float3>(
120 3, "Barycentric Weights");
121
122 mask.foreach_index([&](const int i) {
123 const ReverseUVSampler::Result result = reverse_uv_sampler_->sample(sample_uvs[i]);
124 if (!is_valid.is_empty()) {
125 is_valid[i] = result.type == ReverseUVSampler::ResultType::Ok;
126 }
127 if (!tri_index.is_empty()) {
128 tri_index[i] = result.tri_index;
129 }
130 if (!bary_weights.is_empty()) {
131 bary_weights[i] = result.bary_weights;
132 }
133 });
134 }
135
136 private:
137 void evaluate_source()
138 {
139 const Mesh &mesh = *source_.get_mesh();
140 source_context_.emplace(bke::MeshFieldContext{mesh, AttrDomain::Corner});
141 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, mesh.corners_num);
142 source_evaluator_->add(src_uv_map_field_);
143 source_evaluator_->evaluate();
144 source_uv_map_ = source_evaluator_->get_evaluated<float2>(0);
145
146 reverse_uv_sampler_.emplace(source_uv_map_, mesh.corner_tris());
147 }
148};
149
151{
152 GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
153 const Mesh *mesh = geometry.get_mesh();
154 if (mesh == nullptr) {
155 params.set_default_remaining_outputs();
156 return;
157 }
158 if (mesh->faces_num == 0 && mesh->verts_num != 0) {
159 params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces"));
160 params.set_default_remaining_outputs();
161 return;
162 }
163
164 /* Do reverse sampling of the UV map first. */
166 const CPPType &float2_type = CPPType::get<float2>();
167 Field<float2> source_uv_map = conversions.try_convert(
168 params.extract_input<Field<float3>>("Source UV Map"), float2_type);
169
170 auto sample_uv_value = params.extract_input<bke::SocketValueVariant>("Sample UV");
171 if (sample_uv_value.is_list()) {
172 params.error_message_add(NodeWarningType::Error,
173 "Lists are not supported for \"Sample UV\" input");
174 }
175 if (sample_uv_value.is_volume_grid()) {
176 params.error_message_add(NodeWarningType::Error,
177 "Volume grids are not supported for \"Sample UV\" input");
178 }
179 Field<float2> sample_uvs = conversions.try_convert(sample_uv_value.extract<Field<float3>>(),
180 float2_type);
181
182 auto uv_op = FieldOperation::from(
183 std::make_shared<ReverseUVSampleFunction>(geometry, std::move(source_uv_map)),
184 {std::move(sample_uvs)});
185 params.set_output("Is Valid", Field<bool>(uv_op, 0));
186
187 /* Use the output of the UV sampling to interpolate the mesh attribute. */
188 GField field = params.extract_input<GField>("Value");
189
190 auto sample_op = FieldOperation::from(
191 std::make_shared<bke::mesh_surface_sample::BaryWeightSampleFn>(std::move(geometry),
192 std::move(field)),
193 {Field<int>(uv_op, 1), Field<float3>(uv_op, 2)});
194 params.set_output("Value", GField(sample_op, 0));
195}
196
197static void node_rna(StructRNA *srna)
198{
200 "data_type",
201 "Data Type",
202 "",
207}
208
209static void node_register()
210{
211 static blender::bke::bNodeType ntype;
212
213 geo_node_type_base(&ntype, "GeometryNodeSampleUVSurface", GEO_NODE_SAMPLE_UV_SURFACE);
214 ntype.ui_name = "Sample UV Surface";
215 ntype.ui_description =
216 "Calculate the interpolated values of a mesh attribute at a UV coordinate";
217 ntype.enum_name_legacy = "SAMPLE_UV_SURFACE";
219 ntype.initfunc = node_init;
220 ntype.declare = node_declare;
225
226 node_rna(ntype.rna_ext.srna);
227}
229
230} // namespace blender::nodes::node_geo_sample_uv_surface_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_SAMPLE_UV_SURFACE
#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
static const CPPType & get()
GVArray try_convert(GVArray varray, const CPPType &to_type) const
void set_signature(const Signature *signature)
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
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
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
const DataTypeConversions & get_implicit_type_conversions()
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5144
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)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
const EnumPropertyItem rna_enum_attribute_type_items[]
StructRNA * srna
int corners_num
int16_t custom1
const Mesh * get_mesh() const
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238