Refactor: Unify vertex and sculpt colors into new

color attribute system.

This commit removes sculpt colors from experimental
status and unifies it with vertex colors. It
introduces the concept of "color attributes", which
are any attributes that represents colors.  Color
attributes can be represented with byte or floating-point
numbers and can be stored in either vertices or
face corners.

Color attributes share a common namespace
(so you can no longer have a floating-point
sculpt color attribute and a byte vertex color
attribute with the same name).

Note: this commit does not include vertex paint mode,
      which is a separate patch, see:
      https://developer.blender.org/D14179

Differential Revision: https://developer.blender.org/D12587
Ref D12587
This commit is contained in:
Joseph Eagar 2022-04-05 11:42:55 -07:00
parent a3e122b9ae
commit eae36be372
Notes: blender-bot 2023-12-07 15:34:44 +01:00
Referenced by commit 93d84e87b2, Fix T103400: Transfer Mesh Data Layout broken for color attributes
Referenced by commit 177bc80748, Fix: add attribute with empty string name crash
Referenced by commit b9799dfb8a, Geometry Nodes: better support for byte color attributes
Referenced by issue #104475, Regression: Data transfer modifier no longer respects vertex group for transfers of vertex color
Referenced by issue #103400, Regression: Not possible to transfer Color Attributs Layer with "Transfer Mesh Data Layout"
Referenced by issue #99719, Vertex Color Node no longer showing in Shader Editor
Referenced by issue #98889, Regression: Sculpting on a linked copy of an objects crashes Blender
Referenced by issue #98565, Regression: Error in console for unknown/unused BRUSH_PAINT icon
Referenced by issue #97895, Regression: Geometry Nodes Color Attributes not working for Eevee on face and edge domains.
Referenced by issue #97183, Regression: Vertex paint mode (old style) work only with 1st attribute layer, no matter what selected is
Referenced by issue #97132, Sculpt mode. Color filter brush moves all geometry when there is no color attribute
Referenced by issue #97110, Regression: Using Sculpt mode Paint tool and Dyntopo crashes blender.
Referenced by issue #96895, GPU Subdivision destroys vertex color in Eevee
Referenced by issue #96209, Smear tool painting vertex colors  corrupts color info: part 2
Referenced by issue #94428, Geometry Nodes - Vertex Color Issues
Referenced by issue #104803, Regression: mesh.vertex_colors.active doesn't work since 3.2
Referenced by issue #107194, AttributeGroup API: "active" is meaningless (always refers to all attributes, not the specific AttributeGroup) -- e.g. mesh.color_attributes.active
Referenced by pull request #114797, Fix #114760: new attribute does not get active for some domains
Referenced by commit 481094ff5c, Fix #114760: new attribute does not get active for some domains
83 changed files with 2725 additions and 574 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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)
]},
)

View File

@ -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.

View File

@ -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()

View File

@ -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,

View File

@ -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")),

View File

@ -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

View File

@ -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")

View File

@ -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
}

View File

@ -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_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_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). */

View File

@ -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. */

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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];

View File

@ -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:

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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]);
}
});
}
}
}

View File

@ -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 */

View File

@ -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.
*

View File

@ -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 */

View File

@ -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;
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;
}
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;
}
*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 */
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++;
else {
memcpy(t_col, lcol, size);
memcpy(lcol, p_col, size);
memcpy(p_col, t_col, size);
}
*lcol = t_col;
n++;
}
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);
}
}

View File

@ -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;
}
}

View File

@ -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 \

View File

@ -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);
@ -546,9 +593,36 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name);
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);
BLI_assert(attrs_needed.num_requests != 0 &&
"No MPropCol layer available in Sculpt, but batches requested anyway!");
Mesh me_query = {0};
BKE_id_attribute_copy_domains_temp(ID_ME, cd_vdata, NULL, cd_ldata, NULL, NULL, &me_query.id);
drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex);
CustomDataLayer *active = BKE_id_attributes_active_color_get(&me_query.id);
CustomDataLayer *render = BKE_id_attributes_render_color_get(&me_query.id);
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;
}

View File

@ -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);
}

View File

@ -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);
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++;
gpuMeshVcol *vcol = mesh_vcol;
const CustomData *cdata = ref.domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata;
const MLoop *ml = coarse_mesh->mloop;
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));
}
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);

View File

@ -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

View File

@ -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},
};

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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",

View File

@ -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",

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
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;
}
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;
}
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);
RNA_enum_item_add_separator(&item, &totitem);
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);
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);
}
}

View File

@ -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). */

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
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);
float accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float totw = 0.0f;
blend_color_interpolate_float(vd.col, ss->cache->prev_colors[vd.index], interp_color, fade);
/*
* 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;
}
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);
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]);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
/** \} */

View File

@ -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);
/**

View File

@ -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]);

View File

@ -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";

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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,7 @@ 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) {
@ -386,9 +392,43 @@ PointerRNA rna_AttributeGroup_iterator_get(CollectionPropertyIterator *iter)
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) {
return PointerRNA_NULL;
}
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)

View File

@ -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,

View File

@ -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]");
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_LOOP]");
RNA_def_property_enum_funcs(
prop, NULL, NULL, "rna_DataTransferModifier_layers_select_dst_itemf");
RNA_def_property_update(prop, 0, "rna_Modifier_update");

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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

View File

@ -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},
};

View File

@ -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(

View File

@ -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)
@ -426,6 +444,9 @@ static void panelRegister(ARegionType *region_type)
modifier_subpanel_register(
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);
@ -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);

View File

@ -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", "" )

View File

@ -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);

View File

@ -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);
}