From 66da2f537ae80ce2b31d1eaf34ad8c03d858938d Mon Sep 17 00:00:00 2001 From: Antonioya Date: Tue, 31 Jul 2018 10:22:19 +0200 Subject: New Grease Pencil object for 2D animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit merge the full development done in greasepencil-object branch and include mainly the following features. - New grease pencil object. - New drawing engine. - New grease pencil modes Draw/Sculpt/Edit and Weight Paint. - New brushes for grease pencil. - New modifiers for grease pencil. - New shaders FX. - New material system (replace old palettes and colors). - Split of annotations (old grease pencil) and new grease pencil object. - UI adapted to blender 2.8. You can get more info here: https://code.blender.org/2017/12/drawing-2d-animation-in-blender-2-8/ https://code.blender.org/2018/07/grease-pencil-status-update/ This is the result of nearly two years of development and I want thanks firstly the other members of the grease pencil team: Daniel M. Lara, Matias Mendiola and Joshua Leung for their support, ideas and to keep working in the project all the time, without them this project had been impossible. Also, I want thanks other Blender developers for their help, advices and to be there always to help me, and specially to Clément Foucault, Dalai Felinto, Pablo Vázquez and Campbell Barton. --- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- .../scripts/modules/bpy_extras/keyconfig_utils.py | 6 + release/scripts/startup/bl_operators/presets.py | 37 + release/scripts/startup/bl_ui/__init__.py | 3 + .../startup/bl_ui/properties_data_gpencil.py | 402 +++++++++++ .../startup/bl_ui/properties_data_modifier.py | 445 +++++++++++- .../startup/bl_ui/properties_data_shaderfx.py | 134 ++++ .../bl_ui/properties_grease_pencil_common.py | 785 +++++++-------------- .../scripts/startup/bl_ui/properties_material.py | 7 +- .../startup/bl_ui/properties_material_gpencil.py | 322 +++++++++ release/scripts/startup/bl_ui/properties_scene.py | 35 +- release/scripts/startup/bl_ui/space_clip.py | 45 +- release/scripts/startup/bl_ui/space_image.py | 55 +- release/scripts/startup/bl_ui/space_node.py | 56 +- release/scripts/startup/bl_ui/space_sequencer.py | 10 - .../startup/bl_ui/space_toolsystem_toolbar.py | 564 ++++++++++++++- release/scripts/startup/bl_ui/space_topbar.py | 11 + release/scripts/startup/bl_ui/space_userpref.py | 11 +- release/scripts/startup/bl_ui/space_view3d.py | 324 +++++++-- .../scripts/startup/bl_ui/space_view3d_toolbar.py | 415 ++++++++++- 21 files changed, 2863 insertions(+), 808 deletions(-) create mode 100644 release/scripts/startup/bl_ui/properties_data_gpencil.py create mode 100644 release/scripts/startup/bl_ui/properties_data_shaderfx.py create mode 100644 release/scripts/startup/bl_ui/properties_material_gpencil.py (limited to 'release/scripts') diff --git a/release/scripts/addons b/release/scripts/addons index c87ee4d46f1..371960484a3 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit c87ee4d46f16d60a2e1db7514c8d5ab42c5d93df +Subproject commit 371960484a38fc64e0a2635170a41a0d8ab2f6bd diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 15b25a42783..47470215783 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 15b25a42783d1e516b5298d70b582fae2559ae17 +Subproject commit 474702157831f1a58bb50f5240ab8b1b02b6ba37 diff --git a/release/scripts/modules/bpy_extras/keyconfig_utils.py b/release/scripts/modules/bpy_extras/keyconfig_utils.py index 6859e327b66..4e5cb7daad9 100644 --- a/release/scripts/modules/bpy_extras/keyconfig_utils.py +++ b/release/scripts/modules/bpy_extras/keyconfig_utils.py @@ -121,6 +121,12 @@ KM_HIERARCHY = [ ('Grease Pencil', 'EMPTY', 'WINDOW', [ # grease pencil stuff (per region) ('Grease Pencil Stroke Edit Mode', 'EMPTY', 'WINDOW', []), + ('Grease Pencil Stroke Paint (Draw brush)', 'EMPTY', 'WINDOW', []), + ('Grease Pencil Stroke Paint (Fill)', 'EMPTY', 'WINDOW', []), + ('Grease Pencil Stroke Paint (Erase)', 'EMPTY', 'WINDOW', []), + ('Grease Pencil Stroke Paint Mode', 'EMPTY', 'WINDOW', []), + ('Grease Pencil Stroke Sculpt Mode', 'EMPTY', 'WINDOW', []), + ('Grease Pencil Stroke Weight Mode', 'EMPTY', 'WINDOW', []), ]), ('Mask Editing', 'EMPTY', 'WINDOW', []), ('Frames', 'EMPTY', 'WINDOW', []), # frame navigation (per region) diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index fe09fada297..0fe45f8fee3 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -670,6 +670,42 @@ class AddPresetUnitsLength(AddPresetBase, Operator): preset_subdir = "units_length" +class AddPresetGpencilBrush(AddPresetBase, Operator): + """Add or remove grease pencil brush preset""" + bl_idname = "scene.gpencil_brush_preset_add" + bl_label = "Add Grease Pencil Brush Preset" + preset_menu = "VIEW3D_PT_gpencil_brush_presets" + + preset_defines = [ + "brush = bpy.context.active_gpencil_brush", + "settings = brush.gpencil_settings" + ] + + preset_values = [ + "settings.input_samples", + "settings.active_smooth_factor", + "settings.angle", + "settings.angle_factor", + "settings.use_stabilizer", + "brush.smooth_stroke_radius", + "brush.smooth_stroke_factor", + "settings.pen_smooth_factor", + "settings.pen_smooth_steps", + "settings.pen_thick_smooth_factor", + "settings.pen_thick_smooth_steps", + "settings.pen_subdivision_steps", + "settings.random_subdiv", + "settings.enable_random", + "settings.random_pressure", + "settings.random_strength", + "settings.uv_random", + "settings.pen_jitter", + "settings.use_jitter_pressure", + ] + + preset_subdir = "gpencil_brush" + + classes = ( AddPresetCamera, AddPresetCloth, @@ -686,6 +722,7 @@ classes = ( AddPresetTrackingSettings, AddPresetTrackingTrackColor, AddPresetUnitsLength, + AddPresetGpencilBrush, ExecutePreset, WM_MT_operator_presets, ) diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 51ba45cdcd7..89aed37f055 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -34,16 +34,19 @@ _modules = [ "properties_data_camera", "properties_data_curve", "properties_data_empty", + "properties_data_gpencil", "properties_data_light", "properties_data_lattice", "properties_data_mesh", "properties_data_metaball", "properties_data_modifier", + "properties_data_shaderfx", "properties_data_lightprobe", "properties_data_speaker", "properties_data_workspace", "properties_mask_common", "properties_material", + "properties_material_gpencil", "properties_object", "properties_paint_common", "properties_grease_pencil_common", diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py new file mode 100644 index 00000000000..14407afa8f2 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py @@ -0,0 +1,402 @@ +# ##### 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 ##### + +# +import bpy +from bpy.types import Menu, Panel, UIList +from rna_prop_ui import PropertyPanel +from .properties_grease_pencil_common import ( + GreasePencilDataPanel, + GreasePencilOnionPanel, + ) + +############################### +# Base-Classes (for shared stuff - e.g. poll, attributes, etc.) + +class DataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'GPENCIL' + + +class LayerDataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + return (context.object and + context.object.type == 'GPENCIL' and + context.active_gpencil_layer) + + +############################### +# GP Object Properties Panels and Helper Classes + +class DATA_PT_gpencil(DataButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + + def draw(self, context): + layout = self.layout + + # Grease Pencil data selector + gpd_owner = context.gpencil_data_owner + gpd = context.gpencil_data + + layout.template_ID(gpd_owner, "data") + + +class GPENCIL_UL_layer(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.GPencilLayer) + gpl = item + gpd = context.gpencil_data + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if gpl.lock: + layout.active = False + + row = layout.row(align=True) + if gpl.is_parented: + icon = 'BONE_DATA' + else: + icon = 'BLANK1' + + row.label(text="", icon=icon) + row.prop(gpl, "info", text="", emboss=False) + + row = layout.row(align=True) + row.prop(gpl, "lock", text="", emboss=False) + row.prop(gpl, "hide", text="", emboss=False) + row.prop(gpl, "unlock_color", text="", emboss=False) + if gpl.use_onion_skinning is False: + icon = 'GHOST_DISABLED' + else: + icon = 'GHOST_ENABLED' + subrow = row.row(align=True) + subrow.prop(gpl, "use_onion_skinning", text="", icon=icon, emboss=False) + subrow.active = gpd.use_onion_skinning + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class GPENCIL_MT_layer_specials(Menu): + bl_label = "Layer" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon + + layout.separator() + + layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + + layout.separator() + + layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All") + + layout.separator() + + layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down") + + +class DATA_PT_gpencil_datapanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Layers" + + @classmethod + def poll(cls, context): + if context.gpencil_data is None: + return False + + ob = context.object + if ob is not None and ob.type == 'GPENCIL': + return True + + return False + + @staticmethod + def draw(self, context): + layout = self.layout + #layout.use_property_split = True + layout.use_property_decorate = False + + gpd = context.gpencil_data + + # Grease Pencil data... + if (gpd is None) or (not gpd.layers): + layout.operator("gpencil.layer_add", text="New Layer") + else: + self.draw_layers(context, layout, gpd) + + def draw_layers(self, context, layout, gpd): + row = layout.row() + + col = row.column() + if len(gpd.layers) >= 2: + layer_rows = 5 + else: + layer_rows = 2 + col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows) + + col = row.column() + + sub = col.column(align=True) + sub.operator("gpencil.layer_add", icon='ZOOMIN', text="") + sub.operator("gpencil.layer_remove", icon='ZOOMOUT', text="") + + gpl = context.active_gpencil_layer + if gpl: + sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="") + + if len(gpd.layers) > 1: + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' + + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True + + row = layout.row(align=True) + if gpl: + row.prop(gpl, "opacity", text="Opacity", slider=True) + + +class DATA_PT_gpencil_layer_optionpanel(LayerDataButtonsPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Adjustments" + bl_parent_id = 'DATA_PT_gpencil_datapanel' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + gpl = context.active_gpencil_layer + layout.active = not gpl.lock + + # Layer options + # Offsets - Color Tint + layout.enabled = not gpl.lock + col = layout.column(align=True) + col.prop(gpl, "tint_color") + col.prop(gpl, "tint_factor", slider=True) + + # Offsets - Thickness + col = layout.row(align=True) + col.prop(gpl, "line_change", text="Stroke Thickness") + + +class DATA_PT_gpencil_parentpanel(LayerDataButtonsPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Relations" + bl_parent_id = 'DATA_PT_gpencil_datapanel' + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + gpl = context.active_gpencil_layer + col = layout.column(align=True) + col.active = not gpl.lock + col.prop(gpl, "parent", text="Parent") + col.prop(gpl, "parent_type", text="Parent Type") + parent = gpl.parent + + if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE': + col.prop_search(gpl, "parent_bone", parent.data, "bones", text="Bone") + + +class DATA_PT_gpencil_onionpanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Onion Skinning" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return bool(context.active_gpencil_layer) + + @staticmethod + def draw_header(self, context): + self.layout.prop(context.gpencil_data, "use_onion_skinning", text="") + + def draw(self, context): + gpd = context.gpencil_data + + layout = self.layout + layout.use_property_split = True + layout.enabled = gpd.use_onion_skinning + + GreasePencilOnionPanel.draw_settings(layout, gpd) + + +class GPENCIL_MT_gpencil_vertex_group(Menu): + bl_label = "GP Vertex Groups" + + def draw(self, context): + layout = self.layout + + layout.operator_context = 'EXEC_AREA' + layout.operator("object.vertex_group_add") + + ob = context.active_object + if ob.vertex_groups.active: + layout.separator() + + layout.operator("gpencil.vertex_group_assign", text="Assign to Active Group") + layout.operator("gpencil.vertex_group_remove_from", text="Remove from Active Group") + + layout.separator() + layout.operator_menu_enum("object.vertex_group_set_active", "group", text="Set Active Group") + layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False + layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True + + layout.separator() + layout.operator("gpencil.vertex_group_select", text="Select Points") + layout.operator("gpencil.vertex_group_deselect", text="Deselect Points") + + +class GPENCIL_UL_vgroups(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + vgroup = item + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon) + # icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED' + # layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class DATA_PT_gpencil_vertexpanel(DataButtonsPanel, Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + bl_label = "Vertex Groups" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + + ob = context.object + group = ob.vertex_groups.active + + rows = 2 + if group: + rows = 4 + + row = layout.row() + row.template_list("GPENCIL_UL_vgroups", "", ob, "vertex_groups", ob.vertex_groups, "active_index", rows=rows) + + col = row.column(align=True) + col.operator("object.vertex_group_add", icon='ZOOMIN', text="") + col.operator("object.vertex_group_remove", icon='ZOOMOUT', text="").all = False + + if ob.vertex_groups: + row = layout.row() + + sub = row.row(align=True) + sub.operator("gpencil.vertex_group_assign", text="Assign") + sub.operator("gpencil.vertex_group_remove_from", text="Remove") + + sub = row.row(align=True) + sub.operator("gpencil.vertex_group_select", text="Select") + sub.operator("gpencil.vertex_group_deselect", text="Deselect") + + layout.prop(context.tool_settings, "vertex_group_weight", text="Weight") + + +class DATA_PT_gpencil_display(DataButtonsPanel, Panel): + bl_label = "Viewport Display" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + + gpd = context.gpencil_data + gpl = context.active_gpencil_layer + + layout.prop(gpd, "xray_mode", text="Depth Ordering") + layout.prop(gpd, "edit_line_color", text="Edit Line Color") + layout.prop(ob, "empty_draw_size", text="Marker Size") + + col = layout.column(align=True) + col.prop(gpd, "show_constant_thickness") + sub = col.column() + sub.active = not gpd.show_constant_thickness + sub.prop(gpd, "pixfactor", text="Thickness Scale") + + if gpl: + layout.prop(gpd, "show_stroke_direction", text="Show Stroke Directions") + + +class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel): + _context_path = "object.data" + _property_type = bpy.types.GreasePencil + +############################### + +classes = ( + DATA_PT_gpencil, + DATA_PT_gpencil_datapanel, + DATA_PT_gpencil_onionpanel, + DATA_PT_gpencil_layer_optionpanel, + DATA_PT_gpencil_parentpanel, + DATA_PT_gpencil_vertexpanel, + DATA_PT_gpencil_display, + DATA_PT_custom_props_gpencil, + + GPENCIL_UL_layer, + GPENCIL_UL_vgroups, + + GPENCIL_MT_layer_specials, + GPENCIL_MT_gpencil_vertex_group, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 03ebea69d2b..2328925bbad 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -28,10 +28,14 @@ class ModifierButtonsPanel: bl_context = "modifier" bl_options = {'HIDE_HEADER'} - class DATA_PT_modifiers(ModifierButtonsPanel, Panel): bl_label = "Modifiers" + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type != 'GPENCIL' + def draw(self, context): layout = self.layout @@ -1563,8 +1567,447 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind") +class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): + bl_label = "Modifiers" + + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type == 'GPENCIL' + + def draw(self, context): + layout = self.layout + + ob = context.object + + layout.operator_menu_enum("object.gpencil_modifier_add", "type") + + for md in ob.grease_pencil_modifiers: + box = layout.template_greasepencil_modifier(md) + if box: + # match enum type to our functions, avoids a lookup table. + getattr(self, md.type)(box, ob, md) + + # the mt.type enum is (ab)used for a lookup on function names + # ...to avoid lengthy if statements + # so each type must have a function here. + + def GP_NOISE(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + row = col.row(align=True) + row.prop(md, "factor") + row.prop(md, "random", text="", icon="TIME", toggle=True) + row = col.row() + row.enabled = md.random + row.prop(md, "step") + col.prop(md, "full_stroke") + col.prop(md, "move_extreme") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row(align=True) + row.label("Affect:") + row = layout.row(align=True) + row.prop(md, "affect_position", text="Position", icon='MESH_DATA', toggle=True) + row.prop(md, "affect_strength", text="Strength", icon='COLOR', toggle=True) + row.prop(md, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) + row.prop(md, "affect_uv", text="UV", icon='MOD_UVPROJECT', toggle=True) + + def GP_SMOOTH(self, layout, ob, md): + gpd = ob.data + row = layout.row(align=False) + row.prop(md, "factor") + row.prop(md, "step") + + split = layout.split() + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col = split.column() + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row(align=True) + row.label("Affect:") + row = layout.row(align=True) + row.prop(md, "affect_position", text="Position", icon='MESH_DATA', toggle=True) + row.prop(md, "affect_strength", text="Strength", icon='COLOR', toggle=True) + row.prop(md, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) + row.prop(md, "affect_uv", text="UV", icon='MOD_UVPROJECT', toggle=True) + + def GP_SUBDIV(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + row = col.row(align=True) + row.prop(md, "level") + row.prop(md, "simple", text="", icon="PARTICLE_POINT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + def GP_SIMPLIFY(self, layout, ob, md): + gpd = ob.data + + row = layout.row() + row.prop(md, "mode") + + split = layout.split() + + col = split.column() + col.label("Settings:") + row = col.row(align=True) + row.enabled = md.mode == 'FIXED' + row.prop(md, "step") + + row = col.row(align=True) + row.enabled = not md.mode == 'FIXED' + row.prop(md, "factor") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + def GP_THICK(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + row = col.row(align=True) + row.prop(md, "thickness") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + col.prop(md, "normalize_thickness") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + if not md.normalize_thickness: + split = layout.split() + col = split.column() + col.prop(md, "use_custom_curve") + + if md.use_custom_curve: + col.template_curve_mapping(md, "curve") + + def GP_TINT(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.prop(md, "color") + col.prop(md, "factor") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row() + row.prop(md, "create_colors") + + def GP_COLOR(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label("Color:") + col.prop(md, "hue", text="H") + col.prop(md, "saturation", text="S") + col.prop(md, "value", text="V") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row() + row.prop(md, "create_colors") + + def GP_OPACITY(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label("Opacity:") + col.prop(md, "factor") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + def GP_INSTANCE(self, layout, ob, md): + gpd = ob.data + + col = layout.column() + col.prop(md, "count") + col.prop(md, "use_make_objects") + + split = layout.split() + col = split.column() + col.label("Offset:") + col.prop(md, "offset", text="") + + col = split.column() + col.label("Shift:") + col.prop(md, "shift", text="") + row = col.row(align=True) + row.prop(md, "lock_axis", expand=True) + + split = layout.split() + col = split.column() + col.label("Rotation:") + col.prop(md, "rotation", text="") + col.separator() + row = col.row(align=True) + row.prop(md, "random_rot", text="", icon="TIME", toggle=True) + row.prop(md, "rot_factor", text="") + + col = split.column() + col.label("Scale:") + col.prop(md, "scale", text="") + col.separator() + row = col.row(align=True) + row.prop(md, "random_scale", text="", icon="TIME", toggle=True) + row.prop(md, "scale_factor", text="") + + split = layout.split() + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + def GP_BUILD(self, layout, ob, md): + gpd = ob.data + + split = layout.split() + + col = split.column() + col.prop(md, "mode") + if md.mode == 'CONCURRENT': + col.prop(md, "concurrent_time_alignment") + else: + col.separator() # For spacing + col.separator() + col.separator() + + col.prop(md, "transition") + sub = col.column(align=True) + sub.prop(md, "start_delay") + sub.prop(md, "length") + + col = split.column(align=True) + col.prop(md, "use_restrict_frame_range") + sub = col.column(align=True) + sub.active = md.use_restrict_frame_range + sub.prop(md, "frame_start", text="Start") + sub.prop(md, "frame_end", text="End") + col.separator() + + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + def GP_LATTICE(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label(text="Object:") + col.prop(md, "object", text="") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + layout.separator() + layout.prop(md, "strength", slider=True) + + def GP_MIRROR(self, layout, ob, md): + gpd = ob.data + + row = layout.row(align=True) + row.prop(md, "x_axis") + row.prop(md, "y_axis") + row.prop(md, "z_axis") + + # GPXX: Not implemented yet + # layout.separator() + # layout.prop(md, "clip") + + layout.label("Layer:") + row = layout.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + row = layout.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + layout.label(text="Object:") + layout.prop(md, "object", text="") + + + def GP_HOOK(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.label(text="Object:") + col.prop(md, "object", text="") + if md.object and md.object.type == 'ARMATURE': + col.label(text="Bone:") + col.prop_search(md, "subtarget", md.object.data, "bones", text="") + + col = split.column() + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + use_falloff = (md.falloff_type != 'NONE') + split = layout.split() + + layout.separator() + + row = layout.row(align=True) + if use_falloff: + row.prop(md, "falloff_radius") + row.prop(md, "strength", slider=True) + layout.prop(md, "falloff_type") + + col = layout.column() + if use_falloff: + if md.falloff_type == 'CURVE': + col.template_curve_mapping(md, "falloff_curve") + + split = layout.split() + + col = split.column() + col.prop(md, "use_falloff_uniform") + + + def GP_OFFSET(self, layout, ob, md): + gpd = ob.data + split = layout.split() + + col = split.column() + col.prop(md, "location") + col.prop(md, "scale") + + col = split.column() + col.prop(md, "rotation") + + + col.label("Layer:") + row = col.row(align=True) + row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') + row.prop(md, "invert_layers", text="", icon="ARROW_LEFTRIGHT") + + col.label("Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon="ARROW_LEFTRIGHT") + + row = col.row(align=True) + row.prop(md, "pass_index", text="Pass") + row.prop(md, "invert_pass", text="", icon="ARROW_LEFTRIGHT") + + classes = ( DATA_PT_modifiers, + DATA_PT_gpencil_modifiers, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_data_shaderfx.py b/release/scripts/startup/bl_ui/properties_data_shaderfx.py new file mode 100644 index 00000000000..5010f56d234 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_shaderfx.py @@ -0,0 +1,134 @@ +# ##### 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 ##### + +# +import bpy +from bpy.types import Panel +from bpy.app.translations import pgettext_iface as iface_ + + +class ShaderFxButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "shaderfx" + bl_options = {'HIDE_HEADER'} + +class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): + bl_label = "Effects" + + @classmethod + def poll(cls, context): + return True + ob = context.object + return ob and ob.type == 'GPENCIL' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + + layout.operator_menu_enum("object.shaderfx_add", "type") + + for fx in ob.shader_effects: + box = layout.template_shaderfx(fx) + if box: + # match enum type to our functions, avoids a lookup table. + getattr(self, fx.type)(box, fx) + + # the mt.type enum is (ab)used for a lookup on function names + # ...to avoid lengthy if statements + # so each type must have a function here. + + def FX_BLUR(self, layout, fx): + + layout.prop(fx, "factor", text="Factor") + layout.prop(fx, "samples", text="Samples") + + layout.separator() + layout.prop(fx, "use_dof_mode") + if fx.use_dof_mode: + layout.prop(fx, "coc") + + def FX_COLORIZE(self, layout, fx): + layout.prop(fx, "mode", text="Mode") + + if fx.mode == 'BITONE': + layout.prop(fx, "low_color", text="Low Color") + if fx.mode == 'CUSTOM': + layout.prop(fx, "low_color", text="Color") + + if fx.mode == 'BITONE': + layout.prop(fx, "high_color", text="High Color") + + if fx.mode in {'BITONE', 'CUSTOM', 'TRANSPARENT'}: + layout.prop(fx, "factor") + + def FX_WAVE(self, layout,fx): + layout.prop(fx, "orientation", expand=True) + + layout.separator() + layout.prop(fx, "amplitude") + layout.prop(fx, "period") + layout.prop(fx, "phase") + + def FX_PIXEL(self, layout, fx): + layout.prop(fx, "size", text="Size") + + layout.prop(fx, "use_lines", text="Display Lines") + + col = layout.column() + col.enabled = fx.use_lines + col.prop(fx, "color") + + def FX_RIM(self, layout, fx): + layout.prop(fx, "offset", text="Offset") + + layout.prop(fx, "rim_color") + layout.prop(fx, "mask_color") + layout.prop(fx, "mode") + layout.prop(fx, "blur") + layout.prop(fx, "samples") + + def FX_SWIRL(self, layout, fx): + layout.prop(fx, "object", text="Object") + + layout.prop(fx, "radius") + layout.prop(fx, "angle") + + layout.prop(fx, "transparent") + + def FX_FLIP(self, layout, fx): + layout.prop(fx, "flip_horizontal") + layout.prop(fx, "flip_vertical") + + def FX_LIGHT(self, layout, fx): + layout.prop(fx, "object", text="Object") + + layout.prop(fx, "energy") + layout.prop(fx, "ambient") + + +classes = ( + DATA_PT_shader_fx, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 55b798d103a..252f87d369f 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -18,45 +18,30 @@ # - +import bpy from bpy.types import Menu, UIList from bpy.app.translations import pgettext_iface as iface_ def gpencil_stroke_placement_settings(context, layout): if context.space_data.type == 'VIEW_3D': - propname = "gpencil_stroke_placement_view3d" + propname = "annotation_stroke_placement_view3d" elif context.space_data.type == 'SEQUENCE_EDITOR': - propname = "gpencil_stroke_placement_sequencer_preview" + propname = "annotation_stroke_placement_sequencer_preview" elif context.space_data.type == 'IMAGE_EDITOR': - propname = "gpencil_stroke_placement_image_editor" + propname = "annotation_stroke_placement_image_editor" else: - propname = "gpencil_stroke_placement_view2d" + propname = "annotation_stroke_placement_view2d" ts = context.tool_settings col = layout.column(align=True) - col.label(text="Stroke Placement:") - - row = col.row(align=True) - row.prop_enum(ts, propname, 'VIEW') - row.prop_enum(ts, propname, 'CURSOR') - - if context.space_data.type == 'VIEW_3D': + if context.space_data.type != 'VIEW_3D': + col.label(text="Stroke Placement:") row = col.row(align=True) - row.prop_enum(ts, propname, 'SURFACE') - row.prop_enum(ts, propname, 'STROKE') - - row = col.row(align=False) - row.active = getattr(ts, propname) in {'SURFACE', 'STROKE'} - row.prop(ts, "use_gpencil_stroke_endpoints") - - if context.scene.tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': - row = col.row(align=True) - row.label("Lock axis:") - row = col.row(align=True) - row.prop(ts.gpencil_sculpt, "lockaxis", expand=True) + row.prop_enum(ts, propname, 'VIEW') + row.prop_enum(ts, propname, 'CURSOR', text="Cursor") def gpencil_active_brush_settings_simple(context, layout): @@ -73,7 +58,7 @@ def gpencil_active_brush_settings_simple(context, layout): row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA') row.prop(brush, "name", text="") - col.prop(brush, "line_width", slider=True) + col.prop(brush, "size", slider=True) row = col.row(align=True) row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE') row.prop(brush, "pen_sensitivity_factor", slider=True) @@ -90,6 +75,7 @@ def gpencil_active_brush_settings_simple(context, layout): row.prop(brush, "angle_factor", text="Factor", slider=True) +# XXX: To be replaced with active tools class GreasePencilDrawingToolsPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' @@ -99,8 +85,7 @@ class GreasePencilDrawingToolsPanel: @classmethod def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False + return True @staticmethod def draw(self, context): @@ -113,12 +98,12 @@ class GreasePencilDrawingToolsPanel: col.label(text="Draw:") row = col.row(align=True) - row.operator("gpencil.draw", icon='GREASEPENCIL', text="Draw").mode = 'DRAW' - row.operator("gpencil.draw", icon='FORCE_CURVE', text="Erase").mode = 'ERASER' # XXX: Needs a dedicated icon + row.operator("gpencil.annotate", icon='GREASEPENCIL', text="Draw").mode = 'DRAW' + row.operator("gpencil.annotate", icon='FORCE_CURVE', text="Erase").mode = 'ERASER' # XXX: Needs a dedicated icon row = col.row(align=True) - row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT' - row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY' + row.operator("gpencil.annotate", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT' + row.operator("gpencil.annotate", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY' col.separator() @@ -126,15 +111,15 @@ class GreasePencilDrawingToolsPanel: sub.operator("gpencil.blank_frame_add", icon='NEW') sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)") - sub = col.column(align=True) - sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing") - sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing") - sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back") + #sub = col.column(align=True) + #sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing") + #sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing") + #sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back") col.separator() col.separator() - if context.space_data.type in {'VIEW_3D', 'CLIP_EDITOR'}: + if context.space_data.type in {'CLIP_EDITOR'}: col.separator() col.label("Data Source:") row = col.row(align=True) @@ -143,8 +128,8 @@ class GreasePencilDrawingToolsPanel: elif is_clip_editor: row.prop(context.space_data, "grease_pencil_source", expand=True) - col.separator() - col.separator() + #col.separator() + #col.separator() gpencil_stroke_placement_settings(context, col) @@ -157,28 +142,16 @@ class GreasePencilDrawingToolsPanel: col = layout.column(align=True) col.prop(gpd, "use_stroke_edit_mode", text="Enable Editing", icon='EDIT', toggle=True) - if is_3d_view: - col.separator() - col.separator() - - col.label(text="Tools:") - col.operator_menu_enum("gpencil.convert", text="Convert to Geometry...", property="type") - col.operator("view3d.ruler") - class GreasePencilStrokeEditPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' bl_label = "Edit Strokes" - bl_category = "Grease Pencil" + bl_category = "Tools" bl_region_type = 'TOOLS' - bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - if context.gpencil_data is None: return False @@ -204,7 +177,7 @@ class GreasePencilStrokeEditPanel: col.operator("gpencil.select_linked") col.operator("gpencil.select_more") col.operator("gpencil.select_less") - col.operator("gpencil.palettecolor_select") + col.operator("gpencil.select_alternate") layout.label(text="Edit:") row = layout.row(align=True) @@ -228,258 +201,124 @@ class GreasePencilStrokeEditPanel: layout.separator() - col = layout.column(align=True) - col.operator("transform.bend", text="Bend") - col.operator("transform.mirror", text="Mirror") - col.operator("transform.shear", text="Shear") - col.operator("transform.tosphere", text="To Sphere") - layout.separator() col = layout.column(align=True) col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction") col.operator("gpencil.stroke_change_color", text="Move to Color") - if is_3d_view: - layout.separator() - layout.separator() col = layout.column(align=True) col.operator("gpencil.stroke_subdivide", text="Subdivide") - col.operator("gpencil.stroke_join", text="Join").type = 'JOIN' - col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' - col.operator("gpencil.stroke_flip", text="Flip Direction") - - gpd = context.gpencil_data - if gpd: - col.prop(gpd, "show_stroke_direction", text="Show Directions") - - if is_3d_view: - layout.separator() - layout.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") - - -class GreasePencilInterpolatePanel: - bl_space_type = 'VIEW_3D' - bl_label = "Interpolate" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.gpencil_data is None: - return False - elif context.space_data.type != 'VIEW_3D': - return False - - gpd = context.gpencil_data - return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) - - @staticmethod - def draw(self, context): - layout = self.layout - settings = context.tool_settings.gpencil_interpolate - - col = layout.column(align=True) - col.operator("gpencil.interpolate", text="Interpolate") - col.operator("gpencil.interpolate_sequence", text="Sequence") - col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns") - - col = layout.column(align=True) - col.label(text="Options:") - col.prop(settings, "interpolate_all_layers") - col.prop(settings, "interpolate_selected_only") - - col = layout.column(align=True) - col.label(text="Sequence Options:") - col.prop(settings, "type") - if settings.type == 'CUSTOM': - box = layout.box() - # TODO: Options for loading/saving curve presets? - box.template_curve_mapping(settings, "interpolation_curve", brush=True) - elif settings.type != 'LINEAR': - col.prop(settings, "easing") - - if settings.type == 'BACK': - layout.prop(settings, "back") - elif setting.type == 'ELASTIC': - sub = layout.column(align=True) - sub.prop(settings, "amplitude") - sub.prop(settings, "period") - - -class GreasePencilBrushPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Drawing Brushes" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False + row = col.row(align=True) + row.operator("gpencil.stroke_simplify_fixed", text="Simplify") + row.operator("gpencil.stroke_simplify", text="Adaptative") - @staticmethod - def draw(self, context): - layout = self.layout + col.separator() - row = layout.row() - col = row.column() - ts = context.scene.tool_settings - if len(ts.gpencil_brushes) >= 2: - brows = 3 - else: - brows = 2 - col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows) + row = col.row(align=True) + row.operator("gpencil.stroke_join", text="Join").type = 'JOIN' + row.operator("gpencil.stroke_join", text="& Copy").type = 'JOINCOPY' - col = row.column() + col.operator("gpencil.stroke_flip", text="Flip Direction") - sub = col.column(align=True) - sub.operator("gpencil.brush_add", icon='ZOOMIN', text="") - sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="") - sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="") - brush = context.active_gpencil_brush - if brush: - if len(ts.gpencil_brushes) > 1: - col.separator() - sub = col.column(align=True) - sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP' - sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN' + if is_3d_view: + layout.separator() - # Brush details - if brush is not None: - row = layout.row() - row.prop(brush, "line_width") - row = layout.row(align=True) - row.prop(brush, "use_random_pressure", text="", icon='RNDCURVE') - row.prop(brush, "pen_sensitivity_factor", slider=True) - row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE') - row = layout.row(align=True) - row.prop(brush, "use_random_strength", text="", icon='RNDCURVE') - row.prop(brush, "strength", slider=True) - row.prop(brush, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') - row = layout.row(align=True) - row.prop(brush, "random_press", slider=True) + col = layout.column(align=True) + col.operator_menu_enum("gpencil.stroke_separate", text="Separate...", property="mode") + col.operator("gpencil.stroke_split", text="Split") - row = layout.row(align=True) - row.prop(brush, "jitter", slider=True) - row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') - row = layout.row() - row.prop(brush, "angle", slider=True) - row.prop(brush, "angle_factor", text="Factor", slider=True) - - box = layout.box() - col = box.column(align=True) - col.label(text="Stroke Quality:") - col.prop(brush, "pen_smooth_factor") - col.prop(brush, "pen_smooth_steps") - col.separator() - row = col.row(align=False) - row.prop(brush, "pen_subdivision_steps") - row.prop(brush, "random_subdiv", text="Randomness", slider=True) + col = layout.column(align=True) + col.label(text="Cleanup:") + col.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") + col.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode") class GreasePencilStrokeSculptPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' bl_label = "Sculpt Strokes" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.gpencil_data is None: - return False - - gpd = context.gpencil_data - return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) + bl_category = "Tools" @staticmethod def draw(self, context): layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False settings = context.tool_settings.gpencil_sculpt tool = settings.tool brush = settings.brush - layout.column().prop(settings, "tool") + layout.template_icon_view(settings, "tool", show_labels=True) - col = layout.column() - col.prop(brush, "size", slider=True) - row = col.row(align=True) + layout.prop(brush, "size", slider=True) + row = layout.row(align=True) row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - col.prop(brush, "use_falloff") - if tool in {'SMOOTH', 'RANDOMIZE'}: - row = layout.row(align=True) - row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True) - row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True) - row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) - layout.separator() + layout.prop(brush, "use_falloff") - if tool == 'THICKNESS': - layout.row().prop(brush, "direction", expand=True) - elif tool == 'PINCH': - row = layout.row(align=True) - row.prop_enum(brush, "direction", 'ADD', text="Pinch") - row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate") - elif settings.tool == 'TWIST': - row = layout.row(align=True) - row.prop_enum(brush, "direction", 'SUBTRACT', text="CW") - row.prop_enum(brush, "direction", 'ADD', text="CCW") + if tool in {'SMOOTH', 'RANDOMIZE'}: + layout.prop(settings, "affect_position", text="Affect Position") + layout.prop(settings, "affect_strength", text="Affect Strength") + layout.prop(settings, "affect_thickness", text="Affect Thickness") - row = layout.row(align=True) - row.prop(settings, "use_select_mask") - row = layout.row(align=True) - row.prop(settings, "selection_alpha", slider=True) + if tool == 'SMOOTH': + layout.prop(brush, "affect_pressure") - if tool == 'SMOOTH': - layout.prop(brush, "affect_pressure") + layout.prop(settings, "affect_uv", text="Affect UV") + if tool in {'THICKNESS', 'PINCH', 'TWIST'}: + layout.prop(brush, "direction", expand=True) -class GreasePencilBrushCurvesPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Brush Curves" - bl_category = "Grease Pencil" - bl_region_type = 'TOOLS' + +# GP Object Tool Settings +class GreasePencilAppearancePanel: + bl_label = "Brush Appearance" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.active_gpencil_brush is None: - return False - - brush = context.active_gpencil_brush - return bool(brush) + ob = context.active_object + return ob and ob.type == 'GPENCIL' @staticmethod def draw(self, context): layout = self.layout - brush = context.active_gpencil_brush - # Brush - layout.label("Sensitivity") - box = layout.box() - box.template_curve_mapping(brush, "curve_sensitivity", brush=True) + layout.use_property_split = True + layout.use_property_decorate = False - layout.label("Strength") - box = layout.box() - box.template_curve_mapping(brush, "curve_strength", brush=True) + ob = context.active_object - layout.label("Jitter") - box = layout.box() - box.template_curve_mapping(brush, "curve_jitter", brush=True) + if ob.mode == 'GPENCIL_PAINT': + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + layout.prop(gp_settings, "gpencil_brush_type", text="Brush Type") + + sub = layout.column(align=True) + sub.enabled = not brush.use_custom_icon + sub.prop(gp_settings, "gp_icon", text="Icon") + + layout.prop(brush, "use_custom_icon") + sub = layout.column() + sub.active = brush.use_custom_icon + sub.prop(brush, "icon_filepath", text="") + + layout.prop(gp_settings, "use_cursor", text="Show Brush") + + if gp_settings.gpencil_brush_type == 'FILL': + layout.prop(brush, "cursor_color_add", text="Color") + + elif ob.mode in ('GPENCIL_SCULPT', 'GPENCIL_WEIGHT'): + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + col = layout.column(align=True) + col.prop(brush, "use_cursor", text="Show Brush") + col.row().prop(brush, "cursor_color_add", text="Add") + col.row().prop(brush, "cursor_color_sub", text="Subtract") ############################### @@ -539,6 +378,7 @@ class GPENCIL_MT_pie_tool_palette(Menu): col.operator("gpencil.select_border", text="Border Select", icon='BORDER_RECT') col.operator("gpencil.select_circle", text="Circle Select", icon='META_EMPTY') col.operator("gpencil.select_lasso", text="Lasso Select", icon='BORDER_LASSO') + col.operator("gpencil.select_alternate", text="Alternate Select", icon='BORDER_LASSO') # SW - Edit Tools col = pie.column() @@ -566,7 +406,7 @@ class GPENCIL_MT_pie_settings_palette(Menu): pie = layout.menu_pie() gpd = context.gpencil_data gpl = context.active_gpencil_layer - palcolor = context.active_gpencil_palettecolor + palcolor = None #context.active_gpencil_palettecolor brush = context.active_gpencil_brush is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes) @@ -737,6 +577,16 @@ class GPENCIL_MT_snap(Menu): layout.operator("view3d.snap_cursor_to_grid", text="Cursor to Grid") +class GPENCIL_MT_separate(Menu): + bl_label = "Separate" + + def draw(self, context): + layout = self.layout + layout.operator("gpencil.stroke_separate", text="Selected Points").mode = 'POINT' + layout.operator("gpencil.stroke_separate", text="Selected Strokes").mode = 'STROKE' + layout.operator("gpencil.stroke_separate", text="Active Layer").mode = 'LAYER' + + class GPENCIL_MT_gpencil_edit_specials(Menu): bl_label = "GPencil Specials" @@ -747,6 +597,14 @@ class GPENCIL_MT_gpencil_edit_specials(Menu): layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.operator("gpencil.stroke_simplify_fixed", text="Simplify") + layout.operator("gpencil.stroke_simplify", text="Simplify Adaptative") + + layout.separator() + layout.menu("GPENCIL_MT_separate", text="Separate") + + layout.separator() + layout.operator("gpencil.stroke_split", text="Split") layout.separator() @@ -754,167 +612,129 @@ class GPENCIL_MT_gpencil_edit_specials(Menu): layout.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' layout.operator("gpencil.stroke_flip", text="Flip Direction") + layout.separator() + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL' + if is_3d_view: layout.separator() layout.operator("gpencil.reproject") -############################### - - -class GPENCIL_UL_brush(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - # assert(isinstance(item, bpy.types.GPencilBrush) - brush = item - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) - row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA') - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - - -class GPENCIL_UL_palettecolor(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - # assert(isinstance(item, bpy.types.PaletteColor) - palcolor = item - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - if palcolor.lock: - layout.active = False - - split = layout.split(percentage=0.25) - row = split.row(align=True) - row.enabled = not palcolor.lock - row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible) - row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible) - split.prop(palcolor, "name", text="", emboss=False) - - row = layout.row(align=True) - row.prop(palcolor, "lock", text="", emboss=False) - row.prop(palcolor, "hide", text="", emboss=False) - if palcolor.ghost is True: - icon = 'GHOST_DISABLED' - else: - icon = 'GHOST_ENABLED' - row.prop(palcolor, "ghost", text="", icon=icon, emboss=False) - - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) - +class GPENCIL_MT_gpencil_sculpt_specials(Menu): + bl_label = "GPencil Specials" -class GPENCIL_UL_layer(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - # assert(isinstance(item, bpy.types.GPencilLayer) - gpl = item + def draw(self, context): + layout = self.layout + is_3d_view = context.space_data.type == 'VIEW_3D' - if self.layout_type in {'DEFAULT', 'COMPACT'}: - if gpl.lock: - layout.active = False + layout.operator_context = 'INVOKE_REGION_WIN' - row = layout.row(align=True) - if gpl.is_parented: - icon = 'BONE_DATA' - else: - icon = 'BLANK1' + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL' - row.label(text="", icon=icon) - row.prop(gpl, "info", text="", emboss=False) + layout.separator() - row = layout.row(align=True) - row.prop(gpl, "lock", text="", emboss=False) - row.prop(gpl, "hide", text="", emboss=False) - row.prop(gpl, "unlock_color", text="", emboss=False) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="", icon_value=icon) + layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.operator("gpencil.stroke_simplify_fixed", text="Simplify") + layout.operator("gpencil.stroke_simplify", text="Simplify Adaptative") -class GPENCIL_MT_layer_specials(Menu): - bl_label = "Layer" +class GPENCIL_MT_gpencil_draw_specials(Menu): + bl_label = "GPencil Draw Specials" def draw(self, context): layout = self.layout + is_3d_view = context.space_data.type == 'VIEW_3D' - layout.operator("gpencil.layer_duplicate", icon='COPY_ID') # XXX: needs a dedicated icon - - layout.separator() + layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("gpencil.reveal", icon='RESTRICT_VIEW_OFF', text="Show All") - layout.operator("gpencil.hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame All Layers").mode = 'ALL' layout.separator() + layout.operator("gpencil.primitive", text="Line", icon='IPO_CONSTANT').type = 'LINE' + layout.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX' + layout.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE' - layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All") - layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All") - + # colors layout.separator() + layout.operator("gpencil.colorpick", text="Colors", icon="GROUP_VCOL") - layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down") - -class GPENCIL_MT_brush_specials(Menu): - bl_label = "Layer" +class GPENCIL_MT_gpencil_draw_delete(Menu): + bl_label = "GPencil Draw Delete" def draw(self, context): layout = self.layout - layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy Current Drawing Brush") - layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a Set of Predefined Brushes") - + is_3d_view = context.space_data.type == 'VIEW_3D' -class GPENCIL_MT_palettecolor_specials(Menu): - bl_label = "Layer" + layout.operator_context = 'INVOKE_REGION_WIN' - def draw(self, context): - layout = self.layout + layout.operator("gpencil.active_frames_delete_all", text="Delete Frame") - layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") - layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True - layout.separator() +class GPENCIL_UL_annotation_layer(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.GPencilLayer) + gpl = item + gpd = context.gpencil_data - layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All") - layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All") - layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color") + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if gpl.lock: + layout.active = False - layout.separator() + split = layout.split(percentage=0.2) + split.prop(gpl, "color", text="", emboss=True) + split.prop(gpl, "info", text="", emboss=False) - layout.operator("gpencil.palettecolor_select", icon='COLOR', text="Select Strokes") - layout.operator("gpencil.stroke_change_color", icon='MAN_TRANS', text="Move to Color") + row = layout.row(align=True) + # row.prop(gpl, "lock", text="", emboss=False) + row.prop(gpl, "hide", text="", emboss=False) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) class GreasePencilDataPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Grease Pencil Layers" + bl_label = "Annotations" bl_region_type = 'UI' + @classmethod + def poll(cls, context): + # Show this panel as long as someone that might own this exists + # AND the owner isn't an object (e.g. GP Object) + if context.gpencil_data_owner is None: + return False + elif type(context.gpencil_data_owner) is bpy.types.Object: + return False + else: + return True + @staticmethod def draw_header(self, context): - self.layout.prop(context.space_data, "show_grease_pencil", text="") + if context.space_data.type != 'VIEW_3D': + self.layout.prop(context.space_data, "show_annotation", text="") @staticmethod def draw(self, context): layout = self.layout + #layout.use_property_split = True + layout.use_property_decorate = False # owner of Grease Pencil data gpd_owner = context.gpencil_data_owner gpd = context.gpencil_data # Owner Selector - if context.space_data.type == 'VIEW_3D': - layout.row().prop(context.tool_settings, "grease_pencil_source", expand=True) - elif context.space_data.type == 'CLIP_EDITOR': + if context.space_data.type == 'CLIP_EDITOR': layout.row().prop(context.space_data, "grease_pencil_source", expand=True) - # Grease Pencil data selector layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.data_add", unlink="gpencil.data_unlink") # Grease Pencil data... if (gpd is None) or (not gpd.layers): - layout.operator("gpencil.layer_add", text="New Layer") + layout.operator("gpencil.layer_add", text="New Note") else: self.draw_layers(context, layout, gpd) @@ -926,7 +746,7 @@ class GreasePencilDataPanel: layer_rows = 5 else: layer_rows = 2 - col.template_list("GPENCIL_UL_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows) + col.template_list("GPENCIL_UL_annotation_layer", "", gpd, "layers", gpd.layers, "active_index", rows=layer_rows) col = row.column() @@ -936,8 +756,6 @@ class GreasePencilDataPanel: gpl = context.active_gpencil_layer if gpl: - sub.menu("GPENCIL_MT_layer_specials", icon='DOWNARROW_HLT', text="") - if len(gpd.layers) > 1: col.separator() @@ -945,203 +763,70 @@ class GreasePencilDataPanel: sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' - col.separator() - - sub = col.column(align=True) - sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False - sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True - if gpl: - self.draw_layer(context, layout, gpl) - - def draw_layer(self, context, layout, gpl): - row = layout.row(align=True) - row.prop(gpl, "opacity", text="Opacity", slider=True) - - # Layer options - split = layout.split(percentage=0.5) - split.active = not gpl.lock - split.prop(gpl, "show_x_ray") - split.prop(gpl, "show_points") - - # Offsets + Parenting (where available) - if context.space_data.type == 'VIEW_3D': - split = layout.split(percentage=0.5) - else: - split = layout.column() # parenting is not available in 2D editors... - split.active = not gpl.lock - - # Offsets - Color Tint - col = split.column() - subcol = col.column(align=True) - subcol.label("Tint") - subcol.enabled = not gpl.lock - subcol.prop(gpl, "tint_color", text="") - subcol.prop(gpl, "tint_factor", text="Factor", slider=True) - - # Offsets - Thickness - row = col.row(align=True) - row.prop(gpl, "line_change", text="Thickness Change", slider=True) - row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="") + # layout.prop(gpl, "opacity", text="Opacity", slider=True) + # layout.prop(gpl, "thickness", text="Thickness") + # + # layout.separator() - # Parenting - if context.space_data.type == 'VIEW_3D': - col = split.column(align=True) - col.label(text="Parent:") - col.prop(gpl, "parent", text="") + # Full-Row - Frame Locking (and Delete Frame) + row = layout.row(align=True) + row.active = not gpl.lock - sub = col.column() - sub.prop(gpl, "parent_type", text="") - parent = gpl.parent - if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE': - sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="") + if gpl.active_frame: + lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked") + lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status) + else: + lock_label = iface_("Lock Frame") + row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED') + row.operator("gpencil.active_frame_delete", text="", icon='X') - layout.separator() - # Full-Row - Frame Locking (and Delete Frame) - row = layout.row(align=True) - row.active = not gpl.lock - if gpl.active_frame: - lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked") - lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status) - else: - lock_label = iface_("Lock Frame") - row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED') - row.operator("gpencil.active_frame_delete", text="", icon='X') - - layout.separator() +class GreasePencilOnionPanel: + @staticmethod + def draw_settings(layout, gp): + col = layout.column() - # Onion skinning - col = layout.column(align=True) - col.active = not gpl.lock + col.prop(gp, "onion_mode") row = col.row() - row.prop(gpl, "use_onion_skinning") - sub = row.row(align=True) - icon = 'RESTRICT_RENDER_OFF' if gpl.use_ghosts_always else 'RESTRICT_RENDER_ON' - sub.prop(gpl, "use_ghosts_always", text="", icon=icon) - sub.prop(gpl, "use_ghost_custom_colors", text="", icon='COLOR') - - split = col.split(percentage=0.5) - split.active = gpl.use_onion_skinning + row.prop(gp, "onion_factor", text="Opacity", slider=True) # - Before Frames - sub = split.column(align=True) + sub = layout.column(align=True) row = sub.row(align=True) - row.active = gpl.use_ghost_custom_colors - row.prop(gpl, "before_color", text="") - sub.prop(gpl, "ghost_before_range", text="Before") + row.active = gp.use_ghost_custom_colors + row.prop(gp, "before_color", text="Color Before") - # - After Frames - sub = split.column(align=True) row = sub.row(align=True) - row.active = gpl.use_ghost_custom_colors - row.prop(gpl, "after_color", text="") - sub.prop(gpl, "ghost_after_range", text="After") - - -class GreasePencilPaletteColorPanel: - # subclass must set - bl_label = "Grease Pencil Colors" - bl_region_type = 'UI' - - @classmethod - def poll(cls, context): - # XXX - disabled in 2.8 branch. - return False - - if context.gpencil_data is None: - return False - - gpd = context.gpencil_data - return bool(gpd.layers.active) - - @staticmethod - def draw(self, context): - layout = self.layout - palette = context.active_gpencil_palette + row.active = gp.onion_mode in ('ABSOLUTE', 'RELATIVE') + row.prop(gp, "ghost_before_range", text="Frames Before") - if palette: - row = layout.row(align=True) - row.operator_context = 'EXEC_REGION_WIN' - row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR') - row.prop(palette, "name", text="") - row.operator("gpencil.palette_add", icon='ZOOMIN', text="") - row.operator("gpencil.palette_remove", icon='X', text="") - - # Palette colors - row = layout.row() - col = row.column() - if len(palette.colors) >= 2: - color_rows = 5 - else: - color_rows = 2 - col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index", - rows=color_rows) - - col = row.column() + # - After Frames + sub = layout.column(align=True) + row = sub.row(align=True) + row.active = gp.use_ghost_custom_colors + row.prop(gp, "after_color", text="Color After") - sub = col.column(align=True) - sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="") - sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="") + row = sub.row(align=True) + row.active = gp.onion_mode in ('ABSOLUTE', 'RELATIVE') + row.prop(gp, "ghost_after_range", text="Frames After") - palcol = context.active_gpencil_palettecolor - if palcol: - sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="") + layout.prop(gp, "use_ghost_custom_colors", text="Use Custom Color") + layout.prop(gp, "use_ghosts_always", text="View In Render") - if len(palette.colors) > 1: - col.separator() + # - fade and loop + row = layout.row() + row.active = gp.use_onion_skinning + row.prop(gp, "use_onion_fade", text="Fade") + if hasattr(gp, "use_onion_loop"): # XXX + subrow = layout.row() + subrow.active = gp.onion_mode in ('RELATIVE', 'SELECTED') + subrow.prop(gp, "use_onion_loop", text="Loop") - sub = col.column(align=True) - sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP' - sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN' - - row = layout.row() - sub = row.row(align=True) - sub.label(text="Isolate:") # based on active color only - sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False - sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True - sub = row.row(align=True) - sub.label(text="Lock:") # based on other stuff... - sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="") - sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="") - - pcolor = palette.colors.active - if pcolor: - self.draw_palettecolors(layout, pcolor) - - # Draw palette colors - def draw_palettecolors(self, layout, pcolor): - # color settings - split = layout.split(percentage=0.5) - split.active = not pcolor.lock - - # Column 1 - Stroke - col = split.column(align=True) - col.enabled = not pcolor.lock - col.label(text="Stroke:") - col.prop(pcolor, "color", text="") - col.prop(pcolor, "alpha", slider=True) - - # Column 2 - Fill - col = split.column(align=True) - col.enabled = not pcolor.lock - col.label(text="Fill:") - col.prop(pcolor, "fill_color", text="") - col.prop(pcolor, "fill_alpha", text="Opacity", slider=True) - - # Options - split = layout.split(percentage=0.5) - split.active = not pcolor.lock - - col = split.column(align=True) - col.active = not pcolor.lock - col.prop(pcolor, "use_volumetric_strokes") - col = split.column(align=True) - col.active = not pcolor.lock - col.prop(pcolor, "use_hq_fill") +############################### class GreasePencilToolsPanel: # For use in "2D" Editors without their own toolbar @@ -1150,6 +835,7 @@ class GreasePencilToolsPanel: # bl_options = {'DEFAULT_CLOSED'} bl_label = "Grease Pencil Settings" bl_region_type = 'UI' + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -1183,20 +869,23 @@ class GreasePencilToolsPanel: gpencil_stroke_placement_settings(context, layout) +############################### classes = ( GPENCIL_MT_pie_tool_palette, GPENCIL_MT_pie_settings_palette, GPENCIL_MT_pie_tools_more, GPENCIL_MT_pie_sculpt, + GPENCIL_MT_snap, + GPENCIL_MT_separate, + GPENCIL_MT_gpencil_edit_specials, - GPENCIL_UL_brush, - GPENCIL_UL_palettecolor, - GPENCIL_UL_layer, - GPENCIL_MT_layer_specials, - GPENCIL_MT_brush_specials, - GPENCIL_MT_palettecolor_specials, + GPENCIL_MT_gpencil_sculpt_specials, + GPENCIL_MT_gpencil_draw_specials, + GPENCIL_MT_gpencil_draw_delete, + + GPENCIL_UL_annotation_layer, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 0e3e50b3497..5d12f762073 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -86,8 +86,11 @@ class EEVEE_MATERIAL_PT_context_material(MaterialButtonsPanel, Panel): @classmethod def poll(cls, context): - engine = context.engine - return (context.material or context.object) and (engine in cls.COMPAT_ENGINES) + if context.active_object and context.active_object.type == 'GPENCIL': + return False + else: + engine = context.engine + return (context.material or context.object) and (engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py new file mode 100644 index 00000000000..2d823594547 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py @@ -0,0 +1,322 @@ +# ##### 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 ##### + +# +import bpy +from bpy.types import Menu, Panel, UIList +from rna_prop_ui import PropertyPanel + + +class GPENCIL_MT_color_specials(Menu): + bl_label = "Layer" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.color_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.color_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + + layout.separator() + + layout.operator("gpencil.color_lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.color_unlock_all", icon='UNLOCKED', text="UnLock All") + + layout.separator() + + layout.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="Lock Unselected") + layout.operator("gpencil.lock_layer", icon='COLOR', text="Lock Unused") + + +class GPENCIL_UL_matslots(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + slot = item + ma = slot.material + if (ma is not None) and (ma.grease_pencil is not None): + gpcolor = ma.grease_pencil + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if gpcolor.lock: + layout.active = False + + row = layout.row(align=True) + row.enabled = not gpcolor.lock + row.prop(ma, "name", text="", emboss=False, icon_value=icon) + + row = layout.row(align=True) + row.prop(gpcolor, "lock", text="", emboss=False) + row.prop(gpcolor, "hide", text="", emboss=False) + if gpcolor.ghost is True: + icon = 'GHOST_DISABLED' + else: + icon = 'GHOST_ENABLED' + row.prop(gpcolor, "ghost", text="", icon=icon, emboss=False) + + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class GPMaterialButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + + @classmethod + def poll(cls, context): + ob = context.object + return (ob and ob.type == 'GPENCIL' and + ob.active_material and + ob.active_material.grease_pencil) + + + +class MATERIAL_PT_gpencil_slots(Panel): + bl_label = "Grease Pencil Material Slots" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + bl_options = {'HIDE_HEADER'} + + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type == 'GPENCIL' + + @staticmethod + def draw(self, context): + layout = self.layout + gpd = context.gpencil_data + + mat = context.object.active_material + ob = context.object + slot = context.material_slot + space = context.space_data + + if ob: + is_sortable = len(ob.material_slots) > 1 + rows = 1 + if (is_sortable): + rows = 4 + + row = layout.row() + + row.template_list("GPENCIL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows) + + col = row.column(align=True) + col.operator("object.material_slot_add", icon='ZOOMIN', text="") + col.operator("object.material_slot_remove", icon='ZOOMOUT', text="") + + col.menu("GPENCIL_MT_color_specials", icon='DOWNARROW_HLT', text="") + + if is_sortable: + col.separator() + + col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP' + col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True + + row = layout.row() + + if ob: + row.template_ID(ob, "active_material", new="material.new", live_icon=True) + + if slot: + icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA' + row.prop(slot, "link", icon=icon_link, icon_only=True) + + if gpd.use_stroke_edit_mode: + row = layout.row(align=True) + row.operator("gpencil.stroke_change_color", text="Assign") + row.operator("gpencil.color_select", text="Select") + + elif mat: + row.template_ID(space, "pin_id") + + +# Used as parent for "Stroke" and "Fill" panels +class MATERIAL_PT_gpencil_surface(GPMaterialButtonsPanel, Panel): + bl_label = "Surface" + + @classmethod + def poll(cls, context): + ob = context.object + ma = context.object.active_material + if ma is None or ma.grease_pencil is None: + return False + + return ob and ob.type == 'GPENCIL' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + +class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel): + bl_label = "Stroke" + bl_parent_id = 'MATERIAL_PT_gpencil_surface' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ma = context.object.active_material + if ma is not None and ma.grease_pencil is not None: + gpcolor = ma.grease_pencil + + col = layout.column() + col.active = not gpcolor.lock + + col.prop(gpcolor, "mode") + + col.prop(gpcolor, "stroke_style", text="Style") + + if gpcolor.stroke_style == 'TEXTURE': + row = col.row() + row.enabled = not gpcolor.lock + col = row.column(align=True) + col.template_ID(gpcolor, "stroke_image", open="image.open") + col.prop(gpcolor, "pixel_size", text="UV Factor") + col.prop(gpcolor, "use_stroke_pattern", text="Use As Pattern") + + if gpcolor.stroke_style == 'SOLID' or gpcolor.use_stroke_pattern is True: + col.prop(gpcolor, "color", text="Color") + + +class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel): + bl_label = "Fill" + bl_parent_id = 'MATERIAL_PT_gpencil_surface' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ma = context.object.active_material + if ma is not None and ma.grease_pencil: + gpcolor = ma.grease_pencil + + # color settings + col = layout.column() + col.active = not gpcolor.lock + col.prop(gpcolor, "fill_style", text="Style") + + if gpcolor.fill_style == 'GRADIENT': + col.prop(gpcolor, "gradient_type") + + if gpcolor.fill_style != 'TEXTURE': + col.prop(gpcolor, "fill_color", text="Color") + + if gpcolor.fill_style in ('GRADIENT', 'CHESSBOARD'): + col.prop(gpcolor, "mix_color", text="Secondary Color") + + if gpcolor.fill_style == 'GRADIENT': + col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True) + + if gpcolor.fill_style in ('GRADIENT', 'CHESSBOARD'): + col.prop(gpcolor, "flip", text="Flip Colors") + + col.prop(gpcolor, "pattern_shift", text="Location") + col.prop(gpcolor, "pattern_scale", text="Scale") + + if gpcolor.gradient_type == 'RADIAL' and gpcolor.fill_style not in ('SOLID', 'CHESSBOARD'): + col.prop(gpcolor, "pattern_radius", text="Radius") + else: + if gpcolor.fill_style != 'SOLID': + col.prop(gpcolor, "pattern_angle", text="Angle") + + if gpcolor.fill_style == 'CHESSBOARD': + col.prop(gpcolor, "pattern_gridsize", text="Box Size") + + # Texture + if gpcolor.fill_style == 'TEXTURE' or (gpcolor.texture_mix is True and gpcolor.fill_style == 'SOLID'): + col.template_ID(gpcolor, "fill_image", open="image.open") + + if gpcolor.fill_style == 'TEXTURE': + col.prop(gpcolor, "use_fill_pattern", text="Use As Pattern") + if gpcolor.use_fill_pattern is True: + col.prop(gpcolor, "fill_color", text="Color") + + col.prop(gpcolor, "texture_offset", text="Offset") + col.prop(gpcolor, "texture_scale", text="Scale") + col.prop(gpcolor, "texture_angle") + col.prop(gpcolor, "texture_opacity") + col.prop(gpcolor, "texture_clamp", text="Clip Image") + + if gpcolor.use_fill_pattern is False: + col.prop(gpcolor, "texture_mix", text="Mix With Color") + + if gpcolor.texture_mix is True: + col.prop(gpcolor, "fill_color", text="Mix Color") + col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True) + + +class MATERIAL_PT_gpencil_preview(GPMaterialButtonsPanel, Panel): + bl_label = "Preview" + COMPAT_ENGINES = {'BLENDER_EEVEE'} + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + ma = context.object.active_material + self.layout.label(ma.name) + self.layout.template_preview(ma) + + +class MATERIAL_PT_gpencil_custom_props(GPMaterialButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_OPENGL'} + _context_path = "object.active_material" + _property_type = bpy.types.Material + + +class MATERIAL_PT_gpencil_options(GPMaterialButtonsPanel, Panel): + bl_label = "Options" + bl_options = {'DEFAULT_CLOSED'} + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ma = context.object.active_material + if ma is not None and ma.grease_pencil is not None: + gpcolor = ma.grease_pencil + layout.prop(gpcolor, "pass_index") + + +classes = ( + GPENCIL_UL_matslots, + GPENCIL_MT_color_specials, + MATERIAL_PT_gpencil_slots, + MATERIAL_PT_gpencil_preview, + MATERIAL_PT_gpencil_surface, + MATERIAL_PT_gpencil_strokecolor, + MATERIAL_PT_gpencil_fillcolor, + MATERIAL_PT_gpencil_options, + MATERIAL_PT_gpencil_custom_props, +) + +if __name__ == "__main__": # only for live edit. + from bpy.utils import register_class + for cls in classes: + register_class(cls) diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index 38bfc6ad294..91be9bb5d0a 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -28,9 +28,9 @@ from rna_prop_ui import PropertyPanel from bl_operators.presets import PresetMenu from .properties_physics_common import ( - point_cache_ui, - effector_weights_ui, -) + point_cache_ui, + effector_weights_ui, + ) class SCENE_PT_units_length_presets(PresetMenu): @@ -104,7 +104,6 @@ class SCENE_PT_unit(SceneButtonsPanel, Panel): col.prop(unit, "scale_length") col.prop(unit, "use_separate") - class SceneKeyingSetsPanel: @staticmethod @@ -568,6 +567,33 @@ class SCENE_PT_simplify_render(SceneButtonsPanel, Panel): col.prop(rd, "simplify_child_particles_render", text="Max Child Particles") +class SCENE_PT_simplify_greasepencil(SceneButtonsPanel, Panel): + bl_label = "Simplify Grease Pencil" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'} + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + rd = context.scene.render + self.layout.prop(rd, "simplify_gpencil", text="") + + def draw(self, context): + layout = self.layout + + rd = context.scene.render + + layout.active = rd.simplify_gpencil + + row = layout.row() + row.prop(rd, "simplify_gpencil_onplay", text="Only on Play") + + split = layout.split() + + col = split.column() + col.prop(rd, "simplify_gpencil_view_fill", text="Fill") + col.prop(rd, "simplify_gpencil_remove_lines", text="Remove Fill Lines") + col.prop(rd, "simplify_gpencil_view_modifier", text="Modifiers") + + class SCENE_PT_custom_props(SceneButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} _context_path = "scene" @@ -593,6 +619,7 @@ classes = ( SCENE_PT_simplify, SCENE_PT_simplify_viewport, SCENE_PT_simplify_render, + SCENE_PT_simplify_greasepencil, SCENE_PT_custom_props, ) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 23c3e97ac9a..8e32d98529b 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -23,14 +23,8 @@ from bpy.types import Panel, Header, Menu, UIList from bpy.app.translations import pgettext_iface as iface_ from bl_operators.presets import PresetMenu from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel, - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, -) + GreasePencilDrawingToolsPanel, + GreasePencilDataPanel) class CLIP_UL_tracking_objects(UIList): @@ -1154,40 +1148,12 @@ class CLIP_PT_grease_pencil(GreasePencilDataPanel, CLIP_PT_clip_view_panel, Pane # But, this should only be visible in "clip" view -# Grease Pencil palette colors -class CLIP_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, CLIP_PT_clip_view_panel, Panel): - bl_space_type = 'CLIP_EDITOR' - bl_region_type = 'UI' - bl_options = {'DEFAULT_CLOSED'} - - # NOTE: this is just a wrapper around the generic GP Panel - # But, this should only be visible in "clip" view - - # Grease Pencil drawing tools class CLIP_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_space_type = 'CLIP_EDITOR' + bl_region_type = 'TOOLS' -# Grease Pencil stroke editing tools -class CLIP_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - - -# Grease Pencil stroke sculpting tools -class CLIP_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - - -# Grease Pencil drawing brushes -class CLIP_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - - -# Grease Pencil drawing curves -class CLIP_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'CLIP_EDITOR' - class CLIP_MT_view(Menu): bl_label = "View" @@ -1515,12 +1481,7 @@ classes = ( CLIP_PT_footage_info, CLIP_PT_tools_scenesetup, CLIP_PT_grease_pencil, - CLIP_PT_grease_pencil_palettecolor, CLIP_PT_tools_grease_pencil_draw, - CLIP_PT_tools_grease_pencil_edit, - CLIP_PT_tools_grease_pencil_sculpt, - CLIP_PT_tools_grease_pencil_brush, - CLIP_PT_tools_grease_pencil_brushcurves, CLIP_MT_view, CLIP_MT_clip, CLIP_MT_proxy, diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 1303e46ab6c..501f58e9901 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -21,20 +21,15 @@ import bpy import math from bpy.types import Header, Menu, Panel, UIList from .properties_paint_common import ( - UnifiedPaintPanel, - brush_texture_settings, - brush_texpaint_common, - brush_mask_texture_settings, -) + UnifiedPaintPanel, + brush_texture_settings, + brush_texpaint_common, + brush_mask_texture_settings, + ) from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel, - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, -) + GreasePencilDrawingToolsPanel, + GreasePencilDataPanel + ) from bpy.app.translations import pgettext_iface as iface_ @@ -1346,39 +1341,12 @@ class IMAGE_PT_grease_pencil(GreasePencilDataPanel, Panel): # NOTE: this is just a wrapper around the generic GP Panel - -# Grease Pencil palette colors -class IMAGE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - # Grease Pencil drawing tools class IMAGE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'TOOLS' -# Grease Pencil stroke editing tools -class IMAGE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - - -# Grease Pencil stroke sculpting tools -class IMAGE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - - -# Grease Pencil drawing brushes -class IMAGE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - - -# Grease Pencil drawing curves -class IMAGE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'IMAGE_EDITOR' - classes = ( IMAGE_MT_view, @@ -1430,12 +1398,7 @@ classes = ( IMAGE_PT_sample_line, IMAGE_PT_scope_sample, IMAGE_PT_grease_pencil, - IMAGE_PT_grease_pencil_palettecolor, IMAGE_PT_tools_grease_pencil_draw, - IMAGE_PT_tools_grease_pencil_edit, - IMAGE_PT_tools_grease_pencil_sculpt, - IMAGE_PT_tools_grease_pencil_brush, - IMAGE_PT_tools_grease_pencil_brushcurves, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 45343c09b27..affbf70b1a0 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -23,15 +23,10 @@ from bpy.types import Header, Menu, Panel from bpy.app.translations import pgettext_iface as iface_ from bl_operators.presets import PresetMenu from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel, - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, - GreasePencilToolsPanel -) + GreasePencilDrawingToolsPanel, + GreasePencilDataPanel, + GreasePencilToolsPanel + ) class NODE_HT_header(Header): @@ -539,19 +534,6 @@ class NODE_PT_grease_pencil(GreasePencilDataPanel, Panel): return snode is not None and snode.node_tree is not None -# Grease Pencil palette colors -class NODE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - @classmethod - def poll(cls, context): - snode = context.space_data - return snode is not None and snode.node_tree is not None - - class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' @@ -571,31 +553,6 @@ class NODE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_region_type = 'TOOLS' -# Grease Pencil stroke editing tools -class NODE_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - - -# Grease Pencil stroke sculpting tools -class NODE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - -# Grease Pencil drawing brushes - - -class NODE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - -# Grease Pencil drawing curves - - -class NODE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' - # ----------------------------- @@ -620,13 +577,8 @@ classes = ( NODE_PT_quality, NODE_UL_interface_sockets, NODE_PT_grease_pencil, - NODE_PT_grease_pencil_palettecolor, NODE_PT_grease_pencil_tools, NODE_PT_tools_grease_pencil_draw, - NODE_PT_tools_grease_pencil_edit, - NODE_PT_tools_grease_pencil_sculpt, - NODE_PT_tools_grease_pencil_brush, - NODE_PT_tools_grease_pencil_brushcurves, ) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index bac462dcbab..84ae59772b6 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -22,7 +22,6 @@ from bpy.types import Header, Menu, Panel from rna_prop_ui import PropertyPanel from .properties_grease_pencil_common import ( GreasePencilDataPanel, - GreasePencilPaletteColorPanel, GreasePencilToolsPanel, ) from bpy.app.translations import pgettext_iface as iface_ @@ -1281,14 +1280,6 @@ class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Ou # But, it should only show up when there are images in the preview region -class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel): - bl_space_type = 'SEQUENCE_EDITOR' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - # But, it should only show up when there are images in the preview region - - class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel): bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' @@ -1333,7 +1324,6 @@ classes = ( SEQUENCER_PT_view_safe_areas, SEQUENCER_PT_modifiers, SEQUENCER_PT_grease_pencil, - SEQUENCER_PT_grease_pencil_palettecolor, SEQUENCER_PT_grease_pencil_tools, SEQUENCER_PT_custom_props, ) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 3101ffc8336..b682588629b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -24,6 +24,7 @@ # For now keep this in a single file since it's an area that may change, # so avoid making changes all over the place. +import bpy from bpy.types import Panel from .space_toolsystem_common import ( @@ -39,21 +40,77 @@ def generate_from_brushes_ex( brush_category_attr, brush_category_layout, ): + def draw_settings(context, layout, tool): + _defs_gpencil_paint.draw_settings_common(context, layout, tool) + # Categories brush_categories = {} - for brush in context.blend_data.brushes: - if getattr(brush, brush_test_attr): - category = getattr(brush, brush_category_attr) - name = brush.name - brush_categories.setdefault(category, []).append( - ToolDef.from_dict( - dict( - text=name, - icon=icon_prefix + category.lower(), - data_block=name, + if context.mode != 'GPENCIL_PAINT': + for brush in context.blend_data.brushes: + if getattr(brush, brush_test_attr) and brush.gpencil_settings is None: + category = getattr(brush, brush_category_attr) + name = brush.name + brush_categories.setdefault(category, []).append( + ToolDef.from_dict( + dict( + text=name, + icon=icon_prefix + category.lower(), + data_block=name, + ) ) ) - ) + else: + for brush_type in brush_category_layout: + for brush in context.blend_data.brushes: + if getattr(brush, brush_test_attr) and brush.gpencil_settings.gp_icon == brush_type[0]: + category = brush_type[0] + name = brush.name + + # rename default brushes for tool bar + if name.startswith("Draw "): + text = name.replace("Draw ", "") + elif name.startswith("Eraser "): + text = name.replace("Eraser ", "") + elif name.startswith("Fill "): + text = name.replace(" Area", "") + else: + text = name + + # define icon + gp_icon = brush.gpencil_settings.gp_icon + if gp_icon == 'PENCIL': + icon_name = 'draw_pencil' + elif gp_icon == 'PEN': + icon_name = 'draw_pen' + elif gp_icon == 'INK': + icon_name = 'draw_ink' + elif gp_icon == 'INKNOISE': + icon_name = 'draw_noise' + elif gp_icon == 'BLOCK': + icon_name = 'draw_block' + elif gp_icon == 'MARKER': + icon_name = 'draw_marker' + elif gp_icon == 'FILL': + icon_name = 'draw_fill' + elif gp_icon == 'SOFT': + icon_name = 'draw.eraser_soft' + elif gp_icon == 'HARD': + icon_name = 'draw.eraser_hard' + elif gp_icon == 'STROKE': + icon_name = 'draw.eraser_stroke' + + brush_categories.setdefault(category, []).append( + ToolDef.from_dict( + dict( + text=text, + icon=icon_prefix + icon_name, + data_block=name, + widget=None, + operator="gpencil.draw", + draw_settings=draw_settings, + ) + ) + ) def tools_from_brush_group(groups): assert(type(groups) is tuple) @@ -61,6 +118,7 @@ def generate_from_brushes_ex( tool_defs = tuple(brush_categories.pop(groups[0], ())) else: tool_defs = tuple(item for g in groups for item in brush_categories.pop(g, ())) + if len(tool_defs) > 1: return (tool_defs,) else: @@ -125,6 +183,112 @@ class _defs_view3d_generic: ) +class _defs_annotate: + @classmethod + def draw_settings_common(cls, context, layout, tool): + user_prefs = context.user_preferences + ts = context.tool_settings + + # XXX: These context checks are needed for layer-dependent settings, + # but this breaks for using topbar for 2D editor active tools, etc. + if type(context.gpencil_data_owner) is bpy.types.Object: + gpd = context.scene.grease_pencil + else: + gpd = context.gpencil_data + + gpl = gpd.layers.active if gpd else None + + if gpd and gpl: + layout.prop(gpd.layers, "active_note", text="") + layout.prop(gpl, "thickness", text="Thickness") + else: + layout.prop(user_prefs.edit, "grease_pencil_default_color", text="Color") + layout.prop(ts, "annotation_thickness", text="Thickness") + + # For 3D view, show the stroke placement settings + # XXX: How to tell what editor the active tool comes from? + is_3d_view = True + if is_3d_view: + layout.separator() + + row = layout.row(align=True) + row.prop(ts, "annotation_stroke_placement_view3d", text="Orientation") + if ts.gpencil_stroke_placement_view3d == 'CURSOR': + row.prop(ts.gpencil_sculpt, "lockaxis") + elif ts.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: + row.prop(ts, "use_gpencil_stroke_endpoints") + + @ToolDef.from_fn + def scribble(): + def draw_settings(context, layout, tool): + _defs_annotate.draw_settings_common(context, layout, tool) + + return dict( + text="Annotate", + icon="ops.gpencil.draw", + cursor='PAINT_BRUSH', + keymap=( + ("gpencil.annotate", + dict(mode='DRAW', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def line(): + def draw_settings(context, layout, tool): + _defs_annotate.draw_settings_common(context, layout, tool) + + return dict( + text="Draw Line", + icon="ops.gpencil.draw.line", + cursor='CROSSHAIR', + keymap=( + ("gpencil.annotate", + dict(mode='DRAW_STRAIGHT', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def poly(): + def draw_settings(context, layout, tool): + _defs_annotate.draw_settings_common(context, layout, tool) + + return dict( + text="Draw Polygon", + icon="ops.gpencil.draw.poly", + cursor='CROSSHAIR', + keymap=( + ("gpencil.annotate", + dict(mode='DRAW_POLY', wait_for_input=False), + dict(type='ACTIONMOUSE', value='PRESS')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def eraser(): + def draw_settings(context, layout, tool): + # TODO: Move this setting to toolsettings + user_prefs = context.user_preferences + layout.prop(user_prefs.edit, "grease_pencil_eraser_radius", text="Radius") + + return dict( + text="Eraser", + icon="ops.gpencil.draw.eraser", + cursor='CROSSHAIR', # XXX: Always show brush circle when enabled + keymap=( + ("gpencil.annotate", + dict(mode='ERASER', wait_for_input=False), + dict(type='ACTIONMOUSE', value='PRESS')), + ), + draw_settings=draw_settings, + ) + + class _defs_transform: @ToolDef.from_fn @@ -865,6 +1029,331 @@ class _defs_uv_select: ), ) +class _defs_gpencil_paint: + @classmethod + def draw_color_selector(cls, context, layout): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + ts = context.tool_settings + row = layout.row(align=True) + row.prop(ts, "use_gpencil_thumbnail_list", text="", icon="IMGDISPLAY") + if ts.use_gpencil_thumbnail_list is False: + row.template_ID(gp_settings, "material", live_icon=True) + else: + row.template_greasepencil_color(gp_settings, "material", rows=3, cols=8, scale=0.8) + + @classmethod + def draw_settings_common(cls, context, layout, tool): + ob = context.active_object + if ob and ob.mode == 'GPENCIL_PAINT': + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + tool_settings= context.tool_settings + + if gp_settings.gpencil_brush_type == 'ERASE': + row = layout.row() + row.prop(brush, "size", text="Radius") + elif gp_settings.gpencil_brush_type == 'FILL': + row = layout.row() + row.prop(gp_settings, "gpencil_fill_leak", text="Leak Size") + row.prop(brush, "size", text="Thickness") + row.prop(gp_settings, "gpencil_fill_simplyfy_level", text="Simplify") + + _defs_gpencil_paint.draw_color_selector(context, layout) + + row = layout.row(align=True) + row.prop(gp_settings, "gpencil_fill_draw_mode", text="") + row.prop(gp_settings, "gpencil_fill_show_boundary", text="", icon='GRID') + + else: # bgpsettings.gpencil_brush_type == 'DRAW': + row = layout.row(align=True) + row.prop(brush, "size", text="Radius") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row = layout.row(align=True) + row.prop(gp_settings, "pen_strength", slider=True) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + + _defs_gpencil_paint.draw_color_selector(context, layout) + + + @staticmethod + def generate_from_brushes(context): + return generate_from_brushes_ex( + context, + icon_prefix="brush.gpencil.", + brush_test_attr="use_paint_grease_pencil", + brush_category_attr="grease_pencil_tool", + brush_category_layout=( + ('PENCIL',), + ('PEN',), + ('INK',), + ('INKNOISE',), + ('BLOCK',), + ('MARKER',), + ('FILL',), + ('SOFT',), + ('HARD',), + ('STROKE',), + ) + ) + + +class _defs_gpencil_edit: + @ToolDef.from_fn + def bend(): + return dict( + text="Bend", + icon="ops.gpencil.edit_bend", + widget=None, + keymap=( + ("transform.bend", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + @ToolDef.from_fn + def mirror(): + return dict( + text="Mirror", + icon="ops.gpencil.edit_mirror", + widget=None, + keymap=( + ("transform.mirror", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + @ToolDef.from_fn + def shear(): + return dict( + text="Shear", + icon="ops.gpencil.edit_shear", + widget=None, + keymap=( + ("transform.shear", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + @ToolDef.from_fn + def tosphere(): + return dict( + text="To Sphere", + icon="ops.gpencil.edit_to_sphere", + widget=None, + keymap=( + ("transform.tosphere", + dict(), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + ) + + +class _defs_gpencil_sculpt: + @classmethod + def draw_settings_common(cls, context, layout, tool): + ob = context.active_object + if ob and ob.mode == 'GPENCIL_SCULPT': + ts = context.tool_settings + settings = ts.gpencil_sculpt + brush = settings.brush + + layout.prop(brush, "size", slider=True) + + row = layout.row(align=True) + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_pressure_strength", text="") + row.separator() + row.prop(ts.gpencil_sculpt, "use_select_mask", text="") + + @ToolDef.from_fn + def smooth(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Smooth", + icon="ops.gpencil.sculpt_smooth", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='SMOOTH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def thickness(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Thickness", + icon="ops.gpencil.sculpt_thickness", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='THICKNESS', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def strength(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Strength", + icon="ops.gpencil.sculpt_strength", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='STRENGTH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def grab(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Grab", + icon="ops.gpencil.sculpt_grab", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='GRAB', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def push(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Push", + icon="ops.gpencil.sculpt_push", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='PUSH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def twist(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Twist", + icon="ops.gpencil.sculpt_twist", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='TWIST', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def pinch(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Pinch", + icon="ops.gpencil.sculpt_pinch", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='PINCH', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def randomize(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Randomize", + icon="ops.gpencil.sculpt_randomize", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='RANDOMIZE', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def clone(): + def draw_settings(context, layout, tool): + _defs_gpencil_sculpt.draw_settings_common(context, layout, tool) + + return dict( + text="Clone", + icon="ops.gpencil.sculpt_clone", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='CLONE', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + + +class _defs_gpencil_weight: + @classmethod + def draw_settings_common(cls, context, layout, tool): + ob = context.active_object + if ob and ob.mode == 'GPENCIL_WEIGHT': + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + layout.prop(brush, "size", slider=True) + + row = layout.row(align=True) + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_pressure_strength", text="") + + @ToolDef.from_fn + def paint(): + def draw_settings(context, layout, tool): + _defs_gpencil_weight.draw_settings_common(context, layout, tool) + + return dict( + text="Draw", + icon="ops.gpencil.sculpt_weight", + widget=None, + keymap=( + ("gpencil.brush_paint", + dict(mode='WEIGHT', wait_for_input=False), + dict(type='EVT_TWEAK_A', value='ANY')), + ), + draw_settings=draw_settings, + ) + class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): bl_space_type = 'IMAGE_EDITOR' @@ -951,8 +1440,6 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_transform.scale, _defs_transform.scale_cage, ), - None, - _defs_view3d_generic.ruler, ) _tools_select = ( @@ -963,6 +1450,16 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), ) + _tools_annotate = ( + ( + _defs_annotate.scribble, + _defs_annotate.line, + _defs_annotate.poly, + _defs_annotate.eraser, + ), + _defs_view3d_generic.ruler, + ) + _tools = { None: [ _defs_view3d_generic.cursor, @@ -972,21 +1469,27 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): *_tools_select, None, *_tools_transform, + None, + *_tools_annotate, ], 'POSE': [ *_tools_select, *_tools_transform, None, + *_tools_annotate, + None, ( _defs_pose.breakdown, _defs_pose.push, _defs_pose.relax, - ) + ), ], 'EDIT_ARMATURE': [ *_tools_select, None, *_tools_transform, + None, + *_tools_annotate, _defs_edit_armature.roll, ( _defs_edit_armature.bone_size, @@ -996,13 +1499,15 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ( _defs_edit_armature.extrude, _defs_edit_armature.extrude_cursor, - ) + ), ], 'EDIT_MESH': [ *_tools_select, None, *_tools_transform, None, + *_tools_annotate, + None, _defs_edit_mesh.cube_add, None, ( @@ -1047,6 +1552,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_transform, None, + *_tools_annotate, + None, _defs_edit_curve.draw, _defs_edit_curve.extrude_cursor, ], @@ -1075,6 +1582,33 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, _defs_weight_paint.gradient, ], + 'GPENCIL_PAINT': [ + _defs_gpencil_paint.generate_from_brushes, + ], + 'GPENCIL_EDIT': [ + *_tools_select, + None, + *_tools_transform, + None, + _defs_gpencil_edit.bend, + _defs_gpencil_edit.mirror, + _defs_gpencil_edit.shear, + _defs_gpencil_edit.tosphere, + ], + 'GPENCIL_SCULPT': [ + _defs_gpencil_sculpt.smooth, + _defs_gpencil_sculpt.thickness, + _defs_gpencil_sculpt.strength, + _defs_gpencil_sculpt.grab, + _defs_gpencil_sculpt.push, + _defs_gpencil_sculpt.twist, + _defs_gpencil_sculpt.pinch, + _defs_gpencil_sculpt.randomize, + _defs_gpencil_sculpt.clone, + ], + 'GPENCIL_WEIGHT': [ + _defs_gpencil_weight.paint, + ], } diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 55129aa0ce1..0d837ae413a 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -128,6 +128,8 @@ class TOPBAR_HT_lower_bar(Header): pass elif mode == 'PARTICLE': layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".paint_common", category="") + elif mode == 'GPENCIL_PAINT': + layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".greasepencil_paint", category="") def draw_center(self, context): pass @@ -165,6 +167,15 @@ class TOPBAR_HT_lower_bar(Header): layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".particlemode", category="") elif mode == 'OBJECT': layout.popover_group(space_type='PROPERTIES', region_type='WINDOW', context=".objectmode", category="") + elif mode == 'GPENCIL_PAINT': + layout.prop(context.tool_settings, "gpencil_stroke_placement_view3d", text='') + if context.tool_settings.gpencil_stroke_placement_view3d in ('ORIGIN', 'CURSOR'): + layout.prop(context.tool_settings.gpencil_sculpt, "lockaxis", text='') + layout.prop(context.tool_settings, "use_gpencil_draw_onback", text="", icon='ORTHO') + layout.prop(context.tool_settings, "use_gpencil_additive_drawing", text="", icon='FREEZE') + + elif mode == 'GPENCIL_SCULPT': + layout.prop(context.tool_settings.gpencil_sculpt, "lockaxis", text='') class _draw_left_context_mode: diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 53aaff7c4bf..52d4640806d 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -361,14 +361,16 @@ class USERPREF_PT_edit(Panel): row.separator() col = row.column() - col.label(text="Grease Pencil:") + col.label(text="Annotations:") + sub = col.row() + sub.prop(edit, "grease_pencil_default_color", text="Default Color") col.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") col.separator() + col.label(text="Grease Pencil/Annotations:") + col.separator() col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance") col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") col.separator() - col.prop(edit, "grease_pencil_default_color", text="Default Color") - col.separator() col.prop(edit, "use_grease_pencil_simplify_stroke", text="Simplify Stroke") col.separator() col.separator() @@ -527,7 +529,10 @@ class USERPREF_PT_system(Panel): col.prop(system, "gpu_viewport_quality") col.separator() + col.label(text="Grease Pencil Options:") + col.prop(system, "gpencil_multi_sample", text="") + col.separator() col.label(text="Text Draw Options:") col.prop(system, "use_text_antialiasing") if system.use_text_antialiasing: diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index aed5faff73c..eb595e9f12d 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -19,11 +19,8 @@ # import bpy from bpy.types import Header, Menu, Panel -from .properties_grease_pencil_common import ( - GreasePencilDataPanel, - GreasePencilPaletteColorPanel, -) from .properties_paint_common import UnifiedPaintPanel +from .properties_grease_pencil_common import GreasePencilDataPanel from bpy.app.translations import contexts as i18n_contexts @@ -80,18 +77,40 @@ class VIEW3D_HT_header(Header): row.operator("pose.paste", text="", icon='PASTEDOWN').flipped = False row.operator("pose.paste", text="", icon='PASTEFLIPDOWN').flipped = True - # GPencil - if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode: - row = layout.row(align=True) - row.operator("gpencil.copy", text="", icon='COPYDOWN') - row.operator("gpencil.paste", text="", icon='PASTEDOWN') + # Grease Pencil + if obj and obj.type == 'GPENCIL' and context.gpencil_data: + gpd = context.gpencil_data - # XXX: icon - layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH') + if gpd.is_stroke_paint_mode: + row = layout.row(align=True) + row.popover( + panel="VIEW3D_PT_tools_grease_pencil_shapes", + text="Shapes" + ) - row = layout.row(align=True) - row.prop(tool_settings.gpencil_sculpt, "use_select_mask") - row.prop(tool_settings.gpencil_sculpt, "selection_alpha", slider=True) + if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode: + row = layout.row(align=True) + row.prop(gpd, "use_multiedit", text="", icon="FORCE_HARMONIC") + + sub = row.row(align=True) + sub.active = gpd.use_multiedit + sub.popover( + panel="VIEW3D_PT_gpencil_multi_frame", + text="Multiframe" + ) + + if gpd.use_stroke_edit_mode: + row = layout.row(align=True) + row.operator("gpencil.copy", text="", icon='COPYDOWN') + row.operator("gpencil.paste", text="", icon='PASTEDOWN') + + row = layout.row(align=True) + row.prop(tool_settings.gpencil_sculpt, "use_select_mask", text="") + + row.popover( + panel="VIEW3D_PT_tools_grease_pencil_interpolate", + text="Interpolate" + ) VIEW3D_MT_editor_menus.draw_collapsible(context, layout) @@ -101,7 +120,7 @@ class VIEW3D_HT_header(Header): scene = context.scene # Orientation - if object_mode in {'OBJECT', 'EDIT', 'POSE'}: + if object_mode in {'OBJECT', 'EDIT', 'POSE', 'GPENCIL_EDIT'}: orientation = scene.transform_orientation current_orientation = scene.current_orientation @@ -126,7 +145,8 @@ class VIEW3D_HT_header(Header): if obj is None: show_snap = True else: - if object_mode not in {'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT'}: + if object_mode not in {'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', + 'GPENCIL_PAINT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'}: show_snap = True else: @@ -160,13 +180,15 @@ class VIEW3D_HT_header(Header): # Proportional editing if obj: - if context.gpencil_data and context.gpencil_data.use_stroke_edit_mode: - row = layout.row(align=True) - row.prop(tool_settings, "proportional_edit", icon_only=True) + gpd = context.gpencil_data + if gpd is not None: + if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode: + row = layout.row(align=True) + row.prop(tool_settings, "proportional_edit", icon_only=True) - sub = row.row(align=True) - sub.active = tool_settings.proportional_edit != 'DISABLED' - sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True) + sub = row.row(align=True) + sub.active = tool_settings.proportional_edit != 'DISABLED' + sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True) elif object_mode in {'EDIT', 'PARTICLE_EDIT'}: row = layout.row(align=True) @@ -190,7 +212,7 @@ class VIEW3D_HT_header(Header): sub.prop(tool_settings, "proportional_edit_falloff", icon_only=True) # Pivot - if object_mode in {'OBJECT', 'EDIT', 'POSE'}: + if object_mode in {'OBJECT', 'EDIT', 'POSE', 'GPENCIL_EDIT', 'GPENCIL_SCULPT'}: pivot_point = tool_settings.transform_pivot_point act_pivot_point = bpy.types.ToolSettings.bl_rna.properties["transform_pivot_point"].enum_items[pivot_point] row = layout.row(align=True) @@ -234,13 +256,14 @@ class VIEW3D_MT_editor_menus(Menu): obj = context.active_object mode_string = context.mode edit_object = context.edit_object - gp_edit = context.gpencil_data and context.gpencil_data.use_stroke_edit_mode + gp_edit = obj and obj.mode in {'GPENCIL_EDIT', 'GPENCIL_PAINT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'} layout.menu("VIEW3D_MT_view") # Select Menu if gp_edit: - layout.menu("VIEW3D_MT_select_gpencil") + if mode_string not in {'GPENCIL_PAINT', 'GPENCIL_WEIGHT'}: + layout.menu("VIEW3D_MT_select_gpencil") elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}: mesh = obj.data if mesh.use_paint_mask: @@ -266,7 +289,15 @@ class VIEW3D_MT_editor_menus(Menu): layout.menu("INFO_MT_edit_armature_add", text="Add") if gp_edit: - layout.menu("VIEW3D_MT_edit_gpencil") + if obj and obj.mode == 'GPENCIL_PAINT': + layout.menu("VIEW3D_MT_paint_gpencil") + elif obj and obj.mode == 'GPENCIL_EDIT': + layout.menu("VIEW3D_MT_edit_gpencil") + elif obj and obj.mode == 'GPENCIL_SCULPT': + layout.menu("VIEW3D_MT_sculpt_gpencil") + elif obj and obj.mode == 'GPENCIL_WEIGHT': + layout.menu("VIEW3D_MT_weight_gpencil") + elif edit_object: layout.menu("VIEW3D_MT_edit_%s" % edit_object.type.lower()) @@ -1194,6 +1225,7 @@ class VIEW3D_MT_select_gpencil(Menu): layout.separator() layout.operator("gpencil.select_linked", text="Linked") + layout.operator("gpencil.select_alternate") layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped") layout.separator() @@ -1454,6 +1486,7 @@ class INFO_MT_add(Menu): layout.menu("INFO_MT_armature_add", icon='OUTLINER_OB_ARMATURE') layout.operator("object.add", text="Lattice", icon='OUTLINER_OB_LATTICE').type = 'LATTICE' layout.operator_menu_enum("object.empty_add", "type", text="Empty", icon='OUTLINER_OB_EMPTY') + layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL') layout.separator() layout.operator("object.speaker_add", text="Speaker", icon='OUTLINER_OB_SPEAKER') @@ -3110,12 +3143,17 @@ class VIEW3D_MT_edit_gpencil_delete(Menu): layout.separator() - layout.operator("gpencil.dissolve") + layout.operator_enum("gpencil.dissolve", "type") layout.separator() layout.operator("gpencil.active_frames_delete_all") + layout.separator() + + layout.operator("gpencil.frame_clean_fill", text="Clean Boundary Strokes").mode = 'ACTIVE' + layout.operator("gpencil.frame_clean_fill", text="Clean Boundary Strokes all Frames").mode = 'ALL' + # Edit Curve # draw_curve is used by VIEW3D_MT_edit_curve and VIEW3D_MT_edit_surface @@ -3476,11 +3514,34 @@ class VIEW3D_MT_edit_armature_delete(Menu): layout.operator("armature.dissolve", text="Dissolve") -# ********** GPencil Stroke Edit menu ********** +# ********** Grease Pencil Stroke menus ********** +class VIEW3D_MT_gpencil_simplify(Menu): + bl_label = "Simplify" + + def draw(self, context): + layout = self.layout + layout.operator("gpencil.stroke_simplify_fixed", text="Fixed") + layout.operator("gpencil.stroke_simplify", text="Adaptative") + + +class VIEW3D_MT_paint_gpencil(Menu): + bl_label = "Strokes" + + def draw(self, context): + + layout = self.layout + + layout.menu("VIEW3D_MT_gpencil_animation") + layout.menu("VIEW3D_MT_edit_gpencil_interpolate") + + layout.separator() + + layout.operator("gpencil.delete", text="Delete Frame").type = 'FRAME' + layout.operator("gpencil.active_frames_delete_all") class VIEW3D_MT_edit_gpencil(Menu): - bl_label = "GPencil" + bl_label = "Strokes" def draw(self, context): tool_settings = context.tool_settings @@ -3488,53 +3549,126 @@ class VIEW3D_MT_edit_gpencil(Menu): layout = self.layout layout.menu("VIEW3D_MT_edit_gpencil_transform") - layout.operator("transform.mirror", text="Mirror") + + layout.separator() layout.menu("GPENCIL_MT_snap") layout.separator() - layout.operator("gpencil.brush_paint", text="Sculpt Strokes").wait_for_input = True - layout.prop_menu_enum(tool_settings.gpencil_sculpt, "tool", text="Sculpt Brush") + layout.menu("VIEW3D_MT_gpencil_animation") layout.separator() - layout.menu("VIEW3D_MT_object_animation") # NOTE: provides keyingset access... layout.menu("VIEW3D_MT_edit_gpencil_interpolate") layout.separator() layout.operator("gpencil.duplicate_move", text="Duplicate") layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.menu("VIEW3D_MT_gpencil_simplify") layout.separator() + layout.operator_menu_enum("gpencil.stroke_separate", "mode", text="Separate...") + layout.operator("gpencil.stroke_split", text="Split") layout.operator_menu_enum("gpencil.stroke_join", "type", text="Join...") layout.operator("gpencil.stroke_flip", text="Flip Direction") layout.separator() layout.operator("gpencil.copy", text="Copy") - layout.operator("gpencil.paste", text="Paste") + layout.operator("gpencil.paste", text="Paste").type = 'COPY' + layout.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE' + + layout.separator() + + layout.operator_menu_enum("gpencil.move_to_layer", "layer", text="Move to Layer") + layout.operator("gpencil.stroke_change_color", text="Change Color") + layout.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange Strokes...") + + layout.separator() + + layout.operator_menu_enum("gpencil.convert", "type", text="Convert to Geometry...") + + layout.separator() + + layout.menu("VIEW3D_MT_edit_gpencil_delete") + layout.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' + + layout.separator() + + layout.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode") + + +class VIEW3D_MT_sculpt_gpencil(Menu): + bl_label = "Strokes" + + def draw(self, context): + layout = self.layout + + layout.menu("VIEW3D_MT_edit_gpencil_transform") layout.separator() + layout.menu("GPENCIL_MT_snap") - layout.operator("gpencil.reveal") - layout.operator("gpencil.hide", text="Show Active Layer Only").unselected = True - layout.operator("gpencil.hide", text="Hide Active Layer").unselected = False + layout.separator() + + layout.operator("gpencil.duplicate_move", text="Duplicate") + layout.operator("gpencil.stroke_subdivide", text="Subdivide") + layout.menu("VIEW3D_MT_gpencil_simplify") + + layout.separator() + + layout.operator_menu_enum("gpencil.stroke_separate", "mode", text="Separate...") + layout.operator("gpencil.stroke_split", text="Split") + layout.operator_menu_enum("gpencil.stroke_join", "type", text="Join...") + layout.operator("gpencil.stroke_flip", text="Flip Direction") + + layout.separator() + + layout.operator("gpencil.copy", text="Copy") + layout.operator("gpencil.paste", text="Paste").type = 'COPY' + layout.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE' layout.separator() layout.operator_menu_enum("gpencil.move_to_layer", "layer", text="Move to Layer") - layout.operator("gpencil.stroke_change_color", text="Move to Color") + layout.operator("gpencil.stroke_change_color", text="Change Color") layout.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange Strokes...") layout.separator() layout.operator_menu_enum("gpencil.convert", "type", text="Convert to Geometry...") - layout.separator() - layout.menu("VIEW3D_MT_edit_gpencil_delete") +class VIEW3D_MT_weight_gpencil(Menu): + bl_label = "Weights" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.vertex_group_invert", text="Invert") + layout.operator("gpencil.vertex_group_smooth", text="Smooth") + + +class VIEW3D_MT_gpencil_animation(Menu): + bl_label = "Animation" + + @classmethod + def poll(cls, context): + ob = context.active_object + return ob and ob.type == 'GPENCIL' and ob.mode != 'OBJECT' + + @staticmethod + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.blank_frame_add") + layout.operator("gpencil.active_frames_delete_all", text="Delete Frame(s)") + + layout.separator() + layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame") + layout.operator("gpencil.frame_duplicate", text="Duplicate All Layers").mode = 'ALL' class VIEW3D_MT_edit_gpencil_transform(Menu): @@ -3595,20 +3729,6 @@ class VIEW3D_MT_view_pie(Menu): # ********** Panel ********** -class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - -class VIEW3D_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - - # NOTE: this is just a wrapper around the generic GP Panel - - class VIEW3D_PT_view3d_properties(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -3720,6 +3840,7 @@ class VIEW3D_PT_object_type_visibility(Panel): "armature", "lattice", "empty", + "grease_pencil", "camera", "light", "light_probe", @@ -4040,6 +4161,8 @@ class VIEW3D_PT_overlay_guides(Panel): if shading.type == 'MATERIAL': col.prop(overlay, "show_look_dev") + col.prop(overlay, "show_annotation", text="Annotations") + class VIEW3D_PT_overlay_object(Panel): bl_space_type = 'VIEW_3D' @@ -4578,6 +4701,60 @@ class VIEW3D_PT_transform_orientations(Panel): row.operator("transform.delete_orientation", text="", icon='X', emboss=False) +class VIEW3D_PT_overlay_gpencil_options(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_parent_id = 'VIEW3D_PT_overlay' + bl_label = "" + + @classmethod + def poll(cls, context): + return context.object and context.object.type == 'GPENCIL' + + def draw_header(self, context): + layout = self.layout + layout.label(text={ + 'GPENCIL_PAINT': "Draw Grease Pencil", + 'GPENCIL_EDIT': "Edit Grease Pencil", + 'GPENCIL_SCULPT': "Sculpt Grease Pencil", + 'GPENCIL_WEIGHT': "Weight Grease Pencil", + 'OBJECT': "Grease Pencil", + }[context.mode]) + + def draw(self, context): + layout = self.layout + view = context.space_data + overlay = view.overlay + + layout.prop(overlay, "use_gpencil_onion_skin", text="Onion Skin") + + col = layout.column() + row = col.row() + row.prop(overlay, "use_gpencil_paper", text="") + sub = row.row() + sub.active = overlay.use_gpencil_paper + sub.prop(overlay, "gpencil_paper_opacity", text="Fade 3D Objects", slider=True) + + col = layout.column() + row = col.row() + row.prop(overlay, "use_gpencil_grid", text="") + sub = row.row() + sub.active = overlay.use_gpencil_grid + sub.prop(overlay, "gpencil_grid_opacity", text="Canvas Grid", slider=True) + + if overlay.use_gpencil_grid: + row = layout.row(align=True) + row.prop(overlay, "gpencil_grid_scale") + col = row.column() + col.prop(overlay, "gpencil_grid_lines", text="Subdivisions") + col.prop(overlay, "gpencil_grid_axis") + + if context.object.mode in {'GPENCIL_EDIT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'}: + layout.prop(overlay, "use_gpencil_edit_lines", text="Edit Lines") + layout.prop(overlay, "use_gpencil_multiedit_line_only", text="Show Edit Lines only in multiframe") + layout.prop(overlay, "vertex_opacity", text="Vertex Opacity", slider=True) + + class VIEW3D_PT_quad_view(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -4605,6 +4782,14 @@ class VIEW3D_PT_quad_view(Panel): row.prop(region, "use_box_clip") +# Annotation properties +class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + + # NOTE: this is just a wrapper around the generic GP Panel + + class VIEW3D_PT_view3d_stereo(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' @@ -4684,6 +4869,27 @@ class VIEW3D_PT_context_properties(Panel): rna_prop_ui.draw(self.layout, context, member, object, False) +# Grease Pencil Object - Multiframe falloff tools +class VIEW3D_PT_gpencil_multi_frame(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Multi Frame" + + @staticmethod + def draw(self, context): + gpd = context.gpencil_data + settings = context.tool_settings.gpencil_sculpt + + layout = self.layout + col = layout.column(align=True) + col.prop(settings, "use_multiframe_falloff") + + # Falloff curve + if gpd.use_multiedit and settings.use_multiframe_falloff: + layout.template_curve_mapping(settings, "multiframe_falloff_curve", brush=True) + + + classes = ( VIEW3D_HT_header, VIEW3D_MT_editor_menus, @@ -4791,8 +4997,13 @@ classes = ( VIEW3D_MT_edit_mesh_clean, VIEW3D_MT_edit_mesh_delete, VIEW3D_MT_edit_mesh_showhide, + VIEW3D_MT_paint_gpencil, VIEW3D_MT_edit_gpencil, VIEW3D_MT_edit_gpencil_delete, + VIEW3D_MT_sculpt_gpencil, + VIEW3D_MT_weight_gpencil, + VIEW3D_MT_gpencil_animation, + VIEW3D_MT_gpencil_simplify, VIEW3D_MT_edit_curve, VIEW3D_MT_edit_curve_ctrlpoints, VIEW3D_MT_edit_curve_segments, @@ -4815,12 +5026,12 @@ classes = ( VIEW3D_MT_edit_gpencil_interpolate, VIEW3D_MT_object_mode_pie, VIEW3D_MT_view_pie, - VIEW3D_PT_grease_pencil, - VIEW3D_PT_grease_pencil_palettecolor, VIEW3D_PT_view3d_properties, VIEW3D_PT_view3d_camera_lock, VIEW3D_PT_view3d_cursor, VIEW3D_PT_object_type_visibility, + VIEW3D_PT_grease_pencil, + VIEW3D_PT_gpencil_multi_frame, VIEW3D_PT_quad_view, VIEW3D_PT_view3d_stereo, VIEW3D_PT_shading, @@ -4849,6 +5060,7 @@ classes = ( VIEW3D_PT_pivot_point, VIEW3D_PT_snapping, VIEW3D_PT_transform_orientations, + VIEW3D_PT_overlay_gpencil_options, VIEW3D_PT_context_properties, ) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 87c6a3840ae..70d8c4089d3 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -20,19 +20,17 @@ import bpy from bpy.types import Menu, Panel, UIList from .properties_grease_pencil_common import ( - GreasePencilDrawingToolsPanel, - GreasePencilStrokeEditPanel, - GreasePencilInterpolatePanel, - GreasePencilStrokeSculptPanel, - GreasePencilBrushPanel, - GreasePencilBrushCurvesPanel -) + GreasePencilStrokeEditPanel, + GreasePencilStrokeSculptPanel, + GreasePencilAppearancePanel, + ) from .properties_paint_common import ( - UnifiedPaintPanel, - brush_texture_settings, - brush_texpaint_common, - brush_mask_texture_settings, -) + UnifiedPaintPanel, + brush_texture_settings, + brush_texpaint_common, + brush_mask_texture_settings, + ) +from bl_operators.presets import PresetMenu class View3DPanel: @@ -70,6 +68,12 @@ def draw_vpaint_symmetry(layout, vpaint): col.use_property_split = True col.prop(vpaint, "radial_symmetry", text="Radial") +# Most of these panels should not be visible in GP edit modes +def is_not_gpencil_edit_mode(context): + is_gpmode = context.active_object and \ + context.active_object.mode in {'GPENCIL_EDIT', 'GPENCIL_PAINT', 'GPENCIL_SCULPT', 'GPENCIL_WEIGHT'} + return not is_gpmode + # ********** default tools for editmode_mesh **************** @@ -1341,9 +1345,272 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, Panel): sub.prop(pe, "fade_frames", slider=True) -# Grease Pencil drawing tools -class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): +# ********** grease pencil object tool panels **************** + +# Grease Pencil drawing brushes +class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Brush" + + @classmethod + def poll(cls, context): + is_3d_view = context.space_data.type == 'VIEW_3D' + if is_3d_view: + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(gpd.is_stroke_paint_mode) + else: + return True + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + ts = context.scene.tool_settings + settings = ts.gpencil_paint + + row = layout.row() + col = row.column() + col.template_ID_preview(settings, "brush", new="brush.add_gpencil", rows=3, cols=8) + + col = row.column() + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + sub = col.column(align=True) + sub.operator("gpencil.brush_presets_create", icon='HELP', text="") + + if brush is not None: + # XXX: Items in "sub" currently show up beside the brush selector in a separate column + if gp_settings.gpencil_brush_type == 'ERASE': + sub.prop(gp_settings, "default_eraser", text="") + + # Brush details + if gp_settings.gpencil_brush_type == 'ERASE': + col = layout.column(align=True) + col.prop(brush, "size", text="Radius") + + col.separator() + row = col.row() + row.prop(gp_settings, "eraser_mode", expand=True) + elif gp_settings.gpencil_brush_type == 'FILL': + col = layout.column(align=True) + col.prop(gp_settings, "gpencil_fill_leak", text="Leak Size") + col.prop(brush, "size", text="Thickness") + col.prop(gp_settings, "gpencil_fill_simplyfy_level", text="Simplify") + + col = layout.row(align=True) + col.template_ID(gp_settings, "material") + + row = layout.row(align=True) + row.prop(gp_settings, "gpencil_fill_draw_mode", text="Boundary Draw Mode") + row.prop(gp_settings, "gpencil_fill_show_boundary", text="", icon='GRID') + + col = layout.column(align=True) + col.enabled = gp_settings.gpencil_fill_draw_mode != "STROKE" + col.prop(gp_settings, "gpencil_fill_hide", text="Hide Transparent Lines") + sub = col.row(align=True) + sub.enabled = gp_settings.gpencil_fill_hide + sub.prop(gp_settings, "gpencil_fill_threshold", text="Threshold") + else: # bgpsettings.gpencil_brush_type == 'DRAW': + row = layout.row(align=True) + row.prop(brush, "size", text="Radius") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row = layout.row(align=True) + row.prop(gp_settings, "pen_strength", slider=True) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + + row = layout.row(align=True) + row.template_ID(gp_settings, "material") + + +# Grease Pencil drawing brushes options +class VIEW3D_PT_tools_grease_pencil_brush_option(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Options" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header_preset(self, context): + VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout) + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + if brush is not None: + col = layout.column(align=True) + col.prop(gp_settings, "input_samples") + col.separator() + + col.prop(gp_settings, "active_smooth_factor") + col.separator() + + col.prop(gp_settings, "angle", slider=True) + col.prop(gp_settings, "angle_factor", text="Factor", slider=True) + col.separator() + + +class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_label = "Stabilizer" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + return brush is not None and gp_settings.gpencil_brush_type == 'DRAW' + + def draw_header(self, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + self.layout.prop(gp_settings, "use_stabilizer", text="") + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + layout.active = gp_settings.use_stabilizer + + layout.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + layout.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + + +class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_label = "Post-processing Settings" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.active_gpencil_brush + + return brush is not None + + def draw_header(self, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + self.layout.prop(gp_settings, "enable_settings", text="") + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + layout.active = gp_settings.enable_settings + + layout.prop(gp_settings, "pen_smooth_factor") + layout.prop(gp_settings, "pen_smooth_steps") + + layout.prop(gp_settings, "pen_thick_smooth_factor") + layout.prop(gp_settings, "pen_thick_smooth_steps") + + layout.prop(gp_settings, "pen_subdivision_steps") + layout.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) + + +class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_label = "Random Settings" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + brush = context.active_gpencil_brush + + return brush is not None + + def draw_header(self, context): + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + self.layout.prop(gp_settings, "enable_random", text="") + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + layout.active = gp_settings.enable_random + + layout.prop(gp_settings, "random_pressure", text="Pressure", slider=True) + layout.prop(gp_settings, "random_strength", text="Strength", slider=True) + layout.prop(gp_settings, "uv_random", text="UV", slider=True) + + row = layout.row(align=True) + row.prop(gp_settings, "pen_jitter", slider=True) + row.prop(gp_settings, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') + + +# Grease Pencil drawingcurves +class VIEW3D_PT_tools_grease_pencil_brushcurves(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Curves" + bl_options = {'DEFAULT_CLOSED'} + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + brush = context.active_gpencil_brush + gp_settings = brush.gpencil_settings + + # Brush + layout.label("Sensitivity") + layout.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True) + + layout.label("Strength") + layout.template_curve_mapping(gp_settings, "curve_strength", brush=True) + + layout.label("Jitter") + layout.template_curve_mapping(gp_settings, "curve_jitter", brush=True) + + +# Grease Pencil create shapes +class VIEW3D_PT_tools_grease_pencil_shapes(View3DPanel, Panel): bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Shapes" + + @classmethod + def poll(cls, context): + ob = context.active_object + return ob and ob.type == 'GPENCIL' + + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + col = layout.column(align=True) + col.operator("gpencil.primitive", text="Line", icon='IPO_CONSTANT').type = 'LINE' + col.operator("gpencil.primitive", text="Rectangle", icon='UV_FACESEL').type = 'BOX' + col.operator("gpencil.primitive", text="Circle", icon='ANTIALIASED').type = 'CIRCLE' + + layout.operator("object.gpencil_add", text="Monkey", icon='MONKEY').type = 'MONKEY' # Grease Pencil stroke editing tools @@ -1352,24 +1619,109 @@ class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): # Grease Pencil stroke interpolation tools -class VIEW3D_PT_tools_grease_pencil_interpolate(GreasePencilInterpolatePanel, Panel): +class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Interpolate" + + @classmethod + def poll(cls, context): + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) + + @staticmethod + def draw(self, context): + layout = self.layout + settings = context.tool_settings.gpencil_interpolate + + col = layout.column(align=True) + col.label("Interpolate Strokes") + col.operator("gpencil.interpolate", text="Interpolate") + col.operator("gpencil.interpolate_sequence", text="Sequence") + col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns") + + col = layout.column(align=True) + col.label(text="Options:") + col.prop(settings, "interpolate_all_layers") + col.prop(settings, "interpolate_selected_only") + + col = layout.column(align=True) + col.label(text="Sequence Options:") + col.prop(settings, "type") + if settings.type == 'CUSTOM': + # TODO: Options for loading/saving curve presets? + col.template_curve_mapping(settings, "interpolation_curve", brush=True) + elif settings.type != 'LINEAR': + col.prop(settings, "easing") + + if settings.type == 'BACK': + layout.prop(settings, "back") + elif setting.type == 'ELASTIC': + sub = layout.column(align=True) + sub.prop(settings, "amplitude") + sub.prop(settings, "period") # Grease Pencil stroke sculpting tools -class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): - bl_space_type = 'VIEW_3D' +class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_category = "Tools" + bl_label = "Sculpt Strokes" -# Grease Pencil drawing brushes -class VIEW3D_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): - bl_space_type = 'VIEW_3D' +# Grease Pencil weight painting tools +class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel): + bl_context = ".greasepencil_weight" + bl_category = "Tools" + bl_label = "Weight Paint" -# Grease Pencil drawingcurves + @staticmethod + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + gpd = context.gpencil_data + settings = context.tool_settings.gpencil_sculpt + tool = settings.tool + brush = settings.brush -class VIEW3D_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): - bl_space_type = 'VIEW_3D' + layout.template_icon_view(settings, "weight_tool", show_labels=True) + + col = layout.column() + col.prop(brush, "size", slider=True) + row = col.row(align=True) + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_pressure_strength", text="") + + col.prop(brush, "use_falloff") + + +# Grease Pencil Brush Appeareance (one for each mode) +class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Appearance" + + +class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): + bl_context = ".greasepencil_sculpt" + bl_label = "Appearance" + + +class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): + bl_context = ".greasepencil_weight" + bl_label = "Appearance" + + +class VIEW3D_PT_gpencil_brush_presets(PresetMenu): + """Brush settings""" + bl_label = "Brush Presets" + preset_subdir = "gpencil_brush" + preset_operator = "script.execute_preset" + preset_add_operator = "scene.gpencil_brush_preset_add" classes = ( @@ -1401,12 +1753,21 @@ classes = ( VIEW3D_PT_tools_projectpaint, VIEW3D_MT_tools_projectpaint_stencil, VIEW3D_PT_tools_particlemode, - VIEW3D_PT_tools_grease_pencil_draw, - VIEW3D_PT_tools_grease_pencil_edit, - VIEW3D_PT_tools_grease_pencil_interpolate, - VIEW3D_PT_tools_grease_pencil_sculpt, + + VIEW3D_PT_gpencil_brush_presets, VIEW3D_PT_tools_grease_pencil_brush, + VIEW3D_PT_tools_grease_pencil_brush_option, + VIEW3D_PT_tools_grease_pencil_brush_settings, + VIEW3D_PT_tools_grease_pencil_brush_stabilizer, + VIEW3D_PT_tools_grease_pencil_brush_random, VIEW3D_PT_tools_grease_pencil_brushcurves, + VIEW3D_PT_tools_grease_pencil_shapes, + VIEW3D_PT_tools_grease_pencil_sculpt, + VIEW3D_PT_tools_grease_pencil_weight_paint, + VIEW3D_PT_tools_grease_pencil_paint_appearance, + VIEW3D_PT_tools_grease_pencil_sculpt_appearance, + VIEW3D_PT_tools_grease_pencil_weight_appearance, + VIEW3D_PT_tools_grease_pencil_interpolate, ) if __name__ == "__main__": # only for live edit. -- cgit v1.2.3