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:
parent
0eeaeb3fc2
commit
173f5f94ff
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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'}:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¶ms->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).
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue