diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_subdiv.h | 73 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv.c | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_displacement.c | 46 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_displacement_multires.c | 440 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_eval.c | 66 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_mesh.c | 291 |
7 files changed, 895 insertions, 25 deletions
diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h index c4628cb9da7..887eff6e7dc 100644 --- a/source/blender/blenkernel/BKE_subdiv.h +++ b/source/blender/blenkernel/BKE_subdiv.h @@ -29,9 +29,12 @@ #include "BLI_sys_types.h" struct Mesh; +struct MultiresModifierData; +struct Object; struct OpenSubdiv_Converter; struct OpenSubdiv_Evaluator; struct OpenSubdiv_TopologyRefiner; +struct Subdiv; /** \file BKE_subdiv.h * \ingroup bke @@ -92,22 +95,47 @@ typedef struct SubdivStats { double begin_timestamp_[NUM_SUBDIV_STATS_VALUES]; } SubdivStats; +/* Functor which evaluates dispalcement at a given (u, v) of given ptex face. */ +typedef struct SubdivDisplacement { + /* Return displacement which is to be added to the original coordinate. + * + * NOTE: This function is supposed to return "continuous" displacement for + * each pf PTex faces created for special (non-quad) polygon. This means, + * if displacement is stored on per-corner manner (like MDisps for multires) + * this is up the displacement implementation to average boundaries of the + * displacement grids if needed. + * + * Averaging of displacement for vertices created for over coarse vertices + * and edges is done by subdiv code. + */ + 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]); + + /* Free the data, not the evaluator itself. */ + void (*free)(struct SubdivDisplacement *displacement); + + void *user_data; +} SubdivDisplacement; + typedef struct Subdiv { /* Settings this subdivision surface is created for. * * It is read-only after assignment in BKE_subdiv_new_from_FOO(). */ SubdivSettings settings; - /* Topology refiner includes all the glue logic to feed Blender side * topology to OpenSubdiv. It can be shared by both evaluator and GL mesh * drawer. */ struct OpenSubdiv_TopologyRefiner *topology_refiner; - /* CPU side evaluator. */ struct OpenSubdiv_Evaluator *evaluator; - + /* Optional displacement evaluator. */ + struct SubdivDisplacement *displacement_evaluator; + /* Statistics for debugging. */ SubdivStats stats; } Subdiv; @@ -148,29 +176,49 @@ void BKE_subdiv_eval_limit_point( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3]); + float r_P[3]); void BKE_subdiv_eval_limit_point_and_derivatives( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3], float dPdu[3], float dPdv[3]); + float r_P[3], float r_dPdu[3], float r_dPdv[3]); void BKE_subdiv_eval_limit_point_and_normal( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3], float N[3]); + float r_P[3], float r_N[3]); void BKE_subdiv_eval_limit_point_and_short_normal( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3], short N[3]); + float r_P[3], short r_N[3]); void BKE_subdiv_eval_face_varying( Subdiv *subdiv, const int face_varying_channel, const int ptex_face_index, const float u, const float v, - float varying[2]); + float r_varying[2]); + +/* NOTE: Expects derivatives to be correct. + * + * TODO(sergey): This is currently used together with + * BKE_subdiv_eval_final_point() which cas easily evaluate derivatives. + * Would be nice to have dispalcement evaluation function which does not require + * knowing derivatives ahead of a time. + */ +void BKE_subdiv_eval_displacement( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + const float dPdu[3], const float dPdv[3], + float r_D[3]); + +void BKE_subdiv_eval_final_point( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float r_P[3]); /* Patch queries at given resolution. * @@ -220,4 +268,13 @@ struct Mesh *BKE_subdiv_to_mesh( const SubdivToMeshSettings *settings, const struct Mesh *coarse_mesh); +/* ============================ DISPLACEMENT API ============================ */ + +void BKE_subdiv_displacement_attach_from_multires( + Subdiv *subdiv, + const struct Object *object, + const struct MultiresModifierData *mmd); + +void BKE_subdiv_displacement_detach(Subdiv *subdiv); + #endif /* __BKE_SUBDIV_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 646665cdec2..aeb4830127d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -194,6 +194,8 @@ set(SRC intern/subdiv.c intern/subdiv_converter.c intern/subdiv_converter_mesh.c + intern/subdiv_displacement.c + intern/subdiv_displacement_multires.c intern/subdiv_eval.c intern/subdiv_mesh.c intern/subdiv_stats.c diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c index 4f630e8c38c..f847d62018a 100644 --- a/source/blender/blenkernel/intern/subdiv.c +++ b/source/blender/blenkernel/intern/subdiv.c @@ -90,6 +90,7 @@ Subdiv *BKE_subdiv_new_from_converter(const SubdivSettings *settings, subdiv->settings = *settings; subdiv->topology_refiner = osd_topology_refiner; subdiv->evaluator = NULL; + subdiv->displacement_evaluator = NULL; BKE_subdiv_stats_end(&stats, SUBDIV_STATS_TOPOLOGY_REFINER_CREATION_TIME); subdiv->stats = stats; return subdiv; @@ -116,5 +117,6 @@ void BKE_subdiv_free(Subdiv *subdiv) if (subdiv->topology_refiner != NULL) { openSubdiv_deleteTopologyRefiner(subdiv->topology_refiner); } + BKE_subdiv_displacement_detach(subdiv); MEM_freeN(subdiv); } diff --git a/source/blender/blenkernel/intern/subdiv_displacement.c b/source/blender/blenkernel/intern/subdiv_displacement.c new file mode 100644 index 00000000000..a6af6f45e59 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_displacement.c @@ -0,0 +1,46 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/subdiv_displacement.c + * \ingroup bke + */ + +#include "BKE_subdiv.h" + +#include "BLI_utildefines.h" + +#include "MEM_guardedalloc.h" + +void BKE_subdiv_displacement_detach(Subdiv *subdiv) +{ + if (subdiv->displacement_evaluator == NULL) { + return; + } + if (subdiv->displacement_evaluator->free != NULL) { + subdiv->displacement_evaluator->free(subdiv->displacement_evaluator); + } + MEM_freeN(subdiv->displacement_evaluator); + subdiv->displacement_evaluator = NULL; +} diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.c b/source/blender/blenkernel/intern/subdiv_displacement_multires.c new file mode 100644 index 00000000000..ce42fa16438 --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.c @@ -0,0 +1,440 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Sergey Sharybin. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/subdiv_displacement.c + * \ingroup bke + */ + +#include "BKE_subdiv.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" + +#include "BKE_customdata.h" + +#include "MEM_guardedalloc.h" + +typedef struct PolyCornerIndex { + int poly_index; + int corner; +} PolyCornerIndex; + +typedef struct MultiresDisplacementData { + int grid_size; + const MPoly *mpoly; + const MDisps *mdisps; + /* Indexed by ptex face index, contains polygon/corner which corresponds + * to it. + * + * NOTE: For quad polygon this is an index of first corner only, since + * there we only have one ptex. + */ + PolyCornerIndex *ptex_poly_corner; +} MultiresDisplacementData; + +/* Denotes which grid to use to average value of the displacement read from the + * grid which corresponds to the ptex face. + */ +typedef enum eAverageWith { + AVERAGE_WITH_NONE, + AVERAGE_WITH_ALL, + AVERAGE_WITH_PREV, + AVERAGE_WITH_NEXT, +} eAverageWith; + +/* Coordinates within grid has different convention from PTex coordinates. + * This function converts the latter ones to former. + */ +BLI_INLINE void ptex_uv_to_grid_uv(const float ptex_u, const float ptex_v, + float *r_grid_u, float *r_grid_v) +{ + *r_grid_u = 1.0f - ptex_v; + *r_grid_v = 1.0f - ptex_u; +} + +/* Simplified version of mdisp_rot_face_to_crn, only handles quad and + * works in normalized coordinates. + * + * NOTE: Output coordinates are in ptex coordinates. + */ +BLI_INLINE int rotate_quad_to_corner(const float u, const float v, + float *r_u, float *r_v) +{ + int corner; + if (u <= 0.5f && v <= 0.5f) { + corner = 0; + *r_u = 2.0f * u; + *r_v = 2.0f * v; + } + else if (u > 0.5f && v <= 0.5f) { + corner = 1; + *r_u = 2.0f * v; + *r_v = 2.0f * (1.0f - u); + } + else if (u > 0.5f && v > 0.5f) { + corner = 2; + *r_u = 2.0f * (1.0f - u); + *r_v = 2.0f * (1.0f - v); + } + else if (u <= 0.5f && v >= 0.5f) { + corner = 3; + *r_u = 2.0f * (1.0f - v); + *r_v = 2.0f * u; + } + else { + BLI_assert(!"Unexpected corner configuration"); + } + return corner; +} + +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) +{ + MultiresDisplacementData *data = displacement->user_data; + const PolyCornerIndex *poly_corner = + &data->ptex_poly_corner[ptex_face_index]; + const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const int start_grid_index = poly->loopstart + poly_corner->corner; + int corner = 0; + if (poly->totloop == 4) { + float corner_u, corner_v; + corner = rotate_quad_to_corner(u, v, &corner_u, &corner_v); + *r_displacement_grid = &data->mdisps[start_grid_index + corner]; + ptex_uv_to_grid_uv(corner_u, corner_v, grid_u, grid_v); + } + else { + *r_displacement_grid = &data->mdisps[start_grid_index]; + ptex_uv_to_grid_uv(u, v, grid_u, grid_v); + } + return corner; +} + +static const MDisps *displacement_get_next_grid( + SubdivDisplacement *displacement, + const int ptex_face_index, const int corner) +{ + MultiresDisplacementData *data = displacement->user_data; + const PolyCornerIndex *poly_corner = + &data->ptex_poly_corner[ptex_face_index]; + const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const int effective_corner = (poly->totloop == 4) ? corner + : poly_corner->corner; + const int next_corner = (effective_corner + 1) % poly->totloop; + return &data->mdisps[poly->loopstart + next_corner]; +} + +static const MDisps *displacement_get_prev_grid( + SubdivDisplacement *displacement, + const int ptex_face_index, const int corner) +{ + MultiresDisplacementData *data = displacement->user_data; + const PolyCornerIndex *poly_corner = + &data->ptex_poly_corner[ptex_face_index]; + const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + const int effective_corner = (poly->totloop == 4) ? corner + : poly_corner->corner; + const int prev_corner = + (effective_corner - 1 + poly->totloop) % poly->totloop; + return &data->mdisps[poly->loopstart + prev_corner]; +} + +/* NOTE: Derivatives are in ptex face space. */ +BLI_INLINE void construct_tangent_matrix(float tangent_matrix[3][3], + const float dPdu[3], + const float dPdv[3], + const int corner) +{ + if (corner == 0) { + copy_v3_v3(tangent_matrix[0], dPdv); + copy_v3_v3(tangent_matrix[1], dPdu); + mul_v3_fl(tangent_matrix[0], -1.0f); + mul_v3_fl(tangent_matrix[1], -1.0f); + } + else if (corner == 1) { + copy_v3_v3(tangent_matrix[0], dPdu); + copy_v3_v3(tangent_matrix[1], dPdv); + mul_v3_fl(tangent_matrix[1], -1.0f); + } + else if (corner == 2) { + copy_v3_v3(tangent_matrix[0], dPdv); + copy_v3_v3(tangent_matrix[1], dPdu); + } + else if (corner == 3) { + copy_v3_v3(tangent_matrix[0], dPdu); + copy_v3_v3(tangent_matrix[1], dPdv); + mul_v3_fl(tangent_matrix[0], -1.0f); + } + cross_v3_v3v3(tangent_matrix[2], dPdu, dPdv); + normalize_v3(tangent_matrix[0]); + normalize_v3(tangent_matrix[1]); + normalize_v3(tangent_matrix[2]); +} + +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]) +{ + if (displacement_grid->disps == NULL) { + zero_v3(r_tangent_D); + return AVERAGE_WITH_NONE; + } + const int x = (grid_u * (grid_size - 1) + 0.5f); + const int y = (grid_v * (grid_size - 1) + 0.5f); + copy_v3_v3(r_tangent_D, displacement_grid->disps[y * grid_size + x]); + if (x == 0 && y == 0) { + return AVERAGE_WITH_ALL; + } + else if (x == 0) { + return AVERAGE_WITH_PREV; + } + else if (y == 0) { + return AVERAGE_WITH_NEXT; + } + return AVERAGE_WITH_NONE; +} + +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_tangent_D[3]) +{ + MultiresDisplacementData *data = displacement->user_data; + const PolyCornerIndex *poly_corner = + &data->ptex_poly_corner[ptex_face_index]; + const MPoly *poly = &data->mpoly[poly_corner->poly_index]; + for (int current_corner = 0; + current_corner < poly->totloop; + current_corner++) + { + if (current_corner == corner) { + continue; + } + const MDisps *displacement_grid = + &data->mdisps[poly->loopstart + current_corner]; + const float *current_tangent_D = displacement_grid->disps[0]; + r_tangent_D[2] += current_tangent_D[2]; + } + r_tangent_D[2] /= (float)poly->totloop; +} + +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_tangent_D[3]) +{ + MultiresDisplacementData *data = displacement->user_data; + const int grid_size = data->grid_size; + const MDisps *next_displacement_grid = displacement_get_next_grid( + displacement, ptex_face_index, corner); + float next_tangent_D[3]; + read_displacement_grid(next_displacement_grid, grid_size, + 0.0f, grid_u, + next_tangent_D); + r_tangent_D[2] += next_tangent_D[2]; + r_tangent_D[2] *= 0.5f; +} + +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_tangent_D[3]) +{ + MultiresDisplacementData *data = displacement->user_data; + const int grid_size = data->grid_size; + const MDisps *prev_displacement_grid = displacement_get_prev_grid( + displacement, ptex_face_index, corner); + float prev_tangent_D[3]; + read_displacement_grid(prev_displacement_grid, grid_size, + grid_v, 0.0f, + prev_tangent_D); + r_tangent_D[2] += prev_tangent_D[2]; + r_tangent_D[2] *= 0.5f; +} + +static void average_displacement(SubdivDisplacement *displacement, + const int ptex_face_index, const int corner, + eAverageWith average_with, + const float grid_u, const float grid_v, + float r_tangent_D[3]) +{ + switch (average_with) { + case AVERAGE_WITH_ALL: + average_with_all(displacement, + ptex_face_index, corner, + grid_u, grid_v, + r_tangent_D); + break; + case AVERAGE_WITH_PREV: + average_with_prev(displacement, + ptex_face_index, corner, + grid_u, grid_v, + r_tangent_D); + break; + case AVERAGE_WITH_NEXT: + average_with_next(displacement, + ptex_face_index, corner, + grid_u, grid_v, + r_tangent_D); + break; + case AVERAGE_WITH_NONE: + break; + } +} + +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]) +{ + MultiresDisplacementData *data = displacement->user_data; + const int grid_size = data->grid_size; + /* Get displacement in tangent space. */ + const MDisps *displacement_grid; + float grid_u, grid_v; + int corner = displacement_get_grid_and_coord(displacement, + ptex_face_index, u, v, + &displacement_grid, + &grid_u, &grid_v); + /* Read displacement from the current displacement grid and see if any + * averaging is needed. + */ + float tangent_D[3]; + eAverageWith average_with = + read_displacement_grid(displacement_grid, grid_size, + grid_u, grid_v, + tangent_D); + average_displacement(displacement, + ptex_face_index, corner, + average_with, grid_u, grid_v, + tangent_D); + /* Convert it to the object space. */ + float tangent_matrix[3][3]; + construct_tangent_matrix(tangent_matrix, dPdu, dPdv, corner); + mul_v3_m3v3(r_D, tangent_matrix, tangent_D); +} + +static void free_displacement(SubdivDisplacement *displacement) +{ + MultiresDisplacementData *data = displacement->user_data; + MEM_freeN(data->ptex_poly_corner); + MEM_freeN(data); +} + +/* TODO(sergey): This seems to be generally used information, which almost + * worth adding to a subdiv itself, with possible cache of the value. + */ +static int count_num_ptex_faces(const Mesh *mesh) +{ + int num_ptex_faces = 0; + const MPoly *mpoly = mesh->mpoly; + for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { + const MPoly *poly = &mpoly[poly_index]; + num_ptex_faces += (poly->totloop == 4) ? 1 : poly->totloop; + } + return num_ptex_faces; +} + +static void displacement_data_init_mapping(SubdivDisplacement *displacement, + const Object *object) +{ + MultiresDisplacementData *data = displacement->user_data; + const Mesh *mesh = (Mesh *)object->data; + const MPoly *mpoly = mesh->mpoly; + const int num_ptex_faces = count_num_ptex_faces(mesh); + /* Allocate memory. */ + data->ptex_poly_corner = MEM_malloc_arrayN(num_ptex_faces, + sizeof(*data->ptex_poly_corner), + "ptex poly corner"); + /* Fill in offsets. */ + int ptex_face_index = 0; + PolyCornerIndex *ptex_poly_corner = data->ptex_poly_corner; + for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) { + const MPoly *poly = &mpoly[poly_index]; + if (poly->totloop == 4) { + ptex_poly_corner[ptex_face_index].poly_index = poly_index; + ptex_poly_corner[ptex_face_index].corner = 0; + ptex_face_index++; + } + else { + for (int corner = 0; corner < poly->totloop; corner++) { + ptex_poly_corner[ptex_face_index].poly_index = poly_index; + ptex_poly_corner[ptex_face_index].corner = corner; + ptex_face_index++; + } + } + } +} + +static void displacement_init_data(SubdivDisplacement *displacement, + const Object *object, + const MultiresModifierData *mmd) +{ + MultiresDisplacementData *data = displacement->user_data; + Mesh *mesh = (Mesh *)object->data; + data->grid_size = (1 << (mmd->totlvl - 1)) + 1; + data->mpoly = mesh->mpoly; + data->mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS); + displacement_data_init_mapping(displacement, object); +} + +static void displacement_init_functions(SubdivDisplacement *displacement) +{ + displacement->eval_displacement = eval_displacement; + displacement->free = free_displacement; +} + +void BKE_subdiv_displacement_attach_from_multires( + Subdiv *subdiv, + const Object *object, + const MultiresModifierData *mmd) +{ + if (object->type != OB_MESH) { + BLI_assert(!"Should never be called for non-mesh objects"); + return; + } + /* Make sure we dont' have previously assigned displacement. */ + BKE_subdiv_displacement_detach(subdiv); + /* Allocate all required memory. */ + SubdivDisplacement *displacement = MEM_callocN(sizeof(SubdivDisplacement), + "multires displacement"); + displacement->user_data = MEM_callocN(sizeof(MultiresDisplacementData), + "multires displacement data"); + displacement_init_data(displacement, object, mmd); + displacement_init_functions(displacement); + /* Finish. */ + subdiv->displacement_evaluator = displacement; +} diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index e3754655ea6..7b234683102 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -164,53 +164,53 @@ void BKE_subdiv_eval_limit_point( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3]) + float r_P[3]) { BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, - P, NULL, NULL); + r_P, NULL, NULL); } void BKE_subdiv_eval_limit_point_and_derivatives( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3], float dPdu[3], float dPdv[3]) + float r_P[3], float r_dPdu[3], float r_dPdv[3]) { subdiv->evaluator->evaluateLimit(subdiv->evaluator, ptex_face_index, u, v, - P, dPdu, dPdv); + r_P, r_dPdu, r_dPdv); } void BKE_subdiv_eval_limit_point_and_normal( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3], float N[3]) + float r_P[3], float r_N[3]) { float dPdu[3], dPdv[3]; BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, - P, dPdu, dPdv); - cross_v3_v3v3(N, dPdu, dPdv); - normalize_v3(N); + r_P, dPdu, dPdv); + cross_v3_v3v3(r_N, dPdu, dPdv); + normalize_v3(r_N); } void BKE_subdiv_eval_limit_point_and_short_normal( Subdiv *subdiv, const int ptex_face_index, const float u, const float v, - float P[3], short N[3]) + float r_P[3], short r_N[3]) { float N_float[3]; BKE_subdiv_eval_limit_point_and_normal(subdiv, ptex_face_index, u, v, - P, N_float); - normal_float_to_short_v3(N, N_float); + r_P, N_float); + normal_float_to_short_v3(r_N, N_float); } void BKE_subdiv_eval_face_varying( @@ -218,13 +218,53 @@ void BKE_subdiv_eval_face_varying( const int face_varying_channel, const int ptex_face_index, const float u, const float v, - float face_varying[2]) + float r_face_varying[2]) { subdiv->evaluator->evaluateFaceVarying(subdiv->evaluator, face_varying_channel, ptex_face_index, u, v, - face_varying); + r_face_varying); +} + +void BKE_subdiv_eval_displacement( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + const float dPdu[3], const float dPdv[3], + float r_D[3]) +{ + if (subdiv->displacement_evaluator == NULL) { + zero_v3(r_D); + return; + } + subdiv->displacement_evaluator->eval_displacement( + subdiv->displacement_evaluator, + ptex_face_index, + u, v, + dPdu, dPdv, + r_D); +} + +void BKE_subdiv_eval_final_point( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float r_P[3]) +{ + if (subdiv->displacement_evaluator) { + float dPdu[3], dPdv[3], D[3]; + BKE_subdiv_eval_limit_point_and_derivatives( + subdiv, ptex_face_index, u, v, r_P, dPdu, dPdv); + BKE_subdiv_eval_displacement(subdiv, + ptex_face_index, u, v, + dPdu, dPdv, + D); + add_v3_v3(r_P, D); + } + else { + BKE_subdiv_eval_limit_point(subdiv, ptex_face_index, u, v, r_P); + } } /* =================== Patch queries at given resolution =================== */ diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index 3d948dc9508..c8f67c2a893 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -668,6 +668,50 @@ static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation) } /* ============================================================================= + * Helper evaluation functions. + */ + +static void eval_final_point_and_vertex_normal( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + float r_P[3], short r_N[3]) +{ + if (subdiv->displacement_evaluator == NULL) { + BKE_subdiv_eval_limit_point_and_short_normal( + subdiv, ptex_face_index, u, v, r_P, r_N); + } + else { + BKE_subdiv_eval_final_point( + subdiv, ptex_face_index, u, v, r_P); + } +} + +/* ============================================================================= + * Displacement helpers + */ + +static void subdiv_accumulate_vertex_displacement( + Subdiv *subdiv, + const int ptex_face_index, + const float u, const float v, + MVert *subdiv_vert) +{ + float dummy_P[3], dPdu[3], dPdv[3], D[3]; + BKE_subdiv_eval_limit_point_and_derivatives( + subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv); + BKE_subdiv_eval_displacement(subdiv, + ptex_face_index, u, v, + dPdu, dPdv, + D); + add_v3_v3(subdiv_vert->co, D); + if (subdiv_vert->flag & ME_VERT_TMP_TAG) { + mul_v3_fl(subdiv_vert->co, 0.5f); + } + subdiv_vert->flag |= ME_VERT_TMP_TAG; +} + +/* ============================================================================= * Vertex subdivision process. */ @@ -682,6 +726,7 @@ static void subdiv_vertex_data_copy( Mesh *subdiv_mesh = ctx->subdiv_mesh; const int coarse_vertex_index = coarse_vertex - coarse_mesh->mvert; const int subdiv_vertex_index = subdiv_vertex - subdiv_mesh->mvert; + subdiv_vertex->flag &= ~ME_VERT_TMP_TAG; CustomData_copy_data(&coarse_mesh->vdata, &ctx->subdiv_mesh->vdata, coarse_vertex_index, @@ -700,6 +745,7 @@ static void subdiv_vertex_data_interpolate( u * (1.0f - v), u * v, (1.0f - u) * v}; + subdiv_vertex->flag &= ~ME_VERT_TMP_TAG; CustomData_interp(vertex_interpolation->vertex_data, &ctx->subdiv_mesh->vdata, vertex_interpolation->vertex_indices, @@ -711,6 +757,7 @@ static void subdiv_vertex_data_interpolate( } } + /* Evaluation of corner vertices. They are coming from coarse vertices. */ static void subdiv_evaluate_corner_vertices_regular( @@ -740,12 +787,19 @@ static void subdiv_evaluate_corner_vertices_regular( const MVert *coarse_vert = &coarse_mvert[coarse_loop->v]; MVert *subdiv_vert = &subdiv_mvert[ ctx->vertices_corner_offset + coarse_loop->v]; + /* Displacement is accumulated in subdiv vertex position. + * need to back it up before copying data fro original vertex. + */ + float D[3]; + copy_v3_v3(D, subdiv_vert->co); subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert); BKE_subdiv_eval_limit_point_and_short_normal( subdiv, ptex_face_index, weights[corner][0], weights[corner][1], subdiv_vert->co, subdiv_vert->no); + /* Apply displacement. */ + add_v3_v3(subdiv_vert->co, D); } } @@ -775,12 +829,19 @@ static void subdiv_evaluate_corner_vertices_special( const MVert *coarse_vert = &coarse_mvert[coarse_loop->v]; MVert *subdiv_vert = &subdiv_mvert[ ctx->vertices_corner_offset + coarse_loop->v]; + /* Displacement is accumulated in subdiv vertex position. + * need to back it up before copying data fro original vertex. + */ + float D[3]; + copy_v3_v3(D, subdiv_vert->co); subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert); BKE_subdiv_eval_limit_point_and_short_normal( subdiv, ptex_face_index, 0.0f, 0.0f, subdiv_vert->co, subdiv_vert->no); + /* Apply displacement. */ + add_v3_v3(subdiv_vert->co, D); } } @@ -795,6 +856,76 @@ static void subdiv_evaluate_corner_vertices(SubdivMeshContext *ctx, } } +static void subdiv_displace_corner_vertices_regular( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const float weights[4][2] = {{0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}}; + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_corner_offset + coarse_loop->v]; + const float u = weights[corner][0]; + const float v = weights[corner][1]; + subdiv_accumulate_vertex_displacement( + subdiv, ptex_face_index, u, v, subdiv_vert); + } +} + +static void subdiv_displace_corner_vertices_special( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + int ptex_face_index = ctx->face_ptex_offset[poly_index]; + for (int corner = 0; + corner < coarse_poly->totloop; + corner++, ptex_face_index++) + { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_corner_offset + coarse_loop->v]; + subdiv_accumulate_vertex_displacement( + subdiv, ptex_face_index, 0.0f, 0.0f, subdiv_vert); + } +} + +static void subdiv_displace_corner_vertices(SubdivMeshContext *ctx) +{ + Subdiv *subdiv = ctx->subdiv; + if (subdiv->displacement_evaluator == NULL) { + return; + } + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) { + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + if (coarse_poly->totloop == 4) { + subdiv_displace_corner_vertices_regular(ctx, coarse_poly); + } + else { + subdiv_displace_corner_vertices_special(ctx, coarse_poly); + } + } +} + /* Evaluation of edge vertices. They are coming from coarse edges. */ static void subdiv_evaluate_edge_vertices_regular( @@ -851,6 +982,11 @@ static void subdiv_evaluate_edge_vertices_regular( u = (corner == 1) ? 1.0f : 0.0f; v = fac; } + /* Displacement is accumulated in subdiv vertex position. + * need to back it up before copying data fro original vertex. + */ + float D[3]; + copy_v3_v3(D, subdiv_vert->co); subdiv_vertex_data_interpolate(ctx, subdiv_vert, vertex_interpolation, @@ -860,6 +996,8 @@ static void subdiv_evaluate_edge_vertices_regular( ptex_face_index, u, v, subdiv_vert->co, subdiv_vert->no); + /* Apply displacement. */ + add_v3_v3(subdiv_vert->co, D); } } } @@ -913,6 +1051,11 @@ static void subdiv_evaluate_edge_vertices_special( vertex_index++, subdiv_vert += veretx_delta) { float u = vertex_index * inv_ptex_resolution_1; + /* Displacement is accumulated in subdiv vertex position. + * need to back it up before copying data fro original vertex. + */ + float D[3]; + copy_v3_v3(D, subdiv_vert->co); subdiv_vertex_data_interpolate(ctx, subdiv_vert, vertex_interpolation, @@ -922,6 +1065,8 @@ static void subdiv_evaluate_edge_vertices_special( ptex_face_index, u, 0.0f, subdiv_vert->co, subdiv_vert->no); + /* Apply displacement. */ + add_v3_v3(subdiv_vert->co, D); } const int next_ptex_face_index = ptex_face_start_index + (corner + 1) % coarse_poly->totloop; @@ -930,6 +1075,11 @@ static void subdiv_evaluate_edge_vertices_special( vertex_index++, subdiv_vert += veretx_delta) { float v = 1.0f - vertex_index * inv_ptex_resolution_1; + /* Displacement is accumulated in subdiv vertex position. + * need to back it up before copying data fro original vertex. + */ + float D[3]; + copy_v3_v3(D, subdiv_vert->co); subdiv_vertex_data_interpolate(ctx, subdiv_vert, vertex_interpolation, @@ -939,6 +1089,8 @@ static void subdiv_evaluate_edge_vertices_special( next_ptex_face_index, 0.0f, v, subdiv_vert->co, subdiv_vert->no); + /* Apply displacement. */ + add_v3_v3(subdiv_vert->co, D); } } } @@ -958,6 +1110,129 @@ static void subdiv_evaluate_edge_vertices( } } +static void subdiv_displace_edge_vertices_regular( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const int resolution = ctx->settings->resolution; + const int resolution_1 = resolution - 1; + const float inv_resolution_1 = 1.0f / (float)resolution_1; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_index = ctx->face_ptex_offset[poly_index]; + for (int corner = 0; corner < coarse_poly->totloop; corner++) { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge]; + for (int vertex_index = 0; + vertex_index < num_subdiv_vertices_per_coarse_edge; + vertex_index++, subdiv_vert++) + { + float fac = (vertex_index + 1) * inv_resolution_1; + if (flip) { + fac = 1.0f - fac; + } + if (corner >= 2) { + fac = 1.0f - fac; + } + float u, v; + if ((corner & 1) == 0) { + u = fac; + v = (corner == 2) ? 1.0f : 0.0f; + } + else { + u = (corner == 1) ? 1.0f : 0.0f; + v = fac; + } + subdiv_accumulate_vertex_displacement( + subdiv, ptex_face_index, u, v, subdiv_vert); + } + } +} + +static void subdiv_displace_edge_vertices_special( + SubdivMeshContext *ctx, + const MPoly *coarse_poly) +{ + const int resolution = ctx->settings->resolution; + const int num_subdiv_vertices_per_coarse_edge = resolution - 2; + const int num_vertices_per_ptex_edge = ((resolution >> 1) + 1); + const float inv_ptex_resolution_1 = + 1.0f / (float)(num_vertices_per_ptex_edge - 1); + Subdiv *subdiv = ctx->subdiv; + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MEdge *coarse_medge = coarse_mesh->medge; + const MLoop *coarse_mloop = coarse_mesh->mloop; + Mesh *subdiv_mesh = ctx->subdiv_mesh; + MVert *subdiv_mvert = subdiv_mesh->mvert; + const int poly_index = coarse_poly - coarse_mesh->mpoly; + const int ptex_face_start_index = ctx->face_ptex_offset[poly_index]; + int ptex_face_index = ptex_face_start_index; + for (int corner = 0; + corner < coarse_poly->totloop; + corner++, ptex_face_index++) + { + const MLoop *coarse_loop = + &coarse_mloop[coarse_poly->loopstart + corner]; + const MEdge *coarse_edge = &coarse_medge[coarse_loop->e]; + const bool flip = (coarse_edge->v2 == coarse_loop->v); + MVert *subdiv_vert = &subdiv_mvert[ + ctx->vertices_edge_offset + + coarse_loop->e * num_subdiv_vertices_per_coarse_edge]; + int veretx_delta = 1; + if (flip) { + subdiv_vert += num_subdiv_vertices_per_coarse_edge - 1; + veretx_delta = -1; + } + for (int vertex_index = 1; + vertex_index < num_vertices_per_ptex_edge; + vertex_index++, subdiv_vert += veretx_delta) + { + float u = vertex_index * inv_ptex_resolution_1; + subdiv_accumulate_vertex_displacement( + subdiv, ptex_face_index, u, 0.0f, subdiv_vert); + } + const int next_ptex_face_index = + ptex_face_start_index + (corner + 1) % coarse_poly->totloop; + for (int vertex_index = 1; + vertex_index < num_vertices_per_ptex_edge - 1; + vertex_index++, subdiv_vert += veretx_delta) + { + float v = 1.0f - vertex_index * inv_ptex_resolution_1; + subdiv_accumulate_vertex_displacement( + subdiv, next_ptex_face_index, 0.0f, v, subdiv_vert); + } + } +} +static void subdiv_displace_edge_vertices(SubdivMeshContext *ctx) +{ + Subdiv *subdiv = ctx->subdiv; + if (subdiv->displacement_evaluator == NULL) { + return; + } + const Mesh *coarse_mesh = ctx->coarse_mesh; + const MPoly *coarse_mpoly = coarse_mesh->mpoly; + for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) { + const MPoly *coarse_poly = &coarse_mpoly[poly_index]; + if (coarse_poly->totloop == 4) { + subdiv_displace_edge_vertices_regular(ctx, coarse_poly); + } + else { + subdiv_displace_edge_vertices_special(ctx, coarse_poly); + } + } +} + /* Evaluation of inner vertices, they are coming from ptex patches. */ static void subdiv_evaluate_inner_vertices_regular( @@ -988,7 +1263,7 @@ static void subdiv_evaluate_inner_vertices_regular( subdiv_vert, vertex_interpolation, u, v); - BKE_subdiv_eval_limit_point_and_short_normal( + eval_final_point_and_vertex_normal( subdiv, ptex_face_index, u, v, @@ -1024,7 +1299,7 @@ static void subdiv_evaluate_inner_vertices_special( subdiv_vert, vertex_interpolation, 1.0f, 1.0f); - BKE_subdiv_eval_limit_point_and_short_normal( + eval_final_point_and_vertex_normal( subdiv, ptex_face_index, 1.0f, 1.0f, @@ -1048,7 +1323,7 @@ static void subdiv_evaluate_inner_vertices_special( subdiv_vert, vertex_interpolation, u, v); - BKE_subdiv_eval_limit_point_and_short_normal( + eval_final_point_and_vertex_normal( subdiv, ptex_face_index, u, v, @@ -2415,9 +2690,14 @@ Mesh *BKE_subdiv_to_mesh( ctx.subdiv_mesh = result; subdiv_mesh_ctx_init_result(&ctx); /* Multi-threaded evaluation. */ - ParallelRangeSettings parallel_range_settings; BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); + /* Single threaded passes to average displacement on the corner vertices + * and boundary edges. + */ + subdiv_displace_corner_vertices(&ctx); + subdiv_displace_edge_vertices(&ctx); + ParallelRangeSettings parallel_range_settings; BLI_parallel_range_settings_defaults(¶llel_range_settings); BLI_task_parallel_range(0, coarse_mesh->totpoly, &ctx, @@ -2439,5 +2719,8 @@ Mesh *BKE_subdiv_to_mesh( BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY); // BKE_mesh_validate(result, true, true); BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); + if (subdiv->displacement_evaluator != NULL) { + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + } return result; } |