Sculpt dyntopo:

Seperate enabling PBVH_BMESH from enabling DynTopo:

* Created a new option to globally disabled
  DynTopo.
* The DynTopo panel header now reads "Dynamic Mode",
  to hopefully signal that turning on PBVH_BMESH is
  a seperate step from enabling or disabling DynTopo
  itself.
* The first checkbox in the panel is "DynTopo" so it
  should be clear enough (it's on by default, with multiple
  layers of file versioning checks).

PBVH_BMesh's undo system:

* CD_MESH_ID layers are now permanently saved once
  they are created (by default they are not).  This
  fixed a *lot* of bugs:

  Before this the undo system had to save maps between
  mesh indices and mesh IDs on transitioning
  between sculpt and global undo steps.  This was
  extremely error prone, and it simply wasn't possible
  to cover all of the corner cases

* Note that there is still an odd bug where the first
  global undo push after a sculpt step gets ignored,
  I dunno what's up with this.

* Dyntopo undo should be nearly (hopefully completely)
  bug-free after this commit.

C++20

* Made a few small changes to get blender to compile
  with c++20.  std::result_of was removed, had to
  replace a couple of usages of it with std::invoke_result.

* I'm planning to do some design studies on rewriting
  sculpt into C++.

* I strongly suspect we are going to need C++20'a new
  concepts feature if we move sculpt into C++.
  I'm planning to do some design studies on how
  that might work.
This commit is contained in:
Joseph Eagar 2021-09-15 01:41:03 -07:00
parent 0eeaeb3fc2
commit 173f5f94ff
29 changed files with 614 additions and 173 deletions

View File

@ -389,7 +389,7 @@
// Does the compiler support result_of?
#ifndef EIGEN_HAS_STD_RESULT_OF
#if EIGEN_MAX_CPP_VER>=11 && ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L)))
#if __cplusplus < 201703L && EIGEN_MAX_CPP_VER>=11 && ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L && __cplusplus)))
#define EIGEN_HAS_STD_RESULT_OF 1
#else
#define EIGEN_HAS_STD_RESULT_OF 0

View File

@ -87,11 +87,17 @@ public:
* \param args The arguments of the task.
* \return A future of the same type as the return type of the task.
*/
#if __cplusplus > 201703L
template<class T, class... Args>
std::future<typename std::invoke_result<T, Args...>::type> enqueue(T&& t, Args&&... args)
{
using pkgdTask = std::packaged_task<typename std::invoke_result<T, Args...>::type()>;
#else
template<class T, class... Args>
std::future<typename std::result_of<T(Args...)>::type> enqueue(T&& t, Args&&... args)
{
using pkgdTask = std::packaged_task<typename std::result_of<T(Args...)>::type()>;
#endif
std::shared_ptr<pkgdTask> task = std::make_shared<pkgdTask>(std::bind(std::forward<T>(t), std::forward<Args>(args)...));
auto result = task->get_future();

View File

@ -409,7 +409,7 @@ static void print_memhead_backtrace(MemHead *memh)
(void)memh; /* Ignored. */
}
# endif /* defined(__linux__) || defined(__APPLE__) */
#endif /* DEBUG_BACKTRACE */
#endif /* DEBUG_BACKTRACE */
static void make_memhead_header(MemHead *memh, size_t len, const char *str)
{

View File

@ -783,7 +783,7 @@ class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
col.label(text="Local Brush Settings")
row = col.row()
row.prop(brush.dyntopo, "disabled", text="Disable Dyntopo")
row.prop(brush.dyntopo, "disabled", text="Disable Dyntopo locally for this brush")
col.label(text="Overrides")
inherit_all = "ALL" in brush.dyntopo.inherit
@ -825,7 +825,7 @@ class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
# TODO, move to space_view3d.py
class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
bl_label = "Dyntopo"
bl_label = "Dynamic Mode"
bl_options = {'DEFAULT_CLOSED'}
bl_ui_units_x = 12
@ -857,6 +857,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
col = layout.column()
col.active = context.sculpt_object.use_dynamic_topology_sculpting
col.prop(sculpt, "use_dyntopo");
sub = col.column()
sub.active = (brush and brush.sculpt_tool != 'MASK')
if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 22
#define BLENDER_FILE_SUBVERSION 23
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file

View File

@ -78,8 +78,12 @@ typedef struct FModifierTypeInfo {
short size;
/** #eFMI_Action_Types. */
short acttype;
#ifdef __cplusplus
short requires_;
#else
/** #eFMI_Requirement_Flags. */
short requires;
#endif
/** name of modifier in interface. */
char name[64];
/** name of struct for SDNA. */

View File

@ -301,6 +301,8 @@ void BKE_pbvh_update_offsets(PBVH *pbvh,
const int cd_face_areas);
void BKE_pbvh_free(PBVH *pbvh);
void BKE_pbvh_set_bm_log(PBVH *pbvh, struct BMLog *log);
/** update original data, only data whose r_** parameters are passed in will be updated*/
void BKE_pbvh_bmesh_update_origvert(
PBVH *pbvh, struct BMVert *v, float **r_co, float **r_no, float **r_color, bool log_undo);
@ -445,17 +447,19 @@ typedef enum {
typedef float (*DyntopoMaskCB)(SculptVertRef vertex, void *userdata);
bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
PBVHTopologyUpdateMode mode,
const float center[3],
const float view_normal[3],
float radius,
const bool use_frontface,
const bool use_projected,
int symaxis,
bool updatePBVH,
DyntopoMaskCB mask_cb,
void *mask_cb_data);
bool BKE_pbvh_bmesh_update_topology(
PBVH *pbvh,
PBVHTopologyUpdateMode mode,
const float center[3],
const float view_normal[3],
float radius,
const bool use_frontface,
const bool use_projected,
int symaxis,
bool updatePBVH,
DyntopoMaskCB mask_cb,
void *mask_cb_data,
int custom_max_steps); // if 0, will use defaul hueristics for max steps
bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
bool (*searchcb)(PBVHNode *node, void *data),

View File

@ -2671,6 +2671,10 @@ void BKE_brush_get_dyntopo(Brush *brush, Sculpt *sd, DynTopoSettings *out)
inherit = 0x7FFFFFFF;
}
if (!(sd->flags & SCULPT_DYNTOPO_ENABLED)) {
out->flag |= DYNTOPO_DISABLED;
}
if (inherit & DYNTOPO_INHERIT_MODE) {
if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) {
out->mode = DYNTOPO_DETAIL_CONSTANT;

View File

@ -2045,60 +2045,62 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDDyntopoVert"};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
.vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT,
.emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT,
.vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_MESH_ID,
.emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_MESH_ID,
.fmask = 0,
.lmask = CD_MASK_MLOOP,
.pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP,
.lmask = CD_MASK_MLOOP | CD_MASK_MESH_ID,
.pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_MESH_ID,
};
const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
.vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX,
.emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX,
.vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID,
.emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID,
.fmask = 0,
.lmask = CD_MASK_MLOOP,
.pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX,
.lmask = CD_MASK_MLOOP | CD_MASK_MESH_ID,
.pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID,
};
const CustomData_MeshMasks CD_MASK_MESH = {
.vmask = (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
.emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.fmask = 0,
.lmask = (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL |
CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL |
CD_MASK_MESH_ID),
.pmask = (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL |
CD_MASK_SCULPT_FACE_SETS),
CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
const CustomData_MeshMasks CD_MASK_EDITMESH = {
.vmask = (CD_MASK_MDEFORMVERT | CD_MASK_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_PROP_ALL),
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
.emask = (CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
.pmask = (CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS),
CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.pmask = (CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
.vmask = (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN |
CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL |
CD_MASK_PROP_COLOR),
.emask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
.emask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.fmask = (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT),
.lmask = (CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP |
CD_MASK_PROP_ALL), /* XXX MISSING CD_MASK_MLOOPTANGENT ? */
CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_PROP_ALL |
CD_MASK_MESH_ID), /* XXX MISSING CD_MASK_MLOOPTANGENT ? */
.pmask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL |
CD_MASK_SCULPT_FACE_SETS),
CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
const CustomData_MeshMasks CD_MASK_BMESH = {
.vmask = (CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR |
CD_MASK_DYNTOPO_VERT),
.emask = (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
CD_MASK_DYNTOPO_VERT | CD_MASK_MESH_ID),
.emask = (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL |
CD_MASK_MESH_ID),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.pmask = (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL |
CD_MASK_SCULPT_FACE_SETS),
CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
/**
* cover values copied by #mesh_loops_to_tessdata

View File

@ -3262,7 +3262,7 @@ static bool pbvh_bmesh_subdivide_long_edges(
#ifdef USE_NEW_SPLIT
BMEdge **edges = NULL;
BLI_array_staticdeclare(edges, 1024);
BLI_array_declare(edges);
#endif
while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) {
@ -5193,7 +5193,8 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
int sym_axis,
bool updatePBVH,
DyntopoMaskCB mask_cb,
void *mask_cb_data)
void *mask_cb_data,
int custom_max_steps)
{
/* 2 is enough for edge faces - manifold edge */
@ -5354,10 +5355,17 @@ typedef struct EdgeQueueContext {
//(pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
brusharea = brusharea * brusharea * M_PI;
int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
max_steps = (int)(brusharea * ratio * 2.0f);
int max_steps;
max_steps = MIN2(max_steps, DYNTOPO_MAX_ITER);
if (custom_max_steps == 0) {
//(int)((float)DYNTOPO_MAX_ITER * ratio);
max_steps = (int)(brusharea * ratio * 2.0f);
max_steps = MIN2(max_steps, DYNTOPO_MAX_ITER);
}
else {
max_steps = custom_max_steps;
}
# ifdef DYNTOPO_REPORT
printf("brusharea: %.2f, ratio: %.2f\n", brusharea, ratio);
@ -5427,8 +5435,15 @@ typedef struct EdgeQueueContext {
float brusharea = radius / (pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
brusharea = brusharea * brusharea * M_PI;
int max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
max_steps = MIN2(max_steps, DYNTOPO_MAX_ITER);
int max_steps;
if (custom_max_steps == 0) {
max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
max_steps = MIN2(max_steps, DYNTOPO_MAX_ITER);
}
else {
max_steps = custom_max_steps;
}
#ifdef DYNTOPO_REPORT
printf("collapse max_steps %d\n", max_steps);
@ -5682,7 +5697,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
{
BMEdge **edges = edges1;
BMFace **faces = NULL;
BLI_array_staticdeclare(faces, 512);
BLI_array_declare(faces);
bm_log_message(" == split edges == ");
@ -5976,6 +5991,12 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
bm_log_message(" == split edges (triangulate) == ");
BMVert **vs = NULL;
BLI_array_staticdeclare(vs, 32);
BMFace **newfaces = NULL;
BLI_array_declare(newfaces);
for (int i = 0; i < totface; i++) {
BMFace *f = faces[i];
int mask = 0;
@ -5995,6 +6016,8 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
j++;
} while ((l = l->next) != f->l_first);
int flen = j;
if (mask >= ARRAY_SIZE(splitmap)) {
printf("splitmap error!\n");
continue;
@ -6007,13 +6030,15 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
continue;
}
if (n != f->len) {
printf("error!\n");
if (n != f->len || n != flen) {
printf("%s: error! %d %d\n", __func__, n, flen);
continue;
}
BMFace *f2 = f;
BMVert **vs = BLI_array_alloca(vs, n);
BLI_array_clear(vs);
BLI_array_reserve(vs, n);
l = f->l_first;
j = 0;
@ -6021,7 +6046,13 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
vs[j++] = l->v;
} while ((l = l->next) != f->l_first);
BMFace **newfaces = BLI_array_alloca(newfaces, n);
if (j != n) {
printf("error! %s\n", __func__);
continue;
}
BLI_array_reserve(newfaces, n);
int count = 0;
for (j = 0; j < n; j++) {
@ -6079,7 +6110,12 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE);
}
newfaces[count++] = newf;
if (count < n) {
newfaces[count++] = newf;
}
else {
printf("error!\n");
}
f2 = newf;
}
else {
@ -6110,7 +6146,10 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
BM_log_face_added(pbvh->bm_log, f);
}
BLI_array_free(vs);
BLI_array_free(newfaces);
BLI_array_free(faces);
# ifdef EXPAND_SPLIT_REGION
if (edges != edges1) {
BLI_array_free(edges);

View File

@ -84,6 +84,7 @@ void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me);
void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
void SCULPT_dyntopo_node_layers_add(SculptSession *ss);
BMesh *SCULPT_dyntopo_empty_bmesh();
void SCULPT_undo_ensure_bmlog(Object *ob);
static void init_mdyntopo_layer(SculptSession *ss, int totvert);
@ -1085,7 +1086,8 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
paint->symmetry_flags |= PAINT_SYMM_X;
/* Make sure at least dyntopo subdivision is enabled */
data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE | SCULPT_DYNTOPO_CLEANUP;
data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE | SCULPT_DYNTOPO_CLEANUP |
SCULPT_DYNTOPO_ENABLED;
}
else if ((GpPaint **)r_paint == &ts->gp_paint) {
GpPaint *data = MEM_callocN(sizeof(*data), __func__);
@ -1957,8 +1959,9 @@ void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene)
sd->dyntopo_radius_scale = 1.0f;
}
// we check these flags here in case versioning code fails
if (!sd->detail_range || !sd->dyntopo_spacing) {
sd->flags |= SCULPT_DYNTOPO_CLEANUP; // should really do this in do_versions_290.c
sd->flags |= SCULPT_DYNTOPO_CLEANUP | SCULPT_DYNTOPO_ENABLED;
}
if (!sd->detail_range) {
@ -2323,15 +2326,11 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
ob->sculpt->pbvh = pbvh;
}
else {
#ifdef WHEN_GLOBAL_UNDO_WORKS
#if 1 // def WHEN_GLOBAL_UNDO_WORKS
/*detect if we are loading from an undo memfile step*/
Mesh *mesh_orig = BKE_object_get_original_mesh(ob);
bool is_dyntopo = (mesh_orig->flag & ME_SCULPT_DYNAMIC_TOPOLOGY);
is_dyntopo = is_dyntopo && CustomData_has_layer(&mesh_orig->vdata, CD_MESH_ID);
is_dyntopo = is_dyntopo && CustomData_has_layer(&mesh_orig->edata, CD_MESH_ID);
is_dyntopo = is_dyntopo && CustomData_has_layer(&mesh_orig->pdata, CD_MESH_ID);
if (is_dyntopo) {
BMesh *bm = SCULPT_dyntopo_empty_bmesh();
@ -2343,12 +2342,14 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
(&(struct BMeshFromMeshParams){.calc_face_normal = true,
.use_shapekey = true,
.active_shapekey = ob->shapenr,
.copy_id_layers = true,
.ignore_id_layers = false,
.copy_temp_cdlayers = true,
.cd_mask_extra = CD_MASK_DYNTOPO_VERT}));
SCULPT_dyntopo_node_layers_add(ob->sculpt);
SCULPT_undo_ensure_bmlog(ob);
pbvh = build_pbvh_for_dynamic_topology(ob);
}
else {

View File

@ -1778,6 +1778,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
MEM_freeN(nodeinfo);
}
void BKE_pbvh_set_bm_log(PBVH *pbvh, struct BMLog *log)
{
pbvh->bm_log = log;
}
/*
static double last_update_time[128] = {
0,
@ -1828,7 +1833,8 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh,
sym_axis,
updatePBVH,
mask_cb,
mask_cb_data);
mask_cb_data,
0);
// double end = PIL_check_seconds_timer();
@ -4711,9 +4717,10 @@ void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out)
BMesh *bm = BM_mesh_create(
&templ,
&((struct BMeshCreateParams){.use_id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.use_id_map = true,
.use_unique_ids = true,
&((struct BMeshCreateParams){.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.id_map = true,
.create_unique_ids = true,
.temporary_ids = false,
.no_reuse_ids = false}));
// reinit pools

View File

@ -332,7 +332,7 @@ void bke_pbvh_update_vert_boundary(int cd_dyn_vert,
BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v)
{
MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
MDynTopoVert *mv = (MDynTopoVert *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
if (mv->flag & DYNVERT_NEED_BOUNDARY) {
bke_pbvh_update_vert_boundary(

View File

@ -22,7 +22,7 @@
#endif
#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer))
# include "sanitizer/asan_interface.h"
# include "sanitizer/asan_interface.h"
#else
/* Ensure return value is used. Just using UNUSED_VARS results in a warning. */
# define ASAN_POISON_MEMORY_REGION(addr, size) (void)(0 && ((size) != 0 && (addr) != NULL))
@ -40,3 +40,59 @@
* Mark a region of memory as usable again.
*/
#define BLI_asan_unpoison(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer))
# include "MEM_guardedalloc.h"
static void *BLI_asan_safe_malloc(size_t size, const char *tag)
{
// align size at 16 bytes
size += 15 - (size & 15);
// add safe padding
size += 32;
void *ret = MEM_mallocN(size, tag);
int *iptr = (int *)ret;
*iptr = (int)size;
char *ptr = (char *)ret;
ptr[4] = 't';
ptr[5] = 'a';
ptr[6] = 'g';
ptr[7] = '1';
BLI_asan_poison(ptr, 16);
BLI_asan_poison(ptr + size - 16, 16);
ret = (void *)(ptr + 16);
return ret;
}
static void BLI_asan_safe_free(void *mem)
{
if (!mem) {
return;
}
mem = (void *)(((char *)mem) - 16);
BLI_asan_unpoison(mem, 16);
int *iptr = (int *)mem;
volatile char *ptr = (char *)mem;
if (ptr[4] != 't' || ptr[5] != 'a' || ptr[6] != 'g' || ptr[7] != '1') {
BLI_asan_poison(mem, 16);
*ptr = 1; // deliberately trigger asan fault
}
BLI_asan_unpoison(ptr + iptr[0] - 16, 16);
MEM_freeN((void *)ptr);
}
#else
# define BLI_asan_safe_malloc(size, tag) MEM_mallocN(size, tag)
# define BLI_asan_safe_free(mem) MEM_SAFE_FREE(mem)
#endif

View File

@ -1178,6 +1178,27 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts) {
ts->unified_paint_settings.flag |= UNIFIED_PAINT_FLAG_HARD_EDGE_MODE;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 23)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts && ts->sculpt) {
ts->sculpt->flags |= SCULPT_DYNTOPO_ENABLED;
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -410,7 +410,8 @@ enum {
// firsst four bits are reserved for BM_VERT/EDGE/LOOP/FACE
BM_HAS_IDS = 1 << 4,
BM_HAS_ID_MAP = 1 << 5,
BM_NO_REUSE_IDS = 1 << 6
BM_NO_REUSE_IDS = 1 << 6,
BM_PERMANENT_IDS = 1 << 7
};
/** #BMHeader.htype (char) */

View File

@ -108,7 +108,7 @@ void bm_id_freelist_push(BMesh *bm, uint id)
// static const int _typemap[] = {0, 0, 1, 0, 2, 0, 0, 0, 3};
static void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id)
void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id)
{
// CustomData *cdata = &bm->vdata + _typemap[elem->head.htype];
// int cd_id_off = cdata->layers[cdata->typemap[CD_MESH_ID]].offset;
@ -143,8 +143,15 @@ static void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id)
}
}
void bm_assign_id(BMesh *bm, BMElem *elem, uint id)
void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unqiue)
{
if (check_unqiue && (bm->idmap.flag & BM_HAS_ID_MAP)) {
if (BM_ELEM_FROM_ID(bm, id)) {
printf("had to alloc a new id in bm_assign_id for %x; old id: %d\n", elem, (int)id);
}
}
#ifdef WITH_BM_ID_FREELIST
bm_id_freelist_take(bm, id);
#else
@ -965,10 +972,11 @@ BMesh *BM_mesh_copy(BMesh *bm_old)
bm_new = BM_mesh_create(
&allocsize,
&((struct BMeshCreateParams){.use_toolflags = bm_old->use_toolflags,
.use_id_elem_mask = bm_old->idmap.flag &
(BM_VERT | BM_EDGE | BM_LOOP | BM_FACE),
.use_unique_ids = !!(bm_old->idmap.flag & BM_HAS_IDS),
.use_id_map = !!(bm_old->idmap.flag & BM_HAS_ID_MAP),
.id_elem_mask = bm_old->idmap.flag &
(BM_VERT | BM_EDGE | BM_LOOP | BM_FACE),
.create_unique_ids = !!(bm_old->idmap.flag & BM_HAS_IDS),
.id_map = !!(bm_old->idmap.flag & BM_HAS_ID_MAP),
.temporary_ids = !(bm_old->idmap.flag & BM_PERMANENT_IDS),
.no_reuse_ids = !!(bm_old->idmap.flag & BM_NO_REUSE_IDS)}));
BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize);
@ -1161,28 +1169,31 @@ void bm_init_idmap_cdlayers(BMesh *bm)
return;
}
if ((bm->idmap.flag & BM_VERT) && !CustomData_has_layer(&bm->vdata, CD_MESH_ID)) {
BM_data_layer_add(bm, &bm->vdata, CD_MESH_ID);
bm->vdata.layers[CustomData_get_layer_index(&bm->vdata, CD_MESH_ID)].flag |=
CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY;
}
bool temp_ids = !(bm->idmap.flag & BM_PERMANENT_IDS);
if ((bm->idmap.flag & BM_EDGE) && !CustomData_has_layer(&bm->edata, CD_MESH_ID)) {
BM_data_layer_add(bm, &bm->edata, CD_MESH_ID);
bm->edata.layers[CustomData_get_layer_index(&bm->edata, CD_MESH_ID)].flag |=
CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY;
}
int types[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE};
CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata};
if ((bm->idmap.flag & BM_LOOP) && !CustomData_has_layer(&bm->ldata, CD_MESH_ID)) {
BM_data_layer_add(bm, &bm->ldata, CD_MESH_ID);
bm->ldata.layers[CustomData_get_layer_index(&bm->ldata, CD_MESH_ID)].flag |=
CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY;
}
for (int i = 0; i < 4; i++) {
CustomDataLayer *layer;
if ((bm->idmap.flag & BM_FACE) && !CustomData_has_layer(&bm->pdata, CD_MESH_ID)) {
BM_data_layer_add(bm, &bm->pdata, CD_MESH_ID);
bm->pdata.layers[CustomData_get_layer_index(&bm->pdata, CD_MESH_ID)].flag |=
CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY;
if (!(bm->idmap.flag & types[i])) {
continue;
}
if (!CustomData_has_layer(cdatas[i], CD_MESH_ID)) {
BM_data_layer_add(bm, cdatas[i], CD_MESH_ID);
}
layer = cdatas[i]->layers + CustomData_get_layer_index(cdatas[i], CD_MESH_ID);
layer->flag |= CD_FLAG_ELEM_NOCOPY;
if (temp_ids) {
layer->flag |= CD_FLAG_TEMPORARY;
}
else {
layer->flag &= ~CD_FLAG_TEMPORARY;
}
}
bm_update_idmap_cdlayers(bm);

View File

@ -765,7 +765,7 @@ static void bm_log_verts_restore(
}
#endif
bm_assign_id(bm, (BMElem *)v, POINTER_AS_UINT(key));
bm_assign_id(bm, (BMElem *)v, POINTER_AS_UINT(key), false);
if (callbacks) {
callbacks->on_vert_add(v, callbacks->userdata);
@ -816,7 +816,7 @@ static void bm_log_edges_restore(
}
#endif
bm_assign_id(bm, (BMElem *)e, POINTER_AS_UINT(key));
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__);
@ -913,14 +913,14 @@ static void bm_log_faces_restore(
CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata_f, &f->head.data);
}
bm_assign_id(bm, (BMElem *)f, POINTER_AS_UINT(key));
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]);
bm_assign_id(bm, (BMElem *)l, lf->l_ids[j], false);
}
if (lf->customdata[j]) {
@ -1689,7 +1689,7 @@ static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry)
.cd_mask_extra = cd_mask_extra,
.copy_temp_cdlayers = true,
.copy_id_layers = true}));
.ignore_id_layers = false}));
bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE;
@ -1841,7 +1841,7 @@ static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry)
int *lmap = idmap->maps[BM_LOOP];
BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) {
bm_assign_id(bm, elem, (uint)map[j]);
bm_assign_id(bm, elem, (uint)map[j], false);
// deal with loops
if (type == BM_FACE && cd_loop_id >= 0) {
@ -1849,7 +1849,7 @@ static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry)
BMLoop *l = f->l_first;
do {
bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi]);
bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi], false);
loopi++;
} while ((l = l->next) != f->l_first);
@ -1905,7 +1905,7 @@ static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
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]);
bm_assign_id(bm, elem, (uint)map[j], false);
map[j] = id;
// deal with loops
@ -1916,7 +1916,7 @@ static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
do {
int id2 = BM_ELEM_CD_GET_INT(l, cd_loop_id);
bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi]);
bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi], false);
lmap[loopi] = id2;
loopi++;
@ -1973,7 +1973,7 @@ static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
.cd_mask_extra = cd_mask_extra,
.copy_temp_cdlayers = true,
.copy_id_layers = 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;
@ -2454,7 +2454,7 @@ BMVert *BM_log_edge_split_do(BMLog *log, BMEdge *e, BMVert *v, BMEdge **newe, fl
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);
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);

View File

@ -178,18 +178,22 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate
bm->idmap.flag = 0;
if (params->use_unique_ids) {
if (!params->temporary_ids) {
bm->idmap.flag |= BM_PERMANENT_IDS;
}
if (params->id_map) {
bm->idmap.flag |= BM_HAS_ID_MAP;
}
if (params->no_reuse_ids) {
bm->idmap.flag |= BM_NO_REUSE_IDS;
}
if (params->create_unique_ids) {
bm->idmap.flag |= BM_HAS_IDS;
if (params->use_id_map) {
bm->idmap.flag |= BM_HAS_ID_MAP;
}
bm->idmap.flag |= params->use_id_elem_mask;
if (params->no_reuse_ids) {
bm->idmap.flag |= BM_NO_REUSE_IDS;
}
bm->idmap.flag |= params->id_elem_mask;
#ifndef WITH_BM_ID_FREELIST
bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1);
@ -221,7 +225,7 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate
CustomData_reset(&bm->ldata);
CustomData_reset(&bm->pdata);
if (params->use_unique_ids) {
if (params->create_unique_ids) {
bm_init_idmap_cdlayers(bm);
if (bm->vdata.totlayer) {

View File

@ -36,11 +36,13 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
struct BMeshCreateParams {
uint use_unique_ids : 1;
uint use_id_elem_mask : 4; // which element types to make unique ids for
uint use_id_map : 1; // maintain an id to element lookup table
uint create_unique_ids : 1;
uint id_elem_mask : 4; // which element types to make unique ids for
uint id_map : 1; // maintain an id to element lookup table
uint use_toolflags : 1;
uint no_reuse_ids : 1;
uint no_reuse_ids : 1; // do not reuse IDs; a GHash will be used internally instead of a lookup
// array
uint temporary_ids : 1;
};
// used to temporary save/restore element IDs

View File

@ -95,6 +95,7 @@
#include "bmesh.h"
#include "intern/bmesh_private.h" /* For element checking. */
#include "range_tree.h"
static void bm_unmark_temp_cdlayers(BMesh *bm)
{
@ -227,6 +228,10 @@ void BM_enter_multires_space(Object *ob, BMesh *bm, int space)
* since this should be kept fast for edit-mode switching and storing undo steps.
*
* \warning This function doesn't calculate face normals.
*
* Mesh IDs will be imported unless requested. If the bmesh was created
* with id map enabled then IDs will be checked for uniqueness, otherwise
* they are imported as is.
*/
void BM_mesh_bm_from_me(Object *ob,
@ -249,12 +254,41 @@ void BM_mesh_bm_from_me(Object *ob,
CustomData_MeshMasks mask = CD_MASK_BMESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
MultiresModifierData *mmd = ob ? get_multires_modifier(NULL, ob, true) : NULL;
const CustomData *cdatas[] = {&me->vdata, &me->edata, &me->ldata, &me->pdata};
const CustomData *bmdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata};
bool check_id_unqiue = false;
if (!params->ignore_id_layers) {
for (int i = 0; i < 4; i++) {
int type = 1 << i;
if (CustomData_has_layer(cdatas[i], CD_MESH_ID)) {
bm->idmap.flag |= type | BM_HAS_IDS;
}
}
bm_init_idmap_cdlayers(bm);
}
// check_id_unqiue
if ((bm->idmap.flag & BM_HAS_IDS)) {
#ifndef WITH_BM_ID_FREELIST
if (!bm->idmap.idtree) {
bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1);
}
#endif
if (bm->idmap.flag & BM_HAS_ID_MAP) {
check_id_unqiue = true;
}
}
if (params->copy_temp_cdlayers) {
bm_unmark_temp_cdlayers(bm);
}
MultiresModifierData *mmd = ob ? get_multires_modifier(NULL, ob, true) : NULL;
if (params->copy_temp_cdlayers) {
mask.vmask |= CD_MASK_MESH_ID;
mask.emask |= CD_MASK_MESH_ID;
@ -405,9 +439,7 @@ void BM_mesh_bm_from_me(Object *ob,
0;
if (bm->idmap.flag & BM_HAS_IDS) {
if (params->copy_id_layers) {
const CustomData *cdatas[] = {&me->vdata, &me->edata, &me->ldata, &me->pdata};
if (!params->ignore_id_layers) {
for (int i = 0; i < 4; i++) {
existing_id_layers[i] = (int *)CustomData_get_layer(cdatas[i], CD_MESH_ID);
@ -453,7 +485,7 @@ void BM_mesh_bm_from_me(Object *ob,
if (has_ids & BM_VERT) {
if (use_exist_ids & BM_VERT) {
bm_assign_id(bm, (BMElem *)v, existing_id_layers[0][i]);
bm_assign_id(bm, (BMElem *)v, existing_id_layers[0][i], false);
}
else {
bm_alloc_id(bm, (BMElem *)v);
@ -502,7 +534,7 @@ void BM_mesh_bm_from_me(Object *ob,
if (has_ids & BM_EDGE) {
if (use_exist_ids & BM_EDGE) {
bm_assign_id(bm, (BMElem *)e, existing_id_layers[1][i]);
bm_assign_id(bm, (BMElem *)e, existing_id_layers[1][i], false);
}
else {
bm_alloc_id(bm, (BMElem *)e);
@ -573,7 +605,7 @@ void BM_mesh_bm_from_me(Object *ob,
if (has_ids & BM_LOOP) {
if (use_exist_ids & BM_LOOP) {
bm_assign_id(bm, (BMElem *)l_iter, existing_id_layers[2][j - 1]);
bm_assign_id(bm, (BMElem *)l_iter, existing_id_layers[2][j - 1], false);
}
else {
bm_alloc_id(bm, (BMElem *)l_iter);
@ -586,7 +618,7 @@ void BM_mesh_bm_from_me(Object *ob,
if (has_ids & BM_FACE) {
if (use_exist_ids & BM_FACE) {
bm_assign_id(bm, (BMElem *)f, existing_id_layers[3][i]);
bm_assign_id(bm, (BMElem *)f, existing_id_layers[3][i], false);
}
else {
bm_alloc_id(bm, (BMElem *)f);
@ -601,6 +633,99 @@ void BM_mesh_bm_from_me(Object *ob,
bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* Added in order, clear dirty flag. */
}
if (check_id_unqiue) {
// validate IDs
// first clear idmap, we want it to have the first elements
// in each id run, not the last
if (bm->idmap.map) {
memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size);
}
else if (bm->idmap.ghash) {
BLI_ghash_free(bm->idmap.ghash, NULL, NULL);
bm->idmap.ghash = BLI_ghash_ptr_new("bm->idmap.ghash");
}
int iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, -1, BM_FACES_OF_MESH};
// find first element in each id run and assign to map
for (int i = 0; i < 4; i++) {
int type = 1 << i;
int iter = iters[i];
if (!(bm->idmap.flag & type)) {
continue;
}
if (iter == -1) {
iter = BM_FACES_OF_MESH;
}
BMElem *elem;
BMIter iterstate;
BM_ITER_MESH (elem, &iterstate, bm, iter) {
if (i == 2) { // loops
BMFace *f = (BMFace *)elem;
BMLoop *l = f->l_first;
do {
uint id = (uint)BM_ELEM_GET_ID(bm, (BMElem *)l);
if (!BM_ELEM_FROM_ID(bm, id)) {
bm_assign_id_intern(bm, (BMElem *)l, id);
}
} while ((l = l->next) != f->l_first);
}
else {
uint id = (uint)BM_ELEM_GET_ID(bm, elem);
if (!BM_ELEM_FROM_ID(bm, id)) {
bm_assign_id_intern(bm, elem, id);
}
}
}
}
// now assign new IDs where necessary
for (int i = 0; i < 4; i++) {
int type = 1 << i;
int iter = iters[i];
if (!(bm->idmap.flag & type)) {
continue;
}
if (iter == -1) {
iter = BM_FACES_OF_MESH;
}
BMElem *elem;
BMIter iterstate;
BM_ITER_MESH (elem, &iterstate, bm, iter) {
if (i == 2) { // loops
BMFace *f = (BMFace *)elem;
BMLoop *l = f->l_first;
do {
uint id = (uint)BM_ELEM_GET_ID(bm, (BMElem *)l);
if (BM_ELEM_FROM_ID(bm, id) != (BMElem *)l) {
bm_alloc_id(bm, (BMElem *)l);
}
} while ((l = l->next) != f->l_first);
}
else {
uint id = (uint)BM_ELEM_GET_ID(bm, elem);
if (BM_ELEM_FROM_ID(bm, id) != elem) {
bm_alloc_id(bm, elem);
}
}
}
}
}
/* -------------------------------------------------------------------- */
/* MSelect clears the array elements (avoid adding multiple times).
*

View File

@ -43,7 +43,7 @@ struct BMeshFromMeshParams {
int active_shapekey;
struct CustomData_MeshMasks cd_mask_extra;
uint copy_temp_cdlayers : 1;
uint copy_id_layers : 1; // make sure to enable ids when creating bmesh properly
uint ignore_id_layers : 1;
};
struct Object;

View File

@ -95,7 +95,8 @@ bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT
#include "intern/bmesh_structure_inline.h"
void bm_assign_id(BMesh *bm, BMElem *elem, uint id);
void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unique);
void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id);
void bm_alloc_id(BMesh *bm, BMElem *elem);
void bm_free_id(BMesh *bm, BMElem *elem);
void bm_init_idmap_cdlayers(BMesh *bm);

View File

@ -121,21 +121,70 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
SCULPT_dyntopo_automasking_init(ss, sd, NULL, ob, &mask_cb, &mask_cb_data);
while (BKE_pbvh_bmesh_update_topology(ss->pbvh,
PBVH_Collapse | PBVH_Subdivide,
center,
NULL,
size,
false,
false,
-1,
false,
mask_cb,
mask_cb_data)) {
const int max_steps = 10;
const int max_dyntopo_steps_coll = 1 << 13;
const int max_dyntopo_steps_subd = 1 << 15;
for (int i = 0; i < totnodes; i++) {
BKE_pbvh_node_mark_topology_update(nodes[i]);
int i = 0;
bool modified = true;
while (modified) {
modified = BKE_pbvh_bmesh_update_topology(ss->pbvh,
PBVH_Collapse,
center,
NULL,
size,
false,
false,
-1,
false,
mask_cb,
mask_cb_data,
max_dyntopo_steps_coll);
for (int j = 0; j < totnodes; j++) {
BKE_pbvh_node_mark_topology_update(nodes[j]);
}
modified |= BKE_pbvh_bmesh_update_topology(ss->pbvh,
PBVH_Subdivide,
center,
NULL,
size,
false,
false,
-1,
false,
mask_cb,
mask_cb_data,
max_dyntopo_steps_subd);
for (int j = 0; j < totnodes; j++) {
BKE_pbvh_node_mark_topology_update(nodes[j]);
}
if (i++ > max_steps) {
break;
}
}
/* one more time, but with cleanup valence 3/4 verts enabled */
for (i = 0; i < 2; i++) {
for (int j = 0; j < totnodes; j++) {
BKE_pbvh_node_mark_topology_update(nodes[j]);
}
BKE_pbvh_bmesh_update_topology(ss->pbvh,
PBVH_Cleanup,
center,
NULL,
size,
false,
false,
-1,
false,
mask_cb,
mask_cb_data,
max_dyntopo_steps_coll);
}
SCULPT_dyntopo_automasking_end(mask_cb_data);

View File

@ -91,9 +91,10 @@ BMesh *SCULPT_dyntopo_empty_bmesh()
BMesh *bm = BM_mesh_create(
&allocsize,
&((struct BMeshCreateParams){.use_toolflags = false,
.use_unique_ids = true,
.use_id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.use_id_map = true,
.create_unique_ids = true,
.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.id_map = true,
.temporary_ids = false,
.no_reuse_ids = false}));
return bm;
@ -780,13 +781,13 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
BKE_mesh_mselect_clear(me);
#if 1
ss->bm = BM_mesh_create(
&allocsize,
&((struct BMeshCreateParams){.use_toolflags = false,
.use_unique_ids = true,
.use_id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.use_id_map = true,
.no_reuse_ids = false}));
ss->bm = BM_mesh_create(&allocsize,
&((struct BMeshCreateParams){.use_toolflags = false,
.create_unique_ids = true,
.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.id_map = true,
.temporary_ids = false,
.no_reuse_ids = false}));
BM_mesh_bm_from_me(NULL,
ss->bm,
@ -1193,7 +1194,10 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Dynamic Topology Toggle";
ot->idname = "SCULPT_OT_dynamic_topology_toggle";
ot->description = "Dynamic topology alters the mesh topology while sculpting";
ot->description =
"Dynamic mode; note that you must now check the DynTopo"
"option to enable dynamic remesher (which updates topology will sculpting)"
"this is on by default.";
/* API callbacks. */
ot->invoke = sculpt_dynamic_topology_toggle_invoke;

View File

@ -1700,6 +1700,8 @@ void SCULPT_replay_test(void);
#endif
struct BMesh *SCULPT_dyntopo_empty_bmesh();
#define SCULPT_stroke_needs_original(brush) \
ELEM(brush->sculpt_tool, \
SCULPT_TOOL_DRAW_SHARP, \

View File

@ -71,6 +71,8 @@
#include "bmesh.h"
#include "sculpt_intern.h"
#define WHEN_GLOBAL_UNDO_WORKS
/* Implementation of undo system for objects in sculpt mode.
*
* Each undo step in sculpt mode consists of list of nodes, each node contains:
@ -714,13 +716,16 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_
ss->active_face_index.i = ss->active_vertex_index.i = 0;
/* Create empty BMesh and enable logging. */
ss->bm = BM_mesh_create(
&bm_mesh_allocsize_default,
&((struct BMeshCreateParams){.use_toolflags = false,
.use_unique_ids = true,
.use_id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.use_id_map = true,
.no_reuse_ids = false}));
ss->bm = SCULPT_dyntopo_empty_bmesh();
#if 0
ss->bm = BM_mesh_create(&bm_mesh_allocsize_default,
&((struct BMeshCreateParams){.use_toolflags = false,
.create_unique_ids = true,
.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
.id_map = true,
.temporary_ids = false,
.no_reuse_ids = false}));
#endif
BM_mesh_bm_from_me(NULL,
ss->bm,
@ -758,14 +763,21 @@ static void sculpt_undo_bmesh_restore_begin(
not entirely sure why, and in thoery it shouldn't be necassary.
ids end up corrupted.
*/
#if 1
// BM_log_all_ids(ss->bm, ss->bm_log, unode->bm_entry);
// need to run bmlog undo on empty log,
// getting a refcount error in the log
// ref counting system otherwise
if (dir == -1) {
BM_log_undo_skip(ss->bm, ss->bm_log);
}
else {
BM_log_redo_skip(ss->bm, ss->bm_log);
}
#endif
}
SCULPT_dynamic_topology_disable(C, unode);
@ -775,13 +787,18 @@ static void sculpt_undo_bmesh_restore_begin(
/*load bmesh from mesh data*/
sculpt_undo_bmesh_enable(ob, unode, true);
/* Restore mesh ids from last log entry, i.e. the one pushed in the primary if branch above */
#if 1
// need to run bmlog undo on empty log,
// getting a refcount error in the log
// ref counting system otherwise
if (dir == 1) {
BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
}
else {
BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
}
#endif
unode->applied = true;
}
@ -799,22 +816,27 @@ static void sculpt_undo_bmesh_restore_end(
/*load bmesh from mesh data*/
sculpt_undo_bmesh_enable(ob, unode, false);
/* Restore mesh ids from last log entry, i.e. the one pushed in the else branch below */
#if 1
// need to run bmlog undo on empty log,
// getting a refcount error in the log
// ref counting system otherwise
if (dir == -1) {
BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
}
else {
BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
}
#endif
unode->applied = false;
}
else {
#if 1
if (ss->bm && ss->bm_log) {
/*note that we can't log ids here.
not entirely sure why, and in thoery it shouldn't be necassary.
ids end up corrupted.
*/
// need to run bmlog undo on empty log,
// getting a refcount error in the log
// ref counting system otherwise
if (dir == -1) {
BM_log_undo_skip(ss->bm, ss->bm_log);
@ -823,6 +845,7 @@ static void sculpt_undo_bmesh_restore_end(
BM_log_redo_skip(ss->bm, ss->bm_log);
}
}
#endif
/* Disable dynamic topology sculpting. */
SCULPT_dynamic_topology_disable(C, NULL);
@ -1703,6 +1726,56 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ
return unode;
}
void SCULPT_undo_ensure_bmlog(Object *ob)
{
if (!ob->sculpt) {
return;
}
UndoStack *ustack = ED_undo_stack_get();
if (!ustack) {
return;
}
UndoStep *us = BKE_undosys_stack_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT);
if (!us) {
return;
}
UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us);
SculptSession *ss = ob->sculpt;
Mesh *me = BKE_object_get_original_mesh(ob);
if (!ss->bm && !(me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY)) {
return;
}
if (!usculpt) {
// happens during file load
return;
}
SculptUndoNode *unode = usculpt->nodes.first;
// this can happen in certain cases when going to/from other undo types
// I think.
if (!ss->bm_log) {
if (unode && unode->bm_entry) {
ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
}
else {
ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert);
}
if (ss->pbvh) {
BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log);
}
}
}
static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
@ -1711,6 +1784,8 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
SculptUndoNode *unode = usculpt->nodes.first;
SCULPT_undo_ensure_bmlog(ob);
bool new_node = false;
if (unode == NULL) {
@ -1721,14 +1796,20 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
unode->type = type;
unode->applied = true;
/* note that every undo type must push a bm_entry for
so we can recreate the BMLog from chained entries
when going to/from other undo system steps */
if (type == SCULPT_UNDO_DYNTOPO_END) {
unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL);
// unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL);
unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
// BM_log_full_mesh(ss->bm, ss->bm_log);
// BM_log_before_all_removed(ss->bm, ss->bm_log);
}
else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) {
unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL);
// unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL);
unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
// BM_log_all_added(ss->bm, ss->bm_log);
// BM_log_full_mesh(ss->bm, ss->bm_log);
@ -2013,6 +2094,8 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check)
{
SCULPT_undo_ensure_bmlog(ob);
UndoStack *ustack = ED_undo_stack_get();
if (ob != NULL) {
@ -2275,6 +2358,11 @@ static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p)
static UndoSculpt *sculpt_undo_get_nodes(void)
{
UndoStack *ustack = ED_undo_stack_get();
if (!ustack) { // happens during file load
return NULL;
}
UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT);
return sculpt_undosys_step_get_nodes(us);
}
@ -2328,7 +2416,8 @@ static void sculpt_undo_push_all_grids(Object *object)
* to the current operation without making any stroke in between.
*
* Skip pushing nodes based on the following logic: on redo SCULPT_UNDO_COORDS will ensure
* PBVH for the new base geometry, which will have same coordinates as if we create PBVH here. */
* PBVH for the new base geometry, which will have same coordinates as if we create PBVH here.
*/
if (ss->pbvh == NULL) {
return;
}

View File

@ -2253,7 +2253,8 @@ typedef enum eSculptFlags {
// hides facesets/masks and forces indexed mode to save GPU bandwidth
SCULPT_FAST_DRAW = (1 << 20),
SCULPT_DYNTOPO_LOCAL_SUBDIVIDE = (1 << 21),
SCULPT_DYNTOPO_LOCAL_COLLAPSE = (1 << 22)
SCULPT_DYNTOPO_LOCAL_COLLAPSE = (1 << 22),
SCULPT_DYNTOPO_ENABLED = (1 << 23),
} eSculptFlags;
/** #ImagePaintSettings.mode */

View File

@ -841,6 +841,12 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_ShowMask_update");
prop = RNA_def_property(srna, "use_dyntopo", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_ENABLED);
RNA_def_property_ui_text(prop, "DynTopo", "Enable DynTopo remesher in dynamic topology mode.");
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update");
prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL);
RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2);
RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC);