Sculpt: sculpt colors fixes

* Paint brush now uses its own temp attribute
  layers instead of hijacking MSculptVert->origcolor.
* The various SCULPT_UNDO_XXX enums are now bit flags.
* Fixed anchored/drag drop mode for the paint brushes.
* Color hardening brush now works with dyntopo.
* Added a CD_FLAG_ELEM_NOINTERP flag to the customdata
  API.  If set it will either copy the first element
  in the sources list if CD_FLAG_ELEM_NOCOPY is unset,
  or nothing at all if it is.

  This necassary to properly support the design
  pattern whereby helper custom attributes
  reset themselves in each brush stroke by comparing
  a per-vertex stroke id with ss->stroke_id, thus obviating
  the need to walk the entire mesh at every stroke start.
This commit is contained in:
Joseph Eagar 2021-10-14 19:57:02 -07:00
parent 38c47e8c03
commit da4138e1de
7 changed files with 151 additions and 59 deletions

View File

@ -3471,10 +3471,32 @@ void CustomData_interp(const CustomData *source,
if (dest->layers[dest_i].type == source->layers[src_i].type) {
void *src_data = source->layers[src_i].data;
if (dest->layers[dest_i].type == CD_MESH_ID) {
continue; // paranoia check that we don't process id layers
}
for (int j = 0; j < count; j++) {
sources[j] = POINTER_OFFSET(src_data, (size_t)src_indices[j] * typeInfo->size);
}
if (dest->layers[dest_i].flag & CD_FLAG_ELEM_NOINTERP) {
if (!(dest->layers[dest_i].flag & CD_FLAG_ELEM_NOCOPY)) {
if (typeInfo->copy) {
typeInfo->copy(
sources[0],
POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dest_index * typeInfo->size),
1);
}
else {
memcpy(POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dest_index * typeInfo->size),
sources[0],
typeInfo->size);
}
}
continue;
}
typeInfo->interp(
sources,
weights,
@ -4621,6 +4643,23 @@ void CustomData_bmesh_interp(CustomData *data,
continue;
}
if (layer->flag & CD_FLAG_ELEM_NOINTERP) {
if (!(layer->flag & CD_FLAG_ELEM_NOCOPY)) {
if (typeInfo->copy) {
typeInfo->copy(POINTER_OFFSET(src_blocks[0], layer->offset),
POINTER_OFFSET(dst_block, layer->offset),
1);
}
else {
memcpy(POINTER_OFFSET(dst_block, layer->offset),
POINTER_OFFSET(src_blocks[0], layer->offset),
typeInfo->size);
}
}
continue;
}
if (typeInfo->interp) {
for (int j = 0; j < count; j++) {
sources[j] = POINTER_OFFSET(src_blocks[j], layer->offset);

View File

@ -1542,6 +1542,8 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode,
if (pbvh->type == PBVH_BMESH) {
if (pbvh->bm) {
pbvh->cd_vcol_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PROP_COLOR);
vdata = &pbvh->bm->vdata;
ldata = &pbvh->bm->ldata;
}
@ -3415,7 +3417,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->cd_sculpt_vert = CustomData_get_offset(vi->bm_vdata, CD_DYNTOPO_VERT);
// we ensure pbvh->cd_vcol_offset is up to date here too
vi->cd_vcol_offset = pbvh->cd_vcol_offset = CustomData_get_offset(vi->bm_vdata, CD_PROP_COLOR);
vi->cd_vcol_offset = CustomData_get_offset(vi->bm_vdata, CD_PROP_COLOR);
vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK);
}

View File

@ -516,6 +516,8 @@ static bool sculpt_temp_customlayer_get(SculptSession *ss,
{
bool simple_array = params->simple_array;
bool permanent = params->permanent;
bool nocopy = params->nocopy;
bool nointerp = params->nointerp;
out->params = *params;
out->proptype = proptype;
@ -668,10 +670,17 @@ static bool sculpt_temp_customlayer_get(SculptSession *ss,
idx = CustomData_get_named_layer_index(cdata, proptype, name);
SCULPT_dyntopo_node_layers_update_offsets(ss);
if (!permanent) {
cdata->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY;
}
}
if (!permanent) {
cdata->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY;
if (nocopy) {
cdata->layers[idx].flag |= CD_FLAG_ELEM_NOCOPY;
}
if (nointerp) {
cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP;
}
out->data = NULL;
@ -757,6 +766,13 @@ static bool sculpt_temp_customlayer_get(SculptSession *ss,
}
}
if (nocopy) {
cdata->layers[idx].flag |= CD_FLAG_ELEM_NOCOPY;
}
if (nointerp) {
cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP;
}
out->data = NULL;
out->is_cdlayer = true;
out->layer = cdata->layers + idx;
@ -3149,68 +3165,68 @@ bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *bru
/*** paint mesh ***/
static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
ATTR_NO_OPT static void paint_mesh_restore_co_task_cb(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
SculptUndoNode *unode;
SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
SCULPT_UNDO_COORDS);
SculptUndoType type = 0;
SculptUndoNode tmp = {0};
if (ss->bm) {
unode = &tmp;
tmp.type = type;
// unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type);
}
else {
unode = SCULPT_undo_get_node(data->nodes[n], type);
if (!unode) {
return;
}
switch (data->brush->sculpt_tool) {
case SCULPT_TOOL_MASK:
type |= SCULPT_UNDO_MASK;
break;
case SCULPT_TOOL_PAINT:
case SCULPT_TOOL_SMEAR:
type |= SCULPT_UNDO_COLOR;
break;
case SCULPT_TOOL_VCOL_BOUNDARY:
type |= SCULPT_UNDO_COLOR | SCULPT_UNDO_COORDS;
break;
default:
type |= SCULPT_UNDO_COORDS;
break;
}
PBVHVertexIter vd;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode);
bool modified = false;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
SCULPT_vertex_check_origdata(ss, vd.vertex);
MSculptVert *mv = SCULPT_vertex_get_mdyntopo(ss, vd.vertex);
if (orig_data.unode->type == SCULPT_UNDO_COORDS) {
if (len_squared_v3v3(vd.co, orig_data.co) > FLT_EPSILON) {
if (type & SCULPT_UNDO_COORDS) {
if (len_squared_v3v3(vd.co, mv->origco) > FLT_EPSILON) {
modified = true;
}
copy_v3_v3(vd.co, orig_data.co);
copy_v3_v3(vd.co, mv->origco);
if (vd.no) {
copy_v3_v3_short(vd.no, orig_data.no);
normal_float_to_short_v3(vd.no, mv->origno);
}
else {
normal_short_to_float_v3(vd.fno, orig_data.no);
copy_v3_v3(vd.fno, mv->origno);
}
}
else if (orig_data.unode->type == SCULPT_UNDO_MASK) {
if ((*vd.mask - orig_data.mask) * (*vd.mask - orig_data.mask) > FLT_EPSILON) {
if (type & SCULPT_UNDO_MASK) {
if ((*vd.mask - mv->origmask) * (*vd.mask - mv->origmask) > FLT_EPSILON) {
modified = true;
}
*vd.mask = orig_data.mask;
*vd.mask = mv->origmask;
}
else if (orig_data.unode->type == SCULPT_UNDO_COLOR && vd.col && orig_data.col) {
if (len_squared_v4v4(vd.col, orig_data.col) > FLT_EPSILON) {
if (type & SCULPT_UNDO_COLOR && vd.col) {
if (len_squared_v4v4(vd.col, mv->origcolor) > FLT_EPSILON) {
modified = true;
}
copy_v4_v4(vd.col, orig_data.col);
copy_v4_v4(vd.col, mv->origcolor);
}
if (vd.mvert) {

View File

@ -55,6 +55,8 @@ enum ePaintSymmetryFlags;
typedef struct SculptLayerParams {
int simple_array : 1; // cannot be combined with permanent
int permanent : 1; // cannot be combined with simple_array
int nocopy : 1;
int nointerp : 1;
} SculptLayerParams;
typedef struct SculptCustomLayer {
@ -848,15 +850,15 @@ void SCULPT_do_symmetrize_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
/* Undo */
typedef enum {
SCULPT_UNDO_COORDS,
SCULPT_UNDO_HIDDEN,
SCULPT_UNDO_MASK,
SCULPT_UNDO_DYNTOPO_BEGIN,
SCULPT_UNDO_DYNTOPO_END,
SCULPT_UNDO_DYNTOPO_SYMMETRIZE,
SCULPT_UNDO_GEOMETRY,
SCULPT_UNDO_FACE_SETS,
SCULPT_UNDO_COLOR,
SCULPT_UNDO_COORDS = 1 << 0,
SCULPT_UNDO_HIDDEN = 1 << 1,
SCULPT_UNDO_MASK = 1 << 2,
SCULPT_UNDO_DYNTOPO_BEGIN = 1 << 3,
SCULPT_UNDO_DYNTOPO_END = 1 << 4,
SCULPT_UNDO_DYNTOPO_SYMMETRIZE = 1 << 5,
SCULPT_UNDO_GEOMETRY = 1 << 6,
SCULPT_UNDO_FACE_SETS = 1 << 7,
SCULPT_UNDO_COLOR = 1 << 8,
} SculptUndoType;
/* Storage of geometry for the undo node.

View File

@ -120,14 +120,14 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
const Brush *brush = data->brush;
const float bstrength = fabsf(ss->cache->bstrength);
const SculptCustomLayer *buffer_scl = data->scl;
const SculptCustomLayer *stroke_id_scl = data->scl2;
PBVHVertexIter vd;
// PBVHColorBufferNode *color_buffer;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
orig_data.datatype = SCULPT_UNDO_COLOR;
// color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
// SculptOrigVertData orig_data;
// SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
// orig_data.datatype = SCULPT_UNDO_COLOR;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
@ -144,7 +144,21 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
BKE_paint_brush(&data->sd->paint)->channels, data->sd->channels, "strength", NULL);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
// SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
SCULPT_vertex_check_origdata(ss, vd.vertex);
// check if we have a new stroke, in which we need to zero
// our temp layer. do this here before the brush check
// to ensure any geomtry dyntopo might subdivide has
// valid state.
int *stroke_id = (int *)SCULPT_temp_cdata_get(vd.vertex, stroke_id_scl);
float *color_buffer = (float *)SCULPT_temp_cdata_get(vd.vertex,
buffer_scl); // mv->origcolor;
if (*stroke_id != ss->stroke_id) {
*stroke_id = ss->stroke_id;
zero_v4(color_buffer);
}
bool affect_vertex = false;
float distance_to_stroke_location = 0.0f;
@ -187,9 +201,6 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
float wet_mix_color[4];
float buffer_color[4];
MSculptVert *mv = SCULPT_vertex_get_mdyntopo(ss, vd.vertex);
float *color_buffer = mv->origcolor;
mul_v4_v4fl(paint_color, brush_color, fade * ss->cache->paint_brush.flow);
mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * ss->cache->paint_brush.flow);
@ -201,7 +212,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
/* Final mix over the original color using brush alpha. */
mul_v4_v4fl(buffer_color, color_buffer, alpha);
IMB_blend_color_float(vd.col, orig_data.col, buffer_color, brush->blend);
MSculptVert *mv = SCULPT_vertex_get_mdyntopo(ss, vd.vertex);
IMB_blend_color_float(vd.col, mv->origcolor, buffer_color, brush->blend);
CLAMP4(vd.col, 0.0f, 1.0f);
@ -369,6 +381,24 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
}
}
SculptCustomLayer buffer_scl;
SculptCustomLayer stroke_id_scl;
SculptLayerParams params = {.permanent = false, .simple_array = false};
SculptLayerParams params_id = {
.permanent = false, .simple_array = false, .nocopy = false, .nointerp = true};
// reuse smear's buffer name
SCULPT_temp_customlayer_ensure(
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", &params);
SCULPT_temp_customlayer_ensure(
ss, ATTR_DOMAIN_POINT, CD_PROP_INT32, "_paint_buffer_stroke_id", &params_id);
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", &buffer_scl, &params);
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_INT32, "_paint_buffer_stroke_id", &stroke_id_scl, &params_id);
/* Threaded loop over nodes. */
SculptThreadedTaskData data = {
.sd = sd,
@ -377,6 +407,8 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
.nodes = nodes,
.wet_mix_sampled_color = wet_color,
.mat = mat,
.scl = &buffer_scl,
.scl2 = &stroke_id_scl,
.brush_color = brush_color,
};
@ -498,9 +530,10 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
SculptCustomLayer prev_scl;
SculptLayerParams params = {.permanent = false, .simple_array = false};
SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "smear_previous", &params);
SCULPT_temp_customlayer_ensure(
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", &params);
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "smear_previous", &prev_scl, &params);
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", &prev_scl, &params);
SCULPT_vertex_random_access_ensure(ss);

View File

@ -551,11 +551,10 @@ typedef enum eBrushUVSculptTool {
SCULPT_TOOL_BOUNDARY, \
SCULPT_TOOL_POSE, \
SCULPT_TOOL_DRAW_FACE_SETS, \
SCULPT_TOOL_UV_SMOOTH, \
\
/* These brushes could handle dynamic topology, \ \
* but user feedback indicates it's better not to */ \
SCULPT_TOOL_VCOL_BOUNDARY, \
SCULPT_TOOL_UV_SMOOTH, \
SCULPT_TOOL_MASK) == 0)
#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \

View File

@ -263,6 +263,7 @@ enum {
/* Indicates external data is read into memory */
CD_FLAG_IN_MEMORY = (1 << 4),
CD_FLAG_ELEM_NOCOPY = (1 << 5), // disables CustomData_bmesh_copy_data.
CD_FLAG_ELEM_NOINTERP = (1 << 6),
};
/* Limits */