Multires: Support smooth shading when sculpting

On CCG side it is done similar to displacement, where we have
a dedicated functor which evaluates displacement. Might be seemed
as an overkill, but allows to decouple SubdivCCG from mesh entirely,
and maybe even free up coarse mesh in order to save some memory.

Some weak-looking aspect is the call to update normals from the
draw manager. Ideally, the manager will only draw what is already
evaluated. But it's a bit tricky to find a best place for this since
we avoid dependency graph updates during sculpt as much as possible.
The new code mimics the old code, this is how it was in 2.7.

Fix shading part of T58307.
This commit is contained in:
Sergey Sharybin 2019-02-22 16:56:54 +01:00
parent b6c61945ae
commit 3b132778de
7 changed files with 262 additions and 33 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -40,6 +40,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(&parallel_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(&parallel_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,
&parallel_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.
*/

View File

@ -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 =

View File

@ -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;
}

View File

@ -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) {