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