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:
parent
38c47e8c03
commit
da4138e1de
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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", ¶ms);
|
||||
SCULPT_temp_customlayer_ensure(
|
||||
ss, ATTR_DOMAIN_POINT, CD_PROP_INT32, "_paint_buffer_stroke_id", ¶ms_id);
|
||||
|
||||
SCULPT_temp_customlayer_get(
|
||||
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", &buffer_scl, ¶ms);
|
||||
SCULPT_temp_customlayer_get(
|
||||
ss, ATTR_DOMAIN_POINT, CD_PROP_INT32, "_paint_buffer_stroke_id", &stroke_id_scl, ¶ms_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", ¶ms);
|
||||
SCULPT_temp_customlayer_ensure(
|
||||
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", ¶ms);
|
||||
SCULPT_temp_customlayer_get(
|
||||
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "smear_previous", &prev_scl, ¶ms);
|
||||
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "_sculpt_smear_previous", &prev_scl, ¶ms);
|
||||
|
||||
SCULPT_vertex_random_access_ensure(ss);
|
||||
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue