Blender V5.0
subdiv_eval.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2018 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_subdiv_eval.hh"
10
11#include "BLI_math_vector.h"
12#include "BLI_math_vector.hh"
13#include "BLI_task.h"
14
15#include "BKE_customdata.hh"
16#include "BKE_mesh.hh"
17#include "BKE_subdiv.hh"
18
19#include "MEM_guardedalloc.h"
20
22#ifdef WITH_OPENSUBDIV
25#endif
26
27/* --------------------------------------------------------------------
28 * Helper functions.
29 */
30
31namespace blender::bke::subdiv {
32
33#ifdef WITH_OPENSUBDIV
34
35static eOpenSubdivEvaluator opensubdiv_evaluator_from_subdiv_evaluator_type(
36 eSubdivEvaluatorType evaluator_type)
37{
38 switch (evaluator_type) {
41 }
44 }
45 }
46 BLI_assert_msg(0, "Unknown evaluator type");
48}
49
50#endif
51
52/* --------------------------------------------------------------------
53 * Main subdivision evaluation.
54 */
55
57 eSubdivEvaluatorType evaluator_type,
58 OpenSubdiv_EvaluatorCache *evaluator_cache,
59 const OpenSubdiv_EvaluatorSettings *settings)
60{
61#ifdef WITH_OPENSUBDIV
63 if (subdiv->topology_refiner == nullptr) {
64 /* Happens on input mesh with just loose geometry,
65 * or when OpenSubdiv is disabled */
66 return false;
67 }
68 if (subdiv->evaluator == nullptr) {
69 eOpenSubdivEvaluator opensubdiv_evaluator_type =
70 opensubdiv_evaluator_from_subdiv_evaluator_type(evaluator_type);
73 subdiv->topology_refiner, opensubdiv_evaluator_type, evaluator_cache);
75 if (subdiv->evaluator == nullptr) {
76 return false;
77 }
78 }
79 else {
80 /* TODO(sergey): Check for topology change. */
81 }
82 subdiv->evaluator->eval_output->setSettings(settings);
84 return true;
85#else
86 UNUSED_VARS(subdiv, evaluator_type, evaluator_cache, settings);
87 return false;
88#endif
89}
90
91#ifdef WITH_OPENSUBDIV
92
93static void set_coarse_positions(Subdiv *subdiv,
94 const Span<float3> positions,
95 const bke::LooseVertCache &verts_no_face)
96{
97 OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
98 if (verts_no_face.count == 0) {
100 reinterpret_cast<const float *>(positions.data()), 0, positions.size());
101 return;
102 }
103 Array<float3> used_vert_positions(positions.size() - verts_no_face.count);
104 const BitSpan bits = verts_no_face.is_loose_bits;
105 int used_vert_count = 0;
106 for (const int vert : positions.index_range()) {
107 if (bits[vert]) {
108 continue;
109 }
110 used_vert_positions[used_vert_count] = positions[vert];
111 used_vert_count++;
112 }
114 reinterpret_cast<const float *>(used_vert_positions.data()), 0, used_vert_positions.size());
115}
116
117/* Context which is used to fill face varying data in parallel. */
118struct FaceVaryingDataFromUVContext {
119 opensubdiv::TopologyRefinerImpl *topology_refiner;
120 const Mesh *mesh;
121 OffsetIndices<int> faces;
122 const float (*uv_map)[2];
123 float (*buffer)[2];
124 int layer_index;
125};
126
127static void set_face_varying_data_from_uv_task(void *__restrict userdata,
128 const int face_index,
129 const TaskParallelTLS *__restrict /*tls*/)
130{
131 FaceVaryingDataFromUVContext *ctx = static_cast<FaceVaryingDataFromUVContext *>(userdata);
132 opensubdiv::TopologyRefinerImpl *topology_refiner = ctx->topology_refiner;
133 const int layer_index = ctx->layer_index;
134 const float (*mluv)[2] = &ctx->uv_map[ctx->faces[face_index].start()];
135
136 /* TODO(sergey): OpenSubdiv's C-API converter can change winding of
137 * loops of a face, need to watch for that, to prevent wrong UVs assigned.
138 */
139 const OpenSubdiv::Vtr::ConstIndexArray uv_indices =
140 topology_refiner->base_level().GetFaceFVarValues(face_index, layer_index);
141 for (int vertex_index = 0; vertex_index < uv_indices.size(); vertex_index++, mluv++) {
142 copy_v2_v2(ctx->buffer[uv_indices[vertex_index]], *mluv);
143 }
144}
145
146static void set_face_varying_data_from_uv(Subdiv *subdiv,
147 const Mesh *mesh,
148 const float (*uv_map)[2],
149 const int layer_index)
150{
151 opensubdiv::TopologyRefinerImpl *topology_refiner = subdiv->topology_refiner;
152 OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
153 const int num_faces = topology_refiner->base_level().GetNumFaces();
154 const float (*mluv)[2] = uv_map;
155
156 const int num_fvar_values = topology_refiner->base_level().GetNumFVarValues(layer_index);
157 /* Use a temporary buffer so we do not upload UVs one at a time to the GPU. */
158 float (*buffer)[2] = MEM_malloc_arrayN<float[2]>(size_t(num_fvar_values), __func__);
159
160 FaceVaryingDataFromUVContext ctx;
161 ctx.topology_refiner = topology_refiner;
162 ctx.layer_index = layer_index;
163 ctx.uv_map = mluv;
164 ctx.mesh = mesh;
165 ctx.faces = mesh->faces();
166 ctx.buffer = buffer;
167
168 TaskParallelSettings parallel_range_settings;
169 BLI_parallel_range_settings_defaults(&parallel_range_settings);
170 parallel_range_settings.min_iter_per_thread = 1;
171
173 0, num_faces, &ctx, set_face_varying_data_from_uv_task, &parallel_range_settings);
174
175 evaluator->eval_output->setFaceVaryingData(layer_index, &buffer[0][0], 0, num_fvar_values);
176
177 MEM_freeN(buffer);
178}
179
180static void set_vertex_data_from_orco(Subdiv *subdiv, const Mesh *mesh)
181{
182 const float (*orco)[3] = static_cast<const float (*)[3]>(
184 const float (*cloth_orco)[3] = static_cast<const float (*)[3]>(
186
187 if (orco || cloth_orco) {
188 blender::opensubdiv::TopologyRefinerImpl *topology_refiner = subdiv->topology_refiner;
189 OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
190 const int num_verts = topology_refiner->base_level().GetNumVertices();
191
192 if (orco && cloth_orco) {
193 /* Set one by one if have both. */
194 for (int i = 0; i < num_verts; i++) {
195 float data[6];
196 copy_v3_v3(data, orco[i]);
197 copy_v3_v3(data + 3, cloth_orco[i]);
198 evaluator->eval_output->setVertexData(data, i, 1);
199 }
200 }
201 else {
202 /* Faster single call if we have either. */
203 if (orco) {
204 evaluator->eval_output->setVertexData(orco[0], 0, num_verts);
205 }
206 else if (cloth_orco) {
207 evaluator->eval_output->setVertexData(cloth_orco[0], 0, num_verts);
208 }
209 }
210 }
211}
212
213static void get_mesh_evaluator_settings(OpenSubdiv_EvaluatorSettings *settings, const Mesh *mesh)
214{
215 settings->num_vertex_data = (CustomData_has_layer(&mesh->vert_data, CD_ORCO) ? 3 : 0) +
217}
218
219#endif
220
222 const Mesh *mesh,
223 eSubdivEvaluatorType evaluator_type,
224 const Span<float3> coarse_vert_positions,
225 OpenSubdiv_EvaluatorCache *evaluator_cache)
226{
227#ifdef WITH_OPENSUBDIV
228 OpenSubdiv_EvaluatorSettings settings = {0};
229 get_mesh_evaluator_settings(&settings, mesh);
230 if (!eval_begin(subdiv, evaluator_type, evaluator_cache, &settings)) {
231 return false;
232 }
233 return eval_refine_from_mesh(subdiv, mesh, coarse_vert_positions);
234#else
235 UNUSED_VARS(subdiv, mesh, coarse_vert_positions, evaluator_type, evaluator_cache);
236 return false;
237#endif
238}
239
241 const Mesh *mesh,
242 const Span<float3> coarse_vert_positions)
243{
244#ifdef WITH_OPENSUBDIV
245 if (subdiv->evaluator == nullptr) {
246 /* NOTE: This situation is supposed to be handled by begin(). */
247 BLI_assert_msg(0, "Is not supposed to happen");
248 return false;
249 }
250 /* Set coordinates of base mesh vertices. */
251 set_coarse_positions(subdiv,
252 coarse_vert_positions.is_empty() ? mesh->vert_positions() :
253 coarse_vert_positions,
254 mesh->verts_no_face());
255
256 /* Set face-varying data to UV maps. */
257 const int num_uv_layers = CustomData_number_of_layers(&mesh->corner_data, CD_PROP_FLOAT2);
258 for (int layer_index = 0; layer_index < num_uv_layers; layer_index++) {
259 const float (*uv_map)[2] = static_cast<const float (*)[2]>(
260 CustomData_get_layer_n(&mesh->corner_data, CD_PROP_FLOAT2, layer_index));
261 set_face_varying_data_from_uv(subdiv, mesh, uv_map, layer_index);
262 }
263 /* Set vertex data to orco. */
264 set_vertex_data_from_orco(subdiv, mesh);
265 /* Update evaluator to the new coarse geometry. */
267 subdiv->evaluator->eval_output->refine();
269 return true;
270#else
271 UNUSED_VARS(subdiv, mesh, coarse_vert_positions);
272 return false;
273#endif
274}
275
277{
278 if (subdiv->displacement_evaluator == nullptr) {
279 return;
280 }
281 if (subdiv->displacement_evaluator->initialize == nullptr) {
282 return;
283 }
284 subdiv->displacement_evaluator->initialize(subdiv->displacement_evaluator);
285}
286
287/* --------------------------------------------------------------------
288 * Single point queries.
289 */
290
291float3 eval_limit_point(Subdiv *subdiv, const int ptex_face_index, const float u, const float v)
292{
293#ifdef WITH_OPENSUBDIV
294 float3 r_P;
295 subdiv->evaluator->eval_output->evaluateLimit(ptex_face_index, u, v, r_P, nullptr, nullptr);
296 return r_P;
297#else
298 UNUSED_VARS(subdiv, ptex_face_index, u, v);
299 return {0.0f, 0.0f, 0.0f};
300#endif
301}
302
304 const int ptex_face_index,
305 const float u,
306 const float v,
307 float3 &r_P,
308 float3 &r_dPdu,
309 float3 &r_dPdv)
310{
311#ifdef WITH_OPENSUBDIV
312 subdiv->evaluator->eval_output->evaluateLimit(ptex_face_index, u, v, r_P, r_dPdu, r_dPdv);
313
314 /* NOTE: In a very rare occasions derivatives are evaluated to zeros or are exactly equal.
315 * This happens, for example, in single vertex on Suzannne's nose (where two quads have 2 common
316 * edges).
317 *
318 * This makes tangent space displacement (such as multi-resolution) impossible to be used in
319 * those vertices, so those needs to be addressed in one way or another.
320 *
321 * Simplest thing to do: step inside of the face a little bit, where there is known patch at
322 * which there must be proper derivatives. This might break continuity of normals, but is better
323 * that giving totally unusable derivatives. */
324
325 if ((math::is_zero(r_dPdu) || math::is_zero(r_dPdv)) || math::is_equal(r_dPdu, r_dPdv)) {
326 subdiv->evaluator->eval_output->evaluateLimit(
327 ptex_face_index, u * 0.999f + 0.0005f, v * 0.999f + 0.0005f, r_P, r_dPdu, r_dPdv);
328 }
329#else
330 UNUSED_VARS(subdiv, ptex_face_index, u, v, r_P, r_dPdu, r_dPdv);
331#endif
332}
333
335 const int ptex_face_index,
336 const float u,
337 const float v,
338 float3 &r_P,
339 float3 &r_N)
340{
341 float3 dPdu;
342 float3 dPdv;
343 eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, r_P, dPdu, dPdv);
344 r_N = math::normalize(math::cross(dPdu, dPdv));
345}
346
348 Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_vertex_data[])
349{
350#ifdef WITH_OPENSUBDIV
351 subdiv->evaluator->eval_output->evaluateVertexData(ptex_face_index, u, v, r_vertex_data);
352#else
353 UNUSED_VARS(subdiv, ptex_face_index, u, v, r_vertex_data);
354#endif
355}
356
358 const int face_varying_channel,
359 const int ptex_face_index,
360 const float u,
361 const float v,
362 float2 &r_face_varying)
363{
364#ifdef WITH_OPENSUBDIV
365 subdiv->evaluator->eval_output->evaluateFaceVarying(
366 face_varying_channel, ptex_face_index, u, v, r_face_varying);
367#else
368 UNUSED_VARS(subdiv, ptex_face_index, face_varying_channel, u, v, r_face_varying);
369#endif
370}
371
373 const int ptex_face_index,
374 const float u,
375 const float v,
376 const float3 &dPdu,
377 const float3 &dPdv,
378 float3 &r_D)
379{
380 if (subdiv->displacement_evaluator == nullptr) {
381 r_D = float3(0.0f);
382 return;
383 }
384 subdiv->displacement_evaluator->eval_displacement(
385 subdiv->displacement_evaluator, ptex_face_index, u, v, dPdu, dPdv, r_D);
386}
387
388float3 eval_final_point(Subdiv *subdiv, const int ptex_face_index, const float u, const float v)
389{
390 float3 r_P;
391 if (subdiv->displacement_evaluator) {
392 float3 dPdu;
393 float3 dPdv;
394 float3 D;
395 eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, r_P, dPdu, dPdv);
396 eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
397 r_P += D;
398 }
399 else {
400 r_P = eval_limit_point(subdiv, ptex_face_index, u, v);
401 }
402 return r_P;
403}
404
405} // namespace blender::bke::subdiv
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
#define D
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
struct TaskParallelTLS TaskParallelTLS
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
struct TaskParallelSettings TaskParallelSettings
#define UNUSED_VARS(...)
@ CD_PROP_FLOAT2
@ CD_CLOTH_ORCO
struct Mesh Mesh
Read Guarded memory(de)allocation.
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
void setFaceVaryingData(const int face_varying_channel, const float *varying_data, const int start_vertex_index, const int num_vertices)
void setVertexData(const float *data, const int start_vertex_index, const int num_vertices)
void setCoarsePositions(const float *positions, const int start_vertex_index, const int num_vertices)
const OpenSubdiv::Far::TopologyLevel & base_level() const
nullptr float
OpenSubdiv_Evaluator * openSubdiv_createEvaluatorFromTopologyRefiner(blender::opensubdiv::TopologyRefinerImpl *topology_refiner, eOpenSubdivEvaluator evaluator_type, OpenSubdiv_EvaluatorCache *evaluator_cache_descr)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void eval_init_displacement(Subdiv *subdiv)
float3 eval_final_point(Subdiv *subdiv, int ptex_face_index, float u, float v)
bool eval_begin_from_mesh(Subdiv *subdiv, const Mesh *mesh, eSubdivEvaluatorType evaluator_type, Span< float3 > coarse_vert_positions={}, OpenSubdiv_EvaluatorCache *evaluator_cache=nullptr)
float3 eval_limit_point(Subdiv *subdiv, int ptex_face_index, float u, float v)
bool eval_begin(Subdiv *subdiv, eSubdivEvaluatorType evaluator_type, OpenSubdiv_EvaluatorCache *evaluator_cache, const OpenSubdiv_EvaluatorSettings *settings)
void eval_limit_point_and_normal(Subdiv *subdiv, int ptex_face_index, float u, float v, float3 &r_P, float3 &r_N)
void stats_reset(SubdivStats *stats, StatsValue value)
void eval_limit_point_and_derivatives(Subdiv *subdiv, int ptex_face_index, float u, float v, float3 &r_P, float3 &r_dPdu, float3 &r_dPdv)
void eval_displacement(Subdiv *subdiv, int ptex_face_index, float u, float v, const float3 &dPdu, const float3 &dPdv, float3 &r_D)
void stats_begin(SubdivStats *stats, StatsValue value)
void stats_end(SubdivStats *stats, StatsValue value)
bool eval_refine_from_mesh(Subdiv *subdiv, const Mesh *mesh, Span< float3 > coarse_vert_positions)
void eval_face_varying(Subdiv *subdiv, int face_varying_channel, int ptex_face_index, float u, float v, float2 &r_face_varying)
void eval_vertex_data(Subdiv *subdiv, int ptex_face_index, float u, float v, float r_vertex_data[])
bool is_zero(const T &a)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
bool is_equal(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, const T epsilon=T(0))
VecBase< float, 2 > float2
VecBase< float, 3 > float3
eOpenSubdivEvaluator
@ OPENSUBDIV_EVALUATOR_GPU
@ OPENSUBDIV_EVALUATOR_CPU
CustomData vert_data
blender::opensubdiv::EvalOutputAPI * eval_output
blender::BitVector is_loose_bits
i
Definition text_draw.cc:230