2
$\begingroup$

Being able to quickly change blending mode in texture painting would be a HUGE efficiency improvment.

I've been looking for a while to achieve this but didn't manage :(

Even if its not possible to bind specific blending modes to a key, it would be great simply to have a dedicated shortcut just to open blending modes window

enter image description here

Edit v2: so I digged a bit inside the source code, as Craig D Jones suggested, but I still can't wrap my head around how I could do this (again, I'm an artist and not a programmer, altho I'm slowly learning how python works. But I'd REALLY appreciate if someone knowledgeable in this area could help me out)

# init blend mode
class PAINT_OT_InitPaintBlend(Operator):
    '''Init to mix paint  mode'''
    bl_idname = "paint.init_blend_mode"
    bl_label = "Init paint blend mode"

    @classmethod
    def poll(cls, context):
        return bpy.ops.paint.image_paint.poll()

    def invoke(self, context, event):
        brush = context.tool_settings.image_paint.brush
        brush.blend = 'MIX'

        wm = context.window_manager
        if "tpp_toolmode_on_screen" in wm:
            init_temp_props()
            co2d = (event.mouse_region_x, event.mouse_region_y)
            wm["tpp_toolmode_brushloc"] = co2d
            args = (self, context)
            self._handle = bpy.types.SpaceView3D.draw_handler_add(\
                                                        toolmode_draw_callback,
                                                        args,
                                                        'WINDOW',
                                                        'POST_PIXEL')
        return{'FINISHED'}

I also found this code below mentioning toggle between ADD and MULTIPLY blending mode but maybe it will be useful

class PAINT_OT_ToggleAddMultiply(Operator):
    '''Toggle between Add and Multiply blend modes'''
    bl_idname = "paint.toggle_add_multiply"
    bl_label = "Toggle add/multiply"

    @classmethod
    def poll(cls, context):
        return bpy.ops.paint.image_paint.poll()

    def invoke(self, context, event):
        brush = context.tool_settings.image_paint.brush
        if brush.blend != 'MUL':
            brush.blend = 'MUL'
        else:
            brush.blend = 'ADD'

        wm = context.window_manager
        if "tpp_toolmode_on_screen" in wm:
            init_temp_props()
            co2d = (event.mouse_region_x, event.mouse_region_y)
            wm["tpp_toolmode_brushloc"] = co2d
            args = (self, context)
            self._handle = bpy.types.SpaceView3D.draw_handler_add(\
                                                        toolmode_draw_callback,
                                                        args,
                                                        'WINDOW',
                                                        'POST_PIXEL')
        return {"FINISHED"}

Thanks for checking this out, hope someone manages to help!

$\endgroup$
  • $\begingroup$ github.com/artistCDMJ/ez_paint/archive/master.zip You can use our add-on EZPaint as an example of how it works to script the schortcuts, or just use the add-on as is. The 2.8 version is working mostly, still being fixed up. To call the brush-popup press the W key while in paint mode. Also Alt-W brings up a textures/masks popup related to the brush that is active. $\endgroup$ – Craig D Jones Jun 17 at 18:50
  • 1
    $\begingroup$ thanks for answer! I checked out that addon and its pretty neat, altho sadly, even after looking at the code, I still have no idea how to extract a line that would change my blending mode :( . I'm really hoping someone can help me out on this $\endgroup$ – Alumx Jun 18 at 2:27
  • 1
    $\begingroup$ I’ll look tomorrow at lunch to see if I can make a simple pie menu for just blend modes $\endgroup$ – Craig D Jones Jun 18 at 2:38
  • 1
    $\begingroup$ I'm still working on the pie menu, but 2.8 seems to need a better context for the switch to work. If I simply plug the line to the console C.tool_settings.image_paint.brush.blend = 'MUL' and press enter, it changes to Multiply. If I set up a simple script of if/else to toggle, for some reason I am not getting it to work yet. $\endgroup$ – Craig D Jones Jun 20 at 17:12
3
+50
$\begingroup$

I think I figured it out - but I couldn't get the normal variable = property to work, so I just spelled it out to get it to work correctly. This is a pie menu add-on that you need to compress to zip before installing, and it is set to use Shift-G to call the pie menu. The included toggles can give you an idea of how you can set up the others you want, or you can set this up to just make the change per menu item instead of toggles if you want.

# -*- coding: utf8 -*-
# python
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

bl_info = {"name": "Blend Modes Pie",
           "author": "CDMJ",
           "version": (1, 0, 0),
           "blender": (2, 80, 0),
           "location": "",
           "description": "Assistant to help toggle blend modes in Texture Paint",
           "warning": "Alpha version",
           "category": "3D View"}


import bpy
from bpy.types import Menu

#operators
class VIEW3D_OT_add_multiply(bpy.types.Operator):
    """Toggle Add Multiply"""
    bl_idname = "object.toggle_add_multiply"

    bl_label = "Toggle Add Multiply"
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):

        scene = context.scene
        #new code
        #brush = bpy.context.tool_settings.image_paint.brush.blend
        if bpy.context.tool_settings.image_paint.brush.blend != 'MUL':
            bpy.context.tool_settings.image_paint.brush.blend = 'MUL'
        else:
            bpy.context.tool_settings.image_paint.brush.blend = 'ADD'

        return {'FINISHED'}

class VIEW3D_OT_screen_mix(bpy.types.Operator):
    """Toggle Screen Mix"""
    bl_idname = "object.toggle_screen_mix"

    bl_label = "Toggle Screen Mix"
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):

        scene = context.scene
        #new code
        #brush = bpy.context.tool_settings.image_paint.brush.blend
        bpy.context.tool_settings.image_paint.brush.blend
        if bpy.context.tool_settings.image_paint.brush.blend != 'MIX':
            bpy.context.tool_settings.image_paint.brush.blend = 'MIX'
        else:
            bpy.context.tool_settings.image_paint.brush.blend = 'SCREEN'

        return {'FINISHED'}

class VIEW3D_OT_color_overlay(bpy.types.Operator):
    """Toggle Color Overaly"""
    bl_idname = "object.toggle_color_overlay"

    bl_label = "Toggle Color Overlay"
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):

        scene = context.scene
        #new code
        #brush = bpy.context.tool_settings.image_paint.brush.blend
        if bpy.context.tool_settings.image_paint.brush.blend != 'OVERLAY':
            bpy.context.tool_settings.image_paint.brush.blend= 'OVERLAY'
        else:
            bpy.context.tool_settings.image_paint.brush.blend = 'COLOR'

        return {'FINISHED'}

class VIEW3D_OT_add_erase_alpha(bpy.types.Operator):
    """Toggle Add/Erase Alpha"""
    bl_idname = "object.toggle_add_erase_alpha"

    bl_label = "Toggle Add/Erase Alpha"
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):

        scene = context.scene
        #new code
        #brush = bpy.context.tool_settings.image_paint.brush.blend
        if bpy.context.tool_settings.image_paint.brush.blend != 'ERASE_ALPHA':
            bpy.context.tool_settings.image_paint.brush.blend = 'ERASE_ALPHA'
        else:
            bpy.context.tool_settings.image_paint.brush.blend = 'ADD_ALPHA'

        return {'FINISHED'}

class VIEW3D_OT_hard_soft_light(bpy.types.Operator):
    """Toggle Hard/Soft Light"""
    bl_idname = "object.toggle_hard_soft_light"

    bl_label = "Toggle Hard/Soft Light"
    bl_options = { 'REGISTER', 'UNDO' }

    def execute(self, context):

        scene = context.scene
        #new code
        #brush = bpy.context.tool_settings.image_paint.brush.blend
        if bpy.context.tool_settings.image_paint.brush.blend != 'HARDLIGHT':
            bpy.context.tool_settings.image_paint.brush.blend = 'HARDLIGHT'
        else:
            bpy.context.tool_settings.image_paint.brush.blend = 'SOFTLIGHT'

        return {'FINISHED'}

class VIEW3D_PIE_blendmode(Menu):
    # label is displayed at the center of the pie menu.
    bl_label = "Blend Mode"

    def draw(self, context):
        layout = self.layout

        pie = layout.menu_pie()


        pie.operator("object.toggle_add_multiply", text='ADD/MULTIPLY',
            icon='RENDERLAYERS')
        pie.operator("object.toggle_screen_mix", text='SCREEN/MIX',
            icon='RENDERLAYERS')
        pie.operator("object.toggle_color_overlay", text='COLOR/OVERLAY',
            icon='RENDERLAYERS')
        pie.operator("object.toggle_add_erase_alpha", text='ADD/ERASE ALPHA',
            icon='RENDERLAYERS')
        pie.operator("object.toggle_hard_soft_light", text='HARD/SOFT LIGHT',
            icon='RENDERLAYERS')

_CLASSES = (
    VIEW3D_OT_add_multiply,
    VIEW3D_OT_screen_mix,
    VIEW3D_OT_color_overlay,
    VIEW3D_OT_add_erase_alpha,
    VIEW3D_OT_hard_soft_light
)


def register():
    for cls in _CLASSES:
        bpy.utils.register_class(cls)

    km_list = ['3D View']
    for i in km_list:
        sm = bpy.context.window_manager
        km = sm.keyconfigs.default.keymaps[i]
        kmi = km.keymap_items.new('wm.call_menu_pie', 'G', 'PRESS', shift=True)
        kmi.properties.name = "VIEW3D_PIE_blendmode"

def unregister():
    for cls in _CLASSES:
        bpy.utils.unregister_class(cls)

    km_list = ['3D View']
    for i in km_list:
        sm = bpy.context.window_manager
        km = sm.keyconfigs.default.keymaps[i]
        for kmi in (kmi for kmi in km.keymap_items \
                            if (kmi.idname == "VIEW3D_PIE_blendmode")):
            km.keymap_items.remove(kmi)
$\endgroup$
  • 1
    $\begingroup$ Oh thank you for answer! This is looking quite promising, but every time I try to activate it I get this error message in console (UI_pie_menu_invoke: named menu "VIEW3D_PIE_blendmode" not found). Also It doesn't seem to register shortcut the way it was set up in code, I actually need to insert it manually (in case that helps to find the problem with code) $\endgroup$ – Alumx Jun 21 at 8:03
  • 1
    $\begingroup$ I'll run through it again at lunch and see if I can fix that issue. The main thing is to show you how to do this - I also tried setting up a popup menu that can give you all the modes as if you are pressing the option in the tool properties, but then realized that is the same as the topbar. Also, I want to find a way to maybe enumerate from the property instead of making dummy operators all over the place. The pie above is more useful to me, but the EZPaint W key popup is my goto when painting as it gives me the old toolbar panel. $\endgroup$ – Craig D Jones Jun 21 at 15:09
0
$\begingroup$

enter image description here

Alright, so I managed to create a script to address this issue

Thanks to Craig D Jones for fishing out the line of code I was looking for in order to change blending modes! It was crucial step

I apologize for how crude this code is, but it works, and if someone can make it more user friendly, feel free to contribute! enter image description here

How to use the addon:

  • Install the BlendingHotkeys.zip (just save the code in a text file with .py extension and place it inside a zip folder for blender to see it as an addon)
  • Create new shortcuts in 3D View > Image Paint > Image Paint (Global)
  • Use one of following identifiers to call desired function

blendmode.mix -----> Set blending mode to mix

blendmode.dark -----> "Set blending mode to dark"

blendmode.mul -----> "Set blending mode to multiply"

blendmode.colorburn -----> "Set blending mode to colorburn"

blendmode.linearburn -----> "Set blending mode to linearburn"

blendmode.lighten -----> "Set blending mode to lighten"

blendmode.screen -----> "Set blending mode to screen"

blendmode.colordodge -----> "Set blending mode to color dodge"

blendmode.overlay -----> "Set blending mode to overlay"

blendmode.softlight -----> "Set blending mode to soft light"

blendmode.hardlight -----> "Set blending mode to hard light"

blendmode.vividlight -----> "Set blending mode to vivid light"

blendmode.linearlight -----> "Set blending mode to linear light"

blendmode.pinlight -----> "Set blending mode to pin light"

blendmode.difference -----> "Set blending mode to difference"

blendmode.exclusion -----> "Set blending mode to exclusion"

blendmode.sub -----> "Set blending mode to subtract"

blendmode.hue -----> "Set blending mode to hue"

blendmode.saturation -----> "Set blending mode to saturation"

blendmode.color -----> "Set blending mode to color"

blendmode.luminosity -----> "Set blending mode to luminosity"

blendmode.erase_alpha -----> "Set blending mode to erase alpha"

blendmode.add_alpha -----> "Set blending mode to add alpha"

    bl_info = {
    "name" : "Shortcuts for Blending modes",
    "author" : "Alumx",
    "description" : "",
    "blender" : (2, 80, 0),
    "version" : (0, 1, 2),
    "location" : "",
    "warning" : "",
    "category" : "Paint"
}

import bpy
from bpy.types import Menu



class BlendModeMix(bpy.types.Operator):
    bl_idname = "blendmode.mix"
    bl_label = "Set blending mode to mix"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'MIX'
        return {'FINISHED'}



class BlendModeDark(bpy.types.Operator):
    bl_idname = "blendmode.dark"
    bl_label = "Set blending mode to dark"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'DARKEN'
        return {'FINISHED'}



class BlendModeMul(bpy.types.Operator):
    bl_idname = "blendmode.mul"
    bl_label = "Set blending mode to multiply"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'MUL'
        return {'FINISHED'}



class BlendModeColorburn(bpy.types.Operator):
    bl_idname = "blendmode.colorburn"
    bl_label = "Set blending mode to colorburn"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'COLORBURN'
        return {'FINISHED'}



class BlendModeLinearburn(bpy.types.Operator):
    bl_idname = "blendmode.linearburn"
    bl_label = "Set blending mode to linearburn"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'LINEARBURN'
        return {'FINISHED'}



class BlendModeLighten(bpy.types.Operator):
    bl_idname = "blendmode.lighten"
    bl_label = "Set blending mode to lighten"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'LIGHTEN'
        return {'FINISHED'}



class BlendModeScreen(bpy.types.Operator):
    bl_idname = "blendmode.screen"
    bl_label = "Set blending mode to screen"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'SCREEN'
        return {'FINISHED'}



class BlendModeColordodge(bpy.types.Operator):
    bl_idname = "blendmode.colordodge"
    bl_label = "Set blending mode to color dodge"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'COLORDODGE'
        return {'FINISHED'}



class BlendModeOverlay(bpy.types.Operator):
    bl_idname = "blendmode.overlay"
    bl_label = "Set blending mode to overlay"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'OVERLAY'
        return {'FINISHED'}



class BlendModeSoftlight(bpy.types.Operator):
    bl_idname = "blendmode.softlight"
    bl_label = "Set blending mode to soft light"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'SOFTLIGHT'
        return {'FINISHED'}



class BlendModeHardlight(bpy.types.Operator):
    bl_idname = "blendmode.hardlight"
    bl_label = "Set blending mode to hard light"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'HARDLIGHT'
        return {'FINISHED'}




class BlendModeVividlight(bpy.types.Operator):
    bl_idname = "blendmode.vividlight"
    bl_label = "Set blending mode to vivid light"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'VIVIDLIGHT'
        return {'FINISHED'}




class BlendModeLinearlight(bpy.types.Operator):
    bl_idname = "blendmode.linearlight"
    bl_label = "Set blending mode to linear light"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'LINEARLIGHT'
        return {'FINISHED'}




class BlendModePinlight(bpy.types.Operator):
    bl_idname = "blendmode.pinlight"
    bl_label = "Set blending mode to pin light"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'PINLIGHT'
        return {'FINISHED'}




class BlendModeDifference(bpy.types.Operator):
    bl_idname = "blendmode.difference"
    bl_label = "Set blending mode to difference"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'DIFFERENCE'
        return {'FINISHED'}




class BlendModeExclusion(bpy.types.Operator):
    bl_idname = "blendmode.exclusion"
    bl_label = "Set blending mode to exclusion"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'EXCLUSION'
        return {'FINISHED'}




class BlendModeSub(bpy.types.Operator):
    bl_idname = "blendmode.sub"
    bl_label = "Set blending mode to subtract"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'SUB'
        return {'FINISHED'}




class BlendModeHue(bpy.types.Operator):
    bl_idname = "blendmode.hue"
    bl_label = "Set blending mode to hue"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'HUE'
        return {'FINISHED'}




class BlendModeSaturation(bpy.types.Operator):
    bl_idname = "blendmode.saturation"
    bl_label = "Set blending mode to saturation"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'SATURATION'
        return {'FINISHED'}




class BlendModeCalor(bpy.types.Operator):
    bl_idname = "blendmode.color"
    bl_label = "Set blending mode to color"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'COLOR'
        return {'FINISHED'}




class BlendModeLuminosity(bpy.types.Operator):
    bl_idname = "blendmode.luminosity"
    bl_label = "Set blending mode to luminosity"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'LUMINOSITY'
        return {'FINISHED'}




class BlendModeErase_alpha(bpy.types.Operator):
    bl_idname = "blendmode.erase_alpha"
    bl_label = "Set blending mode to erase alpha"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'ERASE_ALPHA'
        return {'FINISHED'}




class BlendModeAdd_alpha(bpy.types.Operator):
    bl_idname = "blendmode.add_alpha"
    bl_label = "Set blending mode to add alpha"

    def execute(self, context):
        bpy.context.tool_settings.image_paint.brush.blend = 'ADD_ALPHA'
        return {'FINISHED'}





def register():
    bpy.utils.register_class(BlendModeAdd_alpha)
    bpy.utils.register_class(BlendModeCalor)
    bpy.utils.register_class(BlendModeColorburn)
    bpy.utils.register_class(BlendModeColordodge)
    bpy.utils.register_class(BlendModeDark)
    bpy.utils.register_class(BlendModeDifference)
    bpy.utils.register_class(BlendModeErase_alpha)
    bpy.utils.register_class(BlendModeExclusion)
    bpy.utils.register_class(BlendModeHardlight)
    bpy.utils.register_class(BlendModeHue)
    bpy.utils.register_class(BlendModeLighten)
    bpy.utils.register_class(BlendModeLinearburn)
    bpy.utils.register_class(BlendModeLuminosity)
    bpy.utils.register_class(BlendModeMix)
    bpy.utils.register_class(BlendModeMul)
    bpy.utils.register_class(BlendModeOverlay)
    bpy.utils.register_class(BlendModePinlight)
    bpy.utils.register_class(BlendModeSaturation)
    bpy.utils.register_class(BlendModeScreen)
    bpy.utils.register_class(BlendModeSoftlight)
    bpy.utils.register_class(BlendModeSub)
    bpy.utils.register_class(BlendModeVividlight)

def unregister():
    bpy.utils.register_class(BlendModeAdd_alpha)
    bpy.utils.register_class(BlendModeCalor)
    bpy.utils.register_class(BlendModeColorburn)
    bpy.utils.register_class(BlendModeColordodge)
    bpy.utils.register_class(BlendModeDark)
    bpy.utils.register_class(BlendModeDifference)
    bpy.utils.register_class(BlendModeErase_alpha)
    bpy.utils.register_class(BlendModeExclusion)
    bpy.utils.register_class(BlendModeHardlight)
    bpy.utils.register_class(BlendModeHue)
    bpy.utils.register_class(BlendModeLighten)
    bpy.utils.register_class(BlendModeLinearburn)
    bpy.utils.register_class(BlendModeLuminosity)
    bpy.utils.register_class(BlendModeMix)
    bpy.utils.register_class(BlendModeMul)
    bpy.utils.register_class(BlendModeOverlay)
    bpy.utils.register_class(BlendModePinlight)
    bpy.utils.register_class(BlendModeSaturation)
    bpy.utils.register_class(BlendModeScreen)
    bpy.utils.register_class(BlendModeSoftlight)
    bpy.utils.register_class(BlendModeSub)
    bpy.utils.register_class(BlendModeVividlight)    


if __name__ == "__main__":
    register()    
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.