diff options
-rw-r--r-- | source/blender/blenkernel/BKE_subdiv.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_subdiv_ccg.h | 40 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_ccg.c | 148 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_ccg_mask.c | 15 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_ccg_material.c | 67 | ||||
-rw-r--r-- | source/blender/draw/modes/sculpt_mode.c | 19 |
7 files changed, 262 insertions, 33 deletions
diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h index 4fddb3d5188..1ade8a0e44d 100644 --- a/source/blender/blenkernel/BKE_subdiv.h +++ b/source/blender/blenkernel/BKE_subdiv.h @@ -145,8 +145,7 @@ typedef struct SubdivDisplacement { typedef struct Subdiv { /* Settings this subdivision surface is created for. * - * It is read-only after assignment in BKE_subdiv_new_from_FOO(). - */ + * 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 @@ -162,7 +161,7 @@ typedef struct Subdiv { /* Cached values, are not supposed to be accessed directly. */ struct { /* Indexed by base face index, element indicates total number of ptex - *faces created for preceding base faces. */ + * faces created for preceding base faces. */ int *face_ptex_offset; } cache_; } Subdiv; diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index 804a5d0b500..822f4906bae 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -25,6 +25,7 @@ #define __BKE_SUBDIV_CCG_H__ #include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" #include "BLI_bitmap.h" #include "BLI_sys_types.h" @@ -40,20 +41,41 @@ struct Subdiv; */ /* Functor which evaluates mask value at a given (u, v) of given ptex face. */ -typedef struct SubdivCCGMask { - float (*eval_mask)(struct SubdivCCGMask *mask, +typedef struct SubdivCCGMaskEvaluator { + float (*eval_mask)(struct SubdivCCGMaskEvaluator *mask_evaluator, const int ptex_face_index, const float u, const float v); /* Free the data, not the evaluator itself. */ - void (*free)(struct SubdivCCGMask *mask); + void (*free)(struct SubdivCCGMaskEvaluator *mask_evaluator); void *user_data; -} SubdivCCGMask; +} SubdivCCGMaskEvaluator; /* Return true if mesh has mask and evaluator can be used. */ bool BKE_subdiv_ccg_mask_init_from_paint( - SubdivCCGMask *mask_evaluator, + SubdivCCGMaskEvaluator *mask_evaluator, + const struct Mesh *mesh); + +/* ============================================================================= + * Materials. + */ + +/* Functor which evaluates material and flags of a given coarse face. */ +typedef struct SubdivCCGMaterialFlagsEvaluator { + DMFlagMat (*eval_material_flags)( + struct SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, + const int coarse_face_index); + + /* Free the data, not the evaluator itself. */ + void (*free)( + struct SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator); + + void *user_data; +} SubdivCCGMaterialFlagsEvaluator; + +void BKE_subdiv_ccg_material_flags_init_from_mesh( + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, const struct Mesh *mesh); /* ============================================================================= @@ -196,7 +218,8 @@ typedef struct SubdivCCG { struct SubdivCCG *BKE_subdiv_to_ccg( struct Subdiv *subdiv, const SubdivToCCGSettings *settings, - SubdivCCGMask *mask_evaluator); + SubdivCCGMaskEvaluator *mask_evaluator, + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator); /* Destroy CCG representation of subdivision surface. */ void BKE_subdiv_ccg_destroy(SubdivCCG *subdiv_ccg); @@ -218,6 +241,11 @@ void BKE_subdiv_ccg_key_top_level( /* Recalculate all normals based on grid element coordinates. */ void BKE_subdiv_ccg_recalc_normals(SubdivCCG *subdiv_ccg); +/* Update normals of affected faces. */ +void BKE_subdiv_ccg_update_normals(SubdivCCG *subdiv_ccg, + struct CCGFace **effected_faces, + int num_effected_faces); + /* Average grid coordinates and normals along the grid boundatries. */ void BKE_subdiv_ccg_average_grids(SubdivCCG *subdiv_ccg); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b30acbcae24..e3afbc507bf 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -194,6 +194,7 @@ set(SRC intern/subdiv.c intern/subdiv_ccg.c intern/subdiv_ccg_mask.c + intern/subdiv_ccg_material.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 bfcd4b8d52d..c35b38b4184 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -41,6 +41,19 @@ #include "opensubdiv_topology_refiner_capi.h" /* ============================================================================= + * Various forward declarations. + */ + +static void subdiv_ccg_average_all_boundaries_and_corners( + SubdivCCG *subdiv_ccg, + CCGKey *key); + +static void subdiv_ccg_average_inner_face_grids( + SubdivCCG *subdiv_ccg, + CCGKey *key, + SubdivCCGFace *face); + +/* ============================================================================= * Generally useful internal helpers. */ @@ -163,7 +176,8 @@ typedef struct CCGEvalGridsData { SubdivCCG *subdiv_ccg; Subdiv *subdiv; int *face_ptex_offset; - SubdivCCGMask *mask_evaluator; + SubdivCCGMaskEvaluator *mask_evaluator; + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator; } CCGEvalGridsData; static void subdiv_ccg_eval_grid_element( @@ -232,6 +246,10 @@ static void subdiv_ccg_eval_regular_grid(CCGEvalGridsData *data, } /* Assign grid's face. */ grid_faces[grid_index] = &faces[face_index]; + /* Assign material flags. */ + subdiv_ccg->grid_flag_mats[grid_index] = + data->material_flags_evaluator->eval_material_flags( + data->material_flags_evaluator, face_index); } } @@ -247,13 +265,13 @@ static void subdiv_ccg_eval_special_grid(CCGEvalGridsData *data, const SubdivCCGFace *face = &faces[face_index]; for (int corner = 0; corner < face->num_grids; corner++) { const int grid_index = face->start_grid_index + corner; + const int ptex_face_index = + data->face_ptex_offset[face_index] + corner; unsigned char *grid = (unsigned char *)subdiv_ccg->grids[grid_index]; for (int y = 0; y < grid_size; y++) { const float u = 1.0f - ((float)y * grid_size_1_inv); for (int x = 0; x < grid_size; x++) { const float v = 1.0f - ((float)x * grid_size_1_inv); - const int ptex_face_index = - data->face_ptex_offset[face_index] + corner; const size_t grid_element_index = (size_t)y * grid_size + x; const size_t grid_element_offset = grid_element_index * element_size; @@ -265,6 +283,10 @@ static void subdiv_ccg_eval_special_grid(CCGEvalGridsData *data, } /* Assign grid's face. */ grid_faces[grid_index] = &faces[face_index]; + /* Assign material flags. */ + subdiv_ccg->grid_flag_mats[grid_index] = + data->material_flags_evaluator->eval_material_flags( + data->material_flags_evaluator, face_index); } } @@ -287,7 +309,8 @@ static void subdiv_ccg_eval_grids_task( static bool subdiv_ccg_evaluate_grids( SubdivCCG *subdiv_ccg, Subdiv *subdiv, - SubdivCCGMask *mask_evaluator) + SubdivCCGMaskEvaluator *mask_evaluator, + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator) { OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner; const int num_faces = topology_refiner->getNumFaces(topology_refiner); @@ -297,6 +320,7 @@ static bool subdiv_ccg_evaluate_grids( data.subdiv = subdiv; data.face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv); data.mask_evaluator = mask_evaluator; + data.material_flags_evaluator = material_flags_evaluator; /* Threaded grids evaluation. */ ParallelRangeSettings parallel_range_settings; BLI_parallel_range_settings_defaults(¶llel_range_settings); @@ -591,7 +615,8 @@ static void subdiv_ccg_init_faces_neighborhood(SubdivCCG *subdiv_ccg) SubdivCCG *BKE_subdiv_to_ccg( Subdiv *subdiv, const SubdivToCCGSettings *settings, - SubdivCCGMask *mask_evaluator) + SubdivCCGMaskEvaluator *mask_evaluator, + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator) { BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG); SubdivCCG *subdiv_ccg = MEM_callocN(sizeof(SubdivCCG), "subdiv ccg"); @@ -602,7 +627,8 @@ 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, mask_evaluator)) { + if (!subdiv_ccg_evaluate_grids( + subdiv_ccg, subdiv, mask_evaluator, material_flags_evaluator)) { BKE_subdiv_ccg_destroy(subdiv_ccg); BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG); return NULL; @@ -624,11 +650,17 @@ Mesh *BKE_subdiv_to_ccg_mesh( } } BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_CCG); - SubdivCCGMask mask_evaluator; + SubdivCCGMaskEvaluator mask_evaluator; bool has_mask = BKE_subdiv_ccg_mask_init_from_paint( - &mask_evaluator, coarse_mesh); + &mask_evaluator, coarse_mesh); + SubdivCCGMaterialFlagsEvaluator material_flags_evaluator; + BKE_subdiv_ccg_material_flags_init_from_mesh( + &material_flags_evaluator, coarse_mesh); SubdivCCG *subdiv_ccg = BKE_subdiv_to_ccg( - subdiv, settings, has_mask ? &mask_evaluator : NULL); + subdiv, + settings, + has_mask ? &mask_evaluator : NULL, + &material_flags_evaluator); if (has_mask) { mask_evaluator.free(&mask_evaluator); } @@ -723,12 +755,11 @@ typedef struct RecalcInnerNormalsTLSData { * * The result is stored in normals storage from TLS. */ static void subdiv_ccg_recalc_inner_face_normals( - RecalcInnerNormalsData *data, + SubdivCCG *subdiv_ccg, + CCGKey *key, RecalcInnerNormalsTLSData *tls, const int grid_index) { - SubdivCCG *subdiv_ccg = data->subdiv_ccg; - CCGKey *key = data->key; const int grid_size = subdiv_ccg->grid_size; const int grid_size_1 = grid_size - 1; CCGElem *grid = subdiv_ccg->grids[grid_index]; @@ -761,12 +792,11 @@ static void subdiv_ccg_recalc_inner_face_normals( /* Average normals at every grid element, using adjacent faces normals. */ static void subdiv_ccg_average_inner_face_normals( - RecalcInnerNormalsData *data, + SubdivCCG *subdiv_ccg, + CCGKey *key, RecalcInnerNormalsTLSData *tls, const int grid_index) { - SubdivCCG *subdiv_ccg = data->subdiv_ccg; - CCGKey *key = data->key; const int grid_size = subdiv_ccg->grid_size; const int grid_size_1 = grid_size - 1; CCGElem *grid = subdiv_ccg->grids[grid_index]; @@ -811,8 +841,10 @@ static void subdiv_ccg_recalc_inner_normal_task( { RecalcInnerNormalsData *data = userdata_v; RecalcInnerNormalsTLSData *tls = tls_v->userdata_chunk; - subdiv_ccg_recalc_inner_face_normals(data, tls, grid_index); - subdiv_ccg_average_inner_face_normals(data, tls, grid_index); + subdiv_ccg_recalc_inner_face_normals( + data->subdiv_ccg, data->key, tls, grid_index); + subdiv_ccg_average_inner_face_normals( + data->subdiv_ccg, data->key, tls, grid_index); } static void subdiv_ccg_recalc_inner_normal_finalize( @@ -855,6 +887,88 @@ void BKE_subdiv_ccg_recalc_normals(SubdivCCG *subdiv_ccg) BKE_subdiv_ccg_average_grids(subdiv_ccg); } +typedef struct RecalcModifiedInnerNormalsData { + SubdivCCG *subdiv_ccg; + CCGKey *key; + SubdivCCGFace **effected_ccg_faces; +} RecalcModifiedInnerNormalsData; + +static void subdiv_ccg_recalc_modified_inner_normal_task( + void *__restrict userdata_v, + const int face_index, + const ParallelRangeTLS *__restrict tls_v) +{ + RecalcModifiedInnerNormalsData *data = userdata_v; + SubdivCCG *subdiv_ccg = data->subdiv_ccg; + CCGKey *key = data->key; + RecalcInnerNormalsTLSData *tls = tls_v->userdata_chunk; + SubdivCCGFace **faces = data->effected_ccg_faces; + SubdivCCGFace *face = faces[face_index]; + const int num_face_grids = face->num_grids; + for (int i = 0; i < num_face_grids; i++) { + const int grid_index = face->start_grid_index + i; + subdiv_ccg_recalc_inner_face_normals( + data->subdiv_ccg, data->key, tls, grid_index); + subdiv_ccg_average_inner_face_normals( + data->subdiv_ccg, data->key, tls, grid_index); + } + subdiv_ccg_average_inner_face_grids(subdiv_ccg, key, face); +} + +static void subdiv_ccg_recalc_modified_inner_normal_finalize( + void *__restrict UNUSED(userdata), + void *__restrict tls_v) +{ + RecalcInnerNormalsTLSData *tls = tls_v; + MEM_SAFE_FREE(tls->face_normals); +} + +static void subdiv_ccg_recalc_modified_inner_grid_normals( + SubdivCCG *subdiv_ccg, + struct CCGFace **effected_faces, + int num_effected_faces) +{ + CCGKey key; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + RecalcModifiedInnerNormalsData data = { + .subdiv_ccg = subdiv_ccg, + .key = &key, + .effected_ccg_faces = (SubdivCCGFace **)effected_faces, + }; + RecalcInnerNormalsTLSData tls_data = {NULL}; + ParallelRangeSettings parallel_range_settings; + BLI_parallel_range_settings_defaults(¶llel_range_settings); + parallel_range_settings.userdata_chunk = &tls_data; + parallel_range_settings.userdata_chunk_size = sizeof(tls_data); + parallel_range_settings.func_finalize = + subdiv_ccg_recalc_modified_inner_normal_finalize; + BLI_task_parallel_range(0, num_effected_faces, + &data, + subdiv_ccg_recalc_modified_inner_normal_task, + ¶llel_range_settings); +} + +void BKE_subdiv_ccg_update_normals(SubdivCCG *subdiv_ccg, + struct CCGFace **effected_faces, + int num_effected_faces) +{ + if (!subdiv_ccg->has_normal) { + /* Grids don't have normals, can do early output. */ + return; + } + if (num_effected_faces == 0) { + /* No faces changed, so nothing to do here. */ + return; + } + subdiv_ccg_recalc_modified_inner_grid_normals( + subdiv_ccg, effected_faces, num_effected_faces); + /* TODO(sergey): Only average elements which are adjacent to modified + * faces. */ + CCGKey key; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key); +} + /* ============================================================================= * Boundary averaging/stitching. */ diff --git a/source/blender/blenkernel/intern/subdiv_ccg_mask.c b/source/blender/blenkernel/intern/subdiv_ccg_mask.c index e90d5df3774..ec9dfd30f16 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg_mask.c +++ b/source/blender/blenkernel/intern/subdiv_ccg_mask.c @@ -54,7 +54,7 @@ typedef struct GridPaintMaskData { } GridPaintMaskData; static int mask_get_grid_and_coord( - SubdivCCGMask *mask_evaluator, + SubdivCCGMaskEvaluator *mask_evaluator, const int ptex_face_index, const float u, const float v, const GridPaintMask **r_mask_grid, float *grid_u, float *grid_v) @@ -92,7 +92,7 @@ BLI_INLINE float read_mask_grid(const GridPaintMask *mask_grid, return mask_grid->data[y * grid_size + x]; } -static float eval_mask(SubdivCCGMask *mask_evaluator, +static float eval_mask(SubdivCCGMaskEvaluator *mask_evaluator, const int ptex_face_index, const float u, const float v) { @@ -105,7 +105,7 @@ static float eval_mask(SubdivCCGMask *mask_evaluator, return read_mask_grid(mask_grid, grid_u, grid_v); } -static void free_mask_data(SubdivCCGMask *mask_evaluator) +static void free_mask_data(SubdivCCGMaskEvaluator *mask_evaluator) { GridPaintMaskData *data = mask_evaluator->user_data; MEM_freeN(data->ptex_poly_corner); @@ -126,7 +126,7 @@ static int count_num_ptex_faces(const Mesh *mesh) return num_ptex_faces; } -static void mask_data_init_mapping(SubdivCCGMask *mask_evaluator, +static void mask_data_init_mapping(SubdivCCGMaskEvaluator *mask_evaluator, const Mesh *mesh) { GridPaintMaskData *data = mask_evaluator->user_data; @@ -156,7 +156,8 @@ static void mask_data_init_mapping(SubdivCCGMask *mask_evaluator, } } -static void mask_init_data(SubdivCCGMask *mask_evaluator, const Mesh *mesh) +static void mask_init_data(SubdivCCGMaskEvaluator *mask_evaluator, + const Mesh *mesh) { GridPaintMaskData *data = mask_evaluator->user_data; data->mpoly = mesh->mpoly; @@ -165,14 +166,14 @@ static void mask_init_data(SubdivCCGMask *mask_evaluator, const Mesh *mesh) mask_data_init_mapping(mask_evaluator, mesh); } -static void mask_init_functions(SubdivCCGMask *mask_evaluator) +static void mask_init_functions(SubdivCCGMaskEvaluator *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, + SubdivCCGMaskEvaluator *mask_evaluator, const struct Mesh *mesh) { GridPaintMask *grid_paint_mask = diff --git a/source/blender/blenkernel/intern/subdiv_ccg_material.c b/source/blender/blenkernel/intern/subdiv_ccg_material.c new file mode 100644 index 00000000000..0dc86095cff --- /dev/null +++ b/source/blender/blenkernel/intern/subdiv_ccg_material.c @@ -0,0 +1,67 @@ +/* + * 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. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_subdiv_ccg.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" + +typedef struct CCGMaterialFromMeshData { + const Mesh *mesh; +} CCGMaterialFromMeshData; + +static DMFlagMat subdiv_ccg_material_flags_eval( + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, + const int coarse_face_index) +{ + CCGMaterialFromMeshData *data = + (CCGMaterialFromMeshData *)material_flags_evaluator->user_data; + const Mesh *mesh = data->mesh; + BLI_assert(coarse_face_index < mesh->totpoly); + const MPoly *mpoly = mesh->mpoly; + const MPoly *poly = &mpoly[coarse_face_index]; + DMFlagMat material_flags; + material_flags.flag = poly->flag; + material_flags.mat_nr = poly->mat_nr; + return material_flags; +} + +static void subdiv_ccg_material_flags_free( + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator) +{ + MEM_freeN(material_flags_evaluator->user_data); +} + +void BKE_subdiv_ccg_material_flags_init_from_mesh( + SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, + const Mesh *mesh) +{ + CCGMaterialFromMeshData *data = MEM_mallocN( + sizeof(CCGMaterialFromMeshData), "ccg material eval"); + data->mesh = mesh; + material_flags_evaluator->eval_material_flags = + subdiv_ccg_material_flags_eval; + material_flags_evaluator->free = subdiv_ccg_material_flags_free; + material_flags_evaluator->user_data = data; +}
\ No newline at end of file diff --git a/source/blender/draw/modes/sculpt_mode.c b/source/blender/draw/modes/sculpt_mode.c index 5d008a35a61..bb13cb36c01 100644 --- a/source/blender/draw/modes/sculpt_mode.c +++ b/source/blender/draw/modes/sculpt_mode.c @@ -29,6 +29,7 @@ #include "BKE_pbvh.h" #include "BKE_paint.h" +#include "BKE_subdiv_ccg.h" /* If builtin shaders are needed */ #include "GPU_shader.h" @@ -154,6 +155,22 @@ static void sculpt_draw_mask_cb( } } +static void sculpt_update_pbvh_normals(Object *object) +{ + Mesh *mesh = object->data; + PBVH *pbvh = object->sculpt->pbvh; + SubdivCCG *subdiv_ccg = mesh->runtime.subdiv_ccg; + if (pbvh == NULL || subdiv_ccg == NULL) { + return; + } + struct CCGFace **faces; + int num_faces; + BKE_pbvh_get_grid_updates(pbvh, 1, (void ***)&faces, &num_faces); + if (num_faces > 0) { + BKE_subdiv_ccg_update_normals(subdiv_ccg, faces, num_faces); + } +} + /* Add geometry to shadingGroups. Execute for each objects */ static void SCULPT_cache_populate(void *vedata, Object *ob) { @@ -166,6 +183,8 @@ static void SCULPT_cache_populate(void *vedata, Object *ob) const DRWContextState *draw_ctx = DRW_context_state_get(); if (ob->sculpt && (ob == draw_ctx->obact)) { + sculpt_update_pbvh_normals(ob); + /* XXX, needed for dyntopo-undo (which clears). * probably depsgraph should handlle? in 2.7x getting derived-mesh does this (mesh_build_data) */ if (ob->sculpt->pbvh == NULL) { |