Sculpt Vertex Colors: Initial implementation

Sculpt Vertex Colors is a painting system that runs inside sculpt mode, reusing all its tools and optimizations. This provides much better performance, easier to maintain code and more advanced features (new brush engine, filters, symmetry options, masks and face sets compatibility...). This is also the initial step for future features like vertex painting in Multires and brushes that can sculpt and paint at the same time.

This commit includes:
  - SCULPT_UNDO_COLOR for undo support in sculpt mode
  - SCULPT_UPDATE_COLOR and PBVH flags and rendering
  - Sculpt Color API functions
  - Sculpt capability for sculpt tools (only enabled in the Paint Brush for now)
  - Rendering support in workbench (default to Sculpt Vertex Colors except in Vertex Paint)
  - Conversion operator between MPropCol (Sculpt Vertex Colors) and MLoopCol (Vertex Paint)
  - Remesher reprojection in the Voxel Remehser
  - Paint Brush and Smear Brush with color smoothing in alt-smooth mode
  - Parameters for the new brush engine (density, opacity, flow, wet paint mixing, tip scale) implemented in Sculpt Vertex Colors
  - Color Filter
  - Color picker (uses S shortcut, replaces smooth)
  - Color selector in the top bar

Reviewed By: brecht

Maniphest Tasks: T72866

Differential Revision: https://developer.blender.org/D5975
This commit is contained in:
Pablo Dobarro 2020-06-22 20:05:28 +02:00
parent 27972c4225
commit f7bbc7cdbb
Notes: blender-bot 2023-02-14 06:23:08 +01:00
Referenced by commit 44aa9e40ff, Cleanup: Remove unused sculpt and vertex color operators
Referenced by commit 9f634a195d, Fix crash drawing non-mesh objects with vertex color
Referenced by issue #80478, Vertex color doesn't show in edit mode
Referenced by issue #78431, Atomics DRW_MeshCDMask
Referenced by issue #78283, Vertex Colors don't display in modes other than Vertex Paint
Referenced by issue #78225, Viewport vertex color mode: now it uses only sculpt vertex colors, even if datablock do not exist
Referenced by issue #72866, EngineeringPlan: Vertex Painting in Sculpt Mode
58 changed files with 2123 additions and 145 deletions

View File

@ -4343,6 +4343,8 @@ def km_sculpt(params):
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None),
("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
# Color
("sculpt.sample_color", {"type": 'S', "value": 'PRESS'}, None),
# Brush properties
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
{"properties": [("scalar", 0.9)]}),
@ -4365,7 +4367,7 @@ def km_sculpt(params):
# Tools
("paint.brush_select", {"type": 'X', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'DRAW')]}),
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
("paint.brush_select", {"type": 'S', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'SMOOTH')]}),
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'PINCH')]}),
@ -6280,6 +6282,16 @@ def km_3d_view_tool_sculpt_cloth_filter(params):
]},
)
def km_3d_view_tool_sculpt_color_filter(params):
return (
"3D View Tool: Sculpt, Color Filter",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("sculpt.color_filter", {"type": params.tool_tweak, "value": 'ANY'},
None)
]},
)
def km_3d_view_tool_paint_weight_sample_weight(params):
return (
"3D View Tool: Paint Weight, Sample Weight",
@ -6820,6 +6832,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_sculpt_lasso_mask(params),
km_3d_view_tool_sculpt_mesh_filter(params),
km_3d_view_tool_sculpt_cloth_filter(params),
km_3d_view_tool_sculpt_color_filter(params),
km_3d_view_tool_paint_weight_sample_weight(params),
km_3d_view_tool_paint_weight_sample_vertex_group(params),
km_3d_view_tool_paint_weight_gradient(params),

View File

@ -459,6 +459,31 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, Panel):
col.operator("mesh.vertex_color_remove", icon='REMOVE', text="")
class DATA_PT_sculpt_vertex_colors(MeshButtonsPanel, Panel):
bl_label = "Sculpt Vertex Colors"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
def draw(self, context):
layout = self.layout
me = context.mesh
row = layout.row()
col = row.column()
col.template_list("MESH_UL_vcols", "svcols", me, "sculpt_vertex_colors", me.sculpt_vertex_colors, "active_index", rows=2)
col = row.column(align=True)
col.operator("mesh.sculpt_vertex_color_add", icon='ADD', text="")
col.operator("mesh.sculpt_vertex_color_remove", icon='REMOVE', text="")
row = layout.row()
col = row.column()
col.operator("sculpt.vertex_to_loop_colors", text="Store Sculpt Vertex Color")
col.operator("sculpt.loop_to_vertex_colors", text="Load Sculpt Vertex Color")
class DATA_PT_remesh(MeshButtonsPanel, Panel):
bl_label = "Remesh"
bl_options = {'DEFAULT_CLOSED'}
@ -483,6 +508,8 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel):
col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask")
col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets")
col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
col.operator("object.voxel_remesh", text="Voxel Remesh")
else:
col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh")
@ -537,6 +564,7 @@ classes = (
DATA_PT_shape_keys,
DATA_PT_uv_texture,
DATA_PT_vertex_colors,
DATA_PT_sculpt_vertex_colors,
DATA_PT_face_maps,
DATA_PT_normals,
DATA_PT_texture_space,

View File

@ -607,6 +607,10 @@ def brush_settings(layout, context, brush, popover=False):
layout.operator("sculpt.set_persistent_base")
layout.separator()
if capabilities.has_color:
UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="Paint Color")
layout.prop(brush, "blend", text="Blend Mode")
if brush.sculpt_tool == 'CLAY_STRIPS':
row = layout.row()
row.prop(brush, "tip_roundness")
@ -655,6 +659,15 @@ def brush_settings(layout, context, brush, popover=False):
if brush.sculpt_tool == 'GRAB':
layout.prop(brush, "use_grab_active_vertex")
if brush.sculpt_tool == 'PAINT':
col = layout.column()
col.prop(brush, "flow")
col.prop(brush, "wet_mix")
col.prop(brush, "wet_persistence")
col.prop(brush, "density")
col.prop(brush, "tip_roundness")
col.prop(brush, "tip_scale_x")
if brush.sculpt_tool == 'MULTIPLANE_SCRAPE':
col = layout.column()
col.prop(brush, "multiplane_scrape_angle")

View File

@ -1258,6 +1258,22 @@ class _defs_sculpt:
draw_settings=draw_settings,
)
@ToolDef.from_fn
def color_filter():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("sculpt.color_filter")
layout.prop(props, "type", expand=False)
layout.prop(props, "strength")
return dict(
idname="builtin.color_filter",
label="Color Filter",
icon="ops.sculpt.color_filter",
widget=None,
keymap=(),
draw_settings=draw_settings,
)
class _defs_vertex_paint:
@ -2433,6 +2449,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_sculpt.mesh_filter,
_defs_sculpt.cloth_filter,
_defs_sculpt.color_filter,
None,
_defs_transform.translate,
_defs_transform.rotate,

View File

@ -295,6 +295,11 @@ class _draw_tool_settings_context_mode:
if not capabilities.has_direction:
layout.row().prop(brush, "direction", expand=True, text="")
if capabilities.has_color:
UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text = "")
layout.prop(brush, "blend", text="", expand = False)
return True
@staticmethod
@ -7333,6 +7338,12 @@ class VIEW3D_PT_sculpt_context_menu(Panel):
brush = context.tool_settings.sculpt.brush
capabilities = brush.sculpt_capabilities
if capabilities.has_color:
split = layout.split(factor=0.1)
UnifiedPaintPanel.prop_unified_color(split, context, brush, "color", text="")
UnifiedPaintPanel.prop_unified_color_picker(split, context, brush, "color", value_slider=True)
layout.prop(brush, "blend", text="")
UnifiedPaintPanel.prop_unified(
layout,
context,

View File

@ -806,6 +806,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask")
col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets")
col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
layout.operator("object.voxel_remesh", text="Remesh")

View File

@ -60,6 +60,7 @@ struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh,
/* Data reprojection functions */
void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
void BKE_remesh_reproject_vertex_paint(struct Mesh *target, struct Mesh *source);
void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, struct Mesh *source);
#ifdef __cplusplus

View File

@ -308,6 +308,7 @@ typedef struct SculptSession {
int totvert, totpoly;
struct KeyBlock *shapekey_active;
struct MPropCol *vcol;
float *vmask;
/* Mesh connectivity */
@ -430,7 +431,8 @@ void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph,
struct Object *ob_orig,
bool need_pmap,
bool need_mask);
bool need_mask,
bool need_colors);
void BKE_sculpt_update_object_before_eval(struct Object *ob_eval);
void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Object *ob_eval);

View File

@ -58,6 +58,10 @@ typedef struct {
float (*co)[3];
} PBVHProxyNode;
typedef struct {
float (*color)[4];
} PBVHColorBufferNode;
typedef enum {
PBVH_Leaf = 1 << 0,
@ -75,6 +79,7 @@ typedef enum {
PBVH_FullyUnmasked = 1 << 12,
PBVH_UpdateTopology = 1 << 13,
PBVH_UpdateColor = 1 << 14,
} PBVHNodeFlags;
typedef struct PBVHFrustumPlanes {
@ -252,6 +257,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
void BKE_pbvh_node_mark_update(PBVHNode *node);
void BKE_pbvh_node_mark_update_mask(PBVHNode *node);
void BKE_pbvh_node_mark_update_color(PBVHNode *node);
void BKE_pbvh_node_mark_update_visibility(PBVHNode *node);
void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node);
void BKE_pbvh_node_mark_redraw(PBVHNode *node);
@ -352,6 +358,7 @@ typedef struct PBVHVertexIter {
struct MVert *mverts;
int totvert;
const int *vert_indices;
struct MPropCol *vcol;
float *vmask;
/* bmesh */
@ -368,6 +375,7 @@ typedef struct PBVHVertexIter {
short *no;
float *fno;
float *mask;
float *col;
bool visible;
} PBVHVertexIter;
@ -419,7 +427,9 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi.no = vi.mvert->no; \
vi.index = vi.vert_indices[vi.i]; \
if (vi.vmask) \
vi.mask = &vi.vmask[vi.vert_indices[vi.gx]]; \
vi.mask = &vi.vmask[vi.index]; \
if (vi.vcol) \
vi.col = vi.vcol[vi.index].color; \
} \
else { \
if (!BLI_gsetIterator_done(&vi.bm_unique_verts)) { \
@ -472,6 +482,9 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings,
struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh);
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
#ifdef __cplusplus
}
#endif

View File

@ -1570,6 +1570,23 @@ void BKE_brush_sculpt_reset(Brush *br)
br->alpha = 1.0f;
br->height = 0.05f;
break;
case SCULPT_TOOL_PAINT:
br->hardness = 0.4f;
br->spacing = 10;
br->alpha = 0.6f;
br->flow = 1.0f;
br->tip_scale_x = 1.0f;
br->tip_roundness = 1.0f;
br->density = 1.0f;
br->flag &= ~BRUSH_SPACE_ATTEN;
zero_v3(br->rgb);
break;
case SCULPT_TOOL_SMEAR:
br->alpha = 1.0f;
br->spacing = 7;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_SPHERE;
break;
default:
break;
}
@ -1629,14 +1646,15 @@ void BKE_brush_sculpt_reset(Brush *br)
break;
case SCULPT_TOOL_SIMPLIFY:
case SCULPT_TOOL_PAINT:
case SCULPT_TOOL_MASK:
case SCULPT_TOOL_DRAW_FACE_SETS:
br->add_col[0] = 0.750000;
br->add_col[1] = 0.750000;
br->add_col[2] = 0.750000;
br->sub_col[0] = 0.750000;
br->sub_col[1] = 0.750000;
br->sub_col[2] = 0.750000;
br->add_col[0] = 0.75f;
br->add_col[1] = 0.75f;
br->add_col[2] = 0.75f;
br->sub_col[0] = 0.75f;
br->sub_col[1] = 0.75f;
br->sub_col[2] = 0.75f;
break;
case SCULPT_TOOL_CLOTH:

View File

@ -858,7 +858,6 @@ static void layerDoMinMax_mloopcol(const void *data, void *vmin, void *vmax)
if (m->a < min->a) {
min->a = m->a;
}
if (m->r > max->r) {
max->r = m->r;
}
@ -1355,7 +1354,7 @@ static void layerCopyValue_propcol(const void *source,
/* Modes that do a full copy or nothing. */
if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) {
/* TODO: Check for a real valid way to get 'factor' value of our dest color? */
const float f = (m2->col[0] + m2->col[1] + m2->col[2]) / 3.0f;
const float f = (m2->color[0] + m2->color[1] + m2->color[2]) / 3.0f;
if (mixmode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && f < mixfactor) {
return; /* Do Nothing! */
}
@ -1363,29 +1362,29 @@ static void layerCopyValue_propcol(const void *source,
return; /* Do Nothing! */
}
}
copy_v3_v3(m2->col, m1->col);
copy_v3_v3(m2->color, m1->color);
}
else { /* Modes that support 'real' mix factor. */
if (mixmode == CDT_MIX_MIX) {
blend_color_mix_float(tmp_col, m2->col, m1->col);
blend_color_mix_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_ADD) {
blend_color_add_float(tmp_col, m2->col, m1->col);
blend_color_add_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_SUB) {
blend_color_sub_float(tmp_col, m2->col, m1->col);
blend_color_sub_float(tmp_col, m2->color, m1->color);
}
else if (mixmode == CDT_MIX_MUL) {
blend_color_mul_float(tmp_col, m2->col, m1->col);
blend_color_mul_float(tmp_col, m2->color, m1->color);
}
else {
memcpy(tmp_col, m1->col, sizeof(tmp_col));
memcpy(tmp_col, m1->color, sizeof(tmp_col));
}
blend_color_interpolate_float(m2->col, m2->col, tmp_col, mixfactor);
blend_color_interpolate_float(m2->color, m2->color, tmp_col, mixfactor);
copy_v3_v3(m2->col, m1->col);
copy_v3_v3(m2->color, m1->color);
}
m2->col[3] = m1->col[3];
m2->color[3] = m1->color[3];
}
static bool layerEqual_propcol(const void *data1, const void *data2)
@ -1394,7 +1393,7 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
float tot = 0;
for (int i = 0; i < 4; i++) {
float c = (m1->col[i] - m2->col[i]);
float c = (m1->color[i] - m2->color[i]);
tot += c * c;
}
@ -1404,29 +1403,29 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
static void layerMultiply_propcol(void *data, float fac)
{
MPropCol *m = data;
mul_v4_fl(m->col, fac);
mul_v4_fl(m->color, fac);
}
static void layerAdd_propcol(void *data1, const void *data2)
{
MPropCol *m = data1;
const MPropCol *m2 = data2;
add_v4_v4(m->col, m2->col);
add_v4_v4(m->color, m2->color);
}
static void layerDoMinMax_propcol(const void *data, void *vmin, void *vmax)
{
const MPropCol *m = data;
MPropCol *min = vmin, *max = vmax;
minmax_v4v4_v4(min->col, max->col, m->col);
minmax_v4v4_v4(min->color, max->color, m->color);
}
static void layerInitMinMax_propcol(void *vmin, void *vmax)
{
MPropCol *min = vmin, *max = vmax;
copy_v4_fl(min->col, FLT_MAX);
copy_v4_fl(max->col, FLT_MIN);
copy_v4_fl(min->color, FLT_MAX);
copy_v4_fl(max->color, FLT_MIN);
}
static void layerDefault_propcol(void *data, int count)
@ -1436,7 +1435,7 @@ static void layerDefault_propcol(void *data, int count)
MPropCol *pcol = (MPropCol *)data;
int i;
for (i = 0; i < count; i++) {
copy_v4_v4(pcol[i].col, default_propcol.col);
copy_v4_v4(pcol[i].color, default_propcol.color);
}
}
@ -1450,14 +1449,14 @@ static void layerInterp_propcol(
float weight = weights ? weights[i] : 1.0f;
const MPropCol *src = sources[i];
if (sub_weights) {
madd_v4_v4fl(col, src->col, (*sub_weight) * weight);
madd_v4_v4fl(col, src->color, (*sub_weight) * weight);
sub_weight++;
}
else {
madd_v4_v4fl(col, src->col, weight);
madd_v4_v4fl(col, src->color, weight);
}
}
copy_v4_v4(mc->col, col);
copy_v4_v4(mc->color, col);
}
static int layerMaxNum_propcol(void)
@ -1871,7 +1870,7 @@ const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
};
const CustomData_MeshMasks CD_MASK_MESH = {
.vmask = (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
CD_MASK_GENERIC_DATA),
CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = 0,
.lmask = (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL |
@ -1881,7 +1880,7 @@ const CustomData_MeshMasks CD_MASK_MESH = {
};
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_GENERIC_DATA),
CD_MASK_SHAPE_KEYINDEX | CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_GENERIC_DATA),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@ -1901,7 +1900,8 @@ const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
};
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_GENERIC_DATA),
CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_GENERIC_DATA |
CD_MASK_PROP_COLOR),
.emask = (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@ -1925,7 +1925,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = {
.vmask = (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL |
CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO |
CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK |
CD_MASK_GENERIC_DATA),
CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
.emask = (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT |
CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
.fmask = (CD_MASK_MFACE | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_MTFACE | CD_MASK_MCOL |

View File

@ -405,6 +405,37 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
free_bvhtree_from_mesh(&bvhtree);
}
void BKE_remesh_reproject_vertex_paint(Mesh *target, Mesh *source)
{
BVHTreeFromMesh bvhtree = {
.nearest_callback = NULL,
};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR);
for (int layer_n = 0; layer_n < tot_color_layer; layer_n++) {
const char *layer_name = CustomData_get_layer_name(&source->vdata, CD_PROP_COLOR, layer_n);
CustomData_add_layer_named(
&target->vdata, CD_PROP_COLOR, CD_CALLOC, NULL, target->totvert, layer_name);
MPropCol *target_color = CustomData_get_layer_n(&target->vdata, CD_PROP_COLOR, layer_n);
MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
MPropCol *source_color = CustomData_get_layer_n(&source->vdata, CD_PROP_COLOR, layer_n);
for (int i = 0; i < target->totvert; i++) {
BVHTreeNearest nearest;
nearest.index = -1;
nearest.dist_sq = FLT_MAX;
BLI_bvhtree_find_nearest(
bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
if (nearest.index != -1) {
copy_v4_v4(target_color[i].color, source_color[nearest.index].color);
}
}
}
free_bvhtree_from_mesh(&bvhtree);
}
struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
{
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);

View File

@ -183,7 +183,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
#endif
/* Always compute UVs, vertex colors as orcos for render. */
cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
cddata_masks.vmask |= CD_MASK_ORCO;
cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
if (em) {
makeDerivedMesh(depsgraph, scene, ob, em, &cddata_masks); /* was CD_MASK_BAREMESH */

View File

@ -1474,8 +1474,12 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
/**
* \param need_mask: So that the evaluated mesh that is returned has mask data.
*/
static void sculpt_update_object(
Depsgraph *depsgraph, Object *ob, Mesh *me_eval, bool need_pmap, bool need_mask)
static void sculpt_update_object(Depsgraph *depsgraph,
Object *ob,
Mesh *me_eval,
bool need_pmap,
bool need_mask,
bool need_colors)
{
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
@ -1503,6 +1507,16 @@ static void sculpt_update_object(
}
}
/* Add a color layer if a color tool is used. */
Mesh *orig_me = BKE_object_get_original_mesh(ob);
if (need_colors) {
if (!CustomData_has_layer(&orig_me->vdata, CD_PROP_COLOR)) {
CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert);
BKE_mesh_update_customdata_pointers(orig_me, true);
DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY);
}
}
/* tessfaces aren't used and will become invalid */
BKE_mesh_tessface_clear(me);
@ -1535,6 +1549,7 @@ static void sculpt_update_object(
ss->multires.modifier = NULL;
ss->multires.level = 0;
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
ss->vcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
}
/* Sculpt Face Sets. */
@ -1663,13 +1678,11 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
BLI_assert(me_eval != NULL);
sculpt_update_object(depsgraph, ob_orig, me_eval, false, false);
sculpt_update_object(depsgraph, ob_orig, me_eval, false, false, false);
}
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph,
Object *ob_orig,
bool need_pmap,
bool need_mask)
void BKE_sculpt_update_object_for_edit(
Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool need_colors)
{
/* Update from sculpt operators and undo, to update sculpt session
* and PBVH after edits. */
@ -1679,7 +1692,7 @@ void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph,
BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask);
sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask, need_colors);
}
int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)

View File

@ -1325,6 +1325,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
pbvh->face_sets_color_seed,
pbvh->face_sets_color_default,
CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR),
update_flags);
break;
case PBVH_BMESH:
@ -1442,6 +1443,10 @@ void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flag)
pbvh_update_mask_redraw(pbvh, nodes, totnode, flag);
}
if (flag & (PBVH_UpdateColor)) {
/* Do nothing */
}
if (flag & (PBVH_UpdateVisibility)) {
pbvh_update_visibility_redraw(pbvh, nodes, totnode, flag);
}
@ -1729,6 +1734,11 @@ void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
node->flag |= PBVH_UpdateMask | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_update_color(PBVHNode *node)
{
node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
void BKE_pbvh_node_mark_update_visibility(PBVHNode *node)
{
node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers |
@ -2905,6 +2915,26 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot)
*r_tot = tot;
}
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node)
{
if (!node->color_buffer.color) {
node->color_buffer.color = MEM_callocN(node->uniq_verts * sizeof(float) * 4, "Color buffer");
}
return &node->color_buffer;
}
void BKE_pbvh_node_color_buffer_free(PBVH *pbvh)
{
PBVHNode **nodes;
int totnode;
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
for (int i = 0; i < totnode; i++) {
MEM_SAFE_FREE(nodes[i]->color_buffer.color);
}
MEM_SAFE_FREE(nodes);
}
void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode)
{
struct CCGElem **grids;
@ -2958,6 +2988,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->mask = NULL;
if (pbvh->type == PBVH_FACES) {
vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK);
vi->vcol = CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR);
}
}

View File

@ -105,6 +105,9 @@ struct PBVHNode {
float (*bm_orco)[3];
int (*bm_ortri)[3];
int bm_tot_ortri;
/* Used to store the brush color during a stroke and composite it over the original color */
PBVHColorBufferNode color_buffer;
};
typedef enum {

View File

@ -1749,6 +1749,16 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
/* Paint Brush. This ensure that the brush paints by default. Used during the develpment and
* patch review of the initial Sculpt Vertex Colors implementation (D5975) */
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->ob_mode & OB_MODE_SCULPT && brush->sculpt_tool == SCULPT_TOOL_PAINT) {
brush->tip_roundness = 1.0f;
brush->flow = 1.0f;
brush->density = 1.0f;
brush->tip_scale_x = 1.0f;
}
}
}
}
@ -3474,7 +3484,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Mesh *me = bmain->meshes.first; me; me = me->id.next) {
me->flag &= ~(ME_FLAG_UNUSED_0 | ME_FLAG_UNUSED_1 | ME_FLAG_UNUSED_3 | ME_FLAG_UNUSED_4 |
ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_FLAG_UNUSED_8);
ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_VERTEX_COLORS);
}
for (Material *mat = bmain->materials.first; mat; mat = mat->id.next) {

View File

@ -682,6 +682,22 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX;
}
brush_name = "Paint";
brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_PAINT;
}
brush_name = "Smear";
brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_SMEAR;
}
brush_name = "Simplify";
brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
if (!brush) {

View File

@ -174,8 +174,19 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd,
color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR);
if (use_single_drawcall) {
struct GPUBatch *geom = (use_vcol) ? DRW_cache_mesh_surface_vertpaint_get(ob) :
DRW_cache_object_surface_get(ob);
struct GPUBatch *geom;
if (use_vcol) {
if (ob->mode & OB_MODE_VERTEX_PAINT) {
geom = DRW_cache_mesh_surface_vertpaint_get(ob);
}
else {
geom = DRW_cache_mesh_surface_sculptcolors_get(ob);
}
}
else {
geom = DRW_cache_object_surface_get(ob);
}
if (geom) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, r_transp);
DRW_shgroup_call(grp, geom, ob);
@ -247,8 +258,13 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (me == NULL || me->mloopuv == NULL)) {
color_type = V3D_SHADING_MATERIAL_COLOR;
}
if ((color_type == V3D_SHADING_VERTEX_COLOR) && (me == NULL || me->mloopcol == NULL)) {
color_type = V3D_SHADING_OBJECT_COLOR;
if (color_type == V3D_SHADING_VERTEX_COLOR) {
if (me == NULL) {
color_type = V3D_SHADING_OBJECT_COLOR;
}
if (!CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
color_type = V3D_SHADING_OBJECT_COLOR;
}
}
if (r_sculpt_pbvh) {

View File

@ -2870,6 +2870,12 @@ GPUBatch *DRW_cache_mesh_surface_vertpaint_get(Object *ob)
return DRW_mesh_batch_cache_get_surface_vertpaint(ob->data);
}
GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);
return DRW_mesh_batch_cache_get_surface_sculpt(ob->data);
}
GPUBatch *DRW_cache_mesh_surface_weights_get(Object *ob)
{
BLI_assert(ob->type == OB_MESH);

View File

@ -136,6 +136,7 @@ struct GPUBatch **DRW_cache_mesh_surface_shaded_get(struct Object *ob,
struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_vertpaint_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_weights_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob);
struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob);

View File

@ -53,6 +53,7 @@ typedef struct DRW_MeshCDMask {
uint32_t uv : 8;
uint32_t tan : 8;
uint32_t vcol : 8;
uint32_t sculpt_vcol : 8;
uint32_t orco : 1;
uint32_t tan_orco : 1;
/** Edit uv layer is from the base edit mesh as

View File

@ -2177,7 +2177,9 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
GPU_vertformat_deinterleave(&format);
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
uint32_t vcol_layers = mr->cache->cd_used.vcol;
uint32_t svcol_layers = mr->cache->cd_used.sculpt_vcol;
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
@ -2194,6 +2196,33 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
GPU_vertformat_alias_add(&format, "ac");
}
/* Gather number of auto layers. */
/* We only do vcols that are not overridden by uvs and sculpt vertex colors */
if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
GPU_vertformat_alias_add(&format, attr_name);
}
}
}
/* Sculpt Vertex Colors */
for (int i = 0; i < 8; i++) {
if (svcol_layers & (1 << i)) {
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
GPU_vertformat_alias_add(&format, "c");
}
if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
GPU_vertformat_alias_add(&format, "ac");
}
/* Gather number of auto layers. */
/* We only do vcols that are not overridden by uvs */
if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
@ -2202,6 +2231,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
}
}
}
GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@ -2211,6 +2241,8 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
} gpuMeshVcol;
gpuMeshVcol *vcol_data = (gpuMeshVcol *)vbo->data;
MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
for (int i = 0; i < MAX_MCOL; i++) {
if (vcol_layers & (1 << i)) {
if (mr->extract_type == MR_EXTRACT_BMESH) {
@ -2239,6 +2271,36 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
}
}
}
if (svcol_layers & (1 << i)) {
if (mr->extract_type == MR_EXTRACT_BMESH) {
int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
BMIter l_iter, f_iter;
BMLoop *loop;
BMFace *efa;
BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(loop->v, cd_ofs);
vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
vcol_data++;
}
}
}
else {
MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
for (int l = 0; l < mr->loop_len; l++, vcol_data++) {
vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[l].v].color[0]);
vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[l].v].color[1]);
vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[l].v].color[2]);
vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[l].v].color[3]);
}
}
vcol_data += mr->loop_len;
}
}
return NULL;
}

View File

@ -170,6 +170,7 @@ struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me,
struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Mesh *me);
struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me);
/* edit-mesh drawing */
struct GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(struct Mesh *me);

View File

@ -112,6 +112,21 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
return &me->ldata;
}
BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
{
switch ((eMeshWrapperType)me->runtime.wrapper_type) {
case ME_WRAPPER_TYPE_MDATA:
return &me->vdata;
break;
case ME_WRAPPER_TYPE_BMESH:
return &me->edit_mesh->bm->vdata;
break;
}
BLI_assert(0);
return &me->vdata;
}
static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
@ -135,7 +150,19 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd
static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR);
if (layer != -1) {
cd_used->sculpt_vcol |= (1 << layer);
}
}
static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
const CustomData *cd_ldata = &me_final->ldata;
int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
if (layer != -1) {
cd_used->vcol |= (1 << layer);
@ -148,6 +175,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
{
const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
/* See: DM_vertex_attributes_from_gpu for similar logic */
DRW_MeshCDMask cd_used;
@ -175,6 +203,11 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name);
type = CD_MCOL;
}
if (layer == -1) {
layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name);
type = CD_PROP_COLOR;
}
#if 0 /* Tangents are always from UV's - this will never happen. */
if (layer == -1) {
layer = CustomData_get_named_layer(cd_ldata, CD_TANGENT, name);
@ -222,7 +255,20 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
}
break;
}
case CD_PROP_COLOR: {
/* Sculpt Vertex Colors */
if (layer == -1) {
layer = (name[0] != '\0') ?
CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) :
CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR);
}
if (layer != -1) {
cd_used.sculpt_vcol |= (1 << layer);
}
break;
}
case CD_MCOL: {
/* Vertex Color Data */
if (layer == -1) {
layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) :
CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL);
@ -230,6 +276,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
if (layer != -1) {
cd_used.vcol |= (1 << layer);
}
break;
}
case CD_ORCO: {
@ -679,10 +726,22 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me)
{
DRW_MeshCDMask cd_needed;
mesh_cd_layers_type_clear(&cd_needed);
mesh_cd_calc_active_vcol_layer(me, &cd_needed);
mesh_cd_calc_active_mloopcol_layer(me, &cd_needed);
BLI_assert(cd_needed.vcol != 0 &&
"No vcol layer available in vertpaint, but batches requested anyway!");
"No MLOOPCOL layer available in vertpaint, but batches requested anyway!");
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me)
{
DRW_MeshCDMask cd_needed;
mesh_cd_layers_type_clear(&cd_needed);
mesh_cd_calc_active_vcol_layer(me, &cd_needed);
BLI_assert(cd_needed.sculpt_vcol != 0 &&
"No MPropCol layer available in Sculpt, but batches requested anyway!");
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
@ -799,6 +858,14 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me)
return DRW_batch_request(&cache->batch.surface);
}
GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
sculpt_request_active_vcol(cache, me);
mesh_batch_cache_add_request(cache, MBC_SURFACE);
return DRW_batch_request(&cache->batch.surface);
}
int DRW_mesh_material_count_get(Mesh *me)
{
return mesh_render_mat_len_get(me);
@ -1137,7 +1204,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.orco != cache->cd_needed.orco) {
GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.orco);
}
if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) {
if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) ||
((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) !=
cache->cd_needed.sculpt_vcol)) {
GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol);
}
}
@ -1218,7 +1287,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.uv != 0) {
DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.uv);
}
if (cache->cd_used.vcol != 0) {
if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol);
}
}
@ -1286,7 +1355,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) {
DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.tan);
}
if (cache->cd_used.vcol != 0) {
if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.vcol);
}
if (cache->cd_used.orco != 0) {

View File

@ -426,6 +426,15 @@ bool ED_mesh_color_remove_index(struct Mesh *me, const int n);
bool ED_mesh_color_remove_active(struct Mesh *me);
bool ED_mesh_color_remove_named(struct Mesh *me, const char *name);
bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name);
int ED_mesh_sculpt_color_add(struct Mesh *me,
const char *name,
const bool active_set,
const bool do_init);
bool ED_mesh_sculpt_color_remove_index(struct Mesh *me, const int n);
bool ED_mesh_sculpt_color_remove_active(struct Mesh *me);
bool ED_mesh_sculpt_color_remove_named(struct Mesh *me, const char *name);
void ED_mesh_report_mirror(struct wmOperator *op, int totmirr, int totfail);
void ED_mesh_report_mirror_ex(struct wmOperator *op, int totmirr, int totfail, char selectmode);

View File

@ -121,8 +121,15 @@ static void delete_customdata_layer(Mesh *me, CustomDataLayer *layer)
CustomData *data;
int layer_index, tot, n;
data = mesh_customdata_get_type(
me, (ELEM(type, CD_MLOOPUV, CD_MLOOPCOL)) ? BM_LOOP : BM_FACE, &tot);
char htype = BM_FACE;
if (ELEM(type, CD_MLOOPCOL, CD_MLOOPUV)) {
htype = BM_LOOP;
}
else if (ELEM(type, CD_PROP_COLOR)) {
htype = BM_VERT;
}
data = mesh_customdata_get_type(me, htype, &tot);
layer_index = CustomData_get_layer_index(data, type);
n = (layer - &data->layers[layer_index]);
BLI_assert(n >= 0 && (n + layer_index) < data->totlayer);
@ -488,6 +495,117 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name)
}
}
/*********************** Sculpt Vertex colors operators ************************/
/* note: keep in sync with ED_mesh_uv_texture_add */
int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool active_set, const bool do_init)
{
BMEditMesh *em;
int layernum;
if (me->edit_mesh) {
em = me->edit_mesh;
layernum = CustomData_number_of_layers(&em->bm->vdata, CD_PROP_COLOR);
if (layernum >= MAX_MCOL) {
return -1;
}
/* CD_PROP_COLOR */
BM_data_layer_add_named(em->bm, &em->bm->vdata, CD_PROP_COLOR, name);
/* copy data from active vertex color layer */
if (layernum && do_init) {
const int layernum_dst = CustomData_get_active_layer(&em->bm->vdata, CD_PROP_COLOR);
BM_data_layer_copy(em->bm, &em->bm->vdata, CD_PROP_COLOR, layernum_dst, layernum);
}
if (active_set || layernum == 0) {
CustomData_set_layer_active(&em->bm->vdata, CD_PROP_COLOR, layernum);
}
}
else {
layernum = CustomData_number_of_layers(&me->vdata, CD_PROP_COLOR);
if (layernum >= MAX_MCOL) {
return -1;
}
if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) {
MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
CustomData_add_layer_named(
&me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name);
}
else {
CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
}
if (active_set || layernum == 0) {
CustomData_set_layer_active(&me->vdata, CD_PROP_COLOR, layernum);
}
BKE_mesh_update_customdata_pointers(me, true);
}
DEG_id_tag_update(&me->id, 0);
WM_main_add_notifier(NC_GEOM | ND_DATA, me);
return layernum;
}
bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name)
{
BLI_assert(me->edit_mesh == NULL);
if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
BKE_mesh_update_customdata_pointers(me, true);
}
DEG_id_tag_update(&me->id, 0);
return (me->mloopcol != NULL);
}
bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n)
{
CustomData *vdata = GET_CD_DATA(me, vdata);
CustomDataLayer *cdl;
int index;
index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n);
cdl = (index == -1) ? NULL : &vdata->layers[index];
if (!cdl) {
return false;
}
delete_customdata_layer(me, cdl);
DEG_id_tag_update(&me->id, 0);
WM_main_add_notifier(NC_GEOM | ND_DATA, me);
return true;
}
bool ED_mesh_sculpt_color_remove_active(Mesh *me)
{
CustomData *vdata = GET_CD_DATA(me, vdata);
const int n = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
if (n != -1) {
return ED_mesh_sculpt_color_remove_index(me, n);
}
else {
return false;
}
}
bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name)
{
CustomData *vdata = GET_CD_DATA(me, vdata);
const int n = CustomData_get_named_layer(vdata, CD_PROP_COLOR, name);
if (n != -1) {
return ED_mesh_sculpt_color_remove_index(me, n);
}
else {
return false;
}
}
/*********************** UV texture operators ************************/
static bool layers_poll(bContext *C)
@ -619,6 +737,62 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/*********************** Sculpt Vertex Color Operators ************************/
static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
Mesh *me = ob->data;
if (ED_mesh_sculpt_color_add(me, NULL, true, true) == -1) {
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Sculpt Vertex Color";
ot->description = "Add vertex color layer";
ot->idname = "MESH_OT_sculpt_vertex_color_add";
/* api callbacks */
ot->poll = layers_poll;
ot->exec = mesh_sculpt_vertex_color_add_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
Mesh *me = ob->data;
if (!ED_mesh_sculpt_color_remove_active(me)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Sculpt Vertex Color";
ot->description = "Remove vertex color layer";
ot->idname = "MESH_OT_sculpt_vertex_color_remove";
/* api callbacks */
ot->exec = mesh_sculpt_vertex_color_remove_exec;
ot->poll = layers_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* *** CustomData clear functions, we need an operator for each *** */
static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int type)

View File

@ -267,6 +267,8 @@ void MESH_OT_uv_texture_add(struct wmOperatorType *ot);
void MESH_OT_uv_texture_remove(struct wmOperatorType *ot);
void MESH_OT_vertex_color_add(struct wmOperatorType *ot);
void MESH_OT_vertex_color_remove(struct wmOperatorType *ot);
void MESH_OT_sculpt_vertex_color_add(struct wmOperatorType *ot);
void MESH_OT_sculpt_vertex_color_remove(struct wmOperatorType *ot);
/* no create_mask yet */
void MESH_OT_customdata_mask_clear(struct wmOperatorType *ot);
void MESH_OT_customdata_skin_add(struct wmOperatorType *ot);

View File

@ -155,6 +155,8 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_uv_texture_remove);
WM_operatortype_append(MESH_OT_vertex_color_add);
WM_operatortype_append(MESH_OT_vertex_color_remove);
WM_operatortype_append(MESH_OT_sculpt_vertex_color_add);
WM_operatortype_append(MESH_OT_sculpt_vertex_color_remove);
WM_operatortype_append(MESH_OT_customdata_mask_clear);
WM_operatortype_append(MESH_OT_customdata_skin_add);
WM_operatortype_append(MESH_OT_customdata_skin_clear);

View File

@ -174,6 +174,11 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
BKE_remesh_reproject_sculpt_face_sets(new_mesh, mesh);
}
if (mesh->flag & ME_REMESH_REPROJECT_VERTEX_COLORS) {
BKE_mesh_runtime_clear_geometry(mesh);
BKE_remesh_reproject_vertex_paint(new_mesh, mesh);
}
BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) {

View File

@ -64,10 +64,12 @@ set(SRC
sculpt_detail.c
sculpt_dyntopo.c
sculpt_face_set.c
sculpt_filter_color.c
sculpt_filter_mask.c
sculpt_filter_mesh.c
sculpt_mask_expand.c
sculpt_multiplane_scrape.c
sculpt_paint_color.c
sculpt_pose.c
sculpt_smooth.c
sculpt_transform.c

View File

@ -1441,7 +1441,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
* cursor won't be tagged to update, so always initialize the preview chain if it is
* null before drawing it. */
if (update_previews || !ss->pose_ik_chain_preview) {
BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false);
BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false, false);
/* Free the previous pose brush preview. */
if (ss->pose_ik_chain_preview) {

View File

@ -151,7 +151,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
mode = RNA_enum_get(op->ptr, "mode");
value = RNA_float_get(op->ptr, "value");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@ -313,7 +313,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *
/* Transform the clip planes in object space. */
ED_view3d_clipping_calc(&bb, clip_planes, vc->region, vc->obact, rect);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
@ -500,7 +500,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);

View File

@ -1101,12 +1101,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph,
BLI_assert(ob->sculpt == NULL);
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = object_mode;
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
}
static void vertex_paint_init_stroke(Depsgraph *depsgraph, Object *ob)
{
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
}
static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)

View File

@ -30,6 +30,7 @@
#include "BLI_gsqueue.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@ -153,6 +154,21 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
return NULL;
}
const float *SCULPT_vertex_color_get(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
if (ss->vcol) {
return ss->vcol[index].color;
}
break;
case PBVH_BMESH:
case PBVH_GRIDS:
break;
}
return NULL;
}
void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
@ -998,6 +1014,8 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
SCULPT_TOOL_LAYER,
SCULPT_TOOL_POSE,
SCULPT_TOOL_CLOTH,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_SMEAR,
SCULPT_TOOL_DRAW_FACE_SETS);
}
@ -1045,9 +1063,7 @@ typedef enum StrokeFlags {
/* Initialize a SculptOrigVertData for accessing original vertex data;
* handles BMesh, mesh, and multires. */
static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
Object *ob,
SculptUndoNode *unode)
void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
BMesh *bm = ss->bm;
@ -1062,6 +1078,7 @@ static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
data->coords = data->unode->co;
data->normals = data->unode->no;
data->vmasks = data->unode->mask;
data->colors = data->unode->col;
}
}
@ -1071,7 +1088,7 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *
{
SculptUndoNode *unode;
unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
sculpt_orig_vert_data_unode_init(data, ob, unode);
SCULPT_orig_vert_data_unode_init(data, ob, unode);
}
/* Update a SculptOrigVertData for a particular vertex from the PBVH
@ -1087,6 +1104,9 @@ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter
orig_data->no = orig_data->normals[iter->i];
}
}
else if (orig_data->unode->type == SCULPT_UNDO_COLOR) {
orig_data->col = orig_data->colors[iter->i];
}
else if (orig_data->unode->type == SCULPT_UNDO_MASK) {
if (orig_data->bm_log) {
orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert);
@ -1247,7 +1267,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode);
SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
@ -1265,6 +1285,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
else if (orig_data.unode->type == SCULPT_UNDO_MASK) {
*vd.mask = orig_data.mask;
}
else if (orig_data.unode->type == SCULPT_UNDO_COLOR) {
copy_v4_v4(vd.col, orig_data.col);
}
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@ -1301,6 +1324,8 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode);
BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings);
BKE_pbvh_node_color_buffer_free(ss->pbvh);
MEM_SAFE_FREE(nodes);
}
@ -1478,6 +1503,9 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test,
{
float side = M_SQRT1_2;
float local_co[3];
float i_local[4][4];
invert_m4_m4(i_local, local);
if (sculpt_brush_test_clipping(test, co)) {
return false;
@ -2116,6 +2144,12 @@ static float brush_strength(const Sculpt *sd,
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_SLIDE_RELAX:
return alpha * pressure * overlap * feather * 2.0f;
case SCULPT_TOOL_PAINT:
final_pressure = pressure * pressure;
return final_pressure * overlap * feather;
case SCULPT_TOOL_SMEAR:
final_pressure = pressure * pressure;
return final_pressure * overlap * feather;
case SCULPT_TOOL_CLAY_STRIPS:
/* Clay Strips needs less strength to compensate the curve. */
final_pressure = powf(pressure, 1.5f);
@ -5261,17 +5295,16 @@ static void do_brush_action_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
else {
SCULPT_undo_push_node(data->ob,
data->nodes[n],
data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
SCULPT_UNDO_COORDS);
}
if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
BKE_pbvh_node_mark_update_color(data->nodes[n]);
}
else {
SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
@ -5282,7 +5315,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
int totnode;
PBVHNode **nodes;
/* Build a list of all nodes that are potentially within the brush's area of influence. */
/* Check for unsupported features. */
PBVHType type = BKE_pbvh_type(ss->pbvh);
if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) {
return;
}
if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
return;
}
/* Build a list of all nodes that are potentially within the brush's area of influence */
/* These brushes need to update all nodes as they are not constrained by the brush radius */
/* Elastic deform needs all nodes to avoid artifacts as the effect of the brush is not
@ -5471,6 +5514,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
case SCULPT_TOOL_DRAW_FACE_SETS:
SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_PAINT:
SCULPT_do_paint_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SMEAR:
SCULPT_do_smear_brush(sd, ob, nodes, totnode);
break;
}
if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
@ -5998,6 +6047,10 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Cloth Brush";
case SCULPT_TOOL_DRAW_FACE_SETS:
return "Draw Face Sets";
case SCULPT_TOOL_PAINT:
return "Paint Brush";
case SCULPT_TOOL_SMEAR:
return "Smear Brush";
}
return "Sculpting";
@ -6012,6 +6065,7 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->dial);
MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
MEM_SAFE_FREE(cache->layer_displacement_factor);
MEM_SAFE_FREE(cache->prev_colors);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@ -6126,7 +6180,11 @@ static void sculpt_update_cache_invariants(
cache->saved_mask_brush_tool = brush->mask_tool;
brush->mask_tool = BRUSH_MASK_SMOOTH;
}
else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
else if (ELEM(brush->sculpt_tool,
SCULPT_TOOL_SLIDE_RELAX,
SCULPT_TOOL_DRAW_FACE_SETS,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_SMEAR)) {
/* Do nothing, this tool has its own smooth mode. */
}
else {
@ -6273,6 +6331,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
int tool = brush->sculpt_tool;
if (ELEM(tool,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_GRAB,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_CLOTH,
@ -6526,7 +6585,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) ||
(brush->sculpt_tool == SCULPT_TOOL_POSE) ||
(brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) ||
(brush->sculpt_tool == SCULPT_TOOL_CLOTH) ||
(brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
(brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS));
}
@ -6540,7 +6599,7 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b
if (ss->shapekey_active || ss->deform_modifiers_active ||
(!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false);
}
}
@ -6884,7 +6943,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
SculptSession *ss = CTX_data_active_object(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
int mode = RNA_enum_get(op->ptr, "mode");
bool is_smooth;
bool is_smooth, needs_colors;
bool need_mask = false;
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
@ -6899,7 +6958,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
sculpt_brush_init_tex(scene, sd, ss);
is_smooth = sculpt_needs_connectivity_info(sd, brush, ss, mode);
BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask);
needs_colors = ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors);
}
static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
@ -7048,6 +7108,10 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
}
if (update_flags & SCULPT_UPDATE_COLOR) {
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
}
@ -7162,6 +7226,9 @@ static void sculpt_stroke_update_step(bContext *C,
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
}
else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
}
else {
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
}
@ -7199,7 +7266,11 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
brush->mask_tool = ss->cache->saved_mask_brush_tool;
}
else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
else if (ELEM(brush->sculpt_tool,
SCULPT_TOOL_SLIDE_RELAX,
SCULPT_TOOL_DRAW_FACE_SETS,
SCULPT_TOOL_PAINT,
SCULPT_TOOL_SMEAR)) {
/* Do nothing. */
}
else {
@ -7215,6 +7286,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
SCULPT_automasking_end(ob);
}
BKE_pbvh_node_color_buffer_free(ss->pbvh);
SCULPT_cache_free(ss->cache);
ss->cache = NULL;
@ -7353,7 +7425,7 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
if (ss) {
SCULPT_vertex_random_access_init(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
MEM_SAFE_FREE(ss->layer_base);
@ -7555,7 +7627,7 @@ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob)
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = OB_MODE_SCULPT;
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
/* Here we can detect geometry that was just added to Sculpt Mode as it has the
* SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
@ -7841,7 +7913,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
return;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
if (!ss->pmap) {
return;
@ -7893,6 +7965,166 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
ss->preview_vert_index_count = totpoints;
}
static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
ID *data;
data = ob->data;
if (data && ID_IS_LINKED(data)) {
return OPERATOR_CANCELLED;
}
if (ob->type != OB_MESH) {
return OPERATOR_CANCELLED;
}
Mesh *mesh = ob->data;
const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
if (mloopcol_layer_n == -1) {
return OPERATOR_CANCELLED;
}
MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
if (MPropCol_layer_n == -1) {
return OPERATOR_CANCELLED;
}
MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
for (int i = 0; i < mesh->totpoly; i++) {
MPoly *c_poly = &polys[i];
for (int j = 0; j < c_poly->totloop; j++) {
int loop_index = c_poly->loopstart + j;
MLoop *c_loop = &loops[c_poly->loopstart + j];
loopcols[loop_index].r = (char)(vertcols[c_loop->v].color[0] * 255);
loopcols[loop_index].g = (char)(vertcols[c_loop->v].color[1] * 255);
loopcols[loop_index].b = (char)(vertcols[c_loop->v].color[2] * 255);
loopcols[loop_index].a = (char)(vertcols[c_loop->v].color[3] * 255);
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
return OPERATOR_FINISHED;
}
static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Sculpt Vertex Color to Vertex Color";
ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
ot->idname = "SCULPT_OT_vertex_to_loop_colors";
/* api callbacks */
ot->poll = SCULPT_mode_poll;
ot->exec = vertex_to_loop_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
ID *data;
data = ob->data;
if (data && ID_IS_LINKED(data)) {
return OPERATOR_CANCELLED;
}
if (ob->type != OB_MESH) {
return OPERATOR_CANCELLED;
}
Mesh *mesh = ob->data;
const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
if (mloopcol_layer_n == -1) {
return OPERATOR_CANCELLED;
}
MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
if (MPropCol_layer_n == -1) {
return OPERATOR_CANCELLED;
}
MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
for (int i = 0; i < mesh->totpoly; i++) {
MPoly *c_poly = &polys[i];
for (int j = 0; j < c_poly->totloop; j++) {
int loop_index = c_poly->loopstart + j;
MLoop *c_loop = &loops[c_poly->loopstart + j];
vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
return OPERATOR_FINISHED;
}
static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Color to Sculpt Vertex Color";
ot->description = "Copy the active loop color layer to the vertex color";
ot->idname = "SCULPT_OT_loop_to_vertex_colors";
/* api callbacks */
ot->poll = SCULPT_mode_poll;
ot->exec = loop_to_vertex_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int sculpt_sample_color_invoke(bContext *C,
wmOperator *UNUSED(op),
const wmEvent *UNUSED(e))
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
int active_vertex = SCULPT_active_vertex_get(ss);
const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
if (!active_vertex_color) {
return OPERATOR_CANCELLED;
}
brush->rgb[0] = active_vertex_color[0];
brush->rgb[1] = active_vertex_color[1];
brush->rgb[2] = active_vertex_color[2];
brush->alpha = active_vertex_color[3];
return OPERATOR_FINISHED;
}
static void SCULPT_OT_sample_color(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Sample color";
ot->idname = "SCULPT_OT_sample_color";
ot->description = "Sample the vertex color of the active vertex";
/* api callbacks */
ot->invoke = sculpt_sample_color_invoke;
ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER;
}
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@ -7915,4 +8147,8 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_cloth_filter);
WM_operatortype_append(SCULPT_OT_face_sets_edit);
WM_operatortype_append(SCULPT_OT_sample_color);
WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
WM_operatortype_append(SCULPT_OT_color_filter);
}

View File

@ -873,7 +873,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
@ -922,10 +922,10 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
/* Needs mask data to be available as it is used when solving the constraints. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_undo_push_begin("Cloth filter");
SCULPT_filter_cache_init(ob, sd);
SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");

View File

@ -181,7 +181,7 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
/* Update the active vertex. */
float mouse[2] = {mx, my};
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
int active_vertex = SCULPT_active_vertex_get(ss);

View File

@ -269,7 +269,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
float threshold = 0.5f;
@ -630,7 +630,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
@ -787,7 +787,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
const int mode = RNA_enum_get(op->ptr, "mode");
@ -1083,7 +1083,7 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
return OPERATOR_CANCELLED;
}
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;

View File

@ -0,0 +1,304 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_task.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "UI_interface.h"
#include "bmesh.h"
#include <math.h>
#include <stdlib.h>
typedef enum eSculptColorFilterTypes {
COLOR_FILTER_HUE,
COLOR_FILTER_SATURATION,
COLOR_FILTER_VALUE,
COLOR_FILTER_BRIGHTNESS,
COLOR_FILTER_CONTRAST,
COLOR_FILTER_RED,
COLOR_FILTER_GREEN,
COLOR_FILTER_BLUE,
COLOR_FILTER_SMOOTH,
} eSculptColorFilterTypes;
EnumPropertyItem prop_color_filter_types[] = {
{COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"},
{COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"},
{COLOR_FILTER_VALUE, "VALUE", 0, "Value", "Change value"},
{COLOR_FILTER_BRIGHTNESS, "BRIGTHNESS", 0, "Brightness", "Change brightness"},
{COLOR_FILTER_CONTRAST, "CONTRAST", 0, "Contrast", "Change contrast"},
{COLOR_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth colors"},
{COLOR_FILTER_RED, "RED", 0, "Red", "Change red channel"},
{COLOR_FILTER_GREEN, "GREEN", 0, "Green", "Change green channel"},
{COLOR_FILTER_BLUE, "BLUE", 0, "Blue", "Change blue channel"},
{0, NULL, 0, NULL, NULL},
};
static void color_filter_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const int mode = data->filter_type;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
SCULPT_orig_vert_data_update(&orig_data, &vd);
float orig_color[3], final_color[4], hsv_color[3];
int hue;
float brightness, contrast, gain, delta, offset;
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
copy_v3_v3(orig_color, orig_data.col);
switch (mode) {
case COLOR_FILTER_HUE:
rgb_to_hsv_v(orig_color, hsv_color);
hue = hsv_color[0] + fade;
hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
hsv_to_rgb_v(hsv_color, final_color);
break;
case COLOR_FILTER_SATURATION:
rgb_to_hsv_v(orig_color, hsv_color);
hsv_color[1] = hsv_color[1] + fade;
CLAMP(hsv_color[1], 0.0f, 1.0f);
hsv_to_rgb_v(hsv_color, final_color);
break;
case COLOR_FILTER_VALUE:
rgb_to_hsv_v(orig_color, hsv_color);
hsv_color[2] = hsv_color[2] + fade;
CLAMP(hsv_color[2], 0.0f, 1.0f);
hsv_to_rgb_v(hsv_color, final_color);
break;
case COLOR_FILTER_RED:
orig_color[0] = orig_color[0] + fade;
CLAMP(orig_color[0], 0.0f, 1.0f);
copy_v3_v3(final_color, orig_color);
break;
case COLOR_FILTER_GREEN:
orig_color[1] = orig_color[1] + fade;
CLAMP(orig_color[1], 0.0f, 1.0f);
copy_v3_v3(final_color, orig_color);
break;
case COLOR_FILTER_BLUE:
orig_color[2] = orig_color[2] + fade;
CLAMP(orig_color[2], 0.0f, 1.0f);
copy_v3_v3(final_color, orig_color);
break;
case COLOR_FILTER_BRIGHTNESS:
CLAMP(fade, -1.0f, 1.0f);
brightness = fade;
contrast = 0;
delta = contrast / 2.0f;
gain = 1.0f - delta * 2.0f;
delta *= -1;
offset = gain * (brightness + delta);
for (int i = 0; i < 3; i++) {
final_color[i] = gain * orig_color[i] + offset;
CLAMP(final_color[i], 0.0f, 1.0f);
}
break;
case COLOR_FILTER_CONTRAST:
CLAMP(fade, -1.0f, 1.0f);
brightness = 0;
contrast = fade;
delta = contrast / 2.0f;
gain = 1.0f - delta * 2.0f;
if (contrast > 0) {
gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
offset = gain * (brightness - delta);
}
else {
delta *= -1;
offset = gain * (brightness + delta);
}
for (int i = 0; i < 3; i++) {
final_color[i] = gain * orig_color[i] + offset;
CLAMP(final_color[i], 0.0f, 1.0f);
}
break;
case COLOR_FILTER_SMOOTH: {
CLAMP(fade, -1.0f, 1.0f);
float smooth_color[4];
SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
break;
}
}
copy_v3_v3(vd.col, final_color);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(data->nodes[n]);
}
static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *ar = CTX_wm_region(C);
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
const int mode = RNA_enum_get(op->ptr, "type");
float filter_strength = RNA_float_get(op->ptr, "strength");
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
SCULPT_undo_push_end();
SCULPT_filter_cache_free(ss);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR);
return OPERATOR_FINISHED;
}
if (event->type != MOUSEMOVE) {
return OPERATOR_RUNNING_MODAL;
}
float len = event->prevclickx - event->mval[0];
filter_strength = filter_strength * -len * 0.001f;
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
.filter_type = mode,
.filter_strength = filter_strength,
};
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
BKE_pbvh_parallel_range_settings(
&settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, color_filter_task_cb, &settings);
ED_region_tag_redraw(ar);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
return OPERATOR_RUNNING_MODAL;
}
static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
int mode = RNA_enum_get(op->ptr, "type");
PBVH *pbvh = ob->sculpt->pbvh;
/* Disable for multires and dyntopo for now */
if (!ss->pbvh) {
return OPERATOR_CANCELLED;
}
if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
return OPERATOR_CANCELLED;
}
if (!ss->vcol) {
return OPERATOR_CANCELLED;
}
SCULPT_undo_push_begin("color filter");
bool needs_pmap = mode == COLOR_FILTER_SMOOTH;
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, true);
if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
return OPERATOR_CANCELLED;
}
SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COLOR);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
void SCULPT_OT_color_filter(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Filter color";
ot->idname = "SCULPT_OT_color_filter";
ot->description = "Applies a filter to modify the current sculpt vertex colors";
/* api callbacks */
ot->invoke = sculpt_color_filter_invoke;
ot->modal = sculpt_color_filter_modal;
ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* rna */
RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
RNA_def_float(
ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
}

View File

@ -202,7 +202,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
int totnode;
int filter_type = RNA_enum_get(op->ptr, "filter_type");
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_init(ss);
@ -432,7 +432,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int totnode;
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SCULPT_vertex_random_access_init(ss);

View File

@ -70,10 +70,10 @@ static void filter_cache_init_task_cb(void *__restrict userdata,
SculptThreadedTaskData *data = userdata;
PBVHNode *node = data->nodes[i];
SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
SCULPT_undo_push_node(data->ob, node, data->filter_undo_type);
}
void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ob->sculpt->pbvh;
@ -110,6 +110,7 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
.filter_undo_type = undo_type,
};
TaskParallelSettings settings;
@ -475,7 +476,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
SCULPT_vertex_random_access_init(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
SculptThreadedTaskData data = {
.sd = sd,
@ -542,7 +543,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_vertex_random_access_init(ss);
bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
const int totvert = SCULPT_vertex_count_get(ss);
if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
@ -551,7 +552,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_undo_push_begin("Mesh filter");
SCULPT_filter_cache_init(ob, sd);
SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
if (use_face_sets) {
ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss);

View File

@ -27,6 +27,7 @@
#include "DNA_brush_types.h"
#include "DNA_key_types.h"
#include "DNA_listBase.h"
#include "DNA_meshdata_types.h"
#include "DNA_vec_types.h"
#include "BLI_bitmap.h"
@ -56,6 +57,7 @@ typedef enum SculptUpdateType {
SCULPT_UPDATE_COORDS = 1 << 0,
SCULPT_UPDATE_MASK = 1 << 1,
SCULPT_UPDATE_VISIBILITY = 1 << 2,
SCULPT_UPDATE_COLOR = 1 << 3,
} SculptUpdateType;
void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags);
@ -92,6 +94,7 @@ int SCULPT_vertex_count_get(struct SculptSession *ss);
const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
@ -179,15 +182,20 @@ typedef struct {
float (*coords)[3];
short (*normals)[3];
const float *vmasks;
float (*colors)[4];
/* Original coordinate, normal, and mask. */
const float *co;
const short *no;
float mask;
const float *col;
} SculptOrigVertData;
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
Object *ob,
struct SculptUndoNode *unode);
/* Utils. */
void SCULPT_calc_brush_plane(struct Sculpt *sd,
@ -305,7 +313,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
float *automask_factor);
/* Filters. */
void SCULPT_filter_cache_init(Object *ob, Sculpt *sd);
void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type);
void SCULPT_filter_cache_free(SculptSession *ss);
void SCULPT_mask_filter_smooth_apply(
@ -374,6 +382,12 @@ void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
/* Draw Face Sets Brush. */
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Paint Brush. */
void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Smear Brush. */
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Smooth Brush. */
void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert);
@ -383,6 +397,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct
void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
void SCULPT_smooth(Sculpt *sd,
Object *ob,
@ -427,6 +442,7 @@ typedef enum {
SCULPT_UNDO_DYNTOPO_SYMMETRIZE,
SCULPT_UNDO_GEOMETRY,
SCULPT_UNDO_FACE_SETS,
SCULPT_UNDO_COLOR,
} SculptUndoType;
/* Storage of geometry for the undo node.
@ -457,6 +473,7 @@ typedef struct SculptUndoNode {
float (*co)[3];
float (*orig_co)[3];
short (*no)[3];
float (*col)[4];
float *mask;
int totvert;
@ -568,6 +585,8 @@ typedef struct SculptThreadedTaskData {
bool any_vertex_sampled;
float *wet_mix_sampled_color;
float *prev_mask;
float *pose_factor;
@ -601,6 +620,7 @@ typedef struct SculptThreadedTaskData {
bool dirty_mask_dirty_only;
int face_set;
int filter_undo_type;
ThreadMutex mutex;
@ -720,6 +740,8 @@ typedef struct StrokeCache {
float bstrength;
float normal_weight; /* from brush (with optional override) */
float (*prev_colors)[4];
/* The rest is temporary storage that isn't saved as a property */
bool first_time; /* Beginning of stroke may do some things special */
@ -813,6 +835,9 @@ typedef struct StrokeCache {
float stroke_local_mat[4][4];
float multiplane_scrape_angle;
float wet_mix_prev_color[4];
float density_seed;
rcti previous_r; /* previous redraw rectangle */
rcti current_r; /* current redraw rectangle */
@ -901,6 +926,9 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot);
/* Cloth Filter. */
void SCULPT_OT_cloth_filter(struct wmOperatorType *ot);
/* Color Filter. */
void SCULPT_OT_color_filter(struct wmOperatorType *ot);
/* Mask filter and Dirty Mask. */
void SCULPT_OT_mask_filter(struct wmOperatorType *ot);
void SCULPT_OT_dirty_mask(struct wmOperatorType *ot);

View File

@ -206,7 +206,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
/* Smooth iterations. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
SCULPT_mask_filter_smooth_apply(
sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations);
@ -365,7 +365,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
int vertex_count = SCULPT_vertex_count_get(ss);

View File

@ -0,0 +1,471 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
#include "BLI_task.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "UI_interface.h"
#include "IMB_imbuf.h"
#include "bmesh.h"
#include <math.h>
#include <stdlib.h>
static void do_color_smooth_task_cb_exec(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
float smooth_color[4];
SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
BKE_pbvh_vertex_iter_end;
}
static void do_paint_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = fabsf(ss->cache->bstrength);
PBVHVertexIter vd;
PBVHColorBufferNode *color_buffer;
SculptOrigVertData orig_data;
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
SCULPT_orig_vert_data_update(&orig_data, &vd);
bool affect_vertex = false;
float distance_to_stroke_location = 0.0f;
if (brush->tip_roundness < 1.0f) {
affect_vertex = SCULPT_brush_test_cube(&test, vd.co, data->mat, brush->tip_roundness);
distance_to_stroke_location = ss->cache->radius * test.dist;
}
else {
affect_vertex = sculpt_brush_test_sq_fn(&test, vd.co);
distance_to_stroke_location = sqrtf(test.dist);
}
if (affect_vertex) {
float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
distance_to_stroke_location,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
/* Density. */
float noise = 1.0f;
const float density = brush->density;
if (density < 1.0f) {
const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index);
if (hash_noise > density) {
noise = density * hash_noise;
fade = fade * noise;
}
}
/* Brush paint color, brush test falloff and flow. */
float paint_color[4] = {brush->rgb[0], brush->rgb[1], brush->rgb[2], 1.0f};
float wet_mix_color[4];
float buffer_color[4];
mul_v4_fl(paint_color, fade * brush->flow);
mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * brush->flow);
/* Interpolate with the wet_mix color for wet paint mixing. */
blend_color_interpolate_float(paint_color, paint_color, wet_mix_color, brush->wet_mix);
blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color);
/* Final mix over the original color using brush alpha. */
mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha);
IMB_blend_color_float(vd.col, orig_data.col, buffer_color, brush->blend);
}
CLAMP4(vd.col, 0.0f, 1.0f);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
}
typedef struct SampleWetPaintTLSData {
int tot_samples;
float color[4];
} SampleWetPaintTLSData;
static void do_sample_wet_paint_task_cb(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
SampleWetPaintTLSData *swptd = tls->userdata_chunk;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
add_v4_v4(swptd->color, vd.col);
swptd->tot_samples++;
}
}
BKE_pbvh_vertex_iter_end;
}
static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata),
void *__restrict chunk_join,
void *__restrict chunk)
{
SampleWetPaintTLSData *join = chunk_join;
SampleWetPaintTLSData *swptd = chunk;
join->tot_samples += swptd->tot_samples;
add_v4_v4(join->color, swptd->color);
}
void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
if (!ss->vcol) {
return;
}
if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
ss->cache->density_seed = BLI_hash_int_01(ss->cache->location[0] * 1000);
return;
}
BKE_curvemapping_initialize(brush->curve);
float area_no[3];
float mat[4][4];
float scale[4][4];
float tmat[4][4];
/* If the brush is round the tip does not need to be aligned to the surface, so this saves a
* whole iteration over the affected nodes. */
if (brush->tip_roundness < 1.0f) {
SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no);
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
mat[0][3] = 0;
cross_v3_v3v3(mat[1], area_no, mat[0]);
mat[1][3] = 0;
copy_v3_v3(mat[2], area_no);
mat[2][3] = 0;
copy_v3_v3(mat[3], ss->cache->location);
mat[3][3] = 1;
normalize_m4(mat);
scale_m4_fl(scale, ss->cache->radius);
mul_m4_m4m4(tmat, mat, scale);
mul_v3_fl(tmat[1], brush->tip_scale_x);
invert_m4_m4(mat, tmat);
if (is_zero_m4(mat)) {
return;
}
}
/* Smooth colors mode. */
if (ss->cache->alt_smooth) {
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
.mat = mat,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
return;
}
/* Regular Paint mode. */
/* Wet paint color sampling. */
float wet_color[4] = {0.0f};
if (brush->wet_mix > 0.0f) {
SculptThreadedTaskData task_data = {
.sd = sd,
.ob = ob,
.nodes = nodes,
.brush = brush,
};
SampleWetPaintTLSData swptd;
swptd.tot_samples = 0;
zero_v4(swptd.color);
TaskParallelSettings settings_sample;
BKE_pbvh_parallel_range_settings(&settings_sample, (sd->flags & SCULPT_USE_OPENMP), totnode);
settings_sample.func_reduce = sample_wet_paint_reduce;
settings_sample.userdata_chunk = &swptd;
settings_sample.userdata_chunk_size = sizeof(SampleWetPaintTLSData);
BLI_task_parallel_range(0, totnode, &task_data, do_sample_wet_paint_task_cb, &settings_sample);
if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) {
copy_v4_v4(wet_color, swptd.color);
mul_v4_fl(wet_color, 1.0f / (float)swptd.tot_samples);
CLAMP4(wet_color, 0.0f, 1.0f);
if (ss->cache->first_time) {
copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
}
blend_color_interpolate_float(
wet_color, wet_color, ss->cache->wet_mix_prev_color, brush->wet_persistence);
copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
CLAMP4(ss->cache->wet_mix_prev_color, 0.0f, 1.0f);
}
}
/* Threaded loop over nodes. */
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
.wet_mix_sampled_color = wet_color,
.mat = mat,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings);
}
static void do_smear_brush_task_cb_exec(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength;
PBVHVertexIter vd;
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
vd.index,
thread_id);
float current_disp[3];
float current_disp_norm[3];
float interp_color[4];
copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]);
sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
normalize_v3_v3(current_disp_norm, current_disp);
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
const float *neighbor_color = ss->cache->prev_colors[ni.index];
normalize_v3_v3(vertex_disp_norm, vertex_disp);
if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) {
const float color_interp = clamp_f(
-dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
float color_mix[4];
copy_v4_v4(color_mix, neighbor_color);
mul_v4_fl(color_mix, color_interp * fade);
blend_color_mix_float(interp_color, interp_color, color_mix);
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
}
BKE_pbvh_vertex_iter_end;
}
static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index));
}
BKE_pbvh_vertex_iter_end;
}
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
if (!ss->vcol) {
return;
}
const int totvert = SCULPT_vertex_count_get(ss);
if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
if (!ss->cache->prev_colors) {
ss->cache->prev_colors = MEM_callocN(sizeof(float) * 4 * totvert, "prev colors");
for (int i = 0; i < totvert; i++) {
copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i));
}
}
}
BKE_curvemapping_initialize(brush->curve);
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
/* Smooth colors mode. */
if (ss->cache->alt_smooth) {
BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
}
else {
/* Smear mode. */
BLI_task_parallel_range(0, totnode, &data, do_smear_store_prev_colors_task_cb_exec, &settings);
BLI_task_parallel_range(0, totnode, &data, do_smear_brush_task_cb_exec, &settings);
}
}

View File

@ -226,6 +226,26 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
}
}
void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
{
float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
mul_v4_v4fl(result, avg, 1.0f / (float)total);
}
else {
copy_v4_v4(result, SCULPT_vertex_color_get(ss, index));
}
}
static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)

View File

@ -71,12 +71,12 @@ void ED_sculpt_init_transform(struct bContext *C)
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
SCULPT_undo_push_begin("Transform");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
ss->pivot_rot[3] = 1.0f;
SCULPT_vertex_random_access_init(ss);
SCULPT_filter_cache_init(ob, sd);
SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
}
static void sculpt_transform_task_cb(void *__restrict userdata,
@ -127,7 +127,7 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
SCULPT_vertex_random_access_init(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
SculptThreadedTaskData data = {
.sd = sd,
@ -253,7 +253,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
int mode = RNA_enum_get(op->ptr, "mode");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
/* Pivot to center. */
if (mode == SCULPT_PIVOT_POSITION_ORIGIN) {

View File

@ -205,7 +205,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (kb) {
ob->shapenr = BLI_findindex(&key->block, kb) + 1;
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
WM_event_add_notifier(C, NC_OBJECT | ND_DATA, ob);
}
else {
@ -326,6 +326,29 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
return true;
}
static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = OBACT(view_layer);
SculptSession *ss = ob->sculpt;
MVert *mvert;
MPropCol *vcol;
int *index, i;
if (unode->maxvert) {
/* regular mesh restore */
index = unode->index;
mvert = ss->mvert;
vcol = ss->vcol;
for (i = 0; i < unode->totvert; i++) {
copy_v4_v4(vcol[index[i]].color, unode->col[i]);
mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
}
}
return true;
}
static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -633,7 +656,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
rebuild = true;
BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask);
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false);
SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
@ -659,7 +682,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
* ensure object is updated after the node is handled. */
const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first;
if (first_unode->type != SCULPT_UNDO_GEOMETRY) {
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
}
if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) {
@ -712,10 +735,15 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
break;
case SCULPT_UNDO_FACE_SETS:
break;
case SCULPT_UNDO_COLOR:
if (sculpt_undo_restore_color(C, unode)) {
update = true;
}
break;
case SCULPT_UNDO_GEOMETRY:
sculpt_undo_geometry_restore(unode, ob);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
@ -997,6 +1025,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
break;
case SCULPT_UNDO_COLOR:
unode->col = MEM_callocN(sizeof(MPropCol) * allvert, "SculptUndoNode.col");
usculpt->undo_size += (sizeof(MPropCol) * sizeof(int)) * allvert;
break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
@ -1083,6 +1117,18 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode)
BKE_pbvh_vertex_iter_end;
}
static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL)
{
copy_v4_v4(unode->col[vd.i], vd.col);
}
BKE_pbvh_vertex_iter_end;
}
static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode)
{
if (!unode->geometry_original.is_initialized) {
@ -1203,6 +1249,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
case SCULPT_UNDO_GEOMETRY:
case SCULPT_UNDO_FACE_SETS:
case SCULPT_UNDO_COLOR:
break;
}
}
@ -1272,6 +1319,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
case SCULPT_UNDO_MASK:
sculpt_undo_store_mask(ob, unode);
break;
case SCULPT_UNDO_COLOR:
sculpt_undo_store_color(ob, unode);
break;
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:

View File

@ -1014,7 +1014,8 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) {
PointerRNA dataptr = RNA_pointer_get(&obptr, "data");
uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL);
uiItemPointerR(
layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL);
}
else {
uiItemL(layout, "No mesh in active object.", ICON_ERROR);

View File

@ -2376,7 +2376,7 @@ void ED_view3d_datamask(const bContext *C,
{
if (ELEM(v3d->shading.type, OB_TEXTURE, OB_MATERIAL, OB_RENDER)) {
r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
r_cddata_masks->vmask |= CD_MASK_ORCO;
r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
else if (v3d->shading.type == OB_SOLID) {
if (v3d->shading.color_type == V3D_SHADING_TEXTURE_COLOR) {
@ -2384,6 +2384,7 @@ void ED_view3d_datamask(const bContext *C,
}
if (v3d->shading.color_type == V3D_SHADING_VERTEX_COLOR) {
r_cddata_masks->lmask |= CD_MASK_MLOOPCOL;
r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
}

View File

@ -37,6 +37,7 @@ struct DMFlagMat;
struct GSet;
struct MLoop;
struct MLoopCol;
struct MPropCol;
struct MLoopTri;
struct MPoly;
struct MVert;
@ -82,6 +83,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const int *sculpt_face_sets,
const int face_sets_color_seed,
const int face_sets_color_default,
const struct MPropCol *vtcol,
const int update_flags);
void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,

View File

@ -34,6 +34,7 @@
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_math_color.h"
#include "BLI_math_color_blend.h"
#include "BLI_utildefines.h"
#include "DNA_meshdata_types.h"
@ -227,12 +228,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const int *sculpt_face_sets,
const int face_sets_color_seed,
const int face_sets_color_default,
const MPropCol *vtcol,
const int update_flags)
{
const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
const bool show_vcol = vcol && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
const bool show_face_sets = sculpt_face_sets &&
(update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0;
const bool show_vcol = (vcol || vtcol) && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
bool empty_mask = true;
bool default_face_set = true;
@ -244,8 +246,8 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
GPUVertBufRaw pos_step = {0};
GPUVertBufRaw nor_step = {0};
GPUVertBufRaw msk_step = {0};
GPUVertBufRaw col_step = {0};
GPUVertBufRaw fset_step = {0};
GPUVertBufRaw col_step = {0};
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.pos, &pos_step);
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.nor, &nor_step);
@ -312,25 +314,34 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
*(uchar *)GPU_vertbuf_raw_step(&msk_step) = cmask;
empty_mask = empty_mask && (cmask == 0);
/* Vertex Colors. */
if (show_vcol) {
const uint loop_index = lt->tri[j];
const MLoopCol *mcol = &vcol[loop_index];
ushort scol[4];
scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f));
memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
ushort scol[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (vtcol) {
scol[0] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[0]);
scol[1] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[1]);
scol[2] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[2]);
scol[3] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[3]);
memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
}
else {
const uint loop_index = lt->tri[j];
const MLoopCol *mcol = &vcol[loop_index];
scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f));
memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
}
}
/* Face Sets. */
memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar) * 3);
}
}
gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
/* Face Sets. */
memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar) * 3);
}
}
gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
}
/* Get material index from the first face of this buffer. */

View File

@ -439,6 +439,21 @@ typedef struct Brush {
float rgb[3];
/** Opacity. */
float alpha;
/** Hardness */
float hardness;
/** Flow */
float flow;
/** Wet Mix */
float wet_mix;
float wet_persistence;
/** Density */
float density;
/** Tip Shape */
/* Factor that controls the shape of the brush tip by rounding the corners of a square. */
/* 0.0 value produces a square, 1.0 produces a circle. */
float tip_roundness;
float tip_scale_x;
/** Background color. */
float secondary_rgb[3];
@ -459,7 +474,7 @@ typedef struct Brush {
/** Source for fill tool color gradient application. */
char gradient_fill_mode;
char _pad0[1];
char _pad0[5];
/** Projection shape (sphere, circle). */
char falloff_shape;
@ -503,16 +518,11 @@ typedef struct Brush {
float texture_sample_bias;
int curve_preset;
float hardness;
/* automasking */
int automasking_flags;
int automasking_boundary_edges_propagation_steps;
/* Factor that controls the shape of the brush tip by rounding the corners of a square. */
/* 0.0 value produces a square, 1.0 produces a circle. */
float tip_roundness;
int elastic_deform_type;
float elastic_deform_volume_preservation;
@ -720,6 +730,8 @@ typedef enum eBrushSculptTool {
SCULPT_TOOL_CLAY_THUMB = 25,
SCULPT_TOOL_CLOTH = 26,
SCULPT_TOOL_DRAW_FACE_SETS = 27,
SCULPT_TOOL_PAINT = 28,
SCULPT_TOOL_SMEAR = 29,
} eBrushSculptTool;
/* Brush.uv_sculpt_tool */
@ -762,6 +774,8 @@ typedef enum eBrushUVSculptTool {
SCULPT_TOOL_ELASTIC_DEFORM, \
SCULPT_TOOL_POSE, \
SCULPT_TOOL_DRAW_FACE_SETS, \
SCULPT_TOOL_PAINT, \
SCULPT_TOOL_SMEAR, \
\
/* These brushes could handle dynamic topology, \ \
* but user feedback indicates it's better not to */ \

View File

@ -148,10 +148,9 @@ typedef enum CustomDataType {
CD_CUSTOMLOOPNORMAL = 41,
CD_SCULPT_FACE_SETS = 42,
/* Hair and PointCloud */
CD_LOCATION = 43,
CD_RADIUS = 44,
CD_HAIRCURVE = 45,
CD_RADIUS = 44,
CD_HAIRMAPPING = 46,
CD_PROP_COLOR = 47,
@ -205,7 +204,7 @@ typedef enum CustomDataType {
#define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL)
#define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL)
#define CD_MASK_SCULPT_FACE_SETS (1LL << CD_SCULPT_FACE_SETS)
#define CD_MASK_PROP_COLOR (1LL << CD_PROP_COLOR)
#define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR)
/** Data types that may be defined for all mesh elements types. */
#define CD_MASK_GENERIC_DATA (CD_MASK_PROP_FLOAT | CD_MASK_PROP_INT32 | CD_MASK_PROP_STRING)

View File

@ -46,6 +46,7 @@ struct MLoopTri;
struct MLoopUV;
struct MPoly;
struct MVert;
struct MPropCol;
struct Material;
struct Mesh;
struct Multires;
@ -285,7 +286,7 @@ enum {
ME_AUTOSMOOTH = 1 << 5,
ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */
ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */
ME_FLAG_UNUSED_8 = 1 << 8, /* cleared */
ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8,
ME_DS_EXPAND = 1 << 9,
ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10,
ME_REMESH_SMOOTH_NORMALS = 1 << 11,

View File

@ -345,7 +345,7 @@ typedef struct MLoopCol {
} MLoopCol;
typedef struct MPropCol {
float col[4];
float color[4];
} MPropCol;
/** Multi-Resolution loop data. */

View File

@ -387,6 +387,8 @@ extern StructRNA RNA_MeshIntProperty;
extern StructRNA RNA_MeshLoop;
extern StructRNA RNA_MeshLoopColor;
extern StructRNA RNA_MeshLoopColorLayer;
extern StructRNA RNA_MeshVertColor;
extern StructRNA RNA_MeshVertColorLayer;
extern StructRNA RNA_MeshLoopTriangle;
extern StructRNA RNA_MeshPolygon;
extern StructRNA RNA_MeshSequenceCacheModifier;

View File

@ -99,6 +99,8 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{SCULPT_TOOL_CLOTH, "CLOTH", ICON_BRUSH_SCULPT_DRAW, "Cloth", ""},
{SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""},
{SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""},
{SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""},
{SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""},
{SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""},
{0, NULL, 0, NULL, NULL},
};
@ -314,7 +316,8 @@ static bool rna_BrushCapabilitiesSculpt_has_topology_rake_get(PointerRNA *ptr)
static bool rna_BrushCapabilitiesSculpt_has_auto_smooth_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
return !ELEM(br->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH);
return !ELEM(
br->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
}
static bool rna_BrushCapabilitiesSculpt_has_height_get(PointerRNA *ptr)
@ -409,6 +412,12 @@ static bool rna_BrushCapabilitiesSculpt_has_sculpt_plane_get(PointerRNA *ptr)
SCULPT_TOOL_SMOOTH);
}
static bool rna_BrushCapabilitiesSculpt_has_color_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
return ELEM(br->sculpt_tool, SCULPT_TOOL_PAINT);
}
static bool rna_BrushCapabilitiesSculpt_has_secondary_color_get(PointerRNA *ptr)
{
Brush *br = (Brush *)ptr->data;
@ -1053,6 +1062,7 @@ static void rna_def_sculpt_capabilities(BlenderRNA *brna)
SCULPT_TOOL_CAPABILITY(has_plane_offset, "Has Plane Offset");
SCULPT_TOOL_CAPABILITY(has_random_texture_angle, "Has Random Texture Angle");
SCULPT_TOOL_CAPABILITY(has_sculpt_plane, "Has Sculpt Plane");
SCULPT_TOOL_CAPABILITY(has_color, "Has Color");
SCULPT_TOOL_CAPABILITY(has_secondary_color, "Has Secondary Color");
SCULPT_TOOL_CAPABILITY(has_smooth_stroke, "Has Smooth Stroke");
SCULPT_TOOL_CAPABILITY(has_space_attenuation, "Has Space Attenuation");
@ -2221,6 +2231,46 @@ static void rna_def_brush(BlenderRNA *brna)
prop, "Strength", "How powerful the effect of the brush is when applied");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "flow", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "flow");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
RNA_def_property_ui_text(prop, "Flow", "Amount of paint that is applied per stroke sample");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "wet_mix", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "wet_mix");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
RNA_def_property_ui_text(
prop, "Wet Mix", "Amount of paint that is picked from the surface into the brush color");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "wet_persistence", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "wet_persistence");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
RNA_def_property_ui_text(
prop,
"Wet Persistence",
"Amount of wet paint that stays in the brush after applyig paint to the surface");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "density");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
RNA_def_property_ui_text(
prop, "Density", "Amount of random elements that are going to be affected by the brush");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "tip_scale_x", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "tip_scale_x");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
RNA_def_property_ui_text(prop, "Tip Scale X", "Scale of the brush tip in the X axis");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "dash_ratio", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "dash_ratio");
RNA_def_property_range(prop, 0.0f, 1.0f);

View File

@ -758,6 +758,47 @@ static void rna_MeshLoopColorLayer_active_set(PointerRNA *ptr, bool value)
rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_MLOOPCOL, 0);
}
/* sculpt_vertex_color_layers */
DEFINE_CUSTOMDATA_LAYER_COLLECTION(sculpt_vertex_color, vdata, CD_PROP_COLOR)
DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(
sculpt_vertex_color, vdata, CD_PROP_COLOR, active, MeshVertColorLayer)
static void rna_MeshVertColorLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
rna_iterator_array_begin(
iter, layer->data, sizeof(MPropCol), (me->edit_mesh) ? 0 : me->totvert, 0, NULL);
}
static int rna_MeshVertColorLayer_data_length(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
return (me->edit_mesh) ? 0 : me->totvert;
}
static bool rna_MeshVertColorLayer_active_render_get(PointerRNA *ptr)
{
return rna_CustomDataLayer_active_get(ptr, rna_mesh_vdata(ptr), CD_PROP_COLOR, 1);
}
static bool rna_MeshVertColorLayer_active_get(PointerRNA *ptr)
{
return rna_CustomDataLayer_active_get(ptr, rna_mesh_vdata(ptr), CD_PROP_COLOR, 0);
}
static void rna_MeshVertColorLayer_active_render_set(PointerRNA *ptr, bool value)
{
rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 1);
}
static void rna_MeshVertColorLayer_active_set(PointerRNA *ptr, bool value)
{
rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 0);
}
static int rna_float_layer_check(CollectionPropertyIterator *UNUSED(iter), void *data)
{
CustomDataLayer *layer = (CustomDataLayer *)data;
@ -1218,6 +1259,19 @@ static char *rna_MeshColor_path(PointerRNA *ptr)
return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_MLOOPCOL);
}
static char *rna_MeshVertColorLayer_path(PointerRNA *ptr)
{
CustomDataLayer *cdl = ptr->data;
char name_esc[sizeof(cdl->name) * 2];
BLI_strescape(name_esc, cdl->name, sizeof(name_esc));
return BLI_sprintfN("sculpt_vertex_colors[\"%s\"]", name_esc);
}
static char *rna_MeshVertColor_path(PointerRNA *ptr)
{
return rna_VertCustomData_data_path(ptr, "sculpt_vertex_colors", CD_PROP_COLOR);
}
/**** Float Property Layer API ****/
static char *rna_MeshVertexFloatPropertyLayer_path(PointerRNA *ptr)
{
@ -1439,6 +1493,33 @@ static void rna_Mesh_vertex_color_remove(struct Mesh *me,
}
}
static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me,
const char *name,
const bool do_init)
{
PointerRNA ptr;
CustomData *vdata;
CustomDataLayer *cdl = NULL;
int index = ED_mesh_sculpt_color_add(me, name, false, do_init);
if (index != -1) {
vdata = rna_mesh_vdata_helper(me);
cdl = &vdata->layers[CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, index)];
}
RNA_pointer_create(&me->id, &RNA_MeshVertColorLayer, cdl, &ptr);
return ptr;
}
static void rna_Mesh_sculpt_vertex_color_remove(struct Mesh *me,
ReportList *reports,
CustomDataLayer *layer)
{
if (ED_mesh_sculpt_color_remove_named(me, layer->name) == false) {
BKE_reportf(reports, RPT_ERROR, "Sculpt vertex color '%s' not found", layer->name);
}
}
# define DEFINE_CUSTOMDATA_PROPERTY_API( \
elemname, datatype, cd_prop_type, cdata, countvar, layertype) \
static PointerRNA rna_Mesh_##elemname##_##datatype##_property_new(struct Mesh *me, \
@ -1510,10 +1591,6 @@ static void UNUSED_FUNCTION(rna_mesh_unused)(void)
(void)rna_Mesh_uv_layer_render_index_get;
(void)rna_Mesh_uv_layer_render_index_set;
(void)rna_Mesh_uv_layer_render_set;
(void)rna_Mesh_vertex_color_render_get;
(void)rna_Mesh_vertex_color_render_index_get;
(void)rna_Mesh_vertex_color_render_index_set;
(void)rna_Mesh_vertex_color_render_set;
(void)rna_Mesh_face_map_index_range;
(void)rna_Mesh_face_map_active_index_set;
(void)rna_Mesh_face_map_active_index_get;
@ -2034,6 +2111,65 @@ static void rna_def_mloopcol(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
}
static void rna_def_MPropCol(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "MeshVertColorLayer", NULL);
RNA_def_struct_ui_text(srna,
"Mesh Sculpt Vertex Color Layer",
"Layer of sculpt vertex colors in a Mesh data-block");
RNA_def_struct_sdna(srna, "CustomDataLayer");
RNA_def_struct_path_func(srna, "rna_MeshVertColorLayer_path");
RNA_def_struct_ui_icon(srna, ICON_GROUP_VCOL);
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_struct_name_property(srna, prop);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshVertexLayer_name_set");
RNA_def_property_ui_text(prop, "Name", "Name of Sculpt Vertex color layer");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(
prop, "rna_MeshVertColorLayer_active_get", "rna_MeshVertColorLayer_active_set");
RNA_def_property_ui_text(
prop, "Active", "Sets the sculpt vertex color layer as active for display and editing");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
prop = RNA_def_property(srna, "active_render", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "active_rnd", 0);
RNA_def_property_boolean_funcs(prop,
"rna_MeshVertColorLayer_active_render_get",
"rna_MeshVertColorLayer_active_render_set");
RNA_def_property_ui_text(
prop, "Active Render", "Sets the sculpt vertex color layer as active for rendering");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "MeshVertColor");
RNA_def_property_ui_text(prop, "Data", "");
RNA_def_property_collection_funcs(prop,
"rna_MeshVertColorLayer_data_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_MeshVertColorLayer_data_length",
NULL,
NULL,
NULL);
srna = RNA_def_struct(brna, "MeshVertColor", NULL);
RNA_def_struct_sdna(srna, "MPropCol");
RNA_def_struct_ui_text(srna, "Mesh Sculpt Vertex Color", "Vertex colors in a Mesh");
RNA_def_struct_path_func(srna, "rna_MeshVertColor_path");
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_array(prop, 4);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Color", "");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
}
static void rna_def_mproperties(BlenderRNA *brna)
{
StructRNA *srna;
@ -2373,6 +2509,60 @@ static void rna_def_loop_colors(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
}
static void rna_def_vert_colors(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
PropertyRNA *prop;
FunctionRNA *func;
PropertyRNA *parm;
RNA_def_property_srna(cprop, "VertColors");
srna = RNA_def_struct(brna, "VertColors", NULL);
RNA_def_struct_sdna(srna, "Mesh");
RNA_def_struct_ui_text(srna, "Vert Colors", "Collection of sculpt vertex colors");
func = RNA_def_function(srna, "new", "rna_Mesh_sculpt_vertex_color_new");
RNA_def_function_ui_description(func, "Add a sculpt vertex color layer to Mesh");
RNA_def_string(func, "name", "Col", 0, "", "Sculpt Vertex color name");
RNA_def_boolean(func,
"do_init",
true,
"",
"Whether new layer's data should be initialized by copying current active one");
parm = RNA_def_pointer(func, "layer", "MeshVertColorLayer", "", "The newly created layer");
RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_Mesh_sculpt_vertex_color_remove");
RNA_def_function_ui_description(func, "Remove a vertex color layer");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "layer", "MeshVertColorLayer", "", "The layer to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "MeshVertColorLayer");
RNA_def_property_pointer_funcs(prop,
"rna_Mesh_sculpt_vertex_color_active_get",
"rna_Mesh_sculpt_vertex_color_active_set",
NULL,
NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
RNA_def_property_ui_text(
prop, "Active Sculpt Vertex Color Layer", "Active sculpt vertex color layer");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_funcs(prop,
"rna_Mesh_sculpt_vertex_color_active_index_get",
"rna_Mesh_sculpt_vertex_color_active_index_set",
"rna_Mesh_sculpt_vertex_color_index_range");
RNA_def_property_ui_text(
prop, "Active Sculpt Vertex Color Index", "Active sculpt vertex color index");
RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
}
static void rna_def_uv_layers(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@ -2839,6 +3029,23 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Vertex Colors", "All vertex colors");
rna_def_loop_colors(brna, prop);
/* Sculpt Vertex colors */
prop = RNA_def_property(srna, "sculpt_vertex_colors", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
RNA_def_property_collection_funcs(prop,
"rna_Mesh_sculpt_vertex_colors_begin",
NULL,
NULL,
NULL,
"rna_Mesh_sculpt_vertex_colors_length",
NULL,
NULL,
NULL);
RNA_def_property_struct_type(prop, "MeshVertColorLayer");
RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors");
rna_def_vert_colors(brna, prop);
/* TODO, edge customdata layers (bmesh py api can access already) */
prop = RNA_def_property(srna, "vertex_layers_float", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
@ -3033,6 +3240,13 @@ static void rna_def_mesh(BlenderRNA *brna)
prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS);
RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(
prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
prop = RNA_def_property(srna, "remesh_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "remesh_mode");
RNA_def_property_enum_items(prop, rna_enum_mesh_remesh_mode_items);
@ -3183,6 +3397,7 @@ void RNA_def_mesh(BlenderRNA *brna)
rna_def_mpolygon(brna);
rna_def_mloopuv(brna);
rna_def_mloopcol(brna);
rna_def_MPropCol(brna);
rna_def_mproperties(brna);
rna_def_face_map(brna);
}