diff options
author | meta-androcto <meta.androcto1@gmail.com> | 2017-03-20 01:50:42 +0300 |
---|---|---|
committer | meta-androcto <meta.androcto1@gmail.com> | 2017-03-20 01:50:42 +0300 |
commit | 9007bcd10713e55168235e9e8420b17172674638 (patch) | |
tree | 57ecdb75283765246970b188bd473c83ba6908e3 /space_view3d_brush_menus | |
parent | 7f6ae93c548a8ce99ae0a21b10e9a686105f08d3 (diff) |
brush menus alt/v: T42564
Diffstat (limited to 'space_view3d_brush_menus')
-rw-r--r-- | space_view3d_brush_menus/Utils/__init__.py | 0 | ||||
-rw-r--r-- | space_view3d_brush_menus/Utils/core.py | 204 | ||||
-rw-r--r-- | space_view3d_brush_menus/__init__.py | 138 | ||||
-rw-r--r-- | space_view3d_brush_menus/brush_menu.py | 522 | ||||
-rw-r--r-- | space_view3d_brush_menus/brushes.py | 137 | ||||
-rw-r--r-- | space_view3d_brush_menus/curve_menu.py | 57 | ||||
-rw-r--r-- | space_view3d_brush_menus/dyntopo_menu.py | 125 | ||||
-rw-r--r-- | space_view3d_brush_menus/stroke_menu.py | 117 | ||||
-rw-r--r-- | space_view3d_brush_menus/symmetry_menu.py | 55 | ||||
-rw-r--r-- | space_view3d_brush_menus/texture_menu.py | 306 |
10 files changed, 1661 insertions, 0 deletions
diff --git a/space_view3d_brush_menus/Utils/__init__.py b/space_view3d_brush_menus/Utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/space_view3d_brush_menus/Utils/__init__.py diff --git a/space_view3d_brush_menus/Utils/core.py b/space_view3d_brush_menus/Utils/core.py new file mode 100644 index 00000000..ea73a87b --- /dev/null +++ b/space_view3d_brush_menus/Utils/core.py @@ -0,0 +1,204 @@ +import bpy +import time +import sys +import os +import re + +object_mode = 'OBJECT' +edit = 'EDIT' +sculpt = 'SCULPT' +vertex_paint = 'VERTEX_PAINT' +weight_paint = 'WEIGHT_PAINT' +texture_paint = 'TEXTURE_PAINT' +particle_edit = 'PARTICLE_EDIT' +pose = 'POSE' +gpencil_edit = 'GPENCIL_EDIT' + +PIW = ' ' + +a_props = [] + +class Menu(): + def __init__(self, menu): + self.layout = menu.layout + self.items = {} + self.current_item = None + + def add_item(self, ui_type="row", parent=None, name=None, **kwargs): + # set the parent layout + if parent: + layout = parent + else: + layout = self.layout + + # set unique identifier for new items + if not name: + name = len(self.items) + 1 + + # create and return a ui layout + if ui_type == "row": + self.current_item = self.items[name] = layout.row(**kwargs) + + return self.current_item + + elif ui_type == "column": + self.current_item = self.items[name] = layout.column(**kwargs) + + return self.current_item + + elif ui_type == "column_flow": + self.current_item = self.items[name] = layout.column_flow(**kwargs) + + return self.current_item + + elif ui_type == "box": + self.current_item = self.items[name] = layout.box(**kwargs) + + return self.current_item + + elif ui_type == "split": + self.current_item = self.items[name] = layout.split(**kwargs) + + return self.current_item + + else: + print("Unknown Type") + + +def get_selected(): + # get the number of verts from the information string on the info header + sel_verts_num = (e for e in bpy.context.scene.statistics().split(" | ") + if e.startswith("Verts:")).__next__()[6:].split("/") + + # turn the number of verts from a string to an int + sel_verts_num = int(sel_verts_num[0].replace("," ,"")) + + # get the number of edges from the information string on the info header + sel_edges_num = (e for e in bpy.context.scene.statistics().split(" | ") + if e.startswith("Edges:")).__next__()[6:].split("/") + + # turn the number of edges from a string to an int + sel_edges_num = int(sel_edges_num[0].replace(",", "")) + + # get the number of faces from the information string on the info header + sel_faces_num = (e for e in bpy.context.scene.statistics().split(" | ") + if e.startswith("Faces:")).__next__()[6:].split("/") + + # turn the number of faces from a string to an int + sel_faces_num = int(sel_faces_num[0].replace(",", "")) + + return sel_verts_num, sel_edges_num, sel_faces_num + + +def get_mode(): + if bpy.context.gpencil_data and \ + bpy.context.object.mode == object_mode and \ + bpy.context.scene.grease_pencil.use_stroke_edit_mode: + return gpencil_edit + else: + return bpy.context.object.mode + +def menuprop(item, name, value, data_path, + icon='NONE', disable=False, disable_icon=None, + custom_disable_exp=None, method=None, path=False): + + # disable the ui + if disable: + disabled = False + + # used if you need a custom expression to disable the ui + if custom_disable_exp: + if custom_disable_exp[0] == custom_disable_exp[1]: + item.enabled = False + disabled = True + + # check if the ui should be disabled for numbers + elif isinstance(eval("bpy.context.{}".format(data_path)), float): + if round(eval("bpy.context.{}".format(data_path)), 2) == value: + item.enabled = False + disabled = True + + # check if the ui should be disabled for anything else + else: + if eval("bpy.context.{}".format(data_path)) == value: + item.enabled = False + disabled = True + + # change the icon to the disable_icon if the ui has been disabled + if disable_icon and disabled: + icon = disable_icon + + # creates the menu item + prop = item.operator("wm.context_set_value", text=name, icon=icon) + + # sets what the menu item changes + if path: + prop.value = value + value = eval(value) + + elif type(value) == str: + prop.value = "'{}'".format(value) + + else: + prop.value = '{}'.format(value) + + # sets the path to what is changed + prop.data_path = data_path + +# used for global blender properties +def set_prop(prop_type, path, **kwargs): + kwstring = "" + + # turn **kwargs into a string that can be used with exec + for k, v in kwargs.items(): + if type(v) is str: + v = '"{}"'.format(v) + + if callable(v): + exec("from {0} import {1}".format(v.__module__, v.__name__)) + v = v.__name__ + + kwstring += "{0}={1}, ".format(k, v) + + kwstring = kwstring[:-2] + + # create the property + exec("{0} = bpy.props.{1}({2})".format(path, prop_type, kwstring)) + + # add the path to a list of property paths + a_props.append(path) + + return eval(path) + +# used for removing properties created with set_prop +def del_props(): + for prop in a_props: + exec("del {}".format(prop)) + + a_props.clear() + +class SendReport(bpy.types.Operator): + bl_label = "Send Report" + bl_idname = "view3d.send_report" + + message = bpy.props.StringProperty() + + def draw(self, context): + self.layout.label("Error", icon='ERROR') + self.layout.label(self.message) + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_popup(self, width=400, height=200) + + def execute(self, context): + self.report({'INFO'}, self.message) + print(self.message) + return {'FINISHED'} + +def send_report(message): + def report(scene): + bpy.ops.view3d.send_report('INVOKE_DEFAULT', message=message) + bpy.app.handlers.scene_update_pre.remove(report) + + bpy.app.handlers.scene_update_pre.append(report) diff --git a/space_view3d_brush_menus/__init__.py b/space_view3d_brush_menus/__init__.py new file mode 100644 index 00000000..6613b82f --- /dev/null +++ b/space_view3d_brush_menus/__init__.py @@ -0,0 +1,138 @@ +# ##### 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 ##### +# Modified by Meta-Androcto + + +""" Copyright 2011 GPL licence applies""" + +bl_info = { + "name": "Sculpt/Paint Brush Menus", + "description": "Fast access to brushes & tools in Sculpt and Paint Modes", + "author": "Ryan Inch (Imaginer)", + "version": (1, 1, 3), + "blender": (2, 7, 8), + "location": "Alt V in Sculpt/Paint Modes", + "warning": '', # used for warning icon and text in addons panel + "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/Advanced_UI_Menus", + "category": "3D View"} + +import sys +import os +from bl_ui.properties_paint_common import ( + UnifiedPaintPanel, + brush_texture_settings, + brush_texpaint_common, + brush_mask_texture_settings, + ) +from .Utils.core import * + +from . import brush_menu +from . import brushes +from . import curve_menu +from . import dyntopo_menu +from . import stroke_menu +from . import symmetry_menu +from . import texture_menu + +# Use compact brushes menus # +def UseBrushesLists(): + # separate function just for more convience + useLists = bpy.context.user_preferences.addons[__name__].preferences.use_brushes_lists + + return bool(useLists) + +class VIEW3D_MT_Brush_Selection1(bpy.types.Menu): + bl_label = "Brush Tool" + + def draw(self, context): + layout = self.layout + settings = UnifiedPaintPanel.paint_settings(context) + + # check if brush exists (for instance, in paint mode before adding a slot) + if hasattr(settings, 'brush'): + brush = settings.brush + else: + brush = None + + if not brush: + return + + if not context.particle_edit_object: + if UseBrushesLists(): + flow = layout.column_flow(columns=3) + + for brsh in bpy.data.brushes: + if (context.sculpt_object and brsh.use_paint_sculpt): + props = flow.operator("wm.context_set_id", text=brsh.name, + icon_value=layout.icon(brsh)) + props.data_path = "tool_settings.sculpt.brush" + props.value = brsh.name + elif (context.image_paint_object and brsh.use_paint_image): + props = flow.operator("wm.context_set_id", text=brsh.name, + icon_value=layout.icon(brsh)) + props.data_path = "tool_settings.image_paint.brush" + props.value = brsh.name + elif (context.vertex_paint_object and brsh.use_paint_vertex): + props = flow.operator("wm.context_set_id", text=brsh.name, + icon_value=layout.icon(brsh)) + props.data_path = "tool_settings.vertex_paint.brush" + props.value = brsh.name + elif (context.weight_paint_object and brsh.use_paint_weight): + props = flow.operator("wm.context_set_id", text=brsh.name, + icon_value=layout.icon(brsh)) + props.data_path = "tool_settings.weight_paint.brush" + props.value = brsh.name + else: + layout.template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8) + + +class VIEW3D_MT_Brushes_Pref(bpy.types.AddonPreferences): + bl_idname = __name__ + + + use_brushes_lists = bpy.props.BoolProperty( + name="Use compact menus for brushes", + default=True, + description=("Use more compact menus instead \n" + "of thumbnails for displaying brushes") + ) + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "use_brushes_lists") + +def register(): + # register all blender classes + bpy.utils.register_module(__name__) + + # register brush menu + brush_menu.register() + +def unregister(): + # unregister brush menu + brush_menu.unregister() + + # delete all the properties you have created + del_props() + + # unregister all blender classes + bpy.utils.unregister_module(__name__) + +if __name__ == "__main__": + register() diff --git a/space_view3d_brush_menus/brush_menu.py b/space_view3d_brush_menus/brush_menu.py new file mode 100644 index 00000000..91daf19c --- /dev/null +++ b/space_view3d_brush_menus/brush_menu.py @@ -0,0 +1,522 @@ +from bpy.props import * +from .Utils.core import * + +def get_current_brush_icon(tool): + if get_mode() == sculpt: + icons = {"BLOB":'BRUSH_BLOB', + "CLAY":'BRUSH_CLAY', + "CLAY_STRIPS":'BRUSH_CLAY_STRIPS', + "CREASE":'BRUSH_CREASE', + "DRAW":'BRUSH_SCULPT_DRAW', + "FILL":'BRUSH_FILL', + "FLATTEN":'BRUSH_FLATTEN', + "GRAB":'BRUSH_GRAB', + "INFLATE":'BRUSH_INFLATE', + "LAYER":'BRUSH_LAYER', + "MASK":'BRUSH_MASK', + "NUDGE":'BRUSH_NUDGE', + "PINCH":'BRUSH_PINCH', + "ROTATE":'BRUSH_ROTATE', + "SCRAPE":'BRUSH_SCRAPE', + "SIMPLIFY":'BRUSH_SUBTRACT', + "SMOOTH":'BRUSH_SMOOTH', + "SNAKE_HOOK":'BRUSH_SNAKE_HOOK', + "THUMB":'BRUSH_THUMB'} + + elif get_mode() == vertex_paint: + icons = {"ADD":'BRUSH_ADD', + "BLUR":'BRUSH_BLUR', + "DARKEN":'BRUSH_DARKEN', + "LIGHTEN":'BRUSH_LIGHTEN', + "MIX":'BRUSH_MIX', + "MUL":'BRUSH_MULTIPLY', + "SUB":'BRUSH_SUBTRACT'} + + elif get_mode() == weight_paint: + icons = {"ADD":'BRUSH_ADD', + "BLUR":'BRUSH_BLUR', + "DARKEN":'BRUSH_DARKEN', + "LIGHTEN":'BRUSH_LIGHTEN', + "MIX":'BRUSH_MIX', + "MUL":'BRUSH_MULTIPLY', + "SUB":'BRUSH_SUBTRACT'} + + elif get_mode() == texture_paint: + icons = {"CLONE":'BRUSH_CLONE', + "DRAW":'BRUSH_TEXDRAW', + "FILL":'BRUSH_TEXFILL', + "MASK":'BRUSH_TEXMASK', + "SMEAR":'BRUSH_SMEAR', + "SOFTEN":'BRUSH_SOFTEN'} + + icon = icons[tool] + + return icon + +class BrushOptionsMenu(bpy.types.Menu): + bl_label = "Brush Options" + bl_idname = "VIEW3D_MT_sv3_brush_options" + + @classmethod + def poll(self, context): + if get_mode() in [sculpt, vertex_paint, weight_paint, texture_paint, particle_edit]: + return True + else: + return False + + def draw(self, context): + menu = Menu(self) + + if get_mode() == sculpt: + self.sculpt(menu, context) + + elif get_mode() in [vertex_paint, weight_paint]: + self.vw_paint(menu, context) + + elif get_mode() == texture_paint: + self.texpaint(menu, context) + + else: + self.particle(menu, context) + + def sculpt(self, menu, context): + menu.add_item().menu("VIEW3D_MT_Brush_Selection1", text="Brush", icon=get_current_brush_icon(context.tool_settings.sculpt.brush.sculpt_tool)) + menu.add_item().menu(BrushRadiusMenu.bl_idname) + menu.add_item().menu(BrushStrengthMenu.bl_idname) + menu.add_item().menu(BrushAutosmoothMenu.bl_idname) + menu.add_item().menu(BrushModeMenu.bl_idname) + menu.add_item().menu("VIEW3D_MT_sv3_texture_menu") + menu.add_item().menu("VIEW3D_MT_sv3_stroke_options") + menu.add_item().menu("VIEW3D_MT_sv3_brush_curve_menu") + menu.add_item().menu("VIEW3D_MT_sv3_dyntopo") + menu.add_item().menu("VIEW3D_MT_sv3_master_symmetry_menu") + + def vw_paint(self, menu, context): + if get_mode() == vertex_paint: + menu.add_item().operator(ColorPickerPopup.bl_idname, icon="COLOR") + menu.add_item().separator() + menu.add_item().menu("VIEW3D_MT_Brush_Selection1", text="Brush", icon=get_current_brush_icon(context.tool_settings.vertex_paint.brush.vertex_tool)) + menu.add_item().menu(BrushRadiusMenu.bl_idname) + menu.add_item().menu(BrushStrengthMenu.bl_idname) + menu.add_item().menu(BrushModeMenu.bl_idname) + menu.add_item().menu("VIEW3D_MT_sv3_texture_menu") + menu.add_item().menu("VIEW3D_MT_sv3_stroke_options") + menu.add_item().menu("VIEW3D_MT_sv3_brush_curve_menu") + if get_mode() == weight_paint: + menu.add_item().menu("VIEW3D_MT_Brush_Selection1", text="Brush", icon=get_current_brush_icon(context.tool_settings.vertex_paint.brush.vertex_tool)) + menu.add_item().menu(BrushWeightMenu.bl_idname) + menu.add_item().menu(BrushRadiusMenu.bl_idname) + menu.add_item().menu(BrushStrengthMenu.bl_idname) + menu.add_item().menu(BrushModeMenu.bl_idname) + menu.add_item().menu("VIEW3D_MT_sv3_stroke_options") + menu.add_item().menu("VIEW3D_MT_sv3_brush_curve_menu") + + def texpaint(self, menu, context): + toolsettings = context.tool_settings.image_paint + + if context.image_paint_object and not toolsettings.detect_data(): + menu.add_item().label("Missing Data", icon='ERROR') + menu.add_item().label("See Tool Shelf") + else: + if toolsettings.brush.image_tool in {'DRAW', 'FILL'} and \ + toolsettings.brush.blend not in {'ERASE_ALPHA', 'ADD_ALPHA'}: + menu.add_item().operator(ColorPickerPopup.bl_idname, icon="COLOR") + menu.add_item().separator() + + menu.add_item().menu("VIEW3D_MT_Brush_Selection1", text="Brush", icon=get_current_brush_icon(toolsettings.brush.image_tool)) + + if toolsettings.brush.image_tool in {'MASK'}: + menu.add_item().menu(BrushWeightMenu.bl_idname, text="Mask Value") + + if toolsettings.brush.image_tool not in {'FILL'}: + menu.add_item().menu(BrushRadiusMenu.bl_idname) + + menu.add_item().menu(BrushStrengthMenu.bl_idname) + + if toolsettings.brush.image_tool in {'DRAW'}: + menu.add_item().menu(BrushModeMenu.bl_idname) + menu.add_item().menu("VIEW3D_MT_sv3_texture_menu") + menu.add_item().menu("VIEW3D_MT_sv3_stroke_options") + menu.add_item().menu("VIEW3D_MT_sv3_brush_curve_menu") + menu.add_item().menu("VIEW3D_MT_sv3_master_symmetry_menu") + + + def particle(self, menu, context): + if context.tool_settings.particle_edit.tool == 'NONE': + menu.add_item().label("No Brush Selected") + menu.add_item().menu("VIEW3D_MT_sv3_brushes_menu", text="Select Brush") + else: + menu.add_item().menu("VIEW3D_MT_sv3_brushes_menu") + menu.add_item().menu(BrushRadiusMenu.bl_idname) + if context.tool_settings.particle_edit.tool != 'ADD': + menu.add_item().menu(BrushStrengthMenu.bl_idname) + + else: + menu.add_item().menu(ParticleCountMenu.bl_idname) + menu.add_item().separator() + menu.add_item().prop(context.tool_settings.particle_edit, + "use_default_interpolate", toggle=True) + + menu.add_item().prop(context.tool_settings.particle_edit.brush, + "steps", slider=True) + menu.add_item().prop(context.tool_settings.particle_edit, + "default_key_count", slider=True) + + if context.tool_settings.particle_edit.tool == 'LENGTH': + menu.add_item().separator() + menu.add_item().menu(ParticleLengthMenu.bl_idname) + + if context.tool_settings.particle_edit.tool == 'PUFF': + menu.add_item().separator() + menu.add_item().menu(ParticlePuffMenu.bl_idname) + menu.add_item().prop(context.tool_settings.particle_edit.brush, + "use_puff_volume", toggle=True) + + +class BrushRadiusMenu(bpy.types.Menu): + bl_label = "Radius" + bl_idname = "VIEW3D_MT_sv3_brush_radius_menu" + + def init(self, context): + if get_mode() == particle_edit: + settings = [["100", 100], + ["70", 70], + ["50", 50], + ["30", 30], + ["20", 20], + ["10", 10]] + + datapath = "tool_settings.particle_edit.brush.size" + proppath = context.tool_settings.particle_edit.brush + + else: + settings = [["200", 200], + ["150", 150], + ["100", 100], + ["50", 50], + ["35", 35], + ["10", 10]] + + datapath = "tool_settings.unified_paint_settings.size" + proppath = context.tool_settings.unified_paint_settings + + return settings, datapath, proppath + + def draw(self, context): + settings, datapath, proppath = self.init(context) + menu = Menu(self) + + # add the top slider + menu.add_item().prop(proppath, "size", slider=True) + menu.add_item().separator() + + # add the rest of the menu items + for i in range(len(settings)): + menuprop(menu.add_item(), settings[i][0], settings[i][1], + datapath, icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + +class BrushStrengthMenu(bpy.types.Menu): + bl_label = "Strength" + bl_idname = "VIEW3D_MT_sv3_brush_strength_menu" + + def init(self, context): + settings = [["1.0", 1.0], + ["0.7", 0.7], + ["0.5", 0.5], + ["0.3", 0.3], + ["0.2", 0.2], + ["0.1", 0.1]] + + if get_mode() == sculpt: + datapath = "tool_settings.sculpt.brush.strength" + proppath = context.tool_settings.sculpt.brush + + elif get_mode() == vertex_paint: + datapath = "tool_settings.vertex_paint.brush.strength" + proppath = context.tool_settings.vertex_paint.brush + + elif get_mode() == weight_paint: + datapath = "tool_settings.weight_paint.brush.strength" + proppath = context.tool_settings.weight_paint.brush + + elif get_mode() == texture_paint: + datapath = "tool_settings.image_paint.brush.strength" + proppath = context.tool_settings.image_paint.brush + + else: + datapath = "tool_settings.particle_edit.brush.strength" + proppath = context.tool_settings.particle_edit.brush + + return settings, datapath, proppath + + def draw(self, context): + settings, datapath, proppath = self.init(context) + menu = Menu(self) + + # add the top slider + menu.add_item().prop(proppath, "strength", slider=True) + menu.add_item().separator() + + # add the rest of the menu items + for i in range(len(settings)): + menuprop(menu.add_item(), settings[i][0], settings[i][1], + datapath, icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + +class BrushModeMenu(bpy.types.Menu): + bl_label = "Brush Mode" + bl_idname = "VIEW3D_MT_sv3_brush_mode_menu" + + def init(self): + if get_mode() == sculpt: + enum = bpy.context.tool_settings.sculpt.brush.bl_rna.properties['sculpt_plane'].enum_items + path = "tool_settings.sculpt.brush.sculpt_plane" + + elif get_mode() == texture_paint: + enum = bpy.context.tool_settings.image_paint.brush.bl_rna.properties['blend'].enum_items + path = "tool_settings.image_paint.brush.blend" + + else: + enum = bpy.context.tool_settings.vertex_paint.brush.bl_rna.properties['vertex_tool'].enum_items + path = "tool_settings.vertex_paint.brush.vertex_tool" + + return enum, path + + def draw(self, context): + enum, path = self.init() + menu = Menu(self) + + menu.add_item().label(text="Brush Mode") + menu.add_item().separator() + + if get_mode() == texture_paint: + column_flow = menu.add_item("column_flow", columns=2) + + # add all the brush modes to the menu + for brush in enum: + menuprop(menu.add_item(parent=column_flow), brush.name, + brush.identifier, path, icon='RADIOBUT_OFF', + disable=True, disable_icon='RADIOBUT_ON') + + else: + # add all the brush modes to the menu + for brush in enum: + menuprop(menu.add_item(), brush.name, + brush.identifier, path, icon='RADIOBUT_OFF', + disable=True, disable_icon='RADIOBUT_ON') + + +class BrushAutosmoothMenu(bpy.types.Menu): + bl_label = "Autosmooth" + bl_idname = "VIEW3D_MT_sv3_brush_autosmooth_menu" + + def init(self): + settings = [["1.0", 1.0], + ["0.7", 0.7], + ["0.5", 0.5], + ["0.3", 0.3], + ["0.2", 0.2], + ["0.1", 0.1]] + + return settings + + def draw(self, context): + settings = self.init() + menu = Menu(self) + + # add the top slider + menu.add_item().prop(context.tool_settings.sculpt.brush, + "auto_smooth_factor", slider=True) + menu.add_item().separator() + + # add the rest of the menu items + for i in range(len(settings)): + menuprop(menu.add_item(), settings[i][0], settings[i][1], + "tool_settings.sculpt.brush.auto_smooth_factor", + icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + +class BrushWeightMenu(bpy.types.Menu): + bl_label = "Weight" + bl_idname = "VIEW3D_MT_sv3_brush_weight_menu" + + def draw(self, context): + if get_mode() == weight_paint: + brush = context.tool_settings.unified_paint_settings + brushstr = "tool_settings.unified_paint_settings.weight" + name = "Weight" + else: + brush = context.tool_settings.image_paint.brush + brushstr = "tool_settings.image_paint.brush.weight" + name = "Mask Value" + + menu = Menu(self) + settings = [["1.0", 1.0], + ["0.7", 0.7], + ["0.5", 0.5], + ["0.3", 0.3], + ["0.2", 0.2], + ["0.1", 0.1]] + + # add the top slider + menu.add_item().prop(brush, + "weight", text=name, slider=True) + menu.add_item().separator() + + # add the rest of the menu items + for i in range(len(settings)): + menuprop(menu.add_item(), settings[i][0], settings[i][1], + brushstr, + icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + +class ParticleCountMenu(bpy.types.Menu): + bl_label = "Count" + bl_idname = "VIEW3D_MT_sv3_particle_count_menu" + + def init(self): + settings = [["50", 50], + ["25", 25], + ["10", 10], + ["5", 5], + ["3", 3], + ["1", 1]] + + return settings + + def draw(self, context): + settings = self.init() + menu = Menu(self) + + # add the top slider + menu.add_item().prop(context.tool_settings.particle_edit.brush, + "count", slider=True) + menu.add_item().separator() + + # add the rest of the menu items + for i in range(len(settings)): + menuprop(menu.add_item(), settings[i][0], settings[i][1], + "tool_settings.particle_edit.brush.count", + icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + +class ParticleLengthMenu(bpy.types.Menu): + bl_label = "Length Mode" + bl_idname = "VIEW3D_MT_sv3_particle_length_menu" + + def draw(self, context): + menu = Menu(self) + path = "tool_settings.particle_edit.brush.length_mode" + + # add the menu items + for item in context.tool_settings.particle_edit.brush.bl_rna.properties['length_mode'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') + +class ParticlePuffMenu(bpy.types.Menu): + bl_label = "Puff Mode" + bl_idname = "VIEW3D_MT_sv3_particle_puff_menu" + + def draw(self, context): + menu = Menu(self) + path = "tool_settings.particle_edit.brush.puff_mode" + + # add the menu items + for item in context.tool_settings.particle_edit.brush.bl_rna.properties['puff_mode'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') + +class FlipColorsTex(bpy.types.Operator): + bl_label = "Flip Colors" + bl_idname = "view3d.sv3_flip_colors_tex" + + def execute(self, context): + try: + bpy.ops.paint.brush_colors_flip() + except: + pass + + return {'FINISHED'} + +class FlipColorsVert(bpy.types.Operator): + bl_label = "Flip Colors" + bl_idname = "view3d.sv3_flip_colors_vert" + + def execute(self, context): + color = context.tool_settings.vertex_paint.brush.color + secondary_color = context.tool_settings.vertex_paint.brush.secondary_color + + orig_prim = color.hsv + orig_sec = secondary_color.hsv + + color.hsv = orig_sec + secondary_color.hsv = orig_prim + + return {'FINISHED'} + +class ColorPickerPopup(bpy.types.Operator): + bl_label = "Color" + bl_idname = "view3d.sv3_color_picker_popup" + bl_options = {'REGISTER'} + + def check(self, context): + return True + + def draw(self, context): + menu = Menu(self) + + if get_mode() == texture_paint: + settings = context.tool_settings.image_paint + brush = settings.brush + + else: + settings = context.tool_settings.vertex_paint + brush = settings.brush + + menu.add_item().template_color_picker(brush, "color", value_slider=True) + menu.add_item().prop(brush, "color", text="") + menu.current_item.prop(brush, "secondary_color", text="") + if get_mode() == vertex_paint: + menu.current_item.operator(FlipColorsVert.bl_idname, icon='FILE_REFRESH', text="") + else: + menu.current_item.operator(FlipColorsTex.bl_idname, icon='FILE_REFRESH', text="") + + if settings.palette: + menu.add_item("column").template_palette(settings, "palette", color=True) + + menu.add_item().template_ID(settings, "palette", new="palette.new") + + def execute(self, context): + return context.window_manager.invoke_popup(self, width=180) + + +### ------------ New hotkeys and registration ------------ ### + +addon_keymaps = [] + + +def register(): + wm = bpy.context.window_manager + modes = ['Sculpt', 'Vertex Paint', 'Weight Paint', 'Image Paint', 'Particle'] + + for mode in modes: + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('wm.call_menu', 'V', 'PRESS', alt=True) + kmi.properties.name = "VIEW3D_MT_sv3_brush_options" + addon_keymaps.append((km, kmi)) + + +def unregister(): + for km, kmi in addon_keymaps: + km.keymap_items.remove(kmi) + addon_keymaps.clear() diff --git a/space_view3d_brush_menus/brushes.py b/space_view3d_brush_menus/brushes.py new file mode 100644 index 00000000..48a0ca38 --- /dev/null +++ b/space_view3d_brush_menus/brushes.py @@ -0,0 +1,137 @@ +from bpy.props import * +from .Utils.core import * + + + +class BrushesMenu(bpy.types.Menu): + bl_label = "Brush" + bl_idname = "VIEW3D_MT_sv3_brushes_menu" + + def init(self): + if get_mode() == sculpt: + datapath = "tool_settings.sculpt.brush" + icon = {"BLOB": 'BRUSH_BLOB', + "CLAY": 'BRUSH_CLAY', + "CLAY_STRIPS": 'BRUSH_CLAY_STRIPS', + "CREASE": 'BRUSH_CREASE', + "DRAW": 'BRUSH_SCULPT_DRAW', + "FILL": 'BRUSH_FILL', + "FLATTEN": 'BRUSH_FLATTEN', + "GRAB": 'BRUSH_GRAB', + "INFLATE": 'BRUSH_INFLATE', + "LAYER": 'BRUSH_LAYER', + "MASK": 'BRUSH_MASK', + "NUDGE": 'BRUSH_NUDGE', + "PINCH": 'BRUSH_PINCH', + "ROTATE": 'BRUSH_ROTATE', + "SCRAPE": 'BRUSH_SCRAPE', + "SIMPLIFY": 'BRUSH_SUBTRACT', + "SMOOTH": 'BRUSH_SMOOTH', + "SNAKE_HOOK": 'BRUSH_SNAKE_HOOK', + "THUMB": 'BRUSH_THUMB'} + + elif get_mode() == vertex_paint: + datapath = "tool_settings.vertex_paint.brush" + icon = {"ADD": 'BRUSH_ADD', + "BLUR": 'BRUSH_BLUR', + "DARKEN": 'BRUSH_DARKEN', + "LIGHTEN": 'BRUSH_LIGHTEN', + "MIX": 'BRUSH_MIX', + "MUL": 'BRUSH_MULTIPLY', + "SUB": 'BRUSH_SUBTRACT'} + + elif get_mode() == weight_paint: + datapath = "tool_settings.weight_paint.brush" + icon = {"ADD": 'BRUSH_ADD', + "BLUR": 'BRUSH_BLUR', + "DARKEN": 'BRUSH_DARKEN', + "LIGHTEN": 'BRUSH_LIGHTEN', + "MIX": 'BRUSH_MIX', + "MUL": 'BRUSH_MULTIPLY', + "SUB": 'BRUSH_SUBTRACT'} + + elif get_mode() == texture_paint: + datapath = "tool_settings.image_paint.brush" + icon = {"CLONE": 'BRUSH_CLONE', + "DRAW": 'BRUSH_TEXDRAW', + "FILL": 'BRUSH_TEXFILL', + "MASK": 'BRUSH_TEXMASK', + "SMEAR": 'BRUSH_SMEAR', + "SOFTEN": 'BRUSH_SOFTEN'} + + elif get_mode() == particle_edit: + datapath = "tool_settings.particle_edit.tool" + icon = None + + else: + datapath = "" + + return datapath, icon + + def draw(self, context): + datapath, icon = self.init() + menu = Menu(self) + + menu.add_item().label(text="Brush") + menu.add_item().separator() + + current_brush = eval("bpy.context.{}".format(datapath)) + + # get the current brush's name + if current_brush and get_mode() != particle_edit: + current_brush = current_brush.name + + if get_mode() == particle_edit: + particle_tools = [["None", 'NONE'], + ["Comb", 'COMB'], + ["Smooth", 'SMOOTH'], + ["Add", 'ADD'], + ["Length", 'LENGTH'], + ["Puff", 'PUFF'], + ["Cut", 'CUT'], + ["Weight", 'WEIGHT']] + + # if you are in particle edit mode add the menu items for particle mode + for tool in particle_tools: + menuprop(menu.add_item(), tool[0], tool[1], datapath, + icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + else: + # iterate over all the brushes + for item in bpy.data.brushes: + if get_mode() == sculpt: + if item.use_paint_sculpt: + # if you are in sculpt mode and the brush is a sculpt brush add the brush to the menu + menuprop(menu.add_item(), item.name, + 'bpy.data.brushes["%s"]' % item.name, + datapath, icon=icon[item.sculpt_tool], + disable=True, custom_disable_exp=[item.name, current_brush], + path=True) + + if get_mode() == vertex_paint: + if item.use_paint_vertex: + # if you are in vertex paint mode and the brush is a vertex paint brush add the brush to the menu + menuprop(menu.add_item(), item.name, + 'bpy.data.brushes["%s"]' % item.name, + datapath, icon=icon[item.vertex_tool], + disable=True, custom_disable_exp=[item.name, current_brush], + path=True) + + if get_mode() == weight_paint: + if item.use_paint_weight: + # if you are in weight paint mode and the brush is a weight paint brush add the brush to the menu + menuprop(menu.add_item(), item.name, + 'bpy.data.brushes["%s"]' % item.name, + datapath, icon=icon[item.vertex_tool], + disable=True, custom_disable_exp=[item.name, current_brush], + path=True) + + if get_mode() == texture_paint: + if item.use_paint_image: + # if you are in texture paint mode and the brush is a texture paint brush add the brush to the menu + menuprop(menu.add_item(), item.name, + 'bpy.data.brushes["%s"]' % item.name, + datapath, icon=icon[item.image_tool], + disable=True, custom_disable_exp=[item.name, current_brush], + path=True) diff --git a/space_view3d_brush_menus/curve_menu.py b/space_view3d_brush_menus/curve_menu.py new file mode 100644 index 00000000..b7762e01 --- /dev/null +++ b/space_view3d_brush_menus/curve_menu.py @@ -0,0 +1,57 @@ +from .Utils.core import * + + +class BrushCurveMenu(bpy.types.Menu): + bl_label = "Curve" + bl_idname = "VIEW3D_MT_sv3_brush_curve_menu" + + @classmethod + def poll(self, context): + if get_mode() in [sculpt, vertex_paint, weight_paint, texture_paint, particle_edit]: + return True + else: + return False + + def draw(self, context): + menu = Menu(self) + curves = [["Smooth", "SMOOTH", "SMOOTHCURVE"], + ["Sphere", "ROUND", "SPHERECURVE"], + ["Root", "ROOT", "ROOTCURVE"], + ["Sharp", "SHARP", "SHARPCURVE"], + ["Linear", "LINE", "LINCURVE"], + ["Constant", "MAX", "NOCURVE"]] + + # add the top slider + menu.add_item().operator(CurvePopup.bl_idname, icon="RNDCURVE") + menu.add_item().separator() + + # add the rest of the menu items + for curve in curves: + item = menu.add_item().operator("brush.curve_preset", text=curve[0], icon=curve[2]) + item.shape = curve[1] + + +class CurvePopup(bpy.types.Operator): + bl_label = "Adjust Curve" + bl_idname = "view3d.sv3_curve_popup" + bl_options = {'REGISTER'} + + def draw(self, context): + menu = Menu(self) + + if get_mode() == sculpt: + brush = context.tool_settings.sculpt.brush + + elif get_mode() == vertex_paint: + brush = context.tool_settings.vertex_paint.brush + + elif get_mode() == weight_paint: + brush = context.tool_settings.weight_paint.brush + + else: + brush = context.tool_settings.image_paint.brush + + menu.add_item("column").template_curve_mapping(brush, "curve", brush=True) + + def execute(self, context): + return context.window_manager.invoke_popup(self, width=180) diff --git a/space_view3d_brush_menus/dyntopo_menu.py b/space_view3d_brush_menus/dyntopo_menu.py new file mode 100644 index 00000000..a15c562c --- /dev/null +++ b/space_view3d_brush_menus/dyntopo_menu.py @@ -0,0 +1,125 @@ +from bpy.props import * +from .Utils.core import * + +class DynTopoMenu(bpy.types.Menu): + bl_label = "Dyntopo" + bl_idname = "VIEW3D_MT_sv3_dyntopo" + + @classmethod + def poll(self, context): + if get_mode() == sculpt: + return True + else: + return False + + def draw(self, context): + menu = Menu(self) + + if context.object.use_dynamic_topology_sculpting: + menu.add_item().operator("sculpt.dynamic_topology_toggle", "Disable Dynamic Topology") + + menu.add_item().separator() + + menu.add_item().menu(DynDetailMenu.bl_idname) + menu.add_item().menu(DetailMethodMenu.bl_idname) + + menu.add_item().separator() + + menu.add_item().operator("sculpt.optimize") + if bpy.context.tool_settings.sculpt.detail_type_method == 'CONSTANT': + menu.add_item().operator("sculpt.detail_flood_fill") + + menu.add_item().menu(SymmetrizeMenu.bl_idname) + menu.add_item().prop(context.tool_settings.sculpt, "use_smooth_shading", toggle=True) + + else: + menu.add_item() + menu.current_item.operator_context = 'INVOKE_DEFAULT' + menu.current_item.operator("sculpt.dynamic_topology_toggle", "Enable Dynamic Topology") + + +class DynDetailMenu(bpy.types.Menu): + bl_label = "Detail Size" + bl_idname = "VIEW3D_MT_sv3_dyn_detail" + + def init(self): + settings = [["40", 40], ["30", 30], ["20", 20], + ["10", 10], ["5", 5], ["1", 1]] + + if bpy.context.tool_settings.sculpt.detail_type_method == 'RELATIVE': + datapath = "tool_settings.sculpt.detail_size" + slider_setting = "detail_size" + + else: + datapath = "tool_settings.sculpt.constant_detail" + slider_setting = "constant_detail" + + return settings, datapath, slider_setting + + def draw(self, context): + settings, datapath, slider_setting = self.init() + menu = Menu(self) + + # add the top slider + menu.add_item().prop(context.tool_settings.sculpt, slider_setting, slider=True) + menu.add_item().separator() + + # add the rest of the menu items + for i in range(len(settings)): + menuprop(menu.add_item(), settings[i][0], settings[i][1], datapath, + icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') + + +class DetailMethodMenu(bpy.types.Menu): + bl_label = "Detail Method" + bl_idname = "VIEW3D_MT_sv3_detail_method_menu" + + def draw(self, context): + menu = Menu(self) + refine_path = "tool_settings.sculpt.detail_refine_method" + type_path = "tool_settings.sculpt.detail_type_method" + + refine_items = [["Subdivide Edges", 'SUBDIVIDE'], + ["Collapse Edges", 'COLLAPSE'], + ["Subdivide Collapse", 'SUBDIVIDE_COLLAPSE']] + + type_items = [["Relative Detail", 'RELATIVE'], + ["Constant Detail", 'CONSTANT']] + + menu.add_item().label("Refine") + menu.add_item().separator() + + # add the refine menu items + for item in refine_items: + menuprop(menu.add_item(), item[0], item[1], refine_path, disable=True, + icon='RADIOBUT_OFF', disable_icon='RADIOBUT_ON') + + menu.add_item().label("") + + menu.add_item().label("Type") + menu.add_item().separator() + + # add the type menu items + for item in type_items: + menuprop(menu.add_item(), item[0], item[1], type_path, disable=True, + icon='RADIOBUT_OFF', disable_icon='RADIOBUT_ON') + + +class SymmetrizeMenu(bpy.types.Menu): + bl_label = "Symmetrize" + bl_idname = "VIEW3D_MT_sv3_symmetrize_menu" + + def draw(self, context): + menu = Menu(self) + path = "tool_settings.sculpt.symmetrize_direction" + + # add the the symmetrize operator to the menu + menu.add_item().operator("sculpt.symmetrize") + menu.add_item().separator() + + # add the rest of the menu items + for item in context.tool_settings.sculpt.bl_rna.properties['symmetrize_direction'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, disable=True, + icon='RADIOBUT_OFF', disable_icon='RADIOBUT_ON') + diff --git a/space_view3d_brush_menus/stroke_menu.py b/space_view3d_brush_menus/stroke_menu.py new file mode 100644 index 00000000..ff0708e7 --- /dev/null +++ b/space_view3d_brush_menus/stroke_menu.py @@ -0,0 +1,117 @@ +from bpy.props import * +from .Utils.core import * + +airbrush = 'AIRBRUSH' +anchored = 'ANCHORED' +space = 'SPACE' +drag_dot = 'DRAG_DOT' +dots = 'DOTS' +line = 'LINE' +curve = 'CURVE' + + +class StrokeOptionsMenu(bpy.types.Menu): + bl_label = "Stroke Options" + bl_idname = "VIEW3D_MT_sv3_stroke_options" + + @classmethod + def poll(self, context): + if get_mode() in [sculpt, vertex_paint, weight_paint, texture_paint, particle_edit]: + return True + else: + return False + + def init(self): + if get_mode() == sculpt: + settings = bpy.context.tool_settings.sculpt + brush = settings.brush + + if bpy.app.version > (2, 71): + stroke_method = brush.stroke_method + + else: + stroke_method = brush.sculpt_stroke_method + + elif get_mode() == texture_paint: + settings = bpy.context.tool_settings.image_paint + brush = settings.brush + stroke_method = brush.stroke_method + + else: + settings = bpy.context.tool_settings.vertex_paint + brush = settings.brush + stroke_method = brush.stroke_method + + return settings, brush, stroke_method + + def draw(self, context): + settings, brush, stroke_method = self.init() + menu = Menu(self) + + menu.add_item().menu(StrokeMethodMenu.bl_idname) + + menu.add_item().separator() + + if stroke_method == space: + menu.add_item().prop(brush, "spacing", text=PIW+"Spacing", slider=True) + + elif stroke_method == airbrush: + menu.add_item().prop(brush, "rate", text=PIW+"Rate", slider=True) + + elif stroke_method == anchored: + menu.add_item().prop(brush, "use_edge_to_edge") + + else: + pass + + if get_mode() == sculpt and stroke_method in [drag_dot, anchored]: + pass + else: + menu.add_item().prop(brush, "jitter", text=PIW+"Jitter", slider=True) + + menu.add_item().prop(settings, "input_samples", text=PIW+"Input Samples", slider=True) + + if stroke_method in [dots, space, airbrush]: + menu.add_item().separator() + + menu.add_item().prop(brush, "use_smooth_stroke", toggle=True) + + if brush.use_smooth_stroke: + menu.add_item().prop(brush, "smooth_stroke_radius", text=PIW+"Radius", slider=True) + menu.add_item().prop(brush, "smooth_stroke_factor", text=PIW+"Factor", slider=True) + + +class StrokeMethodMenu(bpy.types.Menu): + bl_label = "Stroke Method" + bl_idname = "VIEW3D_MT_sv3_stroke_method" + + def init(self): + if get_mode() == sculpt: + brush = bpy.context.tool_settings.sculpt.brush + path = "tool_settings.sculpt.brush.stroke_method" + + elif get_mode() == texture_paint: + brush = bpy.context.tool_settings.image_paint.brush + path = "tool_settings.image_paint.brush.stroke_method" + + else: + brush = bpy.context.tool_settings.vertex_paint.brush + path = "tool_settings.vertex_paint.brush.stroke_method" + + return brush, path + + def draw(self, context): + brush, path = self.init() + menu = Menu(self) + + menu.add_item().label(text="Stroke Method") + menu.add_item().separator() + + # add the menu items dynamicaly based on values in enum property + for tool in brush.bl_rna.properties['stroke_method'].enum_items: + if tool.identifier in [anchored, drag_dot] and get_mode() in [vertex_paint, weight_paint]: + continue + + menuprop(menu.add_item(), tool.name, tool.identifier, path, + icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON') diff --git a/space_view3d_brush_menus/symmetry_menu.py b/space_view3d_brush_menus/symmetry_menu.py new file mode 100644 index 00000000..f345e290 --- /dev/null +++ b/space_view3d_brush_menus/symmetry_menu.py @@ -0,0 +1,55 @@ +from bpy.props import * +from .Utils.core import * + + +class MasterSymmetryMenu(bpy.types.Menu): + bl_label = "Symmetry Options" + bl_idname = "VIEW3D_MT_sv3_master_symmetry_menu" + + @classmethod + def poll(self, context): + if get_mode() in [sculpt, texture_paint]: + return True + else: + return False + + def draw(self, context): + menu = Menu(self) + + if get_mode() == texture_paint: + menu.add_item().prop(context.tool_settings.image_paint, "use_symmetry_x", toggle=True) + menu.add_item().prop(context.tool_settings.image_paint, "use_symmetry_y", toggle=True) + menu.add_item().prop(context.tool_settings.image_paint, "use_symmetry_z", toggle=True) + else: + + menu.add_item().menu(SymmetryMenu.bl_idname) + menu.add_item().menu(SymmetryRadialMenu.bl_idname) + menu.add_item().prop(context.tool_settings.sculpt, "use_symmetry_feather", toggle=True) + + +class SymmetryMenu(bpy.types.Menu): + bl_label = "Symmetry" + bl_idname = "VIEW3D_MT_sv3_symmetry_menu" + + def draw(self, context): + menu = Menu(self) + + menu.add_item().label(text="Symmetry") + menu.add_item().separator() + + menu.add_item().prop(context.tool_settings.sculpt, "use_symmetry_x", toggle=True) + menu.add_item().prop(context.tool_settings.sculpt, "use_symmetry_y", toggle=True) + menu.add_item().prop(context.tool_settings.sculpt, "use_symmetry_z", toggle=True) + + +class SymmetryRadialMenu(bpy.types.Menu): + bl_label = "Radial" + bl_idname = "VIEW3D_MT_sv3_symmetry_radial_menu" + + def draw(self, context): + menu = Menu(self) + + menu.add_item().label(text="Radial") + menu.add_item().separator() + + menu.add_item("column").prop(context.tool_settings.sculpt, "radial_symmetry", text="", slider=True) diff --git a/space_view3d_brush_menus/texture_menu.py b/space_view3d_brush_menus/texture_menu.py new file mode 100644 index 00000000..1f44bc8f --- /dev/null +++ b/space_view3d_brush_menus/texture_menu.py @@ -0,0 +1,306 @@ +from bpy.props import * +from .Utils.core import * + + +class TextureMenu(bpy.types.Menu): + bl_label = "Texture Options" + bl_idname = "VIEW3D_MT_sv3_texture_menu" + + @classmethod + def poll(self, context): + if get_mode() in [sculpt, vertex_paint, texture_paint]: + return True + else: + return False + + def draw(self, context): + menu = Menu(self) + + if get_mode() == sculpt: + self.sculpt(menu, context) + + elif get_mode() == vertex_paint: + self.vertpaint(menu, context) + + else: + self.texpaint(menu, context) + + def sculpt(self, menu, context): + tex_slot = context.tool_settings.sculpt.brush.texture_slot + + # Menus + menu.add_item().menu(Textures.bl_idname) + menu.add_item().menu(TextureMapMode.bl_idname) + + # Checkboxes + if tex_slot.map_mode != '3D': + if tex_slot.map_mode in ['RANDOM', 'VIEW_PLANE', 'AREA_PLANE']: + if bpy.app.version >= (2, 75): + menu.add_item().prop(tex_slot, "use_rake", toggle=True) + menu.add_item().prop(tex_slot, "use_random", toggle=True) + else: + menu.add_item().menu(TextureAngleSource.bl_idname) + + # Sliders + menu.add_item().prop(tex_slot, "angle", text=PIW+"Angle", slider=True) + + if tex_slot.tex_paint_map_mode in ['RANDOM', 'VIEW_PLANE'] and tex_slot.use_random: + menu.add_item().prop(tex_slot, "random_angle", text=PIW+"Random Angle", slider=True) + + # Operator + if tex_slot.tex_paint_map_mode == 'STENCIL': + menu.add_item().operator("brush.stencil_reset_transform") + + def vertpaint(self, menu, context): + tex_slot = context.tool_settings.vertex_paint.brush.texture_slot + + # Menus + menu.add_item().menu(Textures.bl_idname) + menu.add_item().menu(TextureMapMode.bl_idname) + + # Checkboxes + if tex_slot.tex_paint_map_mode != '3D': + + if tex_slot.tex_paint_map_mode in ['RANDOM', 'VIEW_PLANE']: + if bpy.app.version >= (2, 75): + menu.add_item().prop(tex_slot, "use_rake", toggle=True) + menu.add_item().prop(tex_slot, "use_random", toggle=True) + else: + menu.add_item().menu(TextureAngleSource.bl_idname) + + # Sliders + menu.add_item().prop(tex_slot, "angle", text=PIW+"Angle", slider=True) + + if tex_slot.tex_paint_map_mode in ['RANDOM', 'VIEW_PLANE'] and tex_slot.use_random: + menu.add_item().prop(tex_slot, "random_angle", text=PIW+"Random Angle", slider=True) + + # Operator + if tex_slot.tex_paint_map_mode == 'STENCIL': + menu.add_item().operator("brush.stencil_reset_transform") + + def texpaint(self, menu, context): + tex_slot = context.tool_settings.image_paint.brush.texture_slot + mask_tex_slot = context.tool_settings.image_paint.brush.mask_texture_slot + + # Texture Section + menu.add_item().label(text="Texture", icon='TEXTURE') + + # Menus + menu.add_item().menu(Textures.bl_idname) + menu.add_item().menu(TextureMapMode.bl_idname) + + # Checkboxes + if tex_slot.tex_paint_map_mode != '3D': + if tex_slot.tex_paint_map_mode in ['RANDOM', 'VIEW_PLANE']: + if bpy.app.version >= (2, 75): + menu.add_item().prop(tex_slot, "use_rake", toggle=True) + menu.add_item().prop(tex_slot, "use_random", toggle=True) + else: + menu.add_item().menu(TextureAngleSource.bl_idname) + + # Sliders + menu.add_item().prop(tex_slot, "angle", text=PIW+"Angle", slider=True) + + if tex_slot.tex_paint_map_mode in ['RANDOM', 'VIEW_PLANE'] and tex_slot.use_random: + menu.add_item().prop(tex_slot, "random_angle", text=PIW+"Random Angle", slider=True) + + # Operator + if tex_slot.tex_paint_map_mode == 'STENCIL': + menu.add_item().operator("brush.stencil_reset_transform") + + menu.add_item().separator() + + # Texture Mask Section + menu.add_item().label(text="Texture Mask", icon='MOD_MASK') + + # Menus + menu.add_item().menu(MaskTextures.bl_idname) + menu.add_item().menu(MaskMapMode.bl_idname) + + # Checkboxes + if mask_tex_slot.mask_map_mode in ['RANDOM', 'VIEW_PLANE']: + if bpy.app.version >= (2, 75): + menu.add_item().prop(mask_tex_slot, "use_rake", toggle=True) + menu.add_item().prop(mask_tex_slot, "use_random", toggle=True) + else: + menu.add_item().menu(TextureAngleSource.bl_idname) + + # Sliders + menu.add_item().prop(mask_tex_slot, "angle", text=PIW+"Angle", icon_value=5, slider=True) + + if mask_tex_slot.mask_map_mode in ['RANDOM', 'VIEW_PLANE'] and mask_tex_slot.use_random: + menu.add_item().prop(mask_tex_slot, "random_angle", text=PIW+"Random Angle", slider=True) + + # Operator + if mask_tex_slot.mask_map_mode == 'STENCIL': + prop = menu.add_item().operator("brush.stencil_reset_transform") + prop.mask = True + + +class Textures(bpy.types.Menu): + bl_label = "Brush Texture" + bl_idname = "VIEW3D_MT_sv3_texture_list" + + def init(self): + if get_mode() == sculpt: + datapath = "tool_settings.sculpt.brush.texture" + + elif get_mode() == vertex_paint: + datapath = "tool_settings.vertex_paint.brush.texture" + + elif get_mode() == texture_paint: + datapath = "tool_settings.image_paint.brush.texture" + + else: + datapath = "" + + return datapath + + def draw(self, context): + datapath = self.init() + current_texture = eval("bpy.context.{}".format(datapath)) + menu = Menu(self) + + # get the current texture's name + if current_texture: + current_texture = current_texture.name + + menu.add_item().label(text="Brush Texture") + menu.add_item().separator() + + # add an item to set the texture to None + menuprop(menu.add_item(), "None", "None", + datapath, icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON', + custom_disable_exp=[None, current_texture], + path=True) + + # add the menu items + for item in bpy.data.textures: + menuprop(menu.add_item(), item.name, + 'bpy.data.textures["%s"]' % item.name, + datapath, icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON', + custom_disable_exp=[item.name, current_texture], + path=True) + + +class TextureMapMode(bpy.types.Menu): + bl_label = "Brush Mapping" + bl_idname = "VIEW3D_MT_sv3_texture_map_mode" + + def draw(self, context): + menu = Menu(self) + + menu.add_item().label(text="Brush Mapping") + menu.add_item().separator() + + if get_mode() == sculpt: + path = "tool_settings.sculpt.brush.texture_slot.map_mode" + + # add the menu items + for item in context.tool_settings.sculpt.brush.texture_slot.bl_rna.properties['map_mode'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') + + elif get_mode() == vertex_paint: + path = "tool_settings.vertex_paint.brush.texture_slot.tex_paint_map_mode" + + # add the menu items + for item in context.tool_settings.vertex_paint.brush.texture_slot.bl_rna.properties['tex_paint_map_mode'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') + + else: + path = "tool_settings.image_paint.brush.texture_slot.tex_paint_map_mode" + + # add the menu items + for item in context.tool_settings.image_paint.brush.texture_slot.bl_rna.properties['tex_paint_map_mode'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') + + +class MaskTextures(bpy.types.Menu): + bl_label = "Mask Texture" + bl_idname = "VIEW3D_MT_sv3_mask_texture_list" + + def draw(self, context): + menu = Menu(self) + datapath = "tool_settings.image_paint.brush.mask_texture" + current_texture = eval("bpy.context.{}".format(datapath)) + + menu.add_item().label(text="Mask Texture") + menu.add_item().separator() + + # get the current texture's name + if current_texture: + current_texture = current_texture.name + + # add an item to set the texture to None + menuprop(menu.add_item(), "None", "None", + datapath, icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON', + custom_disable_exp=[None, current_texture], + path=True) + + # add the menu items + for item in bpy.data.textures: + menuprop(menu.add_item(), item.name, 'bpy.data.textures["%s"]' % item.name, + datapath, icon='RADIOBUT_OFF', disable=True, + disable_icon='RADIOBUT_ON', + custom_disable_exp=[item.name, current_texture], + path=True) + + +class MaskMapMode(bpy.types.Menu): + bl_label = "Mask Mapping" + bl_idname = "VIEW3D_MT_sv3_mask_map_mode" + + def draw(self, context): + menu = Menu(self) + + path = "tool_settings.image_paint.brush.mask_texture_slot.mask_map_mode" + + menu.add_item().label(text="Mask Mapping") + menu.add_item().separator() + + # add the menu items + for item in context.tool_settings.image_paint.brush.mask_texture_slot.bl_rna.properties['mask_map_mode'].enum_items: + menuprop(menu.add_item(), item.name, item.identifier, path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') + + +class TextureAngleSource(bpy.types.Menu): + bl_label = "Texture Angle Source" + bl_idname = "VIEW3D_MT_sv3_texture_angle_source" + + def draw(self, context): + menu = Menu(self) + + if get_mode() == sculpt: + items = context.tool_settings.sculpt.brush.bl_rna.properties['texture_angle_source_random'].enum_items + path = "tool_settings.sculpt.brush.texture_angle_source_random" + + elif get_mode() == vertex_paint: + items = context.tool_settings.vertex_paint.brush.bl_rna.properties['texture_angle_source_random'].enum_items + path = "tool_settings.vertex_paint.brush.texture_angle_source_random" + + else: + items = context.tool_settings.image_paint.brush.bl_rna.properties['texture_angle_source_random'].enum_items + path = "tool_settings.image_paint.brush.texture_angle_source_random" + + # add the menu items + for item in items: + menuprop(menu.add_item(), item[0], item[1], path, + icon='RADIOBUT_OFF', + disable=True, + disable_icon='RADIOBUT_ON') |