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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py11
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py2
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py144
-rw-r--r--source/blender/blenkernel/BKE_attribute.h1
-rw-r--r--source/blender/blenkernel/BKE_material.h4
-rw-r--r--source/blender/blenkernel/BKE_node.h8
-rw-r--r--source/blender/blenkernel/BKE_paint.h8
-rw-r--r--source/blender/blenkernel/intern/attribute.c15
-rw-r--r--source/blender/blenkernel/intern/material.c135
-rw-r--r--source/blender/blenkernel/intern/node.cc20
-rw-r--r--source/blender/blenkernel/intern/paint.c7
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c14
-rw-r--r--source/blender/editors/include/ED_paint.h25
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/paint_canvas.cc203
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c32
-rw-r--r--source/blender/makesdna/DNA_material_types.h9
-rw-r--r--source/blender/makesdna/DNA_node_types.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h34
-rw-r--r--source/blender/makesrna/intern/rna_material.c108
-rw-r--r--source/blender/makesrna/intern/rna_scene.c4
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c60
-rw-r--r--source/blender/makesrna/intern/rna_workspace.c13
-rw-r--r--source/blender/nodes/shader/node_shader_util.cc35
28 files changed, 785 insertions, 127 deletions
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 97519e55b48..1bfd82cc790 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -565,7 +565,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel):
layout.label(text="Name collisions: " + ", ".join(set(colliding_names)), icon='ERROR')
-class MESH_UL_color_attributes(UIList):
+class ColorAttributesListBase():
display_domain_names = {
'POINT': "Vertex",
'EDGE': "Edge",
@@ -588,6 +588,8 @@ class MESH_UL_color_attributes(UIList):
return ret, idxs
+
+class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
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]
@@ -613,6 +615,12 @@ class MESH_UL_color_attributes(UIList):
sub.label(text="%s â–¶ %s" % (domain_name, data_type.name))
+class MESH_UL_color_attributes_selector(UIList, ColorAttributesListBase):
+ def draw_item(self, _context, layout, data, attribute, _icon, _active_data, _active_propname, _index):
+ layout.emboss = 'NONE'
+ layout.prop(attribute, "name", text="", icon='COLOR')
+
+
class DATA_PT_vertex_colors(DATA_PT_mesh_attributes, Panel):
bl_label = "Color Attributes"
bl_options = {'DEFAULT_CLOSED'}
@@ -663,6 +671,7 @@ classes = (
DATA_PT_customdata,
DATA_PT_custom_props_mesh,
MESH_UL_color_attributes,
+ MESH_UL_color_attributes_selector,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 9e40a8d364a..064a99034d3 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -238,7 +238,7 @@ class ClonePanel(BrushPanel):
col.label(text="Source Clone Slot")
col.template_list(
"TEXTURE_UL_texpaintslots", "",
- mat, "texture_paint_images",
+ mat, "texture_paint_slots",
mat, "paint_clone_slot",
rows=2,
)
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 750e9b527f0..332933be68a 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -280,7 +280,7 @@ class TEXTURE_UL_texpaintslots(UIList):
# mat = data
if self.layout_type in {'DEFAULT', 'COMPACT'}:
- layout.prop(item, "name", text="", emboss=False, icon_value=icon)
+ layout.prop(item, "name", text="", emboss=False, icon_value=item.icon_value)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.label(text="")
@@ -459,15 +459,11 @@ class VIEW3D_MT_tools_projectpaint_uvlayer(Menu):
props.value = i
-class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel):
+class SelectPaintSlotHelper:
bl_category = "Tool"
- bl_context = ".imagepaint" # dot on purpose (access from topbar)
- bl_label = "Texture Slots"
- @classmethod
- def poll(cls, context):
- brush = context.tool_settings.image_paint.brush
- return (brush is not None and context.active_object is not None)
+ canvas_source_attr_name = "canvas_source"
+ canvas_image_attr_name = "canvas_image"
def draw(self, context):
layout = self.layout
@@ -475,51 +471,66 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel):
layout.use_property_decorate = False
settings = context.tool_settings.image_paint
+ mode_settings = self.get_mode_settings(context)
ob = context.active_object
- layout.prop(settings, "mode", text="Mode")
+ layout.prop(mode_settings, self.canvas_source_attr_name, text="Mode")
layout.separator()
- if settings.mode == 'MATERIAL':
- if len(ob.material_slots) > 1:
- layout.template_list("MATERIAL_UL_matslots", "layers",
- ob, "material_slots",
- ob, "active_material_index", rows=2)
- mat = ob.active_material
- if mat and mat.texture_paint_images:
- row = layout.row()
- row.template_list("TEXTURE_UL_texpaintslots", "",
- mat, "texture_paint_images",
- mat, "paint_active_slot", rows=2)
-
- if mat.texture_paint_slots:
- slot = mat.texture_paint_slots[mat.paint_active_slot]
+ have_image = False
+
+ match getattr(mode_settings, self.canvas_source_attr_name):
+ case 'MATERIAL':
+ if len(ob.material_slots) > 1:
+ layout.template_list("MATERIAL_UL_matslots", "layers",
+ ob, "material_slots",
+ ob, "active_material_index", rows=2)
+ mat = ob.active_material
+ if mat and mat.texture_paint_images:
+ row = layout.row()
+ row.template_list("TEXTURE_UL_texpaintslots", "",
+ mat, "texture_paint_slots",
+ mat, "paint_active_slot", rows=2)
+
+ if mat.texture_paint_slots:
+ slot = mat.texture_paint_slots[mat.paint_active_slot]
+ else:
+ slot = None
+
+ have_image = slot is not None
else:
- slot = None
+ row = layout.row()
- have_image = slot is not None
- else:
- row = layout.row()
-
- box = row.box()
- box.label(text="No Textures")
- have_image = False
+ box = row.box()
+ box.label(text="No Textures")
- sub = row.column(align=True)
- sub.operator_menu_enum("paint.add_texture_paint_slot", "type", icon='ADD', text="")
+ sub = row.column(align=True)
+ sub.operator_menu_enum("paint.add_texture_paint_slot", "type", icon='ADD', text="")
- elif settings.mode == 'IMAGE':
- mesh = ob.data
- uv_text = mesh.uv_layers.active.name if mesh.uv_layers.active else ""
- layout.template_ID(settings, "canvas", new="image.new", open="image.open")
- if settings.missing_uvs:
- layout.operator("paint.add_simple_uvs", icon='ADD', text="Add UVs")
- else:
- layout.menu("VIEW3D_MT_tools_projectpaint_uvlayer", text=uv_text, translate=False)
- have_image = settings.canvas is not None
-
- layout.prop(settings, "interpolation", text="")
+ case 'IMAGE':
+ mesh = ob.data
+ uv_text = mesh.uv_layers.active.name if mesh.uv_layers.active else ""
+ layout.template_ID(mode_settings, self.canvas_image_attr_name, new="image.new", open="image.open")
+ if settings.missing_uvs:
+ layout.operator("paint.add_simple_uvs", icon='ADD', text="Add UVs")
+ else:
+ layout.menu("VIEW3D_MT_tools_projectpaint_uvlayer", text=uv_text, translate=False)
+ have_image = getattr(settings, self.canvas_image_attr_name) is not None
+
+ self.draw_image_interpolation(layout=layout, mode_settings=mode_settings)
+
+ case 'COLOR_ATTRIBUTE':
+ mesh = ob.data
+ layout.template_list(
+ "MESH_UL_color_attributes_selector",
+ "color_attributes",
+ mesh,
+ "color_attributes",
+ mesh.color_attributes,
+ "active_color_index",
+ rows=3,
+ )
if settings.missing_uvs:
layout.separator()
@@ -531,6 +542,50 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel):
layout.operator("image.save_all_modified", text="Save All Images", icon='FILE_TICK')
+class VIEW3D_PT_slots_projectpaint(SelectPaintSlotHelper, View3DPanel, Panel):
+ bl_category = "Tool"
+ bl_context = ".imagepaint" # dot on purpose (access from topbar)
+ bl_label = "Texture Slots"
+
+ canvas_source_attr_name = "mode"
+ canvas_image_attr_name = "canvas"
+
+ @classmethod
+ def poll(cls, context):
+ brush = context.tool_settings.image_paint.brush
+ return (brush is not None and context.active_object is not None)
+
+ def get_mode_settings(self, context):
+ return context.tool_settings.image_paint
+
+ def draw_image_interpolation(self, layout, mode_settings):
+ layout.prop(mode_settings, "interpolation", text="")
+
+
+
+class VIEW3D_PT_slots_paint_canvas(SelectPaintSlotHelper, View3DPanel, Panel):
+ bl_category = "Tool"
+ bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
+ bl_label = "Canvas"
+
+ @classmethod
+ def poll(cls, context):
+ if not context.preferences.experimental.use_sculpt_texture_paint:
+ return False
+
+ from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
+ tool = ToolSelectPanelHelper.tool_active_from_context(context)
+ if tool is None:
+ return False
+ return tool.use_paint_canvas
+
+ def get_mode_settings(self, context):
+ return context.tool_settings.paint_mode
+
+ def draw_image_interpolation(self, **kwargs):
+ pass
+
+
class VIEW3D_PT_mask(View3DPanel, Panel):
bl_category = "Tool"
bl_context = ".imagepaint" # dot on purpose (access from topbar)
@@ -2232,6 +2287,7 @@ classes = (
VIEW3D_PT_tools_posemode_options,
VIEW3D_PT_slots_projectpaint,
+ VIEW3D_PT_slots_paint_canvas,
VIEW3D_PT_tools_brush_select,
VIEW3D_PT_tools_brush_settings,
VIEW3D_PT_tools_brush_color,
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index c5aca6a9373..62469a28eb1 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -122,6 +122,7 @@ 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);
+struct CustomDataLayer *BKE_id_attributes_color_find(const struct ID *id, const char *name);
bool BKE_id_attribute_calc_unique_name(struct ID *id, const char *name, char *outname);
diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h
index 0870a099c76..f38f6afff7e 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -104,7 +104,9 @@ bool BKE_object_material_slot_used(struct Object *object, short actcol);
struct Material *BKE_gpencil_material(struct Object *ob, short act);
struct MaterialGPencilStyle *BKE_gpencil_material_settings(struct Object *ob, short act);
-void BKE_texpaint_slot_refresh_cache(struct Scene *scene, struct Material *ma);
+void BKE_texpaint_slot_refresh_cache(struct Scene *scene,
+ struct Material *ma,
+ const struct Object *ob);
void BKE_texpaint_slots_refresh_object(struct Scene *scene, struct Object *ob);
struct bNode *BKE_texpaint_slot_material_find_node(struct Material *ma, short texpaint_slot);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index fa199300780..23f14f9be9d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -770,6 +770,14 @@ void nodeClearActive(struct bNodeTree *ntree);
* Two active flags, ID nodes have special flag for buttons display.
*/
struct bNode *nodeGetActiveTexture(struct bNodeTree *ntree);
+struct bNode *nodeGetActivePaintCanvas(struct bNodeTree *ntree);
+
+/**
+ * @brief Does the given node supports the sub active flag.
+ *
+ * @param sub_active The active flag to check. NODE_ACTIVE_TEXTURE/NODE_ACTIVE_PAINT_CANVAS
+ */
+bool nodeSupportsActiveFlag(const struct bNode *node, int sub_active);
int nodeSocketIsHidden(const struct bNodeSocket *sock);
void nodeSetSocketAvailability(struct bNodeTree *ntree,
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 5633c476dc1..1b296277b8f 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -641,6 +641,14 @@ typedef struct SculptSession {
*/
char needs_flush_to_id;
+ /**
+ * Some tools follows the shading chosen by the last used tool canvas.
+ * When not set the viewport shading color would be used.
+ *
+ * NOTE: This setting is temporarily until paint mode is added.
+ */
+ bool sticky_shading_color;
+
} SculptSession;
void BKE_sculptsession_free(struct Object *ob);
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index c3d4eb72c0d..d8c7c3c6dd7 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -617,6 +617,21 @@ void BKE_id_attributes_render_color_set(ID *id, CustomDataLayer *active_layer)
id, active_layer, CD_FLAG_COLOR_RENDER, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
}
+CustomDataLayer *BKE_id_attributes_color_find(const ID *id, const char *name)
+{
+ CustomDataLayer *layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_POINT);
+ if (layer == NULL) {
+ layer = BKE_id_attribute_find(id, name, CD_PROP_COLOR, ATTR_DOMAIN_CORNER);
+ }
+ if (layer == NULL) {
+ layer = BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_POINT);
+ }
+ if (layer == NULL) {
+ layer = BKE_id_attribute_find(id, name, CD_MLOOPCOL, ATTR_DOMAIN_CORNER);
+ }
+ return layer;
+}
+
void BKE_id_attribute_copy_domains_temp(short id_type,
const CustomData *vdata,
const CustomData *edata,
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 7d01a92e829..bc569956f66 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -43,6 +43,7 @@
#include "BLT_translation.h"
#include "BKE_anim_data.h"
+#include "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
@@ -1347,21 +1348,36 @@ static bNode *nodetree_uv_node_recursive(bNode *node)
return NULL;
}
+/** Bitwise filter for updating paint slots. */
+typedef enum ePaintSlotFilter {
+ PAINT_SLOT_IMAGE = 1 << 0,
+ PAINT_SLOT_COLOR_ATTRIBUTE = 1 << 1,
+} ePaintSlotFilter;
+
typedef bool (*ForEachTexNodeCallback)(bNode *node, void *userdata);
static bool ntree_foreach_texnode_recursive(bNodeTree *nodetree,
ForEachTexNodeCallback callback,
- void *userdata)
+ void *userdata,
+ ePaintSlotFilter slot_filter)
{
+ const bool do_image_nodes = (slot_filter & PAINT_SLOT_IMAGE) != 0;
+ const bool do_color_attributes = (slot_filter & PAINT_SLOT_COLOR_ATTRIBUTE) != 0;
LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) {
- if (node->typeinfo->nclass == NODE_CLASS_TEXTURE &&
+ if (do_image_nodes && node->typeinfo->nclass == NODE_CLASS_TEXTURE &&
node->typeinfo->type == SH_NODE_TEX_IMAGE && node->id) {
if (!callback(node, userdata)) {
return false;
}
}
+ if (do_color_attributes && node->typeinfo->type == SH_NODE_ATTRIBUTE) {
+ if (!callback(node, userdata)) {
+ return false;
+ }
+ }
else if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) {
/* recurse into the node group and see if it contains any textures */
- if (!ntree_foreach_texnode_recursive((bNodeTree *)node->id, callback, userdata)) {
+ if (!ntree_foreach_texnode_recursive(
+ (bNodeTree *)node->id, callback, userdata, slot_filter)) {
return false;
}
}
@@ -1375,16 +1391,17 @@ static bool count_texture_nodes_cb(bNode *UNUSED(node), void *userdata)
return true;
}
-static int count_texture_nodes_recursive(bNodeTree *nodetree)
+static int count_texture_nodes_recursive(bNodeTree *nodetree, ePaintSlotFilter slot_filter)
{
int tex_nodes = 0;
- ntree_foreach_texnode_recursive(nodetree, count_texture_nodes_cb, &tex_nodes);
+ ntree_foreach_texnode_recursive(nodetree, count_texture_nodes_cb, &tex_nodes, slot_filter);
return tex_nodes;
}
struct FillTexPaintSlotsData {
bNode *active_node;
+ const Object *ob;
Material *ma;
int index;
int slot_len;
@@ -1402,21 +1419,45 @@ static bool fill_texpaint_slots_cb(bNode *node, void *userdata)
ma->paint_active_slot = index;
}
- ma->texpaintslot[index].ima = (Image *)node->id;
- ma->texpaintslot[index].interp = ((NodeTexImage *)node->storage)->interpolation;
+ switch (node->type) {
+ case SH_NODE_TEX_IMAGE: {
+ TexPaintSlot *slot = &ma->texpaintslot[index];
+ slot->ima = (Image *)node->id;
+ slot->interp = ((NodeTexImage *)node->storage)->interpolation;
+ /* for new renderer, we need to traverse the treeback in search of a UV node */
+ bNode *uvnode = nodetree_uv_node_recursive(node);
- /* for new renderer, we need to traverse the treeback in search of a UV node */
- bNode *uvnode = nodetree_uv_node_recursive(node);
+ if (uvnode) {
+ NodeShaderUVMap *storage = (NodeShaderUVMap *)uvnode->storage;
+ slot->uvname = storage->uv_map;
+ /* set a value to index so UI knows that we have a valid pointer for the mesh */
+ slot->valid = true;
+ }
+ else {
+ /* just invalidate the index here so UV map does not get displayed on the UI */
+ slot->valid = false;
+ }
+ break;
+ }
- if (uvnode) {
- NodeShaderUVMap *storage = (NodeShaderUVMap *)uvnode->storage;
- ma->texpaintslot[index].uvname = storage->uv_map;
- /* set a value to index so UI knows that we have a valid pointer for the mesh */
- ma->texpaintslot[index].valid = true;
- }
- else {
- /* just invalidate the index here so UV map does not get displayed on the UI */
- ma->texpaintslot[index].valid = false;
+ case SH_NODE_ATTRIBUTE: {
+ TexPaintSlot *slot = &ma->texpaintslot[index];
+ NodeShaderAttribute *storage = node->storage;
+ slot->attribute_name = storage->name;
+ if (storage->type == SHD_ATTRIBUTE_GEOMETRY) {
+ const Mesh *mesh = (const Mesh *)fill_data->ob->data;
+ CustomDataLayer *layer = BKE_id_attributes_color_find(&mesh->id, storage->name);
+ slot->valid = layer != NULL;
+ }
+
+ /* Do not show unsupported attributes. */
+ if (!slot->valid) {
+ slot->attribute_name = NULL;
+ fill_data->index--;
+ }
+
+ break;
+ }
}
return fill_data->index != fill_data->slot_len;
@@ -1424,14 +1465,26 @@ static bool fill_texpaint_slots_cb(bNode *node, void *userdata)
static void fill_texpaint_slots_recursive(bNodeTree *nodetree,
bNode *active_node,
+ const Object *ob,
Material *ma,
- int slot_len)
+ int slot_len,
+ ePaintSlotFilter slot_filter)
+{
+ struct FillTexPaintSlotsData fill_data = {active_node, ob, ma, 0, slot_len};
+ ntree_foreach_texnode_recursive(nodetree, fill_texpaint_slots_cb, &fill_data, slot_filter);
+}
+
+/** Check which type of paint slots should be filled for the given object. */
+static ePaintSlotFilter material_paint_slot_filter(const struct Object *ob)
{
- struct FillTexPaintSlotsData fill_data = {active_node, ma, 0, slot_len};
- ntree_foreach_texnode_recursive(nodetree, fill_texpaint_slots_cb, &fill_data);
+ ePaintSlotFilter slot_filter = PAINT_SLOT_IMAGE;
+ if (ob->mode == OB_MODE_SCULPT && U.experimental.use_sculpt_texture_paint) {
+ slot_filter |= PAINT_SLOT_COLOR_ATTRIBUTE;
+ }
+ return slot_filter;
}
-void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma)
+void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma, const struct Object *ob)
{
int count = 0;
@@ -1439,6 +1492,8 @@ void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma)
return;
}
+ const ePaintSlotFilter slot_filter = material_paint_slot_filter(ob);
+
/* COW needed when adding texture slot on an object with no materials. */
DEG_id_tag_update(&ma->id, ID_RECALC_SHADING | ID_RECALC_COPY_ON_WRITE);
@@ -1460,7 +1515,7 @@ void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma)
return;
}
- count = count_texture_nodes_recursive(ma->nodetree);
+ count = count_texture_nodes_recursive(ma->nodetree, slot_filter);
if (count == 0) {
ma->paint_active_slot = 0;
@@ -1470,9 +1525,9 @@ void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma)
ma->texpaintslot = MEM_callocN(sizeof(*ma->texpaintslot) * count, "texpaint_slots");
- bNode *active_node = nodeGetActiveTexture(ma->nodetree);
+ bNode *active_node = nodeGetActivePaintCanvas(ma->nodetree);
- fill_texpaint_slots_recursive(ma->nodetree, active_node, ma, count);
+ fill_texpaint_slots_recursive(ma->nodetree, active_node, ob, ma, count, slot_filter);
ma->tot_slots = count;
@@ -1489,22 +1544,32 @@ void BKE_texpaint_slots_refresh_object(Scene *scene, struct Object *ob)
{
for (int i = 1; i < ob->totcol + 1; i++) {
Material *ma = BKE_object_material_get(ob, i);
- BKE_texpaint_slot_refresh_cache(scene, ma);
+ BKE_texpaint_slot_refresh_cache(scene, ma, ob);
}
}
struct FindTexPaintNodeData {
- Image *ima;
+ TexPaintSlot *slot;
bNode *r_node;
};
static bool texpaint_slot_node_find_cb(bNode *node, void *userdata)
{
struct FindTexPaintNodeData *find_data = userdata;
- Image *ima = (Image *)node->id;
- if (find_data->ima == ima) {
- find_data->r_node = node;
- return false;
+ if (find_data->slot->ima && node->type == SH_NODE_TEX_IMAGE) {
+ Image *node_ima = (Image *)node->id;
+ if (find_data->slot->ima == node_ima) {
+ find_data->r_node = node;
+ return false;
+ }
+ }
+
+ if (find_data->slot->attribute_name && node->type == SH_NODE_ATTRIBUTE) {
+ NodeShaderAttribute *storage = node->storage;
+ if (STREQLEN(find_data->slot->attribute_name, storage->name, sizeof(storage->name))) {
+ find_data->r_node = node;
+ return false;
+ }
}
return true;
@@ -1512,8 +1577,12 @@ static bool texpaint_slot_node_find_cb(bNode *node, void *userdata)
bNode *BKE_texpaint_slot_material_find_node(Material *ma, short texpaint_slot)
{
- struct FindTexPaintNodeData find_data = {ma->texpaintslot[texpaint_slot].ima, NULL};
- ntree_foreach_texnode_recursive(ma->nodetree, texpaint_slot_node_find_cb, &find_data);
+ TexPaintSlot *slot = &ma->texpaintslot[texpaint_slot];
+ struct FindTexPaintNodeData find_data = {slot, NULL};
+ ntree_foreach_texnode_recursive(ma->nodetree,
+ texpaint_slot_node_find_cb,
+ &find_data,
+ PAINT_SLOT_IMAGE | PAINT_SLOT_COLOR_ATTRIBUTE);
return find_data.r_node;
}
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 76b66beaf0d..7efdd855a04 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -3610,19 +3610,17 @@ void nodeClearActive(bNodeTree *ntree)
void nodeSetActive(bNodeTree *ntree, bNode *node)
{
- /* make sure only one node is active, and only one per ID type */
- LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) {
- tnode->flag &= ~NODE_ACTIVE;
+ const bool is_paint_canvas = nodeSupportsActiveFlag(node, NODE_ACTIVE_PAINT_CANVAS);
+ const bool is_texture_class = nodeSupportsActiveFlag(node, NODE_ACTIVE_TEXTURE);
+ int flags_to_set = NODE_ACTIVE;
+ SET_FLAG_FROM_TEST(flags_to_set, is_paint_canvas, NODE_ACTIVE_PAINT_CANVAS);
+ SET_FLAG_FROM_TEST(flags_to_set, is_texture_class, NODE_ACTIVE_TEXTURE);
- if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
- tnode->flag &= ~NODE_ACTIVE_TEXTURE;
- }
- }
-
- node->flag |= NODE_ACTIVE;
- if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
- node->flag |= NODE_ACTIVE_TEXTURE;
+ /* Make sure only one node is active per node tree. */
+ LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) {
+ tnode->flag &= ~flags_to_set;
}
+ node->flag |= flags_to_set;
}
int nodeSocketIsHidden(const bNodeSocket *sock)
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 8e87f6ea243..eb3f47760fc 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -42,6 +42,7 @@
#include "BKE_key.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
@@ -1769,6 +1770,12 @@ static void sculpt_update_object(Depsgraph *depsgraph,
}
}
}
+
+ /* We could be more precise when we have access to the active tool. */
+ const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0;
+ if (use_paint_slots) {
+ BKE_texpaint_slots_refresh_object(scene, ob);
+ }
}
void BKE_sculpt_update_object_before_eval(Object *ob)
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 8da95c8387b..85c8f9c420a 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -27,6 +27,8 @@
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
+#include "ED_paint.h"
+
#include "workbench_engine.h"
#include "workbench_private.h"
@@ -94,8 +96,6 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd,
eV3DShadingColorType color_type)
{
const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR);
- BLI_assert(color_type != V3D_SHADING_TEXTURE_COLOR);
-
if (use_single_drawcall) {
DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL);
DRW_shgroup_call_sculpt(grp, ob, false, false);
@@ -324,6 +324,16 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd,
color_type = V3D_SHADING_MATERIAL_COLOR;
}
+ if (is_sculpt_pbvh) {
+ /* Bad call C is required to access the tool system that is context aware. Cast to non-const
+ * due to current API. */
+ bContext *C = (bContext *)DRW_context_state_get()->evil_C;
+ if (C != NULL) {
+ color_type = ED_paint_shading_color_override(
+ C, &wpd->scene->toolsettings->paint_mode, ob, color_type);
+ }
+ }
+
if (r_draw_shadow) {
*r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd);
/* Currently unsupported in sculpt mode. We could revert to the slow
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
index 2df0b5d2c01..6b015895b60 100644
--- a/source/blender/editors/include/ED_paint.h
+++ b/source/blender/editors/include/ED_paint.h
@@ -6,10 +6,13 @@
#pragma once
+#include "DNA_view3d_enums.h"
+
#ifdef __cplusplus
extern "C" {
#endif
+struct PaintModeSettings;
struct ImBuf;
struct Image;
struct ImageUser;
@@ -109,6 +112,28 @@ void ED_paintcurve_undo_push_end(struct bContext *C);
/** Export for ED_undo_sys. */
void ED_paintcurve_undosys_type(struct UndoType *ut);
+/* paint_canvas.cc */
+struct Image *ED_paint_canvas_image_get(const struct PaintModeSettings *settings,
+ struct Object *ob);
+int ED_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings,
+ struct Object *ob);
+
+/** Color type of an object can be overridden in sculpt/paint mode. */
+eV3DShadingColorType ED_paint_shading_color_override(struct bContext *C,
+ const struct PaintModeSettings *settings,
+ struct Object *ob,
+ eV3DShadingColorType orig_color_type);
+
+/**
+ * Does the given tool use a paint canvas.
+ *
+ * When #tref isn't given the active tool from the context is used.
+ */
+bool ED_paint_tool_use_canvas(struct bContext *C, struct bToolRef *tref);
+
+/* Store the last used tool in the sculpt session. */
+void ED_paint_tool_update_sticky_shading_color(struct bContext *C, struct Object *ob);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index fe7683d12f5..b89cbcf87fa 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -34,6 +34,7 @@ set(SRC
curves_sculpt_grow_shrink.cc
curves_sculpt_ops.cc
curves_sculpt_snake_hook.cc
+ paint_canvas.cc
paint_cursor.c
paint_curve.c
paint_curve_undo.c
diff --git a/source/blender/editors/sculpt_paint/paint_canvas.cc b/source/blender/editors/sculpt_paint/paint_canvas.cc
new file mode 100644
index 00000000000..9a1a61cf3ab
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_canvas.cc
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_compiler_compat.h"
+
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_node_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_workspace_types.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_material.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+
+#include "DEG_depsgraph.h"
+
+#include "NOD_shader.h"
+
+#include "WM_toolsystem.h"
+
+namespace blender::ed::sculpt_paint::canvas {
+static TexPaintSlot *get_active_slot(Object *ob)
+{
+ Material *mat = BKE_object_material_get(ob, ob->actcol);
+ if (mat == nullptr) {
+ return nullptr;
+ }
+ if (mat->texpaintslot == nullptr) {
+ return nullptr;
+ }
+ if (mat->paint_active_slot >= mat->tot_slots) {
+ return nullptr;
+ }
+
+ TexPaintSlot *slot = &mat->texpaintslot[mat->paint_active_slot];
+ return slot;
+}
+
+} // namespace blender::ed::sculpt_paint::canvas
+
+extern "C" {
+
+using namespace blender;
+using namespace blender::ed::sculpt_paint::canvas;
+
+/* Does the paint tool with the given idname uses a canvas. */
+static bool paint_tool_uses_canvas(StringRef idname)
+{
+ return ELEM(idname, "builtin_brush.Paint", "builtin_brush.Smear", "builtin.color_filter");
+}
+
+static bool paint_tool_shading_color_follows_last_used(StringRef idname)
+{
+ /* TODO(jbakker): complete this list. */
+ return ELEM(idname, "builtin_brush.Mask");
+}
+
+void ED_paint_tool_update_sticky_shading_color(struct bContext *C, struct Object *ob)
+{
+ if (ob == nullptr || ob->sculpt == nullptr) {
+ return;
+ }
+
+ bToolRef *tref = WM_toolsystem_ref_from_context(C);
+ if (tref == nullptr) {
+ return;
+ }
+ /* Do not modify when tool follows lat used tool. */
+ if (paint_tool_shading_color_follows_last_used(tref->idname)) {
+ return;
+ }
+
+ ob->sculpt->sticky_shading_color = paint_tool_uses_canvas(tref->idname);
+}
+
+static bool paint_tool_shading_color_follows_last_used_tool(struct bContext *C, struct Object *ob)
+{
+ if (ob == nullptr || ob->sculpt == nullptr) {
+ return false;
+ }
+
+ bToolRef *tref = WM_toolsystem_ref_from_context(C);
+ if (tref == nullptr) {
+ return false;
+ }
+
+ return paint_tool_shading_color_follows_last_used(tref->idname);
+}
+
+bool ED_paint_tool_use_canvas(struct bContext *C, bToolRef *tref)
+{
+ if (tref == nullptr) {
+ tref = WM_toolsystem_ref_from_context(C);
+ }
+ if (tref == nullptr) {
+ return false;
+ }
+
+ return paint_tool_uses_canvas(tref->idname);
+}
+
+eV3DShadingColorType ED_paint_shading_color_override(bContext *C,
+ const PaintModeSettings *settings,
+ Object *ob,
+ eV3DShadingColorType orig_color_type)
+{
+ if (!U.experimental.use_sculpt_texture_paint) {
+ return orig_color_type;
+ }
+ /* NOTE: This early exit is temporarily, until a paint mode has been added.
+ * For better integration with the vertex paint in sculpt mode we sticky
+ * with the last stoke when using tools like masking.
+ */
+ if (!ED_paint_tool_use_canvas(C, nullptr) &&
+ !(paint_tool_shading_color_follows_last_used_tool(C, ob) &&
+ ob->sculpt->sticky_shading_color)) {
+ return orig_color_type;
+ }
+
+ eV3DShadingColorType color_type = orig_color_type;
+ switch (settings->canvas_source) {
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
+ color_type = V3D_SHADING_VERTEX_COLOR;
+ break;
+ case PAINT_CANVAS_SOURCE_IMAGE:
+ color_type = V3D_SHADING_TEXTURE_COLOR;
+ break;
+ case PAINT_CANVAS_SOURCE_MATERIAL: {
+ TexPaintSlot *slot = get_active_slot(ob);
+ if (slot == nullptr) {
+ break;
+ }
+
+ if (slot->ima) {
+ color_type = V3D_SHADING_TEXTURE_COLOR;
+ }
+ if (slot->attribute_name) {
+ color_type = V3D_SHADING_VERTEX_COLOR;
+ }
+
+ break;
+ }
+ }
+
+ return color_type;
+}
+
+Image *ED_paint_canvas_image_get(const struct PaintModeSettings *settings, struct Object *ob)
+{
+ switch (settings->canvas_source) {
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
+ return nullptr;
+ case PAINT_CANVAS_SOURCE_IMAGE:
+ return settings->canvas_image;
+ case PAINT_CANVAS_SOURCE_MATERIAL: {
+ TexPaintSlot *slot = get_active_slot(ob);
+ if (slot == nullptr) {
+ break;
+ }
+ return slot->ima;
+ }
+ }
+ return nullptr;
+}
+
+int ED_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings,
+ struct Object *ob)
+{
+ switch (settings->canvas_source) {
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
+ return -1;
+ case PAINT_CANVAS_SOURCE_IMAGE: {
+ /* Use active uv map of the object. */
+ if (ob->type != OB_MESH) {
+ return -1;
+ }
+
+ const Mesh *mesh = static_cast<Mesh *>(ob->data);
+ return CustomData_get_active_layer_index(&mesh->ldata, CD_MLOOPUV);
+ }
+ case PAINT_CANVAS_SOURCE_MATERIAL: {
+ /* Use uv map of the canvas. */
+ TexPaintSlot *slot = get_active_slot(ob);
+ if (slot == nullptr) {
+ break;
+ }
+
+ if (ob->type != OB_MESH) {
+ return -1;
+ }
+
+ if (slot->uvname == nullptr) {
+ return -1;
+ }
+
+ const Mesh *mesh = static_cast<Mesh *>(ob->data);
+ return CustomData_get_named_layer_index(&mesh->ldata, CD_MLOOPUV, slot->uvname);
+ }
+ }
+ return -1;
+}
+}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 233cfc3b33d..1303da71435 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -6336,7 +6336,7 @@ bool ED_paint_proj_mesh_data_check(
hasmat = true;
if (ma->texpaintslot == NULL) {
/* refresh here just in case */
- BKE_texpaint_slot_refresh_cache(scene, ma);
+ BKE_texpaint_slot_refresh_cache(scene, ma, ob);
}
if (ma->texpaintslot != NULL &&
(ma->texpaintslot[ma->paint_active_slot].ima == NULL ||
@@ -6607,7 +6607,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
nodePositionPropagate(out_node);
if (ima) {
- BKE_texpaint_slot_refresh_cache(scene, ma);
+ BKE_texpaint_slot_refresh_cache(scene, ma, ob);
BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE);
WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima);
}
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 4b5ac51a525..3f26f590b70 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -281,7 +281,7 @@ static void imapaint_pick_uv(
float p[2], w[3], absw, minabsw;
float matrix[4][4], proj[4][4];
int view[4];
- const eImagePaintMode mode = scene->toolsettings->imapaint.mode;
+ const ePaintCanvasSource mode = scene->toolsettings->imapaint.mode;
const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval);
const int tottri = me_eval->runtime.looptris.len;
@@ -317,7 +317,7 @@ static void imapaint_pick_uv(
copy_v3_v3(tri_co[j], mvert[mloop[lt->tri[j]].v].co);
}
- if (mode == IMAGEPAINT_MODE_MATERIAL) {
+ if (mode == PAINT_CANVAS_SOURCE_MATERIAL) {
const Material *ma;
const TexPaintSlot *slot;
@@ -431,7 +431,7 @@ void paint_sample_color(
Material *ma = BKE_object_material_get(ob_eval, mp->mat_nr + 1);
/* Force refresh since paint slots are not updated when changing interpolation. */
- BKE_texpaint_slot_refresh_cache(scene, ma);
+ BKE_texpaint_slot_refresh_cache(scene, ma, ob);
if (ma && ma->texpaintslot) {
image = ma->texpaintslot[ma->paint_active_slot].ima;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index e03f12025c8..24c5a9fce52 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -70,6 +70,7 @@
#include "WM_types.h"
#include "ED_object.h"
+#include "ED_paint.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "ED_view3d.h"
@@ -4991,6 +4992,8 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op)
* earlier steps modifying the data. */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
BKE_sculpt_update_object_for_edit(depsgraph, ob, need_pmap, need_mask, needs_colors);
+
+ ED_paint_tool_update_sticky_shading_color(C, ob);
}
static void sculpt_restore_mesh(Sculpt *sd, Object *ob)
@@ -5191,6 +5194,8 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ /* NOTE: This should be removed when paint mode is available. Paint mode can force based on the
+ * canvas it is painting on. (ref. use_sculpt_texture_paint). */
if (brush && SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) {
View3D *v3d = CTX_wm_view3d(C);
if (v3d) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index cbb9180a209..e4180a36a98 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -36,6 +36,7 @@
#include "WM_types.h"
#include "ED_object.h"
+#include "ED_paint.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "paint_intern.h"
@@ -293,6 +294,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
FilterCache *filter_cache = ss->filter_cache;
filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
+ ED_paint_tool_update_sticky_shading_color(C, ob);
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 3f2fbb97de1..6cb3d629e55 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1300,6 +1300,27 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params)
}
}
+static void view3d_do_msg_notify_workbench_view_update(struct bContext *C,
+ struct wmMsgSubscribeKey *UNUSED(msg_key),
+ struct wmMsgSubscribeValue *msg_val)
+{
+ Scene *scene = CTX_data_scene(C);
+ ScrArea *area = (ScrArea *)msg_val->user_data;
+ View3D *v3d = (View3D *)area->spacedata.first;
+ if (v3d->shading.type == OB_SOLID) {
+ RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->shading.type);
+ DRWUpdateContext drw_context = {NULL};
+ drw_context.bmain = CTX_data_main(C);
+ drw_context.depsgraph = CTX_data_depsgraph_pointer(C);
+ drw_context.scene = scene;
+ drw_context.view_layer = CTX_data_view_layer(C);
+ drw_context.region = (ARegion *)(msg_val->owner);
+ drw_context.v3d = v3d;
+ drw_context.engine_type = engine_type;
+ DRW_notify_view_update(&drw_context);
+ }
+}
+
static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params)
{
struct wmMsgBus *mbus = params->message_bus;
@@ -1341,6 +1362,12 @@ static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeP
.notify = ED_region_do_msg_notify_tag_redraw,
};
+ wmMsgSubscribeValue msg_sub_value_workbench_view_update = {
+ .owner = region,
+ .user_data = area,
+ .notify = view3d_do_msg_notify_workbench_view_update,
+ };
+
for (int i = 0; i < ARRAY_SIZE(type_array); i++) {
msg_key_params.ptr.type = type_array[i];
WM_msg_subscribe_rna_params(mbus, &msg_key_params, &msg_sub_value_region_tag_redraw, __func__);
@@ -1374,6 +1401,11 @@ static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeP
case OB_MODE_PARTICLE_EDIT:
WM_msg_subscribe_rna_anon_type(mbus, ParticleEdit, &msg_sub_value_region_tag_redraw);
break;
+
+ case OB_MODE_SCULPT:
+ WM_msg_subscribe_rna_anon_prop(
+ mbus, WorkSpace, tools, &msg_sub_value_workbench_view_update);
+ break;
default:
break;
}
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index e83e68a091b..b535d3cdb8a 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -27,11 +27,16 @@ struct bNodeTree;
/* WATCH IT: change type? also make changes in ipo.h */
typedef struct TexPaintSlot {
- /** Image to be painted on. */
+ /** Image to be painted on. Mutual exclusive with attribute_name. */
struct Image *ima;
/** Custom-data index for uv layer, #MAX_NAME. */
char *uvname;
- /** Do we have a valid image and UV map. */
+ /**
+ * Color attribute name when painting using color attributes. Mutual exclusive with ima.
+ * Points to the name of a CustomDataLayer.
+ */
+ char *attribute_name;
+ /** Do we have a valid image and UV map or attribute. */
int valid;
/** Copy of node interpolation setting. */
int interp;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index ff7686d87af..5f909ea325b 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -397,6 +397,8 @@ typedef struct bNode {
#define NODE_DO_OUTPUT_RECALC (1 << 17)
/* A preview for the data in this node can be displayed in the spreadsheet editor. */
#define __NODE_ACTIVE_PREVIEW (1 << 18) /* deprecated */
+/* Active node that is used to paint on. */
+#define NODE_ACTIVE_PAINT_CANVAS (1 << 19)
/* node->update */
#define NODE_UPDATE_ID 1 /* associated id data block has changed */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index fb1ba15a099..9cc4d5ed55b 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -930,6 +930,19 @@ typedef struct ImagePaintSettings {
} ImagePaintSettings;
/* ------------------------------------------- */
+/* Paint mode settings */
+
+typedef struct PaintModeSettings {
+ /** Source to select canvas from to paint on (ePaintCanvasSource) */
+ char canvas_source;
+ char _pad[7];
+
+ /** Selected image when canvas_source=PAINT_CANVAS_SOURCE_IMAGE. */
+ Image *canvas_image;
+
+} PaintModeSettings;
+
+/* ------------------------------------------- */
/* Particle Edit */
/** Settings for a Particle Editing Brush. */
@@ -1462,6 +1475,9 @@ typedef struct ToolSettings {
/* Image Paint (8 bytes aligned please!) */
struct ImagePaintSettings imapaint;
+ /** Settings for paint mode. */
+ struct PaintModeSettings paint_mode;
+
/* Particle Editing */
struct ParticleEditSettings particle;
@@ -2278,11 +2294,21 @@ typedef enum eSculptFlags {
SCULPT_HIDE_FACE_SETS = (1 << 17),
} eSculptFlags;
+/** PaintModeSettings.mode */
+typedef enum ePaintCanvasSource {
+ /** Paint on the active node of the active material slot. */
+ PAINT_CANVAS_SOURCE_MATERIAL = 0,
+ /** Paint on a selected image. */
+ PAINT_CANVAS_SOURCE_IMAGE = 1,
+ /** Paint on the active color attribute (vertex color) layer. */
+ PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE = 2,
+} ePaintCanvasSource;
+
/** #ImagePaintSettings.mode */
-typedef enum eImagePaintMode {
- IMAGEPAINT_MODE_MATERIAL = 0, /* detect texture paint slots from the material */
- IMAGEPAINT_MODE_IMAGE = 1, /* select texture paint image directly */
-} eImagePaintMode;
+/* Defines to let old texture painting use the new enum. */
+/* TODO(jbakker): rename usages. */
+#define IMAGEPAINT_MODE_MATERIAL PAINT_CANVAS_SOURCE_MATERIAL
+#define IMAGEPAINT_MODE_IMAGE PAINT_CANVAS_SOURCE_IMAGE
/** #ImagePaintSettings.interp */
enum {
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index fc3baa65ef6..15e7e12bbf8 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -8,10 +8,13 @@
#include <stdlib.h>
#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_texture_types.h"
#include "BLI_math.h"
+#include "BKE_customdata.h"
+
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -136,10 +139,9 @@ static void rna_Material_texpaint_begin(CollectionPropertyIterator *iter, Pointe
iter, (void *)ma->texpaintslot, sizeof(TexPaintSlot), ma->tot_slots, 0, NULL);
}
-static void rna_Material_active_paint_texture_index_update(Main *bmain,
- Scene *UNUSED(scene),
- PointerRNA *ptr)
+static void rna_Material_active_paint_texture_index_update(bContext *C, PointerRNA *ptr)
{
+ Main *bmain = CTX_data_main(C);
bScreen *screen;
Material *ma = (Material *)ptr->owner_id;
@@ -152,26 +154,43 @@ static void rna_Material_active_paint_texture_index_update(Main *bmain,
}
if (ma->texpaintslot) {
- Image *image = ma->texpaintslot[ma->paint_active_slot].ima;
- for (screen = bmain->screens.first; screen; screen = screen->id.next) {
- wmWindow *win = ED_screen_window_find(screen, bmain->wm.first);
- if (win == NULL) {
- continue;
- }
+ TexPaintSlot *slot = &ma->texpaintslot[ma->paint_active_slot];
+ Image *image = slot->ima;
+ if (image) {
+ for (screen = bmain->screens.first; screen; screen = screen->id.next) {
+ wmWindow *win = ED_screen_window_find(screen, bmain->wm.first);
+ if (win == NULL) {
+ continue;
+ }
- ScrArea *area;
- for (area = screen->areabase.first; area; area = area->next) {
- SpaceLink *sl;
- for (sl = area->spacedata.first; sl; sl = sl->next) {
- if (sl->spacetype == SPACE_IMAGE) {
- SpaceImage *sima = (SpaceImage *)sl;
- if (!sima->pin) {
- ED_space_image_set(bmain, sima, image, true);
+ ScrArea *area;
+ for (area = screen->areabase.first; area; area = area->next) {
+ SpaceLink *sl;
+ for (sl = area->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = (SpaceImage *)sl;
+ if (!sima->pin) {
+ ED_space_image_set(bmain, sima, image, true);
+ }
}
}
}
}
}
+
+ /* For compatibility reasons with vertex paint we activate the color attribute. */
+ if (slot->attribute_name) {
+ Object *ob = CTX_data_active_object(C);
+ if (ob != NULL && ob->type == OB_MESH) {
+ Mesh *mesh = ob->data;
+ CustomDataLayer *layer = BKE_id_attributes_color_find(&mesh->id, slot->attribute_name);
+ if (layer != NULL) {
+ BKE_id_attributes_active_color_set(&mesh->id, layer);
+ }
+ DEG_id_tag_update(&ob->id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &ob->id);
+ }
+ }
}
DEG_id_tag_update(&ma->id, 0);
@@ -281,6 +300,49 @@ static void rna_TexPaintSlot_uv_layer_set(PointerRNA *ptr, const char *value)
}
}
+static void rna_TexPaintSlot_name_get(PointerRNA *ptr, char *value)
+{
+ TexPaintSlot *data = (TexPaintSlot *)(ptr->data);
+
+ if (data->ima != NULL) {
+ BLI_strncpy_utf8(value, data->ima->id.name + 2, MAX_NAME);
+ return;
+ }
+
+ if (data->attribute_name != NULL) {
+ BLI_strncpy_utf8(value, data->attribute_name, MAX_NAME);
+ return;
+ }
+
+ value[0] = '\0';
+}
+
+static int rna_TexPaintSlot_name_length(PointerRNA *ptr)
+{
+ TexPaintSlot *data = (TexPaintSlot *)(ptr->data);
+ if (data->ima != NULL) {
+ return strlen(data->ima->id.name) - 2;
+ }
+ if (data->attribute_name != NULL) {
+ return strlen(data->attribute_name);
+ }
+
+ return 0;
+}
+
+static int rna_TexPaintSlot_icon_get(PointerRNA *ptr)
+{
+ TexPaintSlot *data = (TexPaintSlot *)(ptr->data);
+ if (data->ima != NULL) {
+ return ICON_IMAGE;
+ }
+ if (data->attribute_name != NULL) {
+ return ICON_COLOR;
+ }
+
+ return ICON_NONE;
+}
+
static bool rna_is_grease_pencil_get(PointerRNA *ptr)
{
Material *ma = (Material *)ptr->data;
@@ -963,6 +1025,17 @@ static void rna_def_tex_slot(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "Texture Paint Slot", "Slot that contains information about texture painting");
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_TexPaintSlot_name_get", "rna_TexPaintSlot_name_length", NULL);
+ RNA_def_property_ui_text(prop, "Name", "Name of the slot");
+
+ prop = RNA_def_property(srna, "icon_value", PROP_INT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_TexPaintSlot_icon_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Icon", "Paint slot icon");
+
prop = RNA_def_property(srna, "uv_layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_maxlength(prop, 64); /* else it uses the pointer size! */
RNA_def_property_string_sdna(prop, NULL, "uvname");
@@ -1019,6 +1092,7 @@ void rna_def_texpaint_slots(BlenderRNA *brna, StructRNA *srna)
RNA_def_property_range(prop, 0, SHRT_MAX);
RNA_def_property_ui_text(
prop, "Active Paint Texture Index", "Index of active texture paint slot");
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(
prop, NC_MATERIAL | ND_SHADING_LINKS, "rna_Material_active_paint_texture_index_update");
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index fa34e7a40c0..7f0a9627a17 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -3049,6 +3049,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "imapaint");
RNA_def_property_ui_text(prop, "Image Paint", "");
+ prop = RNA_def_property(srna, "paint_mode", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "paint_mode");
+ RNA_def_property_ui_text(prop, "Paint Mode", "");
+
prop = RNA_def_property(srna, "uv_sculpt", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "uvsculpt");
RNA_def_property_ui_text(prop, "UV Sculpt", "");
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 1ea7b35cedb..37c687ddb2b 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -84,6 +84,13 @@ static const EnumPropertyItem rna_enum_gpencil_paint_mode[] = {
};
#endif
+static const EnumPropertyItem rna_enum_canvas_source_items[] = {
+ {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""},
+ {PAINT_CANVAS_SOURCE_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_symmetrize_direction_items[] = {
{BMO_SYMMETRIZE_NEGATIVE_X, "NEGATIVE_X", 0, "-X to +X", ""},
{BMO_SYMMETRIZE_POSITIVE_X, "POSITIVE_X", 0, "+X to -X", ""},
@@ -418,6 +425,11 @@ static char *rna_ImagePaintSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.image_paint");
}
+static char *rna_PaintModeSettings_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.paint_mode");
+}
+
static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("tool_settings.uv_sculpt");
@@ -537,6 +549,30 @@ static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr))
}
}
+/** \name Paint mode settings
+ * \{ */
+
+static bool rna_PaintModeSettings_canvas_image_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ Image *image = (Image *)value.owner_id;
+ return !ELEM(image->type, IMA_TYPE_COMPOSITE, IMA_TYPE_R_RESULT);
+}
+
+static void rna_PaintModeSettings_canvas_source_update(bContext *C, PointerRNA *UNUSED(ptr))
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ /* When canvas source changes the pbvh would require updates when switching between color
+ * attributes. */
+ if (ob && ob->type == OB_MESH) {
+ BKE_texpaint_slots_refresh_object(scene, ob);
+ DEG_id_tag_update(&ob->id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &ob->id);
+ }
+}
+
+/* \} */
+
static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint)
{
return imapaint->missing_data == 0;
@@ -964,6 +1000,29 @@ static void rna_def_vertex_paint(BlenderRNA *brna)
prop, "Radial Symmetry Count X Axis", "Number of times to copy strokes across the surface");
}
+static void rna_def_paint_mode(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "PaintModeSettings", NULL);
+ RNA_def_struct_sdna(srna, "PaintModeSettings");
+ RNA_def_struct_path_func(srna, "rna_PaintModeSettings_path");
+ RNA_def_struct_ui_text(srna, "Paint Mode", "Properties of paint mode");
+
+ prop = RNA_def_property(srna, "canvas_source", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_canvas_source_items);
+ RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
+ RNA_def_property_ui_text(prop, "Source", "Source to select canvas from");
+ RNA_def_property_update(prop, 0, "rna_PaintModeSettings_canvas_source_update");
+
+ prop = RNA_def_property(srna, "canvas_image", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_PaintModeSettings_canvas_image_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE);
+ RNA_def_property_ui_text(prop, "Texture", "Image used as as painting target");
+}
+
static void rna_def_image_paint(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1551,6 +1610,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
rna_def_gp_sculptpaint(brna);
rna_def_gp_weightpaint(brna);
rna_def_vertex_paint(brna);
+ rna_def_paint_mode(brna);
rna_def_image_paint(brna);
rna_def_particle_edit(brna);
rna_def_gpencil_guides(brna);
diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c
index f32a74be6e2..0b6c3934985 100644
--- a/source/blender/makesrna/intern/rna_workspace.c
+++ b/source/blender/makesrna/intern/rna_workspace.c
@@ -32,6 +32,7 @@
# include "DNA_space_types.h"
# include "ED_asset.h"
+# include "ED_paint.h"
# include "RNA_access.h"
@@ -180,6 +181,12 @@ const EnumPropertyItem *rna_WorkSpace_tools_mode_itemf(bContext *UNUSED(C),
return DummyRNA_DEFAULT_items;
}
+static bool rna_WorkSpaceTool_use_paint_canvas_get(PointerRNA *ptr)
+{
+ bToolRef *tref = ptr->data;
+ return ED_paint_tool_use_canvas(NULL, tref);
+}
+
static int rna_WorkSpaceTool_index_get(PointerRNA *ptr)
{
bToolRef *tref = ptr->data;
@@ -291,6 +298,12 @@ static void rna_def_workspace_tool(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Tool Mode", "");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ prop = RNA_def_property(srna, "use_paint_canvas", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Index", "");
+ RNA_def_property_boolean_funcs(prop, "rna_WorkSpaceTool_use_paint_canvas_get", NULL);
+ RNA_def_property_ui_text(prop, "Use Paint Canvas", "Does this tool use an painting canvas");
+
RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "has_datablock", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc
index 16a4c5bae88..728e2760f9a 100644
--- a/source/blender/nodes/shader/node_shader_util.cc
+++ b/source/blender/nodes/shader/node_shader_util.cc
@@ -162,8 +162,21 @@ static void data_from_gpu_stack_list(ListBase *sockets, bNodeStack **ns, GPUNode
}
}
-bNode *nodeGetActiveTexture(bNodeTree *ntree)
+bool nodeSupportsActiveFlag(const bNode *node, int sub_activity)
+{
+ BLI_assert(ELEM(sub_activity, NODE_ACTIVE_TEXTURE, NODE_ACTIVE_PAINT_CANVAS));
+ switch (sub_activity) {
+ case NODE_ACTIVE_TEXTURE:
+ return node->typeinfo->nclass == NODE_CLASS_TEXTURE;
+ case NODE_ACTIVE_PAINT_CANVAS:
+ return ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_ATTRIBUTE);
+ }
+ return false;
+}
+
+static bNode *node_get_active(bNodeTree *ntree, int sub_activity)
{
+ BLI_assert(ELEM(sub_activity, NODE_ACTIVE_TEXTURE, NODE_ACTIVE_PAINT_CANVAS));
/* this is the node we texture paint and draw in textured draw */
bNode *inactivenode = nullptr, *activetexnode = nullptr, *activegroup = nullptr;
bool hasgroup = false;
@@ -173,14 +186,14 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree)
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node->flag & NODE_ACTIVE_TEXTURE) {
+ if (node->flag & sub_activity) {
activetexnode = node;
/* if active we can return immediately */
if (node->flag & NODE_ACTIVE) {
return node;
}
}
- else if (!inactivenode && node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
+ else if (!inactivenode && nodeSupportsActiveFlag(node, sub_activity)) {
inactivenode = node;
}
else if (node->type == NODE_GROUP) {
@@ -195,7 +208,7 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree)
/* first, check active group for textures */
if (activegroup) {
- bNode *tnode = nodeGetActiveTexture((bNodeTree *)activegroup->id);
+ bNode *tnode = node_get_active((bNodeTree *)activegroup->id, sub_activity);
/* active node takes priority, so ignore any other possible nodes here */
if (tnode) {
return tnode;
@@ -210,8 +223,8 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree)
/* node active texture node in this tree, look inside groups */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == NODE_GROUP) {
- bNode *tnode = nodeGetActiveTexture((bNodeTree *)node->id);
- if (tnode && ((tnode->flag & NODE_ACTIVE_TEXTURE) || !inactivenode)) {
+ bNode *tnode = node_get_active((bNodeTree *)node->id, sub_activity);
+ if (tnode && ((tnode->flag & sub_activity) || !inactivenode)) {
return tnode;
}
}
@@ -221,6 +234,16 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree)
return inactivenode;
}
+bNode *nodeGetActiveTexture(bNodeTree *ntree)
+{
+ return node_get_active(ntree, NODE_ACTIVE_TEXTURE);
+}
+
+bNode *nodeGetActivePaintCanvas(bNodeTree *ntree)
+{
+ return node_get_active(ntree, NODE_ACTIVE_PAINT_CANVAS);
+}
+
void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, bNode *output_node)
{
bNodeExec *nodeexec;