Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py15
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py28
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py13
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py17
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py11
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py2
-rw-r--r--source/blender/blenkernel/BKE_mesh_remesh_voxel.h1
-rw-r--r--source/blender/blenkernel/BKE_paint.h4
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h15
-rw-r--r--source/blender/blenkernel/intern/brush.c30
-rw-r--r--source/blender/blenkernel/intern/customdata.c50
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.c31
-rw-r--r--source/blender/blenkernel/intern/object_update.c2
-rw-r--r--source/blender/blenkernel/intern/paint.c29
-rw-r--r--source/blender/blenkernel/intern/pbvh.c31
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h3
-rw-r--r--source/blender/blenloader/intern/versioning_280.c12
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c16
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c24
-rw-r--r--source/blender/draw/intern/draw_cache.c6
-rw-r--r--source/blender/draw/intern/draw_cache.h1
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h1
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c62
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c81
-rw-r--r--source/blender/editors/include/ED_mesh.h9
-rw-r--r--source/blender/editors/mesh/mesh_data.c178
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c2
-rw-r--r--source/blender/editors/object/object_remesh.c5
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c282
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c304
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c11
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h30
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c471
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c20
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c58
-rw-r--r--source/blender/editors/space_node/drawnode.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c3
-rw-r--r--source/blender/gpu/GPU_buffers.h2
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c43
-rw-r--r--source/blender/makesdna/DNA_brush_types.h26
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h5
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h3
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c52
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c223
58 files changed, 2123 insertions, 145 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 33c16b99348..dbfc9daca84 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -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),
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 425c94dfdcd..fbd8e2d7cff 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -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,
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index c82f891ecb0..ba0d904a323 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -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")
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 52c59431316..65f399118e6 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -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,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 0daf9761246..d1fe3f649c8 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -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,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 76bac375d5b..a951025166e 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -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")
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
index b63f9a9814b..24f95f7ed20 100644
--- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
+++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
@@ -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
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 6e4f2efeeb8..56503a0bcc6 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -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);
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 8fb6f140822..87875314aec 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -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
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index b66df6b1da6..0a991d3ef4a 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -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:
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 7dd4d1178ef..642a865afab 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -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 |
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c
index 3daf9f2752e..010b306ec76 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c
@@ -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);
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index c5ef5acb08b..d8c3e0bf714 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -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 */
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index f26b478c680..151e0006092 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -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)
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 19f28047b80..8d7dabf9859 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -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);
}
}
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 7397f939894..6f8bae822ea 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -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 {
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 0648264466d..890be9ff3ef 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -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) {
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 91d89254c90..1217b69f1b5 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -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) {
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 828a9127fb1..791b83a22a8 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -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) {
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index f5540362041..9f30cd85957 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -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);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index 5703f616ad1..2f289bf4110 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -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);
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index f203c2ff1ea..0b1f625dc2c 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -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
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index e1f0cc98745..a8ba716f7b0 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -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;
}
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index f670343140a..5cf1c24af0b 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -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);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 7207345f13c..ea1717f0684 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -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) {
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 17e2043a7a8..9c49d991182 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -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);
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index 51b699acd63..8fce726eff5 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -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)
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 5e70069456b..bebad312454 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -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);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index e10c1286dac..ad1e91a57c0 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -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);
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 1d7920b9991..76323037fd7 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -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) {
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index b8754953741..ed87d524627 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -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
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 4222a466a7b..1291de04634 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -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) {
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index c32e496f4f5..07a6b2873f4 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -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);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 6de54b3ae6a..69c0d5bb712 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -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)
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 84fa46e5b6b..cdffe31041a 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -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);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 7776af11a77..318919f0d48 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -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");
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index f071deaa219..463233fd6fb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -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);
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 7bb54366204..57dc70c5276 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -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;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
new file mode 100644
index 00000000000..77fbe42a984
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -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);
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index d2a683461a7..f5d33d44350 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -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);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index fd0f67f040a..468945e6c42 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -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);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 0e27658e848..6d1315db723 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -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);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index cbb198e14a3..8868eae47d8 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -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);
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
new file mode 100644
index 00000000000..1ae40837998
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -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);
+ }
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 17451cb40ae..02c7b65dcb7 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -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)
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 59a4695ce18..914b1d67a6d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -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) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index d21552efafe..c8bf901b2ef 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -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:
@@ -998,6 +1026,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:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
@@ -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:
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index ab50158c8bc..c3ebbeb5889 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -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);
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index fac378ae104..b3165c9fc78 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -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;
}
}
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index ab16bfc43c4..41a29a4d45d 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -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,
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index cef90d57ef5..53ef305f629 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -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. */
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 7490dbe5cdc..a1c69af4750 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -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 */ \
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index e4999fd4464..c24bbccae1e 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -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)
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index acc020ec710..f802dd8c55b 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -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,
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index 04deecde43e..cc2ba3fb999 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -345,7 +345,7 @@ typedef struct MLoopCol {
} MLoopCol;
typedef struct MPropCol {
- float col[4];
+ float color[4];
} MPropCol;
/** Multi-Resolution loop data. */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 0a26be3a753..0b4f82ca263 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -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;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index cc5cd97a8a0..59656b48cfe 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -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);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 5f986c28964..62318925a0b 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -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);
}