Sculpt Vertex Colors: Initial implementation
authorPablo Dobarro <pablodp606@gmail.com>
Mon, 22 Jun 2020 18:05:28 +0000 (20:05 +0200)
committerPablo Dobarro <pablodp606@gmail.com>
Tue, 23 Jun 2020 14:28:50 +0000 (16:28 +0200)
Sculpt Vertex Colors is a painting system that runs inside sculpt mode, reusing all its tools and optimizations. This provides much better performance, easier to maintain code and more advanced features (new brush engine, filters, symmetry options, masks and face sets compatibility...). This is also the initial step for future features like vertex painting in Multires and brushes that can sculpt and paint at the same time.

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

Reviewed By: brecht

Maniphest Tasks: T72866

Differential Revision: https://developer.blender.org/D5975

58 files changed:
release/scripts/presets/keyconfig/keymap_data/blender_default.py
release/scripts/startup/bl_ui/properties_data_mesh.py
release/scripts/startup/bl_ui/properties_paint_common.py
release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
release/scripts/startup/bl_ui/space_view3d.py
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/blenkernel/BKE_mesh_remesh_voxel.h
source/blender/blenkernel/BKE_paint.h
source/blender/blenkernel/BKE_pbvh.h
source/blender/blenkernel/intern/brush.c
source/blender/blenkernel/intern/customdata.c
source/blender/blenkernel/intern/mesh_remesh_voxel.c
source/blender/blenkernel/intern/object_update.c
source/blender/blenkernel/intern/paint.c
source/blender/blenkernel/intern/pbvh.c
source/blender/blenkernel/intern/pbvh_intern.h
source/blender/blenloader/intern/versioning_280.c
source/blender/blenloader/intern/versioning_defaults.c
source/blender/draw/engines/workbench/workbench_engine.c
source/blender/draw/intern/draw_cache.c
source/blender/draw/intern/draw_cache.h
source/blender/draw/intern/draw_cache_extract.h
source/blender/draw/intern/draw_cache_extract_mesh.c
source/blender/draw/intern/draw_cache_impl.h
source/blender/draw/intern/draw_cache_impl_mesh.c
source/blender/editors/include/ED_mesh.h
source/blender/editors/mesh/mesh_data.c
source/blender/editors/mesh/mesh_intern.h
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/object/object_remesh.c
source/blender/editors/sculpt_paint/CMakeLists.txt
source/blender/editors/sculpt_paint/paint_cursor.c
source/blender/editors/sculpt_paint/paint_mask.c
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/sculpt_paint/sculpt_cloth.c
source/blender/editors/sculpt_paint/sculpt_detail.c
source/blender/editors/sculpt_paint/sculpt_face_set.c
source/blender/editors/sculpt_paint/sculpt_filter_color.c [new file with mode: 0644]
source/blender/editors/sculpt_paint/sculpt_filter_mask.c
source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
source/blender/editors/sculpt_paint/sculpt_intern.h
source/blender/editors/sculpt_paint/sculpt_mask_expand.c
source/blender/editors/sculpt_paint/sculpt_paint_color.c [new file with mode: 0644]
source/blender/editors/sculpt_paint/sculpt_smooth.c
source/blender/editors/sculpt_paint/sculpt_transform.c
source/blender/editors/sculpt_paint/sculpt_undo.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/gpu/GPU_buffers.h
source/blender/gpu/intern/gpu_buffers.c
source/blender/makesdna/DNA_brush_types.h
source/blender/makesdna/DNA_customdata_types.h
source/blender/makesdna/DNA_mesh_types.h
source/blender/makesdna/DNA_meshdata_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_brush.c
source/blender/makesrna/intern/rna_mesh.c

index 33c16b99348ca7d90e955c7b85e3f067f2f51e6d..dbfc9daca84e9cadd128150c030a4ef9ca7b2436 100644 (file)
@@ -4343,6 +4343,8 @@ def km_sculpt(params):
         ("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
         ("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None),
         ("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
+        # Color
+        ("sculpt.sample_color", {"type": 'S', "value": 'PRESS'}, None),
         # Brush properties
         ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
          {"properties": [("scalar", 0.9)]}),
@@ -4365,7 +4367,7 @@ def km_sculpt(params):
         # Tools
         ("paint.brush_select", {"type": 'X', "value": 'PRESS'},
          {"properties": [("sculpt_tool", 'DRAW')]}),
-        ("paint.brush_select", {"type": 'S', "value": 'PRESS'},
+        ("paint.brush_select", {"type": 'S', "value": 'PRESS', "shift": True},
          {"properties": [("sculpt_tool", 'SMOOTH')]}),
         ("paint.brush_select", {"type": 'P', "value": 'PRESS'},
          {"properties": [("sculpt_tool", 'PINCH')]}),
@@ -6280,6 +6282,16 @@ def km_3d_view_tool_sculpt_cloth_filter(params):
         ]},
     )
 
+def km_3d_view_tool_sculpt_color_filter(params):
+    return (
+        "3D View Tool: Sculpt, Color Filter",
+        {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+        {"items": [
+            ("sculpt.color_filter", {"type": params.tool_tweak, "value": 'ANY'},
+             None)
+        ]},
+    )
+
 def km_3d_view_tool_paint_weight_sample_weight(params):
     return (
         "3D View Tool: Paint Weight, Sample Weight",
@@ -6820,6 +6832,7 @@ def generate_keymaps(params=None):
         km_3d_view_tool_sculpt_lasso_mask(params),
         km_3d_view_tool_sculpt_mesh_filter(params),
         km_3d_view_tool_sculpt_cloth_filter(params),
+        km_3d_view_tool_sculpt_color_filter(params),
         km_3d_view_tool_paint_weight_sample_weight(params),
         km_3d_view_tool_paint_weight_sample_vertex_group(params),
         km_3d_view_tool_paint_weight_gradient(params),
index 425c94dfdcda1ca75b764d83b8b9a7c377164603..fbd8e2d7cff82e4d0108310c649feeeee9e62a8b 100644 (file)
@@ -459,6 +459,31 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, Panel):
         col.operator("mesh.vertex_color_remove", icon='REMOVE', text="")
 
 
+class DATA_PT_sculpt_vertex_colors(MeshButtonsPanel, Panel):
+    bl_label = "Sculpt Vertex Colors"
+    bl_options = {'DEFAULT_CLOSED'}
+    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+    def draw(self, context):
+        layout = self.layout
+
+        me = context.mesh
+
+        row = layout.row()
+        col = row.column()
+
+        col.template_list("MESH_UL_vcols", "svcols", me, "sculpt_vertex_colors", me.sculpt_vertex_colors, "active_index", rows=2)
+
+        col = row.column(align=True)
+        col.operator("mesh.sculpt_vertex_color_add", icon='ADD', text="")
+        col.operator("mesh.sculpt_vertex_color_remove", icon='REMOVE', text="")
+
+        row = layout.row()
+        col = row.column()
+        col.operator("sculpt.vertex_to_loop_colors", text="Store Sculpt Vertex Color")
+        col.operator("sculpt.loop_to_vertex_colors", text="Load Sculpt Vertex Color")
+
+
 class DATA_PT_remesh(MeshButtonsPanel, Panel):
     bl_label = "Remesh"
     bl_options = {'DEFAULT_CLOSED'}
@@ -483,6 +508,8 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel):
             col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
             col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask")
             col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets")
+            col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
+
             col.operator("object.voxel_remesh", text="Voxel Remesh")
         else:
             col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh")
@@ -537,6 +564,7 @@ classes = (
     DATA_PT_shape_keys,
     DATA_PT_uv_texture,
     DATA_PT_vertex_colors,
+    DATA_PT_sculpt_vertex_colors,
     DATA_PT_face_maps,
     DATA_PT_normals,
     DATA_PT_texture_space,
index c82f891ecb0d6e74a5859230e01d912c6165f00f..ba0d904a3234f3a754cccce2d0640909fd94cf54 100644 (file)
@@ -607,6 +607,10 @@ def brush_settings(layout, context, brush, popover=False):
             layout.operator("sculpt.set_persistent_base")
             layout.separator()
 
+        if capabilities.has_color:
+            UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="Paint Color")
+            layout.prop(brush, "blend", text="Blend Mode")
+
         if brush.sculpt_tool == 'CLAY_STRIPS':
             row = layout.row()
             row.prop(brush, "tip_roundness")
@@ -655,6 +659,15 @@ def brush_settings(layout, context, brush, popover=False):
         if brush.sculpt_tool == 'GRAB':
             layout.prop(brush, "use_grab_active_vertex")
 
+        if brush.sculpt_tool == 'PAINT':
+            col = layout.column()
+            col.prop(brush, "flow")
+            col.prop(brush, "wet_mix")
+            col.prop(brush, "wet_persistence")
+            col.prop(brush, "density")
+            col.prop(brush, "tip_roundness")
+            col.prop(brush, "tip_scale_x")
+
         if brush.sculpt_tool == 'MULTIPLANE_SCRAPE':
             col = layout.column()
             col.prop(brush, "multiplane_scrape_angle")
index 52c59431316d9b4c538a8715f4614b26a9298ab0..65f399118e6c7386fbc3f4e426f4adc04085fdb7 100644 (file)
@@ -1258,6 +1258,22 @@ class _defs_sculpt:
             draw_settings=draw_settings,
         )
 
+    @ToolDef.from_fn
+    def color_filter():
+        def draw_settings(_context, layout, tool):
+            props = tool.operator_properties("sculpt.color_filter")
+            layout.prop(props, "type", expand=False)
+            layout.prop(props, "strength")
+
+        return dict(
+            idname="builtin.color_filter",
+            label="Color Filter",
+            icon="ops.sculpt.color_filter",
+            widget=None,
+            keymap=(),
+            draw_settings=draw_settings,
+        )
+
 
 class _defs_vertex_paint:
 
@@ -2433,6 +2449,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
             None,
             _defs_sculpt.mesh_filter,
             _defs_sculpt.cloth_filter,
+            _defs_sculpt.color_filter,
             None,
             _defs_transform.translate,
             _defs_transform.rotate,
index 0daf9761246e85fc0d3d56c1e61f9c4e509038bc..d1fe3f649c8b65bb0c252227b895c009135676ef 100644 (file)
@@ -295,6 +295,11 @@ class _draw_tool_settings_context_mode:
         if not capabilities.has_direction:
             layout.row().prop(brush, "direction", expand=True, text="")
 
+        if capabilities.has_color:
+            UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text = "")
+            layout.prop(brush, "blend", text="", expand = False)
+
+
         return True
 
     @staticmethod
@@ -7333,6 +7338,12 @@ class VIEW3D_PT_sculpt_context_menu(Panel):
         brush = context.tool_settings.sculpt.brush
         capabilities = brush.sculpt_capabilities
 
+        if capabilities.has_color:
+            split = layout.split(factor=0.1)
+            UnifiedPaintPanel.prop_unified_color(split, context, brush, "color", text="")
+            UnifiedPaintPanel.prop_unified_color_picker(split, context, brush, "color", value_slider=True)
+            layout.prop(brush, "blend", text="")
+
         UnifiedPaintPanel.prop_unified(
             layout,
             context,
index 76bac375d5b735c52af07f276a4dfdf71a7c4ca3..a951025166ed46f2e4a728224345685413cfbdde 100644 (file)
@@ -806,6 +806,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
         col.prop(mesh, "use_remesh_preserve_volume", text="Volume")
         col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask")
         col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets")
+        col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors")
+
 
         layout.operator("object.voxel_remesh", text="Remesh")
 
index b63f9a9814b2d4621e39d17e9c81f1addf6ba816..24f95f7ed208090ca196c969975befa217e5dd9f 100644 (file)
@@ -60,6 +60,7 @@ struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh,
 
 /* Data reprojection functions */
 void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
+void BKE_remesh_reproject_vertex_paint(struct Mesh *target, struct Mesh *source);
 void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, struct Mesh *source);
 
 #ifdef __cplusplus
index 6e4f2efeeb8c86845e9bb1bb90a378e12588d9fb..56503a0bcc66b998249c95ff216f4a574c5894c8 100644 (file)
@@ -308,6 +308,7 @@ typedef struct SculptSession {
   int totvert, totpoly;
 
   struct KeyBlock *shapekey_active;
+  struct MPropCol *vcol;
   float *vmask;
 
   /* Mesh connectivity */
@@ -430,7 +431,8 @@ void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
 void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph,
                                        struct Object *ob_orig,
                                        bool need_pmap,
-                                       bool need_mask);
+                                       bool need_mask,
+                                       bool need_colors);
 void BKE_sculpt_update_object_before_eval(struct Object *ob_eval);
 void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Object *ob_eval);
 
index 8fb6f140822ca7fe489a429997c038d9efcabf63..87875314aece1d4f199ec9acadee61c84139540f 100644 (file)
@@ -58,6 +58,10 @@ typedef struct {
   float (*co)[3];
 } PBVHProxyNode;
 
+typedef struct {
+  float (*color)[4];
+} PBVHColorBufferNode;
+
 typedef enum {
   PBVH_Leaf = 1 << 0,
 
@@ -75,6 +79,7 @@ typedef enum {
   PBVH_FullyUnmasked = 1 << 12,
 
   PBVH_UpdateTopology = 1 << 13,
+  PBVH_UpdateColor = 1 << 14,
 } PBVHNodeFlags;
 
 typedef struct PBVHFrustumPlanes {
@@ -252,6 +257,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
 
 void BKE_pbvh_node_mark_update(PBVHNode *node);
 void BKE_pbvh_node_mark_update_mask(PBVHNode *node);
+void BKE_pbvh_node_mark_update_color(PBVHNode *node);
 void BKE_pbvh_node_mark_update_visibility(PBVHNode *node);
 void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node);
 void BKE_pbvh_node_mark_redraw(PBVHNode *node);
@@ -352,6 +358,7 @@ typedef struct PBVHVertexIter {
   struct MVert *mverts;
   int totvert;
   const int *vert_indices;
+  struct MPropCol *vcol;
   float *vmask;
 
   /* bmesh */
@@ -368,6 +375,7 @@ typedef struct PBVHVertexIter {
   short *no;
   float *fno;
   float *mask;
+  float *col;
   bool visible;
 } PBVHVertexIter;
 
@@ -419,7 +427,9 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
           vi.no = vi.mvert->no; \
           vi.index = vi.vert_indices[vi.i]; \
           if (vi.vmask) \
-            vi.mask = &vi.vmask[vi.vert_indices[vi.gx]]; \
+            vi.mask = &vi.vmask[vi.index]; \
+          if (vi.vcol) \
+            vi.col = vi.vcol[vi.index].color; \
         } \
         else { \
           if (!BLI_gsetIterator_done(&vi.bm_unique_verts)) { \
@@ -472,6 +482,9 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings,
 
 struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh);
 
+PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
+void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
+
 #ifdef __cplusplus
 }
 #endif
index b66df6b1da6c578768a0a70a5dc36cf2f92f6644..0a991d3ef4aca011b1287f86250076139e487d07 100644 (file)
@@ -1570,6 +1570,23 @@ void BKE_brush_sculpt_reset(Brush *br)
       br->alpha = 1.0f;
       br->height = 0.05f;
       break;
+    case SCULPT_TOOL_PAINT:
+      br->hardness = 0.4f;
+      br->spacing = 10;
+      br->alpha = 0.6f;
+      br->flow = 1.0f;
+      br->tip_scale_x = 1.0f;
+      br->tip_roundness = 1.0f;
+      br->density = 1.0f;
+      br->flag &= ~BRUSH_SPACE_ATTEN;
+      zero_v3(br->rgb);
+      break;
+    case SCULPT_TOOL_SMEAR:
+      br->alpha = 1.0f;
+      br->spacing = 7;
+      br->flag &= ~BRUSH_SPACE_ATTEN;
+      br->curve_preset = BRUSH_CURVE_SPHERE;
+      break;
     default:
       break;
   }
@@ -1629,14 +1646,15 @@ void BKE_brush_sculpt_reset(Brush *br)
       break;
 
     case SCULPT_TOOL_SIMPLIFY:
+    case SCULPT_TOOL_PAINT:
     case SCULPT_TOOL_MASK:
     case SCULPT_TOOL_DRAW_FACE_SETS:
-      br->add_col[0] = 0.750000;
-      br->add_col[1] = 0.750000;
-      br->add_col[2] = 0.750000;
-      br->sub_col[0] = 0.750000;
-      br->sub_col[1] = 0.750000;
-      br->sub_col[2] = 0.750000;
+      br->add_col[0] = 0.75f;
+      br->add_col[1] = 0.75f;
+      br->add_col[2] = 0.75f;
+      br->sub_col[0] = 0.75f;
+      br->sub_col[1] = 0.75f;
+      br->sub_col[2] = 0.75f;
       break;
 
     case SCULPT_TOOL_CLOTH:
index 7dd4d1178ef2f8b9adebadce3e508106301c4b3f..642a865afab3bb54a68bfd3a82d8d4f656694a89 100644 (file)
@@ -858,7 +858,6 @@ static void layerDoMinMax_mloopcol(const void *data, void *vmin, void *vmax)
   if (m->a < min->a) {
     min->a = m->a;
   }
-
   if (m->r > max->r) {
     max->r = m->r;
   }
@@ -1355,7 +1354,7 @@ static void layerCopyValue_propcol(const void *source,
     /* Modes that do a full copy or nothing. */
     if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) {
       /* TODO: Check for a real valid way to get 'factor' value of our dest color? */
-      const float f = (m2->col[0] + m2->col[1] + m2->col[2]) / 3.0f;
+      const float f = (m2->color[0] + m2->color[1] + m2->color[2]) / 3.0f;
       if (mixmode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && f < mixfactor) {
         return; /* Do Nothing! */
       }
@@ -1363,29 +1362,29 @@ static void layerCopyValue_propcol(const void *source,
         return; /* Do Nothing! */
       }
     }
-    copy_v3_v3(m2->col, m1->col);
+    copy_v3_v3(m2->color, m1->color);
   }
   else { /* Modes that support 'real' mix factor. */
     if (mixmode == CDT_MIX_MIX) {
-      blend_color_mix_float(tmp_col, m2->col, m1->col);
+      blend_color_mix_float(tmp_col, m2->color, m1->color);
     }
     else if (mixmode == CDT_MIX_ADD) {
-      blend_color_add_float(tmp_col, m2->col, m1->col);
+      blend_color_add_float(tmp_col, m2->color, m1->color);
     }
     else if (mixmode == CDT_MIX_SUB) {
-      blend_color_sub_float(tmp_col, m2->col, m1->col);
+      blend_color_sub_float(tmp_col, m2->color, m1->color);
     }
     else if (mixmode == CDT_MIX_MUL) {
-      blend_color_mul_float(tmp_col, m2->col, m1->col);
+      blend_color_mul_float(tmp_col, m2->color, m1->color);
     }
     else {
-      memcpy(tmp_col, m1->col, sizeof(tmp_col));
+      memcpy(tmp_col, m1->color, sizeof(tmp_col));
     }
-    blend_color_interpolate_float(m2->col, m2->col, tmp_col, mixfactor);
+    blend_color_interpolate_float(m2->color, m2->color, tmp_col, mixfactor);
 
-    copy_v3_v3(m2->col, m1->col);
+    copy_v3_v3(m2->color, m1->color);
   }
-  m2->col[3] = m1->col[3];
+  m2->color[3] = m1->color[3];
 }
 
 static bool layerEqual_propcol(const void *data1, const void *data2)
@@ -1394,7 +1393,7 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
   float tot = 0;
 
   for (int i = 0; i < 4; i++) {
-    float c = (m1->col[i] - m2->col[i]);
+    float c = (m1->color[i] - m2->color[i]);
     tot += c * c;
   }
 
@@ -1404,29 +1403,29 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
 static void layerMultiply_propcol(void *data, float fac)
 {
   MPropCol *m = data;
-  mul_v4_fl(m->col, fac);
+  mul_v4_fl(m->color, fac);
 }
 
 static void layerAdd_propcol(void *data1, const void *data2)
 {
   MPropCol *m = data1;
   const MPropCol *m2 = data2;
-  add_v4_v4(m->col, m2->col);
+  add_v4_v4(m->color, m2->color);
 }
 
 static void layerDoMinMax_propcol(const void *data, void *vmin, void *vmax)
 {
   const MPropCol *m = data;
   MPropCol *min = vmin, *max = vmax;
-  minmax_v4v4_v4(min->col, max->col, m->col);
+  minmax_v4v4_v4(min->color, max->color, m->color);
 }
 
 static void layerInitMinMax_propcol(void *vmin, void *vmax)
 {
   MPropCol *min = vmin, *max = vmax;
 
-  copy_v4_fl(min->col, FLT_MAX);
-  copy_v4_fl(max->col, FLT_MIN);
+  copy_v4_fl(min->color, FLT_MAX);
+  copy_v4_fl(max->color, FLT_MIN);
 }
 
 static void layerDefault_propcol(void *data, int count)
@@ -1436,7 +1435,7 @@ static void layerDefault_propcol(void *data, int count)
   MPropCol *pcol = (MPropCol *)data;
   int i;
   for (i = 0; i < count; i++) {
-    copy_v4_v4(pcol[i].col, default_propcol.col);
+    copy_v4_v4(pcol[i].color, default_propcol.color);
   }
 }
 
@@ -1450,14 +1449,14 @@ static void layerInterp_propcol(
     float weight = weights ? weights[i] : 1.0f;
     const MPropCol *src = sources[i];
     if (sub_weights) {
-      madd_v4_v4fl(col, src->col, (*sub_weight) * weight);
+      madd_v4_v4fl(col, src->color, (*sub_weight) * weight);
       sub_weight++;
     }
     else {
-      madd_v4_v4fl(col, src->col, weight);
+      madd_v4_v4fl(col, src->color, weight);
     }
   }
-  copy_v4_v4(mc->col, col);
+  copy_v4_v4(mc->color, col);
 }
 
 static int layerMaxNum_propcol(void)
@@ -1871,7 +1870,7 @@ const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
 };
 const CustomData_MeshMasks CD_MASK_MESH = {
     .vmask = (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
-              CD_MASK_GENERIC_DATA),
+              CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
     .emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
     .fmask = 0,
     .lmask = (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL |
@@ -1881,7 +1880,7 @@ const CustomData_MeshMasks CD_MASK_MESH = {
 };
 const CustomData_MeshMasks CD_MASK_EDITMESH = {
     .vmask = (CD_MASK_MDEFORMVERT | CD_MASK_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
-              CD_MASK_SHAPE_KEYINDEX | CD_MASK_GENERIC_DATA),
+              CD_MASK_SHAPE_KEYINDEX | CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
     .emask = (CD_MASK_GENERIC_DATA),
     .fmask = 0,
     .lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@@ -1901,7 +1900,8 @@ const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
 };
 const CustomData_MeshMasks CD_MASK_BMESH = {
     .vmask = (CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
-              CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_GENERIC_DATA),
+              CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_GENERIC_DATA |
+              CD_MASK_PROP_COLOR),
     .emask = (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
     .fmask = 0,
     .lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
@@ -1925,7 +1925,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = {
     .vmask = (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL |
               CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO |
               CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK |
-              CD_MASK_GENERIC_DATA),
+              CD_MASK_GENERIC_DATA | CD_MASK_PROP_COLOR),
     .emask = (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT |
               CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_GENERIC_DATA),
     .fmask = (CD_MASK_MFACE | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_MTFACE | CD_MASK_MCOL |
index 3daf9f2752e3f1fd4abcd96aaace1aa3c36b427e..010b306ec768d6b7103945ff249cac037c2b424c 100644 (file)
@@ -405,6 +405,37 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
   free_bvhtree_from_mesh(&bvhtree);
 }
 
+void BKE_remesh_reproject_vertex_paint(Mesh *target, Mesh *source)
+{
+  BVHTreeFromMesh bvhtree = {
+      .nearest_callback = NULL,
+  };
+  BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
+
+  int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR);
+
+  for (int layer_n = 0; layer_n < tot_color_layer; layer_n++) {
+    const char *layer_name = CustomData_get_layer_name(&source->vdata, CD_PROP_COLOR, layer_n);
+    CustomData_add_layer_named(
+        &target->vdata, CD_PROP_COLOR, CD_CALLOC, NULL, target->totvert, layer_name);
+
+    MPropCol *target_color = CustomData_get_layer_n(&target->vdata, CD_PROP_COLOR, layer_n);
+    MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
+    MPropCol *source_color = CustomData_get_layer_n(&source->vdata, CD_PROP_COLOR, layer_n);
+    for (int i = 0; i < target->totvert; i++) {
+      BVHTreeNearest nearest;
+      nearest.index = -1;
+      nearest.dist_sq = FLT_MAX;
+      BLI_bvhtree_find_nearest(
+          bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
+      if (nearest.index != -1) {
+        copy_v4_v4(target_color[i].color, source_color[nearest.index].color);
+      }
+    }
+  }
+  free_bvhtree_from_mesh(&bvhtree);
+}
+
 struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct Mesh *mesh)
 {
   const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
index c5ef5acb08b9875f95a0089d8b223d54ce1b0fd4..d8c3e0bf7147af9fd27cea0d6eace681d5d3fa99 100644 (file)
@@ -183,7 +183,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
 #endif
         /* Always compute UVs, vertex colors as orcos for render. */
         cddata_masks.lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
-        cddata_masks.vmask |= CD_MASK_ORCO;
+        cddata_masks.vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
       }
       if (em) {
         makeDerivedMesh(depsgraph, scene, ob, em, &cddata_masks); /* was CD_MASK_BAREMESH */
index f26b478c680eae9cea66461c8a702c80ec978cfe..151e000609233210a36bfdcdb0b2ac98cfd88fb1 100644 (file)
@@ -1474,8 +1474,12 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
 /**
  * \param need_mask: So that the evaluated mesh that is returned has mask data.
  */
-static void sculpt_update_object(
-    Depsgraph *depsgraph, Object *ob, Mesh *me_eval, bool need_pmap, bool need_mask)
+static void sculpt_update_object(Depsgraph *depsgraph,
+                                 Object *ob,
+                                 Mesh *me_eval,
+                                 bool need_pmap,
+                                 bool need_mask,
+                                 bool need_colors)
 {
   Scene *scene = DEG_get_input_scene(depsgraph);
   Sculpt *sd = scene->toolsettings->sculpt;
@@ -1503,6 +1507,16 @@ static void sculpt_update_object(
     }
   }
 
+  /* Add a color layer if a color tool is used. */
+  Mesh *orig_me = BKE_object_get_original_mesh(ob);
+  if (need_colors) {
+    if (!CustomData_has_layer(&orig_me->vdata, CD_PROP_COLOR)) {
+      CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert);
+      BKE_mesh_update_customdata_pointers(orig_me, true);
+      DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY);
+    }
+  }
+
   /* tessfaces aren't used and will become invalid */
   BKE_mesh_tessface_clear(me);
 
@@ -1535,6 +1549,7 @@ static void sculpt_update_object(
     ss->multires.modifier = NULL;
     ss->multires.level = 0;
     ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
+    ss->vcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
   }
 
   /* Sculpt Face Sets. */
@@ -1663,13 +1678,11 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
 
   BLI_assert(me_eval != NULL);
 
-  sculpt_update_object(depsgraph, ob_orig, me_eval, false, false);
+  sculpt_update_object(depsgraph, ob_orig, me_eval, false, false, false);
 }
 
-void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph,
-                                       Object *ob_orig,
-                                       bool need_pmap,
-                                       bool need_mask)
+void BKE_sculpt_update_object_for_edit(
+    Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool need_colors)
 {
   /* Update from sculpt operators and undo, to update sculpt session
    * and PBVH after edits. */
@@ -1679,7 +1692,7 @@ void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph,
 
   BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
 
-  sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask);
+  sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask, need_colors);
 }
 
 int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
index 19f28047b80eed044d64960672322bb0f0e69e04..8d7dabf98595722fade68356ea5008b4078fe134 100644 (file)
@@ -1325,6 +1325,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
                                      CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
                                      pbvh->face_sets_color_seed,
                                      pbvh->face_sets_color_default,
+                                     CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR),
                                      update_flags);
         break;
       case PBVH_BMESH:
@@ -1442,6 +1443,10 @@ void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flag)
     pbvh_update_mask_redraw(pbvh, nodes, totnode, flag);
   }
 
+  if (flag & (PBVH_UpdateColor)) {
+    /* Do nothing */
+  }
+
   if (flag & (PBVH_UpdateVisibility)) {
     pbvh_update_visibility_redraw(pbvh, nodes, totnode, flag);
   }
@@ -1729,6 +1734,11 @@ void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
   node->flag |= PBVH_UpdateMask | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
 }
 
+void BKE_pbvh_node_mark_update_color(PBVHNode *node)
+{
+  node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
+}
+
 void BKE_pbvh_node_mark_update_visibility(PBVHNode *node)
 {
   node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers |
@@ -2905,6 +2915,26 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot)
   *r_tot = tot;
 }
 
+PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node)
+{
+
+  if (!node->color_buffer.color) {
+    node->color_buffer.color = MEM_callocN(node->uniq_verts * sizeof(float) * 4, "Color buffer");
+  }
+  return &node->color_buffer;
+}
+
+void BKE_pbvh_node_color_buffer_free(PBVH *pbvh)
+{
+  PBVHNode **nodes;
+  int totnode;
+  BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+  for (int i = 0; i < totnode; i++) {
+    MEM_SAFE_FREE(nodes[i]->color_buffer.color);
+  }
+  MEM_SAFE_FREE(nodes);
+}
+
 void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode)
 {
   struct CCGElem **grids;
@@ -2958,6 +2988,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
   vi->mask = NULL;
   if (pbvh->type == PBVH_FACES) {
     vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK);
+    vi->vcol = CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR);
   }
 }
 
index 7397f93989416e5fd7ebe9678c55b7145a4dcfa5..6f8bae822ea648032e3d8ac85f9a3d2477031694 100644 (file)
@@ -105,6 +105,9 @@ struct PBVHNode {
   float (*bm_orco)[3];
   int (*bm_ortri)[3];
   int bm_tot_ortri;
+
+  /* Used to store the brush color during a stroke and composite it over the original color */
+  PBVHColorBufferNode color_buffer;
 };
 
 typedef enum {
index 0648264466d60254ca895d51d4227e3d8487000c..890be9ff3ef38ce173f91760b865a94a1af9eb34 100644 (file)
@@ -1749,6 +1749,16 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
    */
   {
     /* Keep this block, even when empty. */
+    /* Paint Brush. This ensure that the brush paints by default. Used during the develpment and
+     * patch review of the initial Sculpt Vertex Colors implementation (D5975) */
+    LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+      if (brush->ob_mode & OB_MODE_SCULPT && brush->sculpt_tool == SCULPT_TOOL_PAINT) {
+        brush->tip_roundness = 1.0f;
+        brush->flow = 1.0f;
+        brush->density = 1.0f;
+        brush->tip_scale_x = 1.0f;
+      }
+    }
   }
 }
 
@@ -3474,7 +3484,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
 
     for (Mesh *me = bmain->meshes.first; me; me = me->id.next) {
       me->flag &= ~(ME_FLAG_UNUSED_0 | ME_FLAG_UNUSED_1 | ME_FLAG_UNUSED_3 | ME_FLAG_UNUSED_4 |
-                    ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_FLAG_UNUSED_8);
+                    ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_VERTEX_COLORS);
     }
 
     for (Material *mat = bmain->materials.first; mat; mat = mat->id.next) {
index 91d89254c90fc92be205510b71e44d2389928db6..1217b69f1b58da8ba7d675c004be648836dca150 100644 (file)
@@ -682,6 +682,22 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
       brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX;
     }
 
+    brush_name = "Paint";
+    brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
+    if (!brush) {
+      brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
+      id_us_min(&brush->id);
+      brush->sculpt_tool = SCULPT_TOOL_PAINT;
+    }
+
+    brush_name = "Smear";
+    brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
+    if (!brush) {
+      brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
+      id_us_min(&brush->id);
+      brush->sculpt_tool = SCULPT_TOOL_SMEAR;
+    }
+
     brush_name = "Simplify";
     brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
     if (!brush) {
index 828a9127fb1af451b55af6b59ef487eeddb66f61..791b83a22a8a9e2bd47b361e3cac6eaa86d513e8 100644 (file)
@@ -174,8 +174,19 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd,
       color_type, V3D_SHADING_MATERIAL_COLOR, V3D_SHADING_TEXTURE_COLOR);
 
   if (use_single_drawcall) {
-    struct GPUBatch *geom = (use_vcol) ? DRW_cache_mesh_surface_vertpaint_get(ob) :
-                                         DRW_cache_object_surface_get(ob);
+    struct GPUBatch *geom;
+    if (use_vcol) {
+      if (ob->mode & OB_MODE_VERTEX_PAINT) {
+        geom = DRW_cache_mesh_surface_vertpaint_get(ob);
+      }
+      else {
+        geom = DRW_cache_mesh_surface_sculptcolors_get(ob);
+      }
+    }
+    else {
+      geom = DRW_cache_object_surface_get(ob);
+    }
+
     if (geom) {
       DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, r_transp);
       DRW_shgroup_call(grp, geom, ob);
@@ -247,8 +258,13 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
   if ((color_type == V3D_SHADING_TEXTURE_COLOR) && (me == NULL || me->mloopuv == NULL)) {
     color_type = V3D_SHADING_MATERIAL_COLOR;
   }
-  if ((color_type == V3D_SHADING_VERTEX_COLOR) && (me == NULL || me->mloopcol == NULL)) {
-    color_type = V3D_SHADING_OBJECT_COLOR;
+  if (color_type == V3D_SHADING_VERTEX_COLOR) {
+    if (me == NULL) {
+      color_type = V3D_SHADING_OBJECT_COLOR;
+    }
+    if (!CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
+      color_type = V3D_SHADING_OBJECT_COLOR;
+    }
   }
 
   if (r_sculpt_pbvh) {
index f55403620413d857ac5a001846fbf5189cc6ec3a..9f30cd8595714a54c1f1f45df6bc97f872ee27ee 100644 (file)
@@ -2870,6 +2870,12 @@ GPUBatch *DRW_cache_mesh_surface_vertpaint_get(Object *ob)
   return DRW_mesh_batch_cache_get_surface_vertpaint(ob->data);
 }
 
+GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob)
+{
+  BLI_assert(ob->type == OB_MESH);
+  return DRW_mesh_batch_cache_get_surface_sculpt(ob->data);
+}
+
 GPUBatch *DRW_cache_mesh_surface_weights_get(Object *ob)
 {
   BLI_assert(ob->type == OB_MESH);
index 5703f616ad121113eedb555ec70e1de94ee8b653..2f289bf41104e678fc77b29078f22c32ecfd181e 100644 (file)
@@ -136,6 +136,7 @@ struct GPUBatch **DRW_cache_mesh_surface_shaded_get(struct Object *ob,
 struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob);
 struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob);
 struct GPUBatch *DRW_cache_mesh_surface_vertpaint_get(struct Object *ob);
+struct GPUBatch *DRW_cache_mesh_surface_sculptcolors_get(struct Object *ob);
 struct GPUBatch *DRW_cache_mesh_surface_weights_get(struct Object *ob);
 struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob);
 struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob);
index f203c2ff1ea9342b16b81106c4c7cabc409162a8..0b1f625dc2c7ca2f5845f56005ed56b942498f15 100644 (file)
@@ -53,6 +53,7 @@ typedef struct DRW_MeshCDMask {
   uint32_t uv : 8;
   uint32_t tan : 8;
   uint32_t vcol : 8;
+  uint32_t sculpt_vcol : 8;
   uint32_t orco : 1;
   uint32_t tan_orco : 1;
   /** Edit uv layer is from the base edit mesh as
index e1f0cc98745d3aaa8fefbbfaffb715b697d21c75..a8ba716f7b02d9d673dd29263cd8ab2ab3f897b5 100644 (file)
@@ -2177,7 +2177,9 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
   GPU_vertformat_deinterleave(&format);
 
   CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+  CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
   uint32_t vcol_layers = mr->cache->cd_used.vcol;
+  uint32_t svcol_layers = mr->cache->cd_used.sculpt_vcol;
 
   for (int i = 0; i < MAX_MCOL; i++) {
     if (vcol_layers & (1 << i)) {
@@ -2194,6 +2196,33 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
       if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
         GPU_vertformat_alias_add(&format, "ac");
       }
+
+      /* Gather number of auto layers. */
+      /* We only do vcols that are not overridden by uvs and sculpt vertex colors */
+      if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
+          CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
+        BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+        GPU_vertformat_alias_add(&format, attr_name);
+      }
+    }
+  }
+
+  /* Sculpt Vertex Colors */
+  for (int i = 0; i < 8; i++) {
+    if (svcol_layers & (1 << i)) {
+      char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+      const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
+      GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+
+      BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
+      GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+      if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
+        GPU_vertformat_alias_add(&format, "c");
+      }
+      if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
+        GPU_vertformat_alias_add(&format, "ac");
+      }
       /* Gather number of auto layers. */
       /* We only do vcols that are not overridden by uvs */
       if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
@@ -2202,6 +2231,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
       }
     }
   }
+
   GPUVertBuf *vbo = buf;
   GPU_vertbuf_init_with_format(vbo, &format);
   GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -2211,6 +2241,8 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
   } gpuMeshVcol;
 
   gpuMeshVcol *vcol_data = (gpuMeshVcol *)vbo->data;
+  MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
+
   for (int i = 0; i < MAX_MCOL; i++) {
     if (vcol_layers & (1 << i)) {
       if (mr->extract_type == MR_EXTRACT_BMESH) {
@@ -2239,6 +2271,36 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf)
         }
       }
     }
+
+    if (svcol_layers & (1 << i)) {
+      if (mr->extract_type == MR_EXTRACT_BMESH) {
+        int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
+        BMIter l_iter, f_iter;
+        BMLoop *loop;
+        BMFace *efa;
+        BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+          BM_ITER_ELEM (loop, &l_iter, efa, BM_LOOPS_OF_FACE) {
+            const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(loop->v, cd_ofs);
+            vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
+            vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
+            vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
+            vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
+            vcol_data++;
+          }
+        }
+      }
+      else {
+        MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
+        for (int l = 0; l < mr->loop_len; l++, vcol_data++) {
+          vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[l].v].color[0]);
+          vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[l].v].color[1]);
+          vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[l].v].color[2]);
+          vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[l].v].color[3]);
+        }
+      }
+
+      vcol_data += mr->loop_len;
+    }
   }
   return NULL;
 }
index f670343140a03d88c5283e0534898491de3b4b76..5cf1c24af0b5f7151a817bcefb4edc7d55f9c23a 100644 (file)
@@ -170,6 +170,7 @@ struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(struct Mesh *me,
 struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(struct Mesh *me);
+struct GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(struct Mesh *me);
 struct GPUBatch *DRW_mesh_batch_cache_get_surface_weights(struct Mesh *me);
 /* edit-mesh drawing */
 struct GPUBatch *DRW_mesh_batch_cache_get_edit_triangles(struct Mesh *me);
index 7207345f13c908d437e9ab7e4ce608a4cef1bb49..ea1717f06847a7e6dd546a3d1df36654dc376a70 100644 (file)
@@ -112,6 +112,21 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
   return &me->ldata;
 }
 
+BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
+{
+  switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+    case ME_WRAPPER_TYPE_MDATA:
+      return &me->vdata;
+      break;
+    case ME_WRAPPER_TYPE_BMESH:
+      return &me->edit_mesh->bm->vdata;
+      break;
+  }
+
+  BLI_assert(0);
+  return &me->vdata;
+}
+
 static void mesh_cd_calc_active_uv_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
 {
   const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
@@ -135,7 +150,19 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd
 static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
 {
   const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
-  const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
+  const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
+
+  int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR);
+  if (layer != -1) {
+    cd_used->sculpt_vcol |= (1 << layer);
+  }
+}
+
+static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used)
+{
+  const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
+  const CustomData *cd_ldata = &me_final->ldata;
+
   int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
   if (layer != -1) {
     cd_used->vcol |= (1 << layer);
@@ -148,6 +175,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
 {
   const Mesh *me_final = (me->edit_mesh) ? me->edit_mesh->mesh_eval_final : me;
   const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
+  const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
 
   /* See: DM_vertex_attributes_from_gpu for similar logic */
   DRW_MeshCDMask cd_used;
@@ -175,6 +203,11 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
               layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name);
               type = CD_MCOL;
             }
+
+            if (layer == -1) {
+              layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name);
+              type = CD_PROP_COLOR;
+            }
 #if 0 /* Tangents are always from UV's - this will never happen. */
             if (layer == -1) {
               layer = CustomData_get_named_layer(cd_ldata, CD_TANGENT, name);
@@ -222,7 +255,20 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
             }
             break;
           }
+          case CD_PROP_COLOR: {
+            /* Sculpt Vertex Colors */
+            if (layer == -1) {
+              layer = (name[0] != '\0') ?
+                          CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) :
+                          CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR);
+            }
+            if (layer != -1) {
+              cd_used.sculpt_vcol |= (1 << layer);
+            }
+            break;
+          }
           case CD_MCOL: {
+            /* Vertex Color Data */
             if (layer == -1) {
               layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) :
                                           CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL);
@@ -230,6 +276,7 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me,
             if (layer != -1) {
               cd_used.vcol |= (1 << layer);
             }
+
             break;
           }
           case CD_ORCO: {
@@ -679,10 +726,22 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me)
 {
   DRW_MeshCDMask cd_needed;
   mesh_cd_layers_type_clear(&cd_needed);
-  mesh_cd_calc_active_vcol_layer(me, &cd_needed);
+  mesh_cd_calc_active_mloopcol_layer(me, &cd_needed);
 
   BLI_assert(cd_needed.vcol != 0 &&
-             "No vcol layer available in vertpaint, but batches requested anyway!");
+             "No MLOOPCOL layer available in vertpaint, but batches requested anyway!");
+
+  mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
+}
+
+static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me)
+{
+  DRW_MeshCDMask cd_needed;
+  mesh_cd_layers_type_clear(&cd_needed);
+  mesh_cd_calc_active_vcol_layer(me, &cd_needed);
+
+  BLI_assert(cd_needed.sculpt_vcol != 0 &&
+             "No MPropCol layer available in Sculpt, but batches requested anyway!");
 
   mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
 }
@@ -799,6 +858,14 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Mesh *me)
   return DRW_batch_request(&cache->batch.surface);
 }
 
+GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Mesh *me)
+{
+  MeshBatchCache *cache = mesh_batch_cache_get(me);
+  sculpt_request_active_vcol(cache, me);
+  mesh_batch_cache_add_request(cache, MBC_SURFACE);
+  return DRW_batch_request(&cache->batch.surface);
+}
+
 int DRW_mesh_material_count_get(Mesh *me)
 {
   return mesh_render_mat_len_get(me);
@@ -1137,7 +1204,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
         if (cache->cd_used.orco != cache->cd_needed.orco) {
           GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.orco);
         }
-        if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) {
+        if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) ||
+            ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) !=
+             cache->cd_needed.sculpt_vcol)) {
           GPU_VERTBUF_DISCARD_SAFE(mbuffercache->vbo.vcol);
         }
       }
@@ -1218,7 +1287,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
     if (cache->cd_used.uv != 0) {
       DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.uv);
     }
-    if (cache->cd_used.vcol != 0) {
+    if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
       DRW_vbo_request(cache->batch.surface, &mbufcache->vbo.vcol);
     }
   }
@@ -1286,7 +1355,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
       if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) {
         DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.tan);
       }
-      if (cache->cd_used.vcol != 0) {
+      if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) {
         DRW_vbo_request(cache->surface_per_mat[i], &mbufcache->vbo.vcol);
       }
       if (cache->cd_used.orco != 0) {
index 17e2043a7a854201165e8b1c9edad6d664fcfb2f..9c49d991182d7ec488d00e7622a57050bad102da 100644 (file)
@@ -426,6 +426,15 @@ bool ED_mesh_color_remove_index(struct Mesh *me, const int n);
 bool ED_mesh_color_remove_active(struct Mesh *me);
 bool ED_mesh_color_remove_named(struct Mesh *me, const char *name);
 
+bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name);
+int ED_mesh_sculpt_color_add(struct Mesh *me,
+                             const char *name,
+                             const bool active_set,
+                             const bool do_init);
+bool ED_mesh_sculpt_color_remove_index(struct Mesh *me, const int n);
+bool ED_mesh_sculpt_color_remove_active(struct Mesh *me);
+bool ED_mesh_sculpt_color_remove_named(struct Mesh *me, const char *name);
+
 void ED_mesh_report_mirror(struct wmOperator *op, int totmirr, int totfail);
 void ED_mesh_report_mirror_ex(struct wmOperator *op, int totmirr, int totfail, char selectmode);
 
index 51b699acd6353cc0e4553edb8341ca9c87f4fc39..8fce726eff564b1fdb371b8e139d759e32a977a4 100644 (file)
@@ -121,8 +121,15 @@ static void delete_customdata_layer(Mesh *me, CustomDataLayer *layer)
   CustomData *data;
   int layer_index, tot, n;
 
-  data = mesh_customdata_get_type(
-      me, (ELEM(type, CD_MLOOPUV, CD_MLOOPCOL)) ? BM_LOOP : BM_FACE, &tot);
+  char htype = BM_FACE;
+  if (ELEM(type, CD_MLOOPCOL, CD_MLOOPUV)) {
+    htype = BM_LOOP;
+  }
+  else if (ELEM(type, CD_PROP_COLOR)) {
+    htype = BM_VERT;
+  }
+
+  data = mesh_customdata_get_type(me, htype, &tot);
   layer_index = CustomData_get_layer_index(data, type);
   n = (layer - &data->layers[layer_index]);
   BLI_assert(n >= 0 && (n + layer_index) < data->totlayer);
@@ -488,6 +495,117 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name)
   }
 }
 
+/*********************** Sculpt Vertex colors operators ************************/
+
+/* note: keep in sync with ED_mesh_uv_texture_add */
+int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool active_set, const bool do_init)
+{
+  BMEditMesh *em;
+  int layernum;
+
+  if (me->edit_mesh) {
+    em = me->edit_mesh;
+
+    layernum = CustomData_number_of_layers(&em->bm->vdata, CD_PROP_COLOR);
+    if (layernum >= MAX_MCOL) {
+      return -1;
+    }
+
+    /* CD_PROP_COLOR */
+    BM_data_layer_add_named(em->bm, &em->bm->vdata, CD_PROP_COLOR, name);
+    /* copy data from active vertex color layer */
+    if (layernum && do_init) {
+      const int layernum_dst = CustomData_get_active_layer(&em->bm->vdata, CD_PROP_COLOR);
+      BM_data_layer_copy(em->bm, &em->bm->vdata, CD_PROP_COLOR, layernum_dst, layernum);
+    }
+    if (active_set || layernum == 0) {
+      CustomData_set_layer_active(&em->bm->vdata, CD_PROP_COLOR, layernum);
+    }
+  }
+  else {
+    layernum = CustomData_number_of_layers(&me->vdata, CD_PROP_COLOR);
+    if (layernum >= MAX_MCOL) {
+      return -1;
+    }
+
+    if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) {
+      MPropCol *color_data = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
+      CustomData_add_layer_named(
+          &me->vdata, CD_PROP_COLOR, CD_DUPLICATE, color_data, me->totvert, name);
+    }
+    else {
+      CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
+    }
+
+    if (active_set || layernum == 0) {
+      CustomData_set_layer_active(&me->vdata, CD_PROP_COLOR, layernum);
+    }
+
+    BKE_mesh_update_customdata_pointers(me, true);
+  }
+
+  DEG_id_tag_update(&me->id, 0);
+  WM_main_add_notifier(NC_GEOM | ND_DATA, me);
+
+  return layernum;
+}
+
+bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name)
+{
+  BLI_assert(me->edit_mesh == NULL);
+
+  if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
+    CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, me->totvert, name);
+    BKE_mesh_update_customdata_pointers(me, true);
+  }
+
+  DEG_id_tag_update(&me->id, 0);
+
+  return (me->mloopcol != NULL);
+}
+
+bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n)
+{
+  CustomData *vdata = GET_CD_DATA(me, vdata);
+  CustomDataLayer *cdl;
+  int index;
+
+  index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n);
+  cdl = (index == -1) ? NULL : &vdata->layers[index];
+
+  if (!cdl) {
+    return false;
+  }
+
+  delete_customdata_layer(me, cdl);
+  DEG_id_tag_update(&me->id, 0);
+  WM_main_add_notifier(NC_GEOM | ND_DATA, me);
+
+  return true;
+}
+bool ED_mesh_sculpt_color_remove_active(Mesh *me)
+{
+  CustomData *vdata = GET_CD_DATA(me, vdata);
+  const int n = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
+  if (n != -1) {
+    return ED_mesh_sculpt_color_remove_index(me, n);
+  }
+  else {
+    return false;
+  }
+}
+bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name)
+{
+  CustomData *vdata = GET_CD_DATA(me, vdata);
+  const int n = CustomData_get_named_layer(vdata, CD_PROP_COLOR, name);
+  if (n != -1) {
+    return ED_mesh_sculpt_color_remove_index(me, n);
+  }
+  else {
+    return false;
+  }
+}
+
 /*********************** UV texture operators ************************/
 
 static bool layers_poll(bContext *C)
@@ -619,6 +737,62 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot)
   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+/*********************** Sculpt Vertex Color Operators ************************/
+
+static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+  Object *ob = ED_object_context(C);
+  Mesh *me = ob->data;
+
+  if (ED_mesh_sculpt_color_add(me, NULL, true, true) == -1) {
+    return OPERATOR_CANCELLED;
+  }
+
+  return OPERATOR_FINISHED;
+}
+
+void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Add Sculpt Vertex Color";
+  ot->description = "Add vertex color layer";
+  ot->idname = "MESH_OT_sculpt_vertex_color_add";
+
+  /* api callbacks */
+  ot->poll = layers_poll;
+  ot->exec = mesh_sculpt_vertex_color_add_exec;
+
+  /* flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+  Object *ob = ED_object_context(C);
+  Mesh *me = ob->data;
+
+  if (!ED_mesh_sculpt_color_remove_active(me)) {
+    return OPERATOR_CANCELLED;
+  }
+
+  return OPERATOR_FINISHED;
+}
+
+void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Remove Sculpt Vertex Color";
+  ot->description = "Remove vertex color layer";
+  ot->idname = "MESH_OT_sculpt_vertex_color_remove";
+
+  /* api callbacks */
+  ot->exec = mesh_sculpt_vertex_color_remove_exec;
+  ot->poll = layers_poll;
+
+  /* flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
 /* *** CustomData clear functions, we need an operator for each *** */
 
 static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int type)
index 5e70069456be9c86e936b13faa2188a91e34dc49..bebad3124542f10ab484e53a0fb4de0fdeaa05b3 100644 (file)
@@ -267,6 +267,8 @@ void MESH_OT_uv_texture_add(struct wmOperatorType *ot);
 void MESH_OT_uv_texture_remove(struct wmOperatorType *ot);
 void MESH_OT_vertex_color_add(struct wmOperatorType *ot);
 void MESH_OT_vertex_color_remove(struct wmOperatorType *ot);
+void MESH_OT_sculpt_vertex_color_add(struct wmOperatorType *ot);
+void MESH_OT_sculpt_vertex_color_remove(struct wmOperatorType *ot);
 /* no create_mask yet */
 void MESH_OT_customdata_mask_clear(struct wmOperatorType *ot);
 void MESH_OT_customdata_skin_add(struct wmOperatorType *ot);
index e10c1286dac80f2516233df828fcef9bdb2d85a4..ad1e91a57c0559bfd48a9fce606f494d1172d0dc 100644 (file)
@@ -155,6 +155,8 @@ void ED_operatortypes_mesh(void)
   WM_operatortype_append(MESH_OT_uv_texture_remove);
   WM_operatortype_append(MESH_OT_vertex_color_add);
   WM_operatortype_append(MESH_OT_vertex_color_remove);
+  WM_operatortype_append(MESH_OT_sculpt_vertex_color_add);
+  WM_operatortype_append(MESH_OT_sculpt_vertex_color_remove);
   WM_operatortype_append(MESH_OT_customdata_mask_clear);
   WM_operatortype_append(MESH_OT_customdata_skin_add);
   WM_operatortype_append(MESH_OT_customdata_skin_clear);
index 1d7920b99912956b06d881ce5a1d7421dc7d02c9..76323037fd7a161dd1a5df8dbd542016b8a66cc8 100644 (file)
@@ -174,6 +174,11 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
     BKE_remesh_reproject_sculpt_face_sets(new_mesh, mesh);
   }
 
+  if (mesh->flag & ME_REMESH_REPROJECT_VERTEX_COLORS) {
+    BKE_mesh_runtime_clear_geometry(mesh);
+    BKE_remesh_reproject_vertex_paint(new_mesh, mesh);
+  }
+
   BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
 
   if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) {
index b87549537416745fb41e0a43b2a029e92714a703..ed87d524627f63ac4925a119e7a80d2277cdefae 100644 (file)
@@ -64,10 +64,12 @@ set(SRC
   sculpt_detail.c
   sculpt_dyntopo.c
   sculpt_face_set.c
+  sculpt_filter_color.c
   sculpt_filter_mask.c
   sculpt_filter_mesh.c
   sculpt_mask_expand.c
   sculpt_multiplane_scrape.c
+  sculpt_paint_color.c
   sculpt_pose.c
   sculpt_smooth.c
   sculpt_transform.c
index 4222a466a7b2d29f8d7367360e3d87fd61ebc410..1291de046346f1c74a767f593735cd056f3380a0 100644 (file)
@@ -1441,7 +1441,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
              * cursor won't be tagged to update, so always initialize the preview chain if it is
              * null before drawing it. */
             if (update_previews || !ss->pose_ik_chain_preview) {
-              BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false);
+              BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false, false);
 
               /* Free the previous pose brush preview. */
               if (ss->pose_ik_chain_preview) {
index c32e496f4f502c9f7eea217aabcd4a6b3ad2280b..07a6b2873f41824956dd05b89104545b2c4f477c 100644 (file)
@@ -151,7 +151,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
   mode = RNA_enum_get(op->ptr, "mode");
   value = RNA_float_get(op->ptr, "value");
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
   pbvh = ob->sculpt->pbvh;
   multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
 
@@ -313,7 +313,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *
   /* Transform the clip planes in object space. */
   ED_view3d_clipping_calc(&bb, clip_planes, vc->region, vc->obact, rect);
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
   pbvh = ob->sculpt->pbvh;
   multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
 
@@ -500,7 +500,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
 
     ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect);
 
-    BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+    BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
     pbvh = ob->sculpt->pbvh;
     multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
 
index 6de54b3ae6ae415d003593e59bbb04fc216fcc6e..69c0d5bb712334567b5496fd132247b5e3fb6b11 100644 (file)
@@ -1101,12 +1101,12 @@ static void vertex_paint_init_session(Depsgraph *depsgraph,
   BLI_assert(ob->sculpt == NULL);
   ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
   ob->sculpt->mode_type = object_mode;
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
 }
 
 static void vertex_paint_init_stroke(Depsgraph *depsgraph, Object *ob)
 {
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
 }
 
 static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
index 84fa46e5b6b72b1a84475d19c7e1b7db97cc5252..cdffe31041ad8f3bf1d65e6e95dac2acb6a22328 100644 (file)
@@ -30,6 +30,7 @@
 #include "BLI_gsqueue.h"
 #include "BLI_hash.h"
 #include "BLI_math.h"
+#include "BLI_math_color_blend.h"
 #include "BLI_task.h"
 #include "BLI_utildefines.h"
 
@@ -153,6 +154,21 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
   return NULL;
 }
 
+const float *SCULPT_vertex_color_get(SculptSession *ss, int index)
+{
+  switch (BKE_pbvh_type(ss->pbvh)) {
+    case PBVH_FACES:
+      if (ss->vcol) {
+        return ss->vcol[index].color;
+      }
+      break;
+    case PBVH_BMESH:
+    case PBVH_GRIDS:
+      break;
+  }
+  return NULL;
+}
+
 void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
 {
   switch (BKE_pbvh_type(ss->pbvh)) {
@@ -998,6 +1014,8 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
               SCULPT_TOOL_LAYER,
               SCULPT_TOOL_POSE,
               SCULPT_TOOL_CLOTH,
+              SCULPT_TOOL_PAINT,
+              SCULPT_TOOL_SMEAR,
               SCULPT_TOOL_DRAW_FACE_SETS);
 }
 
@@ -1045,9 +1063,7 @@ typedef enum StrokeFlags {
 
 /* Initialize a SculptOrigVertData for accessing original vertex data;
  * handles BMesh, mesh, and multires. */
-static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
-                                             Object *ob,
-                                             SculptUndoNode *unode)
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode)
 {
   SculptSession *ss = ob->sculpt;
   BMesh *bm = ss->bm;
@@ -1062,6 +1078,7 @@ static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
     data->coords = data->unode->co;
     data->normals = data->unode->no;
     data->vmasks = data->unode->mask;
+    data->colors = data->unode->col;
   }
 }
 
@@ -1071,7 +1088,7 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *
 {
   SculptUndoNode *unode;
   unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
-  sculpt_orig_vert_data_unode_init(data, ob, unode);
+  SCULPT_orig_vert_data_unode_init(data, ob, unode);
 }
 
 /* Update a SculptOrigVertData for a particular vertex from the PBVH
@@ -1087,6 +1104,9 @@ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter
       orig_data->no = orig_data->normals[iter->i];
     }
   }
+  else if (orig_data->unode->type == SCULPT_UNDO_COLOR) {
+    orig_data->col = orig_data->colors[iter->i];
+  }
   else if (orig_data->unode->type == SCULPT_UNDO_MASK) {
     if (orig_data->bm_log) {
       orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert);
@@ -1247,7 +1267,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
     PBVHVertexIter vd;
     SculptOrigVertData orig_data;
 
-    sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode);
+    SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode);
 
     BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
     {
@@ -1265,6 +1285,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
       else if (orig_data.unode->type == SCULPT_UNDO_MASK) {
         *vd.mask = orig_data.mask;
       }
+      else if (orig_data.unode->type == SCULPT_UNDO_COLOR) {
+        copy_v4_v4(vd.col, orig_data.col);
+      }
 
       if (vd.mvert) {
         vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -1301,6 +1324,8 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
   BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode);
   BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings);
 
+  BKE_pbvh_node_color_buffer_free(ss->pbvh);
+
   MEM_SAFE_FREE(nodes);
 }
 
@@ -1478,6 +1503,9 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test,
 {
   float side = M_SQRT1_2;
   float local_co[3];
+  float i_local[4][4];
+
+  invert_m4_m4(i_local, local);
 
   if (sculpt_brush_test_clipping(test, co)) {
     return false;
@@ -2116,6 +2144,12 @@ static float brush_strength(const Sculpt *sd,
       return alpha * pressure * overlap * feather;
     case SCULPT_TOOL_SLIDE_RELAX:
       return alpha * pressure * overlap * feather * 2.0f;
+    case SCULPT_TOOL_PAINT:
+      final_pressure = pressure * pressure;
+      return final_pressure * overlap * feather;
+    case SCULPT_TOOL_SMEAR:
+      final_pressure = pressure * pressure;
+      return final_pressure * overlap * feather;
     case SCULPT_TOOL_CLAY_STRIPS:
       /* Clay Strips needs less strength to compensate the curve. */
       final_pressure = powf(pressure, 1.5f);
@@ -5261,17 +5295,16 @@ static void do_brush_action_task_cb(void *__restrict userdata,
       BKE_pbvh_node_mark_update(data->nodes[n]);
     }
   }
-  else {
-    SCULPT_undo_push_node(data->ob,
-                          data->nodes[n],
-                          data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
-                                                                         SCULPT_UNDO_COORDS);
-  }
-
-  if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
+  else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) {
+    SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
     BKE_pbvh_node_mark_update_mask(data->nodes[n]);
   }
+  else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+    SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+    BKE_pbvh_node_mark_update_color(data->nodes[n]);
+  }
   else {
+    SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
     BKE_pbvh_node_mark_update(data->nodes[n]);
   }
 }
@@ -5282,7 +5315,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
   int totnode;
   PBVHNode **nodes;
 
-  /* Build a list of all nodes that are potentially within the brush's area of influence. */
+  /* Check for unsupported features. */
+  PBVHType type = BKE_pbvh_type(ss->pbvh);
+  if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) {
+    return;
+  }
+
+  if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
+    return;
+  }
+
+  /* Build a list of all nodes that are potentially within the brush's area of influence */
 
   /* These brushes need to update all nodes as they are not constrained by the brush radius */
   /* Elastic deform needs all nodes to avoid artifacts as the effect of the brush is not
@@ -5471,6 +5514,12 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
       case SCULPT_TOOL_DRAW_FACE_SETS:
         SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
         break;
+      case SCULPT_TOOL_PAINT:
+        SCULPT_do_paint_brush(sd, ob, nodes, totnode);
+        break;
+      case SCULPT_TOOL_SMEAR:
+        SCULPT_do_smear_brush(sd, ob, nodes, totnode);
+        break;
     }
 
     if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
@@ -5998,6 +6047,10 @@ static const char *sculpt_tool_name(Sculpt *sd)
       return "Cloth Brush";
     case SCULPT_TOOL_DRAW_FACE_SETS:
       return "Draw Face Sets";
+    case SCULPT_TOOL_PAINT:
+      return "Paint Brush";
+    case SCULPT_TOOL_SMEAR:
+      return "Smear Brush";
   }
 
   return "Sculpting";
@@ -6012,6 +6065,7 @@ void SCULPT_cache_free(StrokeCache *cache)
   MEM_SAFE_FREE(cache->dial);
   MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp);
   MEM_SAFE_FREE(cache->layer_displacement_factor);
+  MEM_SAFE_FREE(cache->prev_colors);
 
   if (cache->pose_ik_chain) {
     SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@@ -6126,7 +6180,11 @@ static void sculpt_update_cache_invariants(
       cache->saved_mask_brush_tool = brush->mask_tool;
       brush->mask_tool = BRUSH_MASK_SMOOTH;
     }
-    else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
+    else if (ELEM(brush->sculpt_tool,
+                  SCULPT_TOOL_SLIDE_RELAX,
+                  SCULPT_TOOL_DRAW_FACE_SETS,
+                  SCULPT_TOOL_PAINT,
+                  SCULPT_TOOL_SMEAR)) {
       /* Do nothing, this tool has its own smooth mode. */
     }
     else {
@@ -6273,6 +6331,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
   int tool = brush->sculpt_tool;
 
   if (ELEM(tool,
+           SCULPT_TOOL_PAINT,
            SCULPT_TOOL_GRAB,
            SCULPT_TOOL_ELASTIC_DEFORM,
            SCULPT_TOOL_CLOTH,
@@ -6526,7 +6585,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
           ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) ||
           (brush->sculpt_tool == SCULPT_TOOL_POSE) ||
           (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) ||
-          (brush->sculpt_tool == SCULPT_TOOL_CLOTH) ||
+          (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
           (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS));
 }
 
@@ -6540,7 +6599,7 @@ void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *b
   if (ss->shapekey_active || ss->deform_modifiers_active ||
       (!BKE_sculptsession_use_pbvh_draw(ob, v3d) && need_pmap)) {
     Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
-    BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false);
+    BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, false, false);
   }
 }
 
@@ -6884,7 +6943,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
   SculptSession *ss = CTX_data_active_object(C)->sculpt;
   Brush *brush = BKE_paint_brush(&sd->paint);
   int mode = RNA_enum_get(op->ptr, "mode");
-  bool is_smooth;
+  bool is_smooth, needs_colors;
   bool need_mask = false;
 
   if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
@@ -6899,7 +6958,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
   sculpt_brush_init_tex(scene, sd, ss);
 
   is_smooth = sculpt_needs_connectivity_info(sd, brush, ss, mode);
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask);
+  needs_colors = ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors);
 }
 
 static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
@@ -7048,6 +7108,10 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
     BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
   }
 
+  if (update_flags & SCULPT_UPDATE_COLOR) {
+    BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
+  }
+
   if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
     BKE_pbvh_bmesh_after_stroke(ss->pbvh);
   }
@@ -7162,6 +7226,9 @@ static void sculpt_stroke_update_step(bContext *C,
   if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
     SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
   }
+  else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+    SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+  }
   else {
     SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
   }
@@ -7199,7 +7266,11 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
       if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
         brush->mask_tool = ss->cache->saved_mask_brush_tool;
       }
-      else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SLIDE_RELAX, SCULPT_TOOL_DRAW_FACE_SETS)) {
+      else if (ELEM(brush->sculpt_tool,
+                    SCULPT_TOOL_SLIDE_RELAX,
+                    SCULPT_TOOL_DRAW_FACE_SETS,
+                    SCULPT_TOOL_PAINT,
+                    SCULPT_TOOL_SMEAR)) {
         /* Do nothing. */
       }
       else {
@@ -7215,6 +7286,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
       SCULPT_automasking_end(ob);
     }
 
+    BKE_pbvh_node_color_buffer_free(ss->pbvh);
     SCULPT_cache_free(ss->cache);
     ss->cache = NULL;
 
@@ -7353,7 +7425,7 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
 
   if (ss) {
     SCULPT_vertex_random_access_init(ss);
-    BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+    BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
 
     MEM_SAFE_FREE(ss->layer_base);
 
@@ -7555,7 +7627,7 @@ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob)
 
   ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
   ob->sculpt->mode_type = OB_MODE_SCULPT;
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
 
   /* Here we can detect geometry that was just added to Sculpt Mode as it has the
    * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
@@ -7841,7 +7913,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
     return;
   }
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   if (!ss->pmap) {
     return;
@@ -7893,6 +7965,166 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
   ss->preview_vert_index_count = totpoints;
 }
 
+static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+  Object *ob = CTX_data_active_object(C);
+
+  ID *data;
+  data = ob->data;
+  if (data && ID_IS_LINKED(data)) {
+    return OPERATOR_CANCELLED;
+  }
+
+  if (ob->type != OB_MESH) {
+    return OPERATOR_CANCELLED;
+  }
+
+  Mesh *mesh = ob->data;
+
+  const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+  if (mloopcol_layer_n == -1) {
+    return OPERATOR_CANCELLED;
+  }
+  MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+  const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+  if (MPropCol_layer_n == -1) {
+    return OPERATOR_CANCELLED;
+  }
+  MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+  MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+  MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+  for (int i = 0; i < mesh->totpoly; i++) {
+    MPoly *c_poly = &polys[i];
+    for (int j = 0; j < c_poly->totloop; j++) {
+      int loop_index = c_poly->loopstart + j;
+      MLoop *c_loop = &loops[c_poly->loopstart + j];
+      loopcols[loop_index].r = (char)(vertcols[c_loop->v].color[0] * 255);
+      loopcols[loop_index].g = (char)(vertcols[c_loop->v].color[1] * 255);
+      loopcols[loop_index].b = (char)(vertcols[c_loop->v].color[2] * 255);
+      loopcols[loop_index].a = (char)(vertcols[c_loop->v].color[3] * 255);
+    }
+  }
+
+  DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+  WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+  return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Sculpt Vertex Color to Vertex Color";
+  ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
+  ot->idname = "SCULPT_OT_vertex_to_loop_colors";
+
+  /* api callbacks */
+  ot->poll = SCULPT_mode_poll;
+  ot->exec = vertex_to_loop_colors_exec;
+
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+  Object *ob = CTX_data_active_object(C);
+
+  ID *data;
+  data = ob->data;
+  if (data && ID_IS_LINKED(data)) {
+    return OPERATOR_CANCELLED;
+  }
+
+  if (ob->type != OB_MESH) {
+    return OPERATOR_CANCELLED;
+  }
+
+  Mesh *mesh = ob->data;
+
+  const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+  if (mloopcol_layer_n == -1) {
+    return OPERATOR_CANCELLED;
+  }
+  MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+  const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+  if (MPropCol_layer_n == -1) {
+    return OPERATOR_CANCELLED;
+  }
+  MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+  MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+  MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+  for (int i = 0; i < mesh->totpoly; i++) {
+    MPoly *c_poly = &polys[i];
+    for (int j = 0; j < c_poly->totloop; j++) {
+      int loop_index = c_poly->loopstart + j;
+      MLoop *c_loop = &loops[c_poly->loopstart + j];
+      vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
+      vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
+      vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
+      vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
+    }
+  }
+
+  DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+  WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+  return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Vertex Color to Sculpt Vertex Color";
+  ot->description = "Copy the active loop color layer to the vertex color";
+  ot->idname = "SCULPT_OT_loop_to_vertex_colors";
+
+  /* api callbacks */
+  ot->poll = SCULPT_mode_poll;
+  ot->exec = loop_to_vertex_colors_exec;
+
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int sculpt_sample_color_invoke(bContext *C,
+                                      wmOperator *UNUSED(op),
+                                      const wmEvent *UNUSED(e))
+{
+  Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+  Object *ob = CTX_data_active_object(C);
+  Brush *brush = BKE_paint_brush(&sd->paint);
+  SculptSession *ss = ob->sculpt;
+  int active_vertex = SCULPT_active_vertex_get(ss);
+  const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
+  if (!active_vertex_color) {
+    return OPERATOR_CANCELLED;
+  }
+  brush->rgb[0] = active_vertex_color[0];
+  brush->rgb[1] = active_vertex_color[1];
+  brush->rgb[2] = active_vertex_color[2];
+  brush->alpha = active_vertex_color[3];
+  return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_sample_color(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Sample color";
+  ot->idname = "SCULPT_OT_sample_color";
+  ot->description = "Sample the vertex color of the active vertex";
+
+  /* api callbacks */
+  ot->invoke = sculpt_sample_color_invoke;
+  ot->poll = SCULPT_mode_poll;
+
+  ot->flag = OPTYPE_REGISTER;
+}
+
 void ED_operatortypes_sculpt(void)
 {
   WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -7915,4 +8147,8 @@ void ED_operatortypes_sculpt(void)
   WM_operatortype_append(SCULPT_OT_face_sets_init);
   WM_operatortype_append(SCULPT_OT_cloth_filter);
   WM_operatortype_append(SCULPT_OT_face_sets_edit);
+  WM_operatortype_append(SCULPT_OT_sample_color);
+  WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
+  WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
+  WM_operatortype_append(SCULPT_OT_color_filter);
 }
index 7776af11a7763b8344e91c2e1559ca2c09e5ded9..318919f0d484859941295fb31f2e29574ac61292 100644 (file)
@@ -873,7 +873,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
 
   SCULPT_vertex_random_access_init(ss);
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   const int totverts = SCULPT_vertex_count_get(ss);
   for (int i = 0; i < totverts; i++) {
@@ -922,10 +922,10 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
   SCULPT_vertex_random_access_init(ss);
 
   /* Needs mask data to be available as it is used when solving the constraints. */
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   SCULPT_undo_push_begin("Cloth filter");
-  SCULPT_filter_cache_init(ob, sd);
+  SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
 
   const float cloth_mass = RNA_float_get(op->ptr, "cloth_mass");
   const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
index f071deaa219d2fc969c294f2a814a2ba42ddbdc6..463233fd6fb082128142236ec7626f6a7d1884d6 100644 (file)
@@ -181,7 +181,7 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
   /* Update the active vertex. */
   float mouse[2] = {mx, my};
   SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
 
   /* Average the edge length of the connected edges to the active vertex. */
   int active_vertex = SCULPT_active_vertex_get(ss);
index 7bb54366204c26c157367024fc7a6a26eae15b91..57dc70c527626f4b71fa69de06e7bae869221214 100644 (file)
@@ -269,7 +269,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
     return OPERATOR_CANCELLED;
   }
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
 
   const int tot_vert = SCULPT_vertex_count_get(ss);
   float threshold = 0.5f;
@@ -630,7 +630,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
     return OPERATOR_CANCELLED;
   }
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
 
   PBVH *pbvh = ob->sculpt->pbvh;
   PBVHNode **nodes;
@@ -787,7 +787,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
     return OPERATOR_CANCELLED;
   }
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   const int tot_vert = SCULPT_vertex_count_get(ss);
   const int mode = RNA_enum_get(op->ptr, "mode");
@@ -1083,7 +1083,7 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
     return OPERATOR_CANCELLED;
   }
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
 
   PBVH *pbvh = ob->sculpt->pbvh;
   PBVHNode **nodes;
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 (file)
index 0000000..77fbe42
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+typedef enum eSculptColorFilterTypes {
+  COLOR_FILTER_HUE,
+  COLOR_FILTER_SATURATION,
+  COLOR_FILTER_VALUE,
+  COLOR_FILTER_BRIGHTNESS,
+  COLOR_FILTER_CONTRAST,
+  COLOR_FILTER_RED,
+  COLOR_FILTER_GREEN,
+  COLOR_FILTER_BLUE,
+  COLOR_FILTER_SMOOTH,
+} eSculptColorFilterTypes;
+
+EnumPropertyItem prop_color_filter_types[] = {
+    {COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"},
+    {COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"},
+    {COLOR_FILTER_VALUE, "VALUE", 0, "Value", "Change value"},
+
+    {COLOR_FILTER_BRIGHTNESS, "BRIGTHNESS", 0, "Brightness", "Change brightness"},
+    {COLOR_FILTER_CONTRAST, "CONTRAST", 0, "Contrast", "Change contrast"},
+
+    {COLOR_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth colors"},
+
+    {COLOR_FILTER_RED, "RED", 0, "Red", "Change red channel"},
+    {COLOR_FILTER_GREEN, "GREEN", 0, "Green", "Change green channel"},
+    {COLOR_FILTER_BLUE, "BLUE", 0, "Blue", "Change blue channel"},
+    {0, NULL, 0, NULL, NULL},
+};
+
+static void color_filter_task_cb(void *__restrict userdata,
+                                 const int n,
+                                 const TaskParallelTLS *__restrict UNUSED(tls))
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+
+  const int mode = data->filter_type;
+
+  SculptOrigVertData orig_data;
+  SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+  PBVHVertexIter vd;
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    SCULPT_orig_vert_data_update(&orig_data, &vd);
+    float orig_color[3], final_color[4], hsv_color[3];
+    int hue;
+    float brightness, contrast, gain, delta, offset;
+    float fade = vd.mask ? *vd.mask : 0.0f;
+    fade = 1.0f - fade;
+    fade *= data->filter_strength;
+
+    copy_v3_v3(orig_color, orig_data.col);
+
+    switch (mode) {
+      case COLOR_FILTER_HUE:
+        rgb_to_hsv_v(orig_color, hsv_color);
+        hue = hsv_color[0] + fade;
+        hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
+        hsv_to_rgb_v(hsv_color, final_color);
+        break;
+      case COLOR_FILTER_SATURATION:
+        rgb_to_hsv_v(orig_color, hsv_color);
+        hsv_color[1] = hsv_color[1] + fade;
+        CLAMP(hsv_color[1], 0.0f, 1.0f);
+        hsv_to_rgb_v(hsv_color, final_color);
+        break;
+      case COLOR_FILTER_VALUE:
+        rgb_to_hsv_v(orig_color, hsv_color);
+        hsv_color[2] = hsv_color[2] + fade;
+        CLAMP(hsv_color[2], 0.0f, 1.0f);
+        hsv_to_rgb_v(hsv_color, final_color);
+        break;
+      case COLOR_FILTER_RED:
+        orig_color[0] = orig_color[0] + fade;
+        CLAMP(orig_color[0], 0.0f, 1.0f);
+        copy_v3_v3(final_color, orig_color);
+        break;
+      case COLOR_FILTER_GREEN:
+        orig_color[1] = orig_color[1] + fade;
+        CLAMP(orig_color[1], 0.0f, 1.0f);
+        copy_v3_v3(final_color, orig_color);
+        break;
+      case COLOR_FILTER_BLUE:
+        orig_color[2] = orig_color[2] + fade;
+        CLAMP(orig_color[2], 0.0f, 1.0f);
+        copy_v3_v3(final_color, orig_color);
+        break;
+      case COLOR_FILTER_BRIGHTNESS:
+        CLAMP(fade, -1.0f, 1.0f);
+        brightness = fade;
+        contrast = 0;
+        delta = contrast / 2.0f;
+        gain = 1.0f - delta * 2.0f;
+        delta *= -1;
+        offset = gain * (brightness + delta);
+        for (int i = 0; i < 3; i++) {
+          final_color[i] = gain * orig_color[i] + offset;
+          CLAMP(final_color[i], 0.0f, 1.0f);
+        }
+        break;
+      case COLOR_FILTER_CONTRAST:
+        CLAMP(fade, -1.0f, 1.0f);
+        brightness = 0;
+        contrast = fade;
+        delta = contrast / 2.0f;
+        gain = 1.0f - delta * 2.0f;
+        if (contrast > 0) {
+          gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
+          offset = gain * (brightness - delta);
+        }
+        else {
+          delta *= -1;
+          offset = gain * (brightness + delta);
+        }
+        for (int i = 0; i < 3; i++) {
+          final_color[i] = gain * orig_color[i] + offset;
+          CLAMP(final_color[i], 0.0f, 1.0f);
+        }
+        break;
+      case COLOR_FILTER_SMOOTH: {
+        CLAMP(fade, -1.0f, 1.0f);
+        float smooth_color[4];
+        SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+        blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
+        break;
+      }
+    }
+
+    copy_v3_v3(vd.col, final_color);
+
+    if (vd.mvert) {
+      vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+    }
+  }
+  BKE_pbvh_vertex_iter_end;
+  BKE_pbvh_node_mark_update_color(data->nodes[n]);
+}
+
+static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+  ARegion *ar = CTX_wm_region(C);
+  Object *ob = CTX_data_active_object(C);
+  SculptSession *ss = ob->sculpt;
+  Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+  const int mode = RNA_enum_get(op->ptr, "type");
+  float filter_strength = RNA_float_get(op->ptr, "strength");
+
+  if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
+    SCULPT_undo_push_end();
+    SCULPT_filter_cache_free(ss);
+    SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR);
+    return OPERATOR_FINISHED;
+  }
+
+  if (event->type != MOUSEMOVE) {
+    return OPERATOR_RUNNING_MODAL;
+  }
+
+  float len = event->prevclickx - event->mval[0];
+  filter_strength = filter_strength * -len * 0.001f;
+
+  SculptThreadedTaskData data = {
+      .sd = sd,
+      .ob = ob,
+      .nodes = ss->filter_cache->nodes,
+      .filter_type = mode,
+      .filter_strength = filter_strength,
+  };
+
+  TaskParallelSettings settings;
+  BLI_parallel_range_settings_defaults(&settings);
+
+  BKE_pbvh_parallel_range_settings(
+      &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode);
+  BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, color_filter_task_cb, &settings);
+
+  ED_region_tag_redraw(ar);
+  WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+  return OPERATOR_RUNNING_MODAL;
+}
+
+static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+  Object *ob = CTX_data_active_object(C);
+  Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+  Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+  SculptSession *ss = ob->sculpt;
+  int mode = RNA_enum_get(op->ptr, "type");
+  PBVH *pbvh = ob->sculpt->pbvh;
+
+  /* Disable for multires and dyntopo for now */
+  if (!ss->pbvh) {
+    return OPERATOR_CANCELLED;
+  }
+  if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
+    return OPERATOR_CANCELLED;
+  }
+
+  if (!ss->vcol) {
+    return OPERATOR_CANCELLED;
+  }
+
+  SCULPT_undo_push_begin("color filter");
+
+  bool needs_pmap = mode == COLOR_FILTER_SMOOTH;
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, true);
+
+  if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
+    return OPERATOR_CANCELLED;
+  }
+
+  SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COLOR);
+
+  WM_event_add_modal_handler(C, op);
+  return OPERATOR_RUNNING_MODAL;
+}
+
+void SCULPT_OT_color_filter(struct wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Filter color";
+  ot->idname = "SCULPT_OT_color_filter";
+  ot->description = "Applies a filter to modify the current sculpt vertex colors";
+
+  /* api callbacks */
+  ot->invoke = sculpt_color_filter_invoke;
+  ot->modal = sculpt_color_filter_modal;
+  ot->poll = SCULPT_mode_poll;
+
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+  /* rna */
+  RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
+  RNA_def_float(
+      ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+}
index d2a683461a766508d63cbdbf8b771dc9d74ddfa1..f5d33d44350b06c71efcfe8423f072a7134c7ac9 100644 (file)
@@ -202,7 +202,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
   int totnode;
   int filter_type = RNA_enum_get(op->ptr, "filter_type");
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   SCULPT_vertex_random_access_init(ss);
 
@@ -432,7 +432,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
   Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
   int totnode;
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   SCULPT_vertex_random_access_init(ss);
 
index fd0f67f040a31270856cdce1852bb59875af08c7..468945e6c420a31abb99464183a595bb6c357233 100644 (file)
@@ -70,10 +70,10 @@ static void filter_cache_init_task_cb(void *__restrict userdata,
   SculptThreadedTaskData *data = userdata;
   PBVHNode *node = data->nodes[i];
 
-  SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
+  SCULPT_undo_push_node(data->ob, node, data->filter_undo_type);
 }
 
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
+void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
 {
   SculptSession *ss = ob->sculpt;
   PBVH *pbvh = ob->sculpt->pbvh;
@@ -110,6 +110,7 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd)
       .sd = sd,
       .ob = ob,
       .nodes = ss->filter_cache->nodes,
+      .filter_undo_type = undo_type,
   };
 
   TaskParallelSettings settings;
@@ -475,7 +476,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent *
   SCULPT_vertex_random_access_init(ss);
 
   bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
 
   SculptThreadedTaskData data = {
       .sd = sd,
@@ -542,7 +543,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
   SCULPT_vertex_random_access_init(ss);
 
   bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets);
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false);
 
   const int totvert = SCULPT_vertex_count_get(ss);
   if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_pmap && !ob->sculpt->pmap) {
@@ -551,7 +552,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
 
   SCULPT_undo_push_begin("Mesh filter");
 
-  SCULPT_filter_cache_init(ob, sd);
+  SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
 
   if (use_face_sets) {
     ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss);
index 0e27658e8480267594f368891336aa47415f3ebf..6d1315db7238ac0b62b2e5628d6deb38f267daa0 100644 (file)
@@ -27,6 +27,7 @@
 #include "DNA_brush_types.h"
 #include "DNA_key_types.h"
 #include "DNA_listBase.h"
+#include "DNA_meshdata_types.h"
 #include "DNA_vec_types.h"
 
 #include "BLI_bitmap.h"
@@ -56,6 +57,7 @@ typedef enum SculptUpdateType {
   SCULPT_UPDATE_COORDS = 1 << 0,
   SCULPT_UPDATE_MASK = 1 << 1,
   SCULPT_UPDATE_VISIBILITY = 1 << 2,
+  SCULPT_UPDATE_COLOR = 1 << 3,
 } SculptUpdateType;
 
 void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags);
@@ -92,6 +94,7 @@ int SCULPT_vertex_count_get(struct SculptSession *ss);
 const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
 void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
 float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
+const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
 
 #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
 typedef struct SculptVertexNeighborIter {
@@ -179,15 +182,20 @@ typedef struct {
   float (*coords)[3];
   short (*normals)[3];
   const float *vmasks;
+  float (*colors)[4];
 
   /* Original coordinate, normal, and mask. */
   const float *co;
   const short *no;
   float mask;
+  const float *col;
 } SculptOrigVertData;
 
 void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
 void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
+                                      Object *ob,
+                                      struct SculptUndoNode *unode);
 
 /* Utils. */
 void SCULPT_calc_brush_plane(struct Sculpt *sd,
@@ -305,7 +313,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
                                         float *automask_factor);
 
 /* Filters. */
-void SCULPT_filter_cache_init(Object *ob, Sculpt *sd);
+void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type);
 void SCULPT_filter_cache_free(SculptSession *ss);
 
 void SCULPT_mask_filter_smooth_apply(
@@ -374,6 +382,12 @@ void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
 /* Draw Face Sets Brush. */
 void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
 
+/* Paint Brush. */
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
+/* Smear Brush. */
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
 /* Smooth Brush. */
 
 void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert);
@@ -383,6 +397,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct
 
 void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
 float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
 
 void SCULPT_smooth(Sculpt *sd,
                    Object *ob,
@@ -427,6 +442,7 @@ typedef enum {
   SCULPT_UNDO_DYNTOPO_SYMMETRIZE,
   SCULPT_UNDO_GEOMETRY,
   SCULPT_UNDO_FACE_SETS,
+  SCULPT_UNDO_COLOR,
 } SculptUndoType;
 
 /* Storage of geometry for the undo node.
@@ -457,6 +473,7 @@ typedef struct SculptUndoNode {
   float (*co)[3];
   float (*orig_co)[3];
   short (*no)[3];
+  float (*col)[4];
   float *mask;
   int totvert;
 
@@ -568,6 +585,8 @@ typedef struct SculptThreadedTaskData {
 
   bool any_vertex_sampled;
 
+  float *wet_mix_sampled_color;
+
   float *prev_mask;
 
   float *pose_factor;
@@ -601,6 +620,7 @@ typedef struct SculptThreadedTaskData {
   bool dirty_mask_dirty_only;
 
   int face_set;
+  int filter_undo_type;
 
   ThreadMutex mutex;
 
@@ -720,6 +740,8 @@ typedef struct StrokeCache {
   float bstrength;
   float normal_weight; /* from brush (with optional override) */
 
+  float (*prev_colors)[4];
+
   /* The rest is temporary storage that isn't saved as a property */
 
   bool first_time; /* Beginning of stroke may do some things special */
@@ -813,6 +835,9 @@ typedef struct StrokeCache {
   float stroke_local_mat[4][4];
   float multiplane_scrape_angle;
 
+  float wet_mix_prev_color[4];
+  float density_seed;
+
   rcti previous_r; /* previous redraw rectangle */
   rcti current_r;  /* current redraw rectangle */
 
@@ -901,6 +926,9 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot);
 /* Cloth Filter. */
 void SCULPT_OT_cloth_filter(struct wmOperatorType *ot);
 
+/* Color Filter. */
+void SCULPT_OT_color_filter(struct wmOperatorType *ot);
+
 /* Mask filter and Dirty Mask. */
 void SCULPT_OT_mask_filter(struct wmOperatorType *ot);
 void SCULPT_OT_dirty_mask(struct wmOperatorType *ot);
index cbb198e14a3df66d02b370c2b168a0fdeb454592..8868eae47d84743e7678c6496cd0023f12c411cc 100644 (file)
@@ -206,7 +206,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
       (event->type == EVT_PADENTER && event->val == KM_PRESS)) {
 
     /* Smooth iterations. */
-    BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+    BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
     const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
     SCULPT_mask_filter_smooth_apply(
         sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations);
@@ -365,7 +365,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
 
   SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
 
   int vertex_count = SCULPT_vertex_count_get(ss);
 
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 (file)
index 0000000..1ae4083
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+
+#include "IMB_imbuf.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+static void do_color_smooth_task_cb_exec(void *__restrict userdata,
+                                         const int n,
+                                         const TaskParallelTLS *__restrict tls)
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+  const Brush *brush = data->brush;
+  const float bstrength = ss->cache->bstrength;
+
+  PBVHVertexIter vd;
+
+  SculptBrushTest test;
+  SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+      ss, &test, data->brush->falloff_shape);
+  const int thread_id = BLI_task_parallel_thread_id(tls);
+
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+      const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+                                                                  brush,
+                                                                  vd.co,
+                                                                  sqrtf(test.dist),
+                                                                  vd.no,
+                                                                  vd.fno,
+                                                                  vd.mask ? *vd.mask : 0.0f,
+                                                                  vd.index,
+                                                                  thread_id);
+
+      float smooth_color[4];
+      SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+      blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade);
+
+      if (vd.mvert) {
+        vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+      }
+    }
+  }
+  BKE_pbvh_vertex_iter_end;
+}
+
+static void do_paint_brush_task_cb_ex(void *__restrict userdata,
+                                      const int n,
+                                      const TaskParallelTLS *__restrict tls)
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+  const Brush *brush = data->brush;
+  const float bstrength = fabsf(ss->cache->bstrength);
+
+  PBVHVertexIter vd;
+  PBVHColorBufferNode *color_buffer;
+
+  SculptOrigVertData orig_data;
+  SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+  color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
+
+  SculptBrushTest test;
+  SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+      ss, &test, data->brush->falloff_shape);
+  const int thread_id = BLI_task_parallel_thread_id(tls);
+
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    SCULPT_orig_vert_data_update(&orig_data, &vd);
+
+    bool affect_vertex = false;
+    float distance_to_stroke_location = 0.0f;
+    if (brush->tip_roundness < 1.0f) {
+      affect_vertex = SCULPT_brush_test_cube(&test, vd.co, data->mat, brush->tip_roundness);
+      distance_to_stroke_location = ss->cache->radius * test.dist;
+    }
+    else {
+      affect_vertex = sculpt_brush_test_sq_fn(&test, vd.co);
+      distance_to_stroke_location = sqrtf(test.dist);
+    }
+
+    if (affect_vertex) {
+      float fade = bstrength * SCULPT_brush_strength_factor(ss,
+                                                            brush,
+                                                            vd.co,
+                                                            distance_to_stroke_location,
+                                                            vd.no,
+                                                            vd.fno,
+                                                            vd.mask ? *vd.mask : 0.0f,
+                                                            vd.index,
+                                                            thread_id);
+
+      /* Density. */
+      float noise = 1.0f;
+      const float density = brush->density;
+      if (density < 1.0f) {
+        const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index);
+        if (hash_noise > density) {
+          noise = density * hash_noise;
+          fade = fade * noise;
+        }
+      }
+
+      /* Brush paint color, brush test falloff and flow. */
+      float paint_color[4] = {brush->rgb[0], brush->rgb[1], brush->rgb[2], 1.0f};
+      float wet_mix_color[4];
+      float buffer_color[4];
+
+      mul_v4_fl(paint_color, fade * brush->flow);
+      mul_v4_v4fl(wet_mix_color, data->wet_mix_sampled_color, fade * brush->flow);
+
+      /* Interpolate with the wet_mix color for wet paint mixing. */
+      blend_color_interpolate_float(paint_color, paint_color, wet_mix_color, brush->wet_mix);
+      blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color);
+
+      /* Final mix over the original color using brush alpha. */
+      mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha);
+
+      IMB_blend_color_float(vd.col, orig_data.col, buffer_color, brush->blend);
+    }
+    CLAMP4(vd.col, 0.0f, 1.0f);
+
+    if (vd.mvert) {
+      vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+    }
+  }
+  BKE_pbvh_vertex_iter_end;
+}
+
+typedef struct SampleWetPaintTLSData {
+  int tot_samples;
+  float color[4];
+} SampleWetPaintTLSData;
+
+static void do_sample_wet_paint_task_cb(void *__restrict userdata,
+                                        const int n,
+                                        const TaskParallelTLS *__restrict tls)
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+  SampleWetPaintTLSData *swptd = tls->userdata_chunk;
+  PBVHVertexIter vd;
+
+  SculptBrushTest test;
+  SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+      ss, &test, data->brush->falloff_shape);
+
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+      add_v4_v4(swptd->color, vd.col);
+      swptd->tot_samples++;
+    }
+  }
+  BKE_pbvh_vertex_iter_end;
+}
+
+static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata),
+                                    void *__restrict chunk_join,
+                                    void *__restrict chunk)
+{
+  SampleWetPaintTLSData *join = chunk_join;
+  SampleWetPaintTLSData *swptd = chunk;
+
+  join->tot_samples += swptd->tot_samples;
+  add_v4_v4(join->color, swptd->color);
+}
+
+void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+  Brush *brush = BKE_paint_brush(&sd->paint);
+  SculptSession *ss = ob->sculpt;
+
+  if (!ss->vcol) {
+    return;
+  }
+
+  if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+    ss->cache->density_seed = BLI_hash_int_01(ss->cache->location[0] * 1000);
+    return;
+  }
+
+  BKE_curvemapping_initialize(brush->curve);
+
+  float area_no[3];
+  float mat[4][4];
+  float scale[4][4];
+  float tmat[4][4];
+
+  /* If the brush is round the tip does not need to be aligned to the surface, so this saves a
+   * whole iteration over the affected nodes. */
+  if (brush->tip_roundness < 1.0f) {
+    SCULPT_calc_area_normal(sd, ob, nodes, totnode, area_no);
+
+    cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
+    mat[0][3] = 0;
+    cross_v3_v3v3(mat[1], area_no, mat[0]);
+    mat[1][3] = 0;
+    copy_v3_v3(mat[2], area_no);
+    mat[2][3] = 0;
+    copy_v3_v3(mat[3], ss->cache->location);
+    mat[3][3] = 1;
+    normalize_m4(mat);
+
+    scale_m4_fl(scale, ss->cache->radius);
+    mul_m4_m4m4(tmat, mat, scale);
+    mul_v3_fl(tmat[1], brush->tip_scale_x);
+    invert_m4_m4(mat, tmat);
+    if (is_zero_m4(mat)) {
+      return;
+    }
+  }
+
+  /* Smooth colors mode. */
+  if (ss->cache->alt_smooth) {
+    SculptThreadedTaskData data = {
+        .sd = sd,
+        .ob = ob,
+        .brush = brush,
+        .nodes = nodes,
+        .mat = mat,
+    };
+
+    TaskParallelSettings settings;
+    BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+    BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
+    return;
+  }
+
+  /* Regular Paint mode. */
+
+  /* Wet paint color sampling. */
+  float wet_color[4] = {0.0f};
+  if (brush->wet_mix > 0.0f) {
+    SculptThreadedTaskData task_data = {
+        .sd = sd,
+        .ob = ob,
+        .nodes = nodes,
+        .brush = brush,
+    };
+
+    SampleWetPaintTLSData swptd;
+    swptd.tot_samples = 0;
+    zero_v4(swptd.color);
+
+    TaskParallelSettings settings_sample;
+    BKE_pbvh_parallel_range_settings(&settings_sample, (sd->flags & SCULPT_USE_OPENMP), totnode);
+    settings_sample.func_reduce = sample_wet_paint_reduce;
+    settings_sample.userdata_chunk = &swptd;
+    settings_sample.userdata_chunk_size = sizeof(SampleWetPaintTLSData);
+    BLI_task_parallel_range(0, totnode, &task_data, do_sample_wet_paint_task_cb, &settings_sample);
+
+    if (swptd.tot_samples > 0 && is_finite_v4(swptd.color)) {
+      copy_v4_v4(wet_color, swptd.color);
+      mul_v4_fl(wet_color, 1.0f / (float)swptd.tot_samples);
+      CLAMP4(wet_color, 0.0f, 1.0f);
+
+      if (ss->cache->first_time) {
+        copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
+      }
+      blend_color_interpolate_float(
+          wet_color, wet_color, ss->cache->wet_mix_prev_color, brush->wet_persistence);
+      copy_v4_v4(ss->cache->wet_mix_prev_color, wet_color);
+      CLAMP4(ss->cache->wet_mix_prev_color, 0.0f, 1.0f);
+    }
+  }
+
+  /* Threaded loop over nodes. */
+  SculptThreadedTaskData data = {
+      .sd = sd,
+      .ob = ob,
+      .brush = brush,
+      .nodes = nodes,
+      .wet_mix_sampled_color = wet_color,
+      .mat = mat,
+  };
+
+  TaskParallelSettings settings;
+  BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+  BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings);
+}
+
+static void do_smear_brush_task_cb_exec(void *__restrict userdata,
+                                        const int n,
+                                        const TaskParallelTLS *__restrict tls)
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+  const Brush *brush = data->brush;
+  const float bstrength = ss->cache->bstrength;
+
+  PBVHVertexIter vd;
+
+  SculptBrushTest test;
+  SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+      ss, &test, data->brush->falloff_shape);
+  const int thread_id = BLI_task_parallel_thread_id(tls);
+
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+      const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+                                                                  brush,
+                                                                  vd.co,
+                                                                  sqrtf(test.dist),
+                                                                  vd.no,
+                                                                  vd.fno,
+                                                                  vd.mask ? *vd.mask : 0.0f,
+                                                                  vd.index,
+                                                                  thread_id);
+
+      float current_disp[3];
+      float current_disp_norm[3];
+      float interp_color[4];
+      copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]);
+
+      sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+      normalize_v3_v3(current_disp_norm, current_disp);
+      mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+      SculptVertexNeighborIter ni;
+      SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+        float vertex_disp[3];
+        float vertex_disp_norm[3];
+        sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+        const float *neighbor_color = ss->cache->prev_colors[ni.index];
+        normalize_v3_v3(vertex_disp_norm, vertex_disp);
+        if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) {
+          const float color_interp = clamp_f(
+              -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
+          float color_mix[4];
+          copy_v4_v4(color_mix, neighbor_color);
+          mul_v4_fl(color_mix, color_interp * fade);
+          blend_color_mix_float(interp_color, interp_color, color_mix);
+        }
+      }
+      SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+      blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade);
+
+      if (vd.mvert) {
+        vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+      }
+    }
+  }
+  BKE_pbvh_vertex_iter_end;
+}
+
+static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
+                                                    const int n,
+                                                    const TaskParallelTLS *__restrict UNUSED(tls))
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+
+  PBVHVertexIter vd;
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index));
+  }
+  BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+  Brush *brush = BKE_paint_brush(&sd->paint);
+  SculptSession *ss = ob->sculpt;
+
+  if (!ss->vcol) {
+    return;
+  }
+
+  const int totvert = SCULPT_vertex_count_get(ss);
+
+  if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0) {
+    if (!ss->cache->prev_colors) {
+      ss->cache->prev_colors = MEM_callocN(sizeof(float) * 4 * totvert, "prev colors");
+      for (int i = 0; i < totvert; i++) {
+        copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i));
+      }
+    }
+  }
+
+  BKE_curvemapping_initialize(brush->curve);
+
+  SculptThreadedTaskData data = {
+      .sd = sd,
+      .ob = ob,
+      .brush = brush,
+      .nodes = nodes,
+  };
+
+  TaskParallelSettings settings;
+  BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+
+  /* Smooth colors mode. */
+  if (ss->cache->alt_smooth) {
+    BLI_task_parallel_range(0, totnode, &data, do_color_smooth_task_cb_exec, &settings);
+  }
+  else {
+    /* Smear mode. */
+    BLI_task_parallel_range(0, totnode, &data, do_smear_store_prev_colors_task_cb_exec, &settings);
+    BLI_task_parallel_range(0, totnode, &data, do_smear_brush_task_cb_exec, &settings);
+  }
+}
index 17451cb40ae87710830043eebae6a434883e08b2..02c7b65dcb7d921ac84e9a8188753cd75b91b97e 100644 (file)
@@ -226,6 +226,26 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
   }
 }
 
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+{
+  float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+  int total = 0;
+
+  SculptVertexNeighborIter ni;
+  SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+    add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+    total++;
+  }
+  SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+  if (total > 0) {
+    mul_v4_v4fl(result, avg, 1.0f / (float)total);
+  }
+  else {
+    copy_v4_v4(result, SCULPT_vertex_color_get(ss, index));
+  }
+}
+
 static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata,
                                             const int n,
                                             const TaskParallelTLS *__restrict tls)
index 59a4695ce18bcf6371cae12ff5e9f94407eca911..914b1d67a6d9bed6a43106a129d6a45eaf89467b 100644 (file)
@@ -71,12 +71,12 @@ void ED_sculpt_init_transform(struct bContext *C)
   copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
 
   SCULPT_undo_push_begin("Transform");
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
 
   ss->pivot_rot[3] = 1.0f;
 
   SCULPT_vertex_random_access_init(ss);
-  SCULPT_filter_cache_init(ob, sd);
+  SCULPT_filter_cache_init(ob, sd, SCULPT_UNDO_COORDS);
 }
 
 static void sculpt_transform_task_cb(void *__restrict userdata,
@@ -127,7 +127,7 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
   const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
 
   SCULPT_vertex_random_access_init(ss);
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
 
   SculptThreadedTaskData data = {
       .sd = sd,
@@ -253,7 +253,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
 
   int mode = RNA_enum_get(op->ptr, "mode");
 
-  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
+  BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
 
   /* Pivot to center. */
   if (mode == SCULPT_PIVOT_POSITION_ORIGIN) {
index d21552efafe3be3034ff32315b83cf4cf910658d..c8bf901b2ef479d182f2bbaa7be426f95fd92851 100644 (file)
@@ -205,7 +205,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
       if (kb) {
         ob->shapenr = BLI_findindex(&key->block, kb) + 1;
 
-        BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
+        BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
         WM_event_add_notifier(C, NC_OBJECT | ND_DATA, ob);
       }
       else {
@@ -326,6 +326,29 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
   return true;
 }
 
+static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
+{
+  ViewLayer *view_layer = CTX_data_view_layer(C);
+  Object *ob = OBACT(view_layer);
+  SculptSession *ss = ob->sculpt;
+  MVert *mvert;
+  MPropCol *vcol;
+  int *index, i;
+
+  if (unode->maxvert) {
+    /* regular mesh restore */
+    index = unode->index;
+    mvert = ss->mvert;
+    vcol = ss->vcol;
+
+    for (i = 0; i < unode->totvert; i++) {
+      copy_v4_v4(vcol[index[i]].color, unode->col[i]);
+      mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+    }
+  }
+  return true;
+}
+
 static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
 {
   ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -633,7 +656,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
       rebuild = true;
       BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild);
 
-      BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask);
+      BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false);
 
       SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
 
@@ -659,7 +682,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
      * ensure object is updated after the node is handled. */
     const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first;
     if (first_unode->type != SCULPT_UNDO_GEOMETRY) {
-      BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+      BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
     }
 
     if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) {
@@ -712,10 +735,15 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
         break;
       case SCULPT_UNDO_FACE_SETS:
         break;
+      case SCULPT_UNDO_COLOR:
+        if (sculpt_undo_restore_color(C, unode)) {
+          update = true;
+        }
+        break;
 
       case SCULPT_UNDO_GEOMETRY:
         sculpt_undo_geometry_restore(unode, ob);
-        BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+        BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false);
         break;
 
       case SCULPT_UNDO_DYNTOPO_BEGIN:
@@ -997,6 +1025,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
 
       usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
 
+      break;
+    case SCULPT_UNDO_COLOR:
+      unode->col = MEM_callocN(sizeof(MPropCol) * allvert, "SculptUndoNode.col");
+
+      usculpt->undo_size += (sizeof(MPropCol) * sizeof(int)) * allvert;
+
       break;
     case SCULPT_UNDO_DYNTOPO_BEGIN:
     case SCULPT_UNDO_DYNTOPO_END:
@@ -1083,6 +1117,18 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode)
   BKE_pbvh_vertex_iter_end;
 }
 
+static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode)
+{
+  SculptSession *ss = ob->sculpt;
+  PBVHVertexIter vd;
+
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL)
+  {
+    copy_v4_v4(unode->col[vd.i], vd.col);
+  }
+  BKE_pbvh_vertex_iter_end;
+}
+
 static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode)
 {
   if (!unode->geometry_original.is_initialized) {
@@ -1203,6 +1249,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
       case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
       case SCULPT_UNDO_GEOMETRY:
       case SCULPT_UNDO_FACE_SETS:
+      case SCULPT_UNDO_COLOR:
         break;
     }
   }
@@ -1272,6 +1319,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
     case SCULPT_UNDO_MASK:
       sculpt_undo_store_mask(ob, unode);
       break;
+    case SCULPT_UNDO_COLOR:
+      sculpt_undo_store_color(ob, unode);
+      break;
     case SCULPT_UNDO_DYNTOPO_BEGIN:
     case SCULPT_UNDO_DYNTOPO_END:
     case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
index ab50158c8bc723456c55b370b285cb7558af98cf..c3ebbeb58898ab779877ef5d6c5b2229d9488ed2 100644 (file)
@@ -1014,7 +1014,8 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer
   PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
   if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) {
     PointerRNA dataptr = RNA_pointer_get(&obptr, "data");
-    uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL);
+    uiItemPointerR(
+        layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL);
   }
   else {
     uiItemL(layout, "No mesh in active object.", ICON_ERROR);
index fac378ae10439c0f8b7f04ad6636fd7b98eca510..b3165c9fc786f2858989245d3ffae9db505ee5b7 100644 (file)
@@ -2376,7 +2376,7 @@ void ED_view3d_datamask(const bContext *C,
 {
   if (ELEM(v3d->shading.type, OB_TEXTURE, OB_MATERIAL, OB_RENDER)) {
     r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
-    r_cddata_masks->vmask |= CD_MASK_ORCO;
+    r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
   }
   else if (v3d->shading.type == OB_SOLID) {
     if (v3d->shading.color_type == V3D_SHADING_TEXTURE_COLOR) {
@@ -2384,6 +2384,7 @@ void ED_view3d_datamask(const bContext *C,
     }
     if (v3d->shading.color_type == V3D_SHADING_VERTEX_COLOR) {
       r_cddata_masks->lmask |= CD_MASK_MLOOPCOL;
+      r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
     }
   }
 
index ab16bfc43c4de5d7879e9c8fee13de9c4ca3a468..41a29a4d45dd20bba16ff22a9944b3fe19ba6520 100644 (file)
@@ -37,6 +37,7 @@ struct DMFlagMat;
 struct GSet;
 struct MLoop;
 struct MLoopCol;
+struct MPropCol;
 struct MLoopTri;
 struct MPoly;
 struct MVert;
@@ -82,6 +83,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
                                   const int *sculpt_face_sets,
                                   const int face_sets_color_seed,
                                   const int face_sets_color_default,
+                                  const struct MPropCol *vtcol,
                                   const int update_flags);
 
 void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
index cef90d57ef5e9ded3bbd93308e0212000583349f..53ef305f6291d16b29300ef23cab18a037ffba7d 100644 (file)
@@ -34,6 +34,7 @@
 #include "BLI_hash.h"
 #include "BLI_math.h"
 #include "BLI_math_color.h"
+#include "BLI_math_color_blend.h"
 #include "BLI_utildefines.h"
 
 #include "DNA_meshdata_types.h"
@@ -227,12 +228,13 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
                                   const int *sculpt_face_sets,
                                   const int face_sets_color_seed,
                                   const int face_sets_color_default,
+                                  const MPropCol *vtcol,
                                   const int update_flags)
 {
   const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
-  const bool show_vcol = vcol && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
   const bool show_face_sets = sculpt_face_sets &&
                               (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0;
+  const bool show_vcol = (vcol || vtcol) && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
   bool empty_mask = true;
   bool default_face_set = true;
 
@@ -244,8 +246,8 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
       GPUVertBufRaw pos_step = {0};
       GPUVertBufRaw nor_step = {0};
       GPUVertBufRaw msk_step = {0};
-      GPUVertBufRaw col_step = {0};
       GPUVertBufRaw fset_step = {0};
+      GPUVertBufRaw col_step = {0};
 
       GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.pos, &pos_step);
       GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.nor, &nor_step);
@@ -312,25 +314,34 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
 
           *(uchar *)GPU_vertbuf_raw_step(&msk_step) = cmask;
           empty_mask = empty_mask && (cmask == 0);
-
+          /* Vertex Colors. */
           if (show_vcol) {
-            const uint loop_index = lt->tri[j];
-            const MLoopCol *mcol = &vcol[loop_index];
-            ushort scol[4];
-            scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
-            scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
-            scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
-            scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f));
-            memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
+            ushort scol[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+            if (vtcol) {
+              scol[0] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[0]);
+              scol[1] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[1]);
+              scol[2] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[2]);
+              scol[3] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[3]);
+              memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
+            }
+            else {
+              const uint loop_index = lt->tri[j];
+              const MLoopCol *mcol = &vcol[loop_index];
+              scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
+              scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
+              scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
+              scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f));
+              memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
+            }
           }
-
-          /* Face Sets. */
-          memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar) * 3);
         }
-      }
 
-      gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
+        /* Face Sets. */
+        memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar) * 3);
+      }
     }
+
+    gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
   }
 
   /* Get material index from the first face of this buffer. */
index 7490dbe5cdc117bc5c5b146b03f00ac11f9ed322..a1c69af47505de49ffc611a1413eecae31955829 100644 (file)
@@ -439,6 +439,21 @@ typedef struct Brush {
   float rgb[3];
   /** Opacity. */
   float alpha;
+  /** Hardness */
+  float hardness;
+  /** Flow */
+  float flow;
+  /** Wet Mix */
+  float wet_mix;
+  float wet_persistence;
+  /** Density */
+  float density;
+
+  /** Tip Shape */
+  /* Factor that controls the shape of the brush tip by rounding the corners of a square. */
+  /* 0.0 value produces a square, 1.0 produces a circle. */
+  float tip_roundness;
+  float tip_scale_x;
 
   /** Background color. */
   float secondary_rgb[3];
@@ -459,7 +474,7 @@ typedef struct Brush {
   /** Source for fill tool color gradient application. */
   char gradient_fill_mode;
 
-  char _pad0[1];
+  char _pad0[5];
 
   /** Projection shape (sphere, circle). */
   char falloff_shape;
@@ -503,16 +518,11 @@ typedef struct Brush {
   float texture_sample_bias;
 
   int curve_preset;
-  float hardness;
 
   /* automasking */
   int automasking_flags;
   int automasking_boundary_edges_propagation_steps;
 
-  /* Factor that controls the shape of the brush tip by rounding the corners of a square. */
-  /* 0.0 value produces a square, 1.0 produces a circle. */
-  float tip_roundness;
-
   int elastic_deform_type;
   float elastic_deform_volume_preservation;
 
@@ -720,6 +730,8 @@ typedef enum eBrushSculptTool {
   SCULPT_TOOL_CLAY_THUMB = 25,
   SCULPT_TOOL_CLOTH = 26,
   SCULPT_TOOL_DRAW_FACE_SETS = 27,
+  SCULPT_TOOL_PAINT = 28,
+  SCULPT_TOOL_SMEAR = 29,
 } eBrushSculptTool;
 
 /* Brush.uv_sculpt_tool */
@@ -762,6 +774,8 @@ typedef enum eBrushUVSculptTool {
         SCULPT_TOOL_ELASTIC_DEFORM, \
         SCULPT_TOOL_POSE, \
         SCULPT_TOOL_DRAW_FACE_SETS, \
+        SCULPT_TOOL_PAINT, \
+        SCULPT_TOOL_SMEAR, \
 \
         /* These brushes could handle dynamic topology, \ \
          * but user feedback indicates it's better not to */ \
index e4999fd44640222bbd6b35b02419b25d00a1d4bd..c24bbccae1e90d78adbcebdd3e0a049e56c3ebb3 100644 (file)
@@ -148,10 +148,9 @@ typedef enum CustomDataType {
   CD_CUSTOMLOOPNORMAL = 41,
   CD_SCULPT_FACE_SETS = 42,
 
-  /* Hair and PointCloud */
   CD_LOCATION = 43,
-  CD_RADIUS = 44,
   CD_HAIRCURVE = 45,
+  CD_RADIUS = 44,
   CD_HAIRMAPPING = 46,
 
   CD_PROP_COLOR = 47,
@@ -205,7 +204,7 @@ typedef enum CustomDataType {
 #define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL)
 #define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL)
 #define CD_MASK_SCULPT_FACE_SETS (1LL << CD_SCULPT_FACE_SETS)
-#define CD_MASK_PROP_COLOR (1LL << CD_PROP_COLOR)
+#define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR)
 
 /** Data types that may be defined for all mesh elements types. */
 #define CD_MASK_GENERIC_DATA (CD_MASK_PROP_FLOAT | CD_MASK_PROP_INT32 | CD_MASK_PROP_STRING)
index acc020ec710ca9eab7f37a62fba473cb9db1ca0f..f802dd8c55b2716a2f39e5d717944291eb2209ff 100644 (file)
@@ -46,6 +46,7 @@ struct MLoopTri;
 struct MLoopUV;
 struct MPoly;
 struct MVert;
+struct MPropCol;
 struct Material;
 struct Mesh;
 struct Multires;
@@ -285,7 +286,7 @@ enum {
   ME_AUTOSMOOTH = 1 << 5,
   ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */
   ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */
-  ME_FLAG_UNUSED_8 = 1 << 8, /* cleared */
+  ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8,
   ME_DS_EXPAND = 1 << 9,
   ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10,
   ME_REMESH_SMOOTH_NORMALS = 1 << 11,
index 04deecde43e84e72925670c0faa14258093102ce..cc2ba3fb9996a0ec0c2ba359a166c1a0690e1f42 100644 (file)
@@ -345,7 +345,7 @@ typedef struct MLoopCol {
 } MLoopCol;
 
 typedef struct MPropCol {
-  float col[4];
+  float color[4];
 } MPropCol;
 
 /** Multi-Resolution loop data. */
index 0a26be3a75349b8e8d0ed95b131fa38486bd8e03..0b4f82ca263ad2d37ebefa42aed5271e2cf9c6db 100644 (file)
@@ -387,6 +387,8 @@ extern StructRNA RNA_MeshIntProperty;
 extern StructRNA RNA_MeshLoop;
 extern StructRNA RNA_MeshLoopColor;
 extern StructRNA RNA_MeshLoopColorLayer;
+extern StructRNA RNA_MeshVertColor;
+extern StructRNA RNA_MeshVertColorLayer;
 extern StructRNA RNA_MeshLoopTriangle;
 extern StructRNA RNA_MeshPolygon;
 extern StructRNA RNA_MeshSequenceCacheModifier;
index cc5cd97a8a0ef7aa0fa742b3a58fb615d99ae78c..59656b48cfe91c365fc9036a64d689de87a3bea1 100644 (file)
@@ -99,6 +99,8 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
     {SCULPT_TOOL_CLOTH, "CLOTH", ICON_BRUSH_SCULPT_DRAW, "Cloth", ""},
     {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""},
     {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""},
+    {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""},
+    {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""},
     {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""},
     {0, NULL, 0, NULL, NULL},
 };
@@ -314,7 +316,8 @@ static bool rna_BrushCapabilitiesSculpt_has_topology_rake_get(PointerRNA *ptr)
 static bool rna_BrushCapabilitiesSculpt_has_auto_smooth_get(PointerRNA *ptr)
 {
   Brush *br = (Brush *)ptr->data;
-  return !ELEM(br->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH);
+  return !ELEM(
+      br->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
 }
 
 static bool rna_BrushCapabilitiesSculpt_has_height_get(PointerRNA *ptr)
@@ -409,6 +412,12 @@ static bool rna_BrushCapabilitiesSculpt_has_sculpt_plane_get(PointerRNA *ptr)
                SCULPT_TOOL_SMOOTH);
 }
 
+static bool rna_BrushCapabilitiesSculpt_has_color_get(PointerRNA *ptr)
+{
+  Brush *br = (Brush *)ptr->data;
+  return ELEM(br->sculpt_tool, SCULPT_TOOL_PAINT);
+}
+
 static bool rna_BrushCapabilitiesSculpt_has_secondary_color_get(PointerRNA *ptr)
 {
   Brush *br = (Brush *)ptr->data;
@@ -1053,6 +1062,7 @@ static void rna_def_sculpt_capabilities(BlenderRNA *brna)
   SCULPT_TOOL_CAPABILITY(has_plane_offset, "Has Plane Offset");
   SCULPT_TOOL_CAPABILITY(has_random_texture_angle, "Has Random Texture Angle");
   SCULPT_TOOL_CAPABILITY(has_sculpt_plane, "Has Sculpt Plane");
+  SCULPT_TOOL_CAPABILITY(has_color, "Has Color");
   SCULPT_TOOL_CAPABILITY(has_secondary_color, "Has Secondary Color");
   SCULPT_TOOL_CAPABILITY(has_smooth_stroke, "Has Smooth Stroke");
   SCULPT_TOOL_CAPABILITY(has_space_attenuation, "Has Space Attenuation");
@@ -2221,6 +2231,46 @@ static void rna_def_brush(BlenderRNA *brna)
       prop, "Strength", "How powerful the effect of the brush is when applied");
   RNA_def_property_update(prop, 0, "rna_Brush_update");
 
+  prop = RNA_def_property(srna, "flow", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "flow");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+  RNA_def_property_ui_text(prop, "Flow", "Amount of paint that is applied per stroke sample");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+  prop = RNA_def_property(srna, "wet_mix", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "wet_mix");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+  RNA_def_property_ui_text(
+      prop, "Wet Mix", "Amount of paint that is picked from the surface into the brush color");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+  prop = RNA_def_property(srna, "wet_persistence", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "wet_persistence");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+  RNA_def_property_ui_text(
+      prop,
+      "Wet Persistence",
+      "Amount of wet paint that stays in the brush after applyig paint to the surface");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+  prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "density");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+  RNA_def_property_ui_text(
+      prop, "Density", "Amount of random elements that are going to be affected by the brush");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+  prop = RNA_def_property(srna, "tip_scale_x", PROP_FLOAT, PROP_FACTOR);
+  RNA_def_property_float_sdna(prop, NULL, "tip_scale_x");
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3);
+  RNA_def_property_ui_text(prop, "Tip Scale X", "Scale of the brush tip in the X axis");
+  RNA_def_property_update(prop, 0, "rna_Brush_update");
+
   prop = RNA_def_property(srna, "dash_ratio", PROP_FLOAT, PROP_FACTOR);
   RNA_def_property_float_sdna(prop, NULL, "dash_ratio");
   RNA_def_property_range(prop, 0.0f, 1.0f);
index 5f986c289645fcb95bc7e6b25a93d53a644c82cd..62318925a0b6ce5fd40f8a038a9017b7277e6c28 100644 (file)
@@ -758,6 +758,47 @@ static void rna_MeshLoopColorLayer_active_set(PointerRNA *ptr, bool value)
   rna_CustomDataLayer_active_set(ptr, rna_mesh_ldata(ptr), value, CD_MLOOPCOL, 0);
 }
 
+/* sculpt_vertex_color_layers */
+
+DEFINE_CUSTOMDATA_LAYER_COLLECTION(sculpt_vertex_color, vdata, CD_PROP_COLOR)
+DEFINE_CUSTOMDATA_LAYER_COLLECTION_ACTIVEITEM(
+    sculpt_vertex_color, vdata, CD_PROP_COLOR, active, MeshVertColorLayer)
+
+
+static void rna_MeshVertColorLayer_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+  Mesh *me = rna_mesh(ptr);
+  CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
+  rna_iterator_array_begin(
+      iter, layer->data, sizeof(MPropCol), (me->edit_mesh) ? 0 : me->totvert, 0, NULL);
+}
+
+static int rna_MeshVertColorLayer_data_length(PointerRNA *ptr)
+{
+  Mesh *me = rna_mesh(ptr);
+  return (me->edit_mesh) ? 0 : me->totvert;
+}
+
+static bool rna_MeshVertColorLayer_active_render_get(PointerRNA *ptr)
+{
+  return rna_CustomDataLayer_active_get(ptr, rna_mesh_vdata(ptr), CD_PROP_COLOR, 1);
+}
+
+static bool rna_MeshVertColorLayer_active_get(PointerRNA *ptr)
+{
+  return rna_CustomDataLayer_active_get(ptr, rna_mesh_vdata(ptr), CD_PROP_COLOR, 0);
+}
+
+static void rna_MeshVertColorLayer_active_render_set(PointerRNA *ptr, bool value)
+{
+  rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 1);
+}
+
+static void rna_MeshVertColorLayer_active_set(PointerRNA *ptr, bool value)
+{
+  rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 0);
+}
+
 static int rna_float_layer_check(CollectionPropertyIterator *UNUSED(iter), void *data)
 {
   CustomDataLayer *layer = (CustomDataLayer *)data;
@@ -1218,6 +1259,19 @@ static char *rna_MeshColor_path(PointerRNA *ptr)
   return rna_LoopCustomData_data_path(ptr, "vertex_colors", CD_MLOOPCOL);
 }
 
+static char *rna_MeshVertColorLayer_path(PointerRNA *ptr)
+{
+  CustomDataLayer *cdl = ptr->data;
+  char name_esc[sizeof(cdl->name) * 2];
+  BLI_strescape(name_esc, cdl->name, sizeof(name_esc));
+  return BLI_sprintfN("sculpt_vertex_colors[\"%s\"]", name_esc);
+}
+
+static char *rna_MeshVertColor_path(PointerRNA *ptr)
+{
+  return rna_VertCustomData_data_path(ptr, "sculpt_vertex_colors", CD_PROP_COLOR);
+}
+
 /**** Float Property Layer API ****/
 static char *rna_MeshVertexFloatPropertyLayer_path(PointerRNA *ptr)
 {
@@ -1439,6 +1493,33 @@ static void rna_Mesh_vertex_color_remove(struct Mesh *me,
   }
 }
 
+static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me,
+                                                   const char *name,
+                                                   const bool do_init)
+{
+  PointerRNA ptr;
+  CustomData *vdata;
+  CustomDataLayer *cdl = NULL;
+  int index = ED_mesh_sculpt_color_add(me, name, false, do_init);
+
+  if (index != -1) {
+    vdata = rna_mesh_vdata_helper(me);
+    cdl = &vdata->layers[CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, index)];
+  }
+
+  RNA_pointer_create(&me->id, &RNA_MeshVertColorLayer, cdl, &ptr);
+  return ptr;
+}
+
+static void rna_Mesh_sculpt_vertex_color_remove(struct Mesh *me,
+                                                ReportList *reports,
+                                                CustomDataLayer *layer)
+{
+  if (ED_mesh_sculpt_color_remove_named(me, layer->name) == false) {
+    BKE_reportf(reports, RPT_ERROR, "Sculpt vertex color '%s' not found", layer->name);
+  }
+}
+
 #  define DEFINE_CUSTOMDATA_PROPERTY_API( \
       elemname, datatype, cd_prop_type, cdata, countvar, layertype) \
     static PointerRNA rna_Mesh_##elemname##_##datatype##_property_new(struct Mesh *me, \
@@ -1510,10 +1591,6 @@ static void UNUSED_FUNCTION(rna_mesh_unused)(void)
   (void)rna_Mesh_uv_layer_render_index_get;
   (void)rna_Mesh_uv_layer_render_index_set;
   (void)rna_Mesh_uv_layer_render_set;
-  (void)rna_Mesh_vertex_color_render_get;
-  (void)rna_Mesh_vertex_color_render_index_get;
-  (void)rna_Mesh_vertex_color_render_index_set;
-  (void)rna_Mesh_vertex_color_render_set;
   (void)rna_Mesh_face_map_index_range;
   (void)rna_Mesh_face_map_active_index_set;
   (void)rna_Mesh_face_map_active_index_get;
@@ -2034,6 +2111,65 @@ static void rna_def_mloopcol(BlenderRNA *brna)
   RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
 }
 
+static void rna_def_MPropCol(BlenderRNA *brna)
+{
+  StructRNA *srna;
+  PropertyRNA *prop;
+
+  srna = RNA_def_struct(brna, "MeshVertColorLayer", NULL);
+  RNA_def_struct_ui_text(srna,
+                         "Mesh Sculpt Vertex Color Layer",
+                         "Layer of sculpt vertex colors in a Mesh data-block");
+  RNA_def_struct_sdna(srna, "CustomDataLayer");
+  RNA_def_struct_path_func(srna, "rna_MeshVertColorLayer_path");
+  RNA_def_struct_ui_icon(srna, ICON_GROUP_VCOL);
+
+  prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+  RNA_def_struct_name_property(srna, prop);
+  RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshVertexLayer_name_set");
+  RNA_def_property_ui_text(prop, "Name", "Name of Sculpt Vertex color layer");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+  prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_funcs(
+      prop, "rna_MeshVertColorLayer_active_get", "rna_MeshVertColorLayer_active_set");
+  RNA_def_property_ui_text(
+      prop, "Active", "Sets the sculpt vertex color layer as active for display and editing");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+  prop = RNA_def_property(srna, "active_render", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_sdna(prop, NULL, "active_rnd", 0);
+  RNA_def_property_boolean_funcs(prop,
+                                 "rna_MeshVertColorLayer_active_render_get",
+                                 "rna_MeshVertColorLayer_active_render_set");
+  RNA_def_property_ui_text(
+      prop, "Active Render", "Sets the sculpt vertex color layer as active for rendering");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+
+  prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
+  RNA_def_property_struct_type(prop, "MeshVertColor");
+  RNA_def_property_ui_text(prop, "Data", "");
+  RNA_def_property_collection_funcs(prop,
+                                    "rna_MeshVertColorLayer_data_begin",
+                                    "rna_iterator_array_next",
+                                    "rna_iterator_array_end",
+                                    "rna_iterator_array_get",
+                                    "rna_MeshVertColorLayer_data_length",
+                                    NULL,
+                                    NULL,
+                                    NULL);
+
+  srna = RNA_def_struct(brna, "MeshVertColor", NULL);
+  RNA_def_struct_sdna(srna, "MPropCol");
+  RNA_def_struct_ui_text(srna, "Mesh Sculpt Vertex Color", "Vertex colors in a Mesh");
+  RNA_def_struct_path_func(srna, "rna_MeshVertColor_path");
+
+  prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR);
+  RNA_def_property_array(prop, 4);
+  RNA_def_property_range(prop, 0.0f, 1.0f);
+  RNA_def_property_ui_text(prop, "Color", "");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
+}
 static void rna_def_mproperties(BlenderRNA *brna)
 {
   StructRNA *srna;
@@ -2373,6 +2509,60 @@ static void rna_def_loop_colors(BlenderRNA *brna, PropertyRNA *cprop)
   RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
 }
 
+static void rna_def_vert_colors(BlenderRNA *brna, PropertyRNA *cprop)
+{
+  StructRNA *srna;
+  PropertyRNA *prop;
+
+  FunctionRNA *func;
+  PropertyRNA *parm;
+
+  RNA_def_property_srna(cprop, "VertColors");
+  srna = RNA_def_struct(brna, "VertColors", NULL);
+  RNA_def_struct_sdna(srna, "Mesh");
+  RNA_def_struct_ui_text(srna, "Vert Colors", "Collection of sculpt vertex colors");
+
+  func = RNA_def_function(srna, "new", "rna_Mesh_sculpt_vertex_color_new");
+  RNA_def_function_ui_description(func, "Add a sculpt vertex color layer to Mesh");
+  RNA_def_string(func, "name", "Col", 0, "", "Sculpt Vertex color name");
+  RNA_def_boolean(func,
+                  "do_init",
+                  true,
+                  "",
+                  "Whether new layer's data should be initialized by copying current active one");
+  parm = RNA_def_pointer(func, "layer", "MeshVertColorLayer", "", "The newly created layer");
+  RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
+  RNA_def_function_return(func, parm);
+
+  func = RNA_def_function(srna, "remove", "rna_Mesh_sculpt_vertex_color_remove");
+  RNA_def_function_ui_description(func, "Remove a vertex color layer");
+  RNA_def_function_flag(func, FUNC_USE_REPORTS);
+  parm = RNA_def_pointer(func, "layer", "MeshVertColorLayer", "", "The layer to remove");
+  RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+  RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+  prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+  RNA_def_property_struct_type(prop, "MeshVertColorLayer");
+  RNA_def_property_pointer_funcs(prop,
+                                 "rna_Mesh_sculpt_vertex_color_active_get",
+                                 "rna_Mesh_sculpt_vertex_color_active_set",
+                                 NULL,
+                                 NULL);
+  RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
+  RNA_def_property_ui_text(
+      prop, "Active Sculpt Vertex Color Layer", "Active sculpt vertex color layer");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
+
+  prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+  RNA_def_property_int_funcs(prop,
+                             "rna_Mesh_sculpt_vertex_color_active_index_get",
+                             "rna_Mesh_sculpt_vertex_color_active_index_set",
+                             "rna_Mesh_sculpt_vertex_color_index_range");
+  RNA_def_property_ui_text(
+      prop, "Active Sculpt Vertex Color Index", "Active sculpt vertex color index");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_data_edit_active_color");
+}
+
 static void rna_def_uv_layers(BlenderRNA *brna, PropertyRNA *cprop)
 {
   StructRNA *srna;
@@ -2839,6 +3029,23 @@ static void rna_def_mesh(BlenderRNA *brna)
   RNA_def_property_ui_text(prop, "Vertex Colors", "All vertex colors");
   rna_def_loop_colors(brna, prop);
 
+  /* Sculpt Vertex colors */
+
+  prop = RNA_def_property(srna, "sculpt_vertex_colors", PROP_COLLECTION, PROP_NONE);
+  RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
+  RNA_def_property_collection_funcs(prop,
+                                    "rna_Mesh_sculpt_vertex_colors_begin",
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    "rna_Mesh_sculpt_vertex_colors_length",
+                                    NULL,
+                                    NULL,
+                                    NULL);
+  RNA_def_property_struct_type(prop, "MeshVertColorLayer");
+  RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "All vertex colors");
+  rna_def_vert_colors(brna, prop);
+
   /* TODO, edge customdata layers (bmesh py api can access already) */
   prop = RNA_def_property(srna, "vertex_layers_float", PROP_COLLECTION, PROP_NONE);
   RNA_def_property_collection_sdna(prop, NULL, "vdata.layers", "vdata.totlayer");
@@ -3033,6 +3240,13 @@ static void rna_def_mesh(BlenderRNA *brna)
       prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh");
   RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
 
+  prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS);
+  RNA_def_property_boolean_default(prop, false);
+  RNA_def_property_ui_text(
+      prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh");
+  RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
   prop = RNA_def_property(srna, "remesh_mode", PROP_ENUM, PROP_NONE);
   RNA_def_property_enum_sdna(prop, NULL, "remesh_mode");
   RNA_def_property_enum_items(prop, rna_enum_mesh_remesh_mode_items);
@@ -3183,6 +3397,7 @@ void RNA_def_mesh(BlenderRNA *brna)
   rna_def_mpolygon(brna);
   rna_def_mloopuv(brna);
   rna_def_mloopcol(brna);
+  rna_def_MPropCol(brna);
   rna_def_mproperties(brna);
   rna_def_face_map(brna);
 }