diff options
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; |