Blender V5.0
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_matrix.hh"
19#include "BLI_math_vector.h"
20#include "BLI_offset_indices.hh"
21
22#include "BKE_customdata.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
36 Subdiv *subdiv = nullptr;
37 int grid_size = 0;
38 /* Mesh is used to read external displacement. */
39 const Mesh *mesh = nullptr;
40 const MultiresModifierData *mmd = nullptr;
42 const MDisps *mdisps = nullptr;
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(). */
54 bool is_initialized = false;
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. */
59enum class AverageWith : int8_t {
64};
65
66static int displacement_get_grid_and_coord(const Displacement &displacement,
67 const int ptex_face_index,
68 const float u,
69 const float v,
70 const MDisps **r_displacement_grid,
71 float &r_grid_u,
72 float &r_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, &r_grid_u, &r_grid_v);
85 }
86 else {
87 *r_displacement_grid = &data.mdisps[start_grid_index];
88 ptex_face_uv_to_grid_uv(u, v, &r_grid_u, &r_grid_v);
89 }
90 return corner;
91}
92
93static const MDisps *displacement_get_other_grid(const Displacement &displacement,
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 float3 &r_tangent_D)
112{
113 if (displacement_grid.disps == nullptr) {
114 r_tangent_D = float3(0.0f);
115 return AverageWith::None;
116 }
117 const int x = roundf(grid_u * (grid_size - 1));
118 const int y = roundf(grid_v * (grid_size - 1));
119 r_tangent_D = displacement_grid.disps[y * grid_size + x];
120 if (x == 0 && y == 0) {
121 return AverageWith::All;
122 }
123 if (x == 0) {
124 return AverageWith::Prev;
125 }
126 if (y == 0) {
127 return AverageWith::Next;
128 }
129 return AverageWith::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 float3x3 &r_tangent_matrix)
154{
155 const bool is_quad = num_corners == 4;
156 const int quad_corner = is_quad ? corner : 0;
157 float3 dummy_P;
158 float3 dPdu;
159 float3 dPdv;
160 eval_limit_point_and_derivatives(&subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
161 BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, quad_corner);
162}
163
165 const MDisps &other_displacement_grid,
166 const float grid_u,
167 const float grid_v,
168 float3 &r_tangent_D)
169{
170 read_displacement_grid(other_displacement_grid, data.grid_size, grid_u, grid_v, r_tangent_D);
171}
172
174 const MDisps &displacement_grid,
175 const float grid_u,
176 const float grid_v,
177 const int ptex_face_index,
178 const int corner_index,
179 float3 &r_D)
180{
181 const PolyCornerIndex &face_corner = data.ptex_face_corner[ptex_face_index];
182 const int num_corners = data.faces[face_corner.face_index].size();
183 /* Get (u, v) coordinate within the other PTEX face which corresponds to
184 * the grid coordinates. */
185 float u, v;
186 average_convert_grid_coord_to_ptex(num_corners, corner_index, grid_u, grid_v, u, v);
187 /* Construct tangent matrix which corresponds to partial derivatives
188 * calculated for the other PTEX face. */
189 float3x3 tangent_matrix;
191 *data.subdiv, num_corners, ptex_face_index, corner_index, u, v, tangent_matrix);
192 /* Read displacement from other grid in a tangent space. */
193 float3 tangent_D;
194 average_read_displacement_tangent(data, displacement_grid, grid_u, grid_v, tangent_D);
195 /* Convert displacement to object space. */
196 r_D = math::transform_direction(tangent_matrix, tangent_D);
197}
198
200 const int ptex_face_index,
201 const int corner,
202 const int corner_delta,
203 int &r_other_ptex_face_index,
204 int &r_other_corner_index)
205{
206 const PolyCornerIndex &face_corner = data.ptex_face_corner[ptex_face_index];
207 const int face_index = face_corner.face_index;
208 const int num_corners = data.faces[face_corner.face_index].size();
209 const bool is_quad = (num_corners == 4);
210 const int start_ptex_face_index = data.face_ptex_offset[face_index];
211 r_other_corner_index = (corner + corner_delta + num_corners) % num_corners;
212 r_other_ptex_face_index = is_quad ? start_ptex_face_index :
213 start_ptex_face_index + r_other_corner_index;
214}
215
216/* NOTE: Grid coordinates are relative to the other grid already. */
217static void average_with_other(const Displacement &displacement,
218 const int ptex_face_index,
219 const int corner,
220 const float grid_u,
221 const float grid_v,
222 const int corner_delta,
223 float3 &r_D)
224{
226 displacement.user_data);
227 const MDisps other_displacement_grid = *displacement_get_other_grid(
228 displacement, ptex_face_index, corner, corner_delta);
229 int other_ptex_face_index, other_corner_index;
231 data, ptex_face_index, corner, corner_delta, other_ptex_face_index, other_corner_index);
232 /* Get displacement in object space. */
233 float3 other_D;
235 other_displacement_grid,
236 grid_u,
237 grid_v,
238 other_ptex_face_index,
239 other_corner_index,
240 other_D);
241 /* Average result with the other displacement vector. */
242 r_D += other_D;
243 r_D *= 0.5f;
244}
245
246static void average_with_all(const Displacement &displacement,
247 const int ptex_face_index,
248 const int corner,
249 const float /*grid_u*/,
250 const float /*grid_v*/,
251 float3 &r_D)
252{
254 displacement.user_data);
255 const PolyCornerIndex &face_corner = data.ptex_face_corner[ptex_face_index];
256 const int num_corners = data.faces[face_corner.face_index].size();
257 for (int corner_delta = 1; corner_delta < num_corners; corner_delta++) {
258 average_with_other(displacement, ptex_face_index, corner, 0.0f, 0.0f, corner_delta, r_D);
259 }
260}
261
262static void average_with_next(const Displacement &displacement,
263 const int ptex_face_index,
264 const int corner,
265 const float grid_u,
266 const float /*grid_v*/,
267 float3 &r_D)
268{
269 average_with_other(displacement, ptex_face_index, corner, 0.0f, grid_u, 1, r_D);
270}
271
272static void average_with_prev(const Displacement &displacement,
273 const int ptex_face_index,
274 const int corner,
275 const float /*grid_u*/,
276 const float grid_v,
277 float3 &r_D)
278{
279 average_with_other(displacement, ptex_face_index, corner, grid_v, 0.0f, -1, r_D);
280}
281
282static void average_displacement(const Displacement &displacement,
283 const AverageWith average_with,
284 const int ptex_face_index,
285 const int corner,
286 const float grid_u,
287 const float grid_v,
288 float3 &r_D)
289{
290 switch (average_with) {
291 case AverageWith::All:
292 average_with_all(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
293 break;
295 average_with_prev(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
296 break;
298 average_with_next(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
299 break;
301 break;
302 }
303}
304
306 const int ptex_face_index,
307 const float u,
308 const float v)
309{
310 const PolyCornerIndex &face_corner = data.ptex_face_corner[ptex_face_index];
311 const int num_corners = data.faces[face_corner.face_index].size();
312 const bool is_quad = (num_corners == 4);
313 if (is_quad) {
314 float dummy_corner_u, dummy_corner_v;
315 return rotate_quad_to_corner(u, v, &dummy_corner_u, &dummy_corner_v);
316 }
317
318 return face_corner.corner;
319}
320
321static void initialize(Displacement *displacement)
322{
324 displacement->user_data);
325 multiresModifier_ensure_external_read(const_cast<Mesh *>(data.mesh), data.mmd);
326 data.is_initialized = true;
327}
328
329static void eval_displacement(Displacement *displacement,
330 const int ptex_face_index,
331 const float u,
332 const float v,
333 const float3 &dPdu,
334 const float3 &dPdv,
335 float3 &r_D)
336{
338 displacement->user_data);
339 BLI_assert(data.is_initialized);
340 const int grid_size = data.grid_size;
341 /* Get displacement in tangent space. */
342 const MDisps *displacement_grid;
343 float grid_u, grid_v;
344 const int corner_of_quad = displacement_get_grid_and_coord(
345 *displacement, ptex_face_index, u, v, &displacement_grid, grid_u, grid_v);
346 /* Read displacement from the current displacement grid and see if any
347 * averaging is needed. */
348 float3 tangent_D;
349 const AverageWith average_with = read_displacement_grid(
350 *displacement_grid, grid_size, grid_u, grid_v, tangent_D);
351 /* Convert it to the object space. */
352 float3x3 tangent_matrix;
353 BKE_multires_construct_tangent_matrix(tangent_matrix, dPdu, dPdv, corner_of_quad);
354
355 r_D = math::transform_direction(tangent_matrix, tangent_D);
356 /* For the boundary points of grid average two (or all) neighbor grids. */
357 const int corner = displacement_get_face_corner(data, ptex_face_index, u, v);
358 average_displacement(*displacement, average_with, ptex_face_index, corner, grid_u, grid_v, r_D);
359}
360
361static void free_displacement(Displacement *displacement)
362{
364 displacement->user_data);
365 MEM_delete(data);
366}
367
368/* TODO(sergey): This seems to be generally used information, which almost
369 * worth adding to a subdiv itself, with possible cache of the value. */
371{
372 int num_ptex_faces = 0;
373 const OffsetIndices faces = mesh.faces();
374 for (int face_index = 0; face_index < mesh.faces_num; face_index++) {
375 num_ptex_faces += (faces[face_index].size() == 4) ? 1 : faces[face_index].size();
376 }
377 return num_ptex_faces;
378}
379
380static void displacement_data_init_mapping(Displacement &displacement, const Mesh &mesh)
381{
383 displacement.user_data);
384 const OffsetIndices faces = mesh.faces();
385 const int num_ptex_faces = count_num_ptex_faces(mesh);
386 data.ptex_face_corner.reinitialize(num_ptex_faces);
387 /* Fill in offsets. */
388 int ptex_face_index = 0;
389 MutableSpan<PolyCornerIndex> ptex_face_corner = data.ptex_face_corner.as_mutable_span();
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 const 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;
423}
424
426{
427 displacement->initialize = initialize;
428 displacement->eval_displacement = eval_displacement;
429 displacement->free = free_displacement;
430}
431
433 const Mesh *mesh,
434 const MultiresModifierData *mmd)
435{
436 /* Make sure we don't have previously assigned displacement. */
438 /* It is possible to have mesh without CD_MDISPS layer. Happens when using
439 * dynamic topology. */
440 if (!CustomData_has_layer(&mesh->corner_data, CD_MDISPS)) {
441 return;
442 }
443 /* Allocate all required memory. */
444 Displacement *displacement = MEM_callocN<Displacement>("multires displacement");
445 displacement->user_data = MEM_new<MultiresDisplacementData>("multires displacement data");
446 displacement_init_data(*displacement, *subdiv, *mesh, *mmd);
447 displacement_init_functions(displacement);
448 /* Finish. */
449 subdiv->displacement_evaluator = displacement;
450}
451
452} // 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(blender::float3x3 &tangent_matrix, const blender::float3 &dPdu, const blender::float3 &dPdv, int corner)
void multiresModifier_ensure_external_read(Mesh *mesh, const MultiresModifierData *mmd)
Definition multires.cc:798
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
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
#define roundf(x)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static char faces[256]
static void average_read_displacement_tangent(const MultiresDisplacementData &data, const MDisps &other_displacement_grid, const float grid_u, const float grid_v, float3 &r_tangent_D)
static void average_with_next(const Displacement &displacement, const int ptex_face_index, const int corner, const float grid_u, const float, float3 &r_D)
BLI_INLINE AverageWith read_displacement_grid(const MDisps &displacement_grid, const int grid_size, const float grid_u, const float grid_v, float3 &r_tangent_D)
static void average_with_other(const Displacement &displacement, const int ptex_face_index, const int corner, const float grid_u, const float grid_v, const int corner_delta, float3 &r_D)
static void average_with_prev(const Displacement &displacement, const int ptex_face_index, const int corner, const float, const float grid_v, float3 &r_D)
BLI_INLINE void rotate_grid_to_quad(int corner, float grid_u, float grid_v, float *r_quad_u, float *r_quad_v)
void displacement_attach_from_multires(Subdiv *subdiv, const Mesh *mesh, const MultiresModifierData *mmd)
static void average_read_displacement_object(const MultiresDisplacementData &data, const MDisps &displacement_grid, const float grid_u, const float grid_v, const int ptex_face_index, const int corner_index, float3 &r_D)
static int count_num_ptex_faces(const Mesh &mesh)
BLI_INLINE void grid_uv_to_ptex_face_uv(float grid_u, float grid_v, float *r_ptex_u, float *r_ptex_v)
static void displacement_data_init_mapping(Displacement &displacement, const Mesh &mesh)
static void average_get_other_ptex_and_corner(const 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)
void displacement_detach(Subdiv *subdiv)
static void average_displacement(const Displacement &displacement, const AverageWith average_with, const int ptex_face_index, const int corner, const float grid_u, const float grid_v, float3 &r_D)
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)
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, float3x3 &r_tangent_matrix)
BLI_INLINE int rotate_quad_to_corner(float quad_u, float quad_v, float *r_corner_u, float *r_corner_v)
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)
static void displacement_init_functions(Displacement *displacement)
void eval_displacement(Subdiv *subdiv, int ptex_face_index, float u, float v, const float3 &dPdu, const float3 &dPdv, float3 &r_D)
static void initialize(Displacement *displacement)
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 int displacement_get_grid_and_coord(const Displacement &displacement, const int ptex_face_index, const float u, const float v, const MDisps **r_displacement_grid, float &r_grid_u, float &r_grid_v)
static void free_displacement(Displacement *displacement)
static void displacement_init_data(Displacement &displacement, Subdiv &subdiv, const Mesh &mesh, const MultiresModifierData &mmd)
static void average_with_all(const Displacement &displacement, const int ptex_face_index, const int corner, const float, const float, float3 &r_D)
static int displacement_get_face_corner(const MultiresDisplacementData &data, const int ptex_face_index, const float u, const float v)
static const MDisps * displacement_get_other_grid(const Displacement &displacement, const int ptex_face_index, const int corner, const int corner_delta)
Span< int > face_ptex_offset_get(Subdiv *subdiv)
Definition subdiv.cc:214
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
float(* disps)[3]
void(* eval_displacement)(Displacement *displacement, int ptex_face_index, float u, float v, const float3 &dPdu, const float3 &dPdv, float3 &r_D)
void(* free)(Displacement *displacement)
void(* initialize)(Displacement *displacement)