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 +#include + +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 +#include + +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: @@ -997,6 +1025,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; + break; + case SCULPT_UNDO_COLOR: + unode->col = MEM_callocN(sizeof(MPropCol) * allvert, "SculptUndoNode.col"); + + usculpt->undo_size += (sizeof(MPropCol) * sizeof(int)) * allvert; + break; case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: @@ -1083,6 +1117,18 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) BKE_pbvh_vertex_iter_end; } +static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) + { + copy_v4_v4(unode->col[vd.i], vd.col); + } + BKE_pbvh_vertex_iter_end; +} + static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode) { if (!unode->geometry_original.is_initialized) { @@ -1203,6 +1249,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: case SCULPT_UNDO_GEOMETRY: case SCULPT_UNDO_FACE_SETS: + case SCULPT_UNDO_COLOR: break; } } @@ -1272,6 +1319,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType case SCULPT_UNDO_MASK: sculpt_undo_store_mask(ob, unode); break; + case SCULPT_UNDO_COLOR: + sculpt_undo_store_color(ob, unode); + break; case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: 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); }