Blender V4.3
node_geo_deform_curves_on_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_curves.hh"
7#include "BKE_editmesh.hh"
8#include "BKE_lib_id.hh"
9#include "BKE_mesh.hh"
10#include "BKE_mesh_wrapper.hh"
11#include "BKE_modifier.hh"
12
13#include "BLI_math_matrix.hh"
14#include "BLI_task.hh"
15
17
19
20#include "node_geometry_util.hh"
21
22#include <fmt/format.h>
23
25
29
31
33{
34 b.add_input<decl::Geometry>("Curves").supported_type(GeometryComponent::Type::Curve);
35 b.add_output<decl::Geometry>("Curves").propagate_all();
36}
37
38static void deform_curves(const CurvesGeometry &curves,
39 const Mesh &surface_mesh_old,
40 const Mesh &surface_mesh_new,
41 const Span<float2> curve_attachment_uvs,
42 const ReverseUVSampler &reverse_uv_sampler_old,
43 const ReverseUVSampler &reverse_uv_sampler_new,
44 const Span<float3> corner_normals_old,
45 const Span<float3> corner_normals_new,
46 const Span<float3> rest_positions,
47 const float4x4 &surface_to_curves,
48 MutableSpan<float3> r_positions,
49 MutableSpan<float3x3> r_rotations,
50 std::atomic<int> &r_invalid_uv_count)
51{
52 /* Find attachment points on old and new mesh. */
53 const int curves_num = curves.curves_num();
54 Array<ReverseUVSampler::Result> surface_samples_old(curves_num);
55 Array<ReverseUVSampler::Result> surface_samples_new(curves_num);
57 1024 < curves_num,
58 [&]() { reverse_uv_sampler_old.sample_many(curve_attachment_uvs, surface_samples_old); },
59 [&]() { reverse_uv_sampler_new.sample_many(curve_attachment_uvs, surface_samples_new); });
60
61 const float4x4 curves_to_surface = math::invert(surface_to_curves);
62
63 const Span<float3> surface_positions_old = surface_mesh_old.vert_positions();
64 const Span<int> surface_corner_verts_old = surface_mesh_old.corner_verts();
65 const Span<int3> surface_corner_tris_old = surface_mesh_old.corner_tris();
66
67 const Span<float3> surface_positions_new = surface_mesh_new.vert_positions();
68 const Span<int> surface_corner_verts_new = surface_mesh_new.corner_verts();
69 const Span<int3> surface_corner_tris_new = surface_mesh_new.corner_tris();
70
71 const OffsetIndices points_by_curve = curves.points_by_curve();
72
73 threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
74 for (const int curve_i : range) {
75 const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i];
76 if (surface_sample_old.type != ReverseUVSampler::ResultType::Ok) {
77 r_invalid_uv_count++;
78 continue;
79 }
80 const ReverseUVSampler::Result &surface_sample_new = surface_samples_new[curve_i];
81 if (surface_sample_new.type != ReverseUVSampler::ResultType::Ok) {
82 r_invalid_uv_count++;
83 continue;
84 }
85
86 const int3 &tri_old = surface_corner_tris_old[surface_sample_old.tri_index];
87 const int3 &tri_new = surface_corner_tris_new[surface_sample_new.tri_index];
88 const float3 &bary_weights_old = surface_sample_old.bary_weights;
89 const float3 &bary_weights_new = surface_sample_new.bary_weights;
90
91 const int corner_0_old = tri_old[0];
92 const int corner_1_old = tri_old[1];
93 const int corner_2_old = tri_old[2];
94
95 const int corner_0_new = tri_new[0];
96 const int corner_1_new = tri_new[1];
97 const int corner_2_new = tri_new[2];
98
99 const int vert_0_old = surface_corner_verts_old[corner_0_old];
100 const int vert_1_old = surface_corner_verts_old[corner_1_old];
101 const int vert_2_old = surface_corner_verts_old[corner_2_old];
102
103 const int vert_0_new = surface_corner_verts_new[corner_0_new];
104 const int vert_1_new = surface_corner_verts_new[corner_1_new];
105 const int vert_2_new = surface_corner_verts_new[corner_2_new];
106
107 const float3 &normal_0_old = corner_normals_old[corner_0_old];
108 const float3 &normal_1_old = corner_normals_old[corner_1_old];
109 const float3 &normal_2_old = corner_normals_old[corner_2_old];
110 const float3 normal_old = math::normalize(
111 mix3(bary_weights_old, normal_0_old, normal_1_old, normal_2_old));
112
113 const float3 &normal_0_new = corner_normals_new[corner_0_new];
114 const float3 &normal_1_new = corner_normals_new[corner_1_new];
115 const float3 &normal_2_new = corner_normals_new[corner_2_new];
116 const float3 normal_new = math::normalize(
117 mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new));
118
119 const float3 &pos_0_old = surface_positions_old[vert_0_old];
120 const float3 &pos_1_old = surface_positions_old[vert_1_old];
121 const float3 &pos_2_old = surface_positions_old[vert_2_old];
122 const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old);
123
124 const float3 &pos_0_new = surface_positions_new[vert_0_new];
125 const float3 &pos_1_new = surface_positions_new[vert_1_new];
126 const float3 &pos_2_new = surface_positions_new[vert_2_new];
127 const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new);
128
129 /* The translation is just the difference between the old and new position on the surface. */
130 const float3 translation = pos_new - pos_old;
131
132 const float3 &rest_pos_0 = rest_positions[vert_0_new];
133 const float3 &rest_pos_1 = rest_positions[vert_1_new];
134
135 /* The tangent reference direction is used to determine the rotation of the surface point
136 * around its normal axis. It's important that the old and new tangent reference are computed
137 * in a consistent way. If the surface has not been rotated, the old and new tangent
138 * reference have to have the same direction. For that reason, the old tangent reference is
139 * computed based on the rest position attribute instead of positions on the old mesh. This
140 * way the old and new tangent reference use the same topology.
141 *
142 * TODO: Figure out if this can be smoothly interpolated across the surface as well.
143 * Currently, this is a source of discontinuity in the deformation, because the vector
144 * changes instantly from one triangle to the next. */
145 const float3 tangent_reference_dir_old = rest_pos_1 - rest_pos_0;
146 const float3 tangent_reference_dir_new = pos_1_new - pos_0_new;
147
148 /* Compute first local tangent based on the (potentially smoothed) normal and the tangent
149 * reference. */
150 const float3 tangent_x_old = math::normalize(
151 math::cross(normal_old, tangent_reference_dir_old));
152 const float3 tangent_x_new = math::normalize(
153 math::cross(normal_new, tangent_reference_dir_new));
154
155 /* The second tangent defined by the normal and first tangent. */
156 const float3 tangent_y_old = math::normalize(math::cross(normal_old, tangent_x_old));
157 const float3 tangent_y_new = math::normalize(math::cross(normal_new, tangent_x_new));
158
159 /* Construct rotation matrix that encodes the orientation of the old surface position. */
160 float3x3 rotation_old(tangent_x_old, tangent_y_old, normal_old);
161
162 /* Construct rotation matrix that encodes the orientation of the new surface position. */
163 float3x3 rotation_new(tangent_x_new, tangent_y_new, normal_new);
164
165 /* Can use transpose instead of inverse because the matrix is orthonormal. In the case of
166 * zero-area triangles, the matrix would not be orthonormal, but in this case, none of this
167 * works anyway. */
168 const float3x3 rotation_old_inv = math::transpose(rotation_old);
169
170 /* Compute a rotation matrix that rotates points from the old to the new surface
171 * orientation. */
172 const float3x3 rotation = rotation_new * rotation_old_inv;
173
174 /* Construction transformation matrix for this surface position that includes rotation and
175 * translation. */
176 /* Subtract and add #pos_old, so that the rotation origin is the position on the surface. */
177 float4x4 surface_transform = math::from_origin_transform<float4x4>(float4x4(rotation),
178 pos_old);
179 surface_transform.location() += translation;
180
181 /* Change the basis of the transformation so to that it can be applied in the local space of
182 * the curves. */
183 const float4x4 curve_transform = surface_to_curves * surface_transform * curves_to_surface;
184
185 /* Actually transform all points. */
186 const IndexRange points = points_by_curve[curve_i];
187 for (const int point_i : points) {
188 const float3 old_point_pos = r_positions[point_i];
189 const float3 new_point_pos = math::transform_point(curve_transform, old_point_pos);
190 r_positions[point_i] = new_point_pos;
191 }
192
193 if (!r_rotations.is_empty()) {
194 for (const int point_i : points) {
195 r_rotations[point_i] = rotation * r_rotations[point_i];
196 }
197 }
198 }
199 });
200}
201
203{
204 GeometrySet curves_geometry = params.extract_input<GeometrySet>("Curves");
205
206 Mesh *surface_mesh_orig = nullptr;
207 bool free_suface_mesh_orig = false;
208 BLI_SCOPED_DEFER([&]() {
209 if (free_suface_mesh_orig) {
210 BKE_id_free(nullptr, surface_mesh_orig);
211 }
212 });
213
214 auto pass_through_input = [&]() { params.set_output("Curves", std::move(curves_geometry)); };
215
216 const Object *self_ob_eval = params.self_object();
217 if (self_ob_eval == nullptr || self_ob_eval->type != OB_CURVES) {
218 pass_through_input();
219 params.error_message_add(NodeWarningType::Error, TIP_("Node only works for curves objects"));
220 return;
221 }
222 const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data);
223 if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') {
224 pass_through_input();
225 params.error_message_add(NodeWarningType::Error, TIP_("Surface UV map not defined"));
226 return;
227 }
228 /* Take surface information from self-object. */
229 Object *surface_ob_eval = self_curves_eval->surface;
230 const StringRefNull uv_map_name = self_curves_eval->surface_uv_map;
231 const StringRefNull rest_position_name = "rest_position";
232
233 if (!curves_geometry.has_curves()) {
234 pass_through_input();
235 return;
236 }
237 if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) {
238 pass_through_input();
239 params.error_message_add(NodeWarningType::Error, TIP_("Curves not attached to a surface"));
240 return;
241 }
242 Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval);
243 Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data);
244
245 if (BMEditMesh *em = surface_object_data.runtime->edit_mesh.get()) {
246 surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, &surface_object_data);
247 free_suface_mesh_orig = true;
248 }
249 else {
250 surface_mesh_orig = &surface_object_data;
251 }
252 Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval);
253 if (surface_mesh_eval == nullptr) {
254 pass_through_input();
255 params.error_message_add(NodeWarningType::Error, TIP_("Surface has no mesh"));
256 return;
257 }
258
259 BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval);
260
261 const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes();
262 const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes();
263
264 Curves &curves_id = *curves_geometry.get_curves_for_write();
265 CurvesGeometry &curves = curves_id.geometry.wrap();
266
267 if (!mesh_attributes_eval.contains(uv_map_name)) {
268 pass_through_input();
269 const std::string message = fmt::format(TIP_("Evaluated surface missing UV map: \"{}\""),
270 uv_map_name);
271 params.error_message_add(NodeWarningType::Error, message);
272 return;
273 }
274 if (!mesh_attributes_orig.contains(uv_map_name)) {
275 pass_through_input();
276 const std::string message = fmt::format(TIP_("Original surface missing UV map: \"{}\""),
277 uv_map_name);
278 params.error_message_add(NodeWarningType::Error, message);
279 return;
280 }
281 if (!mesh_attributes_eval.contains(rest_position_name)) {
282 pass_through_input();
283 params.error_message_add(NodeWarningType::Error,
284 TIP_("Evaluated surface missing attribute: \"rest_position\""));
285 return;
286 }
287 if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) {
288 pass_through_input();
289 params.error_message_add(NodeWarningType::Error,
290 TIP_("Curves are not attached to any UV map"));
291 return;
292 }
293 const VArraySpan uv_map_orig = *mesh_attributes_orig.lookup<float2>(uv_map_name,
294 AttrDomain::Corner);
295 const VArraySpan uv_map_eval = *mesh_attributes_eval.lookup<float2>(uv_map_name,
296 AttrDomain::Corner);
297 const VArraySpan rest_positions = *mesh_attributes_eval.lookup<float3>(rest_position_name,
298 AttrDomain::Point);
299 const VArraySpan surface_uv_coords = *curves.attributes().lookup_or_default<float2>(
300 "surface_uv_coordinate", AttrDomain::Curve, float2(0));
301
302 const Span<int3> corner_tris_orig = surface_mesh_orig->corner_tris();
303 const Span<int3> corner_tris_eval = surface_mesh_eval->corner_tris();
304 const ReverseUVSampler reverse_uv_sampler_orig{uv_map_orig, corner_tris_orig};
305 const ReverseUVSampler reverse_uv_sampler_eval{uv_map_eval, corner_tris_eval};
306
307 /* Retrieve face corner normals from each mesh. It's necessary to use face corner normals
308 * because face normals or vertex normals may lose information (custom normals, auto smooth) in
309 * some cases. */
310 const Span<float3> corner_normals_orig = surface_mesh_orig->corner_normals();
311 const Span<float3> corner_normals_eval = surface_mesh_eval->corner_normals();
312
313 std::atomic<int> invalid_uv_count = 0;
314
315 const bke::CurvesSurfaceTransforms transforms{*self_ob_eval, surface_ob_eval};
316
317 bke::CurvesEditHints *edit_hints = curves_geometry.get_curve_edit_hints_for_write();
318 MutableSpan<float3> edit_hint_positions;
319 MutableSpan<float3x3> edit_hint_rotations;
320 if (edit_hints != nullptr) {
321 if (const std::optional<MutableSpan<float3>> positions = edit_hints->positions_for_write()) {
322 edit_hint_positions = *positions;
323 }
324 if (!edit_hints->deform_mats.has_value()) {
325 edit_hints->deform_mats.emplace(edit_hints->curves_id_orig.geometry.point_num,
326 float3x3::identity());
327 edit_hints->deform_mats->fill(float3x3::identity());
328 }
329 edit_hint_rotations = *edit_hints->deform_mats;
330 }
331
332 if (edit_hint_positions.is_empty()) {
333 deform_curves(curves,
334 *surface_mesh_orig,
335 *surface_mesh_eval,
336 surface_uv_coords,
337 reverse_uv_sampler_orig,
338 reverse_uv_sampler_eval,
339 corner_normals_orig,
340 corner_normals_eval,
341 rest_positions,
342 transforms.surface_to_curves,
343 curves.positions_for_write(),
344 edit_hint_rotations,
345 invalid_uv_count);
346 }
347 else {
348 /* First deform the actual curves in the input geometry. */
349 deform_curves(curves,
350 *surface_mesh_orig,
351 *surface_mesh_eval,
352 surface_uv_coords,
353 reverse_uv_sampler_orig,
354 reverse_uv_sampler_eval,
355 corner_normals_orig,
356 corner_normals_eval,
357 rest_positions,
358 transforms.surface_to_curves,
359 curves.positions_for_write(),
360 {},
361 invalid_uv_count);
362 /* Then also deform edit curve information for use in sculpt mode. */
363 const CurvesGeometry &curves_orig = edit_hints->curves_id_orig.geometry.wrap();
364 const VArraySpan<float2> surface_uv_coords_orig = *curves_orig.attributes().lookup_or_default(
365 "surface_uv_coordinate", AttrDomain::Curve, float2(0));
366 if (!surface_uv_coords_orig.is_empty()) {
367 deform_curves(curves_orig,
368 *surface_mesh_orig,
369 *surface_mesh_eval,
370 surface_uv_coords_orig,
371 reverse_uv_sampler_orig,
372 reverse_uv_sampler_eval,
373 corner_normals_orig,
374 corner_normals_eval,
375 rest_positions,
376 transforms.surface_to_curves,
377 edit_hint_positions,
378 edit_hint_rotations,
379 invalid_uv_count);
380 }
381 }
382
383 curves.tag_positions_changed();
384
385 if (invalid_uv_count) {
386 const std::string message = fmt::format(TIP_("Invalid surface UVs on {} curves"),
387 invalid_uv_count.load());
388 params.error_message_add(NodeWarningType::Warning, message);
389 }
390
391 params.set_output("Curves", curves_geometry);
392}
393
394static void node_register()
395{
396 static blender::bke::bNodeType ntype;
398 &ntype, GEO_NODE_DEFORM_CURVES_ON_SURFACE, "Deform Curves on Surface", NODE_CLASS_GEOMETRY);
400 ntype.declare = node_declare;
401 blender::bke::node_type_size(&ntype, 170, 120, 700);
403}
404NOD_REGISTER_NODE(node_register)
405
406} // namespace blender::nodes::node_geo_deform_curves_on_surface_cc
Low-level operations for curves.
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
Mesh * BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define BLI_SCOPED_DEFER(function_to_defer)
#define TIP_(msgid)
Object * DEG_get_original_object(Object *object)
@ OB_MESH
@ OB_CURVES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
AttributeSet attributes
constexpr bool is_empty() const
Definition BLI_span.hh:510
constexpr bool is_empty() const
Definition BLI_span.hh:261
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
bool contains(const StringRef attribute_id) const
std::optional< MutableSpan< float3 > > positions_for_write()
std::optional< Array< float3x3 > > deform_mats
void sample_many(Span< float2 > query_uvs, MutableSpan< Result > r_results) const
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
T mix3(const float3 &weights, const T &v0, const T &v1, const T &v2)
void node_type_size(bNodeType *ntype, int width, int minwidth, int maxwidth)
Definition node.cc:4602
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
CartesianBasis invert(const CartesianBasis &basis)
static void deform_curves(const CurvesGeometry &curves, const Mesh &surface_mesh_old, const Mesh &surface_mesh_new, const Span< float2 > curve_attachment_uvs, const ReverseUVSampler &reverse_uv_sampler_old, const ReverseUVSampler &reverse_uv_sampler_new, const Span< float3 > corner_normals_old, const Span< float3 > corner_normals_new, const Span< float3 > rest_positions, const float4x4 &surface_to_curves, MutableSpan< float3 > r_positions, MutableSpan< float3x3 > r_rotations, std::atomic< int > &r_invalid_uv_count)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:199
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
CurvesGeometry geometry
struct Object * surface
char * surface_uv_map
MeshRuntimeHandle * runtime
CurvesEditHints * get_curve_edit_hints_for_write()
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347