Fix nasty edge case for BMLog.

This commit is contained in:
Joseph Eagar 2021-05-11 22:57:06 -07:00
parent b047b333b0
commit 582c30d32f
3 changed files with 167 additions and 56 deletions

View File

@ -166,6 +166,8 @@ typedef struct {
#define logkey_cmp BLI_ghashutil_intcmp
static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry);
static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry);
static void bm_log_entry_free(BMLogEntry *entry);
static void *log_ghash_lookup(BMLog *log, GHash *gh, const void *key)
@ -822,18 +824,11 @@ void BM_log_cleanup_entry(BMLogEntry *entry)
}
}
/* Allocate and initialize a new BMLog using existing BMLogEntries
*
* The 'entry' should be the last entry in the BMLog. Its prev pointer
* will be followed back to find the first entry.
*
* The unused IDs field of the log will be initialized by taking all
* keys from all GHashes in the log entry.
*/
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
BMLog *bm_log_from_existing_entries_create(BMesh *bm,
BMLog *log,
BMLogEntry *entry,
bool restore_ids)
{
BMLog *log = BM_log_create(bm, -1);
if (entry->prev) {
log->current_entry = entry;
}
@ -871,21 +866,46 @@ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
return log;
}
BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry)
/* Allocate and initialize a new BMLog using existing BMLogEntries
*
* The 'entry' should be the last entry in the BMLog. Its prev pointer
* will be followed back to find the first entry.
*
* The unused IDs field of the log will be initialized by taking all
* keys from all GHashes in the log entry.
*/
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
{
BMLog *log = BM_log_create(bm, -1);
bm_log_from_existing_entries_create(bm, log, entry, true);
return log;
}
ATTR_NO_OPT BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry)
{
if (!entry || !entry->log) {
return NULL;
}
if (!entry->log->frozen_full_mesh) {
return entry->log;
BMLogEntry *frozen = entry->log->frozen_full_mesh;
if (!frozen && entry->fully_copy) {
frozen = entry;
}
full_copy_swap(bm, entry->log, entry->log->frozen_full_mesh);
if (!frozen) {
return entry->log->bm == bm ? entry->log : NULL;
}
entry->log->frozen_full_mesh->log = NULL;
bm_log_entry_free(entry->log->frozen_full_mesh);
entry->log->frozen_full_mesh = NULL;
entry->log->bm = bm;
full_copy_load(bm, entry->log, frozen);
if (entry->log->frozen_full_mesh) {
entry->log->frozen_full_mesh->log = NULL;
bm_log_entry_free(entry->log->frozen_full_mesh);
entry->log->frozen_full_mesh = NULL;
}
return entry->log;
}
@ -898,11 +918,15 @@ void BM_log_free(BMLog *log, bool safe_mode)
BMLogEntry *entry;
if (safe_mode && log->refcount) {
if (!log->frozen_full_mesh) {
log->frozen_full_mesh = bm_log_entry_create();
bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh);
if (log->frozen_full_mesh) {
log->frozen_full_mesh->log = NULL;
bm_log_entry_free(log->frozen_full_mesh);
}
log->frozen_full_mesh = bm_log_entry_create();
bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh);
return;
}
@ -1158,6 +1182,63 @@ void BM_log_entry_drop(BMLogEntry *entry)
bm_log_entry_free(entry);
BLI_freelinkN(&log->entries, entry);
}
static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry)
{
CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0};
BM_mesh_clear(bm);
BM_mesh_bm_from_me(NULL,
bm,
entry->full_copy_mesh,
(&(struct BMeshFromMeshParams){.calc_face_normal = false,
.add_key_index = false,
.use_shapekey = false,
.active_shapekey = -1,
.cd_mask_extra = cd_mask_extra,
.copy_temp_cdlayers = true}));
bm->elem_index_dirty |= BM_VERT | BM_FACE;
BM_mesh_elem_table_ensure(bm, BM_VERT | BM_FACE);
BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE);
// restores ids
GHashIterator gi;
GHASH_ITER (gi, entry->full_copy_idmap) {
// uint id = POINTER_AS_UINT(BLI_ghashIterator_getKey(&gi));
uintptr_t id = (uintptr_t)BLI_ghashIterator_getKey(&gi);
uintptr_t key = (uintptr_t)BLI_ghashIterator_getValue(&gi);
uintptr_t idx = (key & ((1LL << 31LL) - 1LL));
uintptr_t type = key >> 31LL;
BMHeader *elem = NULL;
switch (type) {
case BM_VERT:
elem = &bm->vtable[idx]->head;
break;
case BM_FACE:
elem = &bm->ftable[idx]->head;
break;
}
if (elem) {
log_ghash_reinsert(log, log->id_to_elem, POINTER_FROM_UINT(id), elem, NULL, NULL);
log_ghash_reinsert(log, log->elem_to_id, elem, POINTER_FROM_UINT(id), NULL, NULL);
}
else {
// eek, error!
printf("bmlog error!\n");
log_ghash_remove(log, log->id_to_elem, (void *)id, NULL, NULL);
}
}
bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE;
bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE;
}
static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
{
CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0};

View File

@ -26,11 +26,11 @@
#include "BLI_alloca.h"
#include "BLI_array.h"
#include "BLI_blenlib.h"
#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_compiler_attrs.h"
#include "BLI_polyfill_2d.h"
#include "BLI_task.h"
@ -91,7 +91,7 @@ void SCULPT_dynamic_topology_triangulate(BMesh *bm)
BMIter iter;
BMFace *f;
BM_ITER_MESH(f, &iter, bm, BM_FACES_OF_MESH) {
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BM_elem_flag_enable(f, BM_ELEM_TAG);
}
@ -128,15 +128,15 @@ void SCULPT_dynamic_topology_triangulate(BMesh *bm)
for (int i = 0; i < faces_array_tot; i++) {
BMFace *f2 = faces_array[i];
//forcibly copy selection state
// forcibly copy selection state
if (sel) {
BM_face_select_set(bm, f2, true);
//restore original face selection state too, triangulate code unset it
// restore original face selection state too, triangulate code unset it
BM_face_select_set(bm, f, true);
}
//paranoia check that tag flag wasn't copied over
// paranoia check that tag flag wasn't copied over
BM_elem_flag_disable(f2, BM_ELEM_TAG);
}
}
@ -206,8 +206,10 @@ static char layer_id[] = "_dyntopo_node_id";
void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss)
{
SCULPT_dyntopo_node_layers_add(ss);
BKE_pbvh_update_offsets(
ss->pbvh, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_dyn_vert);
if (ss->pbvh) {
BKE_pbvh_update_offsets(
ss->pbvh, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_dyn_vert);
}
}
bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name)
@ -608,15 +610,26 @@ static void SCULPT_dynamic_topology_disable_ex(
/* Clear data. */
me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY;
bool disp_saved = false;
if (ss->bm_log) {
if (ss->bm) {
disp_saved = true;
// rebuild ss->persistent_base if necassary
SCULPT_dyntopo_save_persistent_base(ss);
}
BM_log_free(ss->bm_log, true);
ss->bm_log = NULL;
}
/* Typically valid but with global-undo they can be NULL, see: T36234. */
if (ss->bm) {
// rebuild ss->persistent_base if necassary
SCULPT_dyntopo_save_persistent_base(ss);
if (!disp_saved) {
// rebuild ss->persistent_base if necassary
SCULPT_dyntopo_save_persistent_base(ss);
}
BM_mesh_free(ss->bm);
ss->bm = NULL;

View File

@ -463,15 +463,21 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode)
me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* Restore the BMLog using saved entries. */
ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
ss->bm_log = BM_log_unfreeze(ss->bm, unode->bm_entry);
if (!ss->bm_log) {
/* Restore the BMLog using saved entries. */
ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
}
SCULPT_dyntopo_node_layers_update_offsets(ss);
BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
}
static void sculpt_undo_bmesh_restore_begin(bContext *C,
SculptUndoNode *unode,
Object *ob,
SculptSession *ss)
ATTR_NO_OPT static void sculpt_undo_bmesh_restore_begin(bContext *C,
SculptUndoNode *unode,
Object *ob,
SculptSession *ss)
{
if (unode->applied) {
SCULPT_dynamic_topology_disable(C, unode);
@ -487,10 +493,10 @@ static void sculpt_undo_bmesh_restore_begin(bContext *C,
}
}
static void sculpt_undo_bmesh_restore_end(bContext *C,
SculptUndoNode *unode,
Object *ob,
SculptSession *ss)
ATTR_NO_OPT static void sculpt_undo_bmesh_restore_end(bContext *C,
SculptUndoNode *unode,
Object *ob,
SculptSession *ss)
{
if (unode->applied) {
sculpt_undo_bmesh_enable(ob, unode);
@ -506,7 +512,9 @@ static void sculpt_undo_bmesh_restore_end(bContext *C,
unode->applied = true;
}
BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
if (ss->bm) {
BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
}
}
static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Object *object)
@ -593,10 +601,10 @@ static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object)
*
* Returns true if this was a dynamic-topology undo step, otherwise
* returns false to indicate the non-dyntopo code should run. */
static int sculpt_undo_bmesh_restore(bContext *C,
SculptUndoNode *unode,
Object *ob,
SculptSession *ss)
ATTR_NO_OPT static int sculpt_undo_bmesh_restore(bContext *C,
SculptUndoNode *unode,
Object *ob,
SculptSession *ss)
{
if (ss->bm_log && ss->bm) {
SCULPT_dyntopo_node_layers_update_offsets(ss);
@ -610,7 +618,9 @@ static int sculpt_undo_bmesh_restore(bContext *C,
return true;
case SCULPT_UNDO_DYNTOPO_END:
sculpt_undo_bmesh_restore_end(C, unode, ob, ss);
if (ss->bm) {
sculpt_undo_bmesh_restore_end(C, unode, ob, ss);
}
SCULPT_vertex_random_access_ensure(ss);
return true;
default:
@ -671,15 +681,19 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
SCULPT_dynamic_topology_enable_ex(CTX_data_main(C), depsgraph, scene, ob);
//see if we have a saved log in the entry
ss->bm_log = BM_log_unfreeze(ss->bm, unode->bm_entry);
// see if we have a saved log in the entry
BMLog *log = BM_log_unfreeze(ss->bm, unode->bm_entry);
if (!ss->bm_log) {
/* Restore the BMLog using saved entries. */
ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
if (log) {
if (ss->bm_log) {
BM_log_free(ss->bm_log, false);
}
ss->bm_log = log;
SCULPT_dyntopo_node_layers_update_offsets(ss);
BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
}
BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
}
/* Restore pivot. */
@ -1293,7 +1307,7 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ
return unode;
}
static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type)
ATTR_NO_OPT static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
SculptSession *ss = ob->sculpt;
@ -1313,7 +1327,9 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
if (type == SCULPT_UNDO_DYNTOPO_END) {
unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
BM_log_before_all_removed(ss->bm, ss->bm_log);
BM_log_full_mesh(ss->bm, ss->bm_log);
// BM_log_before_all_removed(ss->bm, ss->bm_log);
}
else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) {
/* Store a copy of the mesh's current vertices, loops, and
@ -1325,7 +1341,8 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
sculpt_undo_geometry_store_data(geometry, ob);
unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
BM_log_all_added(ss->bm, ss->bm_log);
//BM_log_all_added(ss->bm, ss->bm_log);
BM_log_full_mesh(ss->bm, ss->bm_log);
}
else {
unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);