Blender V5.0
interpolation.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6
7#include "scene/attribute.h"
8#include "scene/mesh.h"
9
10#include "util/color.h"
11
13
14/* Classes for interpolation to use float math for byte value, for precision. */
15template<typename T> struct SubdFloat {
16 using Type = T;
17 using AccumType = T;
18
19 static Type read(const Type &value)
20 {
21 return value;
22 }
23
24 static Type output(const Type &value)
25 {
26 return value;
27 }
28};
29
30struct SubdByte {
31 using Type = uchar4;
33
34 static AccumType read(const Type &value)
35 {
36 return color_uchar4_to_float4(value);
37 }
38
39 static Type output(const AccumType &value)
40 {
41 return color_float4_to_uchar4(value);
42 }
43};
44
45#ifdef WITH_OPENSUBDIV
47 OsdMesh &osd_mesh,
48 OsdData &osd_data)
49#else
51#endif
52 : mesh(mesh)
53#ifdef WITH_OPENSUBDIV
54 ,
55 osd_mesh(osd_mesh),
56 osd_data(osd_data)
57#endif
58{
59}
60
62{
63 if (mesh.get_num_subd_faces() == 0) {
64 return;
65 }
66
67 for (const Attribute &subd_attr : mesh.subd_attributes.attributes) {
68 if (!support_interp_attribute(subd_attr)) {
69 continue;
70 }
71 Attribute &mesh_attr = mesh.attributes.copy(subd_attr);
72 setup_attribute(subd_attr, mesh_attr);
73 }
74}
75
77{
78 switch (attr.std) {
79 /* Smooth normals are computed from derivatives, for linear interpolate. */
82 if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
83 return false;
84 }
85 break;
86 /* PTex coordinates will be computed by subdivision. */
89 return false;
90 default:
91 break;
92 }
93
94 /* Skip element types that should not exist for subd attributes anyway. */
95 switch (attr.element) {
101 break;
102 default:
103 return false;
104 }
105
106 return true;
107}
108
110{
111 if (subd_attr.element == ATTR_ELEMENT_CORNER_BYTE) {
112 setup_attribute_type<SubdByte>(subd_attr, mesh_attr);
113 }
114 else if (Attribute::same_storage(subd_attr.type, TypeFloat)) {
115 setup_attribute_type<SubdFloat<float>>(subd_attr, mesh_attr);
116 }
117 else if (Attribute::same_storage(subd_attr.type, TypeFloat2)) {
118 setup_attribute_type<SubdFloat<float2>>(subd_attr, mesh_attr);
119 }
120 else if (Attribute::same_storage(subd_attr.type, TypeVector)) {
121 setup_attribute_type<SubdFloat<float3>>(subd_attr, mesh_attr);
122 }
123 else if (Attribute::same_storage(subd_attr.type, TypeFloat4) ||
125 {
126 setup_attribute_type<SubdFloat<float4>>(subd_attr, mesh_attr);
127 }
128}
129
130template<typename T>
132 Attribute &mesh_attr,
133 const int motion_step)
134{
135 SubdAttribute attr;
136
137 const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(
138 subd_attr.data()) +
139 motion_step * mesh.get_num_subd_base_verts();
140 typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data()) +
141 motion_step * mesh.get_verts().size();
142
143 assert(mesh_data != nullptr);
144
145 attr.interp = [this, subd_data, mesh_data](const int /*patch_index*/,
146 const int face_index,
147 const int corner,
148 const int *vert_index,
149 const float2 *vert_uv,
150 const int vert_num) {
151 /* Interpolate values at vertices. */
152 const int *subd_face_corners = mesh.get_subd_face_corners().data();
153
154 Mesh::SubdFace face = mesh.get_subd_face(face_index);
155
156 if (face.is_quad()) {
157 /* Simple case for quads. */
158 const typename T::AccumType value0 = T::read(
159 subd_data[subd_face_corners[face.start_corner + 0]]);
160 const typename T::AccumType value1 = T::read(
161 subd_data[subd_face_corners[face.start_corner + 1]]);
162 const typename T::AccumType value2 = T::read(
163 subd_data[subd_face_corners[face.start_corner + 2]]);
164 const typename T::AccumType value3 = T::read(
165 subd_data[subd_face_corners[face.start_corner + 3]]);
166
167 for (int i = 0; i < vert_num; i++) {
168 const float2 uv = vert_uv[i];
169 const typename T::AccumType value = interp(
170 interp(value0, value1, uv.x), interp(value3, value2, uv.x), uv.y);
171 mesh_data[vert_index[i]] = T::output(value);
172 }
173 }
174 else {
175 /* Other n-gons are split into n quads. */
176
177 /* Compute value at center of polygon. */
178 typename T::AccumType value_center = T::read(
179 subd_data[subd_face_corners[face.start_corner]]);
180 for (int j = 1; j < face.num_corners; j++) {
181 value_center += T::read(subd_data[subd_face_corners[face.start_corner + j]]);
182 }
183 value_center /= (float)face.num_corners;
184
185 /* Compute value at corner at adjacent vertices. */
186 const typename T::AccumType value_corner = T::read(
187 subd_data[subd_face_corners[face.start_corner + corner]]);
188 const typename T::AccumType value_prev =
189 0.5f * (value_corner +
190 T::read(subd_data[subd_face_corners[face.start_corner +
191 mod(corner - 1, face.num_corners)]]));
192 const typename T::AccumType value_next =
193 0.5f * (value_corner +
194 T::read(subd_data[subd_face_corners[face.start_corner +
195 mod(corner + 1, face.num_corners)]]));
196
197 for (int i = 0; i < vert_num; i++) {
198 const float2 uv = vert_uv[i];
199
200 /* Interpolate. */
201 const typename T::AccumType value = interp(
202 interp(value_corner, value_next, uv.x), interp(value_prev, value_center, uv.x), uv.y);
203
204 mesh_data[vert_index[i]] = T::output(value);
205 }
206 }
207 };
208
209 vertex_attributes.push_back(std::move(attr));
210}
211
212#ifdef WITH_OPENSUBDIV
213template<typename T>
214void SubdAttributeInterpolation::setup_attribute_vertex_smooth(const Attribute &subd_attr,
215 Attribute &mesh_attr,
216 const int motion_step)
217{
218 SubdAttribute attr;
219
220 // TODO: Avoid computing derivative weights when not needed
221 // TODO: overhead of FindPatch and EvaluateBasis with vertex position
222 const int num_refiner_verts = osd_data.refiner->GetNumVerticesTotal();
223 const int num_local_points = osd_data.patch_table->GetNumLocalPoints();
224 const int num_base_verts = mesh.get_num_subd_base_verts();
225
226 /* Refine attribute data to get patch coordinates. */
227 attr.refined_data.resize((num_refiner_verts + num_local_points) * sizeof(typename T::AccumType));
228 typename T::AccumType *subd_data = reinterpret_cast<typename T::AccumType *>(
229 attr.refined_data.data());
230
231 const typename T::Type *base_src = reinterpret_cast<const typename T::Type *>(subd_attr.data()) +
232 num_base_verts * motion_step;
233 typename T::AccumType *base_dst = subd_data;
234 for (int i = 0; i < num_base_verts; i++) {
235 base_dst[i] = T::read(base_src[i]);
236 }
237
238 Far::PrimvarRefiner primvar_refiner(*osd_data.refiner);
239 typename T::AccumType *src = subd_data;
240 for (int i = 0; i < osd_data.refiner->GetMaxLevel(); i++) {
241 typename T::AccumType *dest = src + osd_data.refiner->GetLevel(i).GetNumVertices();
242 primvar_refiner.Interpolate(
243 i + 1, (OsdValue<typename T::AccumType> *)src, (OsdValue<typename T::AccumType> *&)dest);
244 src = dest;
245 }
246
247 if (num_local_points) {
248 osd_data.patch_table->ComputeLocalPointValues(
249 (OsdValue<typename T::AccumType> *)subd_data,
250 (OsdValue<typename T::AccumType> *)(subd_data + num_refiner_verts));
251 }
252
253 /* Evaluate patches at limit. */
254 typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data()) +
255 mesh.get_verts().size() * motion_step;
256
257 assert(mesh_data != nullptr);
258
259 /* Compute motion normals alongside positions. */
260 float3 *mesh_normal_data = nullptr;
261 if constexpr (std::is_same_v<typename T::Type, float3>) {
262 if (mesh_attr.std == ATTR_STD_MOTION_VERTEX_POSITION) {
263 Attribute *attr_normal = mesh.attributes.add(ATTR_STD_MOTION_VERTEX_NORMAL);
264 mesh_normal_data = attr_normal->data_float3() + mesh.get_verts().size() * motion_step;
265 }
266 }
267
268 attr.interp = [this, subd_data, mesh_data, mesh_normal_data](const int patch_index,
269 const int /*face_index*/,
270 const int /*corner*/,
271 const int *vert_index,
272 const float2 *vert_uv,
273 const int vert_num) {
274 for (int i = 0; i < vert_num; i++) {
275 /* Compute patch weights. */
276 const float2 uv = vert_uv[i];
277
278 const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
279 patch_index, (double)uv.x, (double)uv.y);
280
281 float p_weights[20], du_weights[20], dv_weights[20];
282 osd_data.patch_table->EvaluateBasis(handle, uv.x, uv.y, p_weights, du_weights, dv_weights);
283 Far::ConstIndexArray cv = osd_data.patch_table->GetPatchVertices(handle);
284
285 /* Compution position. */
286 typename T::AccumType value = subd_data[cv[0]] * p_weights[0];
287 for (int k = 1; k < cv.size(); k++) {
288 value += subd_data[cv[k]] * p_weights[k];
289 }
290 mesh_data[vert_index[i]] = T::output(value);
291
292 /* Optionally compute normal. */
293 if (mesh_normal_data) {
294 if constexpr (std::is_same_v<typename T::Type, float3>) {
295 float3 du = zero_float3();
296 float3 dv = zero_float3();
297 for (int k = 0; k < cv.size(); k++) {
298 const float3 p = subd_data[cv[k]];
299 du += p * du_weights[k];
300 dv += p * dv_weights[k];
301 }
302 mesh_normal_data[vert_index[i]] = safe_normalize_fallback(cross(du, dv),
303 make_float3(0.0f, 0.0f, 1.0f));
304 }
305 }
306 }
307 };
308
309 vertex_attributes.push_back(std::move(attr));
310}
311
312#endif
313
314template<typename T>
316 Attribute &mesh_attr)
317{
318 SubdAttribute attr;
319
320 /* Interpolate values at corners. */
321 const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(subd_attr.data());
322 typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
323
324 assert(mesh_data != nullptr);
325
326 attr.interp = [this, subd_data, mesh_data](const int /*patch_index*/,
327 const int face_index,
328 const int corner,
329 const int *triangle_index,
330 const float2 *triangle_uv,
331 const int triangle_num) {
332 Mesh::SubdFace face = mesh.get_subd_face(face_index);
333
334 if (face.is_quad()) {
335 /* Simple case for quads. */
336 const typename T::AccumType value0 = T::read(subd_data[face.start_corner + 0]);
337 const typename T::AccumType value1 = T::read(subd_data[face.start_corner + 1]);
338 const typename T::AccumType value2 = T::read(subd_data[face.start_corner + 2]);
339 const typename T::AccumType value3 = T::read(subd_data[face.start_corner + 3]);
340
341 for (size_t i = 0; i < triangle_num; i++) {
342 for (int j = 0; j < 3; j++) {
343 const float2 uv = triangle_uv[(i * 3) + j];
344 const typename T::AccumType value = interp(
345 interp(value0, value1, uv.x), interp(value3, value2, uv.x), uv.y);
346 mesh_data[triangle_index[i] * 3 + j] = T::output(value);
347 }
348 }
349 }
350 else {
351 /* Other n-gons are split into n quads. */
352
353 /* Compute value at center of polygon. */
354 typename T::AccumType value_center = T::read(subd_data[face.start_corner]);
355 for (int j = 1; j < face.num_corners; j++) {
356 value_center += T::read(subd_data[face.start_corner + j]);
357 }
358 value_center /= (float)face.num_corners;
359
360 /* Compute value at corner at adjacent vertices. */
361 const typename T::AccumType value_corner = T::read(subd_data[face.start_corner + corner]);
362 const typename T::AccumType value_prev =
363 0.5f * (value_corner +
364 T::read(subd_data[face.start_corner + mod(corner - 1, face.num_corners)]));
365 const typename T::AccumType value_next =
366 0.5f * (value_corner +
367 T::read(subd_data[face.start_corner + mod(corner + 1, face.num_corners)]));
368
369 for (size_t i = 0; i < triangle_num; i++) {
370 for (int j = 0; j < 3; j++) {
371 const float2 uv = triangle_uv[(i * 3) + j];
372
373 /* Interpolate. */
374 const typename T::AccumType value = interp(interp(value_corner, value_next, uv.x),
375 interp(value_prev, value_center, uv.x),
376 uv.y);
377
378 mesh_data[triangle_index[i] * 3 + j] = T::output(value);
379 }
380 }
381 }
382 };
383
384 triangle_attributes.push_back(std::move(attr));
385}
386
387#ifdef WITH_OPENSUBDIV
388template<typename T>
389void SubdAttributeInterpolation::setup_attribute_corner_smooth(Attribute &mesh_attr,
390 const int channel,
391 const vector<char> &merged_values)
392{
393 SubdAttribute attr;
394
395 // TODO: Avoid computing derivative weights when not needed
396 const int num_refiner_fvars = osd_data.refiner->GetNumFVarValuesTotal(channel);
397 const int num_local_points = osd_data.patch_table->GetNumLocalPointsFaceVarying(channel);
398 const int num_base_fvars = osd_data.refiner->GetLevel(0).GetNumFVarValues(channel);
399
400 /* Refine attribute data to get patch coordinates. */
401 attr.refined_data.resize((num_refiner_fvars + num_local_points) * sizeof(typename T::AccumType));
402 typename T::AccumType *refined_data = reinterpret_cast<typename T::AccumType *>(
403 attr.refined_data.data());
404
405 const typename T::Type *base_src = reinterpret_cast<const typename T::Type *>(
406 merged_values.data());
407 typename T::AccumType *base_dst = refined_data;
408 for (int i = 0; i < num_base_fvars; i++) {
409 base_dst[i] = T::read(base_src[i]);
410 }
411
412 Far::PrimvarRefiner primvar_refiner(*osd_data.refiner);
413 typename T::AccumType *src = refined_data;
414 for (int i = 0; i < osd_data.refiner->GetMaxLevel(); i++) {
415 typename T::AccumType *dest = src + osd_data.refiner->GetLevel(i).GetNumFVarValues(channel);
416 primvar_refiner.InterpolateFaceVarying(i + 1,
417 (OsdValue<typename T::AccumType> *)src,
418 (OsdValue<typename T::AccumType> *&)dest,
419 channel);
420 src = dest;
421 }
422
423 if (num_local_points) {
424 osd_data.patch_table->ComputeLocalPointValuesFaceVarying(
425 (OsdValue<typename T::AccumType> *)refined_data,
426 (OsdValue<typename T::AccumType> *)(refined_data + num_refiner_fvars),
427 channel);
428 }
429
430 /* Evaluate patches at limit. */
431 const typename T::AccumType *subd_data = refined_data;
432 typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
433
434 assert(mesh_data != nullptr);
435
436 attr.interp = [this, subd_data, mesh_data, channel](const int patch_index,
437 const int /*face_index*/,
438 const int /*corner*/,
439 const int *triangle_index,
440 const float2 *triangle_uv,
441 const int triangle_num) {
442 for (size_t i = 0; i < triangle_num; i++) {
443 for (int j = 0; j < 3; j++) {
444 /* Compute patch weights. */
445 const float2 uv = triangle_uv[(i * 3) + j];
446 const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
447 patch_index, (double)uv.x, (double)uv.y);
448
449 float p_weights[20], du_weights[20], dv_weights[20];
450 osd_data.patch_table->EvaluateBasisFaceVarying(handle,
451 uv.x,
452 uv.y,
453 p_weights,
454 du_weights,
455 dv_weights,
456 nullptr,
457 nullptr,
458 nullptr,
459 channel);
460 Far::ConstIndexArray cv = osd_data.patch_table->GetPatchFVarValues(handle, channel);
461
462 /* Compution position. */
463 typename T::AccumType value = subd_data[cv[0]] * p_weights[0];
464 for (int k = 1; k < cv.size(); k++) {
465 value += subd_data[cv[k]] * p_weights[k];
466 }
467 mesh_data[triangle_index[i] * 3 + j] = T::output(value);
468 }
469 }
470 };
471
472 triangle_attributes.push_back(std::move(attr));
473}
474#endif
475
476template<typename T>
478 Attribute &mesh_attr)
479{
480 /* Copy value from face to triangle. */
481 SubdAttribute attr;
482 const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(subd_attr.data());
483 typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
484
485 assert(mesh_data != nullptr);
486
487 attr.interp = [subd_data, mesh_data](const int /*patch_index*/,
488 const int face_index,
489 const int /*corner*/,
490 const int *triangle_index,
491 const float2 * /*triangle_uv*/,
492 const int triangle_num) {
493 for (int i = 0; i < triangle_num; i++) {
494 mesh_data[triangle_index[i]] = subd_data[face_index];
495 }
496 };
497
498 triangle_attributes.push_back(std::move(attr));
499}
500
501template<typename T>
503 Attribute &mesh_attr)
504{
505 switch (subd_attr.element) {
506 case ATTR_ELEMENT_VERTEX: {
507#ifdef WITH_OPENSUBDIV
508 if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
509 /* Only smoothly interpolation known position-like attributes. */
510 switch (subd_attr.std) {
514 setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr);
515 break;
516 default:
517 setup_attribute_vertex_linear<T>(subd_attr, mesh_attr);
518 break;
519 }
520 }
521 else
522#endif
523 {
524 setup_attribute_vertex_linear<T>(subd_attr, mesh_attr);
525 }
526 break;
527 }
530#ifdef WITH_OPENSUBDIV
531 if (osd_mesh.use_smooth_fvar(subd_attr)) {
532 for (const auto &merged_fvar : osd_mesh.merged_fvars) {
533 if (&merged_fvar.attr == &subd_attr) {
534 if constexpr (std::is_same_v<typename T::Type, float2>) {
535 setup_attribute_corner_smooth<T>(mesh_attr, merged_fvar.channel, merged_fvar.values);
536 return;
537 }
538 }
539 }
540 }
541#endif
542 setup_attribute_corner_linear<T>(subd_attr, mesh_attr);
543 break;
544 }
546 /* Interpolate each motion step individually. */
547 for (int step = 0; step < mesh.get_motion_steps() - 1; step++) {
548#ifdef WITH_OPENSUBDIV
549 if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
550 setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr, step);
551 }
552 else
553#endif
554 {
555 setup_attribute_vertex_linear<T>(subd_attr, mesh_attr, step);
556 }
557 }
558 break;
559 }
560 case ATTR_ELEMENT_FACE: {
561 setup_attribute_face<T>(subd_attr, mesh_attr);
562 break;
563 }
564 default:
565 break;
566 }
567}
568
void setup_attribute_face(const Attribute &subd_attr, Attribute &mesh_attr)
void setup_attribute_type(const Attribute &subd_attr, Attribute &mesh_attr)
void setup_attribute(const Attribute &subd_attr, Attribute &mesh_attr)
vector< SubdAttribute > triangle_attributes
bool support_interp_attribute(const Attribute &attr) const
void setup_attribute_vertex_linear(const Attribute &subd_attr, Attribute &mesh_attr, const int motion_step=0)
vector< SubdAttribute > vertex_attributes
void setup_attribute_corner_linear(const Attribute &subd_attr, Attribute &mesh_attr)
nullptr float
ccl_device_inline float4 color_uchar4_to_float4(const uchar4 c)
Definition color.h:57
ccl_device uchar4 color_float4_to_uchar4(const float4 c)
Definition color.h:37
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define assert(assertion)
VecBase< float, D > constexpr mod(VecOp< float, D >, VecOp< float, D >) RET
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
VecBase< float, 2 > float2
VecBase< float, 3 > float3
@ ATTR_STD_MOTION_VERTEX_NORMAL
@ ATTR_STD_VERTEX_NORMAL
@ ATTR_STD_POSITION_UNDISPLACED
@ ATTR_STD_PTEX_FACE_ID
@ ATTR_STD_POSITION_UNDEFORMED
@ ATTR_STD_MOTION_VERTEX_POSITION
@ ATTR_STD_PTEX_UV
@ ATTR_STD_GENERATED
@ ATTR_ELEMENT_CORNER_BYTE
@ ATTR_ELEMENT_CORNER
@ ATTR_ELEMENT_VERTEX_MOTION
@ ATTR_ELEMENT_VERTEX
@ ATTR_ELEMENT_FACE
ccl_device_inline float interp(const float a, const float b, const float t)
Definition math_base.h:502
ccl_device_inline float3 safe_normalize_fallback(const float3 a, const float3 fallback)
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
#define T
static constexpr TypeDesc TypeRGBA(TypeDesc::FLOAT, TypeDesc::VEC4, TypeDesc::COLOR)
AttributeElement element
TypeDesc type
AttributeStandard std
static bool same_storage(const TypeDesc a, const TypeDesc b)
float3 * data_float3()
size_t get_num_subd_base_verts() const
Definition scene/mesh.h:243
@ SUBDIVISION_CATMULL_CLARK
Definition scene/mesh.h:120
std::function< void(const int, const int, const int, const int *, const float2 *, const int)> interp
vector< char > refined_data
static AccumType read(const Type &value)
float4 AccumType
static Type output(const AccumType &value)
static Type read(const Type &value)
static Type output(const Type &value)
float x
float y
i
Definition text_draw.cc:230