[#22262] Sculpting shape keys using the Smooth brush switches the shape to the Basis

PBVH used the same verts array as mesh data and shape key/reference key coords
were applying on the mesh data, so on some refreshing undeformed mesh was
displayed.
Added utility functions to get vert coords from key block, apply new vert coords
on keyblock and function to apply coords on bpvh, so now pbvh uses it's ovn
vertex array and no changes are making to the mesh data.

Additional change:
Store key block name in SculptUndoNode, so now shape wouldn't be copied to
wrong keyblock on undo
This commit is contained in:
Sergey Sharybin 2010-06-21 20:10:59 +00:00
parent 72d21c35ad
commit 425da6206f
9 changed files with 326 additions and 57 deletions

@ -75,6 +75,8 @@ void key_to_latt(struct KeyBlock *kb, struct Lattice *lt);
void latt_to_key(struct Lattice *lt, struct KeyBlock *kb);
void key_to_curve(struct KeyBlock *kb, struct Curve *cu, struct ListBase *nurb);
void curve_to_key(struct Curve *cu, struct KeyBlock *kb, struct ListBase *nurb);
float (*key_to_vertcos(struct Object *ob, struct KeyBlock *kb))[3];
void vertcos_to_key(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]);
#ifdef __cplusplus
};

@ -70,7 +70,7 @@ typedef struct SculptSession {
int totvert, totface;
float *face_normals;
struct Object *ob;
struct KeyBlock *kb, *refkb;
struct KeyBlock *kb;
/* Mesh connectivity */
struct ListBase *fmap;
@ -94,6 +94,8 @@ typedef struct SculptSession {
struct StrokeCache *cache;
struct GPUDrawObject *drawobject;
int modifiers_active;
} SculptSession;
void free_sculptsession(struct Object *ob);

@ -186,6 +186,16 @@ static ListBase *cdDM_getFaceMap(Object *ob, DerivedMesh *dm)
return cddm->fmap;
}
static int can_pbvh_draw(Object *ob, DerivedMesh *dm)
{
CDDerivedMesh *cddm = (CDDerivedMesh*) dm;
Mesh *me= (ob)? ob->data: NULL;
if(ob->sculpt->modifiers_active) return 0;
return (cddm->mvert == me->mvert) || ob->sculpt->kb;
}
static struct PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
{
CDDerivedMesh *cddm = (CDDerivedMesh*) dm;
@ -200,7 +210,7 @@ static struct PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
return NULL;
if(ob->sculpt->pbvh) {
cddm->pbvh= ob->sculpt->pbvh;
cddm->pbvh_draw = (cddm->mvert == me->mvert) || ob->sculpt->kb;
cddm->pbvh_draw = can_pbvh_draw(ob, dm);
}
/* always build pbvh from original mesh, and only use it for drawing if
@ -208,7 +218,7 @@ static struct PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
that this is actually for, to support a pbvh on a modified mesh */
if(!cddm->pbvh && ob->type == OB_MESH) {
cddm->pbvh = BLI_pbvh_new();
cddm->pbvh_draw = (cddm->mvert == me->mvert) || ob->sculpt->kb;
cddm->pbvh_draw = can_pbvh_draw(ob, dm);
BLI_pbvh_build_mesh(cddm->pbvh, me->mface, me->mvert,
me->totface, me->totvert);
}

@ -2286,9 +2286,6 @@ void DAG_id_flush_update(ID *id, short flag)
/* no point in trying in this cases */
if(!id || id->us <= 1)
id= NULL;
/* for locked shape keys we make an exception */
else if(ob_get_key(ob) && (ob->shapeflag & OB_SHAPE_LOCK))
id= NULL;
}
}

@ -38,6 +38,7 @@
#include "BLI_blenlib.h"
#include "BLI_editVert.h"
#include "BLI_math_vector.h"
#include "DNA_anim_types.h"
#include "DNA_key_types.h"
@ -1719,3 +1720,151 @@ void key_to_mesh(KeyBlock *kb, Mesh *me)
VECCOPY(mvert->co, fp);
}
}
/************************* vert coords ************************/
float (*key_to_vertcos(Object *ob, KeyBlock *kb))[3]
{
float (*vertCos)[3], *co;
float *fp= kb->data;
int tot= 0, a;
/* Count of vertex coords in array */
if(ob->type == OB_MESH) {
Mesh *me= (Mesh*)ob->data;
tot= me->totvert;
} else if(ob->type == OB_LATTICE) {
Lattice *lt= (Lattice*)ob->data;
tot= lt->pntsu*lt->pntsv*lt->pntsw;
} else if(ELEM(ob->type, OB_CURVE, OB_SURF)) {
Curve *cu= (Curve*)ob->data;
tot= count_curveverts(&cu->nurb);
}
if (tot == 0) return NULL;
vertCos= MEM_callocN(tot*sizeof(*vertCos), "key_to_vertcos vertCos");
/* Copy coords to array */
co= (float*)vertCos;
if(ELEM(ob->type, OB_MESH, OB_LATTICE)) {
for (a= 0; a<tot; a++, fp+=3, co+=3) {
copy_v3_v3(co, fp);
}
} else if(ELEM(ob->type, OB_CURVE, OB_SURF)) {
Curve *cu= (Curve*)ob->data;
Nurb *nu= cu->nurb.first;
BezTriple *bezt;
BPoint *bp;
while (nu) {
if(nu->bezt) {
int i;
bezt= nu->bezt;
a= nu->pntsu;
while (a--) {
for (i= 0; i<3; i++) {
copy_v3_v3(co, fp);
fp+= 3; co+= 3;
}
fp+= 3; /* skip alphas */
bezt++;
}
}
else {
bp= nu->bp;
a= nu->pntsu*nu->pntsv;
while (a--) {
copy_v3_v3(co, fp);
fp+= 4;
co+= 3;
bp++;
}
}
nu= nu->next;
}
}
return vertCos;
}
void vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
{
float *co= (float*)vertCos, *fp;
int tot= 0, a, elemsize;
if (kb->data) MEM_freeN(kb->data);
/* Count of vertex coords in array */
if(ob->type == OB_MESH) {
Mesh *me= (Mesh*)ob->data;
tot= me->totvert;
elemsize= me->key->elemsize;
} else if(ob->type == OB_LATTICE) {
Lattice *lt= (Lattice*)ob->data;
tot= lt->pntsu*lt->pntsv*lt->pntsw;
elemsize= lt->key->elemsize;
} else if(ELEM(ob->type, OB_CURVE, OB_SURF)) {
Curve *cu= (Curve*)ob->data;
elemsize= cu->key->elemsize;
tot= count_curveverts(&cu->nurb);
}
fp= kb->data= MEM_callocN(tot*elemsize, "key_to_vertcos vertCos");
if (tot == 0) return;
/* Copy coords to keyblock */
if(ELEM(ob->type, OB_MESH, OB_LATTICE)) {
for (a= 0; a<tot; a++, fp+=3, co+=3) {
copy_v3_v3(fp, co);
}
} else if(ELEM(ob->type, OB_CURVE, OB_SURF)) {
Curve *cu= (Curve*)ob->data;
Nurb *nu= cu->nurb.first;
BezTriple *bezt;
BPoint *bp;
while (nu) {
if(nu->bezt) {
int i;
bezt= nu->bezt;
a= nu->pntsu;
while (a--) {
for (i= 0; i<3; i++) {
copy_v3_v3(fp, co);
fp+= 3; co+= 3;
}
fp+= 3; /* skip alphas */
bezt++;
}
}
else {
bp= nu->bp;
a= nu->pntsu*nu->pntsv;
while (a--) {
copy_v3_v3(fp, co);
fp+= 4;
co+= 3;
bp++;
}
}
nu= nu->next;
}
}
}

@ -114,6 +114,12 @@ void BLI_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *tot
void BLI_pbvh_grids_update(PBVH *bvh, struct DMGridData **grids,
struct DMGridAdjacency *gridadj, void **gridfaces);
/* vertex deformer */
float (*BLI_pbvh_get_vertCos(struct PBVH *pbvh))[3];
void BLI_pbvh_apply_vertCos(struct PBVH *pbvh, float (*vertCos)[3]);
int BLI_pbvh_isDeformed(struct PBVH *pbvh);
/* Vertex Iterator */
/* this iterator has quite a lot of code, but it's designed to:

@ -29,6 +29,7 @@
#include "BLI_pbvh.h"
#include "BKE_DerivedMesh.h"
#include "BKE_mesh.h" /* for mesh_calc_normals */
#include "gpu_buffers.h"
@ -120,6 +121,9 @@ struct PBVH {
#ifdef PERFCNTRS
int perf_modified;
#endif
/* flag are verts/faces deformed */
int deformed;
};
#define STACK_FIXED_DEPTH 100
@ -576,6 +580,15 @@ void BLI_pbvh_free(PBVH *bvh)
}
}
if (bvh->deformed) {
if (bvh->verts) {
/* if pbvh was deformed, new memory was allocated for verts/faces -- free it */
MEM_freeN(bvh->verts);
MEM_freeN(bvh->faces);
}
}
MEM_freeN(bvh->nodes);
MEM_freeN(bvh->prim_indices);
MEM_freeN(bvh);
@ -1330,3 +1343,55 @@ void BLI_pbvh_grids_update(PBVH *bvh, DMGridData **grids, DMGridAdjacency *grida
bvh->gridfaces= gridfaces;
}
float (*BLI_pbvh_get_vertCos(PBVH *pbvh))[3]
{
int a;
float (*vertCos)[3]= NULL;
if (pbvh->verts) {
float *co;
MVert *mvert= pbvh->verts;
vertCos= MEM_callocN(3*pbvh->totvert*sizeof(float), "BLI_pbvh_get_vertCoords");
co= (float*)vertCos;
for (a= 0; a<pbvh->totvert; a++, mvert++, co+= 3) {
copy_v3_v3(co, mvert->co);
}
}
return vertCos;
}
void BLI_pbvh_apply_vertCos(PBVH *pbvh, float (*vertCos)[3])
{
int a;
if (!pbvh->deformed) {
if (pbvh->verts) {
/* if pbvh is not already deformed, verts/faces points to the */
/* original data and applying new coords to this arrays would lead to */
/* unneeded deformation -- duplicate verts/faces to avoid this */
pbvh->verts= MEM_dupallocN(pbvh->verts);
pbvh->faces= MEM_dupallocN(pbvh->faces);
pbvh->deformed= 1;
}
}
if (pbvh->verts) {
/* copy new verts coords */
for (a= 0; a < pbvh->totvert; ++a) {
copy_v3_v3(pbvh->verts[a].co, vertCos[a]);
}
/* coordinates are new -- normals should also be updated */
mesh_calc_normals(pbvh->verts, pbvh->totvert, pbvh->faces, pbvh->totprim, NULL);
}
}
int BLI_pbvh_isDeformed(PBVH *pbvh)
{
return pbvh->deformed;
}

@ -275,6 +275,9 @@ typedef struct SculptUndoNode {
/* layer brush */
float *layer_disp;
/* shape keys */
char *shapeName[32]; /* keep size in sync with keyblock dna */
} SculptUndoNode;
static void update_cb(PBVHNode *node, void *data)
@ -326,16 +329,53 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
continue;
if(unode->maxvert) {
char *shapeName= (char*)unode->shapeName;
/* regular mesh restore */
if(ss->totvert != unode->maxvert)
continue;
if (ss->kb && strcmp(ss->kb->name, shapeName)) {
/* shape key has been changed before calling undo operator */
Key *key= ob_get_key(ob);
KeyBlock *kb= key_get_named_keyblock(key, shapeName);
if (kb) {
ob->shapenr= BLI_findindex(&key->block, kb) + 1;
ob->shapeflag|= OB_SHAPE_LOCK;
sculpt_update_mesh_elements(scene, ob, 0);
WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob);
} else {
/* key has been removed -- skip this undo node */
continue;
}
}
index= unode->index;
mvert= ss->mvert;
for(i=0; i<unode->totvert; i++) {
swap_v3_v3(mvert[index[i]].co, unode->co[i]);
mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
if (ss->kb) {
float (*vertCos)[3];
vertCos= key_to_vertcos(ob, ss->kb);
for(i=0; i<unode->totvert; i++)
swap_v3_v3(vertCos[index[i]], unode->co[i]);
/* propagate new coords to keyblock */
vertcos_to_key(ob, ss->kb, vertCos);
/* pbvh uses it's own mvert array, so coords should be */
/* propagated to pbvh here */
BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
MEM_freeN(vertCos);
} else {
for(i=0; i<unode->totvert; i++) {
swap_v3_v3(mvert[index[i]].co, unode->co[i]);
mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
else if(unode->maxgrid && dm->getGridData) {
@ -365,9 +405,6 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
}
if(update) {
if(ss->kb) sculpt_mesh_to_key(ss->ob, ss->kb);
if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob);
/* we update all nodes still, should be more clever, but also
needs to work correct when exiting/entering sculpt mode and
the nodes get recreated, though in that case it could do all */
@ -377,7 +414,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
if((mmd=sculpt_multires_active(scene, ob)))
multires_mark_as_modified(ob);
if(sculpt_modifiers_active(scene, ob))
if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1)
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
}
}
@ -476,6 +513,10 @@ static SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node)
if(unode->grids)
memcpy(unode->grids, grids, sizeof(int)*totgrid);
/* store active shape key */
if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name));
else unode->shapeName[0]= '\0';
return unode;
}
@ -1367,6 +1408,17 @@ static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **node
}
}
/* copy the modified vertices from bvh to the active key */
static void sculpt_update_keyblock(SculptSession *ss)
{
float (*vertCos)[3]= BLI_pbvh_get_vertCos(ss->pbvh);
if (vertCos) {
vertcos_to_key(ss->ob, ss->kb, vertCos);
MEM_freeN(vertCos);
}
}
static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
{
SculptSearchSphereData data;
@ -1425,8 +1477,10 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
break;
}
/* copy the modified vertices from mesh to the active key */
if(ss->kb) mesh_to_key(ss->ob->data, ss->kb);
/* optimization: we could avoid copying new coords to keyblock at each */
/* stroke step if there are no modifiers due to pbvh is used for displaying */
/* so to increase speed we'll copy new coords to keyblock when stroke is done */
if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss);
if(nodes)
MEM_freeN(nodes);
@ -1506,40 +1560,18 @@ struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob)
return NULL;
}
void sculpt_key_to_mesh(KeyBlock *kb, Object *ob)
{
Mesh *me= ob->data;
key_to_mesh(kb, me);
mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
}
void sculpt_mesh_to_key(Object *ob, KeyBlock *kb)
{
Mesh *me= ob->data;
mesh_to_key(me, kb);
}
void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap)
{
DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
SculptSession *ss = ob->sculpt;
MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
ss->ob= ob;
if((ob->shapeflag & OB_SHAPE_LOCK) && !mmd) {
ss->kb= ob_get_keyblock(ob);
ss->refkb= ob_get_reference_keyblock(ob);
}
else {
ss->kb= NULL;
ss->refkb= NULL;
}
ss->modifiers_active= sculpt_modifiers_active(scene, ob);
/* need to make PBVH with shape key coordinates */
if(ss->kb) sculpt_key_to_mesh(ss->kb, ss->ob);
if((ob->shapeflag & OB_SHAPE_LOCK) && !mmd) ss->kb= ob_get_keyblock(ob);
else ss->kb= NULL;
if(mmd) {
ss->multires = mmd;
@ -1561,6 +1593,17 @@ void sculpt_update_mesh_elements(Scene *scene, Object *ob, int need_fmap)
ss->pbvh = dm->getPBVH(ob, dm);
ss->fmap = (need_fmap && dm->getFaceMap)? dm->getFaceMap(ob, dm): NULL;
/* if pbvh is deformed, key block is already applied to it */
if (ss->kb && !BLI_pbvh_isDeformed(ss->pbvh)) {
float (*vertCos)[3]= key_to_vertcos(ob, ss->kb);
if (vertCos) {
/* apply shape keys coordinates to PBVH */
BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
MEM_freeN(vertCos);
}
}
}
static int sculpt_mode_poll(bContext *C)
@ -1815,9 +1858,7 @@ static void sculpt_update_cache_variants(Sculpt *sd, SculptSession *ss, struct P
static void sculpt_stroke_modifiers_check(bContext *C, SculptSession *ss)
{
Scene *scene= CTX_data_scene(C);
if(sculpt_modifiers_active(scene, ss->ob)) {
if(ss->modifiers_active) {
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = paint_brush(&sd->paint);
@ -1995,7 +2036,6 @@ static void sculpt_restore_mesh(Sculpt *sd, SculptSession *ss)
static void sculpt_flush_update(bContext *C)
{
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
ARegion *ar = CTX_wm_region(C);
@ -2005,7 +2045,7 @@ static void sculpt_flush_update(bContext *C)
if(mmd)
multires_mark_as_modified(ob);
if(sculpt_modifiers_active(scene, ob)) {
if(ss->modifiers_active) {
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
ED_region_tag_redraw(ar);
}
@ -2089,7 +2129,10 @@ static void sculpt_stroke_done(bContext *C, struct PaintStroke *stroke)
BLI_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL);
if(ss->refkb) sculpt_key_to_mesh(ss->refkb, ob);
/* optimization: if there is locked key and active modifiers present in */
/* the stack, keyblock is updating at each step. otherwise we could update */
/* keyblock only when stroke is finished */
if(ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ss);
ss->partial_redraw = 0;
@ -2225,9 +2268,6 @@ static void sculpt_init_session(Scene *scene, Object *ob)
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
sculpt_update_mesh_elements(scene, ob, 0);
if(ob->sculpt->refkb)
sculpt_key_to_mesh(ob->sculpt->refkb, ob);
}
static int sculpt_toggle_mode(bContext *C, wmOperator *op)

@ -58,8 +58,6 @@ void sculpt(Sculpt *sd);
int sculpt_poll(struct bContext *C);
void sculpt_update_mesh_elements(struct Scene *scene, struct Object *ob, int need_fmap);
void sculpt_key_to_mesh(struct KeyBlock *kb, struct Object *ob);
void sculpt_mesh_to_key(struct Object *ob, struct KeyBlock *kb);
/* Stroke */
struct SculptStroke *sculpt_stroke_new(const int max);