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

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2017-03-20 01:50:42 +0300
committermeta-androcto <meta.androcto1@gmail.com>2017-03-20 01:50:42 +0300
commit9007bcd10713e55168235e9e8420b17172674638 (patch)
tree57ecdb75283765246970b188bd473c83ba6908e3 /space_view3d_brush_menus
parent7f6ae93c548a8ce99ae0a21b10e9a686105f08d3 (diff)
brush menus alt/v: T42564
Diffstat (limited to 'space_view3d_brush_menus')
-rw-r--r--space_view3d_brush_menus/Utils/__init__.py0
-rw-r--r--space_view3d_brush_menus/Utils/core.py204
-rw-r--r--space_view3d_brush_menus/__init__.py138
-rw-r--r--space_view3d_brush_menus/brush_menu.py522
-rw-r--r--space_view3d_brush_menus/brushes.py137
-rw-r--r--space_view3d_brush_menus/curve_menu.py57
-rw-r--r--space_view3d_brush_menus/dyntopo_menu.py125
-rw-r--r--space_view3d_brush_menus/stroke_menu.py117
-rw-r--r--space_view3d_brush_menus/symmetry_menu.py55
-rw-r--r--space_view3d_brush_menus/texture_menu.py306
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')