Today I ran outside screaming and an hour later came back in and made BMLog

reference counted.  This fixes various undo bugs caused by dyntopo
needing to execute an undo push but not being able too (e.g. during
file load, and I think it also happens sometimes with global undo).

A much better way of fixing this would be to add unique IDs to mesh
verts and faces, perhaps as a customdata layer.

The root problem is that BMLog assigns unique IDs to mesh elements,
which it does via a series of GHash's.  I imagine this is a fairly
serious violation of the undo system's design rules, since it means
BMLogs are tied to specific instances of the bmesh in SculptSession->bm.

There were some hacks to try and get around this, but they were buggy
and needed to push the unstack stack to work, which wasn't possible in
all cases (e.g. if G_MAIN->wm.first->undo_stack is NULL, as it is during
file loading and apparently global undo).

Anyway, long story short each chain of SculptUndoNodes needs some way
to reconstruct their ID GHash's when exiting/entering the chain. The
only way I could think to do this was to make BMLog reference counted,
with BMLogEntry the users.

Like I said, having a proper system to assign unique IDs to mesh
elements would be *much* better.
This commit is contained in:
Joseph Eagar 2021-05-11 21:14:45 -07:00
parent dbe767f5d9
commit b8a8e4f9b1
11 changed files with 536 additions and 135 deletions

View File

@ -652,6 +652,10 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
(BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMVert *)(v.i)) : (v.i))
SculptVertRef BKE_pbvh_table_index_to_vertex(PBVH *pbvh, int idx);
#define BKE_pbvh_face_index_to_table(pbvh, v) \
(BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMFace *)(v.i)) : (v.i))
SculptFaceRef BKE_pbvh_table_index_to_face(PBVH *pbvh, int idx);
void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count);
void BKE_pbvh_node_free_proxies(PBVHNode *node);
PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node);

View File

@ -51,6 +51,7 @@
#include "BKE_context.h"
#include "BKE_crazyspace.h"
#include "BKE_deform.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_idtype.h"
#include "BKE_image.h"
@ -67,6 +68,7 @@
#include "BKE_pbvh.h"
#include "BKE_subdiv_ccg.h"
#include "BKE_subsurf.h"
#include "BKE_undo_system.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@ -79,6 +81,7 @@
// XXX todo: work our bad module cross ref
void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me);
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
static void palette_init_data(ID *id)
{
@ -1380,12 +1383,6 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder)
if (ss->bm) {
if (ob->data) {
BMIter iter;
BMFace *efa;
BM_ITER_MESH (efa, &iter, ss->bm, BM_FACES_OF_MESH) {
BM_elem_flag_set(efa, BM_ELEM_SMOOTH, ss->bm_smooth_shading);
}
if (reorder) {
BM_log_mesh_elems_reorder(ss->bm, ss->bm_log);
}
@ -1473,7 +1470,14 @@ void BKE_sculptsession_free(Object *ob)
if (ob && ob->sculpt) {
SculptSession *ss = ob->sculpt;
if (ss->bm_log) {
BM_log_free(ss->bm_log, true);
}
/*try to save current mesh*/
if (ss->bm) {
SCULPT_on_sculptsession_bmesh_free(ss);
BKE_sculptsession_bm_to_me(ob, true);
BM_mesh_free(ss->bm);
}
@ -1489,10 +1493,6 @@ void BKE_sculptsession_free(Object *ob)
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
if (ss->bm_log) {
BM_log_free(ss->bm_log);
}
MEM_SAFE_FREE(ss->texcache);
if (ss->tex_pool) {
@ -1911,7 +1911,7 @@ void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene)
if (!sd->detail_range) {
sd->detail_range = 0.4f;
sd->flags |= SCULPT_DYNTOPO_CLEANUP; //should really do this in do_versions_290.c
sd->flags |= SCULPT_DYNTOPO_CLEANUP; // should really do this in do_versions_290.c
}
if (!sd->detail_percent) {

View File

@ -3174,6 +3174,17 @@ SculptVertRef BKE_pbvh_table_index_to_vertex(PBVH *pbvh, int idx)
return BKE_pbvh_make_vref(idx);
}
SculptFaceRef BKE_pbvh_table_index_to_face(PBVH *pbvh, int idx)
{
if (pbvh->type == PBVH_BMESH) {
SculptFaceRef ref = {(intptr_t)pbvh->bm->ftable[idx]};
return ref;
}
return BKE_pbvh_make_fref(idx);
}
bool pbvh_has_face_sets(PBVH *pbvh)
{
switch (pbvh->type) {

View File

@ -108,6 +108,10 @@ struct BMLog {
/* Tree of free IDs */
struct RangeTreeUInt *unused_ids;
BMLogEntry *frozen_full_mesh;
int refcount;
/* Mapping from unique IDs to vertices and faces
*
* Each vertex and face in the log gets a unique uinteger
@ -161,6 +165,9 @@ typedef struct {
#define logkey_hash BLI_ghashutil_inthash_p_simple
#define logkey_cmp BLI_ghashutil_intcmp
static void full_copy_swap(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)
{
BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ);
@ -665,6 +672,15 @@ static BMLogEntry *bm_log_entry_create(void)
* Note: does not free the log entry itself */
static void bm_log_entry_free(BMLogEntry *entry)
{
if (entry->log) {
entry->log->refcount--;
if (entry->log->refcount < 0) {
fprintf(stderr, "BMLog refcount error\n");
entry->log->refcount = 0;
}
}
if (entry->full_copy_mesh) {
BKE_mesh_free(entry->full_copy_mesh);
@ -855,11 +871,41 @@ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
return log;
}
/* Free all the data in a BMLog including the log itself */
void BM_log_free(BMLog *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;
}
full_copy_swap(bm, entry->log, 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;
}
/* 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
*/
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);
}
return;
}
BLI_rw_mutex_end(&log->lock);
if (log->unused_ids) {
@ -1010,6 +1056,8 @@ BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last)
BLI_addtail(&log->entries, entry);
entry->log = log;
log->refcount++;
#ifdef CUSTOMDATA
if (combine_with_last) {
if (log->current_entry) {
@ -1162,7 +1210,7 @@ static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
else {
// eek, error!
printf("bmlog error!\n");
log_ghash_remove(log, log->id_to_elem, (void*)id, NULL, NULL);
log_ghash_remove(log, log->id_to_elem, (void *)id, NULL, NULL);
}
}
@ -1474,6 +1522,10 @@ void BM_log_face_removed(BMLog *log, BMFace *f)
/* Log all vertices/faces in the BMesh as added */
void BM_log_all_added(BMesh *bm, BMLog *log)
{
if (!log->current_entry) {
BM_log_entry_add_ex(bm, log, false);
}
const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
BMIter bm_iter;
BMVert *v;
@ -1524,6 +1576,10 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log)
/* Log all vertices/faces in the BMesh as removed */
void BM_log_before_all_removed(BMesh *bm, BMLog *log)
{
if (!log->current_entry) {
BM_log_entry_add_ex(bm, log, false);
}
const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
BMIter bm_iter;
BMVert *v;

View File

@ -36,9 +36,11 @@ void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert);
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry);
/* Free all the data in a BMLog including the log itself */
void BM_log_free(BMLog *log);
void BM_log_free(BMLog *log, bool safe_mode);
/* Get the number of log entries */
BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry);
/* Get the number of log entries */
int BM_log_length(const BMLog *log);
/* Apply a consistent ordering to BMesh vertices and faces */

View File

@ -117,6 +117,22 @@ void SCULPT_vertex_random_access_ensure(SculptSession *ss)
}
}
/* Sculpt PBVH abstraction API
*
* This is read-only, for writing use PBVH vertex iterators. There vd.index matches
* the indices used here.
*
* For multi-resolution, the same vertex in multiple grids is counted multiple times, with
* different index for each grid. */
void SCULPT_face_random_access_ensure(SculptSession *ss)
{
if (ss->bm) {
BM_mesh_elem_index_ensure(ss->bm, BM_FACE);
BM_mesh_elem_table_ensure(ss->bm, BM_FACE);
}
}
int SCULPT_vertex_count_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
@ -9176,7 +9192,7 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
}
}
void ED_object_sculptmode_enter_ex(Main *bmain,
ATTR_NO_OPT void ED_object_sculptmode_enter_ex(Main *bmain,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,

View File

@ -23,10 +23,15 @@
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_array.h"
#include "BLI_blenlib.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"
#include "BLT_translation.h"
@ -77,12 +82,79 @@
#include <math.h>
#include <stdlib.h>
void SCULPT_dynamic_topology_triangulate(BMesh *bm)
ATTR_NO_OPT void SCULPT_dynamic_topology_triangulate(BMesh *bm)
{
if (bm->totloop != bm->totface * 3) {
BM_mesh_triangulate(
bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL);
if (bm->totloop == bm->totface * 3) {
return;
}
BMIter iter;
BMFace *f;
BM_ITER_MESH(f, &iter, bm, BM_FACES_OF_MESH) {
BM_elem_flag_enable(f, BM_ELEM_TAG);
}
MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__);
LinkNode *f_double = NULL;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (f->len <= 3) {
continue;
}
bool sel = BM_elem_flag_test(f, BM_ELEM_SELECT);
int faces_array_tot = f->len;
BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
printf("%d: ", f->head.hflag);
BM_face_triangulate(bm,
f,
faces_array,
&faces_array_tot,
NULL,
NULL,
&f_double,
MOD_TRIANGULATE_QUAD_BEAUTY,
MOD_TRIANGULATE_NGON_EARCLIP,
true,
pf_arena,
NULL);
printf("%d, ", f->head.hflag);
for (int i = 0; i < faces_array_tot; i++) {
BMFace *f2 = faces_array[i];
//forcibly copy selection state
if (sel) {
BM_face_select_set(bm, f2, true);
//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
BM_elem_flag_disable(f2, BM_ELEM_TAG);
}
}
printf("\n");
while (f_double) {
LinkNode *next = f_double->next;
BM_face_kill(bm, f_double->link);
MEM_freeN(f_double);
f_double = next;
}
BLI_memarena_free(pf_arena);
// BM_mesh_triangulate(
// bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL,
// NULL);
}
void SCULPT_pbvh_clear(Object *ob)
@ -326,7 +398,7 @@ void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me)
break;
}
//based off of how CustomData_set_layer_XXXX_index works
// based off of how CustomData_set_layer_XXXX_index works
cl3->active = (cl2->active + baseidx) - k;
cl3->active_rnd = (cl2->active_rnd + baseidx) - k;
@ -545,7 +617,7 @@ static void SCULPT_dynamic_topology_disable_ex(
ss->bm = NULL;
}
if (ss->bm_log) {
BM_log_free(ss->bm_log);
BM_log_free(ss->bm_log, true);
ss->bm_log = NULL;
}

View File

@ -156,10 +156,12 @@ enum {
*/
static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
const SculptVertRef v)
{
const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) {
if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) {
return true;
}
}
@ -171,10 +173,18 @@ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
*/
static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
const int f)
const SculptFaceRef f)
{
const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart];
return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v);
if (ss->bm) {
BMFace *bf = (BMFace*)f.i;
BMLoop *l = bf->l_first;
SculptVertRef v = {(intptr_t)l->v};
return sculpt_expand_is_vert_in_active_component(ss, expand_cache, v);
} else {
const MLoop *loop = &ss->mloop[ss->mpoly[f.i].loopstart];
return sculpt_expand_is_vert_in_active_component(ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, loop->v));
}
}
/**
@ -183,24 +193,26 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
*/
static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
const SculptVertRef v)
{
const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
if (expand_cache->texture_distortion_strength == 0.0f) {
return expand_cache->vert_falloff[v];
return expand_cache->vert_falloff[v_i];
}
if (!expand_cache->brush->mtex.tex) {
return expand_cache->vert_falloff[v];
return expand_cache->vert_falloff[v_i];
}
float rgba[4];
const float *vertex_co = SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v));
const float *vertex_co = SCULPT_vertex_co_get(ss, v);
const float avg = BKE_brush_sample_tex_3d(
expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool);
const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength *
expand_cache->max_vert_falloff;
return expand_cache->vert_falloff[v] + distortion;
return expand_cache->vert_falloff[v_i] + distortion;
}
/**
@ -225,11 +237,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache)
* Main function to get the state of a vertex for the current state and settings of a #ExpandCache.
* Returns true when the target data should be modified by expand.
*/
static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v)
static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const SculptVertRef v)
{
SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v);
if (!SCULPT_vertex_visible_get(ss, vref)) {
if (!SCULPT_vertex_visible_get(ss, v)) {
return false;
}
@ -247,7 +257,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
/* Face Sets are not being modified when using this function, so it is ok to get this directly
* from the Sculpt API instead of implementing a custom function to get them from
* expand_cache->original_face_sets. */
const int face_set = SCULPT_vertex_face_set_get(ss, vref);
const int face_set = SCULPT_vertex_face_set_get(ss, v);
enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set));
}
else {
@ -273,9 +283,11 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
* Main function to get the state of a face for the current state and settings of a #ExpandCache.
* Returns true when the target data should be modified by expand.
*/
static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f)
static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const SculptFaceRef f)
{
if (ss->face_sets[f] <= 0) {
const int f_i = BKE_pbvh_face_index_to_table(ss->pbvh, f);
if (ss->face_sets[f_i] <= 0) {
return false;
}
@ -290,7 +302,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
bool enabled = false;
if (expand_cache->snap_enabled_face_sets) {
const int face_set = expand_cache->original_face_sets[f];
const int face_set = expand_cache->original_face_sets[f_i];
enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set));
}
else {
@ -298,12 +310,12 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
SCULPT_EXPAND_LOOP_THRESHOLD;
const float active_factor = fmod(expand_cache->active_falloff, loop_len);
const float falloff_factor = fmod(expand_cache->face_falloff[f], loop_len);
const float falloff_factor = fmod(expand_cache->face_falloff[f_i], loop_len);
enabled = falloff_factor < active_factor;
}
if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) {
if (ss->face_sets[f] == expand_cache->initial_active_face_set) {
if (ss->face_sets[f_i] == expand_cache->initial_active_face_set) {
enabled = false;
}
}
@ -321,7 +333,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
*/
static float sculpt_expand_gradient_value_get(SculptSession *ss,
ExpandCache *expand_cache,
const int v)
const SculptVertRef v)
{
if (!expand_cache->falloff_gradient) {
return 1.0f;
@ -365,7 +377,7 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa
const int totvert = SCULPT_vertex_count_get(ss);
BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
for (int i = 0; i < totvert; i++) {
const bool enabled = sculpt_expand_state_get(ss, expand_cache, i);
const bool enabled = sculpt_expand_state_get(ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
BLI_BITMAP_SET(enabled_vertices, i, enabled);
}
return enabled_vertices;
@ -741,12 +753,15 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss,
{
const int totvert = SCULPT_vertex_count_get(ss);
expand_cache->max_vert_falloff = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
if (expand_cache->vert_falloff[i] == FLT_MAX) {
continue;
}
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
continue;
}
@ -765,11 +780,13 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss,
const int totface = ss->totfaces;
expand_cache->max_face_falloff = -FLT_MAX;
for (int i = 0; i < totface; i++) {
SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, i);
if (expand_cache->face_falloff[i] == FLT_MAX) {
continue;
}
if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, f)) {
continue;
}
@ -1262,12 +1279,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
const float initial_mask = *vd.mask;
const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float new_mask;
if (enabled) {
new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
new_mask = 0.0f;
@ -1298,17 +1315,20 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
*/
static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expand_cache)
{
const int totface = ss->totfaces;
for (int f = 0; f < totface; f++) {
const int totface = ss->totpoly;
for (int f_i = 0; f_i < totface; f_i++) {
SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, f_i);
const bool enabled = sculpt_expand_face_state_get(ss, expand_cache, f);
if (!enabled) {
continue;
}
if (expand_cache->preserve) {
ss->face_sets[f] += expand_cache->next_face_set;
ss->face_sets[f_i] += expand_cache->next_face_set;
}
else {
ss->face_sets[f] = expand_cache->next_face_set;
ss->face_sets[f_i] = expand_cache->next_face_set;
}
}
@ -1336,11 +1356,11 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
float initial_color[4];
copy_v4_v4(initial_color, vd.col);
const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float fade;
if (enabled) {
fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
fade = 0.0f;
@ -1429,9 +1449,22 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
*/
static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expand_cache)
{
const int totfaces = ss->totfaces;
for (int i = 0; i < totfaces; i++) {
ss->face_sets[i] = expand_cache->initial_face_sets[i];
if (ss->bm) {
const int totfaces = ss->totfaces;
const int cd_faceset = ss->cd_faceset_offset;
for (int i = 0; i < totfaces; i++) {
BMFace *f = (BMFace*)BKE_pbvh_table_index_to_face(ss->pbvh, i).i;
BM_ELEM_CD_SET_INT(f, cd_faceset, expand_cache->initial_face_sets[i]);
}
}
else {
const int totfaces = ss->totfaces;
for (int i = 0; i < totfaces; i++) {
ss->face_sets[i] = expand_cache->initial_face_sets[i];
}
}
}
@ -1544,11 +1577,13 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
continue;
}
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
continue;
}
const float *vertex_co = SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
const float *vertex_co = SCULPT_vertex_co_get(ss, v);
if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) {
continue;
@ -1925,6 +1960,96 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
return OPERATOR_RUNNING_MODAL;
}
/**
* Deletes the `delete_id` Face Set ID from the mesh Face Sets
* and stores the result in `r_face_set`.
* The faces that were using the `delete_id` Face Set are filled
* using the content from their neighbors.
*/
static void sculpt_expand_delete_face_set_id_bmesh(int *r_face_sets,
SculptSession *ss,
ExpandCache *expand_cache,
const int delete_id)
{
BMIter iter;
BMFace *f;
int i = 0;
const int totface = ss->bm->totface;
/* Check that all the face sets IDs in the mesh are not equal to `delete_id`
* before attempting to delete it. */
bool all_same_id = true;
BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
SculptFaceRef fref = {(intptr_t)f};
i++;
if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) {
continue;
}
if (r_face_sets[i] != delete_id) {
all_same_id = false;
break;
}
}
if (all_same_id) {
return;
}
BLI_LINKSTACK_DECLARE(queue, void *);
BLI_LINKSTACK_DECLARE(queue_next, void *);
BLI_LINKSTACK_INIT(queue);
BLI_LINKSTACK_INIT(queue_next);
for (int i = 0; i < totface; i++) {
SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
if (r_face_sets[i] == delete_id) {
BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(fref.i));
}
}
while (BLI_LINKSTACK_SIZE(queue)) {
while (BLI_LINKSTACK_SIZE(queue)) {
const SculptFaceRef f = {(intptr_t)(BLI_LINKSTACK_POP(queue))};
BMFace *bf = (BMFace*)f.i;
const int f_index = BM_elem_index_get(bf);
int other_id = delete_id;
BMLoop *l = bf->l_first;
do {
BMLoop *l2 = l->radial_next;
do {
const int neighbor_face_index = BM_elem_index_get(l2->f);
if (r_face_sets[neighbor_face_index] != delete_id) {
other_id = r_face_sets[neighbor_face_index];
}
l2 = l2->radial_next;
} while (l2 != l);
l = l->next;
} while (l != bf->l_first);
if (other_id != delete_id) {
r_face_sets[f_index] = other_id;
}
else {
BLI_LINKSTACK_PUSH(queue_next, bf);
}
}
BLI_LINKSTACK_SWAP(queue, queue_next);
}
BLI_LINKSTACK_FREE(queue);
BLI_LINKSTACK_FREE(queue_next);
}
/**
* Deletes the `delete_id` Face Set ID from the mesh Face Sets
* and stores the result in `r_face_set`.
@ -1937,6 +2062,11 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets,
Mesh *mesh,
const int delete_id)
{
if (ss->bm) {
sculpt_expand_delete_face_set_id_bmesh(r_face_sets, ss, expand_cache, delete_id);
return;
}
const int totface = ss->totvert;
MeshElemMap *pmap = ss->pmap;
@ -1944,7 +2074,7 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets,
* before attempting to delete it. */
bool all_same_id = true;
for (int i = 0; i < totface; i++) {
if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, BKE_pbvh_table_index_to_face(ss->pbvh, i))) {
continue;
}
if (r_face_sets[i] != delete_id) {
@ -2092,12 +2222,14 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
return OPERATOR_CANCELLED;
}
#if 0
/* Face Set operations are not supported in dyntopo. */
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS &&
BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
sculpt_expand_cache_free(ss);
return OPERATOR_CANCELLED;
}
#endif
sculpt_expand_ensure_sculptsession_data(ob);

View File

@ -74,6 +74,78 @@
#include <math.h>
#include <stdlib.h>
int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face)
{
if (ss->bm) {
BMFace *f = (BMFace *)face.i;
return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
}
return ss->face_sets[face.i];
}
// returns previous face set
int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset)
{
int ret = 0;
if (ss->bm) {
BMFace *f = (BMFace *)face.i;
ret = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
}
else {
ret = ss->face_sets[face.i];
ss->face_sets[face.i] = fset;
}
return ret;
}
int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag)
{
if (ss->bm) {
BMFace *f = (BMFace *)face.i;
flag = BM_face_flag_from_mflag(flag);
return f->head.hflag & flag;
}
else {
return ss->mpoly[face.i].flag & flag;
}
}
int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state)
{
int ret;
if (ss->bm) {
BMFace *f = (BMFace *)face.i;
flag = BM_face_flag_from_mflag(flag);
ret = f->head.hflag & flag;
if (state) {
f->head.hflag |= flag;
}
else {
f->head.hflag &= ~flag;
}
}
else {
ret = ss->mpoly[face.i].flag & flag;
if (state) {
ss->mpoly[face.i].flag |= flag;
}
else {
ss->mpoly[face.i].flag &= ~flag;
}
}
return ret;
}
/* Utils. */
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
{
@ -120,6 +192,34 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo
return SCULPT_active_face_set_get(ss);
}
static BMesh *sculpt_faceset_bm_begin(SculptSession *ss, Mesh *mesh)
{
if (ss->bm) {
return ss->bm;
}
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
BMesh *bm = BM_mesh_create(&allocsize,
&((struct BMeshCreateParams){
.use_toolflags = true,
}));
BM_mesh_bm_from_me(NULL,
bm,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
return bm;
}
static void sculpt_faceset_bm_end(SculptSession *ss, BMesh *bm)
{
if (bm != ss->bm) {
BM_mesh_free(bm);
}
}
/* Draw Face Sets Brush. */
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
@ -338,7 +438,7 @@ static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
{0, NULL, 0, NULL, NULL},
};
static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
ATTR_NO_OPT static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
@ -347,6 +447,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
const int mode = RNA_enum_get(op->ptr, "mode");
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
SCULPT_face_random_access_ensure(ss);
const int tot_vert = SCULPT_vertex_count_get(ss);
float threshold = 0.5f;
@ -416,44 +517,23 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
}
if (mode == SCULPT_FACE_SET_SELECTION) {
Mesh *mesh = ob->data;
BMesh *bm;
BMFace *f;
BMIter iter;
const totface = ss->totfaces;
if (ss->bm) {
bm = ss->bm;
BMIter iter;
BMFace *f;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, next_face_set);
}
for (int i = 0; i < totface; i++) {
SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
// XXX check hidden?
int ok = !SCULPT_face_set_flag_get(ss, fref, ME_HIDE);
ok = ok && SCULPT_face_set_flag_get(ss, fref, ME_FACE_SEL);
if (ok) {
SCULPT_face_set_set(ss, fref, next_face_set);
}
}
else {
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
bm = BM_mesh_create(&allocsize,
&((struct BMeshCreateParams){
.use_toolflags = true,
}));
BM_mesh_bm_from_me(NULL,
bm,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
BMIter iter;
BMFace *f;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
ss->face_sets[BM_elem_index_get(f)] = next_face_set;
}
}
BM_mesh_free(bm);
}
}
for (int i = 0; i < totnode; i++) {
@ -629,25 +709,16 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
SculptSession *ss = ob->sculpt;
Mesh *mesh = ob->data;
BMesh *bm;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
bm = BM_mesh_create(&allocsize,
&((struct BMeshCreateParams){
.use_toolflags = true,
}));
BM_mesh_bm_from_me(NULL,
bm,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
bm = sculpt_faceset_bm_begin(ss, mesh);
BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
const int totfaces = mesh->totpoly;
int *face_sets = ss->face_sets;
SCULPT_vertex_random_access_ensure(ss);
SCULPT_face_random_access_ensure(ss);
BM_mesh_elem_table_init(bm, BM_FACE);
BM_mesh_elem_index_ensure(bm, BM_FACE);
BM_mesh_elem_table_ensure(bm, BM_FACE);
int next_face_set = 1;
@ -659,7 +730,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
GSQueue *queue;
queue = BLI_gsqueue_new(sizeof(int));
face_sets[i] = next_face_set;
SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
SCULPT_face_set_set(ss, fref, next_face_set);
BLI_BITMAP_ENABLE(visited_faces, i);
BLI_gsqueue_push(queue, &i);
@ -686,7 +759,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
continue;
}
face_sets[neighbor_face_index] = next_face_set;
SculptFaceRef fref2 = BKE_pbvh_table_index_to_face(ss->pbvh, neighbor_face_index);
SCULPT_face_set_set(ss, fref2, next_face_set);
BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index);
BLI_gsqueue_push(queue, &neighbor_face_index);
}
@ -700,7 +775,7 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
MEM_SAFE_FREE(visited_faces);
BM_mesh_free(bm);
sculpt_faceset_bm_end(ss, bm);
}
static void sculpt_face_sets_init_loop(Object *ob, const int mode)
@ -749,11 +824,6 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
const int mode = RNA_enum_get(op->ptr, "mode");
/* Dyntopo not supported. */
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
return OPERATOR_CANCELLED;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;

View File

@ -48,7 +48,7 @@ enum ePaintSymmetryFlags;
maximum symmetry passes returned by SCULPT_get_symmetry_pass.
enough for about ~30 radial symmetry passes, which seems like plenty
used by various code that needs to statically store per-pass state.
used by various code that needs to statically store per-pass state.
*/
#define SCULPT_MAX_SYMMETRY_PASSES 255
@ -105,6 +105,7 @@ char SCULPT_mesh_symmetry_xyz_get(Object *object);
/* Sculpt PBVH abstraction API */
void SCULPT_vertex_random_access_ensure(struct SculptSession *ss);
void SCULPT_face_random_access_ensure(struct SculptSession *ss);
int SCULPT_vertex_count_get(struct SculptSession *ss);
const float *SCULPT_vertex_co_get(struct SculptSession *ss, SculptVertRef index);
@ -236,6 +237,13 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef ind
void SCULPT_face_sets_visibility_invert(SculptSession *ss);
void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face);
// returns previous face set
int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset);
int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag);
int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state);
bool SCULPT_stroke_is_main_symmetry_pass(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cache);
@ -577,7 +585,10 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Topology rake */
void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v, float projection);
void SCULPT_bmesh_four_neighbor_average(float avg[3],
float direction[3],
struct BMVert *v,
float projection);
/* Smoothing api */
void SCULPT_neighbor_coords_average(SculptSession *ss,
@ -604,7 +615,8 @@ void SCULPT_smooth(Sculpt *sd,
const bool smooth_mask,
float projection);
void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection);
void SCULPT_do_smooth_brush(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection);
/* Surface Smooth Brush. */
@ -1124,9 +1136,9 @@ typedef struct StrokeCache {
rcti previous_r; /* previous redraw rectangle */
rcti current_r; /* current redraw rectangle */
float stroke_distance; // copy of PaintStroke->stroke_distance
float stroke_distance_t; // copy of PaintStroke->stroke_distance_t
float stroke_distance; // copy of PaintStroke->stroke_distance
float stroke_distance_t; // copy of PaintStroke->stroke_distance_t
float last_dyntopo_t;
float last_smooth_t[SCULPT_MAX_SYMMETRY_PASSES];
float last_rake_t[SCULPT_MAX_SYMMETRY_PASSES];
@ -1502,3 +1514,5 @@ void SCULPT_dyntopo_save_persistent_base(SculptSession *ss);
/*get current symmetry pass index inclusive of both
mirror and radial symmetry*/
int SCULPT_get_symmetry_pass(const SculptSession *ss);
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);

View File

@ -113,6 +113,7 @@ typedef struct UndoSculpt {
ListBase nodes;
size_t undo_size;
BMLog *bm_restore;
} UndoSculpt;
static UndoSculpt *sculpt_undo_get_nodes(void);
@ -400,7 +401,7 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode)
Mesh *me = BKE_object_get_original_mesh(ob);
int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
for (int i = 0; i < me->totpoly; i++) {
face_sets[i] = unode->face_sets[i];
SWAP(int, face_sets[i], unode->face_sets[i]);
}
return false;
}
@ -650,7 +651,7 @@ static void sculpt_undo_refine_subdiv(Depsgraph *depsgraph,
MEM_freeN(deformed_verts);
}
static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb)
ATTR_NO_OPT static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -664,6 +665,23 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
bool need_refine_subdiv = false;
for (unode = lb->first; unode; unode = unode->next) {
if (unode->bm_entry && !ss->bm) {
// file loading breaks undo because the stack isn't initialized
// detect that case and try to fix it
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);
if (!ss->bm_log) {
/* Restore the BMLog using saved entries. */
ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
}
BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
}
/* Restore pivot. */
copy_v3_v3(ss->pivot_pos, unode->pivot_pos);
copy_v3_v3(ss->pivot_rot, unode->pivot_rot);
@ -679,6 +697,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
sculpt_undo_print_nodes(NULL);
if (!ss->bm && lb->first) {
unode = lb->first;
if (unode->type == SCULPT_UNDO_FACE_SETS) {
@ -782,7 +802,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
BLI_assert(!"Dynamic topology should've already been handled");
printf("Dynamic topology should've already been handled\n");
break;
}
}
@ -1118,7 +1138,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
BLI_assert(!"Dynamic topology should've already been handled");
printf("Dynamic topology should've already been handled\n");
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
break;
@ -1370,14 +1390,14 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_GEOMETRY:
break;
}
} else {
}
else {
switch (type) {
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
case SCULPT_UNDO_GEOMETRY:
BM_log_full_mesh(ss->bm, ss->bm_log);
break;
}
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
case SCULPT_UNDO_GEOMETRY:
BM_log_full_mesh(ss->bm, ss->bm_log);
break;
}
}
if (new_node) {
@ -1513,7 +1533,7 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
BLI_assert(!"Dynamic topology should've already been handled");
printf("Dynamic topology should've already been handled\n");
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
break;
@ -1797,6 +1817,10 @@ static UndoSculpt *sculpt_undo_get_nodes(void)
return sculpt_undosys_step_get_nodes(us);
}
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss)
{
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1963,7 +1987,7 @@ static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i)
}
void sculpt_undo_print_nodes(void *active)
{
#if 0
#if 1
UndoStack *ustack = ED_undo_stack_get();
UndoStep *us = ustack->steps.first;
if (active == NULL) {