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