Sculpt: New attribute API

New unified attribute API for sculpt code.

= Basic Design =

The sculpt attribute API can create temporary or permanent attributes (only supported in `PBVH_FACES` mode).  Attributes are created via `BKE_sculpt_attribute_ensure.`

Attributes can be explicit CustomData attributes or simple array-based pseudo-attributes (this is useful for PBVH_GRIDS and PBVH_BMESH).

== `SculptAttributePointers` ==

There is a structure in `SculptSession` for convenience attribute pointers, `ss->attrs`.  Standard attributes should assign these; the attribute API will automatically clear them when the associated attributes are released.  For example, the automasking code stores its factor attribute layer in `ss->attrs.automasking_factor`.

== Naming ==

Temporary attributes should use the SCULPT_ATTRIBUTE_NAME macro for naming, it takes an entry in `SculptAttributePointers` and builds a layer name.

== `SculptAttribute` ==

Attributes are referenced by a special `SculptAttribute` structure, which holds
all the info needed to look up elements of an attribute at run time.

All of these structures live in a preallocated flat array in `SculptSession`, `ss->temp_attributes`.  This is extremely important.  Since any change to the `CustomData` layout can in principle invalidate every extant `SculptAttribute`, having them all in one block of memory whose location doesn't change allows us to update them transparently.

This makes for much simpler code and eliminates bugs.  To see why this is tricky to get right, imagine we want to create three attributes in PBVH_BMESH mode and we provide our own `SculptAttribute` structs for the API to fill in.  Each new layer will invalidate the `CustomData` block offsets in the prior one, leading to memory corruption.

Reviewed by: Brecht Van Lommel
Differential Revision: https://developer.blender.org/D15496
Ref D15496
This commit is contained in:
Joseph Eagar 2022-09-16 12:20:28 -07:00
parent 4cea4f4c5f
commit 02575bcbd0
Notes: blender-bot 2023-02-13 14:23:23 +01:00
Referenced by issue #102584, Regression: Boundary Propagation doesn't work
Referenced by issue #101430, Regression: Crash upon undo in sculpt mode after a stroke with no effect
14 changed files with 845 additions and 117 deletions

View File

@ -740,6 +740,8 @@ void CustomData_blend_write(BlendWriter *writer,
void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count);
size_t CustomData_get_elem_size(struct CustomDataLayer *layer);
#ifndef NDEBUG
struct DynStr;
/** Use to inspect mesh data when debugging. */

View File

@ -8,7 +8,9 @@
*/
#include "BLI_bitmap.h"
#include "BLI_compiler_compat.h"
#include "BLI_utildefines.h"
#include "DNA_brush_enums.h"
#include "DNA_object_enums.h"
@ -485,6 +487,74 @@ typedef struct SculptFakeNeighbors {
/* Session data (mode-specific) */
/* Custom Temporary Attributes */
typedef struct SculptAttributeParams {
/* Allocate a flat array outside the CustomData system. Cannot be combined with permanent. */
int simple_array : 1;
/* Do not mark CustomData layer as temporary. Cannot be combined with simple_array. Doesn't
* work with PBVH_GRIDS.
*/
int permanent : 1; /* Cannot be combined with simple_array. */
int stroke_only : 1; /* Release layer at end of struct */
} SculptAttributeParams;
typedef struct SculptAttribute {
/* Domain, data type and name */
eAttrDomain domain;
eCustomDataType proptype;
char name[MAX_CUSTOMDATA_LAYER_NAME];
/* Source layer on mesh/bmesh, if any. */
struct CustomDataLayer *layer;
/* Data stored as flat array. */
void *data;
int elem_size, elem_num;
bool data_for_bmesh; /* Temporary data store as array outside of bmesh. */
/* Data stored per BMesh element. */
int bmesh_cd_offset;
/* Sculpt usage */
SculptAttributeParams params;
/* Used to keep track of which preallocated SculptAttribute instances
* inside of SculptSession.temp_attribute are used.
*/
bool used;
} SculptAttribute;
#define SCULPT_MAX_ATTRIBUTES 64
/* Get a standard attribute name. Key must match up with a member
* of SculptAttributePointers.
*/
#define SCULPT_ATTRIBUTE_NAME(key) \
(offsetof(SculptAttributePointers, key) >= 0 ? /* Spellcheck name. */ \
(".sculpt_" #key) /* Make name. */ \
: \
"You misspelled the layer name key")
/* Convienence pointers for standard sculpt attributes. */
typedef struct SculptAttributePointers {
/* Persistent base. */
SculptAttribute *persistent_co;
SculptAttribute *persistent_no;
SculptAttribute *persistent_disp;
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
* initialized in #SCULPT_automasking_cache_init when needed. */
SculptAttribute *automasking_factor;
/* BMesh */
SculptAttribute *dyntopo_node_id_vertex;
SculptAttribute *dyntopo_node_id_face;
} SculptAttributePointers;
typedef struct SculptSession {
/* Mesh data (not copied) can come either directly from a Mesh, or from a MultiresDM */
struct { /* Special handling for multires meshes */
@ -529,6 +599,7 @@ typedef struct SculptSession {
/* Mesh Face Sets */
/* Total number of polys of the base mesh. */
int totfaces;
/* The 0 ID is not used by the tools or the visibility system, it is just used when creating new
* geometry (the trim tool, for example) to detect which geometry was just added, so it can be
* assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set
@ -542,8 +613,6 @@ typedef struct SculptSession {
/* BMesh for dynamic topology sculpting */
struct BMesh *bm;
int cd_vert_node_offset;
int cd_face_node_offset;
bool bm_smooth_shading;
/* Undo/redo log for dynamic topology sculpting */
struct BMLog *bm_log;
@ -575,7 +644,8 @@ typedef struct SculptSession {
int active_face_index;
int active_grid_index;
/* When active, the cursor draws with faded colors, indicating that there is an action enabled.
/* When active, the cursor draws with faded colors, indicating that there is an action
* enabled.
*/
bool draw_faded_cursor;
float cursor_radius;
@ -584,8 +654,10 @@ typedef struct SculptSession {
float cursor_sampled_normal[3];
float cursor_view_normal[3];
/* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse when
* the gesture starts (intersection with the surface and if they ray hit the surface or not). */
/* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse
* when
* the gesture starts (intersection with the surface and if they ray hit the surface or not).
*/
float gesture_initial_location[3];
float gesture_initial_normal[3];
bool gesture_initial_hit;
@ -606,10 +678,6 @@ typedef struct SculptSession {
/* Boundary Brush Preview */
SculptBoundary *boundary_preview;
/* Mesh State Persistence */
/* This is freed with the PBVH, so it is always in sync with the mesh. */
SculptPersistentBase *persistent_base;
SculptVertexInfo vertex_info;
SculptFakeNeighbors fake_neighbors;
@ -655,6 +723,14 @@ typedef struct SculptSession {
*/
char needs_flush_to_id;
/* This is a fixed-size array so we can pass pointers to its elements
* to client code. This is important to keep bmesh offsets up to date.
*/
struct SculptAttribute temp_attributes[SCULPT_MAX_ATTRIBUTES];
/* Convienence SculptAttribute pointers. */
SculptAttributePointers attrs;
/**
* Some tools follows the shading chosen by the last used tool canvas.
* When not set the viewport shading color would be used.
@ -675,6 +751,75 @@ void BKE_sculptsession_free_deformMats(struct SculptSession *ss);
void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss);
void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder);
void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
int BKE_sculptsession_vertex_count(const SculptSession *ss);
/* Ensure an attribute layer exists.*/
SculptAttribute *BKE_sculpt_attribute_ensure(struct Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name,
const SculptAttributeParams *params);
/* Returns nullptr if attribute does not exist. */
SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name);
bool BKE_sculpt_attribute_exists(struct Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name);
bool BKE_sculpt_attribute_destroy(struct Object *ob, SculptAttribute *attr);
/* Destroy all attributes and psuedo-attributes created by sculpt mode.*/
void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob);
/* Destroy attributes that were marked as stroke only in SculptAttributeParams. */
void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob);
BLI_INLINE void *BKE_sculpt_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr)
{
if (attr->data) {
char *p = (char *)attr->data;
int idx = (int)vertex.i;
if (attr->data_for_bmesh) {
BMElem *v = (BMElem *)vertex.i;
idx = v->head.index;
}
return p + attr->elem_size * (int)idx;
}
else {
BMElem *v = (BMElem *)vertex.i;
return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset);
}
return NULL;
}
BLI_INLINE void *BKE_sculpt_face_attr_get(const PBVHFaceRef vertex, const SculptAttribute *attr)
{
if (attr->data) {
char *p = (char *)attr->data;
int idx = (int)vertex.i;
if (attr->data_for_bmesh) {
BMElem *v = (BMElem *)vertex.i;
idx = v->head.index;
}
return p + attr->elem_size * (int)idx;
}
else {
BMElem *v = (BMElem *)vertex.i;
return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset);
}
return NULL;
}
/**
* Create new color layer on object if it doesn't have one and if experimental feature set has
@ -713,6 +858,7 @@ struct PBVH *BKE_sculpt_object_pbvh_ensure(struct Depsgraph *depsgraph, struct O
void BKE_sculpt_bvh_update_from_ccg(struct PBVH *pbvh, struct SubdivCCG *subdiv_ccg);
void BKE_sculpt_ensure_orig_mesh_data(struct Scene *scene, struct Object *object);
void BKE_sculpt_sync_face_visibility_to_grids(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg);
/**

View File

@ -230,7 +230,7 @@ typedef void (*BKE_pbvh_SearchNearestCallback)(PBVHNode *node, void *data, float
/* Building */
PBVH *BKE_pbvh_new(void);
PBVH *BKE_pbvh_new(PBVHType type);
/**
* Do a full rebuild with on Mesh data structure.
*
@ -268,6 +268,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
int cd_vert_node_offset,
int cd_face_node_offset);
void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset);
void BKE_pbvh_build_pixels(PBVH *pbvh,
struct Mesh *mesh,
struct Image *image,

View File

@ -5546,3 +5546,8 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
/** \} */
} // namespace blender::bke
size_t CustomData_get_elem_size(CustomDataLayer *layer)
{
return LAYERTYPEINFO[layer->type].size;
}

View File

@ -25,6 +25,7 @@
#include "BLI_hash.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@ -65,6 +66,16 @@
#include "bmesh.h"
static void sculpt_attribute_update_refs(Object *ob);
static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name,
const SculptAttributeParams *params,
PBVHType pbvhtype,
bool flat_array_for_bmesh);
void sculptsession_bmesh_add_layers(Object *ob);
using blender::MutableSpan;
using blender::Span;
@ -1430,13 +1441,9 @@ static void sculptsession_free_pbvh(Object *object)
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
MEM_SAFE_FREE(ss->persistent_base);
MEM_SAFE_FREE(ss->preview_vert_list);
ss->preview_vert_count = 0;
MEM_SAFE_FREE(ss->preview_vert_list);
MEM_SAFE_FREE(ss->vertex_info.connected_component);
MEM_SAFE_FREE(ss->vertex_info.boundary);
@ -1470,6 +1477,8 @@ void BKE_sculptsession_free(Object *ob)
if (ob && ob->sculpt) {
SculptSession *ss = ob->sculpt;
BKE_sculpt_attribute_destroy_temporary_all(ob);
if (ss->bm) {
BKE_sculptsession_bm_to_me(ob, true);
BM_mesh_free(ss->bm);
@ -1628,6 +1637,21 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
return false;
}
/* Helper function to keep persistent base attribute references up to
* date. This is a bit more tricky since they persist across strokes.
*/
static void sculpt_update_persistent_base(Object *ob)
{
SculptSession *ss = ob->sculpt;
ss->attrs.persistent_co = BKE_sculpt_attribute_get(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co));
ss->attrs.persistent_no = BKE_sculpt_attribute_get(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no));
ss->attrs.persistent_disp = BKE_sculpt_attribute_get(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp));
}
static void sculpt_update_object(
Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool need_pmap, bool is_paint_tool)
{
@ -1726,6 +1750,9 @@ static void sculpt_update_object(
BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default);
sculpt_attribute_update_refs(ob);
sculpt_update_persistent_base(ob);
if (need_pmap && ob->type == OB_MESH && !ss->pmap) {
BKE_mesh_vert_poly_map_create(&ss->pmap,
&ss->pmap_mem,
@ -2119,13 +2146,16 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
{
PBVH *pbvh = BKE_pbvh_new();
PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH);
sculptsession_bmesh_add_layers(ob);
BKE_pbvh_build_bmesh(pbvh,
ob->sculpt->bm,
ob->sculpt->bm_smooth_shading,
ob->sculpt->bm_log,
ob->sculpt->cd_vert_node_offset,
ob->sculpt->cd_face_node_offset);
ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset,
ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset);
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, false);
return pbvh;
@ -2135,7 +2165,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool
{
Mesh *me = BKE_object_get_original_mesh(ob);
const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop);
PBVH *pbvh = BKE_pbvh_new();
PBVH *pbvh = BKE_pbvh_new(PBVH_FACES);
BKE_pbvh_respect_hide_set(pbvh, respect_hide);
MutableSpan<MVert> verts = me->verts_for_write();
@ -2178,7 +2208,7 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect
{
CCGKey key;
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
PBVH *pbvh = BKE_pbvh_new();
PBVH *pbvh = BKE_pbvh_new(PBVH_GRIDS);
BKE_pbvh_respect_hide_set(pbvh, respect_hide);
Mesh *base_mesh = BKE_mesh_from_object(ob);
@ -2288,3 +2318,562 @@ void BKE_paint_face_set_overlay_color_get(const int face_set, const int seed, uc
&rgba[2]);
rgba_float_to_uchar(r_color, rgba);
}
int BKE_sculptsession_vertex_count(const SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
return ss->totvert;
case PBVH_BMESH:
return BM_mesh_elem_count(ss->bm, BM_VERT);
case PBVH_GRIDS:
return BKE_pbvh_get_grid_num_verts(ss->pbvh);
}
return 0;
}
/** Returns pointer to a CustomData associated with a given domain, if
* one exists. If not nullptr is returned (this may happen with e.g.
* multires and ATTR_DOMAIN_POINT).
*/
static CustomData *sculpt_get_cdata(Object *ob, eAttrDomain domain)
{
SculptSession *ss = ob->sculpt;
if (ss->bm) {
switch (domain) {
case ATTR_DOMAIN_POINT:
return &ss->bm->vdata;
case ATTR_DOMAIN_FACE:
return &ss->bm->pdata;
default:
BLI_assert_unreachable();
return NULL;
}
}
else {
Mesh *me = BKE_object_get_original_mesh(ob);
switch (domain) {
case ATTR_DOMAIN_POINT:
/* Cannot get vertex domain for multires grids. */
if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
return nullptr;
}
return &me->vdata;
case ATTR_DOMAIN_FACE:
return &me->pdata;
default:
BLI_assert_unreachable();
return NULL;
}
}
}
static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain)
{
SculptSession *ss = ob->sculpt;
switch (domain) {
case ATTR_DOMAIN_POINT:
return BKE_sculptsession_vertex_count(ss);
break;
case ATTR_DOMAIN_FACE:
return ss->totfaces;
break;
default:
BLI_assert_unreachable();
return 0;
}
}
static bool sculpt_attribute_create(SculptSession *ss,
Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name,
SculptAttribute *out,
const SculptAttributeParams *params,
PBVHType pbvhtype,
bool flat_array_for_bmesh)
{
Mesh *me = BKE_object_get_original_mesh(ob);
bool simple_array = params->simple_array;
bool permanent = params->permanent;
out->params = *params;
out->proptype = proptype;
out->domain = domain;
BLI_strncpy_utf8(out->name, name, sizeof(out->name));
/* Force non-CustomData simple_array mode if not PBVH_FACES. */
if (pbvhtype == PBVH_GRIDS || (pbvhtype == PBVH_BMESH && flat_array_for_bmesh)) {
if (permanent) {
printf(
"%s: error: tried to make permanent customdata in multires or bmesh mode; will make "
"local "
"array "
"instead.\n",
__func__);
permanent = out->params.permanent = false;
}
simple_array = out->params.simple_array = true;
}
BLI_assert(!(simple_array && permanent));
int totelem = sculpt_attr_elem_count_get(ob, domain);
if (simple_array) {
int elemsize = CustomData_sizeof(proptype);
out->data = MEM_calloc_arrayN(totelem, elemsize, __func__);
out->data_for_bmesh = ss->bm != NULL;
out->bmesh_cd_offset = -1;
out->layer = NULL;
out->elem_size = elemsize;
out->used = true;
out->elem_num = totelem;
return true;
}
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_BMESH: {
CustomData *cdata = NULL;
out->data_for_bmesh = true;
switch (domain) {
case ATTR_DOMAIN_POINT:
cdata = &ss->bm->vdata;
break;
case ATTR_DOMAIN_FACE:
cdata = &ss->bm->pdata;
break;
default:
out->used = false;
return false;
}
BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1);
BM_data_layer_add_named(ss->bm, cdata, proptype, name);
int index = CustomData_get_named_layer_index(cdata, proptype, name);
if (!permanent) {
cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY;
}
out->data = NULL;
out->layer = cdata->layers + index;
out->bmesh_cd_offset = out->layer->offset;
out->elem_size = CustomData_sizeof(proptype);
break;
}
case PBVH_FACES: {
CustomData *cdata = NULL;
out->data_for_bmesh = false;
switch (domain) {
case ATTR_DOMAIN_POINT:
cdata = &me->vdata;
break;
case ATTR_DOMAIN_FACE:
cdata = &me->pdata;
break;
default:
out->used = false;
return false;
}
BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1);
CustomData_add_layer_named(cdata, proptype, CD_SET_DEFAULT, NULL, totelem, name);
int index = CustomData_get_named_layer_index(cdata, proptype, name);
if (!permanent) {
cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY;
}
out->data = NULL;
out->layer = cdata->layers + index;
out->bmesh_cd_offset = -1;
out->data = out->layer->data;
out->elem_size = CustomData_get_elem_size(out->layer);
break;
}
case PBVH_GRIDS: {
/* GRIDS should have been handled as simple arrays. */
BLI_assert_unreachable();
break;
}
default:
BLI_assert_unreachable();
break;
}
out->used = true;
out->elem_num = totelem;
return true;
}
static bool sculpt_attr_update(Object *ob, SculptAttribute *attr)
{
SculptSession *ss = ob->sculpt;
int elem_num = sculpt_attr_elem_count_get(ob, attr->domain);
bool bad = false;
if (attr->params.simple_array) {
bad = attr->elem_num != elem_num;
if (bad) {
MEM_SAFE_FREE(attr->data);
}
}
else {
CustomData *cdata = sculpt_get_cdata(ob, attr->domain);
if (cdata) {
int layer_index = CustomData_get_named_layer_index(cdata, attr->proptype, attr->name);
if (layer_index != -1 && attr->data_for_bmesh) {
attr->bmesh_cd_offset = cdata->layers[layer_index].offset;
}
else {
bad = true;
}
}
}
if (bad) {
sculpt_attribute_create(ss,
ob,
attr->domain,
attr->proptype,
attr->name,
attr,
&attr->params,
BKE_pbvh_type(ss->pbvh),
true);
}
return bad;
}
static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss,
eAttrDomain domain,
eCustomDataType proptype,
const char *name)
{
for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
SculptAttribute *attr = ss->temp_attributes + i;
if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype &&
attr->domain == domain) {
return attr;
}
}
return NULL;
}
bool BKE_sculpt_attribute_exists(Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name)
{
SculptSession *ss = ob->sculpt;
SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name);
if (attr) {
return true;
}
CustomData *cdata = sculpt_get_cdata(ob, domain);
return CustomData_get_named_layer_index(cdata, proptype, name) != -1;
return false;
}
static SculptAttribute *sculpt_alloc_attr(SculptSession *ss)
{
for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
if (!ss->temp_attributes[i].used) {
memset((void *)(ss->temp_attributes + i), 0, sizeof(SculptAttribute));
ss->temp_attributes[i].used = true;
return ss->temp_attributes + i;
}
}
BLI_assert_unreachable();
return NULL;
}
SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name)
{
SculptSession *ss = ob->sculpt;
/* See if attribute is cached in ss->temp_attributes. */
SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name);
if (attr) {
sculpt_attr_update(ob, attr);
return attr;
}
/* Does attribute exist in CustomData layout? */
CustomData *cdata = sculpt_get_cdata(ob, domain);
if (cdata) {
int index = CustomData_get_named_layer_index(cdata, proptype, name);
if (index != -1) {
int totelem = 0;
switch (domain) {
case ATTR_DOMAIN_POINT:
totelem = BKE_sculptsession_vertex_count(ss);
break;
case ATTR_DOMAIN_FACE:
totelem = ss->totfaces;
break;
default:
BLI_assert_unreachable();
break;
}
attr = sculpt_alloc_attr(ss);
attr->used = true;
attr->proptype = proptype;
attr->data = cdata->layers[index].data;
attr->bmesh_cd_offset = cdata->layers[index].offset;
attr->elem_num = totelem;
attr->layer = cdata->layers + index;
attr->elem_size = CustomData_get_elem_size(attr->layer);
BLI_strncpy_utf8(attr->name, name, sizeof(attr->name));
return attr;
}
}
return NULL;
}
static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name,
const SculptAttributeParams *params,
PBVHType pbvhtype,
bool flat_array_for_bmesh)
{
SculptSession *ss = ob->sculpt;
SculptAttribute *attr = BKE_sculpt_attribute_get(ob, domain, proptype, name);
if (attr) {
return attr;
}
attr = sculpt_alloc_attr(ss);
/* Create attribute. */
sculpt_attribute_create(
ss, ob, domain, proptype, name, attr, params, pbvhtype, flat_array_for_bmesh);
sculpt_attribute_update_refs(ob);
return attr;
}
SculptAttribute *BKE_sculpt_attribute_ensure(Object *ob,
eAttrDomain domain,
eCustomDataType proptype,
const char *name,
const SculptAttributeParams *params)
{
SculptAttributeParams temp_params = *params;
return sculpt_attribute_ensure_ex(
ob, domain, proptype, name, &temp_params, BKE_pbvh_type(ob->sculpt->pbvh), true);
}
static void sculptsession_bmesh_attr_update_internal(Object *ob)
{
SculptSession *ss = ob->sculpt;
sculptsession_bmesh_add_layers(ob);
if (ss->pbvh) {
BKE_pbvh_update_bmesh_offsets(ss->pbvh,
ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset,
ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset);
}
}
void sculptsession_bmesh_add_layers(Object *ob)
{
SculptSession *ss = ob->sculpt;
SculptAttributeParams params = {0};
ss->attrs.dyntopo_node_id_vertex = sculpt_attribute_ensure_ex(
ob,
ATTR_DOMAIN_POINT,
CD_PROP_INT32,
SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex),
&params,
PBVH_BMESH,
false);
ss->attrs.dyntopo_node_id_face = sculpt_attribute_ensure_ex(
ob,
ATTR_DOMAIN_FACE,
CD_PROP_INT32,
SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face),
&params,
PBVH_BMESH,
false);
}
void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob)
{
SculptSession *ss = ob->sculpt;
for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
SculptAttribute *attr = ss->temp_attributes + i;
if (attr->params.stroke_only) {
BKE_sculpt_attribute_destroy(ob, attr);
}
}
}
static void sculpt_attribute_update_refs(Object *ob)
{
SculptSession *ss = ob->sculpt;
/* run twice, in case sculpt_attr_update had to recreate a layer and
messed up the bmesh offsets. */
for (int i = 0; i < 2; i++) {
for (int j = 0; j < SCULPT_MAX_ATTRIBUTES; j++) {
SculptAttribute *attr = ss->temp_attributes + j;
if (attr->used) {
sculpt_attr_update(ob, attr);
}
}
if (ss->bm) {
sculptsession_bmesh_attr_update_internal(ob);
}
}
Mesh *me = BKE_object_get_original_mesh(ob);
if (ss->pbvh) {
BKE_pbvh_update_active_vcol(ss->pbvh, me);
}
}
bool BKE_paint_uses_channels(ePaintMode mode)
{
return mode == PAINT_MODE_SCULPT;
}
void BKE_sculpt_attribute_destroy_temporary_all(Object *ob)
{
SculptSession *ss = ob->sculpt;
for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
SculptAttribute *attr = ss->temp_attributes + i;
if (attr->used && !attr->params.permanent) {
BKE_sculpt_attribute_destroy(ob, attr);
}
}
}
bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr)
{
SculptSession *ss = ob->sculpt;
eAttrDomain domain = attr->domain;
BLI_assert(attr->used);
/* Remove from convienience pointer struct. */
SculptAttribute **ptrs = (SculptAttribute **)&ss->attrs;
int ptrs_num = sizeof(ss->attrs) / sizeof(void *);
for (int i = 0; i < ptrs_num; i++) {
if (ptrs[i] == attr) {
ptrs[i] = NULL;
}
}
/* Remove from internal temp_attributes array. */
for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
SculptAttribute *attr2 = ss->temp_attributes + i;
if (STREQ(attr2->name, attr->name) && attr2->domain == attr->domain &&
attr2->proptype == attr->proptype) {
attr2->used = false;
}
}
Mesh *me = BKE_object_get_original_mesh(ob);
if (attr->params.simple_array) {
MEM_SAFE_FREE(attr->data);
}
else if (ss->bm) {
CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->pdata;
BM_data_layer_free_named(ss->bm, cdata, attr->name);
}
else {
CustomData *cdata = NULL;
int totelem = 0;
switch (domain) {
case ATTR_DOMAIN_POINT:
cdata = ss->bm ? &ss->bm->vdata : &me->vdata;
totelem = ss->totvert;
break;
case ATTR_DOMAIN_FACE:
cdata = ss->bm ? &ss->bm->pdata : &me->pdata;
totelem = ss->totfaces;
break;
default:
BLI_assert_unreachable();
return false;
}
/* We may have been called after destroying ss->bm in which case attr->layer
* might be invalid.
*/
int layer_i = CustomData_get_named_layer_index(cdata, attr->proptype, attr->name);
if (layer_i != 0) {
CustomData_free_layer(cdata, attr->proptype, totelem, layer_i);
}
sculpt_attribute_update_refs(ob);
}
attr->data = NULL;
attr->used = false;
return true;
}

View File

@ -631,11 +631,12 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
MEM_freeN(prim_bbc);
}
PBVH *BKE_pbvh_new(void)
PBVH *BKE_pbvh_new(PBVHType type)
{
PBVH *pbvh = MEM_callocN(sizeof(PBVH), "pbvh");
pbvh->respect_hide = true;
pbvh->draw_cache_invalid = true;
pbvh->header.type = type;
return pbvh;
}

View File

@ -1863,6 +1863,12 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
/***************************** Public API *****************************/
void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset)
{
pbvh->cd_vert_node_offset = cd_vert_node_offset;
pbvh->cd_face_node_offset = cd_face_node_offset;
}
void BKE_pbvh_build_bmesh(PBVH *pbvh,
BMesh *bm,
bool smooth_shading,
@ -1870,8 +1876,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
const int cd_vert_node_offset,
const int cd_face_node_offset)
{
pbvh->cd_vert_node_offset = cd_vert_node_offset;
pbvh->cd_face_node_offset = cd_face_node_offset;
pbvh->header.bm = bm;
BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75);
@ -1882,6 +1886,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
/* TODO: choose leaf limit better */
pbvh->leaf_limit = 100;
BKE_pbvh_update_bmesh_offsets(pbvh, cd_vert_node_offset, cd_face_node_offset);
if (smooth_shading) {
pbvh->flags |= PBVH_DYNTOPO_SMOOTH_SHADING;
}

View File

@ -194,9 +194,10 @@ void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]
const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex)
{
if (ss->persistent_base) {
return ss->persistent_base[BKE_pbvh_vertex_to_index(ss->pbvh, vertex)].co;
if (ss->attrs.persistent_co) {
return (const float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co);
}
return SCULPT_vertex_co_get(ss, vertex);
}
@ -240,8 +241,8 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, floa
void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3])
{
if (ss->persistent_base) {
copy_v3_v3(no, ss->persistent_base[vertex.i].no);
if (ss->attrs.persistent_no) {
copy_v3_v3(no, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no));
return;
}
SCULPT_vertex_normal_get(ss, vertex, no);
@ -5322,6 +5323,8 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
}
BKE_sculpt_attributes_destroy_temporary_stroke(ob);
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
}

View File

@ -122,13 +122,11 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
return 1.0f;
}
int index = BKE_pbvh_vertex_to_index(ss->pbvh, vert);
/* If the cache is initialized with valid info, use the cache. This is used when the
* automasking information can't be computed in real time per vertex and needs to be
* initialized for the whole mesh when the stroke starts. */
if (automasking->factor) {
return automasking->factor[index];
if (ss->attrs.automasking_factor) {
return *(float *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
@ -158,7 +156,6 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
return;
}
MEM_SAFE_FREE(automasking->factor);
MEM_SAFE_FREE(automasking);
}
@ -176,7 +173,6 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
}
struct AutomaskFloodFillData {
float *automask_factor;
float radius;
bool use_radius;
float location[3];
@ -190,29 +186,29 @@ static bool automask_floodfill_cb(SculptSession *ss,
void *userdata)
{
AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata;
int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
data->automask_factor[to_v_i] = 1.0f;
data->automask_factor[from_v_i] = 1.0f;
*(float *)SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f;
*(float *)SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f;
return (!data->use_radius ||
SCULPT_is_vertex_inside_brush_radius_symm(
SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
}
static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Topology masking: pmap missing");
return nullptr;
return;
}
const int totvert = SCULPT_vertex_count_get(ss);
for (int i : IndexRange(totvert)) {
automask_factor[i] = 0.0f;
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
(*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
@ -222,9 +218,8 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
AutomaskFloodFillData fdata = {nullptr};
AutomaskFloodFillData fdata = {0};
fdata.automask_factor = automask_factor;
fdata.radius = radius;
fdata.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush);
fdata.symm = SCULPT_mesh_symmetry_xyz_get(ob);
@ -232,22 +227,20 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
return automask_factor;
}
static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
static void sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
return nullptr;
return;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Face Sets automasking: pmap missing");
return nullptr;
return;
}
int tot_vert = SCULPT_vertex_count_get(ss);
@ -256,25 +249,22 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
automask_factor[i] *= 0.0f;
*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = 0.0f;
}
}
return automask_factor;
}
#define EDGE_DISTANCE_INF -1
float *SCULPT_boundary_automasking_init(Object *ob,
eBoundaryAutomaskMode mode,
int propagation_steps,
float *automask_factor)
void SCULPT_boundary_automasking_init(Object *ob,
eBoundaryAutomaskMode mode,
int propagation_steps)
{
SculptSession *ss = ob->sculpt;
if (!ss->pmap) {
BLI_assert_msg(0, "Boundary Edges masking: pmap missing");
return nullptr;
return;
}
const int totvert = SCULPT_vertex_count_get(ss);
@ -316,16 +306,19 @@ float *SCULPT_boundary_automasking_init(Object *ob,
}
for (int i : IndexRange(totvert)) {
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
if (edge_distance[i] == EDGE_DISTANCE_INF) {
continue;
}
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
const float edge_boundary_automask = pow2f(p);
automask_factor[i] *= (1.0f - edge_boundary_automask);
*(float *)SCULPT_vertex_attr_get(
vertex, ss->attrs.automasking_factor) *= (1.0f - edge_boundary_automask);
}
MEM_SAFE_FREE(edge_distance);
return automask_factor;
}
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
@ -355,9 +348,16 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
return automasking;
}
automasking->factor = (float *)MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
SculptAttributeParams params = {0};
params.stroke_only = true;
ss->attrs.automasking_factor = BKE_sculpt_attribute_ensure(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(automasking_factor), &params);
for (int i : IndexRange(totvert)) {
automasking->factor[i] = 1.0f;
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
(*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f;
}
const int boundary_propagation_steps = brush ?
@ -366,22 +366,21 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_topology_automasking_init(sd, ob, automasking->factor);
SCULPT_topology_automasking_init(sd, ob);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
sculpt_face_sets_automasking_init(sd, ob);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps);
}
return automasking;

View File

@ -1472,7 +1472,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
const bool use_persistent_base = !ss->bm && ss->attrs.persistent_co && brush->flag & BRUSH_PERSISTENT;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
@ -1503,7 +1503,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
const int vi = vd.index;
float *disp_factor;
if (use_persistent_base) {
disp_factor = &ss->persistent_base[vi].disp;
disp_factor = (float *)SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp);
}
else {
disp_factor = &ss->cache->layer_displacement_factor[vi];

View File

@ -88,45 +88,6 @@ void SCULPT_pbvh_clear(Object *ob)
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
{
int cd_node_layer_index;
char node_vertex_id[] = "_dyntopo_vnode_id";
char node_face_id[] = "_dyntopo_fnode_id";
cd_node_layer_index = CustomData_get_named_layer_index(
&ss->bm->vdata, CD_PROP_INT32, node_vertex_id);
if (cd_node_layer_index == -1) {
BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, node_vertex_id);
cd_node_layer_index = CustomData_get_named_layer_index(
&ss->bm->vdata, CD_PROP_INT32, node_vertex_id);
}
ss->cd_vert_node_offset = CustomData_get_n_offset(
&ss->bm->vdata,
CD_PROP_INT32,
cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT32));
ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
cd_node_layer_index = CustomData_get_named_layer_index(
&ss->bm->pdata, CD_PROP_INT32, node_face_id);
if (cd_node_layer_index == -1) {
BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, node_face_id);
cd_node_layer_index = CustomData_get_named_layer_index(
&ss->bm->pdata, CD_PROP_INT32, node_face_id);
}
ss->cd_face_node_offset = CustomData_get_n_offset(
&ss->bm->pdata,
CD_PROP_INT32,
cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32));
ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
}
void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
SculptSession *ss = ob->sculpt;
@ -156,8 +117,9 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
.active_shapekey = ob->shapenr,
}));
SCULPT_dynamic_topology_triangulate(ss->bm);
BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
SCULPT_dyntopo_node_layers_add(ss);
/* Make sure the data for existing faces are initialized. */
if (me->totpoly != ss->bm->totface) {
BM_mesh_normals_update(ss->bm);
@ -185,6 +147,9 @@ static void SCULPT_dynamic_topology_disable_ex(
SculptSession *ss = ob->sculpt;
Mesh *me = ob->data;
BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex);
BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_face);
SCULPT_pbvh_clear(ob);
if (unode) {

View File

@ -402,9 +402,6 @@ typedef struct AutomaskingSettings {
typedef struct AutomaskingCache {
AutomaskingSettings settings;
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
* initialized in #SCULPT_automasking_cache_init when needed. */
float *factor;
} AutomaskingCache;
typedef struct FilterCache {
@ -1273,7 +1270,6 @@ void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain,
bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush);
void SCULPT_dynamic_topology_triangulate(struct BMesh *bm);
void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss);
enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
@ -1849,3 +1845,8 @@ BLI_INLINE bool SCULPT_tool_is_face_sets(int tool)
#ifdef __cplusplus
}
#endif
/* Make SCULPT_ alias to a few blenkernel sculpt methods. */
#define SCULPT_vertex_attr_get BKE_sculpt_vertex_attr_get
#define SCULPT_face_attr_get BKE_sculpt_face_attr_get

View File

@ -117,24 +117,33 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
if (!ss) {
/* Do not allow in DynTopo just yet. */
if (!ss || (ss && ss->bm)) {
return OPERATOR_FINISHED;
}
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
MEM_SAFE_FREE(ss->persistent_base);
SculptAttributeParams params = {0};
params.permanent = true;
ss->attrs.persistent_co = BKE_sculpt_attribute_ensure(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co), &params);
ss->attrs.persistent_no = BKE_sculpt_attribute_ensure(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no), &params);
ss->attrs.persistent_disp = BKE_sculpt_attribute_ensure(
ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp), &params);
const int totvert = SCULPT_vertex_count_get(ss);
ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
"layer persistent base");
for (int i = 0; i < totvert; i++) {
PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, vertex));
SCULPT_vertex_normal_get(ss, vertex, ss->persistent_base[i].no);
ss->persistent_base[i].disp = 0.0f;
copy_v3_v3((float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co),
SCULPT_vertex_co_get(ss, vertex));
SCULPT_vertex_normal_get(
ss, vertex, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no));
(*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f;
}
return OPERATOR_FINISHED;

View File

@ -547,7 +547,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode)
.use_toolflags = false,
}));
BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
SCULPT_dyntopo_node_layers_add(ss);
me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* Restore the BMLog using saved entries. */