Blender V4.3
subdiv_displacement_multires.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 <cmath>
10
11#include "BKE_subdiv.hh"
12
13#include "DNA_mesh_types.h"
14#include "DNA_meshdata_types.h"
15#include "DNA_modifier_types.h"
16
17#include "BLI_math_matrix.h"
18#include "BLI_math_vector.h"
19#include "BLI_utildefines.h"
20
21#include "BKE_customdata.hh"
22#include "BKE_mesh.hh"
23#include "BKE_multires.hh"
24#include "BKE_subdiv_eval.hh"
25
26#include "MEM_guardedalloc.h"
27
28namespace blender::bke::subdiv {
29
32 int corner;
33};
34
38 /* Mesh is used to read external displacement. */
42 const MDisps *mdisps;
43 /* Indexed by PTEX face index, contains face/corner which corresponds
44 * to it.
45 *
46 * NOTE: For quad face this is an index of first corner only, since
47 * there we only have one PTEX. */
49 /* Indexed by coarse face index, returns first PTEX face index corresponding
50 * to that coarse face. */
52 /* Sanity check, is used in debug builds.
53 * Controls that initialize() was called prior to eval_displacement(). */
55};
56
57/* Denotes which grid to use to average value of the displacement read from the
58 * grid which corresponds to the PTEX face. */
65
67 const int ptex_face_index,
68 const float u,
69 const float v,
70 const MDisps **r_displacement_grid,
71 float *grid_u,
72 float *grid_v)
73{
75 displacement->user_data);
76 const PolyCornerIndex *face_corner = &data->ptex_face_corner[ptex_face_index];
77 const IndexRange face = data->faces[face_corner->face_index];
78 const int start_grid_index = face.start() + face_corner->corner;
79 int corner = 0;
80 if (face.size() == 4) {
81 float corner_u, corner_v;
82 corner = rotate_quad_to_corner(u, v, &corner_u, &corner_v);
83 *r_displacement_grid = &data->mdisps[start_grid_index + corner];
84 ptex_face_uv_to_grid_uv(corner_u, corner_v, grid_u, grid_v);
85 }
86 else {
87 *r_displacement_grid = &data->mdisps[start_grid_index];
88 ptex_face_uv_to_grid_uv(u, v, grid_u, grid_v);
89 }
90 return corner;
91}
92
94 const int ptex_face_index,
95 const int corner,
96 const int corner_delta)
97{
99 displacement->user_data);
100 const PolyCornerIndex *face_corner = &data->ptex_face_corner[ptex_face_index];
101 const IndexRange face = data->faces[face_corner->face_index];
102 const int effective_corner = (face.size() == 4) ? corner : face_corner->corner;
103 const int next_corner = (effective_corner + corner_delta + face.size()) % face.size();
104 return &data->mdisps[face[next_corner]];
105}
106
108 const int grid_size,
109 const float grid_u,
110 const float grid_v,
111 float r_tangent_D[3])
112{
113 if (displacement_grid->disps == nullptr) {
114 zero_v3(r_tangent_D);
115 return AVERAGE_WITH_NONE;
116 }
117 const int x = roundf(grid_u * (grid_size - 1));
118 const int y = roundf(grid_v * (grid_size - 1));
119 copy_v3_v3(r_tangent_D, displacement_grid->disps[y * grid_size + x]);
120 if (x == 0 && y == 0) {
121 return AVERAGE_WITH_ALL;
122 }
123 if (x == 0) {
124 return AVERAGE_WITH_PREV;
125 }
126 if (y == 0) {
127 return AVERAGE_WITH_NEXT;
128 }
129 return AVERAGE_WITH_NONE;
130}
131
132static void average_convert_grid_coord_to_ptex(const int num_corners,
133 const int corner,
134 const float grid_u,
135 const float grid_v,
136 float *r_ptex_face_u,
137 float *r_ptex_face_v)
138{
139 if (num_corners == 4) {
140 rotate_grid_to_quad(corner, grid_u, grid_v, r_ptex_face_u, r_ptex_face_v);
141 }
142 else {
143 grid_uv_to_ptex_face_uv(grid_u, grid_v, r_ptex_face_u, r_ptex_face_v);
144 }
145}
146
148 const int num_corners,
149 const int ptex_face_index,
150 const int corner,
151 const float u,
152 const float v,
153 float r_tangent_matrix[3][3])
154{
155 const bool is_quad = num_corners == 4;
156 const int quad_corner = is_quad ? corner : 0;
157 float dummy_P[3], dPdu[3], dPdv[3];
158 eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
159 BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, quad_corner);
160}
161
163 const MDisps *other_displacement_grid,
164 const float grid_u,
165 const float grid_v,
166 float r_tangent_D[3])
167{
168 read_displacement_grid(other_displacement_grid, data->grid_size, grid_u, grid_v, r_tangent_D);
169}
170
172 const MDisps *displacement_grid,
173 const float grid_u,
174 const float grid_v,
175 const int ptex_face_index,
176 const int corner_index,
177 float r_D[3])
178{
179 const PolyCornerIndex *face_corner = &data->ptex_face_corner[ptex_face_index];
180 const int num_corners = data->faces[face_corner->face_index].size();
181 /* Get (u, v) coordinate within the other PTEX face which corresponds to
182 * the grid coordinates. */
183 float u, v;
184 average_convert_grid_coord_to_ptex(num_corners, corner_index, grid_u, grid_v, &u, &v);
185 /* Construct tangent matrix which corresponds to partial derivatives
186 * calculated for the other PTEX face. */
187 float tangent_matrix[3][3];
189 data->subdiv, num_corners, ptex_face_index, corner_index, u, v, tangent_matrix);
190 /* Read displacement from other grid in a tangent space. */
191 float tangent_D[3];
192 average_read_displacement_tangent(data, displacement_grid, grid_u, grid_v, tangent_D);
193 /* Convert displacement to object space. */
194 mul_v3_m3v3(r_D, tangent_matrix, tangent_D);
195}
196
198 const int ptex_face_index,
199 const int corner,
200 const int corner_delta,
201 int *r_other_ptex_face_index,
202 int *r_other_corner_index)
203{
204 const PolyCornerIndex *face_corner = &data->ptex_face_corner[ptex_face_index];
205 const int face_index = face_corner->face_index;
206 const int num_corners = data->faces[face_corner->face_index].size();
207 const bool is_quad = (num_corners == 4);
208 const int start_ptex_face_index = data->face_ptex_offset[face_index];
209 *r_other_corner_index = (corner + corner_delta + num_corners) % num_corners;
210 *r_other_ptex_face_index = is_quad ? start_ptex_face_index :
211 start_ptex_face_index + *r_other_corner_index;
212}
213
214/* NOTE: Grid coordinates are relative to the other grid already. */
215static void average_with_other(Displacement *displacement,
216 const int ptex_face_index,
217 const int corner,
218 const float grid_u,
219 const float grid_v,
220 const int corner_delta,
221 float r_D[3])
222{
224 displacement->user_data);
225 const MDisps *other_displacement_grid = displacement_get_other_grid(
226 displacement, ptex_face_index, corner, corner_delta);
227 int other_ptex_face_index, other_corner_index;
229 data, ptex_face_index, corner, corner_delta, &other_ptex_face_index, &other_corner_index);
230 /* Get displacement in object space. */
231 float other_D[3];
233 other_displacement_grid,
234 grid_u,
235 grid_v,
236 other_ptex_face_index,
237 other_corner_index,
238 other_D);
239 /* Average result with the other displacement vector. */
240 add_v3_v3(r_D, other_D);
241 mul_v3_fl(r_D, 0.5f);
242}
243
244static void average_with_all(Displacement *displacement,
245 const int ptex_face_index,
246 const int corner,
247 const float /*grid_u*/,
248 const float /*grid_v*/,
249 float r_D[3])
250{
252 displacement->user_data);
253 const PolyCornerIndex *face_corner = &data->ptex_face_corner[ptex_face_index];
254 const int num_corners = data->faces[face_corner->face_index].size();
255 for (int corner_delta = 1; corner_delta < num_corners; corner_delta++) {
256 average_with_other(displacement, ptex_face_index, corner, 0.0f, 0.0f, corner_delta, r_D);
257 }
258}
259
260static void average_with_next(Displacement *displacement,
261 const int ptex_face_index,
262 const int corner,
263 const float grid_u,
264 const float /*grid_v*/,
265 float r_D[3])
266{
267 average_with_other(displacement, ptex_face_index, corner, 0.0f, grid_u, 1, r_D);
268}
269
270static void average_with_prev(Displacement *displacement,
271 const int ptex_face_index,
272 const int corner,
273 const float /*grid_u*/,
274 const float grid_v,
275 float r_D[3])
276{
277 average_with_other(displacement, ptex_face_index, corner, grid_v, 0.0f, -1, r_D);
278}
279
280static void average_displacement(Displacement *displacement,
281 eAverageWith average_with,
282 const int ptex_face_index,
283 const int corner,
284 const float grid_u,
285 const float grid_v,
286 float r_D[3])
287{
288 switch (average_with) {
289 case AVERAGE_WITH_ALL:
290 average_with_all(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
291 break;
293 average_with_prev(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
294 break;
296 average_with_next(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
297 break;
299 break;
300 }
301}
302
304 const int ptex_face_index,
305 const float u,
306 const float v)
307{
308 const PolyCornerIndex *face_corner = &data->ptex_face_corner[ptex_face_index];
309 const int num_corners = data->faces[face_corner->face_index].size();
310 const bool is_quad = (num_corners == 4);
311 if (is_quad) {
312 float dummy_corner_u, dummy_corner_v;
313 return rotate_quad_to_corner(u, v, &dummy_corner_u, &dummy_corner_v);
314 }
315
316 return face_corner->corner;
317}
318
319static void initialize(Displacement *displacement)
320{
322 displacement->user_data);
323 multiresModifier_ensure_external_read(data->mesh, data->mmd);
324 data->is_initialized = true;
325}
326
327static void eval_displacement(Displacement *displacement,
328 const int ptex_face_index,
329 const float u,
330 const float v,
331 const float dPdu[3],
332 const float dPdv[3],
333 float r_D[3])
334{
336 displacement->user_data);
337 BLI_assert(data->is_initialized);
338 const int grid_size = data->grid_size;
339 /* Get displacement in tangent space. */
340 const MDisps *displacement_grid;
341 float grid_u, grid_v;
342 const int corner_of_quad = displacement_get_grid_and_coord(
343 displacement, ptex_face_index, u, v, &displacement_grid, &grid_u, &grid_v);
344 /* Read displacement from the current displacement grid and see if any
345 * averaging is needed. */
346 float tangent_D[3];
348 displacement_grid, grid_size, grid_u, grid_v, tangent_D);
349 /* Convert it to the object space. */
350 float tangent_matrix[3][3];
351 BKE_multires_construct_tangent_matrix(tangent_matrix, dPdu, dPdv, corner_of_quad);
352 mul_v3_m3v3(r_D, tangent_matrix, tangent_D);
353 /* For the boundary points of grid average two (or all) neighbor grids. */
354 const int corner = displacement_get_face_corner(data, ptex_face_index, u, v);
355 average_displacement(displacement, average_with, ptex_face_index, corner, grid_u, grid_v, r_D);
356}
357
358static void free_displacement(Displacement *displacement)
359{
361 displacement->user_data);
362 MEM_freeN(data->ptex_face_corner);
363 MEM_freeN(data);
364}
365
366/* TODO(sergey): This seems to be generally used information, which almost
367 * worth adding to a subdiv itself, with possible cache of the value. */
368static int count_num_ptex_faces(const Mesh *mesh)
369{
370 int num_ptex_faces = 0;
371 const OffsetIndices faces = mesh->faces();
372 for (int face_index = 0; face_index < mesh->faces_num; face_index++) {
373 num_ptex_faces += (faces[face_index].size() == 4) ? 1 : faces[face_index].size();
374 }
375 return num_ptex_faces;
376}
377
378static void displacement_data_init_mapping(Displacement *displacement, const Mesh *mesh)
379{
381 displacement->user_data);
382 const OffsetIndices faces = mesh->faces();
383 const int num_ptex_faces = count_num_ptex_faces(mesh);
384 /* Allocate memory. */
385 data->ptex_face_corner = static_cast<PolyCornerIndex *>(
386 MEM_malloc_arrayN(num_ptex_faces, sizeof(*data->ptex_face_corner), "PTEX face corner"));
387 /* Fill in offsets. */
388 int ptex_face_index = 0;
389 PolyCornerIndex *ptex_face_corner = data->ptex_face_corner;
390 for (int face_index = 0; face_index < mesh->faces_num; face_index++) {
391 const IndexRange face = faces[face_index];
392 if (face.size() == 4) {
393 ptex_face_corner[ptex_face_index].face_index = face_index;
394 ptex_face_corner[ptex_face_index].corner = 0;
395 ptex_face_index++;
396 }
397 else {
398 for (int corner = 0; corner < face.size(); corner++) {
399 ptex_face_corner[ptex_face_index].face_index = face_index;
400 ptex_face_corner[ptex_face_index].corner = corner;
401 ptex_face_index++;
402 }
403 }
404 }
405}
406
407static void displacement_init_data(Displacement *displacement,
408 Subdiv *subdiv,
409 Mesh *mesh,
410 const MultiresModifierData *mmd)
411{
413 displacement->user_data);
414 data->subdiv = subdiv;
415 data->grid_size = grid_size_from_level(mmd->totlvl);
416 data->mesh = mesh;
417 data->mmd = mmd;
418 data->faces = mesh->faces();
419 data->mdisps = static_cast<const MDisps *>(CustomData_get_layer(&mesh->corner_data, CD_MDISPS));
420 data->face_ptex_offset = face_ptex_offset_get(subdiv);
421 data->is_initialized = false;
422 displacement_data_init_mapping(displacement, mesh);
423}
424
426{
427 displacement->initialize = initialize;
428 displacement->eval_displacement = eval_displacement;
429 displacement->free = free_displacement;
430}
431
433{
434 /* Make sure we don't have previously assigned displacement. */
435 displacement_detach(subdiv);
436 /* It is possible to have mesh without CD_MDISPS layer. Happens when using
437 * dynamic topology. */
438 if (!CustomData_has_layer(&mesh->corner_data, CD_MDISPS)) {
439 return;
440 }
441 /* Allocate all required memory. */
442 Displacement *displacement = MEM_cnew<Displacement>("multires displacement");
443 displacement->user_data = MEM_callocN(sizeof(MultiresDisplacementData),
444 "multires displacement data");
445 displacement_init_data(displacement, subdiv, mesh, mmd);
446 displacement_init_functions(displacement);
447 /* Finish. */
448 subdiv->displacement_evaluator = displacement;
449}
450
451} // namespace blender::bke::subdiv
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3], const float dPdu[3], const float dPdv[3], int corner)
void multiresModifier_ensure_external_read(Mesh *mesh, const MultiresModifierData *mmd)
Definition multires.cc:1524
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v
void initialize()
constexpr int64_t start() const
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void displacement_attach_from_multires(Subdiv *subdiv, Mesh *mesh, const MultiresModifierData *mmd)
static void average_with_prev(Displacement *displacement, const int ptex_face_index, const int corner, const float, const float grid_v, float r_D[3])
static void average_convert_grid_coord_to_ptex(const int num_corners, const int corner, const float grid_u, const float grid_v, float *r_ptex_face_u, float *r_ptex_face_v)
static void average_read_displacement_object(MultiresDisplacementData *data, const MDisps *displacement_grid, const float grid_u, const float grid_v, const int ptex_face_index, const int corner_index, float r_D[3])
static void average_construct_tangent_matrix(Subdiv *subdiv, const int num_corners, const int ptex_face_index, const int corner, const float u, const float v, float r_tangent_matrix[3][3])
static void average_displacement(Displacement *displacement, eAverageWith average_with, const int ptex_face_index, const int corner, const float grid_u, const float grid_v, float r_D[3])
static void displacement_init_data(Displacement *displacement, Subdiv *subdiv, Mesh *mesh, const MultiresModifierData *mmd)
static void average_with_other(Displacement *displacement, const int ptex_face_index, const int corner, const float grid_u, const float grid_v, const int corner_delta, float r_D[3])
static const MDisps * displacement_get_other_grid(Displacement *displacement, const int ptex_face_index, const int corner, const int corner_delta)
BLI_INLINE void rotate_grid_to_quad(int corner, float grid_u, float grid_v, float *r_quad_u, float *r_quad_v)
static void average_read_displacement_tangent(MultiresDisplacementData *data, const MDisps *other_displacement_grid, const float grid_u, const float grid_v, float r_tangent_D[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])
static int displacement_get_grid_and_coord(Displacement *displacement, const int ptex_face_index, const float u, const float v, const MDisps **r_displacement_grid, float *grid_u, float *grid_v)
BLI_INLINE void grid_uv_to_ptex_face_uv(float grid_u, float grid_v, float *r_ptex_u, float *r_ptex_v)
void displacement_detach(Subdiv *subdiv)
BLI_INLINE void ptex_face_uv_to_grid_uv(float ptex_u, float ptex_v, float *r_grid_u, float *r_grid_v)
BLI_INLINE int grid_size_from_level(int level)
BLI_INLINE int rotate_quad_to_corner(float quad_u, float quad_v, float *r_corner_u, float *r_corner_v)
static void average_get_other_ptex_and_corner(MultiresDisplacementData *data, const int ptex_face_index, const int corner, const int corner_delta, int *r_other_ptex_face_index, int *r_other_corner_index)
BLI_INLINE eAverageWith read_displacement_grid(const MDisps *displacement_grid, const int grid_size, const float grid_u, const float grid_v, float r_tangent_D[3])
static void displacement_init_functions(Displacement *displacement)
int * face_ptex_offset_get(Subdiv *subdiv)
Definition subdiv.cc:219
static void free_displacement(Displacement *displacement)
static void displacement_data_init_mapping(Displacement *displacement, const Mesh *mesh)
static int displacement_get_face_corner(MultiresDisplacementData *data, const int ptex_face_index, const float u, const float v)
static int count_num_ptex_faces(const Mesh *mesh)
static void average_with_all(Displacement *displacement, const int ptex_face_index, const int corner, const float, const float, float r_D[3])
static void average_with_next(Displacement *displacement, const int ptex_face_index, const int corner, const float grid_u, const float, float r_D[3])
float(* disps)[3]
void(* free)(Displacement *displacement)
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