Blender V4.5
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_task.h"
13
14#include "BKE_customdata.hh"
15#include "BKE_mesh.hh"
16#include "BKE_subdiv.hh"
17
18#include "MEM_guardedalloc.h"
19
21#ifdef WITH_OPENSUBDIV
24#endif
25
26/* --------------------------------------------------------------------
27 * Helper functions.
28 */
29
30namespace blender::bke::subdiv {
31
32#ifdef WITH_OPENSUBDIV
33
34static eOpenSubdivEvaluator opensubdiv_evaluator_from_subdiv_evaluator_type(
35 eSubdivEvaluatorType evaluator_type)
36{
37 switch (evaluator_type) {
40 }
43 }
44 }
45 BLI_assert_msg(0, "Unknown evaluator type");
47}
48
49#endif
50
51/* --------------------------------------------------------------------
52 * Main subdivision evaluation.
53 */
54
56 eSubdivEvaluatorType evaluator_type,
57 OpenSubdiv_EvaluatorCache *evaluator_cache,
58 const OpenSubdiv_EvaluatorSettings *settings)
59{
60#ifdef WITH_OPENSUBDIV
62 if (subdiv->topology_refiner == nullptr) {
63 /* Happens on input mesh with just loose geometry,
64 * or when OpenSubdiv is disabled */
65 return false;
66 }
67 if (subdiv->evaluator == nullptr) {
68 eOpenSubdivEvaluator opensubdiv_evaluator_type =
69 opensubdiv_evaluator_from_subdiv_evaluator_type(evaluator_type);
72 subdiv->topology_refiner, opensubdiv_evaluator_type, evaluator_cache);
74 if (subdiv->evaluator == nullptr) {
75 return false;
76 }
77 }
78 else {
79 /* TODO(sergey): Check for topology change. */
80 }
81 subdiv->evaluator->eval_output->setSettings(settings);
83 return true;
84#else
85 UNUSED_VARS(subdiv, evaluator_type, evaluator_cache, settings);
86 return false;
87#endif
88}
89
90#ifdef WITH_OPENSUBDIV
91
92static void set_coarse_positions(Subdiv *subdiv,
93 const Span<float3> positions,
94 const bke::LooseVertCache &verts_no_face)
95{
96 OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
97 if (verts_no_face.count == 0) {
99 reinterpret_cast<const float *>(positions.data()), 0, positions.size());
100 return;
101 }
102 Array<float3> used_vert_positions(positions.size() - verts_no_face.count);
103 const BitSpan bits = verts_no_face.is_loose_bits;
104 int used_vert_count = 0;
105 for (const int vert : positions.index_range()) {
106 if (bits[vert]) {
107 continue;
108 }
109 used_vert_positions[used_vert_count] = positions[vert];
110 used_vert_count++;
111 }
113 reinterpret_cast<const float *>(used_vert_positions.data()), 0, used_vert_positions.size());
114}
115
116/* Context which is used to fill face varying data in parallel. */
117struct FaceVaryingDataFromUVContext {
118 opensubdiv::TopologyRefinerImpl *topology_refiner;
119 const Mesh *mesh;
120 OffsetIndices<int> faces;
121 const float (*mloopuv)[2];
122 float (*buffer)[2];
123 int layer_index;
124};
125
126static void set_face_varying_data_from_uv_task(void *__restrict userdata,
127 const int face_index,
128 const TaskParallelTLS *__restrict /*tls*/)
129{
130 FaceVaryingDataFromUVContext *ctx = static_cast<FaceVaryingDataFromUVContext *>(userdata);
131 opensubdiv::TopologyRefinerImpl *topology_refiner = ctx->topology_refiner;
132 const int layer_index = ctx->layer_index;
133 const float(*mluv)[2] = &ctx->mloopuv[ctx->faces[face_index].start()];
134
135 /* TODO(sergey): OpenSubdiv's C-API converter can change winding of
136 * loops of a face, need to watch for that, to prevent wrong UVs assigned.
137 */
138 const OpenSubdiv::Vtr::ConstIndexArray uv_indices =
139 topology_refiner->base_level().GetFaceFVarValues(face_index, layer_index);
140 for (int vertex_index = 0; vertex_index < uv_indices.size(); vertex_index++, mluv++) {
141 copy_v2_v2(ctx->buffer[uv_indices[vertex_index]], *mluv);
142 }
143}
144
145static void set_face_varying_data_from_uv(Subdiv *subdiv,
146 const Mesh *mesh,
147 const float (*mloopuv)[2],
148 const int layer_index)
149{
150 opensubdiv::TopologyRefinerImpl *topology_refiner = subdiv->topology_refiner;
151 OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
152 const int num_faces = topology_refiner->base_level().GetNumFaces();
153 const float(*mluv)[2] = mloopuv;
154
155 const int num_fvar_values = topology_refiner->base_level().GetNumFVarValues(layer_index);
156 /* Use a temporary buffer so we do not upload UVs one at a time to the GPU. */
157 float(*buffer)[2] = MEM_malloc_arrayN<float[2]>(size_t(num_fvar_values), __func__);
158
159 FaceVaryingDataFromUVContext ctx;
160 ctx.topology_refiner = topology_refiner;
161 ctx.layer_index = layer_index;
162 ctx.mloopuv = mluv;
163 ctx.mesh = mesh;
164 ctx.faces = mesh->faces();
165 ctx.buffer = buffer;
166
167 TaskParallelSettings parallel_range_settings;
168 BLI_parallel_range_settings_defaults(&parallel_range_settings);
169 parallel_range_settings.min_iter_per_thread = 1;
170
172 0, num_faces, &ctx, set_face_varying_data_from_uv_task, &parallel_range_settings);
173
174 evaluator->eval_output->setFaceVaryingData(layer_index, &buffer[0][0], 0, num_fvar_values);
175
176 MEM_freeN(buffer);
177}
178
179static void set_vertex_data_from_orco(Subdiv *subdiv, const Mesh *mesh)
180{
181 const float(*orco)[3] = static_cast<const float(*)[3]>(
183 const float(*cloth_orco)[3] = static_cast<const float(*)[3]>(
185
186 if (orco || cloth_orco) {
187 blender::opensubdiv::TopologyRefinerImpl *topology_refiner = subdiv->topology_refiner;
188 OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
189 const int num_verts = topology_refiner->base_level().GetNumVertices();
190
191 if (orco && cloth_orco) {
192 /* Set one by one if have both. */
193 for (int i = 0; i < num_verts; i++) {
194 float data[6];
195 copy_v3_v3(data, orco[i]);
196 copy_v3_v3(data + 3, cloth_orco[i]);
197 evaluator->eval_output->setVertexData(data, i, 1);
198 }
199 }
200 else {
201 /* Faster single call if we have either. */
202 if (orco) {
203 evaluator->eval_output->setVertexData(orco[0], 0, num_verts);
204 }
205 else if (cloth_orco) {
206 evaluator->eval_output->setVertexData(cloth_orco[0], 0, num_verts);
207 }
208 }
209 }
210}
211
212static void get_mesh_evaluator_settings(OpenSubdiv_EvaluatorSettings *settings, const Mesh *mesh)
213{
214 settings->num_vertex_data = (CustomData_has_layer(&mesh->vert_data, CD_ORCO) ? 3 : 0) +
216}
217
218#endif
219
221 const Mesh *mesh,
222 const Span<float3> coarse_vert_positions,
223 eSubdivEvaluatorType evaluator_type,
224 OpenSubdiv_EvaluatorCache *evaluator_cache)
225{
226#ifdef WITH_OPENSUBDIV
227 OpenSubdiv_EvaluatorSettings settings = {0};
228 get_mesh_evaluator_settings(&settings, mesh);
229 if (!eval_begin(subdiv, evaluator_type, evaluator_cache, &settings)) {
230 return false;
231 }
232 return eval_refine_from_mesh(subdiv, mesh, coarse_vert_positions);
233#else
234 UNUSED_VARS(subdiv, mesh, coarse_vert_positions, evaluator_type, evaluator_cache);
235 return false;
236#endif
237}
238
240 const Mesh *mesh,
241 const Span<float3> coarse_vert_positions)
242{
243#ifdef WITH_OPENSUBDIV
244 if (subdiv->evaluator == nullptr) {
245 /* NOTE: This situation is supposed to be handled by begin(). */
246 BLI_assert_msg(0, "Is not supposed to happen");
247 return false;
248 }
249 /* Set coordinates of base mesh vertices. */
250 set_coarse_positions(subdiv,
251 coarse_vert_positions.is_empty() ? mesh->vert_positions() :
252 coarse_vert_positions,
253 mesh->verts_no_face());
254
255 /* Set face-varying data to UV maps. */
256 const int num_uv_layers = CustomData_number_of_layers(&mesh->corner_data, CD_PROP_FLOAT2);
257 for (int layer_index = 0; layer_index < num_uv_layers; layer_index++) {
258 const float(*mloopuv)[2] = static_cast<const float(*)[2]>(
259 CustomData_get_layer_n(&mesh->corner_data, CD_PROP_FLOAT2, layer_index));
260 set_face_varying_data_from_uv(subdiv, mesh, mloopuv, layer_index);
261 }
262 /* Set vertex data to orco. */
263 set_vertex_data_from_orco(subdiv, mesh);
264 /* Update evaluator to the new coarse geometry. */
266 subdiv->evaluator->eval_output->refine();
268 return true;
269#else
270 UNUSED_VARS(subdiv, mesh, coarse_vert_positions);
271 return false;
272#endif
273}
274
276{
277 if (subdiv->displacement_evaluator == nullptr) {
278 return;
279 }
280 if (subdiv->displacement_evaluator->initialize == nullptr) {
281 return;
282 }
283 subdiv->displacement_evaluator->initialize(subdiv->displacement_evaluator);
284}
285
286/* --------------------------------------------------------------------
287 * Single point queries.
288 */
289
291 Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3])
292{
293 eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, r_P, nullptr, nullptr);
294}
295
297 const int ptex_face_index,
298 const float u,
299 const float v,
300 float r_P[3],
301 float r_dPdu[3],
302 float r_dPdv[3])
303{
304#ifdef WITH_OPENSUBDIV
305 subdiv->evaluator->eval_output->evaluateLimit(ptex_face_index, u, v, r_P, r_dPdu, r_dPdv);
306
307 /* NOTE: In a very rare occasions derivatives are evaluated to zeros or are exactly equal.
308 * This happens, for example, in single vertex on Suzannne's nose (where two quads have 2 common
309 * edges).
310 *
311 * This makes tangent space displacement (such as multi-resolution) impossible to be used in
312 * those vertices, so those needs to be addressed in one way or another.
313 *
314 * Simplest thing to do: step inside of the face a little bit, where there is known patch at
315 * which there must be proper derivatives. This might break continuity of normals, but is better
316 * that giving totally unusable derivatives. */
317
318 if (r_dPdu != nullptr && r_dPdv != nullptr) {
319 if ((is_zero_v3(r_dPdu) || is_zero_v3(r_dPdv)) || equals_v3v3(r_dPdu, r_dPdv)) {
320 subdiv->evaluator->eval_output->evaluateLimit(
321 ptex_face_index, u * 0.999f + 0.0005f, v * 0.999f + 0.0005f, r_P, r_dPdu, r_dPdv);
322 }
323 }
324#else
325 UNUSED_VARS(subdiv, ptex_face_index, u, v, r_P, r_dPdu, r_dPdv);
326#endif
327}
328
330 const int ptex_face_index,
331 const float u,
332 const float v,
333 float r_P[3],
334 float r_N[3])
335{
336 float dPdu[3], dPdv[3];
337 eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, r_P, dPdu, dPdv);
338 cross_v3_v3v3(r_N, dPdu, dPdv);
339 normalize_v3(r_N);
340}
341
343 Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_vertex_data[])
344{
345#ifdef WITH_OPENSUBDIV
346 subdiv->evaluator->eval_output->evaluateVertexData(ptex_face_index, u, v, r_vertex_data);
347#else
348 UNUSED_VARS(subdiv, ptex_face_index, u, v, r_vertex_data);
349#endif
350}
351
353 const int face_varying_channel,
354 const int ptex_face_index,
355 const float u,
356 const float v,
357 float r_face_varying[2])
358{
359#ifdef WITH_OPENSUBDIV
360 subdiv->evaluator->eval_output->evaluateFaceVarying(
361 face_varying_channel, ptex_face_index, u, v, r_face_varying);
362#else
363 UNUSED_VARS(subdiv, ptex_face_index, face_varying_channel, u, v, r_face_varying);
364#endif
365}
366
368 const int ptex_face_index,
369 const float u,
370 const float v,
371 const float dPdu[3],
372 const float dPdv[3],
373 float r_D[3])
374{
375 if (subdiv->displacement_evaluator == nullptr) {
376 zero_v3(r_D);
377 return;
378 }
379 subdiv->displacement_evaluator->eval_displacement(
380 subdiv->displacement_evaluator, ptex_face_index, u, v, dPdu, dPdv, r_D);
381}
382
384 Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3])
385{
386 if (subdiv->displacement_evaluator) {
387 float dPdu[3], dPdv[3], D[3];
388 eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, r_P, dPdu, dPdv);
389 eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
390 add_v3_v3(r_P, D);
391 }
392 else {
393 eval_limit_point(subdiv, ptex_face_index, u, v, r_P);
394 }
395}
396
397} // 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 bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[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
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)
void eval_vertex_data(Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_vertex_data[])
bool eval_begin_from_mesh(Subdiv *subdiv, const Mesh *mesh, Span< float3 > coarse_vert_positions, eSubdivEvaluatorType evaluator_type, OpenSubdiv_EvaluatorCache *evaluator_cache)
void eval_limit_point(Subdiv *subdiv, int ptex_face_index, float u, float v, float r_P[3])
void eval_displacement(Subdiv *subdiv, int ptex_face_index, float u, float v, const float dPdu[3], const float dPdv[3], float r_D[3])
void eval_limit_point_and_derivatives(Subdiv *subdiv, int ptex_face_index, float u, float v, float r_P[3], float r_dPdu[3], float r_dPdv[3])
void eval_limit_point_and_normal(Subdiv *subdiv, int ptex_face_index, float u, float v, float r_P[3], float r_N[3])
bool eval_begin(Subdiv *subdiv, eSubdivEvaluatorType evaluator_type, OpenSubdiv_EvaluatorCache *evaluator_cache, const OpenSubdiv_EvaluatorSettings *settings)
void eval_face_varying(Subdiv *subdiv, int face_varying_channel, int ptex_face_index, float u, float v, float r_face_varying[2])
void stats_reset(SubdivStats *stats, StatsValue value)
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_final_point(Subdiv *subdiv, int ptex_face_index, float u, float v, float r_P[3])
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