Blender  V2.93
subdiv_displacement_multires.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2018 by Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include <math.h>
25 
26 #include "BKE_subdiv.h"
27 
28 #include "DNA_mesh_types.h"
29 #include "DNA_meshdata_types.h"
30 #include "DNA_modifier_types.h"
31 #include "DNA_object_types.h"
32 
33 #include "BLI_math_vector.h"
34 #include "BLI_utildefines.h"
35 
36 #include "BKE_customdata.h"
37 #include "BKE_multires.h"
38 #include "BKE_subdiv_eval.h"
39 
40 #include "MEM_guardedalloc.h"
41 
42 typedef struct PolyCornerIndex {
43  int poly_index;
44  int corner;
46 
47 typedef struct MultiresDisplacementData {
49  int grid_size;
50  /* Mesh is used to read external displacement. */
53  const MPoly *mpoly;
54  const MDisps *mdisps;
55  /* Indexed by ptex face index, contains polygon/corner which corresponds
56  * to it.
57  *
58  * NOTE: For quad polygon this is an index of first corner only, since
59  * there we only have one ptex. */
61  /* Indexed by coarse face index, returns first ptex face index corresponding
62  * to that coarse face. */
64  /* Sanity check, is used in debug builds.
65  * Controls that initialize() was called prior to eval_displacement(). */
68 
69 /* Denotes which grid to use to average value of the displacement read from the
70  * grid which corresponds to the ptex face. */
71 typedef enum eAverageWith {
77 
79  const int ptex_face_index,
80  const float u,
81  const float v,
82  const MDisps **r_displacement_grid,
83  float *grid_u,
84  float *grid_v)
85 {
86  MultiresDisplacementData *data = displacement->user_data;
87  const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index];
88  const MPoly *poly = &data->mpoly[poly_corner->poly_index];
89  const int start_grid_index = poly->loopstart + poly_corner->corner;
90  int corner = 0;
91  if (poly->totloop == 4) {
92  float corner_u, corner_v;
93  corner = BKE_subdiv_rotate_quad_to_corner(u, v, &corner_u, &corner_v);
94  *r_displacement_grid = &data->mdisps[start_grid_index + corner];
95  BKE_subdiv_ptex_face_uv_to_grid_uv(corner_u, corner_v, grid_u, grid_v);
96  }
97  else {
98  *r_displacement_grid = &data->mdisps[start_grid_index];
99  BKE_subdiv_ptex_face_uv_to_grid_uv(u, v, grid_u, grid_v);
100  }
101  return corner;
102 }
103 
105  const int ptex_face_index,
106  const int corner,
107  const int corner_delta)
108 {
109  MultiresDisplacementData *data = displacement->user_data;
110  const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index];
111  const MPoly *poly = &data->mpoly[poly_corner->poly_index];
112  const int effective_corner = (poly->totloop == 4) ? corner : poly_corner->corner;
113  const int next_corner = (effective_corner + corner_delta + poly->totloop) % poly->totloop;
114  return &data->mdisps[poly->loopstart + next_corner];
115 }
116 
118  const int grid_size,
119  const float grid_u,
120  const float grid_v,
121  float r_tangent_D[3])
122 {
123  if (displacement_grid->disps == NULL) {
124  zero_v3(r_tangent_D);
125  return AVERAGE_WITH_NONE;
126  }
127  const int x = roundf(grid_u * (grid_size - 1));
128  const int y = roundf(grid_v * (grid_size - 1));
129  copy_v3_v3(r_tangent_D, displacement_grid->disps[y * grid_size + x]);
130  if (x == 0 && y == 0) {
131  return AVERAGE_WITH_ALL;
132  }
133  if (x == 0) {
134  return AVERAGE_WITH_PREV;
135  }
136  if (y == 0) {
137  return AVERAGE_WITH_NEXT;
138  }
139  return AVERAGE_WITH_NONE;
140 }
141 
143  const int corner,
144  const float grid_u,
145  const float grid_v,
146  float *r_ptex_face_u,
147  float *r_ptex_face_v)
148 {
149  if (poly->totloop == 4) {
150  BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, r_ptex_face_u, r_ptex_face_v);
151  }
152  else {
153  BKE_subdiv_grid_uv_to_ptex_face_uv(grid_u, grid_v, r_ptex_face_u, r_ptex_face_v);
154  }
155 }
156 
158  const MPoly *poly,
159  const int ptex_face_index,
160  const int corner,
161  const float u,
162  const float v,
163  float r_tangent_matrix[3][3])
164 {
165  const bool is_quad = (poly->totloop == 4);
166  const int quad_corner = is_quad ? corner : 0;
167  float dummy_P[3], dPdu[3], dPdv[3];
168  BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
169  BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, quad_corner);
170 }
171 
173  const MDisps *other_displacement_grid,
174  const float grid_u,
175  const float grid_v,
176  float r_tangent_D[3])
177 {
178  read_displacement_grid(other_displacement_grid, data->grid_size, grid_u, grid_v, r_tangent_D);
179 }
180 
182  const MDisps *displacement_grid,
183  const float grid_u,
184  const float grid_v,
185  const int ptex_face_index,
186  const int corner_index,
187  float r_D[3])
188 {
189  const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index];
190  const MPoly *poly = &data->mpoly[poly_corner->poly_index];
191  /* Get (u, v) coordinate within the other ptex face which corresponds to
192  * the grid coordinates. */
193  float u, v;
194  average_convert_grid_coord_to_ptex(poly, corner_index, grid_u, grid_v, &u, &v);
195  /* Construct tangent matrix which corresponds to partial derivatives
196  * calculated for the other ptex face. */
197  float tangent_matrix[3][3];
199  data->subdiv, poly, ptex_face_index, corner_index, u, v, tangent_matrix);
200  /* Read displacement from other grid in a tangent space. */
201  float tangent_D[3];
202  average_read_displacement_tangent(data, displacement_grid, grid_u, grid_v, tangent_D);
203  /* Convert displacement to object space. */
204  mul_v3_m3v3(r_D, tangent_matrix, tangent_D);
205 }
206 
208  const int ptex_face_index,
209  const int corner,
210  const int corner_delta,
211  int *r_other_ptex_face_index,
212  int *r_other_corner_index)
213 {
214  const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index];
215  const MPoly *poly = &data->mpoly[poly_corner->poly_index];
216  const int num_corners = poly->totloop;
217  const bool is_quad = (num_corners == 4);
218  const int poly_index = poly - data->mpoly;
219  const int start_ptex_face_index = data->face_ptex_offset[poly_index];
220  *r_other_corner_index = (corner + corner_delta + num_corners) % num_corners;
221  *r_other_ptex_face_index = is_quad ? start_ptex_face_index :
222  start_ptex_face_index + *r_other_corner_index;
223 }
224 
225 /* NOTE: Grid coordinates are relatiev to the other grid already. */
226 static void average_with_other(SubdivDisplacement *displacement,
227  const int ptex_face_index,
228  const int corner,
229  const float grid_u,
230  const float grid_v,
231  const int corner_delta,
232  float r_D[3])
233 {
234  MultiresDisplacementData *data = displacement->user_data;
235  const MDisps *other_displacement_grid = displacement_get_other_grid(
236  displacement, ptex_face_index, corner, corner_delta);
237  int other_ptex_face_index, other_corner_index;
239  data, ptex_face_index, corner, corner_delta, &other_ptex_face_index, &other_corner_index);
240  /* Get displacement in object space. */
241  float other_D[3];
243  other_displacement_grid,
244  grid_u,
245  grid_v,
246  other_ptex_face_index,
247  other_corner_index,
248  other_D);
249  /* Average result with the other displacement vector. */
250  add_v3_v3(r_D, other_D);
251  mul_v3_fl(r_D, 0.5f);
252 }
253 
254 static void average_with_all(SubdivDisplacement *displacement,
255  const int ptex_face_index,
256  const int corner,
257  const float UNUSED(grid_u),
258  const float UNUSED(grid_v),
259  float r_D[3])
260 {
261  MultiresDisplacementData *data = displacement->user_data;
262  const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index];
263  const MPoly *poly = &data->mpoly[poly_corner->poly_index];
264  const int num_corners = poly->totloop;
265  for (int corner_delta = 1; corner_delta < num_corners; corner_delta++) {
266  average_with_other(displacement, ptex_face_index, corner, 0.0f, 0.0f, corner_delta, r_D);
267  }
268 }
269 
270 static void average_with_next(SubdivDisplacement *displacement,
271  const int ptex_face_index,
272  const int corner,
273  const float grid_u,
274  const float UNUSED(grid_v),
275  float r_D[3])
276 {
277  average_with_other(displacement, ptex_face_index, corner, 0.0f, grid_u, 1, r_D);
278 }
279 
280 static void average_with_prev(SubdivDisplacement *displacement,
281  const int ptex_face_index,
282  const int corner,
283  const float UNUSED(grid_u),
284  const float grid_v,
285  float r_D[3])
286 {
287  average_with_other(displacement, ptex_face_index, corner, grid_v, 0.0f, -1, r_D);
288 }
289 
290 static void average_displacement(SubdivDisplacement *displacement,
291  eAverageWith average_with,
292  const int ptex_face_index,
293  const int corner,
294  const float grid_u,
295  const float grid_v,
296  float r_D[3])
297 {
298  switch (average_with) {
299  case AVERAGE_WITH_ALL:
300  average_with_all(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
301  break;
302  case AVERAGE_WITH_PREV:
303  average_with_prev(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
304  break;
305  case AVERAGE_WITH_NEXT:
306  average_with_next(displacement, ptex_face_index, corner, grid_u, grid_v, r_D);
307  break;
308  case AVERAGE_WITH_NONE:
309  break;
310  }
311 }
312 
314  const int ptex_face_index,
315  const float u,
316  const float v)
317 {
318  const PolyCornerIndex *poly_corner = &data->ptex_poly_corner[ptex_face_index];
319  const MPoly *poly = &data->mpoly[poly_corner->poly_index];
320  const int num_corners = poly->totloop;
321  const bool is_quad = (num_corners == 4);
322  if (is_quad) {
323  float dummy_corner_u, dummy_corner_v;
324  return BKE_subdiv_rotate_quad_to_corner(u, v, &dummy_corner_u, &dummy_corner_v);
325  }
326 
327  return poly_corner->corner;
328 }
329 
330 static void initialize(SubdivDisplacement *displacement)
331 {
332  MultiresDisplacementData *data = displacement->user_data;
334  data->is_initialized = true;
335 }
336 
337 static void eval_displacement(SubdivDisplacement *displacement,
338  const int ptex_face_index,
339  const float u,
340  const float v,
341  const float dPdu[3],
342  const float dPdv[3],
343  float r_D[3])
344 {
345  MultiresDisplacementData *data = displacement->user_data;
346  BLI_assert(data->is_initialized);
347  const int grid_size = data->grid_size;
348  /* Get displacement in tangent space. */
349  const MDisps *displacement_grid;
350  float grid_u, grid_v;
351  const int corner_of_quad = displacement_get_grid_and_coord(
352  displacement, ptex_face_index, u, v, &displacement_grid, &grid_u, &grid_v);
353  /* Read displacement from the current displacement grid and see if any
354  * averaging is needed. */
355  float tangent_D[3];
356  eAverageWith average_with = read_displacement_grid(
357  displacement_grid, grid_size, grid_u, grid_v, tangent_D);
358  /* Convert it to the object space. */
359  float tangent_matrix[3][3];
360  BKE_multires_construct_tangent_matrix(tangent_matrix, dPdu, dPdv, corner_of_quad);
361  mul_v3_m3v3(r_D, tangent_matrix, tangent_D);
362  /* For the boundary points of grid average two (or all) neighbor grids. */
363  const int corner = displacement_get_face_corner(data, ptex_face_index, u, v);
364  average_displacement(displacement, average_with, ptex_face_index, corner, grid_u, grid_v, r_D);
365 }
366 
367 static void free_displacement(SubdivDisplacement *displacement)
368 {
369  MultiresDisplacementData *data = displacement->user_data;
370  MEM_freeN(data->ptex_poly_corner);
371  MEM_freeN(data);
372 }
373 
374 /* TODO(sergey): This seems to be generally used information, which almost
375  * worth adding to a subdiv itself, with possible cache of the value. */
376 static int count_num_ptex_faces(const Mesh *mesh)
377 {
378  int num_ptex_faces = 0;
379  const MPoly *mpoly = mesh->mpoly;
380  for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) {
381  const MPoly *poly = &mpoly[poly_index];
382  num_ptex_faces += (poly->totloop == 4) ? 1 : poly->totloop;
383  }
384  return num_ptex_faces;
385 }
386 
387 static void displacement_data_init_mapping(SubdivDisplacement *displacement, const Mesh *mesh)
388 {
389  MultiresDisplacementData *data = displacement->user_data;
390  const MPoly *mpoly = mesh->mpoly;
391  const int num_ptex_faces = count_num_ptex_faces(mesh);
392  /* Allocate memory. */
393  data->ptex_poly_corner = MEM_malloc_arrayN(
394  num_ptex_faces, sizeof(*data->ptex_poly_corner), "ptex poly corner");
395  /* Fill in offsets. */
396  int ptex_face_index = 0;
397  PolyCornerIndex *ptex_poly_corner = data->ptex_poly_corner;
398  for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) {
399  const MPoly *poly = &mpoly[poly_index];
400  if (poly->totloop == 4) {
401  ptex_poly_corner[ptex_face_index].poly_index = poly_index;
402  ptex_poly_corner[ptex_face_index].corner = 0;
403  ptex_face_index++;
404  }
405  else {
406  for (int corner = 0; corner < poly->totloop; corner++) {
407  ptex_poly_corner[ptex_face_index].poly_index = poly_index;
408  ptex_poly_corner[ptex_face_index].corner = corner;
409  ptex_face_index++;
410  }
411  }
412  }
413 }
414 
415 static void displacement_init_data(SubdivDisplacement *displacement,
416  Subdiv *subdiv,
417  Mesh *mesh,
418  const MultiresModifierData *mmd)
419 {
420  MultiresDisplacementData *data = displacement->user_data;
421  data->subdiv = subdiv;
422  data->grid_size = BKE_subdiv_grid_size_from_level(mmd->totlvl);
423  data->mesh = mesh;
424  data->mmd = mmd;
425  data->mpoly = mesh->mpoly;
427  data->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv);
428  data->is_initialized = false;
429  displacement_data_init_mapping(displacement, mesh);
430 }
431 
433 {
434  displacement->initialize = initialize;
435  displacement->eval_displacement = eval_displacement;
436  displacement->free = free_displacement;
437 }
438 
440  Mesh *mesh,
441  const MultiresModifierData *mmd)
442 {
443  /* Make sure we don't have previously assigned displacement. */
445  /* It is possible to have mesh without CD_MDISPS layer. Happens when using
446  * dynamic topology. */
448  return;
449  }
450  /* Allocate all required memory. */
451  SubdivDisplacement *displacement = MEM_callocN(sizeof(SubdivDisplacement),
452  "multires displacement");
453  displacement->user_data = MEM_callocN(sizeof(MultiresDisplacementData),
454  "multires displacement data");
455  displacement_init_data(displacement, subdiv, mesh, mmd);
456  displacement_init_functions(displacement);
457  /* Finish. */
458  subdiv->displacement_evaluator = displacement;
459 }
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_has_layer(const struct CustomData *data, int type)
void * CustomData_get_layer(const struct CustomData *data, int type)
BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3], const float dPdu[3], const float dPdv[3], const int corner)
void multiresModifier_ensure_external_read(struct Mesh *mesh, const struct MultiresModifierData *mmd)
BLI_INLINE void BKE_subdiv_rotate_grid_to_quad(const int corner, const float grid_u, const float grid_v, float *r_quad_u, float *r_quad_v)
Definition: subdiv_inline.h:84
BLI_INLINE int BKE_subdiv_grid_size_from_level(const int level)
Definition: subdiv_inline.h:49
BLI_INLINE int BKE_subdiv_rotate_quad_to_corner(const float quad_u, const float quad_v, float *r_corner_u, float *r_corner_v)
Definition: subdiv_inline.h:54
int * BKE_subdiv_face_ptex_offset_get(Subdiv *subdiv)
Definition: subdiv.c:206
void BKE_subdiv_displacement_detach(Subdiv *subdiv)
BLI_INLINE void BKE_subdiv_grid_uv_to_ptex_face_uv(const float grid_u, const float grid_v, float *r_ptex_u, float *r_ptex_v)
Definition: subdiv_inline.h:40
BLI_INLINE void BKE_subdiv_ptex_face_uv_to_grid_uv(const float ptex_u, const float ptex_v, float *r_grid_u, float *r_grid_v)
Definition: subdiv_inline.h:31
void BKE_subdiv_eval_limit_point_and_derivatives(struct Subdiv *subdiv, const int ptex_face_index, const float u, const float v, float r_P[3], float r_dPdu[3], float r_dPdv[3])
Definition: subdiv_eval.c:177
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define BLI_INLINE
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
Definition: math_matrix.c:901
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])
#define UNUSED(x)
Object is a sort of wrapper for general info.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
float(* disps)[3]
struct CustomData pdata ldata
int totpoly
struct MPoly * mpoly
const MultiresModifierData * mmd
void(* eval_displacement)(struct SubdivDisplacement *displacement, const int ptex_face_index, const float u, const float v, const float dPdu[3], const float dPdv[3], float r_D[3])
Definition: BKE_subdiv.h:153
void(* initialize)(struct SubdivDisplacement *displacement)
Definition: BKE_subdiv.h:141
void(* free)(struct SubdivDisplacement *displacement)
Definition: BKE_subdiv.h:162
struct SubdivDisplacement * displacement_evaluator
Definition: BKE_subdiv.h:184
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])
void BKE_subdiv_displacement_attach_from_multires(Subdiv *subdiv, Mesh *mesh, const MultiresModifierData *mmd)
static void displacement_data_init_mapping(SubdivDisplacement *displacement, const Mesh *mesh)
static const MDisps * displacement_get_other_grid(SubdivDisplacement *displacement, const int ptex_face_index, const int corner, const int corner_delta)
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])
static int displacement_get_grid_and_coord(SubdivDisplacement *displacement, const int ptex_face_index, const float u, const float v, const MDisps **r_displacement_grid, float *grid_u, float *grid_v)
static void average_with_other(SubdivDisplacement *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 void average_displacement(SubdivDisplacement *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_functions(SubdivDisplacement *displacement)
static void average_with_next(SubdivDisplacement *displacement, const int ptex_face_index, const int corner, const float grid_u, const float UNUSED(grid_v), float r_D[3])
static int count_num_ptex_faces(const Mesh *mesh)
static void free_displacement(SubdivDisplacement *displacement)
static void average_construct_tangent_matrix(Subdiv *subdiv, const MPoly *poly, const int ptex_face_index, const int corner, const float u, const float v, float r_tangent_matrix[3][3])
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)
static void average_with_all(SubdivDisplacement *displacement, const int ptex_face_index, const int corner, const float UNUSED(grid_u), const float UNUSED(grid_v), float r_D[3])
struct MultiresDisplacementData MultiresDisplacementData
static int displacement_get_face_corner(MultiresDisplacementData *data, const int ptex_face_index, const float u, const float v)
static void initialize(SubdivDisplacement *displacement)
static void average_convert_grid_coord_to_ptex(const MPoly *poly, const int corner, const float grid_u, const float grid_v, float *r_ptex_face_u, float *r_ptex_face_v)
struct PolyCornerIndex PolyCornerIndex
static void average_with_prev(SubdivDisplacement *displacement, const int ptex_face_index, const int corner, const float UNUSED(grid_u), const float grid_v, float r_D[3])
static void displacement_init_data(SubdivDisplacement *displacement, Subdiv *subdiv, Mesh *mesh, const MultiresModifierData *mmd)
static void eval_displacement(SubdivDisplacement *displacement, const int ptex_face_index, const float u, const float v, const float dPdu[3], const float dPdv[3], float r_D[3])
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])