Painting: Canvas switcher for painting brushes/tools.

This patch adds color attributes to TexPaintSlot. This allows an easier selection
when painting color attributes.

Previously when selecting a paint tool the user had to start a stroke, before the
UI reflected the correct TexPaintSlot. Now when switching the slot the active
tool is checked and immediate the UI is drawn correctly.

In the future the canvas selector will also be used to select an image or image texture node
to paint on. Basic implementation has already been done inside this patch.

A limitation of this patch is that is isn't possible anymore to rename images directly from
the selection panel. This is currently allowed in master. But as CustomDataLayers
aren't ID fields and not owned by the material supporting this wouldn't be easy.

{F12953989}

In the future we should update the create slot operator to also include color attributes.
Sources could also be extended to use other areas of the object that use image textures
(particles, geom nodes, etc... ).

Reviewed By: brecht

Maniphest Tasks: T96709

Differential Revision: https://developer.blender.org/D14455
This commit is contained in:
Jeroen Bakker 2022-04-08 16:37:35 +02:00 committed by Jeroen Bakker
parent 63d2980efa
commit 8b7cd1ed2a
Notes: blender-bot 2023-02-14 11:29:52 +01:00
Referenced by commit 997ff54d30, Fix: UI: broken texpaintslot/color attributes/attributes name filtering
Referenced by commit 634e4a49d6, Fix T101334: Paint Texture Slots appear greyed out
Referenced by issue #102878, Regression: UI texpaintslot/color attributes/attributes name filtering broken
Referenced by issue #101334, i cant select the texture I created in texture paint
Referenced by issue #99868, Regression: Texture slots thumbnails in Texture painting mode are missing
Referenced by issue #99196, Regression: Rendering in the viewport is constantly restarted by some sculpting brushes and the position of mouse cursor
Referenced by issue #99196, Regression: Rendering in the viewport is constantly restarted by some sculpting brushes and the position of mouse cursor
Referenced by issue #96709, Select painting target for paint brushes
28 changed files with 783 additions and 125 deletions

View File

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

View File

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

View File

@ -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)
have_image = False
if mat.texture_paint_slots:
slot = mat.texture_paint_slots[mat.paint_active_slot]
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")
box = row.box()
box.label(text="No Textures")
have_image = False
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="")
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)
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 '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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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, ma, 0, slot_len};
ntree_foreach_texnode_recursive(nodetree, fill_texpaint_slots_cb, &fill_data);
struct FillTexPaintSlotsData fill_data = {active_node, ob, ma, 0, slot_len};
ntree_foreach_texnode_recursive(nodetree, fill_texpaint_slots_cb, &fill_data, slot_filter);
}
void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma)
/** Check which type of paint slots should be filled for the given object. */
static ePaintSlotFilter material_paint_slot_filter(const struct Object *ob)
{
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, 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;
}

View File

@ -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 */
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);
/* Make sure only one node is active per node tree. */
LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) {
tnode->flag &= ~NODE_ACTIVE;
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;
tnode->flag &= ~flags_to_set;
}
node->flag |= flags_to_set;
}
int nodeSocketIsHidden(const bNodeSocket *sock)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -929,6 +929,19 @@ typedef struct ImagePaintSettings {
char _pad[4];
} 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 */
@ -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 {

View File

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

View File

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

View File

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

View File

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

View File

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