diff options
Diffstat (limited to 'source/blender/bmesh/intern/bmesh_log.c')
-rw-r--r-- | source/blender/bmesh/intern/bmesh_log.c | 2401 |
1 files changed, 2043 insertions, 358 deletions
diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 9033e43374b..deb735c8cf3 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -1,3 +1,4 @@ + /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,19 +32,75 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.h" +#include "BLI_compiler_attrs.h" #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_memarena.h" #include "BLI_mempool.h" +#include "BLI_string.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + #include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BLI_strict_flags.h" #include "bmesh.h" #include "bmesh_log.h" +#include "bmesh_private.h" #include "range_tree.h" -#include "BLI_strict_flags.h" +#define CUSTOMDATA + +//#define DO_LOG_PRINT + +#ifdef DO_LOG_PRINT +static int msg_idgen = 1; +static char msg_buffer[256] = {0}; + +# define SET_MSG(le) memcpy(le->msg, msg_buffer, sizeof(le->msg)) +# define GET_MSG(le) le->msg +# define LOGPRINT(...) \ + printf("%s: ", __func__); \ + printf(__VA_ARGS__) +struct Mesh; +#else +# define GET_MSG(le) le ? "" : " " +# define SET_MSG(le) +# define LOGPRINT(...) +#endif + +#include <stdarg.h> + +void bm_log_message(const char *fmt, ...) +{ + char msg[64]; + + va_list args; + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + +#ifdef DO_LOG_PRINT + BLI_snprintf(msg_buffer, 64, "%d %s", msg_idgen, msg); + msg_idgen++; + + printf("%s\n", msg); +#endif +} + +typedef enum { LOG_ENTRY_PARTIAL, LOG_ENTRY_FULL_MESH, LOG_ENTRY_MESH_IDS } BMLogEntryType; + +typedef struct BMLogIdMap { + int elemmask; + int elemtots[15]; + int *maps[15]; +} BMLogIdMap; struct BMLogEntry { struct BMLogEntry *next, *prev; @@ -54,18 +111,25 @@ struct BMLogEntry { /* Elements that were in the previous entry, but have been * deleted */ GHash *deleted_verts; + GHash *deleted_edges; + GHash *deleted_edges_post; // used for split edges GHash *deleted_faces; + /* Elements that were not in the previous entry, but are in the * result of this entry */ GHash *added_verts; + GHash *added_edges; GHash *added_faces; /* Vertices whose coordinates, mask value, or hflag have changed */ GHash *modified_verts; + GHash *modified_edges; GHash *modified_faces; BLI_mempool *pool_verts; + BLI_mempool *pool_edges; BLI_mempool *pool_faces; + MemArena *arena; /* This is only needed for dropping BMLogEntries while still in * dynamic-topology mode, as that should release vert/face IDs @@ -75,11 +139,21 @@ struct BMLogEntry { * This field is not guaranteed to be valid, any use of it should * check for NULL. */ BMLog *log; + + CustomData vdata, edata, ldata, pdata; + struct BMLogEntry *combined_prev, *combined_next; + + BMLogEntryType type; + + struct Mesh + *full_copy_mesh; // avoid excessive memory use by saving a Mesh instead of copying the bmesh + BMLogIdMap idmap; }; struct BMLog { - /* Tree of free IDs */ - struct RangeTreeUInt *unused_ids; + // BMLogEntry *frozen_full_mesh; + + int refcount; /* Mapping from unique IDs to vertices and faces * @@ -90,8 +164,10 @@ struct BMLog { * The ID is needed because element pointers will change as they * are created and deleted. */ - GHash *id_to_elem; - GHash *elem_to_id; + + ThreadRWMutex lock; + + BMesh *bm; /* All BMLogEntrys, ordered from earliest to most recent */ ListBase entries; @@ -105,18 +181,54 @@ struct BMLog { * entries have been applied (i.e. there is nothing left to redo.) */ BMLogEntry *current_entry; + + bool has_edges; + int cd_dyn_vert; + bool dead; }; -typedef struct { +typedef struct BMLogVert { +#ifdef DO_LOG_PRINT + char msg[64]; +#endif + float co[3]; - short no[3]; + float no[3]; char hflag; - float mask; + void *customdata; } BMLogVert; +typedef struct BMLogEdge { +#ifdef DO_LOG_PRINT + char msg[64]; +#endif + + uint v1, v2; + char hflag; + void *customdata; + uint id; +} BMLogEdge; + +#define MAX_FACE_RESERVED 8 + typedef struct { - uint v_ids[3]; +#ifdef DO_LOG_PRINT + char msg[64]; +#endif + + uint *v_ids; + uint *l_ids; + void **customdata; + + float no[3]; + void *customdata_f; char hflag; + + size_t len; + + void *customdata_res[MAX_FACE_RESERVED]; + uint v_ids_res[MAX_FACE_RESERVED]; + uint l_ids_res[MAX_FACE_RESERVED]; } BMLogFace; /************************* Get/set element IDs ************************/ @@ -125,112 +237,330 @@ typedef struct { #define logkey_hash BLI_ghashutil_inthash_p_simple #define logkey_cmp BLI_ghashutil_intcmp +static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry); +static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry); +static void log_idmap_free(BMLogEntry *entry); + +static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry); +static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry); + +BMLogEntry *bm_log_entry_add_ex( + BMesh *bm, BMLog *log, bool combine_with_last, BMLogEntryType type, BMLogEntry *last_entry); +static void bm_log_entry_free(BMLogEntry *entry); +static bool bm_log_free_direct(BMLog *log, bool safe_mode); + +static void *log_ghash_lookup(BMLog *log, GHash *gh, const void *key) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ); + void *ret = BLI_ghash_lookup(gh, key); + BLI_rw_mutex_unlock(&log->lock); + + return ret; +} + +// this is not 100% threadsafe +static void **log_ghash_lookup_p(BMLog *log, GHash *gh, const void *key) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ); + void **ret = BLI_ghash_lookup_p(gh, key); + BLI_rw_mutex_unlock(&log->lock); + + return ret; +} + +static void log_ghash_insert(BMLog *log, GHash *gh, void *key, void *val) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE); + BLI_ghash_insert(gh, key, val); + BLI_rw_mutex_unlock(&log->lock); +} + +static bool log_ghash_remove( + BMLog *log, GHash *gh, const void *key, GHashKeyFreeFP keyfree, GHashValFreeFP valfree) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE); + bool ret = BLI_ghash_remove(gh, key, keyfree, valfree); + BLI_rw_mutex_unlock(&log->lock); + + return ret; +} + +static bool log_ghash_reinsert( + BMLog *log, GHash *gh, void *key, void *val, GHashKeyFreeFP keyfree, GHashValFreeFP valfree) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE); + bool ret = BLI_ghash_reinsert(gh, key, val, keyfree, valfree); + BLI_rw_mutex_unlock(&log->lock); + + return ret; +} + +static void bm_log_copy_id(CustomData *cdata, BMElem *elem, void *data) +{ + int cd_id = cdata->typemap[CD_MESH_ID]; + + if (cd_id >= 0) { + cd_id = cdata->layers[cd_id].offset; + + int id = BM_ELEM_CD_GET_INT(elem, cd_id); + + BMElem elem2; + elem2.head.data = data; + + BM_ELEM_CD_SET_INT(&elem2, cd_id, id); + } +} + +static bool log_ghash_haskey(BMLog *log, GHash *gh, const void *key) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ); + bool ret = BLI_ghash_haskey(gh, key); + BLI_rw_mutex_unlock(&log->lock); + + return ret; +} + +static bool log_ghash_ensure_p(BMLog *log, GHash *gh, void *key, void ***val) +{ + BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE); + bool ret = BLI_ghash_ensure_p(gh, key, val); + BLI_rw_mutex_unlock(&log->lock); + + return ret; +} + /* Get the vertex's unique ID from the log */ static uint bm_log_vert_id_get(BMLog *log, BMVert *v) { - BLI_assert(BLI_ghash_haskey(log->elem_to_id, v)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, v)); + return (uint)BM_ELEM_GET_ID(log->bm, v); } -/* Set the vertex's unique ID in the log */ -static void bm_log_vert_id_set(BMLog *log, BMVert *v, uint id) +/* Get a vertex from its unique ID */ +static BMVert *bm_log_vert_from_id(BMLog *log, uint id) { - void *vid = POINTER_FROM_UINT(id); + return (BMVert *)BM_ELEM_FROM_ID(log->bm, id); +} - BLI_ghash_reinsert(log->id_to_elem, vid, v, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, v, vid, NULL, NULL); +BMVert *BM_log_id_vert_get(BMLog *log, uint id) +{ + return bm_log_vert_from_id(log, id); +} + +/* Get the vertex's unique ID from the log */ +static uint bm_log_edge_id_get(BMLog *log, BMEdge *e) +{ + return (uint)BM_ELEM_GET_ID(log->bm, e); } /* Get a vertex from its unique ID */ -static BMVert *bm_log_vert_from_id(BMLog *log, uint id) +static BMEdge *bm_log_edge_from_id(BMLog *log, uint id) { - void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); + return (BMEdge *)BM_ELEM_FROM_ID(log->bm, id); +} + +BMEdge *BM_log_id_edge_get(BMLog *log, uint id) +{ + return bm_log_edge_from_id(log, id); } /* Get the face's unique ID from the log */ static uint bm_log_face_id_get(BMLog *log, BMFace *f) { - BLI_assert(BLI_ghash_haskey(log->elem_to_id, f)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, f)); + return (uint)BM_ELEM_GET_ID(log->bm, f); } -/* Set the face's unique ID in the log */ -static void bm_log_face_id_set(BMLog *log, BMFace *f, uint id) +uint BM_log_vert_id_get(BMLog *log, BMVert *v) { - void *fid = POINTER_FROM_UINT(id); + return bm_log_vert_id_get(log, v); +} - BLI_ghash_reinsert(log->id_to_elem, fid, f, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, f, fid, NULL, NULL); +uint BM_log_face_id_get(BMLog *log, BMFace *f) +{ + return bm_log_face_id_get(log, f); } /* Get a face from its unique ID */ static BMFace *bm_log_face_from_id(BMLog *log, uint id) { - void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); + return (BMFace *)BM_ELEM_FROM_ID(log->bm, id); +} + +BMFace *BM_log_id_face_get(BMLog *log, uint id) +{ + return bm_log_face_from_id(log, id); } /************************ BMLogVert / BMLogFace ***********************/ -/* Get a vertex's paint-mask value - * - * Returns zero if no paint-mask layer is present */ -static float vert_mask_get(BMVert *v, const int cd_vert_mask_offset) +static void bm_log_vert_customdata( + BMesh *bm, BMLog *log, BMLogEntry *entry, BMVert *v, BMLogVert *lv) { - if (cd_vert_mask_offset != -1) { - return BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset); +#ifdef CUSTOMDATA + // if (!lv) { + // return; + //} + + if (lv->customdata) { + BLI_mempool_free(entry->vdata.pool, lv->customdata); + lv->customdata = NULL; } - return 0.0f; + + CustomData_bmesh_copy_data(&bm->vdata, &entry->vdata, v->head.data, &lv->customdata); + + // forcibly copy id + // bm_log_copy_id(&bm->vdata, (BMElem *)v, lv->customdata); + +#endif } -/* Set a vertex's paint-mask value - * - * Has no effect is no paint-mask layer is present */ -static void vert_mask_set(BMVert *v, const float new_mask, const int cd_vert_mask_offset) +static void bm_log_edge_customdata( + BMesh *bm, BMLog *log, BMLogEntry *entry, BMEdge *e, BMLogEdge *le) { - if (cd_vert_mask_offset != -1) { - BM_ELEM_CD_SET_FLOAT(v, cd_vert_mask_offset, new_mask); + if (le->customdata) { + BLI_mempool_free(entry->edata.pool, le->customdata); + le->customdata = NULL; + } + + CustomData_bmesh_copy_data(&bm->edata, &entry->edata, e->head.data, &le->customdata); +} + +static void bm_log_face_customdata(BMesh *bm, BMLog *log, BMFace *f, BMLogFace *lf) +{ + BMLogEntry *entry = log->current_entry; + + if (!entry || !lf) { + printf("bmlog error\n"); + return; + } + + if (lf->customdata_f) { + BLI_mempool_free(entry->pdata.pool, lf->customdata_f); + lf->customdata_f = NULL; } + + CustomData_bmesh_copy_data(&bm->pdata, &entry->pdata, f->head.data, &lf->customdata_f); + + // forcibly copy id + // bm_log_copy_id(&bm->pdata, (BMElem *)f, lf->customdata_f); + + BMLoop *l = f->l_first; + int i = 0; + do { + if (lf->customdata[i]) { + BLI_mempool_free(entry->ldata.pool, lf->customdata[i]); + lf->customdata[i] = NULL; + } + + CustomData_bmesh_copy_data(&bm->ldata, &entry->ldata, l->head.data, &lf->customdata[i]); + } while ((i++, l = l->next) != f->l_first); } /* Update a BMLogVert with data from a BMVert */ -static void bm_log_vert_bmvert_copy(BMLogVert *lv, BMVert *v, const int cd_vert_mask_offset) +static void bm_log_vert_bmvert_copy(BMLog *log, + BMLogEntry *entry, + BMLogVert *lv, + BMVert *v, + const int cd_vert_mask_offset, + bool copy_customdata) { copy_v3_v3(lv->co, v->co); - normal_float_to_short_v3(lv->no, v->no); - lv->mask = vert_mask_get(v, cd_vert_mask_offset); + copy_v3_v3(lv->no, v->no); + lv->hflag = v->head.hflag; + + if (copy_customdata) { + bm_log_vert_customdata(log->bm, log, entry, v, lv); + } } /* Allocate and initialize a BMLogVert */ -static BMLogVert *bm_log_vert_alloc(BMLog *log, BMVert *v, const int cd_vert_mask_offset) +static BMLogVert *bm_log_vert_alloc(BMLog *log, + BMVert *v, + const int cd_vert_mask_offset, + bool log_customdata) { BMLogEntry *entry = log->current_entry; BMLogVert *lv = BLI_mempool_alloc(entry->pool_verts); + lv->customdata = NULL; - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); + bm_log_vert_bmvert_copy(log, entry, lv, v, -1, log_customdata); return lv; } +static void bm_log_edge_bmedge_copy( + BMLog *log, BMLogEntry *entry, BMLogEdge *le, BMEdge *e, bool copy_customdata) +{ + if (e->head.htype != BM_EDGE) { + printf("%s: e is not an edge; htype: %d\n", __func__, (int)e->head.htype); + } + + le->v1 = (uint)BM_ELEM_GET_ID(log->bm, e->v1); + le->v2 = (uint)BM_ELEM_GET_ID(log->bm, e->v2); + + le->id = (uint)BM_ELEM_GET_ID(log->bm, e); + le->hflag = e->head.hflag; + + if (copy_customdata) { + bm_log_edge_customdata(log->bm, log, entry, e, le); + } +} + +/* Allocate and initialize a BMLogVert */ +static BMLogEdge *bm_log_edge_alloc(BMLog *log, BMEdge *e, bool log_customdata) +{ + BMLogEntry *entry = log->current_entry; + BMLogEdge *le = BLI_mempool_alloc(entry->pool_edges); + le->customdata = NULL; + +#ifdef DO_LOG_PRINT + le->msg[0] = 0; +#endif + + bm_log_edge_bmedge_copy(log, entry, le, e, log_customdata); + + return le; +} + /* Allocate and initialize a BMLogFace */ static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f) { BMLogEntry *entry = log->current_entry; BMLogFace *lf = BLI_mempool_alloc(entry->pool_faces); - BMVert *v[3]; - BLI_assert(f->len == 3); + lf->len = (size_t)f->len; + + bool have_loop_ids = (log->bm->idmap.flag & BM_LOOP); + + if (f->len > MAX_FACE_RESERVED) { + lf->v_ids = (uint *)BLI_memarena_alloc(entry->arena, sizeof(*lf->v_ids) * lf->len); + lf->l_ids = (uint *)BLI_memarena_alloc(entry->arena, sizeof(*lf->l_ids) * lf->len); + lf->customdata = (void **)BLI_memarena_alloc(entry->arena, sizeof(void *) * lf->len); + } + else { + lf->v_ids = lf->v_ids_res; + lf->l_ids = lf->l_ids_res; + lf->customdata = lf->customdata_res; + } - // BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v, 3); - BM_face_as_array_vert_tri(f, v); + lf->customdata_f = NULL; - lf->v_ids[0] = bm_log_vert_id_get(log, v[0]); - lf->v_ids[1] = bm_log_vert_id_get(log, v[1]); - lf->v_ids[2] = bm_log_vert_id_get(log, v[2]); + copy_v3_v3(lf->no, f->no); + + int i = 0; + BMLoop *l = f->l_first; + do { + if (have_loop_ids) { + lf->l_ids[i] = (uint)BM_ELEM_GET_ID(log->bm, l); + } + else { + lf->l_ids[i] = (uint)-1; + } + + lf->v_ids[i] = bm_log_vert_id_get(log, l->v); + + lf->customdata[i] = NULL; + } while ((i++, l = l->next) != f->l_first); lf->hflag = f->head.hflag; return lf; @@ -238,10 +568,10 @@ static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f) /************************ Helpers for undo/redo ***********************/ -static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts) +// exec vert kill callbacks before killing faces +static void bm_log_verts_unmake_pre( + BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks) { - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - GHashIterator gh_iter; GHASH_ITER (gh_iter, verts) { void *key = BLI_ghashIterator_getKey(&gh_iter); @@ -249,78 +579,367 @@ static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts) uint id = POINTER_AS_UINT(key); BMVert *v = bm_log_vert_from_id(log, id); + if (!v) { + printf("bm_log error; vertex id: %p\n", key); + continue; + } + + if (v->head.htype != BM_VERT) { + printf("bm_log error; vertex id: %p, type was: %d\n", key, v->head.htype); + continue; + } + + /* Ensure the log has the final values of the vertex before + * deleting it */ + bm_log_vert_bmvert_copy(log, entry, lv, v, -1, true); + + if (callbacks) { + callbacks->on_vert_kill(v, callbacks->userdata); + } + } +} + +// exec vert kill callbacks before killing faces +static void bm_log_edges_unmake_pre( + BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, edges) { + void *key = BLI_ghashIterator_getKey(&gh_iter); + BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter); + uint id = POINTER_AS_UINT(key); + BMEdge *e = bm_log_edge_from_id(log, id); + + if (!e) { + printf("%s: missing edge; id: %d [%s]\n", __func__, id, GET_MSG(le)); + continue; + } + + if (e->head.htype != BM_EDGE) { + printf("%s: not an edge; edge id: %d, type was: %d [%s]\n", + __func__, + id, + e->head.htype, + GET_MSG(le)); + continue; + } + /* Ensure the log has the final values of the vertex before * deleting it */ - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); + bm_log_edge_bmedge_copy(log, entry, le, e, true); + + if (callbacks) { + callbacks->on_edge_kill(e, callbacks->userdata); + } + } +} + +static void bm_log_edges_unmake( + BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, edges) { + void *key = BLI_ghashIterator_getKey(&gh_iter); + uint id = POINTER_AS_UINT(key); + BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter); + BMEdge *e = bm_log_edge_from_id(log, id); + + if (!e) { + printf("%s: missing edge; edge id: %d [%s]\n", __func__, id, GET_MSG(le)); + continue; + } + + if (e->head.htype != BM_EDGE) { + printf("%s: not an edge; edge id: %d, type: %d [%s]\n", + __func__, + id, + e->head.htype, + GET_MSG(le)); + continue; + } + + BM_edge_kill(bm, e); + } +} + +static void bm_log_verts_unmake( + BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks) +{ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, verts) { + void *key = BLI_ghashIterator_getKey(&gh_iter); + uint id = POINTER_AS_UINT(key); + BMVert *v = bm_log_vert_from_id(log, id); + + if (!v) { + printf("bmlog error. vertex id: %p\n", key); + continue; + } BM_vert_kill(bm, v); } } -static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces) +static void bm_log_faces_unmake( + BMesh *bm, BMLog *log, GHash *faces, BMLogEntry *entry, BMLogCallbacks *callbacks) { GHashIterator gh_iter; + BMEdge **e_tri = NULL; + BLI_array_staticdeclare(e_tri, 32); + GHASH_ITER (gh_iter, faces) { void *key = BLI_ghashIterator_getKey(&gh_iter); + BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); uint id = POINTER_AS_UINT(key); BMFace *f = bm_log_face_from_id(log, id); - BMEdge *e_tri[3]; - BMLoop *l_iter; + + if (!f) { + printf("bmlog error in %s: missing face %d\n", __func__, id); + continue; + } + + if (f->head.htype != BM_FACE) { + printf("bmlog error in %s: f was not a face, type was: %d\n", __func__, f->head.htype); + continue; + } + + BLI_array_clear(e_tri); + + BMLoop *l; int i; - l_iter = BM_FACE_FIRST_LOOP(f); - for (i = 0; i < 3; i++, l_iter = l_iter->next) { - e_tri[i] = l_iter->e; + // ensure we have final customdata for face in log + + l = f->l_first; + i = 0; + do { + if (lf->customdata[i]) { + CustomData_bmesh_copy_data(&bm->ldata, &entry->ldata, l->head.data, &lf->customdata[i]); + } + + BLI_array_append(e_tri, l->e); + } while ((i++, l = l->next) != f->l_first); + + if (lf->customdata_f) { + CustomData_bmesh_copy_data(&bm->pdata, &entry->pdata, f->head.data, &lf->customdata_f); + + // forcibly copy id + // bm_log_copy_id(&bm->pdata, (BMElem *)f, lf->customdata_f); + } + + if (callbacks) { + callbacks->on_face_kill(f, callbacks->userdata); } - /* Remove any unused edges */ BM_face_kill(bm, f); - for (i = 0; i < 3; i++) { + +#if 0 + /* Remove any unused edges */ + for (i = 0; i < (int)lf->len; i++) { if (BM_edge_is_wire(e_tri[i])) { BM_edge_kill(bm, e_tri[i]); } } +#endif } + + BLI_array_free(e_tri); } -static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts) +static void bm_log_verts_restore( + BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks) { - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - GHashIterator gh_iter; GHASH_ITER (gh_iter, verts) { void *key = BLI_ghashIterator_getKey(&gh_iter); BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - BMVert *v = BM_vert_create(bm, lv->co, NULL, BM_CREATE_NOP); - vert_mask_set(v, lv->mask, cd_vert_mask_offset); + + BMVert *v = BM_vert_create(bm, lv->co, NULL, BM_CREATE_SKIP_ID); + v->head.hflag = lv->hflag; - normal_short_to_float_v3(v->no, lv->no); - bm_log_vert_id_set(log, v, POINTER_AS_UINT(key)); + copy_v3_v3(v->no, lv->no); + +#ifdef CUSTOMDATA + if (lv->customdata) { + CustomData_bmesh_copy_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data); + } +#endif + + bm_assign_id(bm, (BMElem *)v, POINTER_AS_UINT(key), false); + + if (callbacks) { + callbacks->on_vert_add(v, callbacks->userdata); + } } } -static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) +static void bm_log_edges_restore( + BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks) { GHashIterator gh_iter; + GHASH_ITER (gh_iter, edges) { + void *key = BLI_ghashIterator_getKey(&gh_iter); + BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter); + uint id = POINTER_AS_UINT(key); + + if (id != le->id) { + printf("%s: id differs from stored id in BMLogEdge!\n", __func__); + } + + BMVert *v1 = bm_log_vert_from_id(log, le->v1); + BMVert *v2 = bm_log_vert_from_id(log, le->v2); + + if (!v1 || !v2) { + printf("%s: missing edge verts: %p %p\n", __func__, v1, v2); + continue; + } + + if (v1->head.htype != BM_VERT || v2->head.htype != BM_VERT) { + printf("%s: edge verts were not verts: %d %d\n", __func__, v1->head.htype, v2->head.htype); + continue; + } + + BMEdge *e = BM_edge_exists(v1, v2); + if (e) { + printf("%s: edge already %d existed\n", __func__, (int)id); + bm_free_id(bm, (BMElem *)e); + } + else { + e = BM_edge_create(bm, v1, v2, NULL, BM_CREATE_SKIP_ID); + } + + e->head.hflag = le->hflag; + +#ifdef CUSTOMDATA + if (le->customdata) { + CustomData_bmesh_copy_data(&entry->edata, &bm->edata, le->customdata, &e->head.data); + } +#endif + + bm_assign_id(bm, (BMElem *)e, POINTER_AS_UINT(key), false); + + if ((uint)BM_ELEM_GET_ID(bm, e) != id) { + printf("%s: error assigning id\n", __func__); + } + + if (callbacks) { + callbacks->on_edge_add(e, callbacks->userdata); + } + } +} + +static void bm_log_faces_restore( + BMesh *bm, BMLog *log, GHash *faces, BMLogEntry *entry, BMLogCallbacks *callbacks) +{ + GHashIterator gh_iter; + BMVert **vs_tmp = NULL; + BLI_array_staticdeclare(vs_tmp, 32); + + bool have_loop_ids = (log->bm->idmap.flag & BM_LOOP); + GHASH_ITER (gh_iter, faces) { void *key = BLI_ghashIterator_getKey(&gh_iter); BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); - BMVert *v[3] = { - bm_log_vert_from_id(log, lf->v_ids[0]), - bm_log_vert_from_id(log, lf->v_ids[1]), - bm_log_vert_from_id(log, lf->v_ids[2]), - }; - BMFace *f; - - f = BM_face_create_verts(bm, v, 3, NULL, BM_CREATE_NOP, true); + + BLI_array_clear(vs_tmp); + bool bad = false; + + for (int i = 0; i < (int)lf->len; i++) { + BMVert *v = bm_log_vert_from_id(log, lf->v_ids[i]); + + if (!v) { + BMIter iter; + BMVert *v2; + const int cd_id = bm->idmap.cd_id_off[BM_VERT]; + + bad = true; + + BM_ITER_MESH (v2, &iter, bm, BM_VERTS_OF_MESH) { + int id = BM_ELEM_CD_GET_INT(v2, cd_id); + + if (lf->v_ids[i] == (uint)id) { + printf("found vertex %d\n", id); + bad = false; + v = v2; + break; + } + } + + if (bad) { + printf("Undo error! %p\n", v); + break; + } + } + + if (bad) { + continue; + } + + if (v->head.htype != BM_VERT) { + printf("vert %d in face %d was not a vertex\n", (int)lf->v_ids[i], POINTER_AS_INT(key)); + continue; + } + BLI_array_append(vs_tmp, v); + } + + if ((int)BLI_array_len(vs_tmp) < 2) { + printf("severely malformed face %d in %s\n", POINTER_AS_INT(key), __func__); + continue; + } + +#if 0 + for (size_t j = 0; j < lf->len; j++) { + BMVert *v1 = bm_log_vert_from_id(log, lf->v_ids[j]); + BMVert *v2 = bm_log_vert_from_id(log, lf->v_ids[(j + 1) % lf->len]); + + if (!v1 || !v2) { + continue; + } + + if (!BM_edge_exists(v1, v2)) { + int id = POINTER_AS_INT(key); + printf("%s: missing edge, face %d had to create it\n", __func__, (int)id); + } + } +#endif + + BMFace *f = BM_face_create_verts( + bm, vs_tmp, (int)BLI_array_len(vs_tmp), NULL, BM_CREATE_SKIP_ID, true); f->head.hflag = lf->hflag; - bm_log_face_id_set(log, f, POINTER_AS_UINT(key)); + + copy_v3_v3(f->no, lf->no); + + if (lf->customdata_f) { + CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata_f, &f->head.data); + } + + bm_assign_id(bm, (BMElem *)f, POINTER_AS_UINT(key), false); + + BMLoop *l = f->l_first; + int j = 0; + + do { + if (have_loop_ids) { + bm_assign_id(bm, (BMElem *)l, lf->l_ids[j], false); + } + + if (lf->customdata[j]) { + CustomData_bmesh_copy_data(&entry->ldata, &bm->ldata, lf->customdata[j], &l->head.data); + } + } while ((j++, l = l->next) != f->l_first); + + if (callbacks) { + callbacks->on_face_add(f, callbacks->userdata); + } } + + BLI_array_free(vs_tmp); } -static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts) +static void bm_log_vert_values_swap( + BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks) { - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + void *scratch = bm->vdata.pool ? BLI_mempool_alloc(bm->vdata.pool) : NULL; GHashIterator gh_iter; GHASH_ITER (gh_iter, verts) { @@ -328,22 +947,85 @@ static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts) BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); uint id = POINTER_AS_UINT(key); BMVert *v = bm_log_vert_from_id(log, id); - float mask; - short normal[3]; + + if (!v) { + printf("missing vert in bmlog! %d", id); + continue; + } + + if (v->head.htype != BM_VERT) { + printf("not a vertex: %d\n", v->head.htype); + continue; + } swap_v3_v3(v->co, lv->co); - copy_v3_v3_short(normal, lv->no); - normal_float_to_short_v3(lv->no, v->no); - normal_short_to_float_v3(v->no, normal); + swap_v3_v3(v->no, lv->no); + SWAP(char, v->head.hflag, lv->hflag); - mask = lv->mask; - lv->mask = vert_mask_get(v, cd_vert_mask_offset); - vert_mask_set(v, mask, cd_vert_mask_offset); + + void *old_cdata = NULL; + + if (lv->customdata) { + if (v->head.data) { + old_cdata = scratch; + memcpy(old_cdata, v->head.data, (size_t)bm->vdata.totsize); + } + + CustomData_bmesh_swap_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data); + } + + if (callbacks) { + callbacks->on_vert_change(v, callbacks->userdata, old_cdata); + } + } + + if (scratch) { + BLI_mempool_free(bm->vdata.pool, scratch); + } +} + +static void bm_log_edge_values_swap( + BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks) +{ + void *scratch = bm->edata.pool ? BLI_mempool_alloc(bm->edata.pool) : NULL; + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, edges) { + void *key = BLI_ghashIterator_getKey(&gh_iter); + BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter); + uint id = POINTER_AS_UINT(key); + BMEdge *e = bm_log_edge_from_id(log, id); + + SWAP(char, e->head.hflag, le->hflag); + + void *old_cdata = NULL; + + if (le->customdata) { + if (e->head.data) { + old_cdata = scratch; + memcpy(old_cdata, e->head.data, (size_t)bm->edata.totsize); + } + + CustomData_bmesh_swap_data(&entry->edata, &bm->edata, le->customdata, &e->head.data); + } + + if (callbacks) { + callbacks->on_edge_change(e, callbacks->userdata, old_cdata); + } + } + + if (scratch) { + BLI_mempool_free(bm->edata.pool, scratch); } } -static void bm_log_face_values_swap(BMLog *log, GHash *faces) +static void bm_log_face_values_swap(BMLog *log, + GHash *faces, + BMLogEntry *entry, + BMLogCallbacks *callbacks) { + void *scratch = log->bm->pdata.pool ? BLI_mempool_alloc(log->bm->pdata.pool) : NULL; + GHashIterator gh_iter; GHASH_ITER (gh_iter, faces) { void *key = BLI_ghashIterator_getKey(&gh_iter); @@ -351,46 +1033,85 @@ static void bm_log_face_values_swap(BMLog *log, GHash *faces) uint id = POINTER_AS_UINT(key); BMFace *f = bm_log_face_from_id(log, id); + swap_v3_v3(f->no, lf->no); SWAP(char, f->head.hflag, lf->hflag); - } -} -/**********************************************************************/ + void *old_cdata = NULL; -/* Assign unique IDs to all vertices and faces already in the BMesh */ -static void bm_log_assign_ids(BMesh *bm, BMLog *log) -{ - BMIter iter; - BMVert *v; - BMFace *f; + if (f->head.data) { + old_cdata = scratch; + memcpy(old_cdata, f->head.data, (size_t)log->bm->pdata.totsize); + } + + if (lf->customdata_f) { + CustomData_bmesh_swap_data(&entry->pdata, &log->bm->pdata, lf->customdata_f, &f->head.data); + } + + int i = 0; + BMLoop *l = f->l_first; - /* Generate vertex IDs */ - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - uint id = range_tree_uint_take_any(log->unused_ids); - bm_log_vert_id_set(log, v, id); + do { + if (lf->customdata[i]) { + CustomData_bmesh_swap_data( + &entry->ldata, &log->bm->ldata, lf->customdata[i], &l->head.data); + } + } while ((i++, l = l->next) != f->l_first); + + if (callbacks) { + callbacks->on_face_change(f, callbacks->userdata, old_cdata); + } } - /* Generate face IDs */ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - uint id = range_tree_uint_take_any(log->unused_ids); - bm_log_face_id_set(log, f, id); + if (scratch) { + BLI_mempool_free(log->bm->pdata.pool, scratch); } } +/**********************************************************************/ + +static void bm_log_full_mesh_intern(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0}; + + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + entry->full_copy_mesh = BKE_mesh_from_bmesh_nomain( + bm, + (&(struct BMeshToMeshParams){.update_shapekey_indices = false, + .calc_object_remap = false, + .cd_mask_extra = cd_mask_extra, + .copy_temp_cdlayers = true, + .ignore_mesh_id_layers = false}), + NULL); +} + /* Allocate an empty log entry */ -static BMLogEntry *bm_log_entry_create(void) +static BMLogEntry *bm_log_entry_create(BMLogEntryType type) { BMLogEntry *entry = MEM_callocN(sizeof(BMLogEntry), __func__); - entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->type = type; + + if (type == LOG_ENTRY_PARTIAL) { + entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->deleted_edges = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->deleted_edges_post = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + + entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->added_edges = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); - entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); + entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->modified_edges = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + + entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); + entry->pool_edges = BLI_mempool_create(sizeof(BMLogEdge), 0, 64, BLI_MEMPOOL_NOP); + entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); + + entry->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmlog arena"); + } return entry; } @@ -398,28 +1119,79 @@ static BMLogEntry *bm_log_entry_create(void) /* Free the data in a log entry * * NOTE: does not free the log entry itself. */ -static void bm_log_entry_free(BMLogEntry *entry) +static void bm_log_entry_free_direct(BMLogEntry *entry) { - BLI_ghash_free(entry->deleted_verts, NULL, NULL); - BLI_ghash_free(entry->deleted_faces, NULL, NULL); - BLI_ghash_free(entry->added_verts, NULL, NULL); - BLI_ghash_free(entry->added_faces, NULL, NULL); - BLI_ghash_free(entry->modified_verts, NULL, NULL); - BLI_ghash_free(entry->modified_faces, NULL, NULL); + switch (entry->type) { + case LOG_ENTRY_MESH_IDS: + log_idmap_free(entry); + break; + case LOG_ENTRY_FULL_MESH: + + BKE_mesh_free_data_for_undo(entry->full_copy_mesh); + break; + case LOG_ENTRY_PARTIAL: + BLI_ghash_free(entry->deleted_verts, NULL, NULL); + BLI_ghash_free(entry->deleted_edges, NULL, NULL); + BLI_ghash_free(entry->deleted_edges_post, NULL, NULL); + BLI_ghash_free(entry->deleted_faces, NULL, NULL); + + BLI_ghash_free(entry->added_verts, NULL, NULL); + BLI_ghash_free(entry->added_edges, NULL, NULL); + BLI_ghash_free(entry->added_faces, NULL, NULL); + + BLI_ghash_free(entry->modified_verts, NULL, NULL); + BLI_ghash_free(entry->modified_edges, NULL, NULL); + BLI_ghash_free(entry->modified_faces, NULL, NULL); + + BLI_mempool_destroy(entry->pool_verts); + BLI_mempool_destroy(entry->pool_edges); + BLI_mempool_destroy(entry->pool_faces); + BLI_memarena_free(entry->arena); + + if (entry->vdata.pool) { + BLI_mempool_destroy(entry->vdata.pool); + } + if (entry->edata.pool) { + BLI_mempool_destroy(entry->edata.pool); + } + if (entry->ldata.pool) { + BLI_mempool_destroy(entry->ldata.pool); + } + if (entry->pdata.pool) { + BLI_mempool_destroy(entry->pdata.pool); + } - BLI_mempool_destroy(entry->pool_verts); - BLI_mempool_destroy(entry->pool_faces); + CustomData_free(&entry->vdata, 0); + CustomData_free(&entry->edata, 0); + CustomData_free(&entry->ldata, 0); + CustomData_free(&entry->pdata, 0); + break; + } } -static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash) +/* Free the data in a log entry + * and handles bmlog ref counting + * NOTE: does not free the log entry itself. */ +static void bm_log_entry_free(BMLogEntry *entry) { - GHashIterator gh_iter; + BMLog *log = entry->log; + bool kill_log = false; - GHASH_ITER (gh_iter, id_ghash) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); + if (log) { + log->refcount--; + + if (log->refcount < 0) { + fprintf(stderr, "BMLog refcount error\n"); + log->refcount = 0; + } - range_tree_uint_retake(unused_ids, id); + kill_log = !log->refcount; + } + + bm_log_entry_free_direct(entry); + + if (kill_log) { + bm_log_free_direct(log, true); } } @@ -454,56 +1226,63 @@ static GHash *bm_log_compress_ids_to_indices(uint *ids, uint totid) return map; } -/* Release all ID keys in id_ghash */ -static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash) -{ - GHashIterator gh_iter; +/***************************** Public API *****************************/ - GHASH_ITER (gh_iter, id_ghash) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - range_tree_uint_release(log->unused_ids, id); - } +void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert) +{ + log->cd_dyn_vert = cd_dyn_vert; } -/***************************** Public API *****************************/ +void BM_log_set_bm(BMesh *bm, BMLog *log) +{ + log->bm = bm; +} /* Allocate, initialize, and assign a new BMLog */ -BMLog *BM_log_create(BMesh *bm) +BMLog *BM_log_create(BMesh *bm, int cd_dyn_vert) { BMLog *log = MEM_callocN(sizeof(*log), __func__); - const uint reserve_num = (uint)(bm->totvert + bm->totface); - log->unused_ids = range_tree_uint_alloc(0, (uint)-1); - log->id_to_elem = BLI_ghash_new_ex(logkey_hash, logkey_cmp, __func__, reserve_num); - log->elem_to_id = BLI_ghash_ptr_new_ex(__func__, reserve_num); + BLI_rw_mutex_init(&log->lock); - /* Assign IDs to all existing vertices and faces */ - bm_log_assign_ids(bm, log); + BM_log_set_cd_offsets(log, cd_dyn_vert); return log; } -void BM_log_cleanup_entry(BMLogEntry *entry) +BMLog *bm_log_from_existing_entries_create(BMesh *bm, BMLog *log, BMLogEntry *entry) { - BMLog *log = entry->log; + log->current_entry = entry; - if (log) { - /* Take all used IDs */ - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->added_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->added_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_faces); + /* Let BMLog manage the entry list again */ + log->entries.first = log->entries.last = entry; + + while (entry->prev) { + entry = entry->prev; + log->entries.first = entry; + } + + entry = log->entries.last; + while (entry->next) { + entry = entry->next; + log->entries.last = entry; + } + + for (entry = log->entries.first; entry; entry = entry->next) { + BMLogEntry *entry2 = entry->combined_prev; + + while (entry2) { + entry2->log = log; + entry2 = entry2->combined_prev; - /* delete entries to avoid releasing ids in node cleanup */ - BLI_ghash_clear(entry->deleted_verts, NULL, NULL); - BLI_ghash_clear(entry->deleted_faces, NULL, NULL); - BLI_ghash_clear(entry->added_verts, NULL, NULL); - BLI_ghash_clear(entry->added_faces, NULL, NULL); - BLI_ghash_clear(entry->modified_verts, NULL, NULL); + log->refcount++; + } + + entry->log = log; + log->refcount++; } + + return log; } /* Allocate and initialize a new BMLog using existing BMLogEntries @@ -516,69 +1295,91 @@ void BM_log_cleanup_entry(BMLogEntry *entry) */ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry) { - BMLog *log = BM_log_create(bm); + BMLog *log = BM_log_create(bm, -1); - if (entry->prev) { - log->current_entry = entry; - } - else { - log->current_entry = NULL; - } + bm_log_from_existing_entries_create(bm, log, entry); - /* Let BMLog manage the entry list again */ - log->entries.first = log->entries.last = entry; + return log; +} - { - while (entry->prev) { - entry = entry->prev; - log->entries.first = entry; - } - entry = log->entries.last; - while (entry->next) { - entry = entry->next; - log->entries.last = entry; - } +BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry) +{ + if (!entry || !entry->log) { + return NULL; } - for (entry = log->entries.first; entry; entry = entry->next) { - entry->log = log; +#if 0 + BMLogEntry *frozen = entry->log->frozen_full_mesh; + if (!frozen && entry->type == LOG_ENTRY_FULL_MESH) { + frozen = entry; + } - /* Take all used IDs */ - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->added_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->added_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_faces); + if (!frozen || frozen->type != LOG_ENTRY_FULL_MESH) { + return entry->log->bm == bm ? entry->log : NULL; } +#endif - return log; + entry->log->bm = bm; + +#if 0 + 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; + } +#endif + return entry->log; } -/* Free all the data in a BMLog including the log itself */ -void BM_log_free(BMLog *log) +/* Free all the data in a BMLog including the log itself + * safe_mode means log->refcount will be checked, and if nonzero log will not be freed + */ +static bool bm_log_free_direct(BMLog *log, bool safe_mode) { BMLogEntry *entry; - if (log->unused_ids) { - range_tree_uint_free(log->unused_ids); - } + if (safe_mode && log->refcount) { +#if 0 + if (log->frozen_full_mesh) { + log->frozen_full_mesh->log = NULL; + bm_log_entry_free(log->frozen_full_mesh); + } +#endif - if (log->id_to_elem) { - BLI_ghash_free(log->id_to_elem, NULL, NULL); - } + // log->frozen_full_mesh = bm_log_entry_create(LOG_ENTRY_FULL_MESH); + // bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh); - if (log->elem_to_id) { - BLI_ghash_free(log->elem_to_id, NULL, NULL); + return false; } + log->dead = true; + + BLI_rw_mutex_end(&log->lock); + /* Clear the BMLog references within each entry, but do not free * the entries themselves */ for (entry = log->entries.first; entry; entry = entry->next) { entry->log = NULL; } - MEM_freeN(log); + return true; +} + +bool BM_log_free(BMLog *log, bool safe_mode) +{ + if (log->dead) { + MEM_freeN(log); + return true; + } + + if (bm_log_free_direct(log, safe_mode)) { + MEM_freeN(log); + return true; + } + + return false; } /* Get the number of log entries */ @@ -587,9 +1388,54 @@ int BM_log_length(const BMLog *log) return BLI_listbase_count(&log->entries); } +void BM_log_print_entry(BMLog *log, BMLogEntry *entry) +{ + BMLogEntry *first = entry; + + if (!log) { + log = entry->log; + } + + while (first->combined_prev) { + first = first->combined_prev; + } + + printf("==bmlog step==\n"); + + while (first) { + switch (first->type) { + case LOG_ENTRY_FULL_MESH: + printf(" ==full mesh copy==\n"); + break; + case LOG_ENTRY_MESH_IDS: + printf("==element IDs snapshot\n"); + break; + case LOG_ENTRY_PARTIAL: + printf("==modified: "); + printf("v: %d ", BLI_ghash_len(first->modified_verts)); + printf("e: %d ", BLI_ghash_len(first->modified_edges)); + printf("f: %d ", BLI_ghash_len(first->modified_faces)); + printf(" new: "); + printf("v: %d ", BLI_ghash_len(first->added_verts)); + printf("e: %d ", BLI_ghash_len(first->added_edges)); + printf("f: %d ", BLI_ghash_len(first->added_faces)); + printf(" deleted: "); + printf("v: %d ", BLI_ghash_len(first->deleted_verts)); + printf("e: %d ", BLI_ghash_len(first->deleted_edges)); + printf("pe: %d ", BLI_ghash_len(first->deleted_edges_post)); + printf("f: %d ", BLI_ghash_len(first->deleted_faces)); + printf("\n"); + break; + } + + first = first->combined_next; + } +} + /* Apply a consistent ordering to BMesh vertices */ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) { +#if 0 // TODO: make sure no edge cases relying on this function still exist uint *varr; uint *farr; @@ -618,7 +1464,7 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { const uint id = bm_log_vert_id_get(log, v); const void *key = POINTER_FROM_UINT(id); - const void *val = BLI_ghash_lookup(id_to_idx, key); + const void *val = log_ghash_lookup(log, id_to_idx, key); varr[i] = POINTER_AS_UINT(val); } BLI_ghash_free(id_to_idx, NULL, NULL); @@ -628,15 +1474,49 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { const uint id = bm_log_face_id_get(log, f); const void *key = POINTER_FROM_UINT(id); - const void *val = BLI_ghash_lookup(id_to_idx, key); + const void *val = log_ghash_lookup(log, id_to_idx, key); farr[i] = POINTER_AS_UINT(val); } BLI_ghash_free(id_to_idx, NULL, NULL); - BM_mesh_remap(bm, varr, NULL, farr); + BM_mesh_remap(bm, varr, NULL, farr, NULL); MEM_freeN(varr); MEM_freeN(farr); +#endif +} + +BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) +{ + BMLogEntry *entry = log->current_entry; + + if (!entry) { + printf("no current entry; creating...\n"); + fflush(stdout); + return BM_log_entry_add_ex(bm, log, false); + } + + if (entry->type != LOG_ENTRY_PARTIAL) { + return BM_log_entry_add_ex(bm, log, true); + } + +#ifndef CUSTOMDATA + return entry; +#else + + CustomData *cd1[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + CustomData *cd2[4] = {&entry->vdata, &entry->edata, &entry->ldata, &entry->pdata}; + + for (int i = 0; i < 4; i++) { + if (!CustomData_layout_is_same(cd1[i], cd2[i])) { + printf("Customdata changed for undo\n"); + fflush(stdout); + return BM_log_entry_add_ex(bm, log, true); + } + } + + return entry; +#endif } /* Start a new log entry and update the log entry list @@ -649,8 +1529,22 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) * * In either case, the new entry is set as the current log entry. */ -BMLogEntry *BM_log_entry_add(BMLog *log) +BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log) { + return BM_log_entry_add_ex(bm, log, false); +} + +BMLogEntry *bm_log_entry_add_ex( + BMesh *bm, BMLog *log, bool combine_with_last, BMLogEntryType type, BMLogEntry *last_entry) +{ + if (log->dead) { + fprintf(stderr, "BMLog Error: log is dead\n"); + fflush(stderr); + return NULL; + } + + log->bm = bm; + /* WARNING: this is now handled by the UndoSystem: BKE_UNDOSYS_TYPE_SCULPT * freeing here causes unnecessary complications. */ BMLogEntry *entry; @@ -668,14 +1562,53 @@ BMLogEntry *BM_log_entry_add(BMLog *log) #endif /* Create and append the new entry */ - entry = bm_log_entry_create(); - BLI_addtail(&log->entries, entry); + entry = bm_log_entry_create(type); + + if (!last_entry || last_entry == log->current_entry) { + BLI_addtail(&log->entries, entry); + } + entry->log = log; + + log->refcount++; + + if (combine_with_last) { + if (!last_entry || last_entry == log->current_entry) { + if (log->current_entry) { + log->current_entry->combined_next = entry; + BLI_remlink(&log->entries, log->current_entry); + } + + entry->combined_prev = log->current_entry; + } + else { + entry->combined_prev = last_entry; + last_entry->combined_next = entry; + } + } + + if (type == LOG_ENTRY_PARTIAL) { + CustomData_copy_all_layout(&bm->vdata, &entry->vdata); + CustomData_copy_all_layout(&bm->edata, &entry->edata); + CustomData_copy_all_layout(&bm->ldata, &entry->ldata); + CustomData_copy_all_layout(&bm->pdata, &entry->pdata); + + CustomData_bmesh_init_pool(&entry->vdata, 0, BM_VERT); + CustomData_bmesh_init_pool(&entry->edata, 0, BM_EDGE); + CustomData_bmesh_init_pool(&entry->ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&entry->pdata, 0, BM_FACE); + } + log->current_entry = entry; return entry; } +BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) +{ + return bm_log_entry_add_ex(bm, log, combine_with_last, LOG_ENTRY_PARTIAL, NULL); +} + /* Remove an entry from the log * * Uses entry->log as the log. If the log is NULL, the entry will be @@ -689,6 +1622,11 @@ void BM_log_entry_drop(BMLogEntry *entry) { BMLog *log = entry->log; + // go to head of entry subgroup + while (entry->combined_next) { + entry = entry->combined_next; + } + if (!log) { /* Unlink */ BLI_assert(!(entry->prev && entry->next)); @@ -699,88 +1637,521 @@ void BM_log_entry_drop(BMLogEntry *entry) entry->next->prev = NULL; } + BMLogEntry *entry2 = entry->combined_prev; + while (entry2) { + BMLogEntry *prev = entry2->combined_prev; + + bm_log_entry_free(entry2); + MEM_freeN(entry2); + + entry2 = prev; + } + bm_log_entry_free(entry); MEM_freeN(entry); return; } - if (!entry->prev) { - /* Release IDs of elements that are deleted by this - * entry. Since the entry is at the beginning of the undo - * stack, and it's being deleted, those elements can never be - * restored. Their IDs can go back into the pool. */ - - /* This would never happen usually since first entry of log is - * usually dyntopo enable, which, when reverted will free the log - * completely. However, it is possible have a stroke instead of - * dyntopo enable as first entry if nodes have been cleaned up - * after sculpting on a different object than A, B. - * - * The steps are: - * A dyntopo enable - sculpt - * B dyntopo enable - sculpt - undo (A objects operators get cleaned up) - * A sculpt (now A's log has a sculpt operator as first entry) - * - * Causing a cleanup at this point will call the code below, however - * this will invalidate the state of the log since the deleted vertices - * have been reclaimed already on step 2 (see BM_log_cleanup_entry) - * - * Also, design wise, a first entry should not have any deleted vertices since it - * should not have anything to delete them -from- - */ - // bm_log_id_ghash_release(log, entry->deleted_faces); - // bm_log_id_ghash_release(log, entry->deleted_verts); - } - else if (!entry->next) { - /* Release IDs of elements that are added by this entry. Since - * the entry is at the end of the undo stack, and it's being - * deleted, those elements can never be restored. Their IDs - * can go back into the pool. */ - bm_log_id_ghash_release(log, entry->added_faces); - bm_log_id_ghash_release(log, entry->added_verts); + if (log && log->current_entry == entry) { + log->current_entry = entry->prev; } - else { - BLI_assert_msg(0, "Cannot drop BMLogEntry from middle"); + + if (log) { + BLI_remlink(&log->entries, entry); } - if (log->current_entry == entry) { - log->current_entry = entry->prev; + // free subentries first + BMLogEntry *entry2 = entry->combined_prev; + while (entry2) { + BMLogEntry *prev = entry2->combined_prev; + + bm_log_entry_free(entry2); + MEM_freeN(entry2); + entry2 = prev; } bm_log_entry_free(entry); - BLI_freelinkN(&log->entries, entry); + MEM_freeN(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, + .ignore_id_layers = false})); + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); +} + +static void log_idmap_free(BMLogEntry *entry) +{ + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + MEM_SAFE_FREE(entry->idmap.maps[type]); + entry->idmap.maps[type] = NULL; + entry->idmap.elemtots[type] = 0; + } +} + +static void log_idmap_save(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + log_idmap_free(entry); + + entry->type = LOG_ENTRY_MESH_IDS; + memset((void *)&entry->idmap, 0, sizeof(entry->idmap)); + + entry->idmap.elemmask = BM_VERT | BM_EDGE | BM_FACE; + BMLogIdMap *idmap = &entry->idmap; + + BMIter iter; + + int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID), + CustomData_get_offset(&bm->edata, CD_MESH_ID), + CustomData_get_offset(&bm->ldata, CD_MESH_ID), + CustomData_get_offset(&bm->pdata, CD_MESH_ID)}; + + const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + + // enforce elemmask + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(idmap->elemmask & type) || !tots[i]) { + tots[i] = 0; + cd_id_offs[i] = -1; + } + } + + // set up loop map which is handled specially + if (cd_id_offs[2] >= 0 && tots[2] > 0) { + idmap->maps[BM_LOOP] = MEM_malloc_arrayN((size_t)tots[2], sizeof(int), "idmap->maps[BM_LOOP]"); + } + + for (int i = 0; i < 4; i++) { + if (i == 2) { // loops are saved in face pass + continue; + } + + int type = 1 << i; + const int cd_off = cd_id_offs[i]; + const int tot = tots[i]; + + idmap->elemtots[type] = tot; + + if (cd_off < 0 || tot == 0) { + continue; + } + + int *map = idmap->maps[type] = MEM_malloc_arrayN( + (size_t)tot, sizeof(int), "idmap->maps entry"); + + BMElem *elem; + int j = 0; + int loopi = 0; + int cd_loop_off = cd_id_offs[2]; + int *lmap = idmap->maps[2]; + + bool reported = false; + + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { + int id = BM_ELEM_CD_GET_INT(elem, cd_off); + + if (!reported && (BMElem *)BM_ELEM_FROM_ID(bm, id) != elem) { + printf("IDMap error for elem type %d\n", elem->head.htype); + printf(" further errors suppressed\n"); + reported = true; + } + + map[j] = id; + + // deal with loops + if (type == BM_FACE && cd_loop_off >= 0 && lmap) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + lmap[loopi++] = BM_ELEM_CD_GET_INT(l, cd_loop_off); + } while ((l = l->next) != f->l_first); + } + } + + if (type == BM_FACE) { + idmap->elemtots[BM_LOOP] = loopi; + } + } +} + +static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + const int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID), + CustomData_get_offset(&bm->edata, CD_MESH_ID), + CustomData_get_offset(&bm->ldata, CD_MESH_ID), + CustomData_get_offset(&bm->pdata, CD_MESH_ID)}; + + const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + const int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + BMLogIdMap *idmap = &entry->idmap; + + BM_clear_ids(bm); + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(idmap->elemmask & type) || i == 2) { + continue; + } + + if (cd_id_offs[i] < 0) { + printf("mesh doesn't have ids for elem type %d\n", type); + continue; + } + + if (idmap->elemtots[type] != tots[i]) { + printf("idmap elem count mismatch error"); + continue; + } + + if (!idmap->elemtots[type]) { + continue; + } + + const int cd_loop_id = (idmap->elemmask & type) ? cd_id_offs[2] : -1; + + int j = 0; + BMElem *elem; + BMIter iter; + int *map = idmap->maps[type]; + int loopi = 0; + int *lmap = idmap->maps[BM_LOOP]; + + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { + bm_assign_id(bm, elem, (uint)map[j], false); + + // deal with loops + if (type == BM_FACE && cd_loop_id >= 0) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi], false); + + loopi++; + } while ((l = l->next) != f->l_first); + } + } + } +} + +static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + const int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID), + CustomData_get_offset(&bm->edata, CD_MESH_ID), + CustomData_get_offset(&bm->ldata, CD_MESH_ID), + CustomData_get_offset(&bm->pdata, CD_MESH_ID)}; + + const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + const int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + BMLogIdMap *idmap = &entry->idmap; + + BM_clear_ids(bm); + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(idmap->elemmask & type) || i == 2) { + continue; + } + + if (cd_id_offs[i] < 0) { + printf("mesh doesn't have ids for elem type %d\n", type); + continue; + } + + if (idmap->elemtots[type] != tots[i]) { + printf("idmap elem count mismatch error"); + continue; + } + + if (!idmap->elemtots[type]) { + continue; + } + + const int cd_loop_id = (idmap->elemmask & type) ? cd_id_offs[2] : -1; + + int cd_id = cd_id_offs[i]; + int j = 0; + BMElem *elem; + BMIter iter; + int *map = idmap->maps[type]; + int loopi = 0; + int *lmap = idmap->maps[BM_LOOP]; + + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { + int id = BM_ELEM_CD_GET_INT(elem, cd_id); + + bm_assign_id(bm, elem, (uint)map[j], false); + map[j] = id; + + // deal with loops + if (type == BM_FACE && cd_loop_id >= 0) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + int id2 = BM_ELEM_CD_GET_INT(l, cd_loop_id); + + bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi], false); + lmap[loopi] = id2; + + loopi++; + } while ((l = l->next) != f->l_first); + } + } + } +} + +void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry) +{ + // you cannot set the current entry to a sub-entry, so this should never happen. + while (entry && entry->combined_next) { + entry = entry->combined_next; + } + + log->current_entry = entry; +} + +BMLogEntry *BM_log_all_ids(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + if (!entry) { + entry = bm_log_entry_add_ex(bm, log, false, LOG_ENTRY_MESH_IDS, NULL); + } + else if (entry->type != LOG_ENTRY_MESH_IDS) { + entry = bm_log_entry_add_ex(bm, log, true, LOG_ENTRY_MESH_IDS, entry); + } + + if (!entry) { + // log was dead + return NULL; + } + + log_idmap_save(bm, log, entry); + return entry; +} + +static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0}; + + BMLogEntry tmp = {0}; + + bm_log_full_mesh_intern(bm, log, &tmp); + + 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, + .ignore_id_layers = false})); + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + BKE_mesh_free_data_for_undo(entry->full_copy_mesh); + + entry->full_copy_mesh = tmp.full_copy_mesh; } /* Undo one BMLogEntry * * Has no effect if there's nothing left to undo */ -void BM_log_undo(BMesh *bm, BMLog *log) +static void bm_log_undo_intern( + BMesh *bm, BMLog *log, BMLogEntry *entry, BMLogCallbacks *callbacks, const char *node_layer_id) +{ + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + if (entry->type == LOG_ENTRY_FULL_MESH) { + full_copy_swap(bm, log, entry); + + if (callbacks) { + callbacks->on_full_mesh_load(callbacks->userdata); + } + return; + } + else if (entry->type == LOG_ENTRY_MESH_IDS) { + log_idmap_load(bm, log, entry); + + if (callbacks && callbacks->on_mesh_id_restore) { + callbacks->on_mesh_id_restore(callbacks->userdata); + } + return; + } + + bm_log_edges_restore(bm, log, entry->deleted_edges_post, entry, callbacks); + + /* Delete added faces and verts */ + bm_log_edges_unmake_pre(bm, log, entry->added_edges, entry, callbacks); + bm_log_verts_unmake_pre(bm, log, entry->added_verts, entry, callbacks); + + bm_log_faces_unmake(bm, log, entry->added_faces, entry, callbacks); + bm_log_edges_unmake(bm, log, entry->added_edges, entry, callbacks); + bm_log_verts_unmake(bm, log, entry->added_verts, entry, callbacks); + + /* Restore deleted verts and faces */ + bm_log_verts_restore(bm, log, entry->deleted_verts, entry, callbacks); + bm_log_edges_restore(bm, log, entry->deleted_edges, entry, callbacks); + bm_log_faces_restore(bm, log, entry->deleted_faces, entry, callbacks); + + /* Restore vertex coordinates, mask, and hflag */ + bm_log_vert_values_swap(bm, log, entry->modified_verts, entry, callbacks); + bm_log_edge_values_swap(bm, log, entry->modified_edges, entry, callbacks); + bm_log_face_values_swap(log, entry->modified_faces, entry, callbacks); +} + +void BM_log_undo_skip(BMesh *bm, BMLog *log) +{ + if (log->current_entry) { + log->current_entry = log->current_entry->prev; + } +} + +void BM_log_redo_skip(BMesh *bm, BMLog *log) +{ + if (log->current_entry) { + log->current_entry = log->current_entry->next; + } + else { + log->current_entry = log->entries.first; + } +} + +void BM_log_undo_single(BMesh *bm, + BMLog *log, + BMLogCallbacks *callbacks, + const char *node_layer_id) { BMLogEntry *entry = log->current_entry; + log->bm = bm; - if (entry) { - log->current_entry = entry->prev; + if (!entry) { + return; + } - /* Delete added faces and verts */ - bm_log_faces_unmake(bm, log, entry->added_faces); - bm_log_verts_unmake(bm, log, entry->added_verts); + BMLogEntry *preventry = entry->prev; - /* Restore deleted verts and faces */ - bm_log_verts_restore(bm, log, entry->deleted_verts); - bm_log_faces_restore(bm, log, entry->deleted_faces); + bm_log_undo_intern(bm, log, entry, callbacks, node_layer_id); + entry = entry->combined_prev; - /* Restore vertex coordinates, mask, and hflag */ - bm_log_vert_values_swap(bm, log, entry->modified_verts); - bm_log_face_values_swap(log, entry->modified_faces); + log->current_entry = entry ? entry : preventry; +} + +void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id) +{ + BMLogEntry *entry = log->current_entry; + log->bm = bm; + + if (!entry) { + return; + } + + BMLogEntry *preventry = entry->prev; + + while (entry) { + bm_log_undo_intern(bm, log, entry, callbacks, node_layer_id); + entry = entry->combined_prev; } + + log->current_entry = preventry; } /* Redo one BMLogEntry * * Has no effect if there's nothing left to redo */ -void BM_log_redo(BMesh *bm, BMLog *log) +static void bm_log_redo_intern( + BMesh *bm, BMLog *log, BMLogEntry *entry, BMLogCallbacks *callbacks, const char *node_layer_id) +{ + if (entry->type == LOG_ENTRY_FULL_MESH) { + // hrm, should we swap? + full_copy_swap(bm, log, entry); + + if (callbacks) { + callbacks->on_full_mesh_load(callbacks->userdata); + } + + return; + } + else if (entry->type == LOG_ENTRY_MESH_IDS) { + log_idmap_load(bm, log, entry); + + if (callbacks && callbacks->on_mesh_id_restore) { + callbacks->on_mesh_id_restore(callbacks->userdata); + } + return; + } + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + /* Re-delete previously deleted faces and verts */ + bm_log_edges_unmake_pre(bm, log, entry->deleted_edges, entry, callbacks); + bm_log_verts_unmake_pre(bm, log, entry->deleted_verts, entry, callbacks); + + bm_log_faces_unmake(bm, log, entry->deleted_faces, entry, callbacks); + bm_log_edges_unmake(bm, log, entry->deleted_edges, entry, callbacks); + bm_log_verts_unmake(bm, log, entry->deleted_verts, entry, callbacks); + + /* Restore previously added verts and faces */ + bm_log_verts_restore(bm, log, entry->added_verts, entry, callbacks); + bm_log_edges_restore(bm, log, entry->added_edges, entry, callbacks); + bm_log_faces_restore(bm, log, entry->added_faces, entry, callbacks); + + bm_log_edges_unmake(bm, log, entry->deleted_edges_post, entry, callbacks); + + /* Restore vertex coordinates, mask, and hflag */ + bm_log_vert_values_swap(bm, log, entry->modified_verts, entry, callbacks); + bm_log_edge_values_swap(bm, log, entry->modified_edges, entry, callbacks); + bm_log_face_values_swap(log, entry->modified_faces, entry, callbacks); +} + +BMLogEntry *BM_log_entry_prev(BMLogEntry *entry) +{ + return entry->prev; +} + +BMLogEntry *BM_log_entry_next(BMLogEntry *entry) +{ + return entry->next; +} + +void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id) { BMLogEntry *entry = log->current_entry; + log->bm = bm; if (!entry) { /* Currently at the beginning of the undo stack, move to first entry */ @@ -790,26 +2161,24 @@ void BM_log_redo(BMesh *bm, BMLog *log) /* Move to next undo entry */ entry = entry->next; } - else { + + if (!entry) { /* Currently at the end of the undo stack, nothing left to redo */ return; } - log->current_entry = entry; - - if (entry) { - /* Re-delete previously deleted faces and verts */ - bm_log_faces_unmake(bm, log, entry->deleted_faces); - bm_log_verts_unmake(bm, log, entry->deleted_verts); + BMLogEntry *nextentry = entry; - /* Restore previously added verts and faces */ - bm_log_verts_restore(bm, log, entry->added_verts); - bm_log_faces_restore(bm, log, entry->added_faces); + while (entry->combined_prev) { + entry = entry->combined_prev; + } - /* Restore vertex coordinates, mask, and hflag */ - bm_log_vert_values_swap(bm, log, entry->modified_verts); - bm_log_face_values_swap(log, entry->modified_faces); + while (entry) { + bm_log_redo_intern(bm, log, entry, callbacks, node_layer_id); + entry = entry->combined_next; } + + log->current_entry = nextentry; } /* Log a vertex before it is modified @@ -835,54 +2204,99 @@ void BM_log_redo(BMesh *bm, BMLog *log) * state so that a subsequent redo operation will restore the newer * vertex state. */ -void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_offset) +void BM_log_vert_before_modified(BMLog *log, + BMVert *v, + const int cd_vert_mask_offset, + bool log_customdata) { BMLogEntry *entry = log->current_entry; BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); + uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v); void *key = POINTER_FROM_UINT(v_id); void **val_p; + // LOGPRINT("key %d\n", (int)key); + /* Find or create the BMLogVert entry */ - if ((lv = BLI_ghash_lookup(entry->added_verts, key))) { - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); + if ((lv = log_ghash_lookup(log, entry->added_verts, key))) { + bm_log_vert_bmvert_copy(log, entry, lv, v, -1, log_customdata); } - else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) { - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); + else if (!log_ghash_ensure_p(log, entry->modified_verts, key, &val_p)) { + lv = bm_log_vert_alloc(log, v, -1, true); *val_p = lv; } } +void BM_log_edge_before_modified(BMLog *log, BMEdge *e, bool log_customdata) +{ + BMLogEntry *entry = log->current_entry; + BMLogEdge *le; + uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e); + void *key = POINTER_FROM_UINT(e_id); + void **val_p; + + /* Find or create the BMLogVert entry */ + if ((le = log_ghash_lookup(log, entry->added_edges, key))) { + bm_log_edge_bmedge_copy(log, entry, le, e, log_customdata); + } + else if (!log_ghash_ensure_p(log, entry->modified_edges, key, &val_p)) { + le = bm_log_edge_alloc(log, e, true); + *val_p = le; + } +} + +/* Log a new edge as added to the BMesh + */ +void BM_log_edge_added(BMLog *log, BMEdge *e) +{ + // return; // XXX + BMLogEdge *le; + uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e); + void *key = POINTER_FROM_UINT(e_id); + void **val = NULL; + + LOGPRINT("key %d\n", (int)key); + + le = bm_log_edge_alloc(log, e, true); + SET_MSG(le); + + if (BLI_ghash_ensure_p(log->current_entry->added_edges, key, &val)) { + BLI_mempool_free(log->current_entry->pool_edges, *val); + } + + *val = le; +} + /* Log a new vertex as added to the BMesh - * - * The new vertex gets a unique ID assigned. It is then added to a map - * of added vertices, with the key being its ID and the value - * containing everything needed to reconstruct that vertex. */ void BM_log_vert_added(BMLog *log, BMVert *v, const int cd_vert_mask_offset) { BMLogVert *lv; - uint v_id = range_tree_uint_take_any(log->unused_ids); + uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v); void *key = POINTER_FROM_UINT(v_id); - bm_log_vert_id_set(log, v, v_id); - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(log->current_entry->added_verts, key, lv); + LOGPRINT("key %d\n", (int)key); + + lv = bm_log_vert_alloc(log, v, -1, true); + log_ghash_insert(log, log->current_entry->added_verts, key, lv); } /* Log a face before it is modified * - * This is intended to handle only header flags and we always - * assume face has been added before + * We always assume face has been added before */ void BM_log_face_modified(BMLog *log, BMFace *f) { BMLogFace *lf; - uint f_id = bm_log_face_id_get(log, f); + uint f_id = (uint)BM_ELEM_GET_ID(log->bm, f); void *key = POINTER_FROM_UINT(f_id); + // LOGPRINT("key %d\n", (int)key); + lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->modified_faces, key, lf); + + log_ghash_insert(log, log->current_entry->modified_faces, key, lf); + bm_log_face_customdata(log->bm, log, f, lf); } /* Log a new face as added to the BMesh @@ -894,15 +2308,15 @@ void BM_log_face_modified(BMLog *log, BMFace *f) void BM_log_face_added(BMLog *log, BMFace *f) { BMLogFace *lf; - uint f_id = range_tree_uint_take_any(log->unused_ids); + uint f_id = (uint)BM_ELEM_GET_ID(log->bm, f); void *key = POINTER_FROM_UINT(f_id); - /* Only triangles are supported for now */ - BLI_assert(f->len == 3); + LOGPRINT("key %d\n", (int)key); - bm_log_face_id_set(log, f, f_id); lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->added_faces, key, lf); + log_ghash_insert(log, log->current_entry->added_faces, key, lf); + + bm_log_face_customdata(log->bm, log, f, lf); } /* Log a vertex as removed from the BMesh @@ -924,28 +2338,185 @@ void BM_log_face_added(BMLog *log, BMFace *f) void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset) { BMLogEntry *entry = log->current_entry; - uint v_id = bm_log_vert_id_get(log, v); + uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v); void *key = POINTER_FROM_UINT(v_id); - /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_verts, key) == - !!BLI_ghash_haskey(entry->added_verts, key)); + LOGPRINT("key %d\n", (int)key); - if (BLI_ghash_remove(entry->added_verts, key, NULL, NULL)) { - range_tree_uint_release(log->unused_ids, v_id); - } - else { + if (!log_ghash_remove(log, entry->added_verts, key, NULL, NULL)) { BMLogVert *lv, *lv_mod; - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(entry->deleted_verts, key, lv); + lv = bm_log_vert_alloc(log, v, -1, false); + log_ghash_insert(log, entry->deleted_verts, key, lv); /* If the vertex was modified before deletion, ensure that the * original vertex values are stored */ - if ((lv_mod = BLI_ghash_lookup(entry->modified_verts, key))) { + if ((lv_mod = log_ghash_lookup(log, entry->modified_verts, key))) { + if (lv->customdata) { + BLI_mempool_free(entry->vdata.pool, lv->customdata); + } + (*lv) = (*lv_mod); - BLI_ghash_remove(entry->modified_verts, key, NULL, NULL); + lv_mod->customdata = NULL; + + log_ghash_remove(log, entry->modified_verts, key, NULL, NULL); + BLI_mempool_free(entry->pool_verts, lv_mod); + } + else { + bm_log_vert_customdata(log->bm, log, entry, v, lv); + } + } +} + +void BM_log_edge_removed_post(BMLog *log, BMEdge *e) +{ + BMLogEntry *entry = log->current_entry; + uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e); + void *key = POINTER_FROM_UINT(e_id); + + LOGPRINT("key %d\n", (int)key); + + if (1) { //! log_ghash_remove(log, entry->added_edges, key, NULL, NULL)) { + BMLogEdge *le, *le_mod; + void **val; + + le = bm_log_edge_alloc(log, e, false); + SET_MSG(le); + + if (BLI_ghash_ensure_p(entry->deleted_edges_post, key, &val)) { + BLI_mempool_free(entry->pool_edges, *val); } + + *val = (void *)le; + +#if 1 + /* If the vertex was modified before deletion, ensure that the + * original edge values are stored */ + if ((le_mod = log_ghash_lookup(log, entry->modified_edges, key))) { + if (le->customdata) { + BLI_mempool_free(entry->edata.pool, le->customdata); + } + + (*le) = (*le_mod); + le_mod->customdata = NULL; + + SET_MSG(le); + + log_ghash_remove(log, entry->modified_edges, key, NULL, NULL); + BLI_mempool_free(entry->pool_edges, le_mod); + } + else { + bm_log_edge_customdata(log->bm, log, entry, e, le); + } +#else + bm_log_edge_customdata(log->bm, log, entry, e, le); +#endif + } +} + +/** +Splits e and logs the new edge and vertex. +e is assigned a new ID. +*/ +BMVert *BM_log_edge_split_do(BMLog *log, BMEdge *e, BMVert *v, BMEdge **newe, float t) +{ +#if 0 + BMVert *newv = BM_edge_split(log->bm, e, v, newe, t); + BM_log_vert_added(log, newv, -1); + + return newv; + +#else + BMEdge *tmp = NULL; + if (!newe) { + newe = &tmp; + } + + BMesh *bm = log->bm; + + int eid0 = BM_ELEM_GET_ID(bm, e); + + bm_log_message("edge split"); + bm_log_message(" esplit: remove edge %d", eid0); + BM_log_edge_removed(log, e); + + BMVert *v1 = e->v1, *v2 = e->v2; + uint id1 = (uint)BM_ELEM_GET_ID(bm, v1); + uint id2 = (uint)BM_ELEM_GET_ID(bm, v2); + + bm_log_message(" esplit: split edge %d (v1=%d v2=%d)", eid0, id1, id2); + BMVert *newv = BM_edge_split(log->bm, e, v, newe, t); + + uint id3 = (uint)BM_ELEM_GET_ID(bm, newv); + uint nid = (uint)BM_ELEM_GET_ID(bm, (*newe)); + + // get a new id + uint id = range_tree_uint_take_any(log->bm->idmap.idtree); + + bm_free_id(log->bm, (BMElem *)e); + bm_assign_id(log->bm, (BMElem *)e, id, false); + + bm_log_message(" esplit: add new vert %d", id3); + BM_log_vert_added(log, newv, -1); + + bm_log_message(" esplit: add old edge (with new id %d)", id); + BM_log_edge_added(log, e); + + bm_log_message(" esplit: add new edge %d", nid); + BM_log_edge_added(log, *newe); + + return newv; +#endif +} + +void BM_log_edge_removed(BMLog *log, BMEdge *e) +{ + if (e->head.htype != BM_EDGE) { + printf("%s: e is not an edge; htype: %d\n", __func__, (int)e->head.htype); + return; + } + + // return; // XXX + BMLogEntry *entry = log->current_entry; + uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e); + void *key = POINTER_FROM_UINT(e_id); + + LOGPRINT("key %d\n", (int)key); + + if (!log_ghash_remove(log, entry->added_edges, key, NULL, NULL)) { + BMLogEdge *le, *le_mod; + void **val; + + le = bm_log_edge_alloc(log, e, false); + SET_MSG(le); + + if (BLI_ghash_ensure_p(entry->deleted_edges, key, &val)) { + BLI_mempool_free(entry->pool_edges, *val); + } + + *val = (void *)le; + +#if 1 + /* If the edge was modified before deletion, ensure that the + * original edge values are stored */ + if ((le_mod = log_ghash_lookup(log, entry->modified_edges, key))) { + if (le->customdata) { + BLI_mempool_free(entry->edata.pool, le->customdata); + } + + (*le) = (*le_mod); + le_mod->customdata = NULL; + SET_MSG(le); + + log_ghash_remove(log, entry->modified_edges, key, NULL, NULL); + BLI_mempool_free(entry->pool_edges, le_mod); + } + else { + bm_log_edge_customdata(log->bm, log, entry, e, le); + } +#else + bm_log_edge_customdata(log->bm, log, entry, e, le); +#endif } } @@ -965,30 +2536,54 @@ void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset) void BM_log_face_removed(BMLog *log, BMFace *f) { BMLogEntry *entry = log->current_entry; - uint f_id = bm_log_face_id_get(log, f); + uint f_id = (uint)BM_ELEM_GET_ID(log->bm, f); void *key = POINTER_FROM_UINT(f_id); + LOGPRINT("key %d\n", (int)key); + /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_faces, key) == - !!BLI_ghash_haskey(entry->added_faces, key)); + BLI_assert(!!log_ghash_lookup(log, entry->added_faces, key) == + !!log_ghash_haskey(log, entry->added_faces, key)); - if (BLI_ghash_remove(entry->added_faces, key, NULL, NULL)) { - range_tree_uint_release(log->unused_ids, f_id); - } - else { - BMLogFace *lf; + if (!log_ghash_remove(log, entry->added_faces, key, NULL, NULL)) { + BMLogFace *lf = bm_log_face_alloc(log, f); + + void **val; + if (BLI_ghash_ensure_p(entry->deleted_faces, key, &val)) { + BMLogFace *lf2 = (BMLogFace *)*val; - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(entry->deleted_faces, key, lf); + if (lf2->customdata_f) { + // BLI_mempool_free(entry->pdata.pool, lf2->customdata_f); + CustomData_bmesh_free_block(&entry->pdata, &lf2->customdata_f); + } + + for (uint i = 0; i < lf2->len; i++) { + if (lf2->customdata[i]) { + CustomData_bmesh_free_block(&entry->ldata, &lf2->customdata[i]); + } + } + + BLI_mempool_free(entry->pool_faces, (void *)lf2); + } + + if (lf) { + bm_log_face_customdata(log->bm, log, f, lf); + } + + *val = lf; } } /* Log all vertices/faces in the BMesh as added */ void BM_log_all_added(BMesh *bm, BMLog *log) { - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + if (!log->current_entry) { + BM_log_entry_add_ex(bm, log, false); + } + BMIter bm_iter; BMVert *v; + BMEdge *e; BMFace *f; /* avoid unnecessary resizing on initialization */ @@ -1002,7 +2597,12 @@ void BM_log_all_added(BMesh *bm, BMLog *log) /* Log all vertices as newly created */ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_added(log, v, cd_vert_mask_offset); + BM_log_vert_added(log, v, -1); + } + + /* Log all edges as newly created */ + BM_ITER_MESH (e, &bm_iter, bm, BM_EDGES_OF_MESH) { + BM_log_edge_added(log, e); } /* Log all faces as newly created */ @@ -1011,12 +2611,52 @@ void BM_log_all_added(BMesh *bm, BMLog *log) } } +void BM_log_full_mesh(BMesh *bm, BMLog *log) +{ + BMLogEntry *entry = log->current_entry; + + if (!entry) { + entry = bm_log_entry_add_ex(bm, log, false, LOG_ENTRY_FULL_MESH, NULL); + } + + // add an entry if current entry isn't empty or isn't LOG_ENTRY_PARTIAL + bool add = false; + + if (entry->type == LOG_ENTRY_PARTIAL) { + add = BLI_ghash_len(entry->added_faces) > 0; + add |= BLI_ghash_len(entry->modified_verts) > 0; + add |= BLI_ghash_len(entry->modified_faces) > 0; + add |= BLI_ghash_len(entry->deleted_verts) > 0; + add |= BLI_ghash_len(entry->deleted_faces) > 0; + } + else { + add = true; + } + + if (add) { + entry = bm_log_entry_add_ex(bm, log, true, LOG_ENTRY_FULL_MESH, NULL); + } + else { + bm_log_entry_free_direct(entry); + entry->type = LOG_ENTRY_FULL_MESH; + } + + bm_log_full_mesh_intern(bm, log, entry); + + // push a fresh entry + BM_log_entry_add_ex(bm, log, true); +} + /* Log all vertices/faces in the BMesh as removed */ void BM_log_before_all_removed(BMesh *bm, BMLog *log) { - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + if (!log->current_entry) { + BM_log_entry_add_ex(bm, log, false); + } + BMIter bm_iter; BMVert *v; + BMEdge *e; BMFace *f; /* Log deletion of all faces */ @@ -1024,9 +2664,13 @@ void BM_log_before_all_removed(BMesh *bm, BMLog *log) BM_log_face_removed(log, f); } + BM_ITER_MESH (e, &bm_iter, bm, BM_EDGES_OF_MESH) { + BM_log_edge_removed(log, e); + } + /* Log deletion of all vertices */ BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_removed(log, v, cd_vert_mask_offset); + BM_log_vert_removed(log, v, -1); } } @@ -1037,65 +2681,57 @@ const float *BM_log_original_vert_co(BMLog *log, BMVert *v) { BMLogEntry *entry = log->current_entry; const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); + uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v); void *key = POINTER_FROM_UINT(v_id); BLI_assert(entry); - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); + BLI_assert(log_ghash_haskey(log, entry->modified_verts, key)); - lv = BLI_ghash_lookup(entry->modified_verts, key); + lv = log_ghash_lookup(log, entry->modified_verts, key); return lv->co; } /* Get the logged normal of a vertex * * Does not modify the log or the vertex */ -const short *BM_log_original_vert_no(BMLog *log, BMVert *v) +const float *BM_log_original_vert_no(BMLog *log, BMVert *v) { BMLogEntry *entry = log->current_entry; const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); + uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v); void *key = POINTER_FROM_UINT(v_id); BLI_assert(entry); - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); + BLI_assert(log_ghash_haskey(log, entry->modified_verts, key)); - lv = BLI_ghash_lookup(entry->modified_verts, key); + lv = log_ghash_lookup(log, entry->modified_verts, key); return lv->no; } -/* Get the logged mask of a vertex +/* DEPRECATED + * Get the logged mask of a vertex * * Does not modify the log or the vertex */ float BM_log_original_mask(BMLog *log, BMVert *v) { - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->mask; + MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, log->cd_dyn_vert); + return mv->origmask; } -void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const short **r_no) +void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no) { BMLogEntry *entry = log->current_entry; const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); + uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v); void *key = POINTER_FROM_UINT(v_id); BLI_assert(entry); - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); + BLI_assert(log_ghash_haskey(log, entry->modified_verts, key)); - lv = BLI_ghash_lookup(entry->modified_verts, key); + lv = log_ghash_lookup(log, entry->modified_verts, key); *r_co = lv->co; *r_no = lv->no; } @@ -1108,12 +2744,6 @@ BMLogEntry *BM_log_current_entry(BMLog *log) return log->current_entry; } -/* For internal use only (unit testing) */ -RangeTreeUInt *BM_log_unused_ids(BMLog *log) -{ - return log->unused_ids; -} - #if 0 /* Print the list of entries, marking the current one * @@ -1131,3 +2761,58 @@ void bm_log_print(const BMLog *log, const char *description) } } #endif + +static int bmlog_entry_memsize(BMLogEntry *entry) +{ + int ret = 0; + + if (entry->type == LOG_ENTRY_PARTIAL) { + ret += (int)BLI_mempool_get_size(entry->pool_verts); + ret += (int)BLI_mempool_get_size(entry->pool_edges); + ret += (int)BLI_mempool_get_size(entry->pool_faces); + ret += entry->vdata.pool ? (int)BLI_mempool_get_size(entry->vdata.pool) : 0; + ret += entry->edata.pool ? (int)BLI_mempool_get_size(entry->edata.pool) : 0; + ret += entry->ldata.pool ? (int)BLI_mempool_get_size(entry->ldata.pool) : 0; + ret += entry->pdata.pool ? (int)BLI_mempool_get_size(entry->pdata.pool) : 0; + + // estimate ghash memory usage + ret += (int)BLI_ghash_len(entry->added_verts) * (int)sizeof(void *) * 4; + ret += (int)BLI_ghash_len(entry->added_edges) * (int)sizeof(void *) * 4; + ret += (int)BLI_ghash_len(entry->added_faces) * (int)sizeof(void *) * 4; + + ret += (int)BLI_ghash_len(entry->modified_verts) * (int)sizeof(void *) * 4; + ret += (int)BLI_ghash_len(entry->modified_edges) * (int)sizeof(void *) * 4; + ret += (int)BLI_ghash_len(entry->modified_faces) * (int)sizeof(void *) * 4; + + ret += (int)BLI_ghash_len(entry->deleted_verts) * (int)sizeof(void *) * 4; + ret += (int)BLI_ghash_len(entry->deleted_edges) * (int)sizeof(void *) * 4; + ret += (int)BLI_ghash_len(entry->deleted_faces) * (int)sizeof(void *) * 4; + } + else if (entry->type == LOG_ENTRY_FULL_MESH) { + Mesh *me = entry->full_copy_mesh; + + ret += me->totvert * me->vdata.totsize; + ret += me->totedge * me->edata.totsize; + ret += me->totloop * me->ldata.totsize; + ret += me->totpoly * me->pdata.totsize; + } + + return ret; +} + +int BM_log_entry_size(BMLogEntry *entry) +{ + while (entry->combined_prev) { + entry = entry->combined_prev; + } + + int ret = 0; + + while (entry) { + ret += bmlog_entry_memsize(entry); + + entry = entry->combined_next; + } + + return ret; +} |