diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2018-09-21 14:58:49 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2018-09-21 16:31:43 +0300 |
commit | b32832a7e8f6a12eee658ff1eb1be7e18d2e6eef (patch) | |
tree | 305a9a0b8efa8b5480414d1533416bc948fb693b /source/blender/blenkernel | |
parent | da800621ed2e65fdf5b6ad665a96b360a51e83fc (diff) |
Subdiv: CCG, initialize grid mask from paint mask
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_subdiv_ccg.h | 28 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_ccg.c | 29 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_ccg_mask.c | 241 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_displacement_multires.c | 2 |
5 files changed, 295 insertions, 6 deletions
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index cee7bce7f05..fa3782bbd0f 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -43,6 +43,31 @@ struct DMFlagMat; struct Mesh; struct Subdiv; +/* ============================================================================= + * Masks. + */ + +/* Functor which evaluates mask value at a given (u, v) of given ptex face. */ +typedef struct SubdivCCGMask { + float (*eval_mask)(struct SubdivCCGMask *mask, + const int ptex_face_index, + const float u, const float v); + + /* Free the data, not the evaluator itself. */ + void (*free)(struct SubdivCCGMask *mask); + + void *user_data; +} SubdivCCGMask; + +/* Return true if mesh has mask and evaluator can be used. */ +bool BKE_subdiv_ccg_mask_init_from_paint( + SubdivCCGMask *mask_evaluator, + const struct Mesh *mesh); + +/* ============================================================================= + * SubdivCCG. + */ + typedef struct SubdivToCCGSettings { /* Resolution at which regular ptex (created for quad polygon) are being * evaluated. This defines how many vertices final mesh will have: every @@ -190,7 +215,8 @@ typedef struct SubdivCCG { */ struct SubdivCCG *BKE_subdiv_to_ccg( struct Subdiv *subdiv, - const SubdivToCCGSettings *settings); + const SubdivToCCGSettings *settings, + SubdivCCGMask *mask_evaluator); /* Destroy CCG representation of subdivision surface. */ void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 932e21b312e..452ca90ecf1 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -195,6 +195,7 @@ set(SRC intern/studiolight.c intern/subdiv.c intern/subdiv_ccg.c + intern/subdiv_ccg_mask.c intern/subdiv_converter.c intern/subdiv_converter_mesh.c intern/subdiv_displacement.c diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 29327a465b3..5ec675494f5 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -183,6 +183,7 @@ typedef struct CCGEvalGridsData { SubdivCCG *subdiv_ccg; Subdiv *subdiv; int *face_ptex_offset; + SubdivCCGMask *mask_evaluator; } CCGEvalGridsData; static void subdiv_ccg_eval_grid_element( @@ -207,6 +208,16 @@ static void subdiv_ccg_eval_grid_element( BKE_subdiv_eval_limit_point( subdiv, ptex_face_index, u, v, (float *)element); } + if (subdiv_ccg->has_mask) { + float *mask_value_ptr = (float *)(element + subdiv_ccg->mask_offset); + if (data->mask_evaluator != NULL) { + *mask_value_ptr = data->mask_evaluator->eval_mask( + data->mask_evaluator, ptex_face_index, u, v); + } + else { + *mask_value_ptr = 0; + } + } } BLI_INLINE void rotate_corner_to_quad( @@ -320,7 +331,8 @@ static void subdiv_ccg_eval_grids_task( static bool subdiv_ccg_evaluate_grids( SubdivCCG *subdiv_ccg, - Subdiv *subdiv) + Subdiv *subdiv, + SubdivCCGMask *mask_evaluator) { OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner; const int num_faces = topology_refiner->getNumFaces(topology_refiner); @@ -329,6 +341,7 @@ static bool subdiv_ccg_evaluate_grids( data.subdiv_ccg = subdiv_ccg; data.subdiv = subdiv; data.face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv); + data.mask_evaluator = mask_evaluator; /* Threaded grids evaluation. */ ParallelRangeSettings parallel_range_settings; BLI_parallel_range_settings_defaults(¶llel_range_settings); @@ -626,7 +639,8 @@ static void subdiv_ccg_init_faces_neighborhood(SubdivCCG *subdiv_ccg) SubdivCCG *BKE_subdiv_to_ccg( Subdiv *subdiv, - const SubdivToCCGSettings *settings) + const SubdivToCCGSettings *settings, + SubdivCCGMask *mask_evaluator) { BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG); SubdivCCG *subdiv_ccg = MEM_callocN(sizeof(SubdivCCG), "subdiv ccg"); @@ -638,7 +652,7 @@ SubdivCCG *BKE_subdiv_to_ccg( subdiv_ccg_alloc_elements(subdiv_ccg, subdiv); subdiv_ccg_init_faces(subdiv_ccg); subdiv_ccg_init_faces_neighborhood(subdiv_ccg); - if (!subdiv_ccg_evaluate_grids(subdiv_ccg, subdiv)) { + if (!subdiv_ccg_evaluate_grids(subdiv_ccg, subdiv, mask_evaluator)) { BKE_subdiv_ccg_destroy(subdiv_ccg); BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG); return NULL; @@ -660,7 +674,14 @@ Mesh *BKE_subdiv_to_ccg_mesh( } } BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG); - SubdivCCG *subdiv_ccg = BKE_subdiv_to_ccg(subdiv, settings); + SubdivCCGMask mask_evaluator; + bool has_mask = BKE_subdiv_ccg_mask_init_from_paint( + &mask_evaluator, coarse_mesh); + SubdivCCG *subdiv_ccg = BKE_subdiv_to_ccg( + subdiv, settings, has_mask ? &mask_evaluator : NULL); + if (has_mask) { + mask_evaluator.free(&mask_evaluator); + } if (subdiv_ccg == NULL) { return NULL; } diff --git a/source/blender/blenkernel/intern/subdiv_ccg_mask.c b/source/blender/blenkernel/intern/subdiv_ccg_mask.c new file mode 100644 index 00000000000..3805e0b4bdc --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_ccg_mask.c @@ -0,0 +1,241 @@ +/* + * ***** 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_ccg_mask.c + * \ingroup bke + */ + +#include "BKE_subdiv_ccg.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 GridPaintMaskData { + // int grid_size; + const MPoly *mpoly; + const GridPaintMask *grid_paint_mask; + /* 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; +} GridPaintMaskData; + +/* 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 mask_get_grid_and_coord( + SubdivCCGMask *mask_evaluator, + const int ptex_face_index, const float u, const float v, + const GridPaintMask **r_mask_grid, + float *grid_u, float *grid_v) +{ + GridPaintMaskData *data = mask_evaluator->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_mask_grid = + &data->grid_paint_mask[start_grid_index + corner]; + ptex_uv_to_grid_uv(corner_u, corner_v, grid_u, grid_v); + } + else { + *r_mask_grid = + &data->grid_paint_mask[start_grid_index]; + ptex_uv_to_grid_uv(u, v, grid_u, grid_v); + } + return corner; +} + +BLI_INLINE float read_mask_grid(const GridPaintMask *mask_grid, + const float grid_u, const float grid_v) +{ + if (mask_grid->data == NULL) { + return 0; + } + const int grid_size = (1 << (mask_grid->level - 1)) + 1; + const int x = (grid_u * (grid_size - 1) + 0.5f); + const int y = (grid_v * (grid_size - 1) + 0.5f); + return mask_grid->data[y * grid_size + x]; +} + +static float eval_mask(SubdivCCGMask *mask_evaluator, + const int ptex_face_index, + const float u, const float v) +{ + const GridPaintMask *mask_grid; + float grid_u, grid_v; + mask_get_grid_and_coord(mask_evaluator, + ptex_face_index, u, v, + &mask_grid, + &grid_u, &grid_v); + return read_mask_grid(mask_grid, grid_u, grid_v); +} + +static void free_mask_data(SubdivCCGMask *mask_evaluator) +{ + GridPaintMaskData *data = mask_evaluator->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(SubdivCCGMask *mask_evaluator, + const Mesh *mesh) +{ + GridPaintMaskData *data = mask_evaluator->user_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(SubdivCCGMask *mask_evaluator, + const Mesh *mesh) +{ + GridPaintMaskData *data = mask_evaluator->user_data; + data->mpoly = mesh->mpoly; + data->grid_paint_mask = + CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK); + displacement_data_init_mapping(mask_evaluator, mesh); +} + +static void displacement_init_functions(SubdivCCGMask *mask_evaluator) +{ + mask_evaluator->eval_mask = eval_mask; + mask_evaluator->free = free_mask_data; +} + +bool BKE_subdiv_ccg_mask_init_from_paint( + SubdivCCGMask *mask_evaluator, + const struct Mesh *mesh) +{ + GridPaintMask *grid_paint_mask = + CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK); + if (grid_paint_mask == NULL) { + return false; + } + /* Allocate all required memory. */ + mask_evaluator->user_data =MEM_callocN(sizeof(GridPaintMaskData), + "mask from grid data"); + displacement_init_data(mask_evaluator, mesh); + displacement_init_functions(mask_evaluator); + return true; +} diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.c b/source/blender/blenkernel/intern/subdiv_displacement_multires.c index 1e0e2ae5152..5af90160f8e 100644 --- a/source/blender/blenkernel/intern/subdiv_displacement_multires.c +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.c @@ -420,7 +420,7 @@ void BKE_subdiv_displacement_attach_from_multires( const Mesh *mesh, const MultiresModifierData *mmd) { - /* Make sure we dont' have previously assigned displacement. */ + /* Make sure we don't have previously assigned displacement. */ BKE_subdiv_displacement_detach(subdiv); /* Allocate all required memory. */ SubdivDisplacement *displacement = MEM_callocN(sizeof(SubdivDisplacement), |