1 # gpl author: Ryan Inch (Imaginer)
4 from bpy
.types
import (
8 from bpy
.props
import BoolProperty
9 from . import utils_core
11 from bl_ui
.properties_paint_common
import UnifiedPaintPanel
13 class BrushOptionsMenu(Menu
):
14 bl_label
= "Brush Options"
15 bl_idname
= "VIEW3D_MT_sv3_brush_options"
18 def poll(self
, context
):
19 return utils_core
.get_mode() in (
20 'SCULPT', 'VERTEX_PAINT',
21 'WEIGHT_PAINT', 'TEXTURE_PAINT',
25 def draw(self
, context
):
26 mode
= utils_core
.get_mode()
29 # add generic menu items
30 layout
.operator_context
= 'INVOKE_REGION_WIN'
31 layout
.operator("wm.search_menu", text
="Search", icon
='VIEWZOOM')
32 layout
.operator("wm.toolbar", text
="Tools", icon
='TOOL_SETTINGS')
33 layout
.menu("SCREEN_MT_user_menu", text
="Quick Favorites", icon
='HEART')
34 layout
.operator_menu_enum("object.mode_set", "mode",
35 text
="Interactive Mode", icon
='VIEW3D')
39 # add mode specific menu items
41 self
.sculpt(mode
, layout
, context
)
43 elif mode
in ('VERTEX_PAINT', 'WEIGHT_PAINT'):
44 self
.vw_paint(mode
, layout
, context
)
46 elif mode
== 'TEXTURE_PAINT':
47 self
.texpaint(mode
, layout
, context
)
50 self
.particle(layout
, context
)
52 def sculpt(self
, mode
, layout
, context
):
53 has_brush
= utils_core
.get_brush_link(context
, types
="brush")
54 icons
= brushes
.brush_icon
[mode
][has_brush
.sculpt_tool
] if \
55 has_brush
else "BRUSH_DATA"
57 layout
.row().menu("VIEW3D_MT_sv3_brushes_menu",
60 layout
.row().menu(BrushRadiusMenu
.bl_idname
)
63 # if the active brush is unlinked these menus don't do anything
64 layout
.row().menu(BrushStrengthMenu
.bl_idname
)
65 layout
.row().menu(BrushAutosmoothMenu
.bl_idname
)
66 layout
.row().menu(BrushModeMenu
.bl_idname
)
67 layout
.row().menu("VIEW3D_MT_sv3_texture_menu")
68 layout
.row().menu("VIEW3D_MT_sv3_stroke_options")
69 layout
.row().menu("VIEW3D_MT_sv3_brush_curve_menu")
71 layout
.row().menu("VIEW3D_MT_sv3_dyntopo")
72 layout
.row().menu("VIEW3D_MT_sv3_master_symmetry_menu")
74 def vw_paint(self
, mode
, layout
, context
):
75 has_brush
= utils_core
.get_brush_link(context
, types
="brush")
76 icons
= brushes
.brush_icon
[mode
][has_brush
.vertex_tool
] if \
77 has_brush
else "BRUSH_DATA"
79 if mode
== 'VERTEX_PAINT':
80 layout
.row().operator(ColorPickerPopup
.bl_idname
, icon
="COLOR")
81 layout
.row().separator()
83 layout
.row().menu("VIEW3D_MT_sv3_brushes_menu",
86 if mode
== 'VERTEX_PAINT':
87 layout
.row().menu(BrushRadiusMenu
.bl_idname
)
90 # if the active brush is unlinked these menus don't do anything
91 layout
.row().menu(BrushStrengthMenu
.bl_idname
)
92 layout
.row().menu(BrushModeMenu
.bl_idname
)
93 layout
.row().menu("VIEW3D_MT_sv3_texture_menu")
94 layout
.row().menu("VIEW3D_MT_sv3_stroke_options")
95 layout
.row().menu("VIEW3D_MT_sv3_brush_curve_menu")
97 if mode
== 'WEIGHT_PAINT':
98 layout
.row().menu(BrushWeightMenu
.bl_idname
)
99 layout
.row().menu(BrushRadiusMenu
.bl_idname
)
102 # if the active brush is unlinked these menus don't do anything
103 layout
.row().menu(BrushStrengthMenu
.bl_idname
)
104 layout
.row().menu(BrushModeMenu
.bl_idname
)
105 layout
.row().menu("VIEW3D_MT_sv3_stroke_options")
106 layout
.row().menu("VIEW3D_MT_sv3_brush_curve_menu")
108 def texpaint(self
, mode
, layout
, context
):
109 toolsettings
= context
.tool_settings
.image_paint
111 has_brush
= utils_core
.get_brush_link(context
, types
="brush")
112 icons
= brushes
.brush_icon
[mode
][has_brush
.image_tool
] if \
113 has_brush
else "BRUSH_DATA"
115 if context
.image_paint_object
and not toolsettings
.detect_data():
116 if toolsettings
.missing_uvs
:
117 layout
.row().label(text
="Missing UVs", icon
='ERROR')
118 layout
.row().operator("paint.add_simple_uvs")
122 elif toolsettings
.missing_materials
or toolsettings
.missing_texture
:
123 layout
.row().label(text
="Missing Data", icon
='ERROR')
124 layout
.row().operator_menu_enum("paint.add_texture_paint_slot", \
127 text
="Add Texture Paint Slot")
131 elif toolsettings
.missing_stencil
:
132 layout
.row().label(text
="Missing Data", icon
='ERROR')
133 layout
.row().label(text
="See Mask Properties", icon
='FORWARD')
134 layout
.row().separator()
135 layout
.row().menu("VIEW3D_MT_sv3_brushes_menu",
141 layout
.row().label(text
="Missing Data", icon
="INFO")
144 if has_brush
and has_brush
.image_tool
in {'DRAW', 'FILL'} and \
145 has_brush
.blend
not in {'ERASE_ALPHA', 'ADD_ALPHA'}:
146 layout
.row().operator(ColorPickerPopup
.bl_idname
, icon
="COLOR")
147 layout
.row().separator()
149 layout
.row().menu("VIEW3D_MT_sv3_brushes_menu",
153 # if the active brush is unlinked these menus don't do anything
154 if has_brush
and has_brush
.image_tool
in {'MASK'}:
155 layout
.row().menu(BrushWeightMenu
.bl_idname
, text
="Mask Value")
157 if has_brush
and has_brush
.image_tool
not in {'FILL'}:
158 layout
.row().menu(BrushRadiusMenu
.bl_idname
)
160 layout
.row().menu(BrushStrengthMenu
.bl_idname
)
162 if has_brush
and has_brush
.image_tool
in {'DRAW'}:
163 layout
.row().menu(BrushModeMenu
.bl_idname
)
165 layout
.row().menu("VIEW3D_MT_sv3_texture_menu")
166 layout
.row().menu("VIEW3D_MT_sv3_stroke_options")
167 layout
.row().menu("VIEW3D_MT_sv3_brush_curve_menu")
169 layout
.row().menu("VIEW3D_MT_sv3_master_symmetry_menu")
171 def particle(self
, layout
, context
):
172 particle_edit
= context
.tool_settings
.particle_edit
174 layout
.row().menu("VIEW3D_MT_sv3_brushes_menu",
176 layout
.row().menu(BrushRadiusMenu
.bl_idname
)
178 if particle_edit
.tool
!= 'ADD':
179 layout
.row().menu(BrushStrengthMenu
.bl_idname
)
181 layout
.row().menu(ParticleCountMenu
.bl_idname
)
182 layout
.row().separator()
183 layout
.row().prop(particle_edit
, "use_default_interpolate", toggle
=True)
185 layout
.row().prop(particle_edit
.brush
, "steps", slider
=True)
186 layout
.row().prop(particle_edit
, "default_key_count", slider
=True)
188 if particle_edit
.tool
== 'LENGTH':
189 layout
.row().separator()
190 layout
.row().menu(ParticleLengthMenu
.bl_idname
)
192 if particle_edit
.tool
== 'PUFF':
193 layout
.row().separator()
194 layout
.row().menu(ParticlePuffMenu
.bl_idname
)
195 layout
.row().prop(particle_edit
.brush
, "use_puff_volume", toggle
=True)
198 class BrushRadiusMenu(Menu
):
200 bl_idname
= "VIEW3D_MT_sv3_brush_radius_menu"
201 bl_description
= "Change the size of the brushes"
204 if utils_core
.get_mode() == 'PARTICLE_EDIT':
205 settings
= (("100", 100),
212 datapath
= "tool_settings.particle_edit.brush.size"
213 proppath
= bpy
.context
.tool_settings
.particle_edit
.brush
216 settings
= (("200", 200),
223 datapath
= "tool_settings.unified_paint_settings.size"
224 proppath
= bpy
.context
.tool_settings
.unified_paint_settings
226 return settings
, datapath
, proppath
228 def draw(self
, context
):
229 settings
, datapath
, proppath
= self
.init()
233 layout
.row().prop(proppath
, "size", slider
=True)
234 layout
.row().separator()
236 # add the rest of the menu items
237 for i
in range(len(settings
)):
239 layout
.row(), settings
[i
][0], settings
[i
][1],
240 datapath
, icon
='RADIOBUT_OFF', disable
=True,
241 disable_icon
='RADIOBUT_ON'
245 class BrushStrengthMenu(Menu
):
246 bl_label
= "Strength"
247 bl_idname
= "VIEW3D_MT_sv3_brush_strength_menu"
250 mode
= utils_core
.get_mode()
251 settings
= (("1.0", 1.0),
258 proppath
= utils_core
.get_brush_link(bpy
.context
, types
="brush")
261 datapath
= "tool_settings.sculpt.brush.strength"
263 elif mode
== 'VERTEX_PAINT':
264 datapath
= "tool_settings.vertex_paint.brush.strength"
266 elif mode
== 'WEIGHT_PAINT':
267 datapath
= "tool_settings.weight_paint.brush.strength"
269 elif mode
== 'TEXTURE_PAINT':
270 datapath
= "tool_settings.image_paint.brush.strength"
273 datapath
= "tool_settings.particle_edit.brush.strength"
274 proppath
= bpy
.context
.tool_settings
.particle_edit
.brush
276 return settings
, datapath
, proppath
278 def draw(self
, context
):
279 settings
, datapath
, proppath
= self
.init()
284 layout
.row().prop(proppath
, "strength", slider
=True)
285 layout
.row().separator()
287 # add the rest of the menu items
288 for i
in range(len(settings
)):
290 layout
.row(), settings
[i
][0], settings
[i
][1],
291 datapath
, icon
='RADIOBUT_OFF', disable
=True,
292 disable_icon
='RADIOBUT_ON'
295 layout
.row().label(text
="No brushes available", icon
="INFO")
298 class BrushModeMenu(Menu
):
299 bl_label
= "Brush Mode"
300 bl_idname
= "VIEW3D_MT_sv3_brush_mode_menu"
303 mode
= utils_core
.get_mode()
304 has_brush
= utils_core
.get_brush_link(bpy
.context
, types
="brush")
307 enum
= has_brush
.bl_rna
.properties
['sculpt_plane'].enum_items
if \
309 path
= "tool_settings.sculpt.brush.sculpt_plane"
311 elif mode
== 'VERTEX_PAINT':
312 enum
= has_brush
.bl_rna
.properties
['blend'].enum_items
if \
314 path
= "tool_settings.vertex_paint.brush.blend"
316 elif mode
== 'WEIGHT_PAINT':
317 enum
= has_brush
.bl_rna
.properties
['blend'].enum_items
if \
319 path
= "tool_settings.weight_paint.brush.blend"
321 elif mode
== 'TEXTURE_PAINT':
322 enum
= has_brush
.bl_rna
.properties
['blend'].enum_items
if \
324 path
= "tool_settings.image_paint.brush.blend"
332 def draw(self
, context
):
333 enum
, path
= self
.init()
335 colum_n
= utils_core
.addon_settings()
337 layout
.row().label(text
="Brush Mode")
338 layout
.row().separator()
341 if utils_core
.get_mode() != 'SCULPT':
342 column_flow
= layout
.column_flow(columns
=colum_n
)
344 # add all the brush modes to the menu
347 column_flow
.row(), brush
.name
,
348 brush
.identifier
, path
, icon
='RADIOBUT_OFF',
349 disable
=True, disable_icon
='RADIOBUT_ON'
352 # add all the brush modes to the menu
355 layout
.row(), brush
.name
,
356 brush
.identifier
, path
, icon
='RADIOBUT_OFF',
357 disable
=True, disable_icon
='RADIOBUT_ON'
360 layout
.row().label(text
="No brushes available", icon
="INFO")
363 class BrushAutosmoothMenu(Menu
):
364 bl_label
= "Autosmooth"
365 bl_idname
= "VIEW3D_MT_sv3_brush_autosmooth_menu"
368 settings
= (("1.0", 1.0),
377 def draw(self
, context
):
378 settings
= self
.init()
380 has_brush
= utils_core
.get_brush_link(context
, types
="brush")
384 layout
.row().prop(has_brush
, "auto_smooth_factor", slider
=True)
385 layout
.row().separator()
387 # add the rest of the menu items
388 for i
in range(len(settings
)):
390 layout
.row(), settings
[i
][0], settings
[i
][1],
391 "tool_settings.sculpt.brush.auto_smooth_factor",
392 icon
='RADIOBUT_OFF', disable
=True,
393 disable_icon
='RADIOBUT_ON'
396 layout
.row().label(text
="No Smooth options available", icon
="INFO")
399 class BrushWeightMenu(Menu
):
401 bl_idname
= "VIEW3D_MT_sv3_brush_weight_menu"
404 settings
= (("1.0", 1.0),
411 if utils_core
.get_mode() == 'WEIGHT_PAINT':
412 brush
= bpy
.context
.tool_settings
.unified_paint_settings
413 brushstr
= "tool_settings.unified_paint_settings.weight"
417 brush
= bpy
.context
.tool_settings
.image_paint
.brush
418 brushstr
= "tool_settings.image_paint.brush.weight"
421 return settings
, brush
, brushstr
, name
423 def draw(self
, context
):
424 settings
, brush
, brushstr
, name
= self
.init()
429 layout
.row().prop(brush
, "weight", text
=name
, slider
=True)
430 layout
.row().separator()
432 # add the rest of the menu items
433 for i
in range(len(settings
)):
435 layout
.row(), settings
[i
][0], settings
[i
][1],
437 icon
='RADIOBUT_OFF', disable
=True,
438 disable_icon
='RADIOBUT_ON'
441 layout
.row().label(text
="No brush available", icon
="INFO")
444 class ParticleCountMenu(Menu
):
446 bl_idname
= "VIEW3D_MT_sv3_particle_count_menu"
449 settings
= (("50", 50),
458 def draw(self
, context
):
459 settings
= self
.init()
463 layout
.row().prop(context
.tool_settings
.particle_edit
.brush
,
464 "count", slider
=True)
465 layout
.row().separator()
467 # add the rest of the menu items
468 for i
in range(len(settings
)):
470 layout
.row(), settings
[i
][0], settings
[i
][1],
471 "tool_settings.particle_edit.brush.count",
472 icon
='RADIOBUT_OFF', disable
=True,
473 disable_icon
='RADIOBUT_ON'
477 class ParticleLengthMenu(Menu
):
478 bl_label
= "Length Mode"
479 bl_idname
= "VIEW3D_MT_sv3_particle_length_menu"
481 def draw(self
, context
):
483 path
= "tool_settings.particle_edit.brush.length_mode"
486 for item
in context
.tool_settings
.particle_edit
.brush
. \
487 bl_rna
.properties
['length_mode'].enum_items
:
489 layout
.row(), item
.name
, item
.identifier
, path
,
492 disable_icon
='RADIOBUT_ON'
496 class ParticlePuffMenu(Menu
):
497 bl_label
= "Puff Mode"
498 bl_idname
= "VIEW3D_MT_sv3_particle_puff_menu"
500 def draw(self
, context
):
502 path
= "tool_settings.particle_edit.brush.puff_mode"
505 for item
in context
.tool_settings
.particle_edit
.brush
. \
506 bl_rna
.properties
['puff_mode'].enum_items
:
508 layout
.row(), item
.name
, item
.identifier
, path
,
511 disable_icon
='RADIOBUT_ON'
515 class FlipColorsAll(Operator
):
516 """Switch between Foreground and Background colors"""
517 bl_label
= "Flip Colors"
518 bl_idname
= "view3d.sv3_flip_colors_all"
519 bl_description
= "Switch between Foreground and Background colors"
521 is_tex
: BoolProperty(
526 def execute(self
, context
):
528 if self
.is_tex
is False:
529 color
= context
.tool_settings
.vertex_paint
.brush
.color
530 secondary_color
= context
.tool_settings
.vertex_paint
.brush
.secondary_color
532 orig_prim
= color
.hsv
533 orig_sec
= secondary_color
.hsv
536 secondary_color
.hsv
= orig_prim
538 color
= context
.tool_settings
.image_paint
.brush
.color
539 secondary_color
= context
.tool_settings
.image_paint
.brush
.secondary_color
541 orig_prim
= color
.hsv
542 orig_sec
= secondary_color
.hsv
545 secondary_color
.hsv
= orig_prim
549 except Exception as e
:
550 utils_core
.error_handlers(self
, "view3d.sv3_flip_colors_all", e
,
551 "Flip Colors could not be completed")
556 class ColorPickerPopup(Operator
):
557 """Open Color Picker"""
559 bl_idname
= "view3d.sv3_color_picker_popup"
560 bl_description
= "Open Color Picker"
561 bl_options
= {'REGISTER'}
564 def poll(self
, context
):
565 return utils_core
.get_mode() in (
570 def check(self
, context
):
574 if utils_core
.get_mode() == 'TEXTURE_PAINT':
575 settings
= bpy
.context
.tool_settings
.image_paint
576 brush
= getattr(settings
, "brush", None)
578 settings
= bpy
.context
.tool_settings
.vertex_paint
579 brush
= settings
.brush
580 brush
= getattr(settings
, "brush", None)
582 return settings
, brush
584 def draw(self
, context
):
586 settings
, brush
= self
.init()
590 layout
.row().template_color_picker(brush
, "color", value_slider
=True)
591 prim_sec_row
= layout
.row(align
=True)
592 prim_sec_row
.prop(brush
, "color", text
="")
593 prim_sec_row
.prop(brush
, "secondary_color", text
="")
595 if utils_core
.get_mode() == 'VERTEX_PAINT':
596 prim_sec_row
.operator(
597 FlipColorsAll
.bl_idname
,
598 icon
='FILE_REFRESH', text
=""
601 prim_sec_row
.operator(
602 FlipColorsAll
.bl_idname
,
603 icon
='FILE_REFRESH', text
=""
607 layout
.column().template_palette(settings
, "palette", color
=True)
609 layout
.row().template_ID(settings
, "palette", new
="palette.new")
611 layout
.row().label(text
="No brushes currently available", icon
="INFO")
615 def execute(self
, context
):
616 return context
.window_manager
.invoke_popup(self
, width
=180)
635 bpy
.utils
.register_class(cls
)
639 bpy
.utils
.unregister_class(cls
)