Multires: Initial work to get sculpting to work with OpenSubdiv

Allows to go to sculpt mode, do brush strokes, get out of sculpt mode
and have deformation preserved.

The issues currently is that the current implementation of CCG
storage is created from the limit surface, without displacement
taken into account. It is trivial to get displaced coordinates,
but it is more tricky to get displaced normals. This is something
to be solved next.

Another limitation is that this only works for sculpting at a maximal
multires level. There is code to be done to support propagation
of displacement onto a higher levels.
This commit is contained in:
Sergey Sharybin 2018-09-14 10:56:54 +02:00
parent 744233f207
commit 8e8952b7e3
Notes: blender-bot 2023-02-14 08:28:46 +01:00
Referenced by issue #57255, Blender crash during the sculpting (multires & smooth)
10 changed files with 310 additions and 62 deletions

View File

@ -33,6 +33,7 @@
*/
enum MultiresModifiedFlags;
struct Depsgraph;
struct DerivedMesh;
struct MDisps;
@ -42,6 +43,7 @@ struct Multires;
struct MultiresModifierData;
struct Object;
struct Scene;
struct SubdivCCG;
struct MLoop;
struct MVert;
@ -126,6 +128,9 @@ bool multiresModifier_reshapeFromDeformModifier(
struct MultiresModifierData *mmd,
struct Object *ob,
struct ModifierData *md);
bool multiresModifier_reshapeFromCCG(
struct Object *dst,
struct SubdivCCG *subdiv_ccg);
/* Subdivision integration, defined in multires_subdiv.c */

View File

@ -56,6 +56,7 @@ struct Scene;
struct ViewLayer;
struct Sculpt;
struct StrokeCache;
struct SubdivCCG;
struct Tex;
struct ImagePool;
struct UnifiedPaintSettings;
@ -198,6 +199,9 @@ typedef struct SculptSession {
/* Undo/redo log for dynamic topology sculpting */
struct BMLog *bm_log;
/* Limit surface/grids. */
struct SubdivCCG *subdiv_ccg;
/* PBVH acceleration structure */
struct PBVH *pbvh;
bool show_diffuse_color;

View File

@ -155,6 +155,8 @@ int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden,
/* multires level, only valid for type == PBVH_GRIDS */
void BKE_pbvh_get_grid_key(const PBVH *pbvh, struct CCGKey *key);
struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh, int *num_grids);
/* Only valid for type == PBVH_BMESH */
struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh);
void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size);

View File

@ -114,6 +114,17 @@ typedef struct SubdivCCG {
/* TODO(sergey): Consider adding CD layers here, so we can draw final mesh
* from grids, and have UVs and such work.
*/
/* Integration with sculpting. */
/* TODO(sergey): Is this really best way to go? Kind of annoying to have
* such use-related flags in a more or less generic structure.
*/
struct {
/* Corresponds to MULTIRES_COORDS_MODIFIED. */
bool coords;
/* Corresponds to MULTIRES_HIDDEN_MODIFIED. */
bool hidden;
} dirty;
} SubdivCCG;
/* Create real hi-res CCG from subdivision.

View File

@ -57,6 +57,7 @@
#include "BKE_multires.h"
#include "BKE_paint.h"
#include "BKE_scene.h"
#include "BKE_subdiv_ccg.h"
#include "BKE_subsurf.h"
#include "BKE_editmesh.h"
@ -389,21 +390,46 @@ static void multires_dm_mark_as_modified(DerivedMesh *dm, MultiresModifiedFlags
ccgdm->multires.modified_flags |= flags;
}
static void multires_ccg_mark_as_modified(SubdivCCG *subdiv_ccg,
MultiresModifiedFlags flags)
{
if (flags & MULTIRES_COORDS_MODIFIED) {
subdiv_ccg->dirty.coords = true;
}
if (flags & MULTIRES_HIDDEN_MODIFIED) {
subdiv_ccg->dirty.hidden = true;
}
}
void multires_mark_as_modified(Object *ob, MultiresModifiedFlags flags)
{
if (ob && ob->derivedFinal)
multires_dm_mark_as_modified(ob->derivedFinal, flags);
if (ob == NULL) {
return;
}
Mesh *mesh = ob->data;
SubdivCCG *subdiv_ccg = mesh->runtime.subsurf_ccg;
if (subdiv_ccg == NULL) {
return;
}
multires_ccg_mark_as_modified(subdiv_ccg, flags);
}
void multires_force_update(Object *ob)
{
if (ob) {
BKE_object_free_derived_caches(ob);
if (ob->sculpt && ob->sculpt->pbvh) {
BKE_pbvh_free(ob->sculpt->pbvh);
ob->sculpt->pbvh = NULL;
if (ob == NULL) {
return;
}
if (ob->sculpt && ob->sculpt->pbvh) {
PBVH *pbvh = ob->sculpt->pbvh;
if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) {
multiresModifier_reshapeFromCCG(ob, ob->sculpt->subdiv_ccg);
}
else {
/* NOTE: Disabled for until OpenSubdiv is enabled by default. */
// BLI_assert(!"multires_force_update is used on non-grids PBVH");
}
BKE_pbvh_free(pbvh);
ob->sculpt->pbvh = NULL;
}
}

View File

@ -35,13 +35,16 @@
#include "BLI_utildefines.h"
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "BKE_ccg.h"
#include "BKE_library.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_ccg.h"
#include "BKE_subdiv_eval.h"
#include "BKE_subdiv_foreach.h"
#include "BKE_subdiv_mesh.h"
@ -126,8 +129,23 @@ BLI_INLINE void construct_tangent_matrix(float tangent_matrix[3][3],
normalize_v3(tangent_matrix[2]);
}
static void multires_reshape_init_mmd(
MultiresModifierData *reshape_mmd,
const MultiresModifierData *mmd)
{
*reshape_mmd = *mmd;
}
static void multires_reshape_init_mmd_top_level(
MultiresModifierData *reshape_mmd,
const MultiresModifierData *mmd)
{
*reshape_mmd = *mmd;
reshape_mmd->lvl = reshape_mmd->totlvl;
}
/* =============================================================================
* Reshape internal functionality.
* General reshape implementaiton, reused by all particular cases.
*/
typedef struct MultiresReshapeContext {
@ -135,25 +153,10 @@ typedef struct MultiresReshapeContext {
Object *object;
const Mesh *coarse_mesh;
MDisps *mdisps;
const float (*deformed_verts)[3];
int num_deformed_verts;
/* NOTE: This is a grid size on th top level. */
int grid_size;
} MultiresReshapeContext;
static bool multires_reshape_topology_info(
const SubdivForeachContext *foreach_context,
const int num_vertices,
const int UNUSED(num_edges),
const int UNUSED(num_loops),
const int UNUSED(num_polygons))
{
MultiresReshapeContext *ctx = foreach_context->user_data;
if (num_vertices != ctx->num_deformed_verts) {
return false;
}
return true;
}
static void multires_reshape_vertex_copy_to_next(
MultiresReshapeContext *ctx,
const MPoly *coarse_poly,
@ -238,13 +241,13 @@ static void copy_boundary_displacement(
}
}
static void multires_reshape_vertex(
static void multires_reshape_vertex_from_final_coord(
MultiresReshapeContext *ctx,
const int ptex_face_index,
const float u, const float v,
const int coarse_poly_index,
const int coarse_corner,
const int subdiv_vertex_index)
const float final_P[3])
{
Subdiv *subdiv = ctx->subdiv;
const int grid_size = ctx->grid_size;
@ -273,7 +276,6 @@ static void multires_reshape_vertex(
ptex_uv_to_grid_uv(u, v, &grid_u, &grid_v);
}
/* Convert object coordinate to a tangent space of displacement grid. */
const float *final_P = ctx->deformed_verts[subdiv_vertex_index];
float D[3];
sub_v3_v3v3(D, final_P, P);
float tangent_matrix[3][3];
@ -292,6 +294,47 @@ static void multires_reshape_vertex(
ctx, coarse_poly, face_corner, grid_x, grid_y, displacement_grid);
}
/* =============================================================================
* Reshape from deformed veretx coordinates.
*/
typedef struct MultiresReshapeFromDeformedVertsContext {
MultiresReshapeContext reshape_ctx;
const float (*deformed_verts)[3];
int num_deformed_verts;
} MultiresReshapeFromDeformedVertsContext;
static bool multires_reshape_topology_info(
const SubdivForeachContext *foreach_context,
const int num_vertices,
const int UNUSED(num_edges),
const int UNUSED(num_loops),
const int UNUSED(num_polygons))
{
MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
if (num_vertices != ctx->num_deformed_verts) {
return false;
}
return true;
}
static void multires_reshape_vertex(
MultiresReshapeFromDeformedVertsContext *ctx,
const int ptex_face_index,
const float u, const float v,
const int coarse_poly_index,
const int coarse_corner,
const int subdiv_vertex_index)
{
const float *final_P = ctx->deformed_verts[subdiv_vertex_index];
multires_reshape_vertex_from_final_coord(
&ctx->reshape_ctx,
ptex_face_index, u, v,
coarse_poly_index,
coarse_corner,
final_P);
}
static void multires_reshape_vertex_inner(
const SubdivForeachContext *foreach_context,
void *UNUSED(tls_v),
@ -301,7 +344,7 @@ static void multires_reshape_vertex_inner(
const int coarse_corner,
const int subdiv_vertex_index)
{
MultiresReshapeContext *ctx = foreach_context->user_data;
MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
multires_reshape_vertex(
ctx,
ptex_face_index, u, v,
@ -320,7 +363,7 @@ static void multires_reshape_vertex_every_corner(
const int coarse_corner,
const int subdiv_vertex_index)
{
MultiresReshapeContext *ctx = foreach_context->user_data;
MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
multires_reshape_vertex(
ctx,
ptex_face_index, u, v,
@ -339,7 +382,7 @@ static void multires_reshape_vertex_every_edge(
const int coarse_corner,
const int subdiv_vertex_index)
{
MultiresReshapeContext *ctx = foreach_context->user_data;
MultiresReshapeFromDeformedVertsContext *ctx = foreach_context->user_data;
multires_reshape_vertex(
ctx,
ptex_face_index, u, v,
@ -376,58 +419,46 @@ static bool multires_reshape_from_vertcos(
{
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Mesh *coarse_mesh = object->data;
MultiresReshapeContext ctx = {
.object = object,
.coarse_mesh = coarse_mesh,
MDisps *mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS);
MultiresReshapeFromDeformedVertsContext reshape_deformed_verts_ctx = {
.reshape_ctx = {
.object = object,
.coarse_mesh = coarse_mesh,
.mdisps = mdisps,
.grid_size = (1 << (mmd->totlvl - 1)) + 1,
},
.deformed_verts = deformed_verts,
.mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS),
.num_deformed_verts = num_deformed_verts,
.grid_size = (1 << (mmd->totlvl - 1)) + 1,
};
SubdivForeachContext foreach_context = {
.topology_info = multires_reshape_topology_info,
.vertex_inner = multires_reshape_vertex_inner,
.vertex_every_edge = multires_reshape_vertex_every_edge,
.vertex_every_corner = multires_reshape_vertex_every_corner,
.user_data = &ctx,
.user_data = &reshape_deformed_verts_ctx,
};
/* Initialize subdivision surface. */
ctx.subdiv = multires_subdiv_for_reshape(depsgraph, object, mmd);
if (ctx.subdiv == NULL) {
Subdiv *subdiv = multires_subdiv_for_reshape(depsgraph, object, mmd);
if (subdiv == NULL) {
return false;
}
reshape_deformed_verts_ctx.reshape_ctx.subdiv = subdiv;
/* Initialize mesh rasterization settings. */
SubdivToMeshSettings mesh_settings;
BKE_multires_subdiv_mesh_settings_init(
&mesh_settings, scene_eval, object, mmd, use_render_params, true);
/* Run all the callbacks. */
BKE_subdiv_foreach_subdiv_geometry(
ctx.subdiv,
subdiv,
&foreach_context,
&mesh_settings,
coarse_mesh);
BKE_subdiv_free(ctx.subdiv);
BKE_subdiv_free(subdiv);
return true;
}
static void multires_reshape_init_mmd(MultiresModifierData *reshape_mmd,
const MultiresModifierData *mmd)
{
/* It is possible that the current subdivision level of multires is lower
* that it's maximum possible one (i.e., viewport is set to a lower level
* for the performance purposes). But even then, we want all the multires
* levels to be reshaped. Most accurate way to do so is to ignore all
* simplifications and calculate deformation modifier for the highest
* possible multires level.
* Alternative would be propagate displacement from current level to a
* higher ones, but that is likely to cause artifacts.
*/
*reshape_mmd = *mmd;
reshape_mmd->lvl = reshape_mmd->totlvl;
}
/* =============================================================================
* Public entry points.
* Reshape from object.
*/
/* Returns truth on success, false otherwise.
@ -469,6 +500,10 @@ bool multiresModifier_reshapeFromObject(
return result;
}
/* =============================================================================
* Reshape from modifier.
*/
bool multiresModifier_reshapeFromDeformModifier(
struct Depsgraph *depsgraph,
MultiresModifierData *mmd,
@ -476,7 +511,16 @@ bool multiresModifier_reshapeFromDeformModifier(
ModifierData *md)
{
MultiresModifierData highest_mmd;
multires_reshape_init_mmd(&highest_mmd, mmd);
/* It is possible that the current subdivision level of multires is lower
* that it's maximum possible one (i.e., viewport is set to a lower level
* for the performance purposes). But even then, we want all the multires
* levels to be reshaped. Most accurate way to do so is to ignore all
* simplifications and calculate deformation modifier for the highest
* possible multires level.
* Alternative would be propagate displacement from current level to a
* higher ones, but that is likely to cause artifacts.
*/
multires_reshape_init_mmd_top_level(&highest_mmd, mmd);
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
/* Perform sanity checks and early output. */
if (multires_get_level(
@ -513,3 +557,142 @@ bool multiresModifier_reshapeFromDeformModifier(
MEM_freeN(deformed_verts);
return result;
}
/* =============================================================================
* Reshape from grids.
*/
typedef struct ReshapeFromCCGTaskData {
MultiresReshapeContext reshape_ctx;
int *face_ptex_offset;
const CCGKey *key;
/*const*/ CCGElem **grids;
} ReshapeFromCCGTaskData;
static void reshape_from_ccg_regular_face(ReshapeFromCCGTaskData *data,
const MPoly *coarse_poly)
{
const CCGKey *key = data->key;
/*const*/ CCGElem **grids = data->grids;
const Mesh *coarse_mesh = data->reshape_ctx.coarse_mesh;
const MPoly *coarse_mpoly = coarse_mesh->mpoly;
const int grid_size = data->reshape_ctx.grid_size;
const int grid_size_1 = grid_size - 1;
const int resolution = 2 * grid_size - 1;
const float resolution_1_inv = 1.0f / (float)(resolution - 1);
const int coarse_poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_index = data->face_ptex_offset[coarse_poly_index];
for (int y = 0; y < resolution; y++) {
const float v = y * resolution_1_inv;
for (int x = 0; x < resolution; x++) {
const float u = x * resolution_1_inv;
float corner_u, corner_v;
float grid_u, grid_v;
const int face_corner = rotate_quad_to_corner(
u, v, &corner_u, &corner_v);
ptex_uv_to_grid_uv(corner_u, corner_v, &grid_u, &grid_v);
/*const*/ CCGElem *grid =
grids[coarse_poly->loopstart + face_corner];
/*const*/ CCGElem *grid_element = CCG_grid_elem(
key, grid, grid_size_1 * grid_u, grid_size_1 * grid_v);
const float *final_P = CCG_elem_co(key, grid_element);
multires_reshape_vertex_from_final_coord(
&data->reshape_ctx,
ptex_face_index,
u, v,
coarse_poly_index,
0,
final_P);
}
}
}
static void reshape_from_ccg_special_face(ReshapeFromCCGTaskData *data,
const MPoly *coarse_poly)
{
const CCGKey *key = data->key;
/*const*/ CCGElem **grids = data->grids;
const Mesh *coarse_mesh = data->reshape_ctx.coarse_mesh;
const MPoly *coarse_mpoly = coarse_mesh->mpoly;
const int grid_size = data->reshape_ctx.grid_size;
const int grid_size_1 = grid_size - 1;
const int resolution = grid_size;
const float resolution_1_inv = 1.0f / (float)(resolution - 1);
const int coarse_poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_index = data->face_ptex_offset[coarse_poly_index];
for (int corner = 0; corner < coarse_poly->totloop; corner++) {
for (int y = 0; y < resolution; y++) {
const float v = y * resolution_1_inv;
for (int x = 0; x < resolution; x++) {
const float u = x * resolution_1_inv;
float grid_u, grid_v;
ptex_uv_to_grid_uv(u, v, &grid_u, &grid_v);
/*const*/ CCGElem *grid =
grids[coarse_poly->loopstart + corner];
/*const*/ CCGElem *grid_element = CCG_grid_elem(
key, grid, grid_size_1 * grid_u, grid_size_1 * grid_v);
const float *final_P = CCG_elem_co(key, grid_element);
multires_reshape_vertex_from_final_coord(
&data->reshape_ctx,
ptex_face_index + corner,
u, v,
coarse_poly_index,
corner,
final_P);
}
}
}
}
static void reshape_from_ccg_task(
void *__restrict userdata,
const int coarse_poly_index,
const ParallelRangeTLS *__restrict UNUSED(tls))
{
ReshapeFromCCGTaskData *data = userdata;
const Mesh *coarse_mesh = data->reshape_ctx.coarse_mesh;
const MPoly *coarse_mpoly = coarse_mesh->mpoly;
const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
if (coarse_poly->totloop == 4) {
reshape_from_ccg_regular_face(data, coarse_poly);
}
else {
reshape_from_ccg_special_face(data, coarse_poly);
}
}
bool multiresModifier_reshapeFromCCG(
Object *dst, SubdivCCG *subdiv_ccg)
{
Mesh *coarse_mesh = dst->data;
CCGKey key;
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
/* Sanity checks. */
if (coarse_mesh->totloop != subdiv_ccg->num_grids) {
/* Grids are supposed to eb created for each face-cornder (aka loop). */
return false;
}
MDisps *mdisps = CustomData_get_layer(&coarse_mesh->ldata, CD_MDISPS);
/* XXX: Key has grid size for the current level. Need to access top
* top level somehow.
*/
Subdiv *subdiv = subdiv_ccg->subdiv;
ReshapeFromCCGTaskData data = {
.reshape_ctx = {
.subdiv = subdiv,
.object = dst,
.coarse_mesh = coarse_mesh,
.mdisps = mdisps,
.grid_size = key.grid_size},
.face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv),
.key = &key,
.grids = subdiv_ccg->grids};
/* Threaded grids iteration. */
ParallelRangeSettings parallel_range_settings;
BLI_parallel_range_settings_defaults(&parallel_range_settings);
BLI_task_parallel_range(0, coarse_mesh->totpoly,
&data,
reshape_from_ccg_task,
&parallel_range_settings);
return true;
}

View File

@ -943,6 +943,8 @@ void BKE_sculpt_update_mesh_elements(
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
}
ss->subdiv_ccg = me_eval->runtime.subsurf_ccg;
PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
BLI_assert(pbvh == ss->pbvh);
UNUSED_VARS_NDEBUG(pbvh);

View File

@ -1341,6 +1341,11 @@ void BKE_pbvh_get_grid_key(const PBVH *bvh, CCGKey *key)
*key = bvh->gridkey;
}
struct CCGElem **BKE_pbvh_get_grids(const PBVH *bvh, int *num_grids) {
BLI_assert(bvh->type == PBVH_GRIDS);
*num_grids = bvh->totgrid;
return bvh->grids;
}
BMesh *BKE_pbvh_get_bmesh(PBVH *bvh)
{

View File

@ -74,6 +74,7 @@
#include "BKE_colortools.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "WM_api.h"
#include "WM_types.h"
@ -4868,8 +4869,12 @@ static void sculpt_flush_update(bContext *C)
ARegion *ar = CTX_wm_region(C);
MultiresModifierData *mmd = ss->multires;
if (mmd)
multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
if (mmd != NULL) {
/* NOTE: SubdivCCG is living in the evaluated object. */
Depsgraph *depsgraph = CTX_data_depsgraph(C);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
multires_mark_as_modified(ob_eval, MULTIRES_COORDS_MODIFIED);
}
DEG_id_tag_update(&ob->id, DEG_TAG_SHADING_UPDATE);

View File

@ -200,6 +200,7 @@ static Mesh *multires_as_ccg(MultiresModifierData *mmd,
if (ccg_settings.resolution < 3) {
return result;
}
BKE_subdiv_displacement_attach_from_multires(subdiv, mesh, mmd);
result = BKE_subdiv_to_ccg_mesh(subdiv, &ccg_settings, mesh);
return result;
}
@ -226,7 +227,11 @@ static Mesh *applyModifier_subdiv(ModifierData *md,
* a wrong impression that things do work, even though crucial areas are
* still missing in implementation.
*/
if ((ctx->object->mode & OB_MODE_SCULPT) && G.debug_value == 128) {
const bool for_orco = (ctx->flag & MOD_APPLY_ORCO) != 0;
if ((ctx->object->mode & OB_MODE_SCULPT) &&
G.debug_value == 128 &&
!for_orco)
{
/* NOTE: CCG takes ownership over Subdiv. */
result = multires_as_ccg(mmd, ctx, mesh, subdiv);
// BKE_subdiv_stats_print(&subdiv->stats);