diff options
83 files changed, 2724 insertions, 573 deletions
diff --git a/release/datafiles/icons/brush.sculpt.paint.dat b/release/datafiles/icons/brush.sculpt.paint.dat Binary files differnew file mode 100644 index 00000000000..1cee34c0dee --- /dev/null +++ b/release/datafiles/icons/brush.sculpt.paint.dat diff --git a/release/datafiles/icons/brush.sculpt.smear.dat b/release/datafiles/icons/brush.sculpt.smear.dat Binary files differnew file mode 100644 index 00000000000..3efba0b4cb1 --- /dev/null +++ b/release/datafiles/icons/brush.sculpt.smear.dat diff --git a/release/datafiles/icons/ops.sculpt.color_filter.dat b/release/datafiles/icons/ops.sculpt.color_filter.dat Binary files differnew file mode 100644 index 00000000000..d589b15a124 --- /dev/null +++ b/release/datafiles/icons/ops.sculpt.color_filter.dat diff --git a/release/datafiles/icons/ops.sculpt.mask_by_color.dat b/release/datafiles/icons/ops.sculpt.mask_by_color.dat Binary files differnew file mode 100644 index 00000000000..637c47d2d84 --- /dev/null +++ b/release/datafiles/icons/ops.sculpt.mask_by_color.dat diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 0422f1afc58..399c4519635 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -7372,8 +7372,7 @@ def km_3d_view_tool_sculpt_mask_by_color(params): "3D View Tool: Sculpt, Mask by Color", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'ANY'}, None), - ("sculpt.mask_by_color", params.tool_tweak_event, None), + ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'CLICK'}, None) ]}, ) diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 8e5f6dba1ab..929953dd411 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -135,19 +135,6 @@ class MESH_UL_uvmaps(UIList): layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) - -class MESH_UL_vcols(UIList): - def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): - # assert(isinstance(item, (bpy.types.MeshTexturePolyLayer, bpy.types.MeshLoopColorLayer))) - if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.prop(item, "name", text="", emboss=False, icon='GROUP_VCOL') - icon = 'RESTRICT_RENDER_OFF' if item.active_render else 'RESTRICT_RENDER_ON' - layout.prop(item, "active_render", text="", icon=icon, emboss=False) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - - class MeshButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -433,64 +420,6 @@ class DATA_PT_uv_texture(MeshButtonsPanel, Panel): col.operator("mesh.uv_texture_add", icon='ADD', text="") col.operator("mesh.uv_texture_remove", icon='REMOVE', text="") - -class DATA_PT_vertex_colors(MeshButtonsPanel, Panel): - bl_label = "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", "vcols", me, "vertex_colors", me.vertex_colors, "active_index", rows=2) - - col = row.column(align=True) - col.operator("mesh.vertex_color_add", icon='ADD', text="") - 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'} - - @classmethod - def poll(cls, context): - return super().poll(context) and context.preferences.experimental.use_sculpt_vertex_colors - - 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'} @@ -514,8 +443,7 @@ 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") - if context.preferences.experimental.use_sculpt_vertex_colors: - col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") + col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Color Attributes") col.operator("object.voxel_remesh", text="Voxel Remesh") else: @@ -645,6 +573,79 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): layout.label(text="Name collisions: {}".format(", ".join(colliding_names)), icon='ERROR') +class MESH_UL_color_attributes(UIList): + display_domain_names = { + 'POINT': "Vertex", + 'EDGE': "Edge", + 'FACE': "Face", + 'CORNER': "Face Corner", + } + + def filter_items(self, context, data, property): + attrs = getattr(data, property) + ret = [] + idxs = [] + + for idx, item in enumerate(attrs): + skip = item.domain not in {"POINT", "CORNER"} + skip = skip or item.data_type not in {"FLOAT_COLOR", "BYTE_COLOR"} + + ret.append(self.bitflag_filter_item if not skip else 0) + idxs.append(idx) + + return ret, idxs + + def draw_item(self, _context, layout, data, attribute, _icon, _active_data, _active_propname, _index): + data_type = attribute.bl_rna.properties['data_type'].enum_items[attribute.data_type] + + domain_name = self.display_domain_names.get(attribute.domain, "") + + split = layout.split(factor=0.50) + split.emboss = 'NONE' + split.prop(attribute, "name", text="") + + active_render = _index == data.color_attributes.render_color_index + + props = split.operator("geometry.color_attribute_render_set", text="", icon = 'RESTRICT_RENDER_OFF'if \ + active_render else 'RESTRICT_RENDER_ON' + ) + + props.name = attribute.name + + sub = split.row() + sub.alignment = 'RIGHT' + sub.active = False + sub.label(text="%s â–¶ %s" % (domain_name, data_type.name)) + + +class DATA_PT_vertex_colors(DATA_PT_mesh_attributes, Panel): + bl_label = "Color Attributes" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + mesh = context.mesh + + layout = self.layout + row = layout.row() + + col = row.column() + col.template_list( + "MESH_UL_color_attributes", + "color_attributes", + mesh, + "color_attributes", + mesh.color_attributes, + "active_color_index", + rows=3, + ) + + col = row.column(align=True) + col.operator("geometry.color_attribute_add", icon='ADD', text="") + col.operator("geometry.color_attribute_remove", icon='REMOVE', text="") + + self.draw_attribute_warnings(context, layout) + classes = ( MESH_MT_vertex_group_context_menu, MESH_MT_shape_key_context_menu, @@ -653,14 +654,12 @@ classes = ( MESH_UL_fmaps, MESH_UL_shape_keys, MESH_UL_uvmaps, - MESH_UL_vcols, MESH_UL_attributes, DATA_PT_context_mesh, DATA_PT_vertex_groups, DATA_PT_shape_keys, DATA_PT_uv_texture, DATA_PT_vertex_colors, - DATA_PT_sculpt_vertex_colors, DATA_PT_face_maps, DATA_PT_mesh_attributes, DATA_PT_normals, @@ -668,6 +667,7 @@ classes = ( DATA_PT_remesh, DATA_PT_customdata, DATA_PT_custom_props_mesh, + MESH_UL_color_attributes, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py index 35b061ecb21..47c61984cbe 100644 --- a/release/scripts/startup/bl_ui/properties_material_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py @@ -30,8 +30,8 @@ class GPENCIL_MT_material_context_menu(Menu): layout.separator() - layout.operator("gpencil.material_to_vertex_color", text="Convert Materials to Vertex Color") - layout.operator("gpencil.extract_palette_vertex", text="Extract Palette from Vertex Color") + layout.operator("gpencil.material_to_vertex_color", text="Convert Materials to Color Attribute") + layout.operator("gpencil.extract_palette_vertex", text="Extract Palette from Color Attribute") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index fc621c5b51e..b8d30f06193 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1310,8 +1310,6 @@ class _defs_sculpt: exclude_filter = {} # Use 'bpy.context' instead of 'context' since it can be None. prefs = bpy.context.preferences - if not prefs.experimental.use_sculpt_vertex_colors: - exclude_filter = {'PAINT', 'SMEAR'} return generate_from_enum_ex( context, @@ -2954,24 +2952,11 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sculpt.trim_lasso, ), _defs_sculpt.project_line, + _defs_sculpt.mask_by_color, None, _defs_sculpt.mesh_filter, _defs_sculpt.cloth_filter, - lambda context: ( - (_defs_sculpt.color_filter,) - if context is None or ( - context.preferences.view.show_developer_ui and - context.preferences.experimental.use_sculpt_vertex_colors) - else () - ), - None, - lambda context: ( - (_defs_sculpt.mask_by_color,) - if context is None or ( - context.preferences.view.show_developer_ui and - context.preferences.experimental.use_sculpt_vertex_colors) - else () - ), + _defs_sculpt.color_filter, None, _defs_sculpt.face_set_edit, None, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index b72abeed394..035c4fd1352 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2259,7 +2259,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): def draw(self, context): self._draw_items( context, ( - ({"property": "use_sculpt_vertex_colors"}, "T71947"), ({"property": "use_sculpt_tools_tilt"}, "T82877"), ({"property": "use_sculpt_texture_paint"}, "T96225"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index b786f5adf33..41c1f3d0683 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -219,6 +219,14 @@ class _draw_tool_settings_context_mode: ups = tool_settings.unified_paint_settings + if capabilities.has_color: + row = layout.row(align=True) + row.ui_units_x = 4 + UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") + UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") + row.separator() + layout.prop(brush, "blend", text="", expand=False) + size = "size" size_owner = ups if ups.use_unified_size else brush if size_owner.use_locked_size == 'SCENE': @@ -253,10 +261,6 @@ 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 @@ -1874,7 +1878,7 @@ class VIEW3D_MT_paint_gpencil(Menu): def draw(self, _context): layout = self.layout - layout.operator("gpencil.vertex_color_set", text="Set Vertex Colors") + layout.operator("gpencil.vertex_color_set", text="Set Color Attribute") layout.operator("gpencil.stroke_reset_vertex_color") layout.separator() layout.operator("gpencil.vertex_color_invert", text="Invert") @@ -1907,7 +1911,7 @@ class VIEW3D_MT_select_gpencil(Menu): layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped") if context.mode == 'VERTEX_GPENCIL': - layout.operator("gpencil.select_vertex_color", text="Vertex Color") + layout.operator("gpencil.select_vertex_color", text="Color Attribute") layout.separator() @@ -7572,7 +7576,7 @@ class TOPBAR_PT_gpencil_materials(GreasePencilMaterialsPanel, Panel): class TOPBAR_PT_gpencil_vertexcolor(GreasePencilVertexcolorPanel, Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' - bl_label = "Vertex Color" + bl_label = "Color Attribute" bl_ui_units_x = 10 @classmethod diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index df07fbb3198..750e9b527f0 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -419,6 +419,9 @@ class VIEW3D_PT_tools_brush_color(Panel, View3DPaintPanel): elif context.vertex_paint_object: capabilities = brush.vertex_paint_capabilities return capabilities.has_color + elif context.sculpt_object: + capabilities = brush.sculpt_capabilities + return capabilities.has_color return False @@ -864,8 +867,7 @@ 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") - if context.preferences.experimental.use_sculpt_vertex_colors: - col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") + col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Color Attributes") layout.operator("object.voxel_remesh", text="Remesh") diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 452d6a4316e..f3968c0d761 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -34,10 +34,25 @@ typedef enum AttributeDomain { ATTR_DOMAIN_NUM } AttributeDomain; -/* Attributes */ +typedef enum AttributeDomainMask { + ATTR_DOMAIN_MASK_POINT = (1 << 0), + ATTR_DOMAIN_MASK_EDGE = (1 << 1), + ATTR_DOMAIN_MASK_FACE = (1 << 2), + ATTR_DOMAIN_MASK_CORNER = (1 << 3), + ATTR_DOMAIN_MASK_CURVE = (1 << 4), + ATTR_DOMAIN_MASK_ALL = (1 << 5) - 1 +} AttributeDomainMask; + +/* All domains that support color attributes. */ +#define ATTR_DOMAIN_MASK_COLOR \ + ((AttributeDomainMask)((ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER))) + +/* Attributes. */ bool BKE_id_attributes_supported(struct ID *id); +/** Create a new attribute layer. + */ struct CustomDataLayer *BKE_id_attribute_new( struct ID *id, const char *name, int type, AttributeDomain domain, struct ReportList *reports); bool BKE_id_attribute_remove(struct ID *id, @@ -49,7 +64,7 @@ struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, int type, AttributeDomain domain); -AttributeDomain BKE_id_attribute_domain(struct ID *id, struct CustomDataLayer *layer); +AttributeDomain BKE_id_attribute_domain(struct ID *id, const struct CustomDataLayer *layer); int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer); bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer); bool BKE_id_attribute_rename(struct ID *id, @@ -57,13 +72,57 @@ bool BKE_id_attribute_rename(struct ID *id, const char *new_name, struct ReportList *reports); -int BKE_id_attributes_length(struct ID *id, CustomDataMask mask); +int BKE_id_attributes_length(const struct ID *id, + AttributeDomainMask domain_mask, + CustomDataMask mask); struct CustomDataLayer *BKE_id_attributes_active_get(struct ID *id); void BKE_id_attributes_active_set(struct ID *id, struct CustomDataLayer *layer); int *BKE_id_attributes_active_index_p(struct ID *id); CustomData *BKE_id_attributes_iterator_next_domain(struct ID *id, struct CustomDataLayer *layers); +CustomDataLayer *BKE_id_attribute_from_index(struct ID *id, + int lookup_index, + AttributeDomainMask domain_mask, + CustomDataMask layer_mask); + +/** Layer is allowed to be nullptr; if so -1 (layer not found) will be returned. */ +int BKE_id_attribute_to_index(const struct ID *id, + const CustomDataLayer *layer, + AttributeDomainMask domain_mask, + CustomDataMask layer_mask); + +struct CustomDataLayer *BKE_id_attribute_subset_active_get(const struct ID *id, + int active_flag, + AttributeDomainMask domain_mask, + CustomDataMask mask); +void BKE_id_attribute_subset_active_set(struct ID *id, + struct CustomDataLayer *layer, + int active_flag, + AttributeDomainMask domain_mask, + CustomDataMask mask); + +/** + * Sets up a temporary ID with arbitrary CustomData domains. r_id will + * be zero initialized with ID type id_type and any non-nullptr + * CustomData parameter will be copied into the appropriate struct members. + * + * \param r_id Pointer to storage sufficient for ID typecode id_type. + */ +void BKE_id_attribute_copy_domains_temp(short id_type, + const struct CustomData *vdata, + const struct CustomData *edata, + const struct CustomData *ldata, + const struct CustomData *pdata, + const struct CustomData *cdata, + struct ID *r_id); + +struct CustomDataLayer *BKE_id_attributes_active_color_get(const struct ID *id); +void BKE_id_attributes_active_color_set(struct ID *id, struct CustomDataLayer *active_layer); +struct CustomDataLayer *BKE_id_attributes_render_color_get(const struct ID *id); +void BKE_id_attributes_render_color_set(struct ID *id, struct CustomDataLayer *active_layer); + +bool BKE_id_attribute_calc_unique_name(struct ID *id, const char *name, char *outname); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_data_transfer.h b/source/blender/blenkernel/BKE_data_transfer.h index fcf84edeb23..1b6c1dc4205 100644 --- a/source/blender/blenkernel/BKE_data_transfer.h +++ b/source/blender/blenkernel/BKE_data_transfer.h @@ -8,6 +8,7 @@ #pragma once #include "BKE_customdata.h" +#include "BLI_compiler_compat.h" #ifdef __cplusplus extern "C" { @@ -32,18 +33,23 @@ enum { DT_TYPE_BWEIGHT_EDGE = 1 << 11, DT_TYPE_FREESTYLE_EDGE = 1 << 12, - DT_TYPE_VCOL = 1 << 16, + DT_TYPE_MPROPCOL_VERT = 1 << 16, DT_TYPE_LNOR = 1 << 17, DT_TYPE_UV = 1 << 24, DT_TYPE_SHARP_FACE = 1 << 25, DT_TYPE_FREESTYLE_FACE = 1 << 26, -#define DT_TYPE_MAX 27 - - DT_TYPE_VERT_ALL = DT_TYPE_MDEFORMVERT | DT_TYPE_SHAPEKEY | DT_TYPE_SKIN | DT_TYPE_BWEIGHT_VERT, + DT_TYPE_MLOOPCOL_VERT = 1 << 27, + DT_TYPE_MPROPCOL_LOOP = 1 << 28, + DT_TYPE_MLOOPCOL_LOOP = 1 << 29, + DT_TYPE_VCOL_ALL = (1 << 16) | (1 << 27) | (1 << 28) | (1 << 29), +#define DT_TYPE_MAX 30 + + DT_TYPE_VERT_ALL = DT_TYPE_MDEFORMVERT | DT_TYPE_SHAPEKEY | DT_TYPE_SKIN | DT_TYPE_BWEIGHT_VERT | + DT_TYPE_MPROPCOL_VERT | DT_TYPE_MLOOPCOL_VERT, DT_TYPE_EDGE_ALL = DT_TYPE_SHARP_EDGE | DT_TYPE_SEAM | DT_TYPE_CREASE | DT_TYPE_BWEIGHT_EDGE | DT_TYPE_FREESTYLE_EDGE, - DT_TYPE_LOOP_ALL = DT_TYPE_VCOL | DT_TYPE_LNOR | DT_TYPE_UV, + DT_TYPE_LOOP_ALL = DT_TYPE_LNOR | DT_TYPE_UV | DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, DT_TYPE_POLY_ALL = DT_TYPE_UV | DT_TYPE_SHARP_FACE | DT_TYPE_FREESTYLE_FACE, }; @@ -62,7 +68,13 @@ int BKE_object_data_transfer_dttype_to_cdtype(int dtdata_type); int BKE_object_data_transfer_dttype_to_srcdst_index(int dtdata_type); #define DT_DATATYPE_IS_VERT(_dt) \ - ELEM(_dt, DT_TYPE_MDEFORMVERT, DT_TYPE_SHAPEKEY, DT_TYPE_SKIN, DT_TYPE_BWEIGHT_VERT) + ELEM(_dt, \ + DT_TYPE_MDEFORMVERT, \ + DT_TYPE_SHAPEKEY, \ + DT_TYPE_SKIN, \ + DT_TYPE_BWEIGHT_VERT, \ + DT_TYPE_MLOOPCOL_VERT, \ + DT_TYPE_MPROPCOL_VERT) #define DT_DATATYPE_IS_EDGE(_dt) \ ELEM(_dt, \ DT_TYPE_CREASE, \ @@ -70,19 +82,28 @@ int BKE_object_data_transfer_dttype_to_srcdst_index(int dtdata_type); DT_TYPE_SEAM, \ DT_TYPE_BWEIGHT_EDGE, \ DT_TYPE_FREESTYLE_EDGE) -#define DT_DATATYPE_IS_LOOP(_dt) ELEM(_dt, DT_TYPE_UV, DT_TYPE_VCOL, DT_TYPE_LNOR) +#define DT_DATATYPE_IS_LOOP(_dt) \ + ELEM(_dt, DT_TYPE_UV, DT_TYPE_LNOR, DT_TYPE_MLOOPCOL_LOOP, DT_TYPE_MPROPCOL_LOOP) #define DT_DATATYPE_IS_POLY(_dt) ELEM(_dt, DT_TYPE_UV, DT_TYPE_SHARP_FACE, DT_TYPE_FREESTYLE_FACE) #define DT_DATATYPE_IS_MULTILAYERS(_dt) \ - ELEM(_dt, DT_TYPE_MDEFORMVERT, DT_TYPE_SHAPEKEY, DT_TYPE_VCOL, DT_TYPE_UV) + ELEM(_dt, \ + DT_TYPE_MDEFORMVERT, \ + DT_TYPE_SHAPEKEY, \ + DT_TYPE_MPROPCOL_VERT, \ + DT_TYPE_MLOOPCOL_VERT, \ + DT_TYPE_MPROPCOL_LOOP, \ + DT_TYPE_MLOOPCOL_LOOP, \ + DT_TYPE_UV) enum { DT_MULTILAYER_INDEX_INVALID = -1, DT_MULTILAYER_INDEX_MDEFORMVERT = 0, DT_MULTILAYER_INDEX_SHAPEKEY = 1, - DT_MULTILAYER_INDEX_VCOL = 2, + DT_MULTILAYER_INDEX_VCOL_LOOP = 2, DT_MULTILAYER_INDEX_UV = 3, - DT_MULTILAYER_INDEX_MAX = 4, + DT_MULTILAYER_INDEX_VCOL_VERT = 4, + DT_MULTILAYER_INDEX_MAX = 5, }; /* Below we keep positive values for real layers idx (generated dynamically). */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 8ab89b6c244..5633c476dc1 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -12,6 +12,8 @@ #include "DNA_brush_enums.h" #include "DNA_object_enums.h" +#include "BKE_attribute.h" + #ifdef __cplusplus extern "C" { #endif @@ -493,6 +495,11 @@ typedef struct SculptSession { struct KeyBlock *shapekey_active; struct MPropCol *vcol; + struct MLoopCol *mcol; + + AttributeDomain vcol_domain; + CustomDataType vcol_type; + float *vmask; /* Mesh connectivity maps. */ diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index eadbe52d091..775847f27f8 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -11,6 +11,7 @@ #include "BLI_ghash.h" /* For embedding CCGKey in iterator. */ +#include "BKE_attribute.h" #include "BKE_ccg.h" #ifdef __cplusplus @@ -34,6 +35,7 @@ struct PBVH; struct PBVHNode; struct SubdivCCG; struct TaskParallelSettings; +struct MeshElemMap; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; @@ -299,6 +301,10 @@ void BKE_pbvh_node_get_verts(PBVH *pbvh, PBVHNode *node, const int **r_vert_indices, struct MVert **r_verts); +void BKE_pbvh_node_get_loops(PBVH *pbvh, + PBVHNode *node, + const int **r_loop_indices, + const struct MLoop **r_loops); void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); @@ -388,7 +394,6 @@ typedef struct PBVHVertexIter { float (*vert_normals)[3]; int totvert; const int *vert_indices; - struct MPropCol *vcol; float *vmask; /* bmesh */ @@ -405,7 +410,6 @@ typedef struct PBVHVertexIter { float *no; float *fno; float *mask; - float *col; bool visible; } PBVHVertexIter; @@ -461,17 +465,14 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m if (vi.vmask) { \ 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)) { \ - vi.bm_vert = BLI_gsetIterator_getKey(&vi.bm_unique_verts); \ + vi.bm_vert = (BMVert *)BLI_gsetIterator_getKey(&vi.bm_unique_verts); \ BLI_gsetIterator_step(&vi.bm_unique_verts); \ } \ else { \ - vi.bm_vert = BLI_gsetIterator_getKey(&vi.bm_other_verts); \ + vi.bm_vert = (BMVert *)BLI_gsetIterator_getKey(&vi.bm_other_verts); \ BLI_gsetIterator_step(&vi.bm_other_verts); \ } \ vi.visible = !BM_elem_flag_test_bool(vi.bm_vert, BM_ELEM_HIDDEN); \ @@ -481,7 +482,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi.co = vi.bm_vert->co; \ vi.fno = vi.bm_vert->no; \ vi.index = BM_elem_index_get(vi.bm_vert); \ - vi.mask = BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \ + vi.mask = (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \ } #define BKE_pbvh_vertex_iter_end \ @@ -526,6 +527,43 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]; PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node); void BKE_pbvh_node_color_buffer_free(PBVH *pbvh); +bool BKE_pbvh_get_color_layer(const struct Mesh *me, + CustomDataLayer **r_layer, + AttributeDomain *r_attr); + +/* Swaps colors at each element in indices (of domain pbvh->vcol_domain) + * with values in colors. */ +void BKE_pbvh_swap_colors(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*colors)[4]); + +/* Stores colors from the elements in indices (of domain pbvh->vcol_domain) + * into colors. */ +void BKE_pbvh_store_colors(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*colors)[4]); + +/* Like BKE_pbvh_store_colors but handles loop->vert conversion */ +void BKE_pbvh_store_colors_vertex(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*colors)[4]); + +bool BKE_pbvh_is_drawing(const PBVH *pbvh); +void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val); + +/* Do not call in PBVH_GRIDS mode */ +void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop); + +void BKE_pbvh_update_active_vcol(PBVH *pbvh, const struct Mesh *mesh); +void BKE_pbvh_pmap_set(PBVH *pbvh, const struct MeshElemMap *pmap); + +void BKE_pbvh_vertex_color_set(PBVH *pbvh, int vertex, const float color[4]); +void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4]); + +void BKE_pbvh_ensure_node_loops(PBVH *pbvh); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index c60d708c075..61131cff06d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -239,6 +239,7 @@ set(SRC intern/particle_child.c intern/particle_distribute.c intern/particle_system.c + intern/pbvh.cc intern/pbvh.c intern/pbvh_bmesh.c intern/pointcache.c diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index ba33a9fee97..13c896fd1ab 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -19,6 +19,7 @@ #include "DNA_pointcloud_types.h" #include "BLI_string_utf8.h" +#include "BLI_string_utils.h" #include "BKE_attribute.h" #include "BKE_curves.h" @@ -91,7 +92,8 @@ static CustomData *attribute_customdata_find(ID *id, CustomDataLayer *layer) for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { CustomData *customdata = info[domain].customdata; - if (customdata && ARRAY_HAS_ITEM(layer, customdata->layers, customdata->totlayer)) { + if (customdata && + ARRAY_HAS_ITEM(layer, (CustomDataLayer const *)customdata->layers, customdata->totlayer)) { return customdata; } } @@ -132,6 +134,44 @@ bool BKE_id_attribute_rename(ID *id, return true; } +typedef struct AttrUniqueData { + ID *id; +} AttrUniqueData; + +static bool unique_name_cb(void *arg, const char *name) +{ + AttrUniqueData *data = (AttrUniqueData *)arg; + + DomainInfo info[ATTR_DOMAIN_NUM]; + get_domains(data->id, info); + + for (AttributeDomain domain = ATTR_DOMAIN_POINT; domain < ATTR_DOMAIN_NUM; domain++) { + if (!info[domain].customdata) { + continue; + } + + CustomData *cdata = info[domain].customdata; + for (int i = 0; i < cdata->totlayer; i++) { + CustomDataLayer *layer = cdata->layers + i; + + if (STREQ(layer->name, name)) { + return true; + } + } + } + + return false; +} + +bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname) +{ + AttrUniqueData data = {.id = id}; + + BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME); + + return BLI_uniquename_cb(unique_name_cb, &data, NULL, '.', outname, MAX_CUSTOMDATA_LAYER_NAME); +} + CustomDataLayer *BKE_id_attribute_new( ID *id, const char *name, const int type, const AttributeDomain domain, ReportList *reports) { @@ -144,25 +184,30 @@ CustomDataLayer *BKE_id_attribute_new( return NULL; } + char uniquename[MAX_CUSTOMDATA_LAYER_NAME]; + BKE_id_attribute_calc_unique_name(id, name, uniquename); + switch (GS(id->name)) { case ID_ME: { Mesh *me = (Mesh *)id; BMEditMesh *em = me->edit_mesh; if (em != NULL) { - BM_data_layer_add_named(em->bm, customdata, type, name); + BM_data_layer_add_named(em->bm, customdata, type, uniquename); } else { - CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name); + CustomData_add_layer_named( + customdata, type, CD_DEFAULT, NULL, info[domain].length, uniquename); } break; } default: { - CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name); + CustomData_add_layer_named( + customdata, type, CD_DEFAULT, NULL, info[domain].length, uniquename); break; } } - const int index = CustomData_get_named_layer_index(customdata, type, name); + const int index = CustomData_get_named_layer_index(customdata, type, uniquename); return (index == -1) ? NULL : &(customdata->layers[index]); } @@ -229,7 +274,7 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id, return NULL; } -int BKE_id_attributes_length(ID *id, const CustomDataMask mask) +int BKE_id_attributes_length(const ID *id, AttributeDomainMask domain_mask, CustomDataMask mask) { DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -238,7 +283,8 @@ int BKE_id_attributes_length(ID *id, const CustomDataMask mask) for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { CustomData *customdata = info[domain].customdata; - if (customdata) { + + if (customdata && ((1 << (int)domain) & domain_mask)) { length += CustomData_number_of_layers_typemask(customdata, mask); } } @@ -246,7 +292,7 @@ int BKE_id_attributes_length(ID *id, const CustomDataMask mask) return length; } -AttributeDomain BKE_id_attribute_domain(ID *id, CustomDataLayer *layer) +AttributeDomain BKE_id_attribute_domain(ID *id, const CustomDataLayer *layer) { DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -295,7 +341,7 @@ bool BKE_id_attribute_required(ID *id, CustomDataLayer *layer) CustomDataLayer *BKE_id_attributes_active_get(ID *id) { int active_index = *BKE_id_attributes_active_index_p(id); - if (active_index > BKE_id_attributes_length(id, CD_MASK_PROP_ALL)) { + if (active_index > BKE_id_attributes_length(id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) { active_index = 0; } @@ -384,3 +430,237 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye return NULL; } + +CustomDataLayer *BKE_id_attribute_from_index(ID *id, + int lookup_index, + AttributeDomainMask domain_mask, + CustomDataMask layer_mask) +{ + DomainInfo info[ATTR_DOMAIN_NUM]; + get_domains(id, info); + + int index = 0; + for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { + CustomData *customdata = info[domain].customdata; + + if (!customdata || !((1 << (int)domain) & domain_mask)) { + continue; + } + + for (int i = 0; i < customdata->totlayer; i++) { + if (!(layer_mask & CD_TYPE_AS_MASK(customdata->layers[i].type)) || + (CD_TYPE_AS_MASK(customdata->layers[i].type) & CD_FLAG_TEMPORARY)) { + continue; + } + + if (index == lookup_index) { + return customdata->layers + i; + } + + index++; + } + } + + return NULL; +} + +/** Get list of domain types but with ATTR_DOMAIN_FACE and + * ATTR_DOMAIN_CORNER swapped. + */ +static void get_domains_types(AttributeDomain domains[ATTR_DOMAIN_NUM]) +{ + for (AttributeDomain i = 0; i < ATTR_DOMAIN_NUM; i++) { + domains[i] = i; + } + + /* Swap corner and face. */ + SWAP(AttributeDomain, domains[ATTR_DOMAIN_FACE], domains[ATTR_DOMAIN_CORNER]); +} + +int BKE_id_attribute_to_index(const struct ID *id, + const CustomDataLayer *layer, + AttributeDomainMask domain_mask, + CustomDataMask layer_mask) +{ + if (!layer) { + return -1; + } + + DomainInfo info[ATTR_DOMAIN_NUM]; + AttributeDomain domains[ATTR_DOMAIN_NUM]; + get_domains_types(domains); + get_domains(id, info); + + int index = 0; + for (int i = 0; i < ATTR_DOMAIN_NUM; i++) { + if (!(domain_mask & (1 << domains[i])) || !info[domains[i]].customdata) { + continue; + } + + CustomData *cdata = info[domains[i]].customdata; + for (int j = 0; j < cdata->totlayer; j++) { + CustomDataLayer *layer_iter = cdata->layers + j; + + if (!(CD_TYPE_AS_MASK(layer_iter->type) & layer_mask) || + (CD_TYPE_AS_MASK(layer_iter->type) & CD_FLAG_TEMPORARY)) { + continue; + } + + if (layer == layer_iter) { + return index; + } + + index++; + } + } + + return -1; +} + +CustomDataLayer *BKE_id_attribute_subset_active_get(const ID *id, + int active_flag, + AttributeDomainMask domain_mask, + CustomDataMask mask) +{ + DomainInfo info[ATTR_DOMAIN_NUM]; + AttributeDomain domains[ATTR_DOMAIN_NUM]; + + get_domains_types(domains); + get_domains(id, info); + + CustomDataLayer *candidate = NULL; + for (int i = 0; i < ARRAY_SIZE(domains); i++) { + if (!((1 << domains[i]) & domain_mask) || !info[domains[i]].customdata) { + continue; + } + + CustomData *cdata = info[domains[i]].customdata; + + for (int j = 0; j < cdata->totlayer; j++) { + CustomDataLayer *layer = cdata->layers + j; + + if (!(CD_TYPE_AS_MASK(layer->type) & mask) || + (CD_TYPE_AS_MASK(layer->type) & CD_FLAG_TEMPORARY)) { + continue; + } + + if (layer->flag & active_flag) { + return layer; + } + + candidate = layer; + } + } + + return candidate; +} + +void BKE_id_attribute_subset_active_set(ID *id, + CustomDataLayer *layer, + int active_flag, + AttributeDomainMask domain_mask, + CustomDataMask mask) +{ + DomainInfo info[ATTR_DOMAIN_NUM]; + AttributeDomain domains[ATTR_DOMAIN_NUM]; + + get_domains_types(domains); + get_domains(id, info); + + for (int i = 0; i < ATTR_DOMAIN_NUM; i++) { + AttributeDomainMask domain_mask2 = (AttributeDomainMask)(1 << domains[i]); + + if (!(domain_mask2 & domain_mask) || !info[domains[i]].customdata) { + continue; + } + + CustomData *cdata = info[domains[i]].customdata; + + for (int j = 0; j < cdata->totlayer; j++) { + CustomDataLayer *layer_iter = cdata->layers + j; + + if (!(CD_TYPE_AS_MASK(layer_iter->type) & mask) || + (CD_TYPE_AS_MASK(layer_iter->type) & CD_FLAG_TEMPORARY)) { + continue; + } + + layer_iter->flag &= ~active_flag; + } + } + + layer->flag |= active_flag; +} + +CustomDataLayer *BKE_id_attributes_active_color_get(const ID *id) +{ + return BKE_id_attribute_subset_active_get( + id, CD_FLAG_COLOR_ACTIVE, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); +} + +void BKE_id_attributes_active_color_set(ID *id, CustomDataLayer *active_layer) +{ + BKE_id_attribute_subset_active_set( + id, active_layer, CD_FLAG_COLOR_ACTIVE, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); +} + +CustomDataLayer *BKE_id_attributes_render_color_get(const ID *id) +{ + return BKE_id_attribute_subset_active_get( + id, CD_FLAG_COLOR_RENDER, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); +} + +void BKE_id_attributes_render_color_set(ID *id, CustomDataLayer *active_layer) +{ + BKE_id_attribute_subset_active_set( + id, active_layer, CD_FLAG_COLOR_RENDER, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); +} + +void BKE_id_attribute_copy_domains_temp(short id_type, + const CustomData *vdata, + const CustomData *edata, + const CustomData *ldata, + const CustomData *pdata, + const CustomData *cdata, + ID *r_id) +{ + CustomData reset; + + CustomData_reset(&reset); + + switch (id_type) { + case ID_ME: { + Mesh *me = (Mesh *)r_id; + memset((void *)me, 0, sizeof(*me)); + + me->edit_mesh = NULL; + + me->vdata = vdata ? *vdata : reset; + me->edata = edata ? *edata : reset; + me->ldata = ldata ? *ldata : reset; + me->pdata = pdata ? *pdata : reset; + + break; + } + case ID_PT: { + PointCloud *pointcloud = (PointCloud *)r_id; + + memset((void *)pointcloud, 0, sizeof(*pointcloud)); + + pointcloud->pdata = vdata ? *vdata : reset; + break; + } + case ID_CV: { + Curves *curves = (Curves *)r_id; + + memset((void *)curves, 0, sizeof(*curves)); + + curves->geometry.point_data = vdata ? *vdata : reset; + curves->geometry.curve_data = cdata ? *cdata : reset; + break; + } + default: + break; + } + + *((short *)r_id->name) = id_type; +} diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 601c867d8db..b9cd9e1ee59 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1834,7 +1834,8 @@ void BKE_brush_sculpt_reset(Brush *br) br->tip_roundness = 1.0f; br->density = 1.0f; br->flag &= ~BRUSH_SPACE_ATTEN; - zero_v3(br->rgb); + copy_v3_fl(br->rgb, 1.0f); + zero_v3(br->secondary_rgb); break; case SCULPT_TOOL_SMEAR: br->alpha = 1.0f; diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 9258c1ffb66..6dd9460aaa9 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2279,7 +2279,8 @@ bool CustomData_merge(const struct CustomData *source, newlayer->active_rnd = lastrender; newlayer->active_clone = lastclone; newlayer->active_mask = lastmask; - newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY); + newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY | CD_FLAG_COLOR_ACTIVE | + CD_FLAG_COLOR_RENDER); changed = true; if (layer->anonymous_id != nullptr) { diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 5be993ca1f7..2369ce88ebc 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -19,6 +19,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_data_transfer.h" #include "BKE_deform.h" @@ -129,7 +130,10 @@ bool BKE_object_data_transfer_get_dttypes_capacity(const int dtdata_types, case DT_TYPE_UV: ret = true; break; - case DT_TYPE_VCOL: + case DT_TYPE_MPROPCOL_VERT: + case DT_TYPE_MLOOPCOL_VERT: + case DT_TYPE_MPROPCOL_LOOP: + case DT_TYPE_MLOOPCOL_LOOP: *r_advanced_mixing = true; *r_threshold = true; ret = true; @@ -209,12 +213,14 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type) return CD_FAKE_SHARP; case DT_TYPE_FREESTYLE_FACE: return CD_FREESTYLE_FACE; - - case DT_TYPE_VCOL: - return CD_MLOOPCOL; case DT_TYPE_LNOR: return CD_FAKE_LNOR; - + case DT_TYPE_MLOOPCOL_VERT: + case DT_TYPE_MLOOPCOL_LOOP: + return CD_MLOOPCOL; + case DT_TYPE_MPROPCOL_VERT: + case DT_TYPE_MPROPCOL_LOOP: + return CD_PROP_COLOR; default: BLI_assert(0); } @@ -230,8 +236,12 @@ int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type) return DT_MULTILAYER_INDEX_SHAPEKEY; case DT_TYPE_UV: return DT_MULTILAYER_INDEX_UV; - case DT_TYPE_VCOL: - return DT_MULTILAYER_INDEX_VCOL; + case DT_TYPE_MPROPCOL_VERT: + case DT_TYPE_MLOOPCOL_VERT: + return DT_MULTILAYER_INDEX_VCOL_VERT; + case DT_TYPE_MPROPCOL_LOOP: + case DT_TYPE_MLOOPCOL_LOOP: + return DT_MULTILAYER_INDEX_VCOL_LOOP; default: return DT_MULTILAYER_INDEX_INVALID; } @@ -1231,6 +1241,7 @@ void BKE_object_data_transfer_layout(struct Depsgraph *depsgraph, cddata_type = BKE_object_data_transfer_dttype_to_cdtype(dtdata_type); fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(dtdata_type); + if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) { fromlayers = fromlayers_select[fromto_idx]; tolayers = tolayers_select[fromto_idx]; diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index b30d8f92cc6..5afc3c0be3b 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -433,7 +433,7 @@ static const char *cmpcode_to_str(int code) case MESHCMP_DVERT_TOTGROUPMISMATCH: return "Vertex Doesn't Belong To Same Number Of Groups"; case MESHCMP_LOOPCOLMISMATCH: - return "Vertex Color Mismatch"; + return "Color Attribute Mismatch"; case MESHCMP_LOOPUVMISMATCH: return "UV Mismatch"; case MESHCMP_LOOPMISMATCH: diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 1c58173f570..80c3ead3039 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -29,6 +29,7 @@ #include "BLT_translation.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_colortools.h" @@ -1666,7 +1667,28 @@ static void sculpt_update_object(Depsgraph *depsgraph, 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); + + CustomDataLayer *layer; + AttributeDomain domain; + + if (BKE_pbvh_get_color_layer(me, &layer, &domain)) { + if (layer->type == CD_PROP_COLOR) { + ss->vcol = layer->data; + } + else { + ss->mcol = layer->data; + } + + ss->vcol_domain = domain; + ss->vcol_type = layer->type; + } + else { + ss->vcol = NULL; + ss->mcol = NULL; + + ss->vcol_type = -1; + ss->vcol_domain = ATTR_DOMAIN_NUM; + } } /* Sculpt Face Sets. */ @@ -1692,6 +1714,10 @@ static void sculpt_update_object(Depsgraph *depsgraph, if (need_pmap && ob->type == OB_MESH && !ss->pmap) { BKE_mesh_vert_poly_map_create( &ss->pmap, &ss->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); + + if (ss->pbvh) { + BKE_pbvh_pmap_set(ss->pbvh, ss->pmap); + } } pbvh_show_mask_set(ss->pbvh, ss->show_mask); @@ -1791,16 +1817,30 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval) void BKE_sculpt_color_layer_create_if_needed(struct Object *object) { Mesh *orig_me = BKE_object_get_original_mesh(object); - if (!U.experimental.use_sculpt_vertex_colors) { - return; + + int types[] = {CD_PROP_COLOR, CD_MLOOPCOL}; + bool has_color = false; + + for (int i = 0; i < ARRAY_SIZE(types); i++) { + has_color = CustomData_has_layer(&orig_me->vdata, types[i]) || + CustomData_has_layer(&orig_me->ldata, types[i]); + + if (has_color) { + break; + } } - if (CustomData_has_layer(&orig_me->vdata, CD_PROP_COLOR)) { + if (has_color) { return; } CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert); + CustomDataLayer *layer = orig_me->vdata.layers + + CustomData_get_layer_index(&orig_me->vdata, CD_PROP_COLOR); + BKE_mesh_update_customdata_pointers(orig_me, true); + + BKE_id_attributes_active_color_set(&orig_me->id, layer); DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES); } @@ -2173,6 +2213,10 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg); } } + + BKE_pbvh_update_active_vcol(pbvh, BKE_object_get_original_mesh(ob)); + BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); + return pbvh; } @@ -2192,6 +2236,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } } + BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); + ob->sculpt->pbvh = pbvh; return pbvh; } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 3ed3c7badc3..5d307697208 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -17,8 +17,10 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_attribute.h" #include "BKE_ccg.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_subdiv_ccg.h" @@ -599,6 +601,8 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, /* Clear the bitmap so it can be used as an update tag later on. */ BLI_bitmap_set_all(pbvh->vert_bitmap, false, totvert); + + BKE_pbvh_update_active_vcol(pbvh, mesh); } void BKE_pbvh_build_grids(PBVH *pbvh, @@ -667,6 +671,9 @@ void BKE_pbvh_free(PBVH *pbvh) if (node->vert_indices) { MEM_freeN((void *)node->vert_indices); } + if (node->loop_indices) { + MEM_freeN(node->loop_indices); + } if (node->face_vert_indices) { MEM_freeN((void *)node->face_vert_indices); } @@ -1254,6 +1261,30 @@ static int pbvh_get_buffers_update_flags(PBVH *UNUSED(pbvh)) return update_flags; } +bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, AttributeDomain *r_attr) +{ + CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)me); + + if (!layer || !ELEM(layer->type, CD_PROP_COLOR, CD_MLOOPCOL)) { + *r_layer = NULL; + *r_attr = ATTR_DOMAIN_NUM; + return false; + } + + AttributeDomain domain = BKE_id_attribute_domain((ID *)me, layer); + + if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + *r_layer = NULL; + *r_attr = ATTR_DOMAIN_NUM; + return false; + } + + *r_layer = layer; + *r_attr = domain; + + return true; +} + static void pbvh_update_draw_buffer_cb(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -1304,18 +1335,25 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, &pbvh->gridkey, update_flags); break; - case PBVH_FACES: + case PBVH_FACES: { + CustomDataLayer *layer = NULL; + AttributeDomain domain; + + BKE_pbvh_get_color_layer(pbvh->mesh, &layer, &domain); + GPU_pbvh_mesh_buffers_update(node->draw_buffers, pbvh->verts, pbvh->vert_normals, CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK), - CustomData_get_layer(pbvh->ldata, CD_MLOOPCOL), + layer ? layer->data : NULL, + layer ? layer->type : -1, + layer ? domain : ATTR_DOMAIN_AUTO, 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: GPU_pbvh_bmesh_buffers_update(node->draw_buffers, pbvh->bm, @@ -1442,7 +1480,9 @@ void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flag) } if (flag & (PBVH_UpdateColor)) { - /* Do nothing */ + for (int i = 0; i < totnode; i++) { + nodes[i]->flag |= PBVH_UpdateRedraw | PBVH_UpdateDrawBuffers | PBVH_UpdateColor; + } } if (flag & (PBVH_UpdateVisibility)) { @@ -1820,6 +1860,22 @@ void BKE_pbvh_vert_mark_update(PBVH *pbvh, int index) BLI_BITMAP_ENABLE(pbvh->vert_bitmap, index); } +void BKE_pbvh_node_get_loops(PBVH *pbvh, + PBVHNode *node, + const int **r_loop_indices, + const MLoop **r_loops) +{ + BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); + + if (r_loop_indices) { + *r_loop_indices = node->loop_indices; + } + + if (r_loops) { + *r_loops = pbvh->mloop; + } +} + void BKE_pbvh_node_get_verts(PBVH *pbvh, PBVHNode *node, const int **r_vert_indices, @@ -2980,7 +3036,6 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->vert_normals = pbvh->vert_normals; vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); - vi->vcol = CustomData_get_layer(pbvh->vdata, CD_PROP_COLOR); } } @@ -3072,3 +3127,81 @@ void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide) { pbvh->respect_hide = respect_hide; } +bool BKE_pbvh_is_drawing(const PBVH *pbvh) +{ + return pbvh->is_drawing; +} + +void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val) +{ + pbvh->is_drawing = val; +} + +void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop) +{ + UNUSED_VARS(pbvh); + BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); + + if (r_totloop) { + *r_totloop = node->loop_indices_num; + } +} + +void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh) +{ + BKE_pbvh_get_color_layer(mesh, &pbvh->color_layer, &pbvh->color_domain); +} + +void BKE_pbvh_pmap_set(PBVH *pbvh, const MeshElemMap *pmap) +{ + pbvh->pmap = pmap; +} + +void BKE_pbvh_ensure_node_loops(PBVH *pbvh) +{ + BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); + + int totloop = 0; + + /* Check if nodes already have loop indices. */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + if (node->loop_indices) { + return; + } + + totloop += node->totprim * 3; + } + + BLI_bitmap *visit = BLI_BITMAP_NEW(totloop, __func__); + + /* Create loop indices from node loop triangles. */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + node->loop_indices = MEM_malloc_arrayN(node->totprim * 3, sizeof(int), __func__); + node->loop_indices_num = 0; + + for (int j = 0; j < node->totprim; j++) { + const MLoopTri *mlt = pbvh->looptri + node->prim_indices[j]; + + for (int k = 0; k < 3; k++) { + if (!BLI_BITMAP_TEST(visit, mlt->tri[k])) { + node->loop_indices[node->loop_indices_num++] = mlt->tri[k]; + BLI_BITMAP_ENABLE(visit, mlt->tri[k]); + } + } + } + } + + MEM_SAFE_FREE(visit); +} diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc new file mode 100644 index 00000000000..d32a03186e3 --- /dev/null +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "BLI_bitmap.h" +#include "BLI_ghash.h" +#include "BLI_index_range.hh" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_span.hh" +#include "BLI_task.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute.h" +#include "BKE_ccg.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_subdiv_ccg.h" + +#include "PIL_time.h" + +#include "GPU_buffers.h" + +#include "bmesh.h" + +#include "atomic_ops.h" + +#include "pbvh_intern.h" + +#include <climits> + +using blender::IndexRange; + +namespace blender::bke { + +template<typename Func> +inline void to_static_color_type(const CustomDataType type, const Func &func) +{ + switch (type) { + case CD_PROP_COLOR: + func(MPropCol()); + break; + case CD_MLOOPCOL: + func(MLoopCol()); + break; + default: + BLI_assert_unreachable(); + break; + } +} + +template<typename T> void to_float(const T &src, float dst[4]); + +template<> void to_float(const MLoopCol &src, float dst[4]) +{ + rgba_uchar_to_float(dst, reinterpret_cast<const unsigned char *>(&src)); + srgb_to_linearrgb_v3_v3(dst, dst); +} +template<> void to_float(const MPropCol &src, float dst[4]) +{ + copy_v4_v4(dst, src.color); +} + +template<typename T> void from_float(const float src[4], T &dst); + +template<> void from_float(const float src[4], MLoopCol &dst) +{ + float temp[4]; + linearrgb_to_srgb_v3_v3(temp, src); + temp[3] = src[3]; + rgba_float_to_uchar(reinterpret_cast<unsigned char *>(&dst), temp); +} +template<> void from_float(const float src[4], MPropCol &dst) +{ + copy_v4_v4(dst.color, src); +} + +template<typename T> +static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4]) +{ + if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { + const MeshElemMap &melem = pbvh.pmap[vertex]; + + int count = 0; + zero_v4(r_color); + for (const int i_poly : Span(melem.indices, melem.count)) { + const MPoly &mp = pbvh.mpoly[i_poly]; + Span<T> colors{static_cast<const T *>(pbvh.color_layer->data) + mp.loopstart, mp.totloop}; + Span<MLoop> loops{pbvh.mloop + mp.loopstart, mp.totloop}; + + for (const int i_loop : IndexRange(mp.totloop)) { + if (loops[i_loop].v == vertex) { + float temp[4]; + to_float(colors[i_loop], temp); + + add_v4_v4(r_color, temp); + count++; + } + } + } + + if (count) { + mul_v4_fl(r_color, 1.0f / (float)count); + } + } + else { + to_float(static_cast<T *>(pbvh.color_layer->data)[vertex], r_color); + } +} + +template<typename T> +static void pbvh_vertex_color_set(PBVH &pbvh, int vertex, const float color[4]) +{ + if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { + const MeshElemMap &melem = pbvh.pmap[vertex]; + + for (const int i_poly : Span(melem.indices, melem.count)) { + const MPoly &mp = pbvh.mpoly[i_poly]; + MutableSpan<T> colors{static_cast<T *>(pbvh.color_layer->data) + mp.loopstart, mp.totloop}; + Span<MLoop> loops{pbvh.mloop + mp.loopstart, mp.totloop}; + + for (const int i_loop : IndexRange(mp.totloop)) { + if (loops[i_loop].v == vertex) { + from_float(color, colors[i_loop]); + } + } + } + } + else { + from_float(color, static_cast<T *>(pbvh.color_layer->data)[vertex]); + } +} + +} // namespace blender::bke + +extern "C" { +void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4]) +{ + blender::bke::to_static_color_type(CustomDataType(pbvh->color_layer->type), [&](auto dummy) { + using T = decltype(dummy); + blender::bke::pbvh_vertex_color_get<T>(*pbvh, vertex, r_color); + }); +} + +void BKE_pbvh_vertex_color_set(PBVH *pbvh, int vertex, const float color[4]) +{ + blender::bke::to_static_color_type(CustomDataType(pbvh->color_layer->type), [&](auto dummy) { + using T = decltype(dummy); + blender::bke::pbvh_vertex_color_set<T>(*pbvh, vertex, color); + }); +} + +void BKE_pbvh_swap_colors(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*r_colors)[4]) +{ + blender::bke::to_static_color_type(CustomDataType(pbvh->color_layer->type), [&](auto dummy) { + using T = decltype(dummy); + T *pbvh_colors = static_cast<T *>(pbvh->color_layer->data); + for (const int i : IndexRange(indices_num)) { + T temp = pbvh_colors[indices[i]]; + blender::bke::from_float(r_colors[i], pbvh_colors[indices[i]]); + blender::bke::to_float(temp, r_colors[i]); + } + }); +} + +void BKE_pbvh_store_colors(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*r_colors)[4]) +{ + blender::bke::to_static_color_type(CustomDataType(pbvh->color_layer->type), [&](auto dummy) { + using T = decltype(dummy); + T *pbvh_colors = static_cast<T *>(pbvh->color_layer->data); + for (const int i : IndexRange(indices_num)) { + blender::bke::to_float(pbvh_colors[indices[i]], r_colors[i]); + } + }); +} + +void BKE_pbvh_store_colors_vertex(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*r_colors)[4]) +{ + if (pbvh->color_domain == ATTR_DOMAIN_POINT) { + BKE_pbvh_store_colors(pbvh, indices, indices_num, r_colors); + } + else { + blender::bke::to_static_color_type(CustomDataType(pbvh->color_layer->type), [&](auto dummy) { + using T = decltype(dummy); + for (const int i : IndexRange(indices_num)) { + blender::bke::pbvh_vertex_color_get<T>(*pbvh, indices[i], r_colors[i]); + } + }); + } +} +} diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index a10d09e106c..37f8dfd9b6b 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -16,6 +16,8 @@ typedef struct { float bmin[3], bmax[3], bcentroid[3]; } BBC; +struct MeshElemMap; + /* NOTE: this structure is getting large, might want to split it into * union'd structs */ struct PBVHNode { @@ -60,6 +62,13 @@ struct PBVHNode { const int *vert_indices; unsigned int uniq_verts, face_verts; + /* Array of indices into the Mesh's MLoop array. + * PBVH_FACES only. The first part of the array + * are loops unique to this node, see comment for + * vert_indices for more details.*/ + int *loop_indices; + unsigned int loop_indices_num; + /* An array mapping face corners into the vert_indices * array. The array is sized to match 'totprim', and each of * the face's corners gets an index into the vert_indices @@ -165,6 +174,13 @@ struct PBVH { struct BMLog *bm_log; struct SubdivCCG *subdiv_ccg; + + const struct MeshElemMap *pmap; + + CustomDataLayer *color_layer; + AttributeDomain color_domain; + + bool is_drawing; }; /* pbvh.c */ diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 3088dc4c7e8..5ba7dc74e3d 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -30,6 +30,7 @@ #include "DNA_lineart_types.h" #include "DNA_listBase.h" #include "DNA_material_types.h" +#include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -41,8 +42,10 @@ #include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_asset.h" +#include "BKE_attribute.h" #include "BKE_collection.h" #include "BKE_curve.h" +#include "BKE_data_transfer.h" #include "BKE_deform.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" @@ -2566,6 +2569,74 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 302, 10)) { + /* While vertex-colors were experimental the smear tool became corrupt due + * to bugs in the wm_toolsystem API (auto-creation of sculpt brushes + * was broken). Go through and reset all smear brushes. */ + LISTBASE_FOREACH (Brush *, br, &bmain->brushes) { + if (br->sculpt_tool == SCULPT_TOOL_SMEAR) { + br->alpha = 1.0f; + br->spacing = 5; + br->flag &= ~BRUSH_ALPHA_PRESSURE; + br->flag &= ~BRUSH_SPACE_ATTEN; + br->curve_preset = BRUSH_CURVE_SPHERE; + } + } + + /* Rebuild active/render color attribute references. */ + LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { + for (int step = 0; step < 2; step++) { + CustomDataLayer *actlayer = NULL; + + int vact1, vact2; + + if (step) { + vact1 = CustomData_get_render_layer_index(&me->vdata, CD_PROP_COLOR); + vact2 = CustomData_get_render_layer_index(&me->ldata, CD_MLOOPCOL); + } + else { + vact1 = CustomData_get_active_layer_index(&me->vdata, CD_PROP_COLOR); + vact2 = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPCOL); + } + + if (vact1 != -1) { + actlayer = me->vdata.layers + vact1; + } + else if (vact2 != -1) { + actlayer = me->ldata.layers + vact2; + } + + if (actlayer) { + if (step) { + BKE_id_attributes_render_color_set(&me->id, actlayer); + } + else { + BKE_id_attributes_active_color_set(&me->id, actlayer); + } + } + } + } + + /* Update data transfer modifiers */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_DataTransfer) { + DataTransferModifierData *dtmd = (DataTransferModifierData *)md; + + for (int i = 0; i < DT_MULTILAYER_INDEX_MAX; i++) { + if (dtmd->layers_select_src[i] == 0) { + dtmd->layers_select_src[i] = DT_LAYERS_ALL_SRC; + } + + if (dtmd->layers_select_dst[i] == 0) { + dtmd->layers_select_dst[i] = DT_LAYERS_NAME_DST; + } + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 276a8abb731..7424fdb9247 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1493,6 +1493,7 @@ static BMOpDefine bmo_rotate_colors_def = { /* slots_in */ {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */ {"use_ccw", BMO_OP_SLOT_BOOL}, /* rotate counter-clockwise if true, otherwise clockwise */ + {"color_index", BMO_OP_SLOT_INT}, /* index into color attribute list */ {{'\0'}}, }, {{{'\0'}}}, /* no output */ @@ -1509,6 +1510,7 @@ static BMOpDefine bmo_reverse_colors_def = { "reverse_colors", /* slots_in */ {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */ + {"color_index", BMO_OP_SLOT_INT}, /* index into color attribute list */ {{'\0'}}, }, {{{'\0'}}}, /* no output */ diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c index c79eecaf1a0..60a893ab5da 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -9,12 +9,16 @@ #include "MEM_guardedalloc.h" +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_types.h" #include "BLI_alloca.h" #include "BLI_math.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" +#include "BKE_object.h" #include "bmesh.h" @@ -553,6 +557,28 @@ void bmo_reverse_uvs_exec(BMesh *bm, BMOperator *op) /**************************************************************************** * * Cycle colors for a face **************************************************************************** */ +static void bmo_get_loop_color_ref(BMesh *bm, + int index, + int *r_cd_color_offset, + int *r_cd_color_type) +{ + Mesh me_query; + + BKE_id_attribute_copy_domains_temp(ID_ME, &bm->vdata, NULL, &bm->ldata, NULL, NULL, &me_query.id); + + CustomDataLayer *layer = BKE_id_attribute_from_index( + &me_query.id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + if (!layer || BKE_id_attribute_domain(&me_query.id, layer) != ATTR_DOMAIN_CORNER) { + *r_cd_color_offset = -1; + return; + } + + int layer_i = CustomData_get_layer_index(&bm->ldata, layer->type); + + *r_cd_color_offset = bm->ldata.layers[layer_i].offset; + *r_cd_color_type = layer->type; +} void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op) { @@ -561,57 +587,67 @@ void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op) BMIter l_iter; /* iteration loop */ const bool use_ccw = BMO_slot_bool_get(op->slots_in, "use_ccw"); - const int cd_loop_color_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPCOL); - if (cd_loop_color_offset != -1) { - BMO_ITER (fs, &fs_iter, op->slots_in, "faces", BM_FACE) { - if (use_ccw == false) { /* same loops direction */ - BMLoop *lf; /* current face loops */ - MLoopCol *f_lcol; /* first face loop color */ - MLoopCol p_col; /* previous color */ - MLoopCol t_col; /* tmp color */ + const int color_index = BMO_slot_int_get(op->slots_in, "color_index"); - int n = 0; - BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { - /* current loop color is the previous loop color */ - MLoopCol *lcol = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_color_offset); - if (n == 0) { - f_lcol = lcol; - p_col = *lcol; - } - else { - t_col = *lcol; - *lcol = p_col; - p_col = t_col; - } - n++; - } + int cd_loop_color_offset; + int cd_loop_color_type; - *f_lcol = p_col; - } - else { /* counter loop direction */ - BMLoop *lf; /* current face loops */ - MLoopCol *p_lcol; /* previous loop color */ - MLoopCol *lcol; - MLoopCol t_col; /* current color */ + bmo_get_loop_color_ref(bm, color_index, &cd_loop_color_offset, &cd_loop_color_type); - int n = 0; - BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { - /* previous loop color is the current loop color */ - lcol = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_color_offset); - if (n == 0) { - p_lcol = lcol; - t_col = *lcol; - } - else { - *p_lcol = *lcol; - p_lcol = lcol; - } - n++; + if (cd_loop_color_offset == -1) { + BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "color_index is invalid"); + return; + } + + const size_t size = cd_loop_color_type == CD_PROP_COLOR ? sizeof(MPropCol) : sizeof(MLoopCol); + void *p_col; /* previous color */ + void *t_col = alloca(size); /* tmp color */ + + BMO_ITER (fs, &fs_iter, op->slots_in, "faces", BM_FACE) { + if (use_ccw == false) { /* same loops direction */ + BMLoop *lf; /* current face loops */ + void *f_lcol; /* first face loop color */ + + int n = 0; + BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { + /* current loop color is the previous loop color */ + void *lcol = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_color_offset); + + if (n == 0) { + f_lcol = lcol; + p_col = lcol; } + else { + memcpy(t_col, lcol, size); + memcpy(lcol, p_col, size); + memcpy(p_col, t_col, size); + } + n++; + } - *lcol = t_col; + memcpy(f_lcol, p_col, size); + } + else { /* counter loop direction */ + BMLoop *lf; /* current face loops */ + void *lcol, *p_lcol; + + int n = 0; + BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) { + /* previous loop color is the current loop color */ + lcol = BM_ELEM_CD_GET_VOID_P(lf, cd_loop_color_offset); + if (n == 0) { + p_lcol = lcol; + memcpy(t_col, lcol, size); + } + else { + memcpy(p_lcol, lcol, size); + p_lcol = lcol; + } + n++; } + + memcpy(lcol, t_col, size); } } } @@ -619,35 +655,53 @@ void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op) /*************************************************************************** * * Reverse colors for a face *************************************************************************** */ -static void bm_face_reverse_colors(BMFace *f, const int cd_loop_color_offset) +static void bm_face_reverse_colors(BMFace *f, + const int cd_loop_color_offset, + const int cd_loop_color_type) { BMIter iter; BMLoop *l; int i; - MLoopCol *cols = BLI_array_alloca(cols, f->len); + const size_t size = cd_loop_color_type == CD_PROP_COLOR ? sizeof(MPropCol) : sizeof(MLoopCol); + + char *cols = alloca(size * f->len); + char *col = cols; BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, i) { - MLoopCol *lcol = BM_ELEM_CD_GET_VOID_P(l, cd_loop_color_offset); - cols[i] = *lcol; + void *lcol = BM_ELEM_CD_GET_VOID_P(l, cd_loop_color_offset); + memcpy((void *)col, lcol, size); + col += size; } /* now that we have the uvs in the array, reverse! */ BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, i) { /* current loop uv is the previous loop color */ - MLoopCol *lcol = BM_ELEM_CD_GET_VOID_P(l, cd_loop_color_offset); - *lcol = cols[(f->len - i - 1)]; + void *lcol = BM_ELEM_CD_GET_VOID_P(l, cd_loop_color_offset); + + col = cols + (f->len - i - 1) * size; + memcpy(lcol, (void *)col, size); } } + void bmo_reverse_colors_exec(BMesh *bm, BMOperator *op) { BMOIter iter; BMFace *f; - const int cd_loop_color_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPCOL); - if (cd_loop_color_offset != -1) { - BMO_ITER (f, &iter, op->slots_in, "faces", BM_FACE) { - bm_face_reverse_colors(f, cd_loop_color_offset); - } + const int color_index = BMO_slot_int_get(op->slots_in, "color_index"); + + int cd_loop_color_offset; + int cd_loop_color_type; + + bmo_get_loop_color_ref(bm, color_index, &cd_loop_color_offset, &cd_loop_color_type); + + if (cd_loop_color_offset == -1) { + BMO_error_raise(bm, op, BMO_ERROR_CANCEL, "color_index is invalid"); + return; + } + + BMO_ITER (f, &iter, op->slots_in, "faces", BM_FACE) { + bm_face_reverse_colors(f, cd_loop_color_offset, cd_loop_color_type); } } diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index f97db2fcda9..8da95c8387b 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -18,6 +18,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BKE_pbvh.h" #include "DNA_curves_types.h" #include "DNA_fluid_types.h" @@ -170,12 +171,7 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd, geom = DRW_cache_mesh_surface_vertpaint_get(ob); } else { - if (U.experimental.use_sculpt_vertex_colors) { - geom = DRW_cache_mesh_surface_sculptcolors_get(ob); - } - else { - geom = DRW_cache_mesh_surface_vertpaint_get(ob); - } + geom = DRW_cache_mesh_surface_sculptcolors_get(ob); } } else { @@ -258,7 +254,6 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, eV3DShadingColorType color_type = wpd->shading.color_type; const Mesh *me = (ob->type == OB_MESH) ? ob->data : NULL; const CustomData *ldata = (me == NULL) ? NULL : workbench_mesh_get_loop_custom_data(me); - const CustomData *vdata = (me == NULL) ? NULL : workbench_mesh_get_vert_custom_data(me); const DRWContextState *draw_ctx = DRW_context_state_get(); const bool is_active = (ob == draw_ctx->obact); @@ -268,6 +263,14 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, const bool is_texpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_TEXTURE); const bool is_vertpaint_mode = is_active && (wpd->ctx_mode == CTX_MODE_PAINT_VERTEX); + /* Needed for mesh cache validation, to prevent two copies of + * of vertex color arrays from being sent to the GPU (e.g. + * when switching from eevee to workbench). + */ + if (ob->sculpt && ob->sculpt->pbvh) { + BKE_pbvh_is_drawing_set(ob->sculpt->pbvh, is_sculpt_pbvh); + } + if (color_type == V3D_SHADING_TEXTURE_COLOR) { if (ob->dt < OB_TEXTURE) { color_type = V3D_SHADING_MATERIAL_COLOR; @@ -278,13 +281,19 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, } } else if (color_type == V3D_SHADING_VERTEX_COLOR) { - if (U.experimental.use_sculpt_vertex_colors) { - if ((me == NULL) || !CustomData_has_layer(vdata, CD_PROP_COLOR)) { - color_type = V3D_SHADING_OBJECT_COLOR; - } + if (!me) { + color_type = V3D_SHADING_OBJECT_COLOR; } else { - if ((me == NULL) || !CustomData_has_layer(ldata, CD_MLOOPCOL)) { + const CustomData *cd_vdata = workbench_mesh_get_vert_custom_data(me); + const CustomData *cd_ldata = workbench_mesh_get_loop_custom_data(me); + + bool has_color = (CustomData_has_layer(cd_vdata, CD_PROP_COLOR) || + CustomData_has_layer(cd_vdata, CD_MLOOPCOL) || + CustomData_has_layer(cd_ldata, CD_PROP_COLOR) || + CustomData_has_layer(cd_ldata, CD_MLOOPCOL)); + + if (!has_color) { color_type = V3D_SHADING_OBJECT_COLOR; } } diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index aaec47b4dce..4567e470146 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -308,6 +308,9 @@ typedef struct MeshBatchCache { float tot_area, tot_uv_area; bool no_loose_wire; + + eV3DShadingColorType color_type; + bool pbvh_is_drawing; } MeshBatchCache; #define MBC_EDITUV \ diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 79a080cfccd..e6f34d3dd0d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -454,29 +454,24 @@ static void mesh_cd_calc_active_mask_uv_layer(const Object *object, } } -static void mesh_cd_calc_active_vcol_layer(const Object *object, - const Mesh *me, - DRW_MeshAttributes *attrs_used) -{ - const Mesh *me_final = editmesh_final_or_this(object, me); - 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) { - drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT); - } -} - static void mesh_cd_calc_active_mloopcol_layer(const Object *object, const Mesh *me, DRW_MeshCDMask *cd_used) { const Mesh *me_final = editmesh_final_or_this(object, me); + Mesh me_query = {0}; + + const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); - int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL); - if (layer != -1) { - cd_used->vcol |= (1 << layer); + BKE_id_attribute_copy_domains_temp(ID_ME, cd_vdata, NULL, cd_ldata, NULL, NULL, &me_query.id); + + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me_query.id); + int layer_i = BKE_id_attribute_to_index( + &me_query.id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + if (layer_i != -1) { + cd_used->vcol |= (1UL << (uint)layer_i); } } @@ -510,6 +505,51 @@ static bool custom_data_match_attribute(const CustomData *custom_data, return false; } +static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query, + const CustomData *cd_vdata, + const CustomData *cd_ldata, + const char name[]) +{ + CustomDataLayer *layer = NULL; + AttributeDomain domain; + + if (name[0]) { + int layer_i = 0; + + domain = ATTR_DOMAIN_POINT; + layer_i = CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, name); + layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_vdata, CD_MLOOPCOL, name) : + layer_i; + + if (layer_i == -1) { + domain = ATTR_DOMAIN_CORNER; + layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_ldata, CD_PROP_COLOR, name) : + layer_i; + layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_ldata, CD_MLOOPCOL, name) : + layer_i; + } + + /* Note: this is not the same as the layer_i below. */ + if (layer_i != -1) { + layer = (domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata)->layers + layer_i; + } + } + else { + layer = BKE_id_attributes_render_color_get(&me_query->id); + } + + if (!layer) { + return -1; + } + + /* Note: this is the logical index into the color attribute list, + * not the customdata index. */ + int vcol_i = BKE_id_attribute_to_index( + (ID *)me_query, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + return vcol_i; +} + static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, const Mesh *me, struct GPUMaterial **gpumat_array, @@ -522,6 +562,13 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final); + /* Create a mesh with final customdata domains + * we can query with attribute API. */ + Mesh me_query = {0}; + + BKE_id_attribute_copy_domains_temp( + ID_ME, cd_vdata, cd_edata, cd_ldata, cd_pdata, NULL, &me_query.id); + /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; mesh_cd_layers_type_clear(&cd_used); @@ -547,8 +594,35 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, type = CD_MTFACE; if (layer == -1) { + layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); + if (layer != -1) { + type = CD_PROP_COLOR; + domain = ATTR_DOMAIN_POINT; + } + } + + if (layer == -1) { + layer = CustomData_get_named_layer(cd_ldata, CD_PROP_COLOR, name); + if (layer != -1) { + type = CD_PROP_COLOR; + domain = ATTR_DOMAIN_CORNER; + } + } + + if (layer == -1) { + layer = CustomData_get_named_layer(cd_vdata, CD_MLOOPCOL, name); + if (layer != -1) { + type = CD_MLOOPCOL; + domain = ATTR_DOMAIN_POINT; + } + } + + if (layer == -1) { layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); - type = CD_MCOL; + if (layer != -1) { + type = CD_MLOOPCOL; + domain = ATTR_DOMAIN_CORNER; + } } #if 0 /* Tangents are always from UV's - this will never happen. */ @@ -619,29 +693,33 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, } 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); - } - if (layer != -1) { - cd_used.vcol |= (1 << layer); - } - break; - } case CD_ORCO: { cd_used.orco = 1; break; } + + /* Note: attr->type will always be CD_PROP_COLOR even for + * CD_MLOOPCOL layers, see node_shader_gpu_vertex_color in + * node_shader_vertex_color.cc. + */ + case CD_MCOL: + case CD_MLOOPCOL: + case CD_PROP_COLOR: { + int vcol_bit = mesh_cd_calc_gpu_layers_vcol_used(&me_query, cd_vdata, cd_ldata, name); + + if (vcol_bit != -1) { + cd_used.vcol |= 1UL << (uint)vcol_bit; + } + + break; + } + case CD_PROP_FLOAT3: case CD_PROP_BOOL: case CD_PROP_INT8: case CD_PROP_INT32: case CD_PROP_FLOAT: - case CD_PROP_FLOAT2: - case CD_PROP_FLOAT3: - case CD_PROP_COLOR: { + case CD_PROP_FLOAT2: { if (layer != -1 && domain != ATTR_DOMAIN_NUM) { drw_mesh_attributes_add_request(attributes, type, layer, domain); } @@ -799,6 +877,12 @@ static bool mesh_batch_cache_valid(Object *object, Mesh *me) return false; } + if (object->sculpt && object->sculpt->pbvh) { + if (cache->pbvh_is_drawing != BKE_pbvh_is_drawing(object->sculpt->pbvh)) { + return false; + } + } + if (cache->is_editmode != (me->edit_mesh != NULL)) { return false; } @@ -827,6 +911,10 @@ static void mesh_batch_cache_init(Object *object, Mesh *me) cache->is_editmode = me->edit_mesh != NULL; + if (object->sculpt && object->sculpt->pbvh) { + cache->pbvh_is_drawing = BKE_pbvh_is_drawing(object->sculpt->pbvh); + } + if (cache->is_editmode == false) { // cache->edge_len = mesh_render_edges_len_get(me); // cache->tri_len = mesh_render_looptri_len_get(me); @@ -1120,14 +1208,28 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Object *object, static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me) { - DRW_MeshAttributes attrs_needed; - drw_mesh_attributes_clear(&attrs_needed); - mesh_cd_calc_active_vcol_layer(object, me, &attrs_needed); + const Mesh *me_final = editmesh_final_or_this(object, me); + const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); + const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); + + Mesh me_query = {0}; + BKE_id_attribute_copy_domains_temp(ID_ME, cd_vdata, NULL, cd_ldata, NULL, NULL, &me_query.id); - BLI_assert(attrs_needed.num_requests != 0 && - "No MPropCol layer available in Sculpt, but batches requested anyway!"); + CustomDataLayer *active = BKE_id_attributes_active_color_get(&me_query.id); + CustomDataLayer *render = BKE_id_attributes_render_color_get(&me_query.id); - drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex); + int active_i = BKE_id_attribute_to_index( + &me_query.id, active, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + int render_i = BKE_id_attribute_to_index( + &me_query.id, render, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + if (active_i >= 0) { + cache->cd_used.vcol |= 1UL << (uint)active_i; + } + + if (render_i >= 0) { + cache->cd_used.vcol |= 1UL << (uint)render_i; + } } GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me) @@ -1148,6 +1250,7 @@ GPUBatch *DRW_mesh_batch_cache_get_surface(Mesh *me) { MeshBatchCache *cache = mesh_batch_cache_get(me); mesh_batch_cache_request_surface_batches(cache); + return cache->batch.surface; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index 4f4aa764fbc..f9e58709c6e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -159,17 +159,6 @@ static void init_vbo_for_attribute(const MeshRenderData *mr, GPU_vertformat_deinterleave(&format); GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode); - /* Ensure Sculpt Vertex Colors are properly aliased. */ - if (request.cd_type == CD_PROP_COLOR && request.domain == ATTR_DOMAIN_POINT) { - CustomData *cd_vdata = get_custom_data_for_domain(mr, ATTR_DOMAIN_POINT); - if (request.layer_index == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (request.layer_index == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - } - if (build_on_device) { GPU_vertbuf_init_build_on_device(vbo, &format, len); } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 7a8f4a9a17e..7d159eb3df2 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -7,11 +7,63 @@ #include "MEM_guardedalloc.h" +#include "BKE_attribute.h" #include "BLI_string.h" +#include "BLI_vector.hh" #include "draw_subdivision.h" #include "extract_mesh.h" +struct VColRef { + const CustomDataLayer *layer; + AttributeDomain domain; +}; + +/** Get all vcol layers as AttributeRefs. + * + * \param vcol_layers: bitmask to filter vcol layers by, each bit + * corresponds to the integer position of the attribute + * within the global color attribute list. + */ +static blender::Vector<VColRef> get_vcol_refs(const CustomData *cd_vdata, + const CustomData *cd_ldata, + const uint vcol_layers) +{ + blender::Vector<VColRef> refs; + uint layeri = 0; + + auto buildList = [&](const CustomData *cdata, AttributeDomain domain) { + for (int i = 0; i < cdata->totlayer; i++) { + const CustomDataLayer *layer = cdata->layers + i; + + if (!(CD_TYPE_AS_MASK(layer->type) & CD_MASK_COLOR_ALL)) { + continue; + } + + if (layer->flag & CD_FLAG_TEMPORARY) { + continue; + } + + if (!(vcol_layers & (1UL << layeri))) { + layeri++; + continue; + } + + VColRef ref; + ref.domain = domain; + ref.layer = layer; + + refs.append(ref); + layeri++; + } + }; + + buildList(cd_vdata, ATTR_DOMAIN_POINT); + buildList(cd_ldata, ATTR_DOMAIN_CORNER); + + return refs; +} + namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -21,34 +73,44 @@ namespace blender::draw { /* Initialize the common vertex format for vcol for coarse and subdivided meshes. */ static void init_vcol_format(GPUVertFormat *format, const MeshBatchCache *cache, - CustomData *cd_ldata) + CustomData *cd_vdata, + CustomData *cd_ldata, + CustomDataLayer *active, + CustomDataLayer *render) { GPU_vertformat_deinterleave(format); const uint32_t vcol_layers = cache->cd_used.vcol; - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + blender::Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers); - 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); + for (const VColRef &ref : refs) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) { - GPU_vertformat_alias_add(format, "c"); - } - if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) { - GPU_vertformat_alias_add(format, "ac"); - } + GPU_vertformat_safe_attr_name(ref.layer->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - /* 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) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(format, attr_name); - } + /* VCol layer 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); + + /* Active layer name. */ + if (ref.layer == active) { + GPU_vertformat_alias_add(format, "ac"); + } + + /* Active render layer name. */ + if (ref.layer == render) { + GPU_vertformat_alias_add(format, "c"); + } + + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs`. */ + bool bad = ref.domain == ATTR_DOMAIN_CORNER; + bad = bad && CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, ref.layer->name) != -1; + + if (!bad) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(format, attr_name); } } } @@ -77,41 +139,101 @@ static void extract_vcol_init(const MeshRenderData *mr, { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf); GPUVertFormat format = {0}; + + CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + + Mesh me_query = {0}; + + BKE_id_attribute_copy_domains_temp( + ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id); + + CustomDataLayer *active_color = BKE_id_attributes_active_color_get(&me_query.id); + CustomDataLayer *render_color = BKE_id_attributes_render_color_get(&me_query.id); + const uint32_t vcol_layers = cache->cd_used.vcol; - init_vcol_format(&format, cache, cd_ldata); + init_vcol_format(&format, cache, cd_vdata, cd_ldata, active_color, render_color); GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->loop_len); gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MLoopCol *mloopcol = (const MLoopCol *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); + blender::Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers); + + for (const VColRef &ref : refs) { + CustomData *cdata = ref.domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata; + + if (mr->extract_type == MR_EXTRACT_BMESH) { + int cd_ofs = ref.layer->offset; + + if (cd_ofs == -1) { + vcol_data += ref.domain == ATTR_DOMAIN_POINT ? mr->bm->totvert : mr->bm->totloop; + continue; + } + + BMIter iter; + const bool is_byte = ref.layer->type == CD_MLOOPCOL; + const bool is_point = ref.domain == ATTR_DOMAIN_POINT; + + BMFace *f; + BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter = f->l_first; + do { + BMElem *elem = is_point ? reinterpret_cast<BMElem *>(l_iter->v) : + reinterpret_cast<BMElem *>(l_iter); + if (is_byte) { + const MLoopCol *mloopcol = (const MLoopCol *)BM_ELEM_CD_GET_VOID_P(elem, cd_ofs); vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } + } + else { + const MPropCol *mpcol = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(elem, cd_ofs); + vcol_data->r = unit_float_to_ushort_clamp(mpcol->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(mpcol->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(mpcol->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(mpcol->color[3]); + vcol_data++; + } + } while ((l_iter = l_iter->next) != f->l_first); + } + } + else { + int totloop = mr->loop_len; + int idx = CustomData_get_named_layer_index(cdata, ref.layer->type, ref.layer->name); + + MLoopCol *mcol = nullptr; + MPropCol *pcol = nullptr; + const MLoop *mloop = mr->mloop; + + if (ref.layer->type == CD_PROP_COLOR) { + pcol = static_cast<MPropCol *>(cdata->layers[idx].data); } else { - const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); + mcol = static_cast<MLoopCol *>(cdata->layers[idx].data); + } + + const bool is_corner = ref.domain == ATTR_DOMAIN_CORNER; + + for (int i = 0; i < totloop; i++, mloop++) { + const int v_i = is_corner ? i : mloop->v; + + if (mcol) { + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[v_i].r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[v_i].g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[v_i].b]); + vcol_data->a = unit_float_to_ushort_clamp(mcol[v_i].a * (1.0f / 255.0f)); + vcol_data++; + } + else if (pcol) { + vcol_data->r = unit_float_to_ushort_clamp(pcol[v_i].color[0]); + vcol_data->g = unit_float_to_ushort_clamp(pcol[v_i].color[1]); + vcol_data->b = unit_float_to_ushort_clamp(pcol[v_i].color[2]); + vcol_data->a = unit_float_to_ushort_clamp(pcol[v_i].color[3]); + vcol_data++; } } } @@ -119,7 +241,7 @@ static void extract_vcol_init(const MeshRenderData *mr, } static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buffer, void *UNUSED(data)) @@ -127,8 +249,23 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); Mesh *coarse_mesh = subdiv_cache->mesh; + bool extract_bmesh = mr->extract_type == MR_EXTRACT_BMESH; + + const CustomData *cd_vdata = extract_bmesh ? &coarse_mesh->edit_mesh->bm->vdata : + &coarse_mesh->vdata; + const CustomData *cd_ldata = extract_bmesh ? &coarse_mesh->edit_mesh->bm->ldata : + &coarse_mesh->ldata; + + Mesh me_query = *coarse_mesh; + BKE_id_attribute_copy_domains_temp( + ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id); + + CustomDataLayer *active_color = BKE_id_attributes_active_color_get(&me_query.id); + CustomDataLayer *render_color = BKE_id_attributes_render_color_get(&me_query.id); + GPUVertFormat format = {0}; - init_vcol_format(&format, cache, &coarse_mesh->ldata); + init_vcol_format( + &format, cache, &coarse_mesh->vdata, &coarse_mesh->ldata, active_color, render_color); GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); @@ -140,32 +277,97 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, gpuMeshVcol *mesh_vcol = (gpuMeshVcol *)GPU_vertbuf_get_data(src_data); - const CustomData *cd_ldata = &coarse_mesh->ldata; - const uint vcol_layers = cache->cd_used.vcol; + blender::Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers); + + gpuMeshVcol *vcol = mesh_vcol; + /* Index of the vertex color layer in the compact buffer. Used vertex color layers are stored in * a single buffer. */ int pack_layer_index = 0; - for (int i = 0; i < MAX_MTFACE; i++) { - if (vcol_layers & (1 << i)) { - /* Include stride in offset, we use a stride of 2 since colors are packed into 2 uints. */ - const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 2 * pack_layer_index++; - const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - - gpuMeshVcol *vcol = mesh_vcol; - - for (int ml_index = 0; ml_index < coarse_mesh->totloop; ml_index++, vcol++, mloopcol++) { - vcol->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - } + for (const VColRef &ref : refs) { + /* Include stride in offset, we use a stride of 2 since colors are packed into 2 uints. */ + const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 2 * pack_layer_index++; + + const CustomData *cdata = ref.domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata; + const MLoop *ml = coarse_mesh->mloop; + + int layer_i = CustomData_get_named_layer_index(cdata, ref.layer->type, ref.layer->name); - /* Ensure data is uploaded properly. */ - GPU_vertbuf_tag_dirty(src_data); - draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset, true); + if (layer_i == -1) { + printf("%s: missing color layer %s\n", __func__, ref.layer->name); + vcol += coarse_mesh->totloop; + continue; + } + + MLoopCol *mcol = nullptr; + MPropCol *pcol = nullptr; + + if (ref.layer->type == CD_PROP_COLOR) { + pcol = static_cast<MPropCol *>(cdata->layers[layer_i].data); + } + else { + mcol = static_cast<MLoopCol *>(cdata->layers[layer_i].data); } + + const bool is_vert = ref.domain == ATTR_DOMAIN_POINT; + + if (extract_bmesh) { + BMesh *bm = coarse_mesh->edit_mesh->bm; + BMIter iter; + BMFace *f; + int cd_ofs = cdata->layers[layer_i].offset; + const bool is_byte = ref.layer->type == CD_MLOOPCOL; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l_iter = f->l_first; + + do { + BMElem *elem = is_vert ? reinterpret_cast<BMElem *>(l_iter->v) : + reinterpret_cast<BMElem *>(l_iter); + + if (is_byte) { + MLoopCol *mcol2 = static_cast<MLoopCol *>(BM_ELEM_CD_GET_VOID_P(elem, cd_ofs)); + + vcol->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol2->r]); + vcol->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol2->g]); + vcol->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol2->b]); + vcol->a = unit_float_to_ushort_clamp(mcol2->a * (1.0f / 255.0f)); + } + else { + MPropCol *pcol2 = static_cast<MPropCol *>(BM_ELEM_CD_GET_VOID_P(elem, cd_ofs)); + + vcol->r = unit_float_to_ushort_clamp(pcol2->color[0]); + vcol->g = unit_float_to_ushort_clamp(pcol2->color[1]); + vcol->b = unit_float_to_ushort_clamp(pcol2->color[2]); + vcol->a = unit_float_to_ushort_clamp(pcol2->color[3]); + } + } while ((l_iter = l_iter->next) != f->l_first); + } + } + else { + for (int ml_index = 0; ml_index < coarse_mesh->totloop; ml_index++, vcol++, ml++) { + int idx = is_vert ? ml->v : ml_index; + + if (mcol) { + vcol->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[idx].r]); + vcol->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[idx].g]); + vcol->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[idx].b]); + vcol->a = unit_float_to_ushort_clamp(mcol[idx].a * (1.0f / 255.0f)); + } + else if (pcol) { + vcol->r = unit_float_to_ushort_clamp(pcol[idx].color[0]); + vcol->g = unit_float_to_ushort_clamp(pcol[idx].color[1]); + vcol->b = unit_float_to_ushort_clamp(pcol[idx].color[2]); + vcol->a = unit_float_to_ushort_clamp(pcol[idx].color[3]); + } + } + } + + /* Ensure data is uploaded properly. */ + GPU_vertbuf_tag_dirty(src_data); + draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset, true); } GPU_vertbuf_discard(src_data); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index ebe61d217ed..8fca0c46c82 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -748,11 +748,13 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.mask brush.sculpt.multiplane_scrape brush.sculpt.nudge + brush.sculpt.paint brush.sculpt.pinch brush.sculpt.pose brush.sculpt.rotate brush.sculpt.scrape brush.sculpt.simplify + brush.sculpt.smear brush.sculpt.smooth brush.sculpt.snake_hook brush.sculpt.thumb @@ -845,6 +847,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.sculpt.border_hide ops.sculpt.border_mask ops.sculpt.box_trim + ops.sculpt.color_filter ops.sculpt.cloth_filter ops.sculpt.face_set_edit ops.sculpt.lasso_face_set @@ -852,6 +855,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.sculpt.lasso_trim ops.sculpt.line_mask ops.sculpt.line_project + ops.sculpt.mask_by_color ops.sculpt.mesh_filter ops.sequencer.blade ops.transform.bone_envelope diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 5d9d02db660..75094b46c8b 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -16,6 +16,7 @@ #include "BKE_deform.h" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" +#include "BKE_mesh.h" #include "BKE_object_deform.h" #include "BKE_report.h" @@ -103,6 +104,35 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void next_color_attribute(struct ID *id, CustomDataLayer *layer, bool is_render) +{ + int index = BKE_id_attribute_to_index(id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + index++; + + layer = BKE_id_attribute_from_index(id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + if (!layer) { + index = 0; + layer = BKE_id_attribute_from_index(id, index, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + } + + if (layer) { + if (is_render) { + BKE_id_attributes_active_color_set(id, layer); + } + else { + BKE_id_attributes_render_color_set(id, layer); + } + } +} + +static void next_color_attributes(struct ID *id, CustomDataLayer *layer) +{ + next_color_attribute(id, layer, false); /* active */ + next_color_attribute(id, layer, true); /* render */ +} + void GEOMETRY_OT_attribute_add(wmOperatorType *ot) { /* identifiers */ @@ -148,9 +178,7 @@ static int geometry_attribute_remove_exec(bContext *C, wmOperator *op) ID *id = static_cast<ID *>(ob->data); CustomDataLayer *layer = BKE_id_attributes_active_get(id); - if (layer == nullptr) { - return OPERATOR_CANCELLED; - } + next_color_attributes(id, layer); if (!BKE_id_attribute_remove(id, layer, op->reports)) { return OPERATOR_CANCELLED; @@ -182,6 +210,33 @@ void GEOMETRY_OT_attribute_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int geometry_color_attribute_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + ID *id = static_cast<ID *>(ob->data); + + char name[MAX_NAME]; + RNA_string_get(op->ptr, "name", name); + CustomDataType type = (CustomDataType)RNA_enum_get(op->ptr, "data_type"); + AttributeDomain domain = (AttributeDomain)RNA_enum_get(op->ptr, "domain"); + CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); + + if (layer == nullptr) { + return OPERATOR_CANCELLED; + } + + BKE_id_attributes_active_color_set(id, layer); + + if (!BKE_id_attributes_render_color_get(id)) { + BKE_id_attributes_render_color_set(id, layer); + } + + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + + return OPERATOR_FINISHED; +} + enum class ConvertAttributeMode { Generic, UVMap, @@ -304,6 +359,156 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Geometry Attribute"; + ot->description = "Add attribute to geometry"; + ot->idname = "GEOMETRY_OT_color_attribute_add"; + + /* api callbacks */ + ot->poll = geometry_attributes_poll; + ot->exec = geometry_color_attribute_add_exec; + ot->invoke = WM_operator_props_popup_confirm; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", "Color", MAX_NAME, "Name", "Name of color attribute"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""}, + {0, nullptr, 0, nullptr, nullptr}}; + + static EnumPropertyItem types[3] = {{CD_PROP_COLOR, "COLOR", 0, "Color", ""}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", ""}, + {0, nullptr, 0, nullptr, nullptr}}; + + prop = RNA_def_enum(ot->srna, + "domain", + domains, + ATTR_DOMAIN_POINT, + "Domain", + "Type of element that attribute is stored on"); + + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, + "data_type", + types, + CD_PROP_COLOR, + "Data Type", + "Type of data stored in attribute"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +static int geometry_color_attribute_set_render_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + ID *id = static_cast<ID *>(ob->data); + + char name[MAX_NAME]; + RNA_string_get(op->ptr, "name", name); + + CustomDataLayer *layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_POINT); + layer = !layer ? BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_POINT) : layer; + layer = !layer ? BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_CORNER) : layer; + layer = !layer ? BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_CORNER) : layer; + + if (layer) { + BKE_id_attributes_render_color_set(id, layer); + + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void GEOMETRY_OT_color_attribute_render_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Render Color Attribute"; + ot->description = "Set default color attribute used for rendering"; + ot->idname = "GEOMETRY_OT_color_attribute_render_set"; + + /* api callbacks */ + ot->poll = geometry_attributes_poll; + ot->exec = geometry_color_attribute_set_render_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; + + /* properties */ + PropertyRNA *prop; + + prop = RNA_def_string(ot->srna, "name", "Color", MAX_NAME, "Name", "Name of color attribute"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +static int geometry_color_attribute_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + ID *id = static_cast<ID *>(ob->data); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); + + if (layer == nullptr) { + return OPERATOR_CANCELLED; + } + + next_color_attributes(id, layer); + + if (!BKE_id_attribute_remove(id, layer, op->reports)) { + return OPERATOR_CANCELLED; + } + + if (GS(id->name) == ID_ME) { + Mesh *me = static_cast<Mesh *>(ob->data); + BKE_mesh_update_customdata_pointers(me, true); + } + + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + + return OPERATOR_FINISHED; +} + +static bool geometry_color_attributes_remove_poll(bContext *C) +{ + if (!geometry_attributes_poll(C)) { + return false; + } + + Object *ob = ED_object_context(C); + ID *data = ob ? static_cast<ID *>(ob->data) : nullptr; + + if (BKE_id_attributes_active_color_get(data) != nullptr) { + return true; + } + + return false; +} +void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Color Attribute"; + ot->description = "Remove color attribute from geometry"; + ot->idname = "GEOMETRY_OT_color_attribute_remove"; + + /* api callbacks */ + ot->exec = geometry_color_attribute_remove_exec; + ot->poll = geometry_color_attributes_remove_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; @@ -345,7 +550,7 @@ void GEOMETRY_OT_attribute_convert(wmOperatorType *ot) {int(ConvertAttributeMode::Generic), "GENERIC", 0, "Generic", ""}, {int(ConvertAttributeMode::UVMap), "UV_MAP", 0, "UV Map", ""}, {int(ConvertAttributeMode::VertexGroup), "VERTEX_GROUP", 0, "Vertex Group", ""}, - {int(ConvertAttributeMode::VertexColor), "VERTEX_COLOR", 0, "Vertex Color", ""}, + {int(ConvertAttributeMode::VertexColor), "VERTEX_COLOR", 0, "Color Attribute", ""}, {0, nullptr, 0, nullptr, nullptr}, }; diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh index fe56bea9f43..bbcb682d6bf 100644 --- a/source/blender/editors/geometry/geometry_intern.hh +++ b/source/blender/editors/geometry/geometry_intern.hh @@ -14,6 +14,9 @@ namespace blender::ed::geometry { /* *** geometry_attributes.cc *** */ void GEOMETRY_OT_attribute_add(struct wmOperatorType *ot); void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot); +void GEOMETRY_OT_color_attribute_add(struct wmOperatorType *ot); +void GEOMETRY_OT_color_attribute_remove(struct wmOperatorType *ot); +void GEOMETRY_OT_color_attribute_render_set(struct wmOperatorType *ot); void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot); } // namespace blender::ed::geometry diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc index a87b09f8ff3..23f6e6f29f4 100644 --- a/source/blender/editors/geometry/geometry_ops.cc +++ b/source/blender/editors/geometry/geometry_ops.cc @@ -19,5 +19,8 @@ void ED_operatortypes_geometry(void) WM_operatortype_append(GEOMETRY_OT_attribute_add); WM_operatortype_append(GEOMETRY_OT_attribute_remove); + WM_operatortype_append(GEOMETRY_OT_color_attribute_add); + WM_operatortype_append(GEOMETRY_OT_color_attribute_remove); + WM_operatortype_append(GEOMETRY_OT_color_attribute_render_set); WM_operatortype_append(GEOMETRY_OT_attribute_convert); } diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 94232a5108d..d1a6501408c 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -903,6 +903,7 @@ DEF_ICON_COLOR(BRUSH_TEXFILL) DEF_ICON_COLOR(BRUSH_TEXMASK) DEF_ICON_COLOR(BRUSH_THUMB) DEF_ICON_COLOR(BRUSH_ROTATE) +DEF_ICON_COLOR(BRUSH_PAINT) /* grease pencil sculpt */ DEF_ICON_COLOR(GPBRUSH_SMOOTH) diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index c9421d9aace..fd454083653 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -374,7 +374,7 @@ void WM_OT_alembic_export(wmOperatorType *ot) RNA_def_boolean(ot->srna, "normals", 1, "Normals", "Export normals"); - RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex Colors", "Export vertex colors"); + RNA_def_boolean(ot->srna, "vcolors", 0, "Color Attributes", "Export color attributes"); RNA_def_boolean(ot->srna, "orcos", diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index cf28c88edf0..51181f7caaa 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -524,7 +524,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "read_mesh_uvs", true, "UV Coordinates", "Read mesh UV coordinates"); - RNA_def_boolean(ot->srna, "read_mesh_colors", false, "Vertex Colors", "Read mesh vertex colors"); + RNA_def_boolean(ot->srna, "read_mesh_colors", false, "Color Attributes", "Read mesh color attributes"); RNA_def_string(ot->srna, "prim_path_mask", diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 0b2944657fd..5a0a2b7a09a 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -27,6 +27,7 @@ #include "BLI_sort_utils.h" #include "BLI_string.h" +#include "BKE_attribute.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_deform.h" @@ -37,6 +38,7 @@ #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" +#include "BKE_object.h" #include "BKE_report.h" #include "BKE_texture.h" @@ -726,7 +728,7 @@ void MESH_OT_edge_collapse(wmOperatorType *ot) /* identifiers */ ot->name = "Collapse Edges & Faces"; ot->description = - "Collapse isolated edge and face regions, merging data such as UV's and vertex colors. " + "Collapse isolated edge and face regions, merging data such as UV's and color attributes. " "This can collapse edge-rings as well as regions of connected faces into vertices"; ot->idname = "MESH_OT_edge_collapse"; @@ -3090,7 +3092,22 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op) BMOperator bmop; - EDBM_op_init(em, &bmop, op, "rotate_colors faces=%hf use_ccw=%b", BM_ELEM_SELECT, use_ccw); + Mesh *me = BKE_object_get_original_mesh(ob); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + + if (!layer || BKE_id_attribute_domain(&me->id, layer) != ATTR_DOMAIN_CORNER) { + continue; + } + + int color_index = BKE_id_attribute_to_index( + &me->id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + EDBM_op_init(em, + &bmop, + op, + "rotate_colors faces=%hf use_ccw=%b color_index=%i", + BM_ELEM_SELECT, + use_ccw, + color_index); BMO_op_exec(em->bm, &bmop); @@ -3127,9 +3144,17 @@ static int edbm_reverse_colors_exec(bContext *C, wmOperator *op) continue; } + Mesh *me = BKE_object_get_original_mesh(obedit); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + + if (!layer || BKE_id_attribute_domain(&me->id, layer) != ATTR_DOMAIN_CORNER) { + continue; + } + BMOperator bmop; - EDBM_op_init(em, &bmop, op, "reverse_colors faces=%hf", BM_ELEM_SELECT); + int color_index = BKE_id_attribute_to_index(&me->id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + EDBM_op_init(em, &bmop, op, "reverse_colors faces=%hf color_index=%i", BM_ELEM_SELECT, color_index); BMO_op_exec(em->bm, &bmop); @@ -3190,7 +3215,7 @@ void MESH_OT_colors_rotate(wmOperatorType *ot) /* identifiers */ ot->name = "Rotate Colors"; ot->idname = "MESH_OT_colors_rotate"; - ot->description = "Rotate vertex colors inside faces"; + ot->description = "Rotate color attributes inside faces"; /* api callbacks */ ot->exec = edbm_rotate_colors_exec; diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 630ef66504f..6f5c9d410c7 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -17,6 +17,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BKE_attribute.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" @@ -429,6 +430,9 @@ bool ED_mesh_color_ensure(struct Mesh *me, const char *name) if (!me->mloopcol && me->totloop) { CustomData_add_layer_named(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, name); + int layer_i = CustomData_get_layer_index(&me->ldata, CD_MLOOPCOL); + + BKE_id_attributes_active_color_set(&me->id, me->ldata.layers + layer_i); BKE_mesh_update_customdata_pointers(me, true); } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 44f05600d7d..fec87fbfa95 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -442,7 +442,7 @@ static bool bake_object_check(ViewLayer *view_layer, if (target == R_BAKE_TARGET_VERTEX_COLORS) { MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); - const bool mcol_valid = (mcol != NULL && U.experimental.use_sculpt_vertex_colors); + const bool mcol_valid = (mcol != NULL); MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); if (mloopcol == NULL && !mcol_valid) { BKE_reportf(reports, @@ -926,7 +926,7 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re Mesh *me = ob->data; MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); - const bool mcol_valid = (mcol != NULL && U.experimental.use_sculpt_vertex_colors); + const bool mcol_valid = (mcol != NULL); MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); if (mloopcol == NULL && !mcol_valid) { BKE_report(reports, RPT_ERROR, "No vertex colors layer found to bake to"); @@ -1080,7 +1080,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) { Mesh *me = ob->data; MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); - const bool mcol_valid = (mcol != NULL && U.experimental.use_sculpt_vertex_colors); + const bool mcol_valid = (mcol != NULL); MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); const int channels_num = targets->channels_num; const float *result = targets->result; diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 6805c9144d6..3447ded5f68 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -72,7 +72,7 @@ static const EnumPropertyItem DT_layer_items[] = { "Transfer Freestyle edge mark"}, {0, "", 0, "Face Corner Data", ""}, {DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"}, - {DT_TYPE_VCOL, "VCOL", 0, "Vertex Colors", "Vertex (face corners) colors"}, + {DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, "VCOL", 0, "Colors", "Color Attributes"}, {DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"}, {0, "", 0, "Face Data", ""}, {DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"}, @@ -84,6 +84,33 @@ static const EnumPropertyItem DT_layer_items[] = { {0, NULL, 0, NULL, NULL}, }; +static void dt_add_vcol_layers(CustomData *cdata, + CustomDataMask mask, + EnumPropertyItem **r_item, + int *r_totitem) +{ + int types[2] = {CD_PROP_COLOR, CD_MLOOPCOL}; + + for (int i = 0; i < 2; i++) { + CustomDataType type = types[i]; + + if (!(mask & CD_TYPE_AS_MASK(type))) { + continue; + } + + int num_data = CustomData_number_of_layers(cdata, type); + + RNA_enum_item_add_separator(r_item, r_totitem); + + for (int j = 0; j < num_data; j++) { + EnumPropertyItem tmp_item; + + tmp_item.value = j; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(cdata, type, j); + RNA_enum_item_add(r_item, r_totitem, &tmp_item); + } + } +} /* NOTE: #rna_enum_dt_layers_select_src_items enum is from rna_modifier.c. */ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, PointerRNA *ptr, @@ -159,23 +186,33 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, RNA_enum_item_add(&item, &totitem, &tmp_item); } } - else if (data_type == DT_TYPE_VCOL) { + else if (data_type & DT_TYPE_VCOL_ALL) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.lmask |= CD_MASK_MLOOPCOL; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL); + if (data_type & (DT_TYPE_MPROPCOL_VERT)) { + cddata_masks.vmask |= CD_MASK_PROP_COLOR; + } + if (data_type & (DT_TYPE_MLOOPCOL_VERT)) { + cddata_masks.vmask |= CD_MASK_MLOOPCOL; + } - RNA_enum_item_add_separator(&item, &totitem); + if (data_type & (DT_TYPE_MPROPCOL_LOOP)) { + cddata_masks.lmask |= CD_MASK_PROP_COLOR; + } + if (data_type & (DT_TYPE_MLOOPCOL_LOOP)) { + cddata_masks.lmask |= CD_MASK_MLOOPCOL; + } - for (int i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( - &me_eval->ldata, CD_MLOOPCOL, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); + + if (data_type & (DT_TYPE_MLOOPCOL_VERT | DT_TYPE_MPROPCOL_VERT)) { + dt_add_vcol_layers(&me_eval->vdata, cddata_masks.vmask, &item, &totitem); + } + if (data_type & (DT_TYPE_MLOOPCOL_LOOP | DT_TYPE_MPROPCOL_LOOP)) { + dt_add_vcol_layers(&me_eval->ldata, cddata_masks.lmask, &item, &totitem); } } diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 57d4bd67438..944b3f953a0 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -376,7 +376,7 @@ static int hide_show_exec(bContext *C, wmOperator *op) } /* End undo. */ - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Ensure that edges and faces get hidden as well (not used by * sculpt but it looks wrong when entering editmode otherwise). */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 5e89a4823db..5929aa75b45 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -173,7 +173,7 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); if (nodes) { MEM_freeN(nodes); @@ -707,7 +707,8 @@ static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext) operation->sculpt_gesture_end(C, sgcontext); - SCULPT_undo_push_end(); + Object *ob = CTX_data_active_object(C); + SCULPT_undo_push_end(ob); SCULPT_tag_update_overlays(C); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 3f0f97dffd2..33b92c22d3f 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -27,6 +27,7 @@ #include "RNA_access.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -190,7 +191,14 @@ bool vertex_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly; + if (!(ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly)) { + return false; + } + + CustomDataLayer *layer = BKE_id_attributes_active_color_get((ID *)ob->data); + AttributeDomain domain = BKE_id_attribute_domain((ID *)ob->data, layer); + + return layer && layer->type == CD_MLOOPCOL && domain == ATTR_DOMAIN_CORNER; } static bool vertex_paint_poll_ex(bContext *C, bool check_tool) @@ -3559,7 +3567,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /* identifiers */ ot->name = "Vertex Paint"; ot->idname = "PAINT_OT_vertex_paint"; - ot->description = "Paint a stroke in the active vertex color layer"; + ot->description = "Paint a stroke in the active color attribute layer"; /* api callbacks */ ot->invoke = vpaint_invoke; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e82ac058281..e03f12025c8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -31,6 +31,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_colortools.h" @@ -143,19 +144,27 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index) return NULL; } -const float *SCULPT_vertex_color_get(SculptSession *ss, int index) +bool SCULPT_has_loop_colors(const Object *ob) { - 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; + Mesh *me = BKE_object_get_original_mesh(ob); + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + + return layer && BKE_id_attribute_domain(&me->id, layer) == ATTR_DOMAIN_CORNER; +} + +bool SCULPT_has_colors(const SculptSession *ss) +{ + return ss->vcol || ss->mcol; +} + +void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]) +{ + BKE_pbvh_vertex_color_get(ss->pbvh, index, r_color); +} + +void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]) +{ + BKE_pbvh_vertex_color_set(ss->pbvh, index, color); } void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) @@ -1045,6 +1054,7 @@ void SCULPT_tag_update_overlays(bContext *C) WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + View3D *v3d = CTX_wm_view3d(C); if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -1378,7 +1388,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, *vd.mask = orig_data.mask; } else if (orig_data.unode->type == SCULPT_UNDO_COLOR) { - copy_v4_v4(vd.col, orig_data.col); + SCULPT_vertex_color_set(ss, vd.index, orig_data.col); } if (vd.mvert) { @@ -3149,7 +3159,7 @@ static void do_brush_action_task_cb(void *__restrict userdata, 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)) { + else if (SCULPT_TOOL_NEEDS_COLOR(data->brush->sculpt_tool)) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); BKE_pbvh_node_mark_update_color(data->nodes[n]); } @@ -3167,12 +3177,13 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* 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; + if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { + if (type != PBVH_FACES) { + return; + } + + BKE_pbvh_ensure_node_loops(ss->pbvh); } /* Build a list of all nodes that are potentially within the brush's area of influence */ @@ -3188,6 +3199,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; float radius_scale = 1.0f; + /* With these options enabled not all required nodes are inside the original brush radius, so * the brush can produce artifacts in some situations. */ if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { @@ -3849,10 +3861,12 @@ bool SCULPT_mode_poll(bContext *C) bool SCULPT_vertex_colors_poll(bContext *C) { - if (!U.experimental.use_sculpt_vertex_colors) { + if (!SCULPT_mode_poll(C)) { return false; } - return SCULPT_mode_poll(C); + + Object *ob = CTX_data_active_object(C); + return ob->sculpt && SCULPT_has_colors(ob->sculpt); } bool SCULPT_mode_poll_view3d(bContext *C) @@ -4591,6 +4605,7 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, (brush->sculpt_tool == SCULPT_TOOL_POSE) || (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) || (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) || + SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) || (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR)); @@ -4950,7 +4965,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, needs_colors; + bool need_pmap, needs_colors; bool need_mask = false; if (brush->sculpt_tool == SCULPT_TOOL_MASK) { @@ -4965,8 +4980,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) view3d_operator_needs_opengl(C); sculpt_brush_init_tex(scene, sd, ss); - is_smooth = sculpt_needs_connectivity_info(sd, brush, ss, mode); - needs_colors = ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR); + need_pmap = sculpt_needs_connectivity_info(sd, brush, ss, mode); + needs_colors = SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool); if (needs_colors) { BKE_sculpt_color_layer_create_if_needed(ob); @@ -4975,7 +4990,7 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - BKE_sculpt_update_object_for_edit(depsgraph, ob, is_smooth, need_mask, needs_colors); + BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, need_mask, needs_colors); } static void sculpt_restore_mesh(Sculpt *sd, Object *ob) @@ -5174,6 +5189,14 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { + View3D *v3d = CTX_wm_view3d(C); + if (v3d) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } + } ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); @@ -5302,7 +5325,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str SCULPT_cache_free(ss->cache); ss->cache = NULL; - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); if (brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index a4cfb611138..dcf90f9e819 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1493,7 +1493,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index dd8921d575f..fe69cf6b84f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -117,7 +117,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) } MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Force rebuild of PBVH for better BB placement. */ SCULPT_pbvh_clear(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 58da5adc5e3..4f884420401 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -274,7 +274,7 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, } SCULPT_dynamic_topology_disable_ex(bmain, depsgraph, scene, ob, NULL); if (use_undo) { - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } } } @@ -294,7 +294,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (use_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 2ef35d540b9..46940b619e6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1128,7 +1128,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp PBVHNode *node = nodes[n]; PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - copy_v4_v4(vd.col, expand_cache->original_colors[vd.index]); + SCULPT_vertex_color_set(ss, vd.index, expand_cache->original_colors[vd.index]); } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_redraw(node); @@ -1192,7 +1192,7 @@ static void sculpt_expand_cancel(bContext *C, wmOperator *UNUSED(op)) sculpt_expand_restore_original_state(C, ob, ss->expand_cache); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); sculpt_expand_cache_free(ss); } @@ -1287,7 +1287,7 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { float initial_color[4]; - copy_v4_v4(initial_color, vd.col); + SCULPT_vertex_color_get(ss, vd.index, initial_color); const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); float fade; @@ -1314,7 +1314,8 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, continue; } - copy_v4_v4(vd.col, final_color); + SCULPT_vertex_color_set(ss, vd.index, final_color); + any_changed = true; if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -1370,7 +1371,7 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) { expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors"); for (int i = 0; i < totvert; i++) { - copy_v4_v4(expand_cache->original_colors[i], SCULPT_vertex_color_get(ss, i)); + SCULPT_vertex_color_get(ss, i, expand_cache->original_colors[i]); } } } @@ -1526,7 +1527,7 @@ static void sculpt_expand_finish(bContext *C) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Tag all nodes to redraw to avoid artifacts after the fast partial updates. */ PBVHNode **nodes; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 23bc9fbb54d..7171c241534 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -398,7 +398,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_tag_update_overlays(C); @@ -737,7 +737,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) break; } - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ SCULPT_visibility_sync_all_face_sets_to_vertices(ob); @@ -927,7 +927,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) /* Sync face sets visibility and vertex visibility. */ SCULPT_visibility_sync_all_face_sets_to_vertices(ob); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); @@ -1354,7 +1354,7 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, SCULPT_undo_push_begin(ob, "face set edit"); SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); face_set_edit_do_post_visibility_updates(ob, nodes, totnode); MEM_freeN(nodes); } @@ -1382,7 +1382,7 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, } SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); MEM_freeN(nodes); } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 377f1e0ed32..cbb9180a209 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -181,12 +181,16 @@ static void color_filter_task_cb(void *__restrict userdata, fade = clamp_f(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); + + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + + blend_color_interpolate_float(final_color, col, smooth_color, fade); break; } } - copy_v3_v3(vd.col, final_color); + SCULPT_vertex_color_set(ss, vd.index, final_color); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -205,7 +209,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent float filter_strength = RNA_float_get(op->ptr, "strength"); if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_filter_cache_free(ss); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR); return OPERATOR_FINISHED; @@ -247,7 +251,6 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(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; const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL); @@ -269,7 +272,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - if (!ss->vcol) { + if (!SCULPT_has_colors(ss)) { return OPERATOR_CANCELLED; } @@ -280,10 +283,9 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - const bool needs_topology_info = mode == COLOR_FILTER_SMOOTH || use_automasking; - BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, true); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); - if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) { + if (BKE_pbvh_type(pbvh) == PBVH_FACES && !ob->sculpt->pmap) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index e88265714b8..c8137301de5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -239,7 +239,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_tag_update_overlays(C); @@ -447,7 +447,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); ED_region_tag_redraw(region); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index b73e182fcab..4b832256dae 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -110,6 +110,10 @@ void SCULPT_filter_cache_init(bContext *C, Object *ob, Sculpt *sd, const int und ss->filter_cache->random_seed = rand(); + if (undo_type == SCULPT_UNDO_COLOR) { + BKE_pbvh_ensure_node_loops(ss->pbvh); + } + const float center[3] = {0.0f}; SculptSearchSphereData search_data = { .original = true, @@ -597,7 +601,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 8cab0349356..73fc5bd68f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -20,6 +20,10 @@ #include "BLI_gsqueue.h" #include "BLI_threads.h" +#ifdef __cplusplus +extern "C" { +#endif + struct AutomaskingCache; struct KeyBlock; struct Object; @@ -139,9 +143,16 @@ typedef struct SculptUndoNode { float *mask; int totvert; + float (*loop_col)[4]; + float (*orig_loop_col)[4]; + int totloop; + /* non-multires */ int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ + int *index; /* Unique vertex indices, to restore into right location */ + int maxloop; + int *loop_index; + BLI_bitmap *vert_hidden; /* multires */ @@ -857,7 +868,14 @@ 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); +void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]); +void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]); + +/** Returns true if a color attribute exists in the current sculpt session. */ +bool SCULPT_has_colors(const SculptSession *ss); + +/** Returns true if the active color attribute is on loop (ATTR_DOMAIN_CORNER) domain. */ +bool SCULPT_has_loop_colors(const struct Object *ob); const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); @@ -1416,8 +1434,8 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node); SculptUndoNode *SCULPT_undo_get_first_node(void); void SCULPT_undo_push_begin(struct Object *ob, const char *name); -void SCULPT_undo_push_end(void); -void SCULPT_undo_push_end_ex(bool use_nested_undo); +void SCULPT_undo_push_end(struct Object *ob); +void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo); /** \} */ @@ -1720,3 +1738,9 @@ void SCULPT_bmesh_topology_rake( void SCULPT_OT_brush_stroke(struct wmOperatorType *ot); /* end sculpt_ops.c */ + +#define SCULPT_TOOL_NEEDS_COLOR(tool) ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 8fc10061f83..201e02b8235 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -82,7 +82,7 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); } SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, NULL); } @@ -237,7 +237,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * SCULPT_filter_cache_free(ss); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index 42101988b11..025f34ab2d7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -150,7 +150,7 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op) multires_stitch_grids(ob); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); MEM_SAFE_FREE(nodes); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index cd174681ccb..f84852d1d0e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -234,7 +234,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) /* Finish undo. */ BM_log_all_added(ss->bm, ss->bm_log); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); break; case PBVH_FACES: @@ -396,7 +396,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (has_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } } else { @@ -508,6 +508,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); if (wm->op_undo_depth <= 1) { SCULPT_undo_push_begin(ob, op->type->name); + SCULPT_undo_push_end(ob); } } } @@ -749,11 +750,14 @@ static int sculpt_sample_color_invoke(bContext *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) { + float active_vertex_color[4]; + + if (!SCULPT_has_colors(ss)) { return OPERATOR_CANCELLED; } + SCULPT_vertex_color_get(ss, active_vertex, active_vertex_color); + float color_srgb[3]; copy_v3_v3(color_srgb, active_vertex_color); IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); @@ -862,7 +866,7 @@ static void do_mask_by_color_contiguous_update_nodes_cb( } BKE_pbvh_vertex_iter_end; if (update_node) { - BKE_pbvh_node_mark_redraw(data->nodes[n]); + BKE_pbvh_node_mark_update_mask(data->nodes[n]); } } @@ -870,7 +874,10 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) { MaskByColorContiguousFloodFillData *data = userdata; - const float *current_color = SCULPT_vertex_color_get(ss, to_v); + float current_color[4]; + + SCULPT_vertex_color_get(ss, to_v, current_color); + float new_vertex_mask = sculpt_mask_by_color_delta_get( current_color, data->initial_color, data->threshold, data->invert); data->new_mask[to_v] = new_vertex_mask; @@ -909,7 +916,11 @@ static void sculpt_mask_by_color_contiguous(Object *object, ffd.threshold = threshold; ffd.invert = invert; ffd.new_mask = new_mask; - copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex)); + + float color[4]; + SCULPT_vertex_color_get(ss, vertex, color); + + copy_v3_v3(ffd.initial_color, color); SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd); SCULPT_floodfill_free(&flood); @@ -951,12 +962,17 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, const float threshold = data->mask_by_color_threshold; const bool invert = data->mask_by_color_invert; const bool preserve_mask = data->mask_by_color_preserve_mask; - const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex); + float active_color[4]; + + SCULPT_vertex_color_get(ss, data->mask_by_color_vertex, active_color); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + const float current_mask = *vd.mask; - const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert); + const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert); *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); if (current_mask == *vd.mask) { @@ -1014,10 +1030,14 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven return OPERATOR_CANCELLED; } - if (!ss->vcol) { + if (!SCULPT_has_colors(ss)) { return OPERATOR_CANCELLED; } + if (SCULPT_has_loop_colors(ob)) { + BKE_pbvh_ensure_node_loops(ss->pbvh); + } + SCULPT_vertex_random_access_ensure(ss); /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, @@ -1043,9 +1063,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven } BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 5d248cb520a..cc4392c6a8a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -85,7 +85,11 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, float smooth_color[4]; SCULPT_neighbor_color_average(ss, smooth_color, vd.index); - blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade); + float col[4]; + + SCULPT_vertex_color_get(ss, vd.index, col); + blend_color_interpolate_float(col, col, smooth_color, fade); + SCULPT_vertex_color_set(ss, vd.index, col); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -154,7 +158,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, float noise = 1.0f; const float density = ss->cache->paint_brush.density; if (density < 1.0f) { - const float hash_noise = BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index); + const float hash_noise = (float) BLI_hash_int_01(ss->cache->density_seed * 1000 * vd.index); if (hash_noise > density) { noise = density * hash_noise; fade = fade * noise; @@ -177,9 +181,11 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, /* Final mix over the original color using brush alpha. */ mul_v4_v4fl(buffer_color, color_buffer->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); + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + IMB_blend_color_float(col, orig_data.col, buffer_color, brush->blend); + CLAMP4(col, 0.0f, 1.0f); + SCULPT_vertex_color_set(ss, vd.index, col); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -214,7 +220,10 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata, continue; } - add_v4_v4(swptd->color, vd.col); + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + + add_v4_v4(swptd->color, col); swptd->tot_samples++; } BKE_pbvh_vertex_iter_end; @@ -236,13 +245,13 @@ 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) { + if (!SCULPT_has_colors(ss)) { return; } if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - ss->cache->density_seed = BLI_hash_int_01(ss->cache->location[0] * 1000); + ss->cache->density_seed = (float) BLI_hash_int_01(ss->cache->location[0] * 1000); } return; } @@ -384,6 +393,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float interp_color[4]; copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]); + float no[3]; + SCULPT_vertex_normal_get(ss, vd.index, no); + switch (brush->smear_deform_type) { case BRUSH_SMEAR_DEFORM_DRAG: sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); @@ -395,29 +407,89 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, sub_v3_v3v3(current_disp, vd.co, ss->cache->location); break; } + + /* Project into vertex plane. */ + madd_v3_v3fl(current_disp, no, -dot_v3v3(current_disp, no)); + 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) { - continue; + float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float totw = 0.0f; + + /* + * NOTE: we have to do a nested iteration here to avoid + * blocky artifacts on quad topologies. The runtime cost + * is not as bad as it seems due to neighbor iteration + * in the sculpt code being cache bound; once the data is in + * the cache iterating over it a few more times is not terribly + * costly. + */ + + SculptVertexNeighborIter ni2; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni2) { + const float *nco = SCULPT_vertex_co_get(ss, ni2.index); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.index, ni) { + if (ni.index == vd.index) { + continue; + } + + float vertex_disp[3]; + float vertex_disp_norm[3]; + + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + + /* Weight by how close we are to our target distance from vd.co. */ + float w = (1.0f + fabsf(len_v3(vertex_disp) / ss->cache->bstrength - 1.0f)); + + /* TODO: use cotangents (or at least face areas) here. */ + float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco); + if (len > 0.0f) { + len = ss->cache->bstrength / len; + } + else { /* Coincident point. */ + len = 1.0f; + } + + /* Multiply weight with edge lengths (in the future this will be + cotangent weights or face areas). */ + w *= len; + + /* Build directional weight. */ + + /* Project into vertex plane. */ + madd_v3_v3fl(vertex_disp, no, -dot_v3v3(no, vertex_disp)); + normalize_v3_v3(vertex_disp_norm, vertex_disp); + + if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) { + continue; + } + + const float *neighbor_color = ss->cache->prev_colors[ni.index]; + float color_interp = -dot_v3v3(current_disp_norm, vertex_disp_norm); + + /* Square directional weight to get a somewhat sharper result. */ + w *= color_interp * color_interp; + + madd_v4_v4fl(accum, neighbor_color, w); + totw += w; } - 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); } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); + + if (totw != 0.0f) { + mul_v4_fl(accum, 1.0f / totw); + } + + blend_color_mix_float(interp_color, interp_color, accum); - blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade); + float col[4]; + SCULPT_vertex_color_get(ss, vd.index, col); + blend_color_interpolate_float(col, ss->cache->prev_colors[vd.index], interp_color, fade); + SCULPT_vertex_color_set(ss, vd.index, col); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -435,7 +507,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, 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)); + SCULPT_vertex_color_get(ss, vd.index, ss->cache->prev_colors[vd.index]); } BKE_pbvh_vertex_iter_end; } @@ -445,7 +517,7 @@ 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) { + if (!SCULPT_has_colors(ss)) { return; } @@ -455,7 +527,7 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode 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)); + SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 482bdf97d78..53babc3d36d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -179,7 +179,11 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index)); + float tmp[4] = {0}; + + SCULPT_vertex_color_get(ss, ni.index, tmp); + + add_v4_v4(avg, tmp); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -188,7 +192,7 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index mul_v4_v4fl(result, avg, 1.0f / total); } else { - copy_v4_v4(result, SCULPT_vertex_color_get(ss, index)); + SCULPT_vertex_color_get(ss, index, result); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 5f55546c8a0..b3616254b26 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -203,7 +203,7 @@ void ED_sculpt_end_transform(struct bContext *C, Object *ob) * undo system works separate from regular undo and this is require to properly * finish an undo step also when canceling. */ const bool use_nested_undo = true; - SCULPT_undo_push_end_ex(use_nested_undo); + SCULPT_undo_push_end_ex(ob, use_nested_undo); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 2ec553c63c7..1354277fbdd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -24,6 +24,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "BKE_attribute.h" #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_customdata.h" @@ -31,6 +32,7 @@ #include "BKE_key.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_multires.h" #include "BKE_object.h" @@ -94,13 +96,38 @@ * End of dynamic topology and symmetrize in this mode are handled in a special * manner as well. */ +#define NO_ACTIVE_LAYER ATTR_DOMAIN_AUTO + typedef struct UndoSculpt { ListBase nodes; size_t undo_size; } UndoSculpt; +typedef struct SculptAttrRef { + AttributeDomain domain; + int type; + char name[MAX_CUSTOMDATA_LAYER_NAME]; + bool was_set; +} SculptAttrRef; + +typedef struct SculptUndoStep { + UndoStep step; + /* NOTE: will split out into list for multi-object-sculpt-mode. */ + UndoSculpt data; + + /* Active color attribute at the start of this undo step. */ + SculptAttrRef active_color_start; + + /* Active color attribute at the end of this undo step. */ + SculptAttrRef active_color_end; + + bContext *C; +} SculptUndoStep; + static UndoSculpt *sculpt_undo_get_nodes(void); +static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b); +static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr); static void update_cb(PBVHNode *node, void *rebuild) { @@ -313,17 +340,30 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode) Object *ob = OBACT(view_layer); SculptSession *ss = ob->sculpt; - if (unode->maxvert) { - /* regular mesh restore */ - int *index = unode->index; - MPropCol *vcol = ss->vcol; + bool modified = false; + /* NOTE: even with loop colors we still store derived + * vertex colors for original data lookup.*/ + if (unode->col && !unode->loop_col) { + BKE_pbvh_swap_colors(ss->pbvh, unode->index, unode->totvert, unode->col); + modified = true; + } + + Mesh *me = BKE_object_get_original_mesh(ob); + + if (unode->loop_col && unode->maxloop == me->totloop) { + BKE_pbvh_swap_colors(ss->pbvh, unode->loop_index, unode->totloop, unode->loop_col); + + modified = true; + } + + if (modified) { for (int i = 0; i < unode->totvert; i++) { - copy_v4_v4(vcol[index[i]].color, unode->col[i]); - BKE_pbvh_vert_mark_update(ss->pbvh, index[i]); + BKE_pbvh_vert_mark_update(ss->pbvh, unode->index[i]); } } - return true; + + return modified; } static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) @@ -739,8 +779,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase if (sculpt_undo_restore_color(C, unode)) { update = true; } - break; + break; case SCULPT_UNDO_GEOMETRY: need_refine_subdiv = true; sculpt_undo_geometry_restore(unode, ob); @@ -843,12 +883,21 @@ static void sculpt_undo_free_list(ListBase *lb) if (unode->co) { MEM_freeN(unode->co); } + if (unode->col) { + MEM_freeN(unode->col); + } + if (unode->loop_col) { + MEM_freeN(unode->loop_col); + } if (unode->no) { MEM_freeN(unode->no); } if (unode->index) { MEM_freeN(unode->index); } + if (unode->loop_index) { + MEM_freeN(unode->loop_index); + } if (unode->grids) { MEM_freeN(unode->grids); } @@ -1010,6 +1059,21 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt unode->totvert = totvert; } + bool need_loops = type == SCULPT_UNDO_COLOR; + + if (need_loops) { + int totloop; + + BKE_pbvh_node_num_loops(ss->pbvh, node, &totloop); + + unode->loop_index = MEM_calloc_arrayN(totloop, sizeof(int), __func__); + unode->maxloop = 0; + unode->totloop = totloop; + + size_t alloc_size = sizeof(int) * (size_t)totloop; + usculpt->undo_size += alloc_size; + } + switch (type) { case SCULPT_UNDO_COORDS: { size_t alloc_size = sizeof(*unode->co) * (size_t)allvert; @@ -1041,9 +1105,20 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt break; } case SCULPT_UNDO_COLOR: { + /* Allocate vertex colors, even for loop colors we still + * need this for original data lookup. */ const size_t alloc_size = sizeof(*unode->col) * (size_t)allvert; unode->col = MEM_callocN(alloc_size, "SculptUndoNode.col"); usculpt->undo_size += alloc_size; + + /* Allocate loop colors separately too. */ + if (ss->vcol_domain == ATTR_DOMAIN_CORNER) { + size_t alloc_size_loop = sizeof(float) * 4 * (size_t)unode->totloop; + + unode->loop_col = MEM_calloc_arrayN( + unode->totloop, sizeof(float) * 4, "SculptUndoNode.loop_col"); + usculpt->undo_size += alloc_size_loop; + } break; } case SCULPT_UNDO_DYNTOPO_BEGIN: @@ -1139,12 +1214,19 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) 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); + BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); + + int allvert; + BKE_pbvh_node_num_verts(ss->pbvh, unode->node, NULL, &allvert); + + /* NOTE: even with loop colors we still store (derived) + * vertex colors for original data lookup. */ + BKE_pbvh_store_colors_vertex(ss->pbvh, unode->index, allvert, unode->col); + + if (unode->loop_col && unode->totloop) { + BKE_pbvh_store_colors(ss->pbvh, unode->loop_index, unode->totloop, unode->loop_col); } - BKE_pbvh_vertex_iter_end; } static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode) @@ -1316,11 +1398,23 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType memcpy(unode->grids, grids, sizeof(int) * totgrid); } else { - const int *vert_indices; - int allvert; - BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); + const int *vert_indices, *loop_indices; + int allvert, allloop; + + BKE_pbvh_node_num_verts(ss->pbvh, unode->node, NULL, &allvert); BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); - memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); + memcpy(unode->index, vert_indices, sizeof(int) * allvert); + + if (unode->loop_index) { + BKE_pbvh_node_num_loops(ss->pbvh, unode->node, &allloop); + BKE_pbvh_node_get_loops(ss->pbvh, unode->node, &loop_indices, NULL); + + if (allloop) { + memcpy(unode->loop_index, loop_indices, sizeof(int) * allloop); + + unode->maxloop = BKE_object_get_original_mesh(ob)->totloop; + } + } } switch (type) { @@ -1362,6 +1456,29 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType return unode; } +static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b) +{ + return a->domain == b->domain && a->type == b->type && STREQ(a->name, b->name); +} + +static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr) +{ + Mesh *me = BKE_object_get_original_mesh(ob); + CustomDataLayer *layer; + + if (ob && me && (layer = BKE_id_attributes_active_color_get((ID *)me))) { + attr->domain = BKE_id_attribute_domain((ID *)me, layer); + BLI_strncpy(attr->name, layer->name, sizeof(attr->name)); + attr->type = layer->type; + } + else { + attr->domain = NO_ACTIVE_LAYER; + attr->name[0] = 0; + } + + attr->was_set = true; +} + void SCULPT_undo_push_begin(Object *ob, const char *name) { UndoStack *ustack = ED_undo_stack_get(); @@ -1376,15 +1493,28 @@ void SCULPT_undo_push_begin(Object *ob, const char *name) /* Special case, we never read from this. */ bContext *C = NULL; - BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); + SculptUndoStep *us = (SculptUndoStep *)BKE_undosys_step_push_init_with_type( + ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); + + if (!us->active_color_start.was_set) { + sculpt_save_active_attribute(ob, &us->active_color_start); + } + + /* Set end attribute in case SCULPT_undo_push_end is not called, + * so we don't end up with corrupted state. + */ + if (!us->active_color_end.was_set) { + sculpt_save_active_attribute(ob, &us->active_color_end); + us->active_color_end.was_set = false; + } } -void SCULPT_undo_push_end(void) +void SCULPT_undo_push_end(Object *ob) { - SCULPT_undo_push_end_ex(false); + SCULPT_undo_push_end_ex(ob, false); } -void SCULPT_undo_push_end_ex(const bool use_nested_undo) +void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptUndoNode *unode; @@ -1408,17 +1538,54 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) } WM_file_tag_modified(); } + + UndoStack *ustack = ED_undo_stack_get(); + SculptUndoStep *us = (SculptUndoStep *)BKE_undosys_stack_init_or_active_with_type( + ustack, BKE_UNDOSYS_TYPE_SCULPT); + + sculpt_save_active_attribute(ob, &us->active_color_end); } /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System * \{ */ -typedef struct SculptUndoStep { - UndoStep step; - /* NOTE: will split out into list for multi-object-sculpt-mode. */ - UndoSculpt data; -} SculptUndoStep; +static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr) +{ + if (attr->domain == ATTR_DOMAIN_AUTO) { + return; + } + + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_object_get_original_mesh(ob); + + SculptAttrRef existing; + sculpt_save_active_attribute(ob, &existing); + + CustomDataLayer *layer; + layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + + if (!layer) { + /* Memfile undo killed the layer; re-create it. */ + CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &me->vdata : &me->ldata; + int totelem = attr->domain == ATTR_DOMAIN_POINT ? me->totvert : me->totloop; + + CustomData_add_layer_named(cdata, attr->type, CD_DEFAULT, NULL, totelem, attr->name); + layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); + } + + if (layer) { + BKE_id_attributes_active_color_set(&me->id, layer); + + if (ob->sculpt && ob->sculpt->pbvh) { + BKE_pbvh_update_active_vcol(ob->sculpt->pbvh, me); + + if (!sculpt_attribute_ref_equals(&existing, attr)) { + BKE_pbvh_update_vertex_data(ob->sculpt->pbvh, PBVH_UpdateColor); + } + } + } +} static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { @@ -1454,6 +1621,7 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == true); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = false; } @@ -1463,6 +1631,7 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == false); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = true; } @@ -1484,10 +1653,17 @@ static void sculpt_undosys_step_decode_undo(struct bContext *C, while ((us_iter != us) || (!is_final && us_iter == us)) { BLI_assert(us_iter->step.type == us->step.type); /* Previous loop ensures this. */ + + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start); sculpt_undosys_step_decode_undo_impl(C, depsgraph, us_iter); + if (us_iter == us) { + if (us_iter->step.prev && us_iter->step.prev->type == BKE_UNDOSYS_TYPE_SCULPT) { + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter->step.prev)->active_color_end); + } break; } + us_iter = (SculptUndoStep *)us_iter->step.prev; } } @@ -1504,8 +1680,11 @@ static void sculpt_undosys_step_decode_redo(struct bContext *C, us_iter = (SculptUndoStep *)us_iter->step.prev; } while (us_iter && (us_iter->step.is_applied == false)) { + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start); sculpt_undosys_step_decode_redo_impl(C, depsgraph, us_iter); + if (us_iter == us) { + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_end); break; } us_iter = (SculptUndoStep *)us_iter->step.next; @@ -1526,7 +1705,7 @@ static void sculpt_undosys_step_decode( ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); if (ob && (ob->type == OB_MESH)) { - if (ob->mode & OB_MODE_SCULPT) { + if (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT)) { /* Pass. */ } else { @@ -1579,7 +1758,7 @@ void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name) void ED_sculpt_undo_geometry_end(struct Object *ob) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); - SCULPT_undo_push_end(); + SCULPT_undo_push_end(ob); } void ED_sculpt_undosys_type(UndoType *ut) @@ -1705,7 +1884,7 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); geometry_unode->geometry_clear_pbvh = false; - SCULPT_undo_push_end(); + SCULPT_undo_push_end(object); } /** \} */ diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index 610b0f384a2..1af0080baef 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -9,6 +9,8 @@ #include <stddef.h> +#include "BKE_attribute.h" + #ifdef __cplusplus extern "C" { #endif @@ -82,11 +84,12 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, const struct MVert *mvert, const float (*vert_normals)[3], const float *vmask, - const struct MLoopCol *vcol, + const void *vcol_data, + int vcol_type, + AttributeDomain vcol_domain, const int *sculpt_face_sets, int face_sets_color_seed, int face_sets_color_default, - const struct MPropCol *vtcol, int update_flags); /** diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index b90dd9c2a8a..110fdfbd2d9 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -25,7 +25,9 @@ #include "DNA_userdef_types.h" #include "BKE_DerivedMesh.h" +#include "BKE_attribute.h" #include "BKE_ccg.h" +#include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -196,18 +198,25 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, const MVert *mvert, const float (*vert_normals)[3], const float *vmask, - const MLoopCol *vcol, + const void *vcol_data, + int vcol_type, + AttributeDomain vcol_domain, const int *sculpt_face_sets, - const int face_sets_color_seed, - const int face_sets_color_default, - const MPropCol *vtcol, - const int update_flags) + int face_sets_color_seed, + int face_sets_color_default, + int update_flags) { + const MPropCol *vtcol = vcol_type == CD_PROP_COLOR ? vcol_data : NULL; + const MLoopCol *vcol = vcol_type == CD_MLOOPCOL ? vcol_data : NULL; + const float(*f3col)[3] = vcol_type == CD_PROP_FLOAT3 ? vcol_data : NULL; + + const bool color_loops = vcol_domain == ATTR_DOMAIN_CORNER; + const bool show_vcol = (vtcol || vcol || f3col) && + (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; + const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 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 && U.experimental.use_sculpt_vertex_colors)) && - (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; bool empty_mask = true; bool default_face_set = true; @@ -290,16 +299,40 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, /* Vertex Colors. */ if (show_vcol) { ushort scol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; - if (vtcol && U.experimental.use_sculpt_vertex_colors) { - 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]); + if (vtcol) { + if (color_loops) { + scol[0] = unit_float_to_ushort_clamp(vtcol[lt->tri[j]].color[0]); + scol[1] = unit_float_to_ushort_clamp(vtcol[lt->tri[j]].color[1]); + scol[2] = unit_float_to_ushort_clamp(vtcol[lt->tri[j]].color[2]); + scol[3] = unit_float_to_ushort_clamp(vtcol[lt->tri[j]].color[3]); + } + else { + 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 { + else if (f3col) { + if (color_loops) { + scol[0] = unit_float_to_ushort_clamp(f3col[lt->tri[j]][0]); + scol[1] = unit_float_to_ushort_clamp(f3col[lt->tri[j]][1]); + scol[2] = unit_float_to_ushort_clamp(f3col[lt->tri[j]][2]); + scol[3] = USHRT_MAX; + } + else { + scol[0] = unit_float_to_ushort_clamp(f3col[vtri[j]][0]); + scol[1] = unit_float_to_ushort_clamp(f3col[vtri[j]][1]); + scol[2] = unit_float_to_ushort_clamp(f3col[vtri[j]][2]); + scol[3] = USHRT_MAX; + } + memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); + } + else if (vcol) { const uint loop_index = lt->tri[j]; - const MLoopCol *mcol = &vcol[loop_index]; + const MLoopCol *mcol = vcol + (color_loops ? loop_index : vtri[j]); + 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]); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index a96f157981f..e462308d3ee 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -635,6 +635,7 @@ static const char *attr_prefix_get(CustomDataType type) case CD_TANGENT: return "t"; case CD_MCOL: + case CD_MLOOPCOL: return "c"; case CD_PROP_COLOR: return "c"; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 05d300dcb63..858de733558 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -227,6 +227,9 @@ typedef enum CustomDataType { CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL | \ CD_MASK_PROP_INT8) +/* All color attributes */ +#define CD_MASK_COLOR_ALL (CD_MASK_PROP_COLOR | CD_MASK_MLOOPCOL) + typedef struct CustomData_MeshMasks { uint64_t vmask; uint64_t emask; @@ -247,6 +250,8 @@ enum { CD_FLAG_EXTERNAL = (1 << 3), /* Indicates external data is read into memory */ CD_FLAG_IN_MEMORY = (1 << 4), + CD_FLAG_COLOR_ACTIVE = (1 << 5), + CD_FLAG_COLOR_RENDER = (1 << 6) }; /* Limits */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 11825de4b33..73c4eeaaab3 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2103,9 +2103,9 @@ typedef struct DataTransferModifierData { char _pad1[4]; /** DT_MULTILAYER_INDEX_MAX; See DT_FROMLAYERS_ enum in ED_object.h. */ - int layers_select_src[4]; + int layers_select_src[5]; /** DT_MULTILAYER_INDEX_MAX; See DT_TOLAYERS_ enum in ED_object.h. */ - int layers_select_dst[4]; + int layers_select_dst[5]; /** See CDT_MIX_ enum in BKE_customdata.h. */ int mix_mode; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 8a698050629..0f6c32e4ddf 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -645,7 +645,6 @@ typedef struct UserDef_Experimental { char use_new_curves_type; char use_new_point_cloud_type; char use_full_frame_compositor; - char use_sculpt_vertex_colors; char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; @@ -653,7 +652,6 @@ typedef struct UserDef_Experimental { char use_select_nearest_on_first_click; char enable_eevee_next; char use_sculpt_texture_paint; - char _pad[7]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index f86383a6328..01266a26104 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -339,6 +339,12 @@ static int rna_Attributes_layer_skip(CollectionPropertyIterator *UNUSED(iter), v return !(CD_TYPE_AS_MASK(layer->type) & CD_MASK_PROP_ALL); } +static int rna_Attributes_noncolor_layer_skip(CollectionPropertyIterator *UNUSED(iter), void *data) +{ + CustomDataLayer *layer = (CustomDataLayer *)data; + return !(CD_TYPE_AS_MASK(layer->type) & CD_MASK_COLOR_ALL) || (layer->flag & CD_FLAG_TEMPORARY); +} + /* Attributes are spread over multiple domains in separate CustomData, we use repeated * array iterators to loop over all. */ static void rna_AttributeGroup_next_domain(ID *id, @@ -377,7 +383,34 @@ void rna_AttributeGroup_iterator_next(CollectionPropertyIterator *iter) PointerRNA rna_AttributeGroup_iterator_get(CollectionPropertyIterator *iter) { - /* refine to the proper type */ + /* Refine to the proper type. */ + CustomDataLayer *layer = rna_iterator_array_get(iter); + StructRNA *type = srna_by_custom_data_layer_type(layer->type); + if (type == NULL) { + return PointerRNA_NULL; + } + return rna_pointer_inherit_refine(&iter->parent, type, layer); +} + +void rna_AttributeGroup_color_iterator_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + memset(&iter->internal.array, 0, sizeof(iter->internal.array)); + rna_AttributeGroup_next_domain(ptr->owner_id, iter, rna_Attributes_noncolor_layer_skip); +} + +void rna_AttributeGroup_color_iterator_next(CollectionPropertyIterator *iter) +{ + rna_iterator_array_next(iter); + + if (!iter->valid) { + ID *id = iter->parent.owner_id; + rna_AttributeGroup_next_domain(id, iter, rna_Attributes_noncolor_layer_skip); + } +} + +PointerRNA rna_AttributeGroup_color_iterator_get(CollectionPropertyIterator *iter) +{ + /* Refine to the proper type. */ CustomDataLayer *layer = rna_iterator_array_get(iter); StructRNA *type = srna_by_custom_data_layer_type(layer->type); if (type == NULL) { @@ -386,9 +419,16 @@ PointerRNA rna_AttributeGroup_iterator_get(CollectionPropertyIterator *iter) return rna_pointer_inherit_refine(&iter->parent, type, layer); } +int rna_AttributeGroup_color_length(PointerRNA *ptr) +{ + return BKE_id_attributes_length(ptr->owner_id, + ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER, + CD_MASK_PROP_COLOR | CD_MASK_MLOOPCOL); +} + int rna_AttributeGroup_length(PointerRNA *ptr) { - return BKE_id_attributes_length(ptr->owner_id, CD_MASK_PROP_ALL); + return BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL); } static int rna_AttributeGroup_active_index_get(PointerRNA *ptr) @@ -424,7 +464,7 @@ static void rna_AttributeGroup_active_index_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { *min = 0; - *max = BKE_id_attributes_length(ptr->owner_id, CD_MASK_PROP_ALL); + *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL); *softmin = *min; *softmax = *max; @@ -435,6 +475,98 @@ static void rna_AttributeGroup_update_active(Main *bmain, Scene *scene, PointerR rna_Attribute_update_data(bmain, scene, ptr); } +static PointerRNA rna_AttributeGroup_active_color_get(PointerRNA *ptr) +{ + ID *id = ptr->owner_id; + CustomDataLayer *layer = BKE_id_attributes_active_color_get(id); + + PointerRNA attribute_ptr; + RNA_pointer_create(id, &RNA_Attribute, layer, &attribute_ptr); + return attribute_ptr; +} + +static void rna_AttributeGroup_active_color_set(PointerRNA *ptr, + PointerRNA attribute_ptr, + ReportList *UNUSED(reports)) +{ + ID *id = ptr->owner_id; + CustomDataLayer *layer = attribute_ptr.data; + + BKE_id_attributes_active_color_set(id, layer); +} + +static int rna_AttributeGroup_active_color_index_get(PointerRNA *ptr) +{ + CustomDataLayer *layer = BKE_id_attributes_active_color_get(ptr->owner_id); + + return BKE_id_attribute_to_index( + ptr->owner_id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); +} + +static void rna_AttributeGroup_active_color_index_set(PointerRNA *ptr, int value) +{ + CustomDataLayer *layer = BKE_id_attribute_from_index( + ptr->owner_id, value, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + if (!layer) { + fprintf(stderr, "%s: error setting active color index to %d\n", __func__, value); + return; + } + + BKE_id_attributes_active_color_set(ptr->owner_id, layer); +} + +static void rna_AttributeGroup_active_color_index_range( + PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + *min = 0; + *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + *softmin = *min; + *softmax = *max; +} + +static void rna_AttributeGroup_update_active_color(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->owner_id; + + /* Cheating way for importers to avoid slow updates. */ + if (id->us > 0) { + DEG_id_tag_update(id, 0); + WM_main_add_notifier(NC_GEOM | ND_DATA, id); + } +} + +static int rna_AttributeGroup_render_color_index_get(PointerRNA *ptr) +{ + CustomDataLayer *layer = BKE_id_attributes_render_color_get(ptr->owner_id); + + return BKE_id_attribute_to_index( + ptr->owner_id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); +} + +static void rna_AttributeGroup_render_color_index_set(PointerRNA *ptr, int value) +{ + CustomDataLayer *layer = BKE_id_attribute_from_index( + ptr->owner_id, value, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + if (!layer) { + fprintf(stderr, "%s: error setting render color index to %d\n", __func__, value); + return; + } + + BKE_id_attributes_render_color_set(ptr->owner_id, layer); +} + +static void rna_AttributeGroup_render_color_index_range( + PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + *min = 0; + *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + + *softmin = *min; + *softmax = *max; +} #else static void rna_def_attribute_float(BlenderRNA *brna) @@ -826,6 +958,33 @@ static void rna_def_attribute_group(BlenderRNA *brna) "rna_AttributeGroup_active_index_set", "rna_AttributeGroup_active_index_range"); RNA_def_property_update(prop, 0, "rna_AttributeGroup_update_active"); + + prop = RNA_def_property(srna, "active_color", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Attribute"); + RNA_def_property_pointer_funcs(prop, + "rna_AttributeGroup_active_color_get", + "rna_AttributeGroup_active_color_set", + NULL, + NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK); + RNA_def_property_ui_text(prop, "Active Color", "Active color attribute"); + RNA_def_property_update(prop, 0, "rna_AttributeGroup_update_active_color"); + + prop = RNA_def_property(srna, "active_color_index", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, + "rna_AttributeGroup_active_color_index_get", + "rna_AttributeGroup_active_color_index_set", + "rna_AttributeGroup_active_color_index_range"); + RNA_def_property_update(prop, 0, "rna_AttributeGroup_update_active_color"); + + prop = RNA_def_property(srna, "render_color_index", PROP_INT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, + "rna_AttributeGroup_render_color_index_get", + "rna_AttributeGroup_render_color_index_set", + "rna_AttributeGroup_render_color_index_range"); + RNA_def_property_update(prop, 0, "rna_AttributeGroup_update_active_color"); } void rna_def_attributes_common(StructRNA *srna) @@ -846,6 +1005,20 @@ void rna_def_attributes_common(StructRNA *srna) RNA_def_property_struct_type(prop, "Attribute"); RNA_def_property_ui_text(prop, "Attributes", "Geometry attributes"); RNA_def_property_srna(prop, "AttributeGroup"); + + prop = RNA_def_property(srna, "color_attributes", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_AttributeGroup_color_iterator_begin", + "rna_AttributeGroup_color_iterator_next", + "rna_iterator_array_end", + "rna_AttributeGroup_color_iterator_get", + "rna_AttributeGroup_color_length", + NULL, + NULL, + NULL); + RNA_def_property_struct_type(prop, "Attribute"); + RNA_def_property_ui_text(prop, "Color Attributes", "Geometry color attributes"); + RNA_def_property_srna(prop, "AttributeGroup"); } void RNA_def_attribute(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 0df0be07fee..ba040f88b55 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -217,6 +217,11 @@ void rna_AttributeGroup_iterator_next(CollectionPropertyIterator *iter); PointerRNA rna_AttributeGroup_iterator_get(CollectionPropertyIterator *iter); int rna_AttributeGroup_length(PointerRNA *ptr); +void rna_AttributeGroup_color_iterator_begin(CollectionPropertyIterator *iter, PointerRNA *ptr); +void rna_AttributeGroup_color_iterator_next(CollectionPropertyIterator *iter); +PointerRNA rna_AttributeGroup_color_iterator_get(CollectionPropertyIterator *iter); +int rna_AttributeGroup_color_length(PointerRNA *ptr); + void rna_def_animdata_common(struct StructRNA *srna); bool rna_AnimaData_override_apply(struct Main *bmain, diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 4f9a10c9993..54031f5a416 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -23,6 +23,7 @@ #include "BLT_translation.h" #include "BKE_animsys.h" +#include "BKE_attribute.h" #include "BKE_curveprofile.h" #include "BKE_data_transfer.h" #include "BKE_dynamicpaint.h" @@ -1360,29 +1361,47 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_src_itemf( } } } - else if (STREQ(RNA_property_identifier(prop), "layers_vcol_select_src")) { + else if (STREQ(RNA_property_identifier(prop), "layers_vcol_select_vert_src") || + STREQ(RNA_property_identifier(prop), "layers_vcol_select_loop_src")) { Object *ob_src = dtmd->ob_source; if (ob_src) { - Mesh *me_eval; - int num_data, i; + AttributeDomain domain = STREQ(RNA_property_identifier(prop), + "layers_vcol_select_vert_src") ? + ATTR_DOMAIN_POINT : + ATTR_DOMAIN_CORNER; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.lmask |= CD_MASK_MLOOPCOL; - me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL); + CustomData *cdata; - RNA_enum_item_add_separator(&item, &totitem); + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - for (i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( - &me_eval->ldata, CD_MLOOPCOL, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); + if (domain == ATTR_DOMAIN_POINT) { + cddata_masks.vmask |= CD_MASK_COLOR_ALL; + cdata = &me_eval->vdata; + } + else { + cddata_masks.lmask |= CD_MASK_COLOR_ALL; + cdata = &me_eval->ldata; + } + + CustomDataType types[2] = {CD_PROP_COLOR, CD_MLOOPCOL}; + + int idx = 0; + for (int i = 0; i < 2; i++) { + int num_data = CustomData_number_of_layers(cdata, types[i]); + + RNA_enum_item_add_separator(&item, &totitem); + + for (int j = 0; j < num_data; j++) { + tmp_item.value = idx++; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(cdata, types[i], j); + RNA_enum_item_add(&item, &totitem, &tmp_item); + } } } } @@ -1459,26 +1478,31 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_dst_itemf( } } } - else if (STREQ(RNA_property_identifier(prop), "layers_vcol_select_dst")) { + else if (STREQ(RNA_property_identifier(prop), "layers_vcol_vert_select_dst") || + STREQ(RNA_property_identifier(prop), "layers_vcol_loop_select_dst")) { /* Only list destination layers if we have a single source! */ - if (dtmd->layers_select_src[DT_MULTILAYER_INDEX_VCOL] >= 0) { + if (dtmd->layers_select_src[DT_MULTILAYER_INDEX_VCOL_LOOP] >= 0) { Object *ob_dst = CTX_data_active_object(C); /* XXX Is this OK? */ if (ob_dst && ob_dst->data) { - Mesh *me_dst; - CustomData *ldata; - int num_data, i; + CustomDataType types[2] = {CD_PROP_COLOR, CD_MLOOPCOL}; - me_dst = ob_dst->data; - ldata = &me_dst->ldata; - num_data = CustomData_number_of_layers(ldata, CD_MLOOPCOL); + Mesh *me_dst = ob_dst->data; + CustomData *cdata = STREQ(RNA_property_identifier(prop), "layers_vcol_vert_select_dst") ? + &me_dst->vdata : + &me_dst->ldata; - RNA_enum_item_add_separator(&item, &totitem); + int idx = 0; + for (int i = 0; i < 2; i++) { + int num_data = CustomData_number_of_layers(cdata, types[i]); - for (i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(ldata, CD_MLOOPCOL, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); + RNA_enum_item_add_separator(&item, &totitem); + + for (int j = 0; j < num_data; j++) { + tmp_item.value = idx++; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(cdata, types[i], j); + RNA_enum_item_add(&item, &totitem, &tmp_item); + } } } } @@ -6330,6 +6354,11 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna) {DT_TYPE_SKIN, "SKIN", 0, "Skin Weight", "Transfer skin weights"}, # endif {DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"}, + {DT_TYPE_MPROPCOL_VERT | DT_TYPE_MLOOPCOL_VERT, + "VCOL", + 0, + "Colors", + "Transfer color attributes"}, {0, NULL, 0, NULL, NULL}, }; @@ -6344,7 +6373,11 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna) static const EnumPropertyItem DT_layer_loop_items[] = { {DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"}, - {DT_TYPE_VCOL, "VCOL", 0, "Vertex Colors", "Vertex (face corners) colors"}, + {DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, + "VCOL", + 0, + "Colors", + "Transfer color attributes"}, {DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"}, {0, NULL, 0, NULL, NULL}, }; @@ -6562,12 +6595,23 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna) # endif prop = RNA_def_enum(srna, - "layers_vcol_select_src", + "layers_vcol_vert_select_src", rna_enum_dt_layers_select_src_items, DT_LAYERS_ALL_SRC, "Source Layers Selection", "Which layers to transfer, in case of multi-layers types"); - RNA_def_property_enum_sdna(prop, NULL, "layers_select_src[DT_MULTILAYER_INDEX_VCOL]"); + RNA_def_property_enum_sdna(prop, NULL, "layers_select_src[DT_MULTILAYER_INDEX_VCOL_VERT]"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_DataTransferModifier_layers_select_src_itemf"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_enum(srna, + "layers_vcol_loop_select_src", + rna_enum_dt_layers_select_src_items, + DT_LAYERS_ALL_SRC, + "Source Layers Selection", + "Which layers to transfer, in case of multi-layers types"); + RNA_def_property_enum_sdna(prop, NULL, "layers_select_src[DT_MULTILAYER_INDEX_VCOL_LOOP]"); RNA_def_property_enum_funcs( prop, NULL, NULL, "rna_DataTransferModifier_layers_select_src_itemf"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -6608,12 +6652,23 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna) # endif prop = RNA_def_enum(srna, - "layers_vcol_select_dst", + "layers_vcol_vert_select_dst", + rna_enum_dt_layers_select_dst_items, + DT_LAYERS_NAME_DST, + "Destination Layers Matching", + "How to match source and destination layers"); + RNA_def_property_enum_sdna(prop, NULL, "layers_select_dst[DT_MULTILAYER_INDEX_VCOL_VERT]"); + RNA_def_property_enum_funcs( + prop, NULL, NULL, "rna_DataTransferModifier_layers_select_dst_itemf"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_enum(srna, + "layers_vcol_loop_select_dst", rna_enum_dt_layers_select_dst_items, DT_LAYERS_NAME_DST, "Destination Layers Matching", "How to match source and destination layers"); - RNA_def_property_enum_sdna(prop, NULL, "layers_select_dst[DT_MULTILAYER_INDEX_VCOL]"); + RNA_def_property_enum_sdna(prop, NULL, "layers_select_dst[DT_MULTILAYER_INDEX_VCOL_LOOP]"); RNA_def_property_enum_funcs( prop, NULL, NULL, "rna_DataTransferModifier_layers_select_dst_itemf"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 70b335446fc..cbac6aefc10 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -6056,7 +6056,7 @@ static void def_sh_vertex_color(StructRNA *srna) RNA_def_struct_sdna_from(srna, "NodeShaderVertexColor", "storage"); prop = RNA_def_property(srna, "layer_name", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text(prop, "Vertex Color", "Vertex Color"); + RNA_def_property_ui_text(prop, "Color Attribute", "Color Attribute"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); RNA_def_struct_sdna_from(srna, "bNode", NULL); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0960d246257..7095d8a4172 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -447,8 +447,8 @@ const EnumPropertyItem rna_enum_bake_target_items[] = { {R_BAKE_TARGET_VERTEX_COLORS, "VERTEX_COLORS", 0, - "Vertex Colors", - "Bake to active vertex color layer on meshes"}, + "Color Attributes", + "Bake to active color attribute layer on meshes"}, {0, NULL, 0, NULL, NULL}, }; @@ -3204,7 +3204,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "uvcalc_flag", UVCALC_TRANSFORM_CORRECT); RNA_def_property_ui_text(prop, "Correct Face Attributes", - "Correct data such as UV's and vertex colors when transforming"); + "Correct data such as UV's and color attributes when transforming"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "use_transform_correct_keep_connected", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 75b41e4c7b9..200e1d65caf 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -357,7 +357,7 @@ void RNA_api_scene(StructRNA *srna) RNA_def_boolean(func, "selected_only", 0, "Selected only", "Export only selected objects"); RNA_def_boolean(func, "uvs", 1, "UVs", "Export UVs"); RNA_def_boolean(func, "normals", 1, "Normals", "Export normals"); - RNA_def_boolean(func, "vcolors", 0, "Vertex colors", "Export vertex colors"); + RNA_def_boolean(func, "vcolors", 0, "Color Attributes", "Export color attributes"); RNA_def_boolean( func, "apply_subdiv", 1, "Subsurfs as meshes", "Export subdivision surfaces as meshes"); RNA_def_boolean(func, "flatten", 0, "Flatten hierarchy", "Flatten hierarchy"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 97e1f325816..1ea7b35cedb 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -78,8 +78,8 @@ static const EnumPropertyItem rna_enum_gpencil_paint_mode[] = { {GPPAINT_FLAG_USE_VERTEXCOLOR, "VERTEXCOLOR", 0, - "Vertex Color", - "Paint the material with custom vertex color"}, + "Color Attribute", + "Paint the material with a color attribute"}, {0, NULL, 0, NULL, NULL}, }; #endif diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index ff272c34c65..a74019f9569 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -424,7 +424,7 @@ static const EnumPropertyItem rna_enum_shading_color_type_items[] = { {V3D_SHADING_SINGLE_COLOR, "SINGLE", 0, "Single", "Show scene in a single color"}, {V3D_SHADING_OBJECT_COLOR, "OBJECT", 0, "Object", "Show object color"}, {V3D_SHADING_RANDOM_COLOR, "RANDOM", 0, "Random", "Show random object color"}, - {V3D_SHADING_VERTEX_COLOR, "VERTEX", 0, "Vertex", "Show active vertex color"}, + {V3D_SHADING_VERTEX_COLOR, "VERTEX", 0, "Color", "Show active color attribute"}, {V3D_SHADING_TEXTURE_COLOR, "TEXTURE", 0, "Texture", "Show texture"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index cbc13943c1f..cf622818a3d 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6399,10 +6399,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Cycles Debug", "Enable Cycles debugging options for developers"); RNA_def_property_update(prop, 0, "rna_userdef_update"); - prop = RNA_def_property(srna, "use_sculpt_vertex_colors", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_vertex_colors", 1); - RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "Use the new Vertex Painting system"); - prop = RNA_def_property(srna, "use_sculpt_tools_tilt", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_tools_tilt", 1); RNA_def_property_ui_text( diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index fa50403d91a..a3b088799cc 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -346,6 +346,22 @@ static void face_corner_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "loop_mapping", 0, IFACE_("Mapping"), ICON_NONE); } +static void vert_vcol_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiLayoutSetActive(layout, + RNA_enum_get(ptr, "data_types_verts") & + (DT_TYPE_MPROPCOL_VERT | DT_TYPE_MLOOPCOL_VERT)); + + uiItemR(layout, ptr, "layers_vcol_vert_select_src", 0, IFACE_("Layer Selection"), ICON_NONE); + uiItemR(layout, ptr, "layers_vcol_vert_select_dst", 0, IFACE_("Layer Mapping"), ICON_NONE); +} + static void face_corner_vcol_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; @@ -354,10 +370,12 @@ static void face_corner_vcol_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiLayoutSetActive(layout, RNA_enum_get(ptr, "data_types_loops") & DT_TYPE_VCOL); + uiLayoutSetActive(layout, + RNA_enum_get(ptr, "data_types_loops") & + (DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP)); - uiItemR(layout, ptr, "layers_vcol_select_src", 0, IFACE_("Layer Selection"), ICON_NONE); - uiItemR(layout, ptr, "layers_vcol_select_dst", 0, IFACE_("Layer Mapping"), ICON_NONE); + uiItemR(layout, ptr, "layers_vcol_loop_select_src", 0, IFACE_("Layer Selection"), ICON_NONE); + uiItemR(layout, ptr, "layers_vcol_loop_select_dst", 0, IFACE_("Layer Mapping"), ICON_NONE); } static void face_corner_uv_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -427,6 +445,9 @@ static void panelRegister(ARegionType *region_type) region_type, "vertex_vgroup", "Vertex Groups", NULL, vertex_vgroup_panel_draw, vertex_panel); modifier_subpanel_register( + region_type, "vert_vcol", "Colors", NULL, vert_vcol_panel_draw, vertex_panel); + + modifier_subpanel_register( region_type, "edge", "", edge_panel_draw_header, edge_panel_draw, panel_type); PanelType *face_corner_panel = modifier_subpanel_register(region_type, @@ -437,7 +458,7 @@ static void panelRegister(ARegionType *region_type) panel_type); modifier_subpanel_register(region_type, "face_corner_vcol", - "Vertex Colors", + "Colors", NULL, face_corner_vcol_panel_draw, face_corner_panel); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 0e1f181eff1..af27ed9bc06 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -109,7 +109,7 @@ DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VEC DefNode(ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" ) DefNode(ShaderNode, SH_NODE_COMBHSV, 0, "COMBHSV", CombineHSV, "Combine HSV", "" ) DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" ) -DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Vertex Color", "" ) +DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "" ) DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" ) DefNode(ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "" ) DefNode(ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc index 95e6e42cdff..22b0744333d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc @@ -22,14 +22,7 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); - if (U.experimental.use_sculpt_vertex_colors && - !RNA_collection_is_empty(&dataptr, "sculpt_vertex_colors")) { - uiItemPointerR( - layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL); - } - else { - uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL); - } + uiItemPointerR(layout, ptr, "layer_name", &dataptr, "color_attributes", "", ICON_GROUP_VCOL); } else { uiItemL(layout, TIP_("No mesh in active object"), ICON_ERROR); @@ -49,11 +42,7 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat, GPUNodeStack *out) { NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage; - if (U.experimental.use_sculpt_vertex_colors) { - GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_PROP_COLOR, vertexColor->layer_name); - return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); - } - GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name); + GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_PROP_COLOR, vertexColor->layer_name); return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); } @@ -65,7 +54,7 @@ void register_node_type_sh_vertex_color() static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VERTEX_COLOR, "Vertex Color", NODE_CLASS_INPUT); + sh_node_type_base(&ntype, SH_NODE_VERTEX_COLOR, "Color Attribute", NODE_CLASS_INPUT); ntype.declare = file_ns::node_declare; ntype.draw_buttons = file_ns::node_shader_buts_vertex_color; node_type_init(&ntype, file_ns::node_shader_init_vertex_color); diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index bc4bbc86367..a8d4ca0f02b 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -197,7 +197,12 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre } else { brush = BKE_brush_add(bmain, items[i].name, paint->runtime.ob_mode); + BKE_brush_tool_set(brush, paint, slot_index); + + if (paint_mode == PAINT_MODE_SCULPT) { + BKE_brush_sculpt_reset(brush); + } } BKE_paint_brush_set(paint, brush); } |