diff options
author | YimingWu <xp8110@outlook.com> | 2020-05-16 18:08:40 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2020-05-16 18:08:40 +0300 |
commit | cd54abd2c1316136753f7bbe227bd762a5f9e7d9 (patch) | |
tree | 69f2a4a1a5013acc78213bb4f3fe888912380ea4 /release/scripts/startup/bl_ui | |
parent | f7770cb97bb9d19d0806f67da9377129fd4d09b0 (diff) | |
parent | eaf7d36d66e551c3262d69f472b4028aeccb7e97 (diff) |
Merge remote-tracking branch 'origin/master' into temp-lanpr-review
Diffstat (limited to 'release/scripts/startup/bl_ui')
46 files changed, 3748 insertions, 2225 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 8fc59ca493a..36df0256c3d 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -35,14 +35,17 @@ _modules = [ "properties_data_curve", "properties_data_empty", "properties_data_gpencil", + "properties_data_hair", "properties_data_light", "properties_data_lattice", "properties_data_mesh", "properties_data_metaball", "properties_data_modifier", + "properties_data_pointcloud", "properties_data_shaderfx", "properties_data_lightprobe", "properties_data_speaker", + "properties_data_volume", "properties_mask_common", "properties_material", "properties_material_gpencil", diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index d44af702d46..05abfa02500 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -84,17 +84,12 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel): layout.prop(arm, "display_type", text="Display As") - flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=True) - col = flow.column() + col = layout.column(heading="Show") col.prop(arm, "show_names", text="Names") - col = flow.column() col.prop(arm, "show_axes", text="Axes") - col = flow.column() col.prop(arm, "show_bone_custom_shapes", text="Shapes") - col = flow.column() col.prop(arm, "show_group_colors", text="Group Colors") if ob: - col = flow.column() col.prop(ob, "show_in_front", text="In Front") diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index c50b9414667..aca358870c8 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -268,18 +268,43 @@ class BONE_PT_display(BoneButtonsPanel, Panel): col = layout.column() col.prop(bone, "hide", text="Hide") + + +class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel): + bl_label = "Custom Shape" + bl_parent_id = "BONE_PT_display" + + @classmethod + def poll(cls, context): + return context.bone + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + bone = context.bone + pchan = None + + if ob and bone: + pchan = ob.pose.bones[bone.name] + elif bone is None: + bone = context.edit_bone + + if bone and pchan: + col = layout.column() + col.prop(pchan, "custom_shape") + sub = col.column() sub.active = bool(pchan and pchan.custom_shape) + sub.separator() + sub.prop(pchan, "custom_shape_scale", text="Scale") + sub.prop_search(pchan, "custom_shape_transform", + ob.pose, "bones", text="Override Transform") + sub.prop(pchan, "use_custom_shape_bone_size") + sub.separator() sub.prop(bone, "show_wire", text="Wireframe") - if pchan: - col = layout.column() - col.prop(pchan, "custom_shape") - if pchan.custom_shape: - col.prop(pchan, "use_custom_shape_bone_size", text="Bone Size") - col.prop(pchan, "custom_shape_scale", text="Scale") - col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones") - class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel): bl_label = "Inverse Kinematics" @@ -431,6 +456,7 @@ classes = ( BONE_PT_inverse_kinematics, BONE_PT_deform, BONE_PT_display, + BONE_PT_display_custom_shape, BONE_PT_custom_props, ) diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 957b119e9ba..62e1bcdfce4 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -360,8 +360,9 @@ class DATA_PT_camera_background_image(CameraButtonsPanel, Panel): col.prop(bg, "rotation") col.prop(bg, "scale") - col.prop(bg, "use_flip_x") - col.prop(bg, "use_flip_y") + col = box.column(heading="Flip") + col.prop(bg, "use_flip_x", text="X") + col.prop(bg, "use_flip_y", text="Y") class DATA_PT_camera_display(CameraButtonsPanel, Panel): @@ -377,21 +378,12 @@ class DATA_PT_camera_display(CameraButtonsPanel, Panel): col = layout.column(align=True) - col.separator() - col.prop(cam, "display_size", text="Size") - col.separator() - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False) - - col = flow.column() + col = layout.column(heading="Show") col.prop(cam, "show_limits", text="Limits") - col = flow.column() col.prop(cam, "show_mist", text="Mist") - col = flow.column() col.prop(cam, "show_sensor", text="Sensor") - col = flow.column() col.prop(cam, "show_name", text="Name") @@ -407,24 +399,20 @@ class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel): cam = context.camera - flow = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False) + layout.prop(cam, "show_composition_thirds") - col = flow.column() + col = layout.column(heading="Center", align=True) col.prop(cam, "show_composition_center") - col = flow.column() - col.prop(cam, "show_composition_center_diagonal") - col = flow.column() - col.prop(cam, "show_composition_thirds") - col = flow.column() - col.prop(cam, "show_composition_golden") - col = flow.column() - col.prop(cam, "show_composition_golden_tria_a") - col = flow.column() - col.prop(cam, "show_composition_golden_tria_b") - col = flow.column() - col.prop(cam, "show_composition_harmony_tri_a") - col = flow.column() - col.prop(cam, "show_composition_harmony_tri_b") + col.prop(cam, "show_composition_center_diagonal", text="Diagonal") + + col = layout.column(heading="Golden", align=True) + col.prop(cam, "show_composition_golden", text="Ratio") + col.prop(cam, "show_composition_golden_tria_a", text="Triangle A") + col.prop(cam, "show_composition_golden_tria_b", text="Triangle B") + + col = layout.column(heading="Harmony", align=True) + col.prop(cam, "show_composition_harmony_tri_a", text="Triangle A") + col.prop(cam, "show_composition_harmony_tri_b", text="Triangle B") class DATA_PT_camera_display_passepartout(CameraButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index b694062dfc5..7e7488f4cf1 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -276,23 +276,23 @@ class DATA_PT_active_spline(CurveButtonsPanelActive, Panel): col.prop(act_spline, "use_smooth") else: - sub = col.column(align=True) - sub.prop(act_spline, "use_cyclic_u") + sub = col.column(heading="Cyclic", align=True) + sub.prop(act_spline, "use_cyclic_u", text="U") if is_surf: sub.prop(act_spline, "use_cyclic_v", text="V") if act_spline.type == 'NURBS': - sub = col.column(align=True) + sub = col.column(heading="Bezier", align=True) # sub.active = (not act_spline.use_cyclic_u) - sub.prop(act_spline, "use_bezier_u", text="Bezier U") + sub.prop(act_spline, "use_bezier_u", text="U") if is_surf: subsub = sub.column() subsub.active = (not act_spline.use_cyclic_v) subsub.prop(act_spline, "use_bezier_v", text="V") - sub = col.column(align=True) - sub.prop(act_spline, "use_endpoint_u", text="Endpoint U") + sub = col.column(heading="Endpoint", align=True) + sub.prop(act_spline, "use_endpoint_u", text="U") if is_surf: subsub = sub.column() diff --git a/release/scripts/startup/bl_ui/properties_data_empty.py b/release/scripts/startup/bl_ui/properties_data_empty.py index 88fdaae0433..1523f69536f 100644 --- a/release/scripts/startup/bl_ui/properties_data_empty.py +++ b/release/scripts/startup/bl_ui/properties_data_empty.py @@ -49,11 +49,15 @@ class DATA_PT_empty(DataButtonsPanel, Panel): col.prop(ob, "empty_image_offset", text="Y", index=1) col = layout.column() - col.row().prop(ob, "empty_image_depth", text="Depth", expand=True) + depth_row = col.row() + depth_row.enabled = not ob.show_in_front + depth_row.prop(ob, "empty_image_depth", text="Depth", expand=True) col.row().prop(ob, "empty_image_side", text="Side", expand=True) - col.prop(ob, "show_empty_image_orthographic", text="Display Orthographic") - col.prop(ob, "show_empty_image_perspective", text="Display Perspective") - col.prop(ob, "show_empty_image_only_axis_aligned") + + col = layout.column(heading="Show in", align=True) + col.prop(ob, "show_empty_image_orthographic", text="Orthographic") + col.prop(ob, "show_empty_image_perspective", text="Perspective") + col.prop(ob, "show_empty_image_only_axis_aligned", text="Only Axis Aligned") class DATA_PT_empty_alpha(DataButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py index 883673ffd7a..4ed5264549f 100644 --- a/release/scripts/startup/bl_ui/properties_data_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py @@ -22,6 +22,7 @@ from bpy.types import Menu, Panel, UIList from rna_prop_ui import PropertyPanel from bl_ui.properties_grease_pencil_common import ( + GreasePencilLayerMasksPanel, GreasePencilLayerAdjustmentsPanel, GreasePencilLayerRelationsPanel, GreasePencilLayerDisplayPanel, @@ -116,7 +117,7 @@ class DATA_PT_gpencil_layers(DataButtonsPanel, Panel): def draw(self, context): layout = self.layout - #layout.use_property_split = True + # layout.use_property_split = True layout.use_property_decorate = False gpd = context.gpencil @@ -166,7 +167,6 @@ class DATA_PT_gpencil_layers(DataButtonsPanel, Panel): col = layout.column(align=True) if gpl: - layout = self.layout layout.use_property_split = True layout.use_property_decorate = True @@ -178,6 +178,15 @@ class DATA_PT_gpencil_layers(DataButtonsPanel, Panel): col = layout.row(align=True) col.prop(gpl, "opacity", text="Opacity", slider=True) + col = layout.row(align=True) + col.prop(gpl, "use_lights") + + +class DATA_PT_gpencil_layer_masks(LayerDataButtonsPanel, GreasePencilLayerMasksPanel, Panel): + bl_label = "Masks" + bl_parent_id = 'DATA_PT_gpencil_layers' + bl_options = {'DEFAULT_CLOSED'} + class DATA_PT_gpencil_layer_adjustments(LayerDataButtonsPanel, GreasePencilLayerAdjustmentsPanel, Panel): bl_label = "Adjustments" @@ -264,7 +273,7 @@ class DATA_PT_gpencil_onion_skinning_display(DataButtonsPanel, Panel): col.prop(gpd, "use_onion_fade", text="Fade") sub = layout.column() sub.active = gpd.onion_mode in {'RELATIVE', 'SELECTED'} - sub.prop(gpd, "use_onion_loop", text="Loop") + sub.prop(gpd, "use_onion_loop", text="Show Start Frame") class GPENCIL_MT_gpencil_vertex_group(Menu): @@ -364,9 +373,6 @@ class DATA_PT_gpencil_strokes(DataButtonsPanel, Panel): sub.active = gpd.stroke_thickness_space == 'WORLDSPACE' sub.prop(gpd, "pixel_factor", text="Thickness Scale") - layout.prop(gpd, "use_force_fill_recalc", text="Force Fill Update") - layout.prop(gpd, "use_adaptive_uv", text="Adaptive UVs") - class DATA_PT_gpencil_display(DataButtonsPanel, Panel): bl_label = "Viewport Display" @@ -381,8 +387,6 @@ class DATA_PT_gpencil_display(DataButtonsPanel, Panel): gpl = gpd.layers.active layout.prop(gpd, "edit_line_color", text="Edit Line Color") - if gpl: - layout.prop(gpd, "show_stroke_direction", text="Show Stroke Directions") class DATA_PT_gpencil_canvas(DataButtonsPanel, Panel): @@ -411,6 +415,7 @@ class DATA_PT_custom_props_gpencil(DataButtonsPanel, PropertyPanel, Panel): _context_path = "object.data" _property_type = bpy.types.GreasePencil + ############################### @@ -420,6 +425,7 @@ classes = ( DATA_PT_gpencil_onion_skinning, DATA_PT_gpencil_onion_skinning_custom_colors, DATA_PT_gpencil_onion_skinning_display, + DATA_PT_gpencil_layer_masks, DATA_PT_gpencil_layer_adjustments, DATA_PT_gpencil_layer_relations, DATA_PT_gpencil_layer_display, @@ -437,5 +443,6 @@ classes = ( 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_hair.py b/release/scripts/startup/bl_ui/properties_data_hair.py new file mode 100644 index 00000000000..6017765b83d --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_hair.py @@ -0,0 +1,78 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> +import bpy +from bpy.types import Panel, UIList +from rna_prop_ui import PropertyPanel + + +class DataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return hasattr(context, 'hair') and context.hair and (engine in cls.COMPAT_ENGINES) + + +class DATA_PT_context_hair(DataButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + + ob = context.object + hair = context.hair + space = context.space_data + + if ob: + layout.template_ID(ob, "data") + elif hair: + layout.template_ID(space, "pin_id") + + +class DATA_PT_hair(DataButtonsPanel, Panel): + bl_label = "Hair" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + hair = context.hair + pass + +class DATA_PT_custom_props_hair(DataButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + _context_path = "object.data" + _property_type = bpy.types.Hair if hasattr(bpy.types, "Hair") else None + + +classes = ( + DATA_PT_context_hair, + DATA_PT_hair, + DATA_PT_custom_props_hair, +) + +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_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 3edce6b3b52..425c94dfdcd 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -55,9 +55,12 @@ class MESH_MT_vertex_group_context_menu(Menu): layout.operator("object.vertex_group_remove", text="Delete All Unlocked Groups").all_unlocked = True layout.operator("object.vertex_group_remove", text="Delete All Groups").all = True layout.separator() - layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All").action = 'LOCK' - layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="UnLock All").action = 'UNLOCK' - layout.operator("object.vertex_group_lock", text="Lock Invert All").action = 'INVERT' + props = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All") + props.action, props.mask = 'LOCK', 'ALL' + props = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="UnLock All") + props.action, props.mask = 'UNLOCK', 'ALL' + props = layout.operator("object.vertex_group_lock", text="Lock Invert All") + props.action, props.mask = 'INVERT', 'ALL' class MESH_MT_shape_key_context_menu(Menu): @@ -187,27 +190,20 @@ class DATA_PT_normals(MeshButtonsPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} def draw(self, context): - pass - - -class DATA_PT_normals_auto_smooth(MeshButtonsPanel, Panel): - bl_label = "Auto Smooth" - bl_parent_id = "DATA_PT_normals" - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - def draw_header(self, context): - mesh = context.mesh - - self.layout.prop(mesh, "use_auto_smooth", text="") - - def draw(self, context): layout = self.layout layout.use_property_split = True mesh = context.mesh - layout.active = mesh.use_auto_smooth and not mesh.has_custom_normals - layout.prop(mesh, "auto_smooth_angle", text="Angle") + col = layout.column(align=False, heading="Auto Smooth") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(mesh, "use_auto_smooth", text="") + sub = sub.row(align=True) + sub.active = mesh.use_auto_smooth and not mesh.has_custom_normals + sub.prop(mesh, "auto_smooth_angle", text="") + row.prop_decorator(mesh, "auto_smooth_angle") class DATA_PT_texture_space(MeshButtonsPanel, Panel): @@ -482,8 +478,11 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): col.prop(mesh, "remesh_voxel_adaptivity") col.prop(mesh, "use_remesh_fix_poles") col.prop(mesh, "use_remesh_smooth_normals") - col.prop(mesh, "use_remesh_preserve_volume") - col.prop(mesh, "use_remesh_preserve_paint_mask") + + col = layout.column(heading="Preserve") + col.prop(mesh, "use_remesh_preserve_volume", text="Volume") + col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") + col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") col.operator("object.voxel_remesh", text="Voxel Remesh") else: col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh") @@ -511,12 +510,12 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel): else: col.operator("mesh.customdata_custom_splitnormals_add", icon='ADD') - col = layout.column() + col = layout.column(heading="Store") col.enabled = obj is not None and obj.mode != 'EDIT' - col.prop(me, "use_customdata_vertex_bevel") - col.prop(me, "use_customdata_edge_bevel") - col.prop(me, "use_customdata_edge_crease") + col.prop(me, "use_customdata_vertex_bevel", text="Vertex Bevel Weight") + col.prop(me, "use_customdata_edge_bevel", text="Edge Bevel Weight") + col.prop(me, "use_customdata_edge_crease", text="Edge Crease") class DATA_PT_custom_props_mesh(MeshButtonsPanel, PropertyPanel, Panel): @@ -540,7 +539,6 @@ classes = ( DATA_PT_vertex_colors, DATA_PT_face_maps, DATA_PT_normals, - DATA_PT_normals_auto_smooth, DATA_PT_texture_space, DATA_PT_remesh, DATA_PT_customdata, diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 197566f16f3..75e9a320130 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -170,7 +170,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.limit_method == 'ANGLE': layout.prop(md, "angle_limit") elif md.limit_method == 'VGROUP': - layout.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = layout.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') layout.label(text="Face Strength Mode:") layout.row().prop(md, "face_strength_mode", expand=True) @@ -316,7 +318,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "object", text="") col = split.column() col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') layout.label(text="Deformation Axis:") layout.row().prop(md, "deform_axis", expand=True) @@ -381,7 +385,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.label(text="Space:") col.prop(md, "space", text="") col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') col = split.column(align=True) col.active = has_texture @@ -390,6 +396,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.texture_coords == 'OBJECT': col.label(text="Object:") col.prop(md, "texture_coords_object", text="") + obj = md.texture_coords_object + if obj and obj.type == 'ARMATURE': + col.label(text="Bone:") + col.prop_search(md, "texture_coords_bone", obj.data, "bones", text="") elif md.texture_coords == 'UV' and ob.type == 'MESH': col.label(text="UV Map:") col.prop_search(md, "uv_layer", ob.data, "uv_layers", text="") @@ -419,7 +429,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col = split.column() col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') sub = col.column() sub.active = bool(md.vertex_group) sub.prop(md, "protect") @@ -450,7 +462,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop_search(md, "subtarget", md.object.data, "bones", text="") col = split.column() col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') layout.separator() @@ -484,13 +498,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop(md, "iterations") - row = layout.row() - row.active = not is_bind - row.label(text="Anchors Vertex Group:") - - row = layout.row() + row = layout.row(align=True) row.enabled = not is_bind - row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop_search(md, "vertex_group", ob, "vertex_groups") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') layout.separator() @@ -519,7 +530,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "use_normalized") layout.label(text="Vertex Group:") - layout.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = layout.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') def LATTICE(self, layout, ob, md): split = layout.split() @@ -530,7 +543,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col = split.column() col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') layout.separator() layout.prop(md, "strength", slider=True) @@ -640,6 +655,8 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): row = layout.row() row.prop(md, "use_mirror_u", text="Flip U") row.prop(md, "use_mirror_v", text="Flip V") + row = layout.row() + row.prop(md, "use_mirror_udim", text="Flip UDIM") col = layout.column(align=True) @@ -654,26 +671,53 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "offset_v") def MULTIRES(self, layout, ob, md): - layout.row().prop(md, "subdivision_type", expand=True) + # Changing some of the properties can not be done once there is an + # actual displacement stored for this multires modifier. This check + # will disallow those properties from change. + # This is a bit stupid check but should be sufficient for the usual + # multires usage. It might become less strict and only disallow + # modifications if there is CD_MDISPS layer, or if there is actual + # non-zero displacement but such checks will be too slow to be done + # on every redraw. + have_displacement = (md.total_levels != 0) + + row = layout.row() + row.enabled = not have_displacement + row.prop(md, "subdivision_type", expand=True) split = layout.split() col = split.column() col.prop(md, "levels", text="Preview") - # TODO(sergey): Expose it again after T58473 is solved. - # col.prop(md, "sculpt_levels", text="Sculpt") + col.prop(md, "sculpt_levels", text="Sculpt") col.prop(md, "render_levels", text="Render") - col.prop(md, "quality") + + row = col.row() + row.enabled = not have_displacement + row.prop(md, "quality") col = split.column() col.enabled = ob.mode != 'EDIT' - col.operator("object.multires_subdivide", text="Subdivide") + op = col.operator("object.multires_subdivide", text="Subdivide") + op.mode = 'CATMULL_CLARK' + + op = col.operator("object.multires_subdivide", text="Subdivide Simple") + op.mode = 'SIMPLE' + + op = col.operator("object.multires_subdivide", text="Subdivide Linear") + op.mode = 'LINEAR' + col.operator("object.multires_higher_levels_delete", text="Delete Higher") + col.operator("object.multires_unsubdivide", text="Unsubdivide") col.operator("object.multires_reshape", text="Reshape") col.operator("object.multires_base_apply", text="Apply Base") + col.operator("object.multires_rebuild_subdiv", text="Rebuild Subdivisions") col.prop(md, "uv_smooth", text="") col.prop(md, "show_only_control_edges") - col.prop(md, "use_creases") + + row = col.row() + row.enabled = not have_displacement + row.prop(md, "use_creases") layout.separator() @@ -714,6 +758,19 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "size") col.prop(md, "spatial_size") + layout.separator() + + layout.prop(md, "spectrum") + + if md.spectrum in {'TEXEL_MARSEN_ARSLOE', 'JONSWAP'}: + split = layout.split() + + col = split.column() + col.prop(md, "sharpen_peak_jonswap") + + col = split.column() + col.prop(md, "fetch_jonswap") + layout.label(text="Waves:") split = layout.split() @@ -963,7 +1020,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "factor") col.prop(md, "iterations") col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') def SOFT_BODY(self, layout, _ob, _md): layout.label(text="Settings are inside the Physics tab") @@ -998,12 +1057,23 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): sub = col.row() sub.active = bool(md.vertex_group) sub.prop(md, "thickness_vertex_group", text="Factor") + if solidify_mode == 'NON_MANIFOLD': + sub = col.row() + sub.active = bool(md.vertex_group) + sub.prop(md, "use_flat_faces") if solidify_mode == 'EXTRUDE': col.label(text="Crease:") col.prop(md, "edge_crease_inner", text="Inner") col.prop(md, "edge_crease_outer", text="Outer") col.prop(md, "edge_crease_rim", text="Rim") + col.label(text="Bevel:") + col.prop(md, "bevel_convex") + else: + col.label(text="Bevel:") + col.prop(md, "bevel_convex") + col.separator() + col.prop(md, "nonmanifold_merge_threshold") col = split.column() @@ -1031,6 +1101,17 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): row.active = md.use_rim row.prop(md, "material_offset_rim", text="Rim") + col.separator() + + row = col.row(align=True) + row.label(text="Shell Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "shell_vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.label(text="Rim Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "rim_vertex_group", ob, "vertex_groups", text="") + def SUBSURF(self, layout, ob, md): from bpy import context layout.row().prop(md, "subdivision_type", expand=True) @@ -1089,13 +1170,24 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.label(text="Settings are inside the Physics tab") def SURFACE_DEFORM(self, layout, _ob, md): - col = layout.column() + split = layout.split() + col = split.column() col.active = not md.is_bound - col.prop(md, "target") - col.prop(md, "falloff") + col.label(text="Target:") + col.prop(md, "target", text="") - layout.separator() + col = split.column() + col.label(text="Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", _ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') + + split = layout.split() + col = split.column() + col.prop(md, "falloff") + col = split.column() + col.prop(md, "strength") col = layout.column() @@ -1132,12 +1224,30 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.label(text="From:") col.prop(md, "object_from", text="") - col.prop(md, "use_volume_preserve") - col = split.column() col.label(text="To:") col.prop(md, "object_to", text="") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + + split = layout.split() + col = split.column() + obj = md.object_from + if obj and obj.type == 'ARMATURE': + col.label(text="Bone:") + col.prop_search(md, "bone_from", obj.data, "bones", text="") + + col = split.column() + obj = md.object_to + if obj and obj.type == 'ARMATURE': + col.label(text="Bone:") + col.prop_search(md, "bone_to", obj.data, "bones", text="") + + split = layout.split() + col = split.column() + col.prop(md, "use_volume_preserve") + col = split.column() + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') col = layout.column() @@ -1163,6 +1273,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.texture_coords == 'OBJECT': layout.prop(md, "texture_coords_object", text="Object") + obj = md.texture_coords_object + if obj and obj.type == 'ARMATURE': + layout.prop_search(md, "texture_coords_bone", obj.data, "bones", text="Bone") elif md.texture_coords == 'UV' and ob.type == 'MESH': layout.prop_search(md, "uv_layer", ob.data, "uv_layers") @@ -1202,7 +1315,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.separator() layout.prop(md, "start_position_object") - layout.prop_search(md, "vertex_group", ob, "vertex_groups") + row = layout.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') split = layout.split(factor=0.33) col = split.column() col.label(text="Texture") @@ -1213,6 +1328,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop_search(md, "uv_layer", ob.data, "uv_layers") elif md.texture_coords == 'OBJECT': layout.prop(md, "texture_coords_object") + obj = md.texture_coords_object + if obj and obj.type == 'ARMATURE': + layout.prop_search(md, "texture_coords_bone", obj.data, "bones") layout.separator() @@ -1234,17 +1352,22 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop(md, "mode") row = layout.row() - row.prop(md, "octree_depth") - row.prop(md, "scale") + if md.mode == 'VOXEL': + layout.prop(md, "voxel_size") + layout.prop(md, "adaptivity") + else: + row.prop(md, "octree_depth") + row.prop(md, "scale") - if md.mode == 'SHARP': - layout.prop(md, "sharpness") + if md.mode == 'SHARP': + layout.prop(md, "sharpness") + + layout.prop(md, "use_remove_disconnected") + row = layout.row() + row.active = md.use_remove_disconnected + row.prop(md, "threshold") layout.prop(md, "use_smooth_shade") - layout.prop(md, "use_remove_disconnected") - row = layout.row() - row.active = md.use_remove_disconnected - row.prop(md, "threshold") @staticmethod def vertex_weight_mask(layout, ob, md): @@ -1257,7 +1380,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if not md.mask_texture: split = layout.split(factor=0.4) split.label(text="Vertex Group Mask:") - split.prop_search(md, "mask_vertex_group", ob, "vertex_groups", text="") + row = split.row(align=True) + row.prop_search(md, "mask_vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_mask_vertex_group", text="", icon='ARROW_LEFTRIGHT') if not md.mask_vertex_group: split = layout.split(factor=0.4) @@ -1276,6 +1401,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.mask_tex_mapping == 'OBJECT': layout.prop(md, "mask_tex_map_object", text="Object") + obj = md.mask_tex_map_object + if obj and obj.type == 'ARMATURE': + layout.prop_search(md, "mask_tex_map_bone", obj.data, "bones", text="Bone") elif md.mask_tex_mapping == 'UV' and ob.type == 'MESH': layout.prop_search(md, "mask_tex_uv_layer", ob.data, "uv_layers") @@ -1303,7 +1431,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.separator() - layout.prop(md, "falloff_type") + row = layout.row(align=True) + row.prop(md, "falloff_type") + row.prop(md, "invert_falloff", text="", icon='ARROW_LEFTRIGHT') if md.falloff_type == 'CURVE': layout.template_curve_mapping(md, "map_curve") @@ -1361,7 +1491,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "max_dist") layout.separator() - layout.prop(md, "falloff_type") + row = layout.row(align=True) + row.prop(md, "falloff_type") + row.prop(md, "invert_falloff", text="", icon='ARROW_LEFTRIGHT') # Common mask options layout.separator() @@ -1442,10 +1574,25 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop_search(md, "bone_to", obj.data, "bones", text="") split = layout.split() + col = split.column() + col.label(text="Offset:") + col.prop(md, "offset", text="") + + col = split.column() + col.label(text="Scale:") + col.prop(md, "scale", text="") + + col = split.column() + col.label(text="Rotate:") + col.prop(md, "rotation", text="") + + split = layout.split() col = split.column() col.label(text="Vertex Group:") - col.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') col = split.column() col.label(text="UV Map:") @@ -1486,7 +1633,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): def WELD(self, layout, ob, md): layout.prop(md, "merge_threshold", text="Distance") layout.prop(md, "max_interactions") - layout.prop_search(md, "vertex_group", ob, "vertex_groups") + row = layout.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') def DATA_TRANSFER(self, layout, ob, md): row = layout.row(align=True) @@ -1641,7 +1790,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop(md, "factor", text="Factor") layout.prop(md, "iterations") - + layout.prop(md, "scale") row = layout.row() row.prop(md, "smooth_type") @@ -1677,6 +1826,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "thresh", text="Threshold") col.prop(md, "face_influence") + def SIMULATION(self, layout, ob, md): + layout.prop(md, "simulation") + layout.prop(md, "data_path") + class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): bl_label = "Modifiers" @@ -1710,132 +1863,110 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): # ...to avoid lengthy if statements # so each type must have a function here. - def GP_NOISE(self, layout, ob, md): + def gpencil_masking(self, layout, ob, md, use_vertex, use_curve=False): 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") - row = col.row() - row.enabled = md.random - row.prop(md, "seed") - col.prop(md, "full_stroke") - col.prop(md, "move_extreme") - - row = layout.row(align=True) - row.label(text="Affect:") - row = layout.row(align=True) - row.prop(md, "use_edit_position", text="Position", icon='MESH_DATA', toggle=True) - row.prop(md, "use_edit_strength", text="Strength", icon='COLOR', toggle=True) - row.prop(md, "use_edit_thickness", text="Thickness", icon='LINE_DATA', toggle=True) - row.prop(md, "use_edit_uv", text="UV", icon='MOD_UVPROJECT', toggle=True) + layout.separator() + layout.label(text="Influence Filters:") - col = layout.column() - col.separator() - col.label(text="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') + split = layout.split(factor=0.25) - col = layout.column() - col.separator() + col1 = split.column() - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') + col1.label(text="Layer:") + col1.label(text="Material:") + if use_vertex: + col1.label(text="Vertex Group:") - col = layout.column() - col.separator() + col2 = split.column() - col.label(text="Layer:") - row = col.row(align=True) + split = col2.split(factor=0.6) + row = split.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 = split.row(align=True) row.prop(md, "layer_pass", text="Pass") row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') - def GP_SMOOTH(self, layout, ob, md): - gpd = ob.data - col = layout.column() - col.prop(md, "factor") - col.prop(md, "step") + split = col2.split(factor=0.6) - col.label(text="Affect:") - row = col.row(align=True) - row.prop(md, "use_edit_position", text="Position", icon='MESH_DATA', toggle=True) - row.prop(md, "use_edit_strength", text="Strength", icon='COLOR', toggle=True) - row.prop(md, "use_edit_thickness", text="Thickness", icon='LINE_DATA', toggle=True) - row.prop(md, "use_edit_uv", text="UV", icon='MOD_UVPROJECT', toggle=True) - - col.separator() - col.label(text="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 = split.row(align=True) - col = layout.column() - col.separator() + valid = md.material in (slot.material for slot in ob.material_slots) or md.material is None + if valid: + icon = 'SHADING_TEXTURE' + else: + icon = 'ERROR' - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') + row.alert = not valid + row.prop_search(md, "material", gpd, "materials", text="", icon=icon) row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) + + row = split.row(align=True) row.prop(md, "pass_index", text="Pass") row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - col = layout.column() - col.separator() + if use_vertex: + row = col2.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex", text="", icon='ARROW_LEFTRIGHT') - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + if use_curve: + col = layout.column() + col.separator() + col.prop(md, "use_custom_curve") + if md.use_custom_curve: + col.template_curve_mapping(md, "curve") - def GP_SUBDIV(self, layout, ob, md): - gpd = ob.data + def GP_NOISE(self, layout, ob, md): split = layout.split() col = split.column() row = col.row(align=True) - row.prop(md, "level") - row.prop(md, "simple", text="", icon='PARTICLE_POINT') + row.prop(md, "factor", text="Position") + row = col.row(align=True) + row.prop(md, "factor_strength", text="Strength") + row = col.row(align=True) + row.prop(md, "factor_thickness", text="Thickness") + row = col.row(align=True) + row.prop(md, "factor_uvs", text="UV") - col = layout.column() col.separator() - - col.label(text="Material:") row = col.row(align=True) + row.prop(md, "random", text="", icon='TIME', toggle=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') + subrow = row.row(align=True) + subrow.enabled = md.random + subrow.prop(md, "step") + subrow.prop(md, "seed") - col = layout.column() col.separator() + col.prop(md, "noise_scale") + + self.gpencil_masking(layout, ob, md, True, True) - col.label(text="Layer:") + def GP_SMOOTH(self, layout, ob, md): + col = layout.column() + col.prop(md, "factor") + col.prop(md, "step", text="Repeat") + + col.label(text="Affect:") 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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + row.prop(md, "use_edit_position", text="Position", toggle=True) + row.prop(md, "use_edit_strength", text="Strength", toggle=True) + row.prop(md, "use_edit_thickness", text="Thickness", toggle=True) + row.prop(md, "use_edit_uv", text="UV", toggle=True) + + self.gpencil_masking(layout, ob, md, True, True) + + def GP_SUBDIV(self, layout, ob, md): + layout.row().prop(md, "subdivision_type", expand=True) + split = layout.split() + col = split.column() + row = col.row(align=True) + row.prop(md, "level", text="Subdivisions") + + self.gpencil_masking(layout, ob, md, False) def GP_SIMPLIFY(self, layout, ob, md): gpd = ob.data @@ -1857,108 +1988,70 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): elif md.mode == 'MERGE': col.prop(md, "distance") - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() - - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, False) 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", text="Thickness Factor") + col = layout.column() col.prop(md, "normalize_thickness") - if not md.normalize_thickness: - split = layout.split() - col = split.column() - col.prop(md, "use_custom_curve") + if md.normalize_thickness: + col.prop(md, "thickness") + else: + col.prop(md, "thickness_factor") - if md.use_custom_curve: - col.template_curve_mapping(md, "curve") + self.gpencil_masking(layout, ob, md, True, True) + def GP_TEXTURE(self, layout, ob, md): col = layout.column() - col.separator() - col.label(text="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') - col = layout.column() - col.separator() + col.prop(md, "mode") + if md.mode in {'STROKE', 'STROKE_AND_FILL'}: + col.label(text="Stroke Texture:") + col.prop(md, "fit_method") + col.prop(md, "uv_offset") + col.prop(md, "uv_scale") - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') + if md.mode == 'STROKE_AND_FILL': + col.separator() - col = layout.column() - col.separator() + if md.mode in {'FILL', 'STROKE_AND_FILL'}: + col.label(text="Fill Texture:") + col.prop(md, "fill_rotation", text="Rotation") + col.prop(md, "fill_offset", text="Location") + col.prop(md, "fill_scale", text="Scale") - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, True) def GP_TINT(self, layout, ob, md): - gpd = ob.data - split = layout.split() + layout.row().prop(md, "tint_type", expand=True) - col = split.column() - col.prop(md, "color") - col.prop(md, "factor") + if md.tint_type == 'UNIFORM': + col = layout.column() + col.prop(md, "color") - row = layout.row() - row.prop(md, "create_materials") - row.prop(md, "modify_color") + col.separator() + col.prop(md, "factor") - col = layout.column() - col.separator() + if md.tint_type == 'GRADIENT': + col = layout.column() + col.label(text="Colors:") + col.template_color_ramp(md, "colors") - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') + col.separator() + + col.label(text="Object:") + col.prop(md, "object", text="") + + col.separator() + row = col.row(align=True) + row.prop(md, "radius") + row.prop(md, "factor") - col = layout.column() col.separator() + col.prop(md, "vertex_mode") - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, True, True) def GP_TIME(self, layout, ob, md): gpd = ob.data @@ -2004,7 +2097,6 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') def GP_COLOR(self, layout, ob, md): - gpd = ob.data split = layout.split() col = split.column() @@ -2014,134 +2106,71 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "value", text="V", slider=True) row = layout.row() - row.prop(md, "create_materials") row.prop(md, "modify_color") - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() - - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, False, True) def GP_OPACITY(self, layout, ob, md): - gpd = ob.data split = layout.split() col = split.column() - col.label(text="Opacity:") - col.prop(md, "factor") + col.prop(md, "modify_color") - row = layout.row() - row.prop(md, "opacity_mode", text="Mode") - - if md.opacity_mode == 'MATERIAL': - row = layout.row() - row.prop(md, "create_materials") - row.prop(md, "modify_color", text="Change") + if md.modify_color == 'HARDNESS': + col.prop(md, "hardness") + show = False else: - col = layout.column() - col.separator() - col.label(text="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') - - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() + col.prop(md, "normalize_opacity") + if md.normalize_opacity is True: + text="Strength" + else: + text="Opacity Factor" - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + col.prop(md, "factor", text=text) + show = True + self.gpencil_masking(layout, ob, md, show, show) def GP_ARRAY(self, layout, ob, md): - gpd = ob.data - col = layout.column() col.prop(md, "count") split = layout.split() col = split.column() - col.label(text="Offset:") - col.prop(md, "offset", text="") - col.prop(md, "offset_object", text="Object") + col.prop(md, "use_constant_offset", text="Constant Offset") + subcol = col.column() + subcol.enabled = md.use_constant_offset + subcol.prop(md, "constant_offset", text="") + + col.prop(md, "use_object_offset") + subcol = col.column() + subcol.enabled = md.use_object_offset + subcol.prop(md, "offset_object", text="") col = split.column() - col.label(text="Shift:") - col.prop(md, "shift", text="") + col.prop(md, "use_relative_offset", text="Relative Offset") + subcol = col.column() + subcol.enabled = md.use_relative_offset + subcol.prop(md, "relative_offset", text="") split = layout.split() col = split.column() - col.label(text="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.label(text="Random Offset:") + col.prop(md, "random_offset", text="") col = split.column() - col.label(text="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="") - - col = layout.column() - col.prop(md, "replace_material", text="Material") - col.prop(md, "keep_on_top", text="Keep original stroke on top") + col.label(text="Random Rotation:") + col.prop(md, "random_rotation", text="") - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') + col = split.column() + col.label(text="Random Scale:") + col.prop(md, "random_scale", text="") col = layout.column() + col.prop(md, "seed") col.separator() + col.prop(md, "replace_material", text="Material Override") - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, False) def GP_BUILD(self, layout, ob, md): gpd = ob.data @@ -2168,18 +2197,31 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): sub.prop(md, "frame_start", text="Start") sub.prop(md, "frame_end", text="End") - col = layout.column() - col.separator() - col.label(text="Layer:") - row = col.row(align=True) + col.prop(md, "use_percentage") + sub = col.column(align=True) + sub.active = md.use_percentage + sub.prop(md, "percentage_factor") + + layout.label(text="Influence Filters:") + + split = layout.split(factor=0.25) + + col1 = split.column() + + col1.label(text="Layer:") + + col2 = split.column() + + split = col2.split(factor=0.6) + row = split.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 = split.row(align=True) row.prop(md, "layer_pass", text="Pass") row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') def GP_LATTICE(self, layout, ob, md): - gpd = ob.data split = layout.split() col = split.column() @@ -2188,70 +2230,20 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): layout.prop(md, "strength", slider=True) - col = layout.column() - col.separator() - col.label(text="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') - - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() - - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, 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") - layout.label(text="Object:") + layout.label(text="Mirror Object:") layout.prop(md, "object", text="") - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() - - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, False) def GP_HOOK(self, layout, ob, md): - gpd = ob.data split = layout.split() col = split.column() @@ -2281,71 +2273,16 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): col = split.column() col.prop(md, "use_falloff_uniform") - col = layout.column() - col.separator() - col.label(text="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') - - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() - - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, True) def GP_OFFSET(self, layout, ob, md): - gpd = ob.data - col = layout.column() - - col.prop(md, "location") - col.prop(md, "scale") - col.prop(md, "rotation") - - col = layout.column() - col.separator() - col.label(text="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') - - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') + split = layout.split() - col = layout.column() - col.separator() + split.column().prop(md, "location") + split.column().prop(md, "rotation") + split.column().prop(md, "scale") - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, True) def GP_ARMATURE(self, layout, ob, md): split = layout.split() @@ -2371,50 +2308,23 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') def GP_MULTIPLY(self, layout, ob, md): - gpd = ob.data col = layout.column() - col.prop(md, "duplications") + col.prop(md, "duplicates") subcol = col.column() - subcol.enabled = md.duplications > 0 + subcol.enabled = md.duplicates > 0 subcol.prop(md, "distance") subcol.prop(md, "offset", slider=True) subcol.separator() - subcol.prop(md, "enable_fading") - if md.enable_fading: + subcol.prop(md, "use_fade") + if md.use_fade: subcol.prop(md, "fading_center") subcol.prop(md, "fading_thickness", slider=True) subcol.prop(md, "fading_opacity", slider=True) - subcol.separator() - - col.prop(md, "enable_angle_splitting") - if md.enable_angle_splitting: - col.prop(md, "split_angle") - - col = layout.column() - col.separator() - - col.label(text="Material:") - row = col.row(align=True) - row.prop_search(md, "material", gpd, "materials", text="", icon='SHADING_TEXTURE') - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - row = layout.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - col = layout.column() - col.separator() - - col.label(text="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 = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + self.gpencil_masking(layout, ob, md, False) classes = ( @@ -2424,5 +2334,6 @@ classes = ( 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_pointcloud.py b/release/scripts/startup/bl_ui/properties_data_pointcloud.py new file mode 100644 index 00000000000..10ebdea3155 --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_pointcloud.py @@ -0,0 +1,78 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> +import bpy +from bpy.types import Panel, UIList +from rna_prop_ui import PropertyPanel + + +class DataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return hasattr(context, 'pointcloud') and context.pointcloud and (engine in cls.COMPAT_ENGINES) + + +class DATA_PT_context_pointcloud(DataButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + + ob = context.object + pointcloud = context.pointcloud + space = context.space_data + + if ob: + layout.template_ID(ob, "data") + elif pointcloud: + layout.template_ID(space, "pin_id") + + +class DATA_PT_pointcloud(DataButtonsPanel, Panel): + bl_label = "Point Cloud" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + pointcloud = context.pointcloud + pass + +class DATA_PT_custom_props_pointcloud(DataButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + _context_path = "object.data" + _property_type = bpy.types.PointCloud if hasattr(bpy.types, "PointCloud") else None + + +classes = ( + DATA_PT_context_pointcloud, + DATA_PT_pointcloud, + DATA_PT_custom_props_pointcloud, +) + +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_shaderfx.py b/release/scripts/startup/bl_ui/properties_data_shaderfx.py index fce86446dfc..1d4bf37b282 100644 --- a/release/scripts/startup/bl_ui/properties_data_shaderfx.py +++ b/release/scripts/startup/bl_ui/properties_data_shaderfx.py @@ -56,27 +56,30 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): def FX_BLUR(self, layout, fx): - layout.prop(fx, "factor", text="Factor") + layout.prop(fx, "use_dof_mode", text="Use Depth of Field") + layout.separator() + + col = layout.column() + col.enabled = not fx.use_dof_mode + col.prop(fx, "size", text="Size") + col.separator() + col.prop(fx, "rotation") + 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': + if fx.mode == 'DUOTONE': layout.prop(fx, "low_color", text="Low Color") if fx.mode == 'CUSTOM': layout.prop(fx, "low_color", text="Color") - if fx.mode == 'BITONE': + if fx.mode == 'DUOTONE': layout.prop(fx, "high_color", text="High Color") - if fx.mode in {'BITONE', 'CUSTOM', 'TRANSPARENT'}: - layout.prop(fx, "factor") + layout.prop(fx, "factor") def FX_WAVE(self, layout, fx): row = layout.row(align=True) @@ -95,7 +98,7 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): layout.prop(fx, "rim_color") layout.prop(fx, "mask_color") - layout.prop(fx, "mode") + layout.prop(fx, "mode", text="Blend") layout.prop(fx, "blur") layout.prop(fx, "samples") @@ -111,7 +114,7 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): layout.prop(fx, "samples") layout.separator() - layout.prop(fx, "use_object", text="Use Object As Pivot") + layout.prop(fx, "use_object", text="Use Object as Pivot") if fx.use_object: row = layout.row() row.prop(fx, "object", text="Object") @@ -127,16 +130,21 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): def FX_GLOW(self, layout, fx): layout.prop(fx, "mode") - layout.prop(fx, "glow_color") if fx.mode == 'LUMINANCE': layout.prop(fx, "threshold") else: layout.prop(fx, "select_color") + layout.prop(fx, "glow_color") layout.separator() - layout.prop(fx, "radius") + layout.prop(fx, "blend_mode", text="Blend") + layout.prop(fx, "opacity") + + layout.prop(fx, "size") + layout.prop(fx, "rotation") layout.prop(fx, "samples") - layout.prop(fx, "use_alpha_mode", text="Use Alpha Mode") + + layout.prop(fx, "use_glow_under", text="Glow Under") def FX_SWIRL(self, layout, fx): layout.prop(fx, "object", text="Object") @@ -144,18 +152,10 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): layout.prop(fx, "radius") layout.prop(fx, "angle") - layout.prop(fx, "use_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, diff --git a/release/scripts/startup/bl_ui/properties_data_volume.py b/release/scripts/startup/bl_ui/properties_data_volume.py new file mode 100644 index 00000000000..b10bb808edd --- /dev/null +++ b/release/scripts/startup/bl_ui/properties_data_volume.py @@ -0,0 +1,174 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> +import bpy +from bpy.types import Panel, UIList +from rna_prop_ui import PropertyPanel + + +class DataButtonsPanel: + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @classmethod + def poll(cls, context): + engine = context.scene.render.engine + return context.volume and (engine in cls.COMPAT_ENGINES) + + +class DATA_PT_context_volume(DataButtonsPanel, Panel): + bl_label = "" + bl_options = {'HIDE_HEADER'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + + ob = context.object + volume = context.volume + space = context.space_data + + if ob: + layout.template_ID(ob, "data") + elif volume: + layout.template_ID(space, "pin_id") + + +class DATA_PT_volume_file(DataButtonsPanel, Panel): + bl_label = "OpenVDB File" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + + volume = context.volume + volume.grids.load() + + layout.prop(volume, "filepath", text="") + + if len(volume.filepath): + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column(align=True) + col.prop(volume, "is_sequence") + if volume.is_sequence: + col.prop(volume, "frame_duration", text="Frames") + col.prop(volume, "frame_start", text="Start") + col.prop(volume, "frame_offset", text="Offset") + col.prop(volume, "sequence_mode", text="Mode") + + error_msg = volume.grids.error_message + if len(error_msg): + layout.separator() + col = layout.column(align=True) + col.label(text="Failed to load volume:") + col.label(text=error_msg) + + +class VOLUME_UL_grids(UIList): + def draw_item(self, context, layout, data, grid, icon, active_data, active_propname, index): + name = grid.name + data_type = grid.bl_rna.properties['data_type'].enum_items[grid.data_type] + + layout.label(text=name) + row = layout.row() + row.alignment = 'RIGHT' + row.active = False + row.label(text=data_type.name) + + +class DATA_PT_volume_grids(DataButtonsPanel, Panel): + bl_label = "Grids" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + + volume = context.volume + volume.grids.load() + + layout.template_list("VOLUME_UL_grids", "grids", volume, "grids", volume.grids, "active_index", rows=3) + + +class DATA_PT_volume_render(DataButtonsPanel, Panel): + bl_label = "Render" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.scene + volume = context.volume + render = volume.render + + col = layout.column(align=True) + col.prop(render, "space") + + if scene.render.engine == 'CYCLES': + col.prop(render, "step_size") + + col = layout.column(align=True) + col.prop(render, "clipping") + + +class DATA_PT_volume_viewport_display(DataButtonsPanel, Panel): + bl_label = "Viewport Display" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + volume = context.volume + display = volume.display + + col = layout.column(align=True) + col.prop(display, "wireframe_type") + sub = col.row() + sub.active = display.wireframe_type in {'BOXES', 'POINTS'} + sub.prop(display, "wireframe_detail", text="Detail") + + layout.prop(display, "density") + + +class DATA_PT_custom_props_volume(DataButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + _context_path = "object.data" + _property_type = bpy.types.Volume + + +classes = ( + DATA_PT_context_volume, + DATA_PT_volume_grids, + DATA_PT_volume_file, + DATA_PT_volume_viewport_display, + DATA_PT_volume_render, + DATA_PT_custom_props_volume, + VOLUME_UL_grids, +) + +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_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index 5fc03e59f9d..f70789ebeed 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -126,6 +126,7 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel): row = layout.row() layout.prop(freestyle, "mode", text="Control Mode") layout.prop(freestyle, "use_view_map_cache", text="View Map Cache") + layout.prop(freestyle, "as_render_pass", text="As Render Pass") layout.label(text="Edge Detection Options:") split = layout.split() 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 2001f46820f..64eda42c87a 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -from bpy.types import Menu, UIList +from bpy.types import Menu, UIList, Operator from bpy.app.translations import pgettext_iface as iface_ @@ -44,38 +44,6 @@ def gpencil_stroke_placement_settings(context, layout): row.prop_enum(tool_settings, propname, 'CURSOR', text="Cursor") -def gpencil_active_brush_settings_simple(context, layout): - tool_settings = context.tool_settings - brush = tool_settings.gpencil_paint.brush - if brush is None: - layout.label(text="No Active Brush") - return - - col = layout.column() - col.label(text="Active Brush: ") - - row = col.row(align=True) - row.operator_context = 'EXEC_REGION_WIN' - row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA') - row.prop(brush, "name", text="") - - 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) - row.prop(brush, "use_pressure", text="", icon='STYLUS_PRESSURE') - row = col.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 = col.row(align=True) - row.prop(brush, "jitter", slider=True) - row.prop(brush, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') - row = col.row() - row.prop(brush, "angle", slider=True) - row.prop(brush, "angle_factor", text="Factor", slider=True) - - # XXX: To be replaced with active tools class AnnotationDrawingToolsPanel: # subclass must set @@ -108,11 +76,6 @@ class AnnotationDrawingToolsPanel: sub.operator("gpencil.blank_frame_add", icon='FILE_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_draw_additive", 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() @@ -121,12 +84,9 @@ class AnnotationDrawingToolsPanel: col.label(text="Data Source:") row = col.row(align=True) if is_3d_view: - row.prop(context.tool_settings, "grease_pencil_source", expand=True) + row.prop(context.tool_settings, "annotation_source", expand=True) elif is_clip_editor: - row.prop(context.space_data, "grease_pencil_source", expand=True) - - # col.separator() - # col.separator() + row.prop(context.space_data, "annotation_source", expand=True) gpencil_stroke_placement_settings(context, col) @@ -136,29 +96,33 @@ class GreasePencilSculptOptionsPanel: @classmethod def poll(cls, context): - settings = context.tool_settings.gpencil_sculpt - tool = settings.sculpt_tool + tool_settings = context.scene.tool_settings + settings = tool_settings.gpencil_sculpt_paint + brush = settings.brush + tool = brush.gpencil_sculpt_tool - return bool(tool in {'SMOOTH', 'RANDOMIZE', 'SMOOTH'}) + return bool(tool in {'SMOOTH', 'RANDOMIZE'}) 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.sculpt_tool + tool_settings = context.scene.tool_settings + settings = tool_settings.gpencil_sculpt_paint brush = settings.brush + gp_settings = brush.gpencil_settings + tool = brush.gpencil_sculpt_tool if tool in {'SMOOTH', 'RANDOMIZE'}: - layout.prop(settings, "use_edit_position", text="Affect Position") - layout.prop(settings, "use_edit_strength", text="Affect Strength") - layout.prop(settings, "use_edit_thickness", text="Affect Thickness") + layout.prop(gp_settings, "use_edit_position", text="Affect Position") + layout.prop(gp_settings, "use_edit_strength", text="Affect Strength") + layout.prop(gp_settings, "use_edit_thickness", text="Affect Thickness") if tool == 'SMOOTH': - layout.prop(brush, "use_edit_pressure") + layout.prop(gp_settings, "use_edit_pressure") - layout.prop(settings, "use_edit_uv", text="Affect UV") + layout.prop(gp_settings, "use_edit_uv", text="Affect UV") # GP Object Tool Settings @@ -174,7 +138,7 @@ class GreasePencilDisplayPanel: if context.mode == 'PAINT_GPENCIL': return brush.gpencil_tool != 'ERASE' else: - # GP Sculpt and Weight Paint always have Brush Tip panel. + # GP Sculpt, Vertex and Weight Paint always have Brush Tip panel. return True return False @@ -182,16 +146,18 @@ class GreasePencilDisplayPanel: if self.is_popover: return + tool_settings = context.tool_settings if context.mode == 'PAINT_GPENCIL': - brush = context.tool_settings.gpencil_paint.brush - gp_settings = brush.gpencil_settings - - self.layout.prop(gp_settings, "use_cursor", text="") - elif context.mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}: - settings = context.tool_settings.gpencil_sculpt - brush = settings.brush - - self.layout.prop(brush, "use_cursor", text="") + settings = tool_settings.gpencil_paint + elif context.mode == 'SCULPT_GPENCIL': + settings = tool_settings.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = tool_settings.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = tool_settings.gpencil_vertex_paint + brush = settings.brush + if brush: + self.layout.prop(settings, "show_brush", text="") def draw(self, context): layout = self.layout @@ -199,279 +165,101 @@ class GreasePencilDisplayPanel: layout.use_property_decorate = False tool_settings = context.tool_settings - ob = context.active_object + if context.mode == 'PAINT_GPENCIL': + settings = tool_settings.gpencil_paint + elif context.mode == 'SCULPT_GPENCIL': + settings = tool_settings.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = tool_settings.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = tool_settings.gpencil_vertex_paint + brush = settings.brush + gp_settings = brush.gpencil_settings + ob = context.active_object if ob.mode == 'PAINT_GPENCIL': - brush = tool_settings.gpencil_paint.brush - gp_settings = brush.gpencil_settings if self.is_popover: row = layout.row(align=True) - row.prop(gp_settings, "use_cursor", text="") + row.prop(settings, "show_brush", text="") row.label(text="Display Cursor") col = layout.column(align=True) - col.active = gp_settings.use_cursor + col.active = settings.show_brush if brush.gpencil_tool == 'DRAW': col.prop(gp_settings, "show_lasso", text="Show Fill Color While Drawing") - if brush.gpencil_tool == 'FILL': - col.prop(brush, "cursor_color_add", text="Cursor Color") - - elif ob.mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}: - settings = tool_settings.gpencil_sculpt - brush = settings.brush - tool = settings.sculpt_tool - - if self.is_popover: - row = layout.row(align=True) - row.prop(brush, "use_cursor", text="") - row.label(text="Display Cursor") - + elif ob.mode == 'SCULPT_GPENCIL': col = layout.column(align=True) - col.active = brush.use_cursor + col.active = settings.show_brush col.prop(brush, "cursor_color_add", text="Cursor Color") - if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}: - col.prop(brush, "cursor_color_sub", text="Inverse Cursor Color") - - -class GPENCIL_MT_pie_tool_palette(Menu): - """A pie menu for quick access to Grease Pencil tools""" - bl_label = "Grease Pencil Tools" - - def draw(self, context): - layout = self.layout - - pie = layout.menu_pie() - gpd = context.gpencil_data - - # W - Drawing Types - col = pie.column() - col.operator("gpencil.draw", text="Draw", icon='GREASEPENCIL').mode = 'DRAW' - col.operator("gpencil.draw", text="Straight Lines", icon='LINE_DATA').mode = 'DRAW_STRAIGHT' - col.operator("gpencil.draw", text="Poly", icon='MESH_DATA').mode = 'DRAW_POLY' - - # E - Eraser - # XXX: needs a dedicated icon... - col = pie.column() - col.operator("gpencil.draw", text="Eraser", icon='FORCE_CURVE').mode = 'ERASER' - - # E - "Settings" Palette is included here too, since it needs to be in a stable position... - if gpd and gpd.layers.active: - col.separator() - col.operator( - "wm.call_menu_pie", - text="Settings...", - icon='SCRIPTWIN').name = "GPENCIL_MT_pie_settings_palette" - - # Editing tools - if gpd: - if gpd.use_stroke_edit_mode and context.editable_gpencil_strokes: - # S - Exit Edit Mode - pie.operator("gpencil.editmode_toggle", text="Exit Edit Mode", icon='EDIT') - - # N - Transforms - col = pie.column() - row = col.row(align=True) - row.operator("transform.translate", icon='MAN_TRANS') - row.operator("transform.rotate", icon='MAN_ROT') - row.operator("transform.resize", text="Scale", icon='MAN_SCALE') - row = col.row(align=True) - row.label(text="Proportional Edit:") - row.prop(context.tool_settings, "use_proportional_edit", text="", icon_only=True) - row.prop(context.tool_settings, "proportional_edit_falloff", text="", icon_only=True) - - # NW - Select (Non-Modal) - col = pie.column() - col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT') - col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1') - col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED') - col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR') - - # NE - Select (Modal) - col = pie.column() - col.operator("gpencil.select_box", text="Box 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() - col.operator("gpencil.duplicate_move", icon='PARTICLE_PATH', text="Duplicate") - col.operator("gpencil.delete", icon='X', text="Delete...") - - # SE - More Tools - pie.operator("wm.call_menu_pie", text="More...").name = "GPENCIL_MT_pie_tools_more" - else: - # Toggle Edit Mode - pie.operator("gpencil.editmode_toggle", text="Enable Stroke Editing", icon='EDIT') + if brush.gpencil_sculpt_tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}: + col.prop(brush, "cursor_color_subtract", text="Inverse Cursor Color") + elif ob.mode == 'WEIGHT_GPENCIL': + col = layout.column(align=True) + col.active = settings.show_brush -class GPENCIL_MT_pie_settings_palette(Menu): - """A pie menu for quick access to Grease Pencil settings""" - bl_label = "Grease Pencil Settings" + col.prop(brush, "cursor_color_add", text="Cursor Color") - @classmethod - def poll(cls, context): - return bool(context.gpencil_data and context.active_gpencil_layer) + elif ob.mode == 'VERTEX_GPENCIL': + row = layout.row(align=True) + row.prop(settings, "show_brush", text="") + row.label(text="Display Cursor") - def draw(self, context): - layout = self.layout - pie = layout.menu_pie() - gpd = context.gpencil_data - gpl = context.active_gpencil_layer - palcolor = None # context.active_gpencil_palettecolor - - is_editmode = bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes) - - # W - Stroke draw settings - col = pie.column(align=True) - if palcolor is not None: - col.enabled = not palcolor.lock - col.label(text="Stroke") - col.prop(palcolor, "color", text="") - col.prop(palcolor, "alpha", text="", slider=True) - - # E - Fill draw settings - col = pie.column(align=True) - if palcolor is not None: - col.enabled = not palcolor.lock - col.label(text="Fill") - col.prop(palcolor, "fill_color", text="") - col.prop(palcolor, "fill_alpha", text="", slider=True) - - # S Brush settings - gpencil_active_brush_settings_simple(context, pie) - - # N - Active Layer - col = pie.column() - col.label(text="Active Layer: ") - - row = col.row() - row.operator_context = 'EXEC_REGION_WIN' - row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL') - row.prop(gpl, "info", text="") - row.operator("gpencil.layer_remove", text="", icon='X') - - row = col.row() - row.prop(gpl, "lock") - row.prop(gpl, "hide") - col.prop(gpl, "use_onion_skinning") - - # NW/NE/SW/SE - These operators are only available in editmode - # as they require strokes to be selected to work - if is_editmode: - # NW - Move stroke Down - col = pie.column(align=True) - col.label(text="Arrange Strokes") - col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM' - col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN' - - # NE - Move stroke Up - col = pie.column(align=True) - col.label(text="Arrange Strokes") - col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP' - col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP' - - # SW - Move stroke to color - col = pie.column(align=True) - col.operator("gpencil.stroke_change_color", text="Move to Color") - - # SE - Join strokes - col = pie.column(align=True) - col.label(text="Join Strokes") - row = col.row() - row.operator("gpencil.stroke_join", text="Join").type = 'JOIN' - row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' - col.operator("gpencil.stroke_flip", text="Flip Direction") - - col.prop(gpd, "show_stroke_direction", text="Show Drawing Direction") - - -class GPENCIL_MT_pie_tools_more(Menu): - """A pie menu for accessing more Grease Pencil tools""" - bl_label = "More Grease Pencil Tools" +class GreasePencilBrushFalloff: + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): - gpd = context.gpencil_data - return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes) - - def draw(self, _context): - layout = self.layout - - pie = layout.menu_pie() - # gpd = context.gpencil_data - - col = pie.column(align=True) - col.operator("gpencil.copy", text="Copy", icon='COPYDOWN') - col.operator("gpencil.paste", text="Paste", icon='PASTEDOWN').type = 'ACTIVE' - col.operator("gpencil.paste", text="Paste by Layer").type = 'LAYER' - - col = pie.column(align=True) - col.operator("gpencil.select_more", icon='ADD') - col.operator("gpencil.select_less", icon='REMOVE') - - pie.operator("transform.mirror", icon='MOD_MIRROR') - pie.operator("transform.bend", icon='MOD_SIMPLEDEFORM') - pie.operator("transform.shear", icon='MOD_TRIANGULATE') - pie.operator("transform.tosphere", icon='MOD_MULTIRES') - - pie.operator("gpencil.convert", icon='OUTLINER_OB_CURVE', text="Convert...") - pie.operator("wm.call_menu_pie", text="Back to Main Palette...").name = "GPENCIL_MT_pie_tool_palette" - + ts = context.tool_settings + settings = None + if context.mode == 'PAINT_GPENCIL': + settings = ts.gpencil_paint + if context.mode == 'SCULPT_GPENCIL': + settings = ts.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = ts.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = ts.gpencil_vertex_paint -class GPENCIL_MT_pie_sculpt(Menu): - """A pie menu for accessing Grease Pencil stroke sculpt settings""" - bl_label = "Grease Pencil Sculpt" - - @classmethod - def poll(cls, context): - gpd = context.gpencil_data - return bool(gpd and gpd.use_stroke_edit_mode and context.editable_gpencil_strokes) + return (settings and settings.brush and settings.brush.curve) def draw(self, context): layout = self.layout + ts = context.tool_settings + settings = None + if context.mode == 'PAINT_GPENCIL': + settings = ts.gpencil_paint + if context.mode == 'SCULPT_GPENCIL': + settings = ts.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = ts.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = ts.gpencil_vertex_paint + + if settings: + brush = settings.brush - pie = layout.menu_pie() - - settings = context.tool_settings.gpencil_sculpt - brush = settings.brush - - # W - Launch Sculpt Mode - col = pie.column() - # col.label(text="Tool:") - col.prop(settings, "sculpt_tool", text="") - col.operator("gpencil.sculpt_paint", text="Sculpt", icon='SCULPTMODE_HLT') - - # E - Common Settings - col = pie.column(align=True) - col.prop(brush, "size", slider=True) - row = col.row(align=True) - row.prop(brush, "strength", slider=True) - # row.prop(brush, "use_pressure_strength", text="", icon_only=True) - col.prop(brush, "use_falloff") - if settings.sculpt_tool in {'SMOOTH', 'RANDOMIZE'}: + col = layout.column(align=True) row = col.row(align=True) - row.prop(settings, "use_edit_position", text="Position", icon='MESH_DATA', toggle=True) - row.prop(settings, "use_edit_strength", text="Strength", icon='COLOR', toggle=True) - row.prop(settings, "use_edit_thickness", text="Thickness", icon='LINE_DATA', toggle=True) + row.prop(brush, "curve_preset", text="") - # S - Change Brush Type Shortcuts - row = pie.row() - row.prop_enum(settings, "tool", value='GRAB') - row.prop_enum(settings, "tool", value='PUSH') - row.prop_enum(settings, "tool", value='CLONE') + if brush.curve_preset == 'CUSTOM': + layout.template_curve_mapping(brush, "curve", brush=True) - # N - Change Brush Type Shortcuts - row = pie.row() - row.prop_enum(settings, "tool", value='SMOOTH') - row.prop_enum(settings, "tool", value='THICKNESS') - row.prop_enum(settings, "tool", value='STRENGTH') - row.prop_enum(settings, "tool", value='RANDOMIZE') + col = layout.column(align=True) + row = col.row(align=True) + row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' class GPENCIL_MT_snap(Menu): @@ -512,9 +300,64 @@ class GPENCIL_MT_move_to_layer(Menu): layout.separator() + layout.operator("gpencil.move_to_layer", text="New Layer", icon='ADD').layer = -1 + + +class GPENCIL_MT_layer_active(Menu): + bl_label = "Change Active Layer" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + + gpd = context.gpencil_data + if gpd: + gpl_active = context.active_gpencil_layer + tot_layers = len(gpd.layers) + i = tot_layers - 1 + while i >= 0: + gpl = gpd.layers[i] + if gpl.info == gpl_active.info: + icon = 'GREASEPENCIL' + else: + icon = 'NONE' + layout.operator("gpencil.layer_active", text=gpl.info, icon=icon).layer = i + i -= 1 + + layout.separator() + layout.operator("gpencil.layer_add", text="New Layer", icon='ADD') +class GPENCIL_MT_material_active(Menu): + bl_label = "Change Active Material" + + @classmethod + def poll(cls, context): + ob = context.active_object + tool_settings = context.scene.tool_settings + mode = tool_settings.gpencil_paint.color_mode + if mode != 'MATERIAL': + return False + + if ob is None or len(ob.material_slots) == 0: + return False + + return True + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + ob = context.active_object + mat_active = ob.active_material + + for slot in ob.material_slots: + mat = slot.material + if mat: + icon = mat.id_data.preview.icon_id + layout.operator("gpencil.material_set", text=mat.name, icon_value=icon).slot = mat.name + + class GPENCIL_MT_gpencil_draw_delete(Menu): bl_label = "Delete" @@ -602,7 +445,7 @@ class AnnotationDataPanel: # Owner selector. if context.space_data.type == 'CLIP_EDITOR': - layout.row().prop(context.space_data, "grease_pencil_source", expand=True) + layout.row().prop(context.space_data, "annotation_source", expand=True) layout.template_ID(gpd_owner, "grease_pencil", new="gpencil.annotation_add", unlink="gpencil.data_unlink") @@ -674,7 +517,7 @@ class AnnotationOnionSkin: if gpl is None: return False - return True + return True def draw_header(self, context): gpl = context.active_annotation_layer @@ -702,30 +545,6 @@ class AnnotationOnionSkin: sub.prop(gpl, "annotation_onion_after_range", text="After") -class GreasePencilToolsPanel: - # For use in "2D" Editors without their own toolbar - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Grease Pencil Settings" - bl_region_type = 'UI' - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, _context): - # XXX - disabled in 2.8 branch. - # return (context.gpencil_data is not None) - return False - - def draw(self, context): - layout = self.layout - - gpencil_active_brush_settings_simple(context, layout) - - layout.separator() - - gpencil_stroke_placement_settings(context, layout) - - class GreasePencilMaterialsPanel: # Mix-in, use for properties editor and top-bar. def draw(self, context): @@ -759,7 +578,7 @@ class GreasePencilMaterialsPanel: col.separator() - col.menu("GPENCIL_MT_color_context_menu", icon='DOWNARROW_HLT', text="") + col.menu("GPENCIL_MT_material_context_menu", icon='DOWNARROW_HLT', text="") if is_sortable: col.separator() @@ -770,8 +589,8 @@ class GreasePencilMaterialsPanel: col.separator() sub = col.column(align=True) - sub.operator("gpencil.color_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True - sub.operator("gpencil.color_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.material_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True + sub.operator("gpencil.material_isolate", icon='LOCKED', text="").affect_visibility = False if show_full_ui: row = layout.row() @@ -786,8 +605,8 @@ class GreasePencilMaterialsPanel: if ob.data.use_stroke_edit_mode: row = layout.row(align=True) row.operator("gpencil.stroke_change_color", text="Assign") - row.operator("gpencil.color_select", text="Select").deselect = False - row.operator("gpencil.color_select", text="Deselect").deselect = True + row.operator("gpencil.material_select", text="Select").deselect = False + row.operator("gpencil.material_select", text="Deselect").deselect = True # stroke color ma = None if is_view3d and brush is not None: @@ -800,11 +619,7 @@ class GreasePencilMaterialsPanel: if ma is not None and ma.grease_pencil is not None: gpcolor = ma.grease_pencil - if ( - gpcolor.stroke_style == 'SOLID' or - gpcolor.use_stroke_pattern or - gpcolor.use_stroke_texture_mix - ): + if gpcolor.stroke_style == 'SOLID': row = layout.row() row.prop(gpcolor, "color", text="Stroke Color") @@ -813,6 +628,44 @@ class GreasePencilMaterialsPanel: row.template_ID(space, "pin_id") +class GreasePencilVertexcolorPanel: + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + ts = context.scene.tool_settings + is_vertex = context.mode == 'VERTEX_GPENCIL' + gpencil_paint = ts.gpencil_vertex_paint if is_vertex else ts.gpencil_paint + brush = gpencil_paint.brush + gp_settings = brush.gpencil_settings + tool = brush.gpencil_vertex_tool if is_vertex else brush.gpencil_tool + + ob = context.object + + if ob: + col = layout.column() + col.template_color_picker(brush, "color", value_slider=True) + + sub_row = layout.row(align=True) + sub_row.prop(brush, "color", text="") + sub_row.prop(brush, "secondary_color", text="") + + sub_row.operator("gpencil.tint_flip", icon='FILE_REFRESH', text="") + + row = layout.row(align=True) + row.template_ID(gpencil_paint, "palette", new="palette.new") + if gpencil_paint.palette: + layout.template_palette(gpencil_paint, "palette", color=True) + + if tool in {'DRAW', 'FILL'} and is_vertex is False: + row = layout.row(align=True) + row.prop(gp_settings, "vertex_mode", text="Mode") + row = layout.row(align=True) + row.prop(gp_settings, "vertex_color_factor", slider=True, text="Mix Factor") + + 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) @@ -830,9 +683,10 @@ class GPENCIL_UL_layer(UIList): row.prop(gpl, "info", text="", emboss=False) row = layout.row(align=True) - row.prop(gpl, "mask_layer", text="", - icon='MOD_MASK' if gpl.mask_layer else 'LAYER_ACTIVE', - emboss=False) + + icon_mask = 'MOD_MASK' if gpl.use_mask_layer else 'LAYER_ACTIVE' + + row.prop(gpl, "use_mask_layer", text="", icon=icon_mask, emboss=False) subrow = row.row(align=True) subrow.prop( @@ -868,16 +722,12 @@ class GreasePencilSimplifyPanel: layout.active = rd.simplify_gpencil col = layout.column() - col.prop(rd, "simplify_gpencil_onplay", text="Playback Only") - col.prop(rd, "simplify_gpencil_view_modifier", text="Modifiers") - col.prop(rd, "simplify_gpencil_shader_fx", text="ShaderFX") - col.prop(rd, "simplify_gpencil_blend", text="Layers Blending") - col.prop(rd, "simplify_gpencil_tint", text="Layers Tinting") - + col.prop(rd, "simplify_gpencil_onplay") col.prop(rd, "simplify_gpencil_view_fill") - sub = col.column() - sub.active = rd.simplify_gpencil_view_fill - sub.prop(rd, "simplify_gpencil_remove_lines", text="Lines") + col.prop(rd, "simplify_gpencil_modifier") + col.prop(rd, "simplify_gpencil_shader_fx") + col.prop(rd, "simplify_gpencil_tint") + col.prop(rd, "simplify_gpencil_antialiasing") class GreasePencilLayerAdjustmentsPanel: @@ -913,6 +763,65 @@ class GreasePencilLayerAdjustmentsPanel: col.prop(gpl, "lock_material") +class GPENCIL_UL_masks(UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + mask = item + if self.layout_type in {'DEFAULT', 'COMPACT'}: + row = layout.row(align=True) + row.prop(mask, "name", text="", emboss=False, icon_value=icon) + row.prop(mask, "invert", text="", emboss=False) + row.prop(mask, "hide", text="", emboss=False) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.prop(mask, "name", text="", emboss=False, icon_value=icon) + + +class GPENCIL_MT_layer_mask_menu(Menu): + bl_label = "Layer Specials" + + def draw(self, context): + layout = self.layout + ob = context.object + gpd = ob.data + gpl_active = gpd.layers.active + done = False + for gpl in gpd.layers: + if gpl != gpl_active and gpl.info not in gpl_active.mask_layers: + done = True + layout.operator("gpencil.layer_mask_add", text=gpl.info).name=gpl.info + + if done is False: + layout.label(text="No layers to add") + + +class GreasePencilLayerMasksPanel: + def draw_header(self, context): + ob = context.active_object + gpd = ob.data + gpl = gpd.layers.active + + self.layout.prop(gpl, "use_mask_layer", text="") + + def draw(self, context): + ob = context.active_object + gpd = ob.data + gpl = gpd.layers.active + + layout = self.layout + layout.enabled = gpl.use_mask_layer + + if gpl: + rows = 4 + row = layout.row() + col = row.column() + col.template_list("GPENCIL_UL_masks", "", gpl, "mask_layers", gpl.mask_layers, + "active_mask_index", rows=rows, sort_lock=True) + + col2 = row.column(align=True) + col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="") + col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="") + + class GreasePencilLayerRelationsPanel: def draw(self, context): @@ -952,20 +861,59 @@ class GreasePencilLayerDisplayPanel: col.prop(gpl, "use_solo_mode", text="Show Only On Keyframed") -classes = ( - GPENCIL_MT_pie_tool_palette, - GPENCIL_MT_pie_settings_palette, - GPENCIL_MT_pie_tools_more, - GPENCIL_MT_pie_sculpt, +class GreasePencilFlipTintColors(Operator): + bl_label = "Flip Colors" + bl_idname = "gpencil.tint_flip" + bl_description = "Switch Tint colors" + def execute(self, context): + try: + ts = context.tool_settings + settings = None + if context.mode == 'PAINT_GPENCIL': + settings = ts.gpencil_paint + if context.mode == 'SCULPT_GPENCIL': + settings = ts.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = ts.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = ts.gpencil_vertex_paint + + brush = settings.brush + if brush is not None: + color = brush.color + secondary_color = brush.secondary_color + + orig_prim = color.hsv + orig_sec = secondary_color.hsv + + color.hsv = orig_sec + secondary_color.hsv = orig_prim + + return {'FINISHED'} + + except Exception as e: + utils_core.error_handlers(self, "gpencil.tint_flip", e, + "Flip Colors could not be completed") + + return {'CANCELLED'} + + +classes = ( GPENCIL_MT_snap, GPENCIL_MT_cleanup, GPENCIL_MT_move_to_layer, + GPENCIL_MT_layer_active, + GPENCIL_MT_material_active, GPENCIL_MT_gpencil_draw_delete, + GPENCIL_MT_layer_mask_menu, GPENCIL_UL_annotation_layer, GPENCIL_UL_layer, + GPENCIL_UL_masks, + + GreasePencilFlipTintColors, ) 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 0849437b680..6aaec9940e8 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -74,7 +74,7 @@ class MATERIAL_PT_preview(MaterialButtonsPanel, Panel): class MATERIAL_PT_custom_props(MaterialButtonsPanel, PropertyPanel, Panel): - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} _context_path = "material" _property_type = bpy.types.Material @@ -172,10 +172,11 @@ class EEVEE_MATERIAL_PT_surface(MaterialButtonsPanel, Panel): layout.prop(mat, "use_nodes", icon='NODETREE') layout.separator() + layout.use_property_split = True + if mat.use_nodes: panel_node_draw(layout, mat.node_tree, 'OUTPUT_MATERIAL', "Surface") else: - layout.use_property_split = True layout.prop(mat, "diffuse_color", text="Base Color") layout.prop(mat, "metallic") layout.prop(mat, "specular_intensity", text="Specular") @@ -197,6 +198,8 @@ class EEVEE_MATERIAL_PT_volume(MaterialButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + mat = context.material panel_node_draw(layout, mat.node_tree, 'OUTPUT_MATERIAL', "Volume") diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py index 4f419ec1f18..5d10a2cef4a 100644 --- a/release/scripts/startup/bl_ui/properties_material_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py @@ -27,26 +27,31 @@ from bl_ui.properties_grease_pencil_common import ( ) -class GPENCIL_MT_color_context_menu(Menu): +class GPENCIL_MT_material_context_menu(Menu): bl_label = "Material Specials" 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.operator("gpencil.material_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.material_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.operator("gpencil.material_lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.material_unlock_all", icon='UNLOCKED', text="UnLock All") - layout.operator("gpencil.stroke_lock_color", text="Lock Unselected") + layout.operator("gpencil.material_lock_unused", text="Lock Unselected") layout.operator("gpencil.lock_layer", text="Lock Unused") layout.separator() layout.operator("object.material_slot_remove_unused") + layout.operator("gpencil.stroke_merge_material", text="Merge Similar") + + layout.separator() + layout.operator("gpencil.material_to_vertex_color", text="Convert Materials to Vertex Color") + layout.operator("gpencil.extract_palette_vertex", text="Extract Palette from Vertex Color") class GPENCIL_UL_matslots(UIList): @@ -142,27 +147,25 @@ class MATERIAL_PT_gpencil_strokecolor(GPMaterialButtonsPanel, Panel): col.prop(gpcolor, "stroke_style", text="Style") + row = col.row() + row.prop(gpcolor, "color", text="Base Color") + 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") + + if gpcolor.stroke_style == 'TEXTURE': + row = col.row() + row.prop(gpcolor, "mix_stroke_factor", text="Blend", slider=True) if gpcolor.mode == 'LINE': col.prop(gpcolor, "pixel_size", text="UV Factor") - col.prop(gpcolor, "use_stroke_pattern", text="Use As Stencil Mask") - if gpcolor.use_stroke_pattern is False: - col.prop(gpcolor, "use_stroke_texture_mix", text="Mix Color") - if gpcolor.use_stroke_texture_mix is True: - col.prop(gpcolor, "mix_stroke_factor", text="Factor") - - if (gpcolor.stroke_style == 'SOLID' or gpcolor.use_stroke_pattern or gpcolor.use_stroke_texture_mix): - col.prop(gpcolor, "color", text="Color") - if gpcolor.mode in {'DOTS', 'BOX'}: col.prop(gpcolor, "alignment_mode") - if gpcolor.mode == 'LINE' and gpcolor.stroke_style != 'TEXTURE': + if gpcolor.mode == 'LINE': col.prop(gpcolor, "use_overlap_strokes") @@ -188,55 +191,37 @@ class MATERIAL_PT_gpencil_fillcolor(GPMaterialButtonsPanel, Panel): col.enabled = 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 == 'SOLID': + col.prop(gpcolor, "fill_color", text="Base Color") - if gpcolor.fill_style in {'GRADIENT', 'CHECKER'}: - col.prop(gpcolor, "mix_color", text="Secondary Color") + elif gpcolor.fill_style == 'GRADIENT': + col.prop(gpcolor, "gradient_type") - if gpcolor.fill_style == 'GRADIENT': - col.prop(gpcolor, "mix_factor", text="Mix Factor", slider=True) + col.prop(gpcolor, "fill_color", text="Base Color") + col.prop(gpcolor, "mix_color", text="Secondary Color") + col.prop(gpcolor, "mix_factor", text="Blend", slider=True) + col.prop(gpcolor, "flip", text="Flip Colors") - if gpcolor.fill_style in {'GRADIENT', 'CHECKER'}: - col.prop(gpcolor, "flip", text="Flip Colors") + col.prop(gpcolor, "texture_offset", text="Location") - col.prop(gpcolor, "pattern_shift", text="Location") - col.prop(gpcolor, "pattern_scale", text="Scale") + row = col.row() + row.enabled = gpcolor.gradient_type == 'LINEAR' + row.prop(gpcolor, "texture_angle", text="Rotation") - if gpcolor.gradient_type == 'RADIAL' and gpcolor.fill_style not in {'SOLID', 'CHECKER'}: - col.prop(gpcolor, "pattern_radius", text="Radius") - else: - if gpcolor.fill_style != 'SOLID': - col.prop(gpcolor, "pattern_angle", text="Angle") + col.prop(gpcolor, "texture_scale", text="Scale") - if gpcolor.fill_style == 'CHECKER': - col.prop(gpcolor, "pattern_gridsize", text="Box Size") + elif gpcolor.fill_style == 'TEXTURE': + col.prop(gpcolor, "fill_color", text="Base Color") - # Texture - if gpcolor.fill_style == 'TEXTURE' or (gpcolor.use_fill_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 Stencil Mask") - if gpcolor.use_fill_pattern is True: - col.prop(gpcolor, "fill_color", text="Color") + col.prop(gpcolor, "mix_factor", text="Blend", slider=True) - col.prop(gpcolor, "texture_offset", text="Offset") + col.prop(gpcolor, "texture_offset", text="Location") + col.prop(gpcolor, "texture_angle", text="Rotation") 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, "use_fill_texture_mix", text="Mix with Color") - - if gpcolor.use_fill_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" @@ -278,7 +263,7 @@ class MATERIAL_PT_gpencil_material_presets(PresetPanel, Panel): classes = ( GPENCIL_UL_matslots, - GPENCIL_MT_color_context_menu, + GPENCIL_MT_material_context_menu, MATERIAL_PT_gpencil_slots, MATERIAL_PT_gpencil_preview, MATERIAL_PT_gpencil_material_presets, @@ -291,5 +276,6 @@ classes = ( 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_object.py b/release/scripts/startup/bl_ui/properties_object.py index bb020084b03..8ce53ed30eb 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -212,45 +212,31 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel): layout = self.layout layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) obj = context.object obj_type = obj.type - is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'}) + is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'}) is_wire = (obj_type in {'CAMERA', 'EMPTY'}) is_empty_image = (obj_type == 'EMPTY' and obj.empty_display_type == 'IMAGE') is_dupli = (obj.instance_type != 'NONE') is_gpencil = (obj_type == 'GPENCIL') - col = flow.column() + col = layout.column(heading="Show") col.prop(obj, "show_name", text="Name") - - col = flow.column() col.prop(obj, "show_axis", text="Axis") # Makes no sense for cameras, armatures, etc.! # but these settings do apply to dupli instances if is_geometry or is_dupli: - col = flow.column() col.prop(obj, "show_wire", text="Wireframe") if obj_type == 'MESH' or is_dupli: - col = flow.column() col.prop(obj, "show_all_edges", text="All Edges") - - col = flow.column() if is_geometry: col.prop(obj, "show_texture_space", text="Texture Space") - col = flow.column() col.prop(obj.display, "show_shadows", text="Shadow") - - col = flow.column() col.prop(obj, "show_in_front", text="In Front") # if obj_type == 'MESH' or is_empty_image: # col.prop(obj, "show_transparent", text="Transparency") - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() if is_wire: # wire objects only use the max. display type for duplis col.active = is_dupli @@ -258,28 +244,17 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel): if is_geometry or is_dupli or is_empty_image or is_gpencil: # Only useful with object having faces/materials... - col = flow.column() col.prop(obj, "color") - -class OBJECT_PT_display_bounds(ObjectButtonsPanel, Panel): - bl_label = "Bounds" - bl_parent_id = "OBJECT_PT_display" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - - obj = context.object - - self.layout.prop(obj, "show_bounds", text="") - - def draw(self, context): - layout = self.layout - obj = context.object - layout.use_property_split = True - - layout.active = obj.show_bounds or (obj.display_type == 'BOUNDS') - layout.prop(obj, "display_bounds_type", text="Shape") + col = layout.column(align=False, heading="Bounds") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(obj, "show_bounds", text="") + sub = sub.row(align=True) + sub.active = obj.show_bounds or (obj.display_type == 'BOUNDS') + sub.prop(obj, "display_bounds_type", text="") + row.prop_decorator(obj, "display_bounds_type") class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): @@ -295,7 +270,6 @@ class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): row.prop(ob, "instance_type", expand=True) layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) if ob.instance_type == 'VERTS': layout.prop(ob, "use_instance_vertices_rotation", text="Align to Vertex Normal") @@ -305,9 +279,9 @@ class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): col.prop(ob, "instance_collection", text="Collection") if ob.instance_type != 'NONE' or ob.particle_systems: - col = flow.column(align=True) - col.prop(ob, "show_instancer_for_viewport") - col.prop(ob, "show_instancer_for_render") + col = layout.column(heading="Show Instancer", align=True) + col.prop(ob, "show_instancer_for_viewport", text="Viewport") + col.prop(ob, "show_instancer_for_render", text="Render") class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel): @@ -385,16 +359,18 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel): layout = self.layout layout.use_property_split = True - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) layout = self.layout ob = context.object - col = flow.column() - col.prop(ob, "hide_viewport", text="Show in Viewports", toggle=False, invert_checkbox=True) - col = flow.column() - col.prop(ob, "hide_render", text="Show in Renders", toggle=False, invert_checkbox=True) - col = flow.column() - col.prop(ob, "hide_select", text="Selectable", toggle=False, invert_checkbox=True) + layout.prop(ob, "hide_select", text="Selectable", toggle=False, invert_checkbox=True) + + col = layout.column(heading="Show in") + col.prop(ob, "hide_viewport", text="Viewports", toggle=False, invert_checkbox=True) + col.prop(ob, "hide_render", text="Renders", toggle=False, invert_checkbox=True) + + if context.object.type == 'GPENCIL': + col = layout.column(heading="Grease Pencil") + col.prop(ob, "use_grease_pencil_lights", toggle=False) class OBJECT_PT_custom_props(ObjectButtonsPanel, PropertyPanel, Panel): @@ -415,7 +391,6 @@ classes = ( OBJECT_PT_motion_paths, OBJECT_PT_motion_paths_display, OBJECT_PT_display, - OBJECT_PT_display_bounds, OBJECT_PT_visibility, OBJECT_PT_custom_props, ) diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py index 748961bb40f..e859798c085 100644 --- a/release/scripts/startup/bl_ui/properties_output.py +++ b/release/scripts/startup/bl_ui/properties_output.py @@ -94,14 +94,14 @@ class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel): return args @staticmethod - def draw_framerate(layout, sub, rd): + def draw_framerate(layout, rd): if RENDER_PT_dimensions._preset_class is None: RENDER_PT_dimensions._preset_class = bpy.types.RENDER_MT_framerate_presets args = rd.fps, rd.fps_base, RENDER_PT_dimensions._preset_class.bl_label fps_label_text, show_framerate = RENDER_PT_dimensions._draw_framerate_label(*args) - sub.menu("RENDER_MT_framerate_presets", text=fps_label_text) + layout.menu("RENDER_MT_framerate_presets", text=fps_label_text) if show_framerate: col = layout.column(align=True) @@ -136,10 +136,8 @@ class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel): col.prop(scene, "frame_end", text="End") col.prop(scene, "frame_step", text="Step") - col = layout.split() - col.alignment = 'RIGHT' - col.label(text="Frame Rate") - self.draw_framerate(layout, col, rd) + col = layout.column(heading="Frame Rate") + self.draw_framerate(col, rd) class RENDER_PT_frame_remapping(RenderOutputButtonsPanel, Panel): @@ -171,10 +169,8 @@ class RENDER_PT_post_processing(RenderOutputButtonsPanel, Panel): rd = context.scene.render - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - col = flow.column() + col = layout.column(heading="Pipeline") col.prop(rd, "use_compositing") - col = flow.column() col.prop(rd, "use_sequencer") layout.prop(rd, "dither_intensity", text="Dither", slider=True) @@ -192,44 +188,23 @@ class RENDER_PT_stamp(RenderOutputButtonsPanel, Panel): rd = context.scene.render - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + if rd.use_sequencer: + layout.prop(rd, "metadata_input") - col = flow.column() + col = layout.column(heading="Include") col.prop(rd, "use_stamp_date", text="Date") - col = flow.column() col.prop(rd, "use_stamp_time", text="Time") - - col = flow.column() col.prop(rd, "use_stamp_render_time", text="Render Time") - col = flow.column() col.prop(rd, "use_stamp_frame", text="Frame") - col = flow.column() col.prop(rd, "use_stamp_frame_range", text="Frame Range") - col = flow.column() col.prop(rd, "use_stamp_memory", text="Memory") - col = flow.column() col.prop(rd, "use_stamp_hostname", text="Hostname") - - col = flow.column() col.prop(rd, "use_stamp_camera", text="Camera") - col = flow.column() col.prop(rd, "use_stamp_lens", text="Lens") - - col = flow.column() col.prop(rd, "use_stamp_scene", text="Scene") - col = flow.column() col.prop(rd, "use_stamp_marker", text="Marker") - - col = flow.column() col.prop(rd, "use_stamp_filename", text="Filename") - col = flow.column() - col.prop(rd, "use_stamp_sequencer_strip", text="Strip Name") - - if rd.use_sequencer: - col = flow.column() - col.prop(rd, "use_stamp_strip_meta", text="Use Strip Metadata") - class RENDER_PT_stamp_note(RenderOutputButtonsPanel, Panel): bl_label = "Note" @@ -293,21 +268,17 @@ class RENDER_PT_output(RenderOutputButtonsPanel, Panel): layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() - col.active = not rd.is_movie_format - col.prop(rd, "use_overwrite") - col = flow.column() - col.active = not rd.is_movie_format - col.prop(rd, "use_placeholder") - col = flow.column() + col = layout.column(heading="Saving") col.prop(rd, "use_file_extension") - col = flow.column() col.prop(rd, "use_render_cache") layout.template_image_settings(image_settings, color_management=False) + if not rd.is_movie_format: + col = layout.column(heading="Image Sequence") + col.prop(rd, "use_overwrite") + col.prop(rd, "use_placeholder") + class RENDER_PT_output_views(RenderOutputButtonsPanel, Panel): bl_label = "Views" diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index a9e8cae3c8b..92d421f63a8 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -89,8 +89,12 @@ class UnifiedPaintPanel: # Grease Pencil settings elif mode == 'PAINT_GPENCIL': return tool_settings.gpencil_paint - elif mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}: - return tool_settings.gpencil_sculpt + elif mode == 'SCULPT_GPENCIL': + return tool_settings.gpencil_sculpt_paint + elif mode == 'WEIGHT_GPENCIL': + return tool_settings.gpencil_weight_paint + elif mode == 'VERTEX_GPENCIL': + return tool_settings.gpencil_vertex_paint return None @staticmethod @@ -211,7 +215,7 @@ class ClonePanel(BrushPanel): settings = cls.paint_settings(context) mode = cls.get_brush_mode(context) - if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + if mode == 'PAINT_TEXTURE': brush = settings.brush return brush.image_tool == 'CLONE' return False @@ -536,6 +540,7 @@ def brush_settings(layout, context, brush, popover=False): # normal_radius_factor layout.prop(brush, "normal_radius_factor", slider=True) + layout.prop(brush, "hardness", slider=True) # auto_smooth_factor and use_inverse_smooth_pressure if capabilities.has_auto_smooth: @@ -582,10 +587,12 @@ def brush_settings(layout, context, brush, popover=False): slider=True, ) - layout.prop(brush, "use_plane_trim", text="Plane Trim") - row = layout.row() - row.active = brush.use_plane_trim - row.prop(brush, "plane_trim", slider=True, text="Distance") + row = layout.row(heading="Plane Trim") + row.prop(brush, "use_plane_trim", text="") + sub = row.row() + sub.active = brush.use_plane_trim + sub.prop(brush, "plane_trim", slider=True, text="") + layout.separator() # height @@ -595,19 +602,14 @@ def brush_settings(layout, context, brush, popover=False): # use_persistent, set_persistent_base if capabilities.has_persistence: ob = context.sculpt_object - do_persistent = True - - # not supported yet for this case - for md in ob.modifiers: - if md.type == 'MULTIRES': - do_persistent = False - break + layout.separator() + layout.prop(brush, "use_persistent") + layout.operator("sculpt.set_persistent_base") + layout.separator() - if do_persistent: - layout.separator() - layout.prop(brush, "use_persistent") - layout.operator("sculpt.set_persistent_base") - layout.separator() + if brush.sculpt_tool == 'CLAY_STRIPS': + row = layout.row() + row.prop(brush, "tip_roundness") if brush.sculpt_tool == 'ELASTIC_DEFORM': layout.separator() @@ -617,17 +619,35 @@ def brush_settings(layout, context, brush, popover=False): if brush.sculpt_tool == 'POSE': layout.separator() + layout.prop(brush, "pose_origin_type") layout.prop(brush, "pose_offset") layout.prop(brush, "pose_smooth_iterations") layout.prop(brush, "pose_ik_segments") + layout.prop(brush, "use_pose_ik_anchored") layout.separator() - + + if brush.sculpt_tool == 'CLOTH': + layout.separator() + layout.prop(brush, "cloth_sim_limit") + layout.prop(brush, "cloth_sim_falloff") + layout.separator() + layout.prop(brush, "cloth_deform_type") + layout.prop(brush, "cloth_force_falloff_type") + layout.separator() + layout.prop(brush, "cloth_mass") + layout.prop(brush, "cloth_damping") + layout.separator() + if brush.sculpt_tool == 'SCRAPE': row = layout.row() + row.prop(brush, "area_radius_factor", slider=True) + row = layout.row() row.prop(brush, "invert_to_scrape_fill", text="Invert to Fill") if brush.sculpt_tool == 'FILL': row = layout.row() + row.prop(brush, "area_radius_factor", slider=True) + row = layout.row() row.prop(brush, "invert_to_scrape_fill", text="Invert to Scrape") if brush.sculpt_tool == 'GRAB': @@ -639,6 +659,14 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "use_multiplane_scrape_dynamic") col.prop(brush, "show_multiplane_scrape_planes_preview") + if brush.sculpt_tool == 'SMOOTH': + col = layout.column() + col.prop(brush, "smooth_deform_type") + if brush.smooth_deform_type == 'SURFACE': + col.prop(brush, "surface_smooth_shape_preservation") + col.prop(brush, "surface_smooth_current_vertex") + col.prop(brush, "surface_smooth_iterations") + if brush.sculpt_tool == 'MASK': layout.row().prop(brush, "mask_tool", expand=True) @@ -782,14 +810,27 @@ def brush_settings_advanced(layout, context, brush, popover=False): use_accumulate = capabilities.has_accumulate use_frontface = True + col = layout.column(heading="Auto-Masking", align=True) + # topology automasking - layout.prop(brush, "use_automasking_topology") + col.prop(brush, "use_automasking_topology", text="Topology") + + # face masks automasking + col.prop(brush, "use_automasking_face_sets", text="Face Sets") + + # boundary edges/face sets automasking + col.prop(brush, "use_automasking_boundary_edges", text="Mesh Boundary") + col.prop(brush, "use_automasking_boundary_face_sets", text="Face Sets Boundary") + col.prop(brush, "automasking_boundary_edges_propagation_steps") + + layout.separator() # sculpt plane settings if capabilities.has_sculpt_plane: layout.prop(brush, "sculpt_plane") - layout.prop(brush, "use_original_normal") - layout.prop(brush, "use_original_plane") + col = layout.column(heading="Use Original", align=True) + col.prop(brush, "use_original_normal", text="Normal") + col.prop(brush, "use_original_plane", text="Plane") layout.separator() # 3D and 2D Texture Paint. @@ -858,7 +899,7 @@ def draw_color_settings(context, layout, brush, color_type=False): UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") row.separator() row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) - row.prop(ups, "use_unified_color", text="", icon='WORLD') + row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL') # Gradient elif brush.color_type == 'GRADIENT': layout.template_color_ramp(brush, "gradient", expand=True) @@ -994,8 +1035,12 @@ def brush_basic_texpaint_settings(layout, context, brush, *, compact=False): def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False): + tool_settings = context.tool_settings + settings = tool_settings.gpencil_paint gp_settings = brush.gpencil_settings tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False) + if gp_settings is None: + return # Brush details if brush.gpencil_tool == 'ERASE': @@ -1016,7 +1061,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row.prop(gp_settings, "eraser_thickness_factor") row = layout.row(align=True) - row.prop(gp_settings, "use_cursor", text="Display Cursor") + row.prop(settings, "show_brush", text="Display Cursor") # FIXME: tools must use their own UI drawing! elif brush.gpencil_tool == 'FILL': @@ -1027,14 +1072,29 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) row = layout.row(align=True) row.prop(gp_settings, "fill_simplify_level", text="Simplify") - else: # brush.gpencil_tool == 'DRAW': + else: # brush.gpencil_tool == 'DRAW/TINT': row = layout.row(align=True) row.prop(brush, "size", text="Radius") row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + + if gp_settings.use_pressure and context.area.type == 'PROPERTIES': + col = layout.column() + col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, + use_negative_slope=True) + row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_strength_pressure and context.area.type == 'PROPERTIES': + col = layout.column() + col.template_curve_mapping(gp_settings, "curve_strength", brush=True, + use_negative_slope=True) + + if brush.gpencil_tool == 'TINT': + row = layout.row(align=True) + row.prop(gp_settings, "vertex_mode", text="Mode") + # FIXME: tools must use their own UI drawing! if tool.idname in { "builtin.arc", @@ -1047,7 +1107,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) settings = context.tool_settings.gpencil_sculpt if compact: row = layout.row(align=True) - row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA') + row.prop(settings, "use_thickness_curve", text="", icon='SPHERECURVE') sub = row.row(align=True) sub.active = settings.use_thickness_curve sub.popover( @@ -1064,50 +1124,66 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) def brush_basic_gpencil_sculpt_settings(layout, context, brush, *, compact=False): - tool_settings = context.tool_settings - settings = tool_settings.gpencil_sculpt - tool = settings.sculpt_tool + gp_settings = brush.gpencil_settings + tool = brush.gpencil_sculpt_tool row = layout.row(align=True) row.prop(brush, "size", slider=True) sub = row.row(align=True) sub.enabled = tool not in {'GRAB', 'CLONE'} - sub.prop(brush, "use_pressure_radius", text="") + sub.prop(gp_settings, "use_pressure", text="") row = layout.row(align=True) row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - layout.prop(brush, "use_falloff") - if compact: if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}: row.separator() - row.prop(brush, "direction", expand=True, text="") + row.prop(gp_settings, "direction", expand=True, text="") else: use_property_split_prev = layout.use_property_split layout.use_property_split = False if tool in {'THICKNESS', 'STRENGTH'}: - layout.row().prop(brush, "direction", expand=True) + layout.row().prop(gp_settings, "direction", expand=True) elif tool == 'PINCH': row = layout.row(align=True) - row.prop_enum(brush, "direction", value='ADD', text="Pinch") - row.prop_enum(brush, "direction", value='SUBTRACT', text="Inflate") + row.prop_enum(gp_settings, "direction", value='ADD', text="Pinch") + row.prop_enum(gp_settings, "direction", value='SUBTRACT', text="Inflate") elif tool == 'TWIST': row = layout.row(align=True) - row.prop_enum(brush, "direction", value='ADD', text="CCW") - row.prop_enum(brush, "direction", value='SUBTRACT', text="CW") + row.prop_enum(gp_settings, "direction", value='ADD', text="CCW") + row.prop_enum(gp_settings, "direction", value='SUBTRACT', text="CW") layout.use_property_split = use_property_split_prev def brush_basic_gpencil_weight_settings(layout, _context, brush, *, compact=False): + gp_settings = brush.gpencil_settings layout.prop(brush, "size", slider=True) row = layout.row(align=True) row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") + layout.prop(brush, "weight", slider=True) - layout.prop(brush, "use_falloff") + + +def brush_basic_gpencil_vertex_settings(layout, _context, brush, *, compact=False): + gp_settings = brush.gpencil_settings + + # Brush details + row = layout.row(align=True) + row.prop(brush, "size", text="Radius") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + + if brush.gpencil_vertex_tool in {'DRAW', 'BLUR', 'SMEAR'}: + row = layout.row(align=True) + row.prop(gp_settings, "pen_strength", slider=True) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + + if brush.gpencil_vertex_tool in {'DRAW', 'REPLACE'}: + row = layout.row(align=True) + row.prop(gp_settings, "vertex_mode", text="Mode") classes = ( diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 3384032e332..cc8fa511e42 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -136,13 +136,13 @@ class PARTICLE_UL_particle_systems(bpy.types.UIList): if md: row.prop( md, - "show_viewport", + "show_render", emboss=False, icon_only=True, ) row.prop( md, - "show_render", + "show_viewport", emboss=False, icon_only=True, ) @@ -413,6 +413,38 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel): box.label(text="Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error)) +class PARTICLE_PT_hair_dynamics_collision(ParticleButtonsPanel, Panel): + bl_label = "Collisions" + bl_parent_id = "PARTICLE_PT_hair_dynamics" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + return context.particle_system.cloth is not None + + def draw(self, context): + layout = self.layout + + psys = context.particle_system + cloth_md = psys.cloth + cloth_collision = cloth_md.collision_settings + + layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked is False + + layout.use_property_split = True + + col = layout.column() + col.prop(cloth_collision, "collision_quality", text="Quality") + + layout.separator() + + col = layout.column() + col.prop(cloth_collision, "distance_min", slider=True, text="Distance") + col.prop(cloth_collision, "impulse_clamp") + col.prop(cloth_collision, "collection") + + class PARTICLE_PT_hair_dynamics_structure(ParticleButtonsPanel, Panel): bl_label = "Structure" bl_parent_id = "PARTICLE_PT_hair_dynamics" @@ -607,7 +639,7 @@ class PARTICLE_PT_rotation(ParticleButtonsPanel, Panel): col.separator() col.prop(part, "phase_factor", slider=True) - col.prop(part, "phase_factor_random", text="Randomize Phase ", slider=True) + col.prop(part, "phase_factor_random", text="Randomize Phase", slider=True) if part.type != 'HAIR': col.prop(part, "use_dynamic_rotation") @@ -1986,6 +2018,7 @@ classes = ( PARTICLE_PT_emission, PARTICLE_PT_emission_source, PARTICLE_PT_hair_dynamics, + PARTICLE_PT_hair_dynamics_collision, PARTICLE_PT_hair_dynamics_structure, PARTICLE_PT_hair_dynamics_volume, PARTICLE_PT_cache, diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py index d9713cb8608..0bf667482c4 100644 --- a/release/scripts/startup/bl_ui/properties_physics_cloth.py +++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py @@ -230,7 +230,7 @@ class PHYSICS_PT_cloth_pressure(PhysicButtonsPanel, Panel): col.prop(cloth, "uniform_pressure_force") col = flow.column() - col.prop(cloth, "use_pressure_volume", text="Custom volume") + col.prop(cloth, "use_pressure_volume", text="Custom Volume") col = flow.column() col.active = cloth.use_pressure_volume diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index 5397020a521..0cd99efcca9 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -227,7 +227,7 @@ def point_cache_ui(self, cache, enabled, cachetype): col.operator("ptcache.bake", text="Bake").bake = True sub = col.row() - sub.enabled = (cache.is_frame_skip or cache.is_outdated) and enabled + sub.enabled = enabled sub.operator("ptcache.bake", text="Calculate To Frame").bake = False sub = col.column() @@ -310,8 +310,10 @@ def basic_force_field_settings_ui(self, field): else: col.prop(field, "flow") - col.prop(field, "apply_to_location", text="Affect Location") - col.prop(field, "apply_to_rotation", text="Affect Rotation") + sub = col.column(heading="Affect") + + sub.prop(field, "apply_to_location", text="Location") + sub.prop(field, "apply_to_rotation", text="Rotation") col = flow.column() sub = col.column(align=True) @@ -336,26 +338,29 @@ def basic_force_field_falloff_ui(self, field): if not field or field.type == 'NONE': return - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - - col = flow.column() + col = layout.column() col.prop(field, "z_direction") col.prop(field, "falloff_power", text="Power") - col = flow.column() - col.prop(field, "use_min_distance", text="Use Minimum") - - sub = col.column(align=True) + col = layout.column(align=False, heading="Min Distance") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(field, "use_min_distance", text="") + sub = sub.row(align=True) sub.active = field.use_min_distance - sub.prop(field, "distance_min", text="Min Distance") - - col = flow.column() - col.prop(field, "use_max_distance", text="Use Maximum") - - sub = col.column(align=True) + sub.prop(field, "distance_min", text="") + row.prop_decorator(field, "distance_min") + + col = layout.column(align=False, heading="Max Distance") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(field, "use_max_distance", text="") + sub = sub.row(align=True) sub.active = field.use_max_distance - sub.prop(field, "distance_max", text="Max Distance") - + sub.prop(field, "distance_max", text="") + row.prop_decorator(field, "distance_max") classes = ( PHYSICS_PT_add, diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py index 00dc068a51a..d1ff1dc9f5e 100644 --- a/release/scripts/startup/bl_ui/properties_physics_field.py +++ b/release/scripts/startup/bl_ui/properties_physics_field.py @@ -123,7 +123,7 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): col.prop(field, "use_object_coords") col.prop(field, "use_2d_force") - elif field.type == 'SMOKE_FLOW': + elif field.type == 'FLUID_FLOW': col = flow.column() col.prop(field, "strength") col.prop(field, "flow") diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 28c9895f53b..4b0791a63e6 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -19,20 +19,18 @@ # <pep8 compliant> import bpy -from bpy.types import ( - Panel, - Menu, -) +from bpy.types import Menu, Panel +from bl_ui.utils import PresetPanel from .properties_physics_common import ( effector_weights_ui, ) -class FLUID_MT_presets(Menu): +class FLUID_PT_presets(PresetPanel, Panel): bl_label = "Fluid Presets" preset_subdir = "fluid" preset_operator = "script.execute_preset" - draw = Menu.draw_preset + preset_add_operator = "fluid.preset_add" class PhysicButtonsPanel: @@ -162,9 +160,6 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): if md.fluid_type == 'DOMAIN': domain = md.domain_settings - # Deactivate UI if guides are enabled but not baked yet. - layout.active = not self.check_domain_has_unbaked_guide(domain) - is_baking_any = domain.is_cache_baking_any has_baked_data = domain.has_cache_baked_data @@ -176,16 +171,17 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): flow.enabled = not is_baking_any and not has_baked_data col = flow.column() + col.enabled = not domain.has_cache_baked_guide col.prop(domain, "resolution_max", text="Resolution Divisions") col.prop(domain, "time_scale", text="Time Scale") col.prop(domain, "cfl_condition", text="CFL Number") col = flow.column() col.prop(domain, "use_adaptive_timesteps") - col1 = col.column(align=True) - col1.enabled = domain.use_adaptive_timesteps - col1.prop(domain, "timesteps_max", text="Timesteps Maximum") - col1.prop(domain, "timesteps_min", text="Minimum") + sub = col.column(align=True) + sub.active = domain.use_adaptive_timesteps + sub.prop(domain, "timesteps_max", text="Timesteps Maximum") + sub.prop(domain, "timesteps_min", text="Minimum") col.separator() @@ -196,12 +192,25 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): sub.prop(domain, "gravity", text="Using Scene Gravity", icon='SCENE_DATA') else: col.prop(domain, "gravity", text="Gravity") - # TODO (sebbas): Clipping var useful for manta openvdb caching? - # col.prop(domain, "clipping", text="Empty Space") + + col = flow.column() + if PhysicButtonsPanel.poll_gas_domain(context): + col.prop(domain, "clipping", text="Empty Space") + col.prop(domain, "delete_in_obstacle", text="Delete In Obstacle") if domain.cache_type == 'MODULAR': col.separator() + + # Deactivate bake operator if guides are enabled but not baked yet. + note_flag = True + if self.check_domain_has_unbaked_guide(domain) and domain.cache_type == 'MODULAR': + note = layout.split() + note_flag = False + note.enabled = note_flag + note.label(icon='INFO', text="Unbaked Guides: Bake Guides or disable them") + split = layout.split() + split.enabled = note_flag bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) if domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: @@ -227,8 +236,8 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): col = grid.column() col.prop(flow, "flow_behavior", expand=False) - if flow.flow_behavior in {'INFLOW'}: - col.prop(flow, "use_inflow", text="Use Inflow") + if flow.flow_behavior in {'INFLOW', 'OUTFLOW'}: + col.prop(flow, "use_inflow", text="Use Flow") col.prop(flow, "subframes", text="Sampling Substeps") @@ -256,16 +265,19 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): row = layout.row() row.prop(effector_settings, "effector_type") - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - col = flow.column() + col = grid.column() + col.prop(effector_settings, "subframes", text="Sampling Substeps") + col.prop(effector_settings, "surface_distance", text="Surface Thickness") + col = grid.column() + + col.prop(effector_settings, "use_effector", text="Use Effector") col.prop(effector_settings, "use_plane_init", text="Is Planar") - col.prop(effector_settings, "surface_distance", text="Surface Thickness") if effector_settings.effector_type == 'GUIDE': col.prop(effector_settings, "velocity_factor", text="Velocity Factor") - col = flow.column() col.prop(effector_settings, "guide_mode", text="Guide Mode") @@ -309,8 +321,8 @@ class PHYSICS_PT_borders(PhysicButtonsPanel, Panel): class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel): - bl_label = "Smoke" - bl_parent_id = 'PHYSICS_PT_settings' + bl_label = "Gas" + bl_parent_id = 'PHYSICS_PT_fluid' COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod @@ -354,10 +366,13 @@ class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel): return (context.engine in cls.COMPAT_ENGINES) def draw_header(self, context): - md = context.fluid - domain = md.domain_settings + md = context.fluid.domain_settings + domain = context.fluid.domain_settings - self.layout.prop(domain, "use_dissolve_smoke", text="") + is_baking_any = domain.is_cache_baking_any + + self.layout.enabled = not is_baking_any + self.layout.prop(md, "use_dissolve_smoke", text="") def draw(self, context): layout = self.layout @@ -383,7 +398,7 @@ class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel): class PHYSICS_PT_fire(PhysicButtonsPanel, Panel): bl_label = "Fire" - bl_parent_id = 'PHYSICS_PT_settings' + bl_parent_id = 'PHYSICS_PT_smoke' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -409,20 +424,22 @@ class PHYSICS_PT_fire(PhysicButtonsPanel, Panel): col = flow.column() col.prop(domain, "burning_rate", text="Reaction Speed") - col = flow.column(align=True) - col.prop(domain, "flame_smoke", text="Flame Smoke") - col.prop(domain, "flame_vorticity", text="Vorticity") + row = col.row() + sub = row.column(align=True) + sub.prop(domain, "flame_smoke", text="Flame Smoke") + sub.prop(domain, "flame_vorticity", text="Vorticity") + col = flow.column(align=True) col.prop(domain, "flame_max_temp", text="Temperature Maximum") col.prop(domain, "flame_ignition", text="Minimum") - col = flow.column() - col.prop(domain, "flame_smoke_color", text="Flame Color") + row = col.row() + row.prop(domain, "flame_smoke_color", text="Flame Color") class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel): bl_label = "Liquid" - bl_parent_id = 'PHYSICS_PT_settings' - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + bl_parent_id = 'PHYSICS_PT_fluid' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): @@ -433,6 +450,11 @@ class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel): def draw_header(self, context): md = context.fluid.domain_settings + domain = context.fluid.domain_settings + + is_baking_any = domain.is_cache_baking_any + + self.layout.enabled = not is_baking_any self.layout.prop(md, "use_flip_particles", text="") def draw(self, context): @@ -445,32 +467,32 @@ class PHYSICS_PT_liquid(PhysicButtonsPanel, Panel): is_baking_any = domain.is_cache_baking_any has_baked_data = domain.has_cache_baked_data + layout.enabled = not is_baking_any and not has_baked_data flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) col = flow.column() - col0 = col.column() - col0.enabled = not is_baking_any and not has_baked_data - col0.prop(domain, "simulation_method", expand=False) - col0.prop(domain, "flip_ratio", text="FLIP Ratio") - col0.prop(domain, "particle_radius", text="Particle Radius") - - col1 = flow.column(align=True) - col1.enabled = not is_baking_any and not has_baked_data - col1.prop(domain, "particle_max", text="Particles Maximum") - col1.prop(domain, "particle_min", text="Minimum") - - col1 = flow.column() - col1.enabled = not is_baking_any and not has_baked_data - col1.prop(domain, "particle_number", text="Particle Sampling") - col1.prop(domain, "particle_band_width", text="Narrow Band Width") - col1.prop(domain, "particle_randomness", text="Particle Randomness") - - col2 = flow.column() - col2.enabled = not is_baking_any and not has_baked_data - col2.prop(domain, "use_fractions", text="Fractional Obstacles") - col3 = col2.column() - col3.enabled = domain.use_fractions and col2.enabled - col3.prop(domain, "fractions_threshold", text="Obstacle-Fluid Threshold") + col.prop(domain, "simulation_method", expand=False) + col.prop(domain, "flip_ratio", text="FLIP Ratio") + col = col.column(align=True) + col.prop(domain, "particle_radius", text="Particle Radius") + col.prop(domain, "particle_number", text="Sampling") + col.prop(domain, "particle_randomness", text="Randomness") + + col = flow.column() + col = col.column(align=True) + col.prop(domain, "particle_max", text="Particles Maximum") + col.prop(domain, "particle_min", text="Minimum") + + col.separator() + + col = col.column() + col.prop(domain, "particle_band_width", text="Narrow Band Width") + + col = col.column() + col.prop(domain, "use_fractions", text="Fractional Obstacles") + sub = col.column() + sub.active = domain.use_fractions + sub.prop(domain, "fractions_threshold", text="Obstacle-Fluid Threshold") class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel): @@ -518,7 +540,7 @@ class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel): class PHYSICS_PT_flow_initial_velocity(PhysicButtonsPanel, Panel): bl_label = "Initial Velocity" bl_parent_id = 'PHYSICS_PT_settings' - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): @@ -609,15 +631,21 @@ class PHYSICS_PT_flow_texture(PhysicButtonsPanel, Panel): class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel): bl_label = "Adaptive Domain" - bl_parent_id = 'PHYSICS_PT_fluid' + bl_parent_id = 'PHYSICS_PT_settings' bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): if not PhysicButtonsPanel.poll_gas_domain(context): return False + md = context.fluid + domain = md.domain_settings + # Effector guides require a fixed domain size + if domain.use_guide and domain.guide_source == 'EFFECTOR': + return False + return (context.engine in cls.COMPAT_ENGINES) def draw_header(self, context): @@ -655,7 +683,7 @@ class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel): class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): bl_label = "Noise" - bl_parent_id = 'PHYSICS_PT_fluid' + bl_parent_id = 'PHYSICS_PT_smoke' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -678,9 +706,7 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): layout.use_property_split = True domain = context.fluid.domain_settings - - # Deactivate UI if guides are enabled but not baked yet. - layout.active = domain.use_noise and not self.check_domain_has_unbaked_guide(domain) + layout.active = domain.use_noise is_baking_any = domain.is_cache_baking_any has_baked_noise = domain.has_cache_baked_noise @@ -701,8 +727,16 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): if domain.cache_type == 'MODULAR': col.separator() + # Deactivate bake operator if data has not been baked yet. + note_flag = True + if domain.use_noise and not domain.has_cache_baked_data and domain.cache_type == 'MODULAR': + note = layout.split() + note_flag = False + note.enabled = note_flag + note.label(icon='INFO', text="Unbaked Data: Bake Data first") + split = layout.split() - split.enabled = domain.has_cache_baked_data + split.enabled = domain.has_cache_baked_data and note_flag bake_incomplete = (domain.cache_frame_pause_noise < domain.cache_frame_end) if domain.has_cache_baked_noise and not domain.is_cache_baking_noise and bake_incomplete: @@ -721,7 +755,7 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): bl_label = "Mesh" - bl_parent_id = 'PHYSICS_PT_fluid' + bl_parent_id = 'PHYSICS_PT_liquid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -744,9 +778,7 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): layout.use_property_split = True domain = context.fluid.domain_settings - - # Deactivate UI if guides are enabled but not baked yet. - layout.active = domain.use_mesh and not self.check_domain_has_unbaked_guide(domain) + layout.active = domain.use_mesh is_baking_any = domain.is_cache_baking_any has_baked_mesh = domain.has_cache_baked_mesh @@ -780,8 +812,16 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): if domain.cache_type == 'MODULAR': col.separator() + # Deactivate bake operator if data has not been baked yet. + note_flag = True + if domain.use_mesh and not domain.has_cache_baked_data and domain.cache_type == 'MODULAR': + note = layout.split() + note_flag = False + note.enabled = note_flag + note.label(icon='INFO', text="Unbaked Data: Bake Data first") + split = layout.split() - split.enabled = domain.has_cache_baked_data + split.enabled = domain.has_cache_baked_data and note_flag bake_incomplete = (domain.cache_frame_pause_mesh < domain.cache_frame_end) if domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh and bake_incomplete: @@ -800,9 +840,9 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): bl_label = "Particles" - bl_parent_id = 'PHYSICS_PT_fluid' + bl_parent_id = 'PHYSICS_PT_liquid' bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): @@ -817,9 +857,6 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): domain = context.fluid.domain_settings - # Deactivate UI if guides are enabled but not baked yet. - layout.active = not self.check_domain_has_unbaked_guide(domain) - is_baking_any = domain.is_cache_baking_any has_baked_particles = domain.has_cache_baked_particles using_particles = domain.use_spray_particles or domain.use_foam_particles or domain.use_bubble_particles @@ -829,36 +866,37 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): sndparticle_combined_export = domain.sndparticle_combined_export col = flow.column() - col.enabled = sndparticle_combined_export in {'OFF', 'FOAM + BUBBLES'} - col.prop(domain, "use_spray_particles", text="Spray") - col = flow.column() - col.enabled = sndparticle_combined_export in {'OFF', 'SPRAY + BUBBLES'} - col.prop(domain, "use_foam_particles", text="Foam") - col = flow.column() - col.enabled = sndparticle_combined_export in {'OFF', 'SPRAY + FOAM'} - col.prop(domain, "use_bubble_particles", text="Bubbles") + row = col.row() + row.enabled = sndparticle_combined_export in {'OFF', 'FOAM + BUBBLES'} + row.prop(domain, "use_spray_particles", text="Spray") + row.prop(domain, "use_foam_particles", text="Foam") + row.prop(domain, "use_bubble_particles", text="Bubbles") + + col.separator() + + col.prop(domain, "sndparticle_combined_export") flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - flow.enabled = not is_baking_any and not has_baked_particles and using_particles + flow.enabled = not is_baking_any and not has_baked_particles + flow.active = using_particles col = flow.column() - col.prop(domain, "sndparticle_combined_export") col.prop(domain, "particle_scale", text="Upres Factor") col.separator() col = flow.column(align=True) - col.prop(domain, "sndparticle_tau_max_wc", text="Wave Crest Potential Maximum") - col.prop(domain, "sndparticle_tau_min_wc", text="Minimum") + col.prop(domain, "sndparticle_potential_max_wavecrest", text="Wave Crest Potential Maximum") + col.prop(domain, "sndparticle_potential_min_wavecrest", text="Minimum") col.separator() col = flow.column(align=True) - col.prop(domain, "sndparticle_tau_max_ta", text="Trapped Air Potential Maximum") - col.prop(domain, "sndparticle_tau_min_ta", text="Minimum") + col.prop(domain, "sndparticle_potential_max_trappedair", text="Trapped Air Potential Maximum") + col.prop(domain, "sndparticle_potential_min_trappedair", text="Minimum") col.separator() col = flow.column(align=True) - col.prop(domain, "sndparticle_tau_max_k", text="Kinetic Energy Potential Maximum") - col.prop(domain, "sndparticle_tau_min_k", text="Minimum") + col.prop(domain, "sndparticle_potential_max_energy", text="Kinetic Energy Potential Maximum") + col.prop(domain, "sndparticle_potential_min_energy", text="Minimum") col.separator() col = flow.column(align=True) @@ -867,18 +905,18 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): col.separator() col = flow.column(align=True) - col.prop(domain, "sndparticle_k_wc", text="Wave Crest Particle Sampling") - col.prop(domain, "sndparticle_k_ta", text="Trapped Air Particle Sampling") + col.prop(domain, "sndparticle_sampling_wavecrest", text="Wave Crest Particle Sampling") + col.prop(domain, "sndparticle_sampling_trappedair", text="Trapped Air Particle Sampling") col.separator() col = flow.column(align=True) - col.prop(domain, "sndparticle_l_max", text="Particle Life Maximum") - col.prop(domain, "sndparticle_l_min", text="Minimum") + col.prop(domain, "sndparticle_life_max", text="Particle Life Maximum") + col.prop(domain, "sndparticle_life_min", text="Minimum") col.separator() col = flow.column(align=True) - col.prop(domain, "sndparticle_k_b", text="Bubble Buoyancy") - col.prop(domain, "sndparticle_k_d", text="Bubble Drag") + col.prop(domain, "sndparticle_bubble_buoyancy", text="Bubble Buoyancy") + col.prop(domain, "sndparticle_bubble_drag", text="Bubble Drag") col.separator() col = flow.column() @@ -887,8 +925,17 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): if domain.cache_type == 'MODULAR': col.separator() + # Deactivate bake operator if data has not been baked yet. + note_flag = True + if using_particles and not domain.has_cache_baked_data and domain.cache_type == 'MODULAR': + note = layout.split() + note_flag = False + note.enabled = note_flag + note.label(icon='INFO', text="Unbaked Data: Bake Data first") + split = layout.split() split.enabled = ( + note_flag and domain.has_cache_baked_data and (domain.use_spray_particles or domain.use_bubble_particles or @@ -913,7 +960,7 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): bl_label = "Diffusion" - bl_parent_id = 'PHYSICS_PT_fluid' + bl_parent_id = 'PHYSICS_PT_liquid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -925,14 +972,23 @@ class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): return (context.engine in cls.COMPAT_ENGINES) + def draw_header(self, context): + md = context.fluid.domain_settings + domain = context.fluid.domain_settings + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + self.layout.enabled = not is_baking_any and not has_baked_any + self.layout.prop(md, "use_diffusion", text="") + + def draw_header_preset(self, _context): + FLUID_PT_presets.draw_panel_header(self.layout) + def draw(self, context): layout = self.layout layout.use_property_split = True domain = context.fluid.domain_settings - - # Deactivate UI if guides are enabled but not baked yet. - layout.active = not self.check_domain_has_unbaked_guide(domain) + layout.active = domain.use_diffusion is_baking_any = domain.is_cache_baking_any has_baked_any = domain.has_cache_baked_any @@ -941,22 +997,11 @@ class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data - row = flow.row() - - col = row.column() - col.label(text="Viscosity Presets:") - col.menu("FLUID_MT_presets", text=bpy.types.FLUID_MT_presets.bl_label) - - col = row.column(align=True) - col.operator("fluid.preset_add", text="", icon='ADD') - col.operator("fluid.preset_add", text="", icon='REMOVE').remove_active = True - col = flow.column(align=True) col.prop(domain, "viscosity_base", text="Base") col.prop(domain, "viscosity_exponent", text="Exponent", slider=True) col = flow.column() - col.prop(domain, "domain_size", text="Real World Size") col.prop(domain, "surface_tension", text="Surface Tension") @@ -964,7 +1009,7 @@ class PHYSICS_PT_guide(PhysicButtonsPanel, Panel): bl_label = "Guides" bl_parent_id = 'PHYSICS_PT_fluid' bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): @@ -1018,7 +1063,8 @@ class PHYSICS_PT_guide(PhysicButtonsPanel, Panel): col = split.column() col.operator("fluid.free_guides", text="Free") elif not domain.has_cache_baked_guide and domain.is_cache_baking_guide: - split.operator("fluid.pause_bake", text="Pause Guides") + split.enabled = False + split.operator("fluid.pause_bake", text="Baking Guides - ESC to pause") elif not domain.has_cache_baked_guide and not domain.is_cache_baking_guide: split.operator("fluid.bake_guides", text="Bake Guides") else: @@ -1072,7 +1118,10 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): domain = context.fluid.domain_settings is_baking_any = domain.is_cache_baking_any - has_baked_any = domain.has_cache_baked_any + has_baked_data = domain.has_cache_baked_data + has_baked_noise = domain.has_cache_baked_noise + has_baked_mesh = domain.has_cache_baked_mesh + has_baked_particles = domain.has_cache_baked_particles col = layout.column() col.prop(domain, "cache_directory", text="") @@ -1086,29 +1135,37 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): col.prop(domain, "cache_type", expand=False) col.enabled = not is_baking_any - col = flow.column(align=True) col.separator() + row = col.row() + col = row.column(align=True) col.prop(domain, "cache_frame_start", text="Frame Start") col.prop(domain, "cache_frame_end", text="End") - col.enabled = not is_baking_any + row.enabled = not is_baking_any col.separator() col = flow.column() - col.enabled = not is_baking_any and not has_baked_any - col.prop(domain, "cache_data_format", text="Data File Format") + row = col.row() + row.enabled = not is_baking_any and not has_baked_data + row.prop(domain, "cache_data_format", text="Data File Format") if md.domain_settings.domain_type in {'GAS'}: if domain.use_noise: - col.prop(domain, "cache_noise_format", text="Noise File Format") + row = col.row() + row.enabled = not is_baking_any and not has_baked_noise + row.prop(domain, "cache_noise_format", text="Noise File Format") if md.domain_settings.domain_type in {'LIQUID'}: # File format for all particle systemes (FLIP and secondary) - col.prop(domain, "cache_particle_format", text="Particle File Format") + row = col.row() + row.enabled = not is_baking_any and not has_baked_particles and not has_baked_data + row.prop(domain, "cache_particle_format", text="Particle File Format") if domain.use_mesh: - col.prop(domain, "cache_mesh_format", text="Mesh File Format") + row = col.row() + row.enabled = not is_baking_any and not has_baked_mesh + row.prop(domain, "cache_mesh_format", text="Mesh File Format") if domain.cache_type == 'FINAL': @@ -1132,7 +1189,8 @@ class PHYSICS_PT_export(PhysicButtonsPanel, Panel): @classmethod def poll(cls, context): - if not PhysicButtonsPanel.poll_fluid_domain(context): + # Only show the advanced panel to advanced users who know Mantaflow's birthday :) + if not PhysicButtonsPanel.poll_fluid_domain(context) or bpy.app.debug_value != 3001: return False return (context.engine in cls.COMPAT_ENGINES) @@ -1186,35 +1244,31 @@ class PHYSICS_PT_viewport_display(PhysicButtonsPanel, Panel): flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) domain = context.fluid.domain_settings - - col = flow.column() - col.prop(domain, "display_thickness") - - col.separator() - - col.prop(domain, "slice_method", text="Slicing") - slice_method = domain.slice_method axis_slice_method = domain.axis_slice_method do_axis_slicing = (slice_method == 'AXIS_ALIGNED') do_full_slicing = (axis_slice_method == 'FULL') - col = col.column() - col.enabled = do_axis_slicing - col.prop(domain, "axis_slice_method") + col = flow.column(align=False) + col.prop(domain, "display_thickness") + col.prop(domain, "display_interpolation") + col.separator() col = flow.column() - sub = col.column() - sub.enabled = not do_full_slicing and do_axis_slicing - sub.prop(domain, "slice_axis") - sub.prop(domain, "slice_depth") + col.prop(domain, "slice_method", text="Slicing") - row = col.row() - row.enabled = do_full_slicing or not do_axis_slicing - row.prop(domain, "slice_per_voxel") + col = col.column() + col.active = do_axis_slicing + col.prop(domain, "axis_slice_method") - col.prop(domain, "display_interpolation") + if not do_full_slicing and do_axis_slicing: + col.prop(domain, "slice_axis") + col.prop(domain, "slice_depth") + + col = col.column() + col.active = do_full_slicing or not do_axis_slicing + col.prop(domain, "slice_per_voxel") class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel): @@ -1237,8 +1291,7 @@ class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel): domain = context.fluid.domain_settings col = layout.column() - col.enabled = domain.use_color_ramp - + col.active = domain.use_color_ramp col.prop(domain, "coba_field") col.use_property_split = False @@ -1269,33 +1322,33 @@ class PHYSICS_PT_viewport_display_debug(PhysicButtonsPanel, Panel): domain = context.fluid.domain_settings col = flow.column() - col.enabled = domain.show_velocity + col.active = domain.show_velocity col.prop(domain, "vector_display_type", text="Display As") col.prop(domain, "vector_scale") classes = ( - FLUID_MT_presets, + FLUID_PT_presets, PHYSICS_PT_fluid, PHYSICS_PT_settings, PHYSICS_PT_borders, + PHYSICS_PT_adaptive_domain, PHYSICS_PT_smoke, PHYSICS_PT_smoke_dissolve, + PHYSICS_PT_noise, PHYSICS_PT_fire, PHYSICS_PT_liquid, - PHYSICS_PT_flow_source, - PHYSICS_PT_flow_initial_velocity, - PHYSICS_PT_flow_texture, - PHYSICS_PT_adaptive_domain, - PHYSICS_PT_noise, - PHYSICS_PT_mesh, - PHYSICS_PT_particles, PHYSICS_PT_diffusion, + PHYSICS_PT_particles, + PHYSICS_PT_mesh, PHYSICS_PT_guide, PHYSICS_PT_collections, PHYSICS_PT_cache, PHYSICS_PT_export, PHYSICS_PT_field_weights, + PHYSICS_PT_flow_source, + PHYSICS_PT_flow_initial_velocity, + PHYSICS_PT_flow_texture, PHYSICS_PT_viewport_display, PHYSICS_PT_viewport_display_color, PHYSICS_PT_viewport_display_debug, diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 6a89fb007cf..c5c75f95937 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -297,7 +297,7 @@ class RENDER_PT_eevee_volumetric_shadows(RenderButtonsPanel, Panel): props = scene.eevee layout.active = props.use_volumetric_shadows - layout.prop(props, "volumetric_shadow_samples", text="Shadow Samples") + layout.prop(props, "volumetric_shadow_samples", text="Samples") class RENDER_PT_eevee_subsurface_scattering(RenderButtonsPanel, Panel): @@ -478,39 +478,47 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel): scene = context.scene rd = scene.render + props = scene.eevee col = layout.column() col.prop(rd, "filter_size") col.prop(rd, "film_transparent", text="Transparent") + col = layout.column(align=False, heading="Overscan") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(props, "use_overscan", text="") + sub = sub.row(align=True) + sub.active = props.use_overscan + sub.prop(props, "overscan_size", text="") + row.prop_decorator(props, "overscan_size") + -class RENDER_PT_eevee_film_overscan(RenderButtonsPanel, Panel): - bl_label = "Overscan" - bl_parent_id = "RENDER_PT_eevee_film" +class RENDER_PT_eevee_hair(RenderButtonsPanel, Panel): + bl_label = "Hair" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_EEVEE'} - def draw_header(self, context): - - scene = context.scene - props = scene.eevee - - self.layout.prop(props, "use_overscan", text="") + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout - layout.use_property_split = True scene = context.scene - props = scene.eevee + rd = scene.render - layout.active = props.use_overscan - layout.prop(props, "overscan_size", text="Size") + layout.use_property_split = True + layout.prop(rd, "hair_type", expand=True) + layout.prop(rd, "hair_subdiv") -class RENDER_PT_eevee_hair(RenderButtonsPanel, Panel): - bl_label = "Hair" + +class RENDER_PT_eevee_performance(RenderButtonsPanel, Panel): + bl_label = "Performance" bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_EEVEE'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): @@ -523,8 +531,28 @@ class RENDER_PT_eevee_hair(RenderButtonsPanel, Panel): layout.use_property_split = True - layout.prop(rd, "hair_type", expand=True) - layout.prop(rd, "hair_subdiv") + layout.prop(rd, "use_high_quality_normals") + + +class RENDER_PT_gpencil(RenderButtonsPanel, Panel): + bl_label = "Grease Pencil" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return True + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + scene = context.scene + props = scene.grease_pencil_settings + + col = layout.column() + col.prop(props, "antialias_threshold") + class RENDER_PT_opengl_sampling(RenderButtonsPanel, Panel): @@ -545,7 +573,7 @@ class RENDER_PT_opengl_sampling(RenderButtonsPanel, Panel): col = layout.column() col.prop(props, "render_aa", text="Render") - col.prop(props, "viewport_aa", text="Viewport Render") + col.prop(props, "viewport_aa", text="Viewport") class RENDER_PT_opengl_film(RenderButtonsPanel, Panel): @@ -632,9 +660,6 @@ class RENDER_PT_simplify_viewport(RenderButtonsPanel, Panel): col = flow.column() col.prop(rd, "simplify_child_particles", text="Max Child Particles") - col = flow.column() - col.prop(rd, "use_simplify_smoke_highres", text="High-resolution Smoke") - class RENDER_PT_simplify_render(RenderButtonsPanel, Panel): bl_label = "Render" @@ -979,12 +1004,14 @@ classes = ( RENDER_PT_eevee_volumetric, RENDER_PT_eevee_volumetric_lighting, RENDER_PT_eevee_volumetric_shadows, + RENDER_PT_eevee_performance, RENDER_PT_eevee_hair, RENDER_PT_eevee_shadows, RENDER_PT_eevee_indirect_lighting, RENDER_PT_eevee_indirect_lighting_display, RENDER_PT_eevee_film, - RENDER_PT_eevee_film_overscan, + + RENDER_PT_gpencil, RENDER_PT_opengl_sampling, RENDER_PT_opengl_lighting, RENDER_PT_opengl_color, diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 40a630ff834..5af8bc2aaa7 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -163,6 +163,8 @@ class TEXTURE_PT_node(TextureButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + node = context.texture_node ntree = node.id_data layout.template_node_view(ntree, node, None) diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index 121b8f2f401..3645f0dc2f2 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -40,17 +40,12 @@ class VIEWLAYER_PT_layer(ViewLayerButtonsPanel, Panel): layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - layout.use_property_split = True - scene = context.scene rd = scene.render layer = context.view_layer - col = flow.column() + col = layout.column() col.prop(layer, "use", text="Use for Rendering") - col = flow.column() col.prop(rd, "use_single_layer", text="Render Single Layer") @@ -59,33 +54,94 @@ class VIEWLAYER_PT_eevee_layer_passes(ViewLayerButtonsPanel, Panel): COMPAT_ENGINES = {'BLENDER_EEVEE'} def draw(self, context): - layout = self.layout + pass - layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) +class VIEWLAYER_PT_eevee_layer_passes_data(ViewLayerButtonsPanel, Panel): + bl_label = "Data" + bl_parent_id = "VIEWLAYER_PT_eevee_layer_passes" + + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + scene = context.scene + rd = scene.render view_layer = context.view_layer - col = flow.column() + col = layout.column() col.prop(view_layer, "use_pass_combined") - col = flow.column() col.prop(view_layer, "use_pass_z") - col = flow.column() col.prop(view_layer, "use_pass_mist") - col = flow.column() col.prop(view_layer, "use_pass_normal") - col = flow.column() - col.prop(view_layer, "use_pass_ambient_occlusion") - col = flow.column() - col.prop(view_layer, "use_pass_subsurface_direct", text="Subsurface Direct") - col = flow.column() - col.prop(view_layer, "use_pass_subsurface_color", text="Subsurface Color") +class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel): + bl_label = "Light" + bl_parent_id = "VIEWLAYER_PT_eevee_layer_passes" + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + view_layer_eevee = view_layer.eevee + scene = context.scene + scene_eevee = scene.eevee + + col = layout.column(heading="Diffuse", align=True) + col.prop(view_layer, "use_pass_diffuse_direct", text="Light") + col.prop(view_layer, "use_pass_diffuse_color", text="Color") + + col = layout.column(heading="Specular", align=True) + col.prop(view_layer, "use_pass_glossy_direct", text="Light") + col.prop(view_layer, "use_pass_glossy_color", text="Color") + + col = layout.column(heading="Volume", align=True) + col.prop(view_layer_eevee, "use_pass_volume_transmittance", text="Transmittance") + col.prop(view_layer_eevee, "use_pass_volume_scatter", text="Scatter") + + col = layout.column(heading="Other", align=True) + col.prop(view_layer, "use_pass_emit", text="Emission") + col.prop(view_layer, "use_pass_environment") + col.prop(view_layer, "use_pass_shadow") + row = col.row() + row.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion") + row.active = scene_eevee.use_gtao + + +class VIEWLAYER_PT_eevee_layer_passes_effects(ViewLayerButtonsPanel, Panel): + bl_label = "Effects" + bl_parent_id = "VIEWLAYER_PT_eevee_layer_passes" + COMPAT_ENGINES = {'BLENDER_EEVEE'} + + def draw(self, context): + layout = self.layout + + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + view_layer_eevee = view_layer.eevee + scene = context.scene + scene_eevee = scene.eevee + + col = layout.column() + col.prop(view_layer_eevee, "use_pass_bloom", text="Bloom") + col.active = scene_eevee.use_bloom + classes = ( VIEWLAYER_PT_layer, VIEWLAYER_PT_eevee_layer_passes, + VIEWLAYER_PT_eevee_layer_passes_data, + VIEWLAYER_PT_eevee_layer_passes_light, + VIEWLAYER_PT_eevee_layer_passes_effects, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_workspace.py b/release/scripts/startup/bl_ui/properties_workspace.py index 322cdee0922..8af30b5c002 100644 --- a/release/scripts/startup/bl_ui/properties_workspace.py +++ b/release/scripts/startup/bl_ui/properties_workspace.py @@ -77,8 +77,6 @@ class WORKSPACE_PT_addons(WorkSpaceButtonsPanel, Panel): if module is None: continue info = addon_utils.module_bl_info(module) - if not info["use_owner"]: - continue is_enabled = module_name in owner_ids row = col.row() row.alignment = 'LEFT' diff --git a/release/scripts/startup/bl_ui/properties_world.py b/release/scripts/startup/bl_ui/properties_world.py index a6a58ec4e2b..4a9f35ee59a 100644 --- a/release/scripts/startup/bl_ui/properties_world.py +++ b/release/scripts/startup/bl_ui/properties_world.py @@ -105,6 +105,8 @@ class EEVEE_WORLD_PT_surface(WorldButtonsPanel, Panel): layout.prop(world, "use_nodes", icon='NODETREE') layout.separator() + layout.use_property_split = True + if world.use_nodes: ntree = world.node_tree node = ntree.get_output_node('EEVEE') @@ -139,6 +141,8 @@ class EEVEE_WORLD_PT_volume(WorldButtonsPanel, Panel): ntree = world.node_tree node = ntree.get_output_node('EEVEE') + layout.use_property_split = True + if node: input = find_node_input(node, 'Volume') if input: diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index f93629a4f03..5b6cc6609e0 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -918,6 +918,10 @@ class CLIP_PT_tracking_lens(Panel): col = layout.column(align=True) col.prop(camera, "division_k1") col.prop(camera, "division_k2") + elif camera.distortion_model == 'NUKE': + col = layout.column(align=True) + col.prop(camera, "nuke_k1") + col.prop(camera, "nuke_k2") class CLIP_PT_marker(CLIP_PT_tracking_panel, Panel): diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index a09e263fd87..3f8c41e4f21 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -26,6 +26,7 @@ from bpy.types import ( ) from bl_ui.properties_grease_pencil_common import ( + GreasePencilLayerMasksPanel, GreasePencilLayerAdjustmentsPanel, GreasePencilLayerRelationsPanel, GreasePencilLayerDisplayPanel, @@ -125,6 +126,12 @@ class DopesheetFilterPopoverBase: flow.prop(dopesheet, "show_lattices", text="Lattices") if bpy.data.metaballs: flow.prop(dopesheet, "show_metaballs", text="Metaballs") + if hasattr(bpy.data, "hairs") and bpy.data.hairs: + flow.prop(dopesheet, "show_hairs", text="Hairs") + if hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds: + flow.prop(dopesheet, "show_pointclouds", text="Point Clouds") + if bpy.data.volumes: + flow.prop(dopesheet, "show_volumes", text="Volumes") # data types flow.prop(dopesheet, "show_worlds", text="Worlds") @@ -251,15 +258,25 @@ class DOPESHEET_HT_editor_buttons(Header): # Layer management if st.mode == 'GPENCIL': - row = layout.row(align=True) - row.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' - row.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' + ob = context.active_object + selected = st.dopesheet.show_only_selected + enable_but = selected and ob is not None and ob.type == 'GPENCIL' row = layout.row(align=True) + row.enabled = enable_but row.operator("gpencil.layer_add", icon='ADD', text="") row.operator("gpencil.layer_remove", icon='REMOVE', text="") + row.menu("GPENCIL_MT_layer_context_menu", icon='DOWNARROW_HLT', text="") - layout.separator_spacer() + row = layout.row(align=True) + row.enabled = enable_but + row.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' + row.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' + + row = layout.row(align=True) + row.enabled = enable_but + row.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_ON', text="").affect_visibility = True + row.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False layout.separator_spacer() @@ -269,15 +286,8 @@ class DOPESHEET_HT_editor_buttons(Header): dopesheet_filter(layout, context) elif st.mode == 'GPENCIL': row = layout.row(align=True) - row.prop(st.dopesheet, "show_gpencil_3d_only", text="Active Only") - - if st.dopesheet.show_gpencil_3d_only: - row = layout.row(align=True) - row.prop(st.dopesheet, "show_only_selected", text="") - row.prop(st.dopesheet, "show_hidden", text="") - - row = layout.row(align=True) - row.prop(st.dopesheet, "filter_text", text="") + row.prop(st.dopesheet, "show_only_selected", text="") + row.prop(st.dopesheet, "show_hidden", text="") layout.popover( panel="DOPESHEET_PT_filters", @@ -497,6 +507,7 @@ class DOPESHEET_MT_key(Menu): layout.operator_menu_enum("action.keyframe_type", "type", text="Keyframe Type") layout.operator_menu_enum("action.handle_type", "type", text="Handle Type") layout.operator_menu_enum("action.interpolation_type", "type", text="Interpolation Mode") + layout.operator_menu_enum("action.easing_type", "type", text="Easing Mode") layout.separator() layout.operator("action.clean").channels = False @@ -599,6 +610,7 @@ class DOPESHEET_MT_context_menu(Menu): layout.operator_menu_enum("action.keyframe_type", "type", text="Keyframe Type") layout.operator_menu_enum("action.handle_type", "type", text="Handle Type") layout.operator_menu_enum("action.interpolation_type", "type", text="Interpolation Mode") + layout.operator_menu_enum("action.easing_type", "type", text="Easing Mode") layout.separator() @@ -616,7 +628,7 @@ class DOPESHEET_MT_context_menu(Menu): class DOPESHEET_MT_channel_context_menu(Menu): bl_label = "Dope Sheet Channel Context Menu" - def draw(self, _context): + def draw(self, context): layout = self.layout layout.operator("anim.channels_setting_enable", text="Mute Channels").type = 'MUTE' @@ -631,7 +643,12 @@ class DOPESHEET_MT_channel_context_menu(Menu): layout.separator() layout.operator("anim.channels_editable_toggle") - layout.operator_menu_enum("action.extrapolation_type", "type", text="Extrapolation Mode") + + if bpy.ops.graph.extrapolation_type.poll(context.copy()): + operator = "graph.extrapolation_type" + else: + operator = "action.extrapolation_type" + layout.operator_menu_enum(operator, "type", text="Extrapolation Mode") layout.separator() layout.operator("anim.channels_expand") @@ -699,6 +716,15 @@ class DOPESHEET_PT_gpencil_mode(LayersDopeSheetPanel, Panel): row = layout.row(align=True) row.prop(gpl, "opacity", text="Opacity", slider=True) + row = layout.row(align=True) + row.prop(gpl, "use_lights") + + +class DOPESHEET_PT_gpencil_layer_masks(LayersDopeSheetPanel, GreasePencilLayerMasksPanel, Panel): + bl_label = "Masks" + bl_parent_id = 'DOPESHEET_PT_gpencil_mode' + bl_options = {'DEFAULT_CLOSED'} + class DOPESHEET_PT_gpencil_layer_adjustments(LayersDopeSheetPanel, GreasePencilLayerAdjustmentsPanel, Panel): bl_label = "Adjustments" @@ -736,6 +762,7 @@ classes = ( DOPESHEET_MT_snap_pie, DOPESHEET_PT_filters, DOPESHEET_PT_gpencil_mode, + DOPESHEET_PT_gpencil_layer_masks, DOPESHEET_PT_gpencil_layer_adjustments, DOPESHEET_PT_gpencil_layer_relations, DOPESHEET_PT_gpencil_layer_display, diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 8dd0eaf5445..9a39d840149 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -20,6 +20,7 @@ from bpy.types import Header, Panel, Menu, UIList + class FILEBROWSER_HT_header(Header): bl_space_type = 'FILE_BROWSER' @@ -31,8 +32,7 @@ class FILEBROWSER_HT_header(Header): if st.active_operator is None: layout.template_header() - layout.menu("FILEBROWSER_MT_view") - layout.menu("FILEBROWSER_MT_select") + FILEBROWSER_MT_editor_menus.draw_collapsible(context, layout) # can be None when save/reload with a file selector open @@ -66,8 +66,9 @@ class FILEBROWSER_PT_display(Panel): if params.display_type == 'THUMBNAIL': layout.prop(params, "display_size", text="Size") else: - layout.prop(params, "show_details_size", text="Size") - layout.prop(params, "show_details_datetime", text="Date") + col = layout.column(heading="Columns", align=True) + col.prop(params, "show_details_size", text="Size") + col.prop(params, "show_details_datetime", text="Date") layout.prop(params, "recursion_level", text="Recursions") @@ -137,6 +138,9 @@ class FILEBROWSER_PT_filter(Panel): row = col.row() row.label(icon='FILE_TEXT') row.prop(params, "use_filter_text", text="Text Files", toggle=0) + row = col.row() + row.label(icon='FILE_VOLUME') + row.prop(params, "use_filter_volume", text="Volume Files", toggle=0) col.separator() @@ -148,7 +152,12 @@ class FILEBROWSER_PT_filter(Panel): if params.use_filter_blendid: row = col.row() row.label(icon='BLANK1') # Indentation - row.prop(params, "filter_id_category", text="") + + sub = row.column(align=True) + filter_id = params.filter_id + for identifier in dir(filter_id): + if identifier.startswith("category_"): + sub.prop(filter_id, identifier, toggle=True) col.separator() @@ -275,7 +284,7 @@ class FILEBROWSER_PT_bookmarks_recents(Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOLS' bl_category = "Bookmarks" - bl_label = "Recents" + bl_label = "Recent" @classmethod def poll(cls, context): @@ -314,8 +323,11 @@ class FILEBROWSER_PT_advanced_filter(Panel): layout.prop(params, "use_filter_blendid") if params.use_filter_blendid: layout.separator() - col = layout.column() - col.prop(params, "filter_id") + col = layout.column(align=True) + filter_id = params.filter_id + for identifier in dir(filter_id): + if identifier.startswith("filter_"): + col.prop(filter_id, identifier, toggle=True) class FILEBROWSER_PT_directory_path(Panel): @@ -399,6 +411,17 @@ class FILEBROWSER_PT_directory_path(Panel): ).region_type = 'TOOL_PROPS' +class FILEBROWSER_MT_editor_menus(Menu): + bl_idname = "FILEBROWSER_MT_editor_menus" + bl_label = "" + + def draw(self, _context): + layout = self.layout + + layout.menu("FILEBROWSER_MT_view") + layout.menu("FILEBROWSER_MT_select") + + class FILEBROWSER_MT_view(Menu): bl_label = "View" @@ -490,6 +513,7 @@ classes = ( FILEBROWSER_PT_bookmarks_recents, FILEBROWSER_PT_advanced_filter, FILEBROWSER_PT_directory_path, + FILEBROWSER_MT_editor_menus, FILEBROWSER_MT_view, FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index 2e853a287ea..c251d55714f 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -171,14 +171,10 @@ class GRAPH_MT_select(Menu): layout.separator() - props = layout.operator("graph.select_box") - props.axis_range = False - props.include_handles = False + layout.operator("graph.select_box") props = layout.operator("graph.select_box", text="Box Select (Axis Range)") props.axis_range = True - props.include_handles = False props = layout.operator("graph.select_box", text="Box Select (Include Handles)") - props.axis_range = False props.include_handles = True layout.operator("graph.select_circle") diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index c6f490f9d26..bc4665209aa 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -116,7 +116,7 @@ class IMAGE_MT_view(Menu): if show_uvedit: layout.operator("image.view_selected", text="Frame Selected") - layout.operator("image.view_all", text="Frame All") + layout.operator("image.view_all") layout.operator("image.view_all", text="Frame All Fit").fit_view = True layout.operator("image.view_center_cursor", text="Center View to Cursor") @@ -238,6 +238,11 @@ class IMAGE_MT_image(Menu): layout.separator() layout.operator("image.pack", text="Pack") + if ima: + layout.separator() + layout.operator("palette.extract_from_image", text="Extract Palette") + layout.operator("gpencil.image_to_grease_pencil", text="Generate Grease Pencil") + class IMAGE_MT_image_invert(Menu): bl_label = "Invert" @@ -379,6 +384,10 @@ class IMAGE_MT_uvs(Menu): layout.separator() + layout.operator("uv.reset") + + layout.separator() + class IMAGE_MT_uvs_select_mode(Menu): bl_label = "UV Select Mode" @@ -698,7 +707,7 @@ class IMAGE_HT_header(Header): layout.prop(tool_settings, "uv_select_mode", text="", expand=True) layout.prop(uvedit, "sticky_select_mode", icon_only=True) - MASK_MT_editor_menus.draw_collapsible(context, layout) + IMAGE_MT_editor_menus.draw_collapsible(context, layout) layout.separator_spacer() @@ -744,8 +753,8 @@ class IMAGE_HT_header(Header): row.operator("image.play_composite", icon='PLAY') -class MASK_MT_editor_menus(Menu): - bl_idname = "MASK_MT_editor_menus" +class IMAGE_MT_editor_menus(Menu): + bl_idname = "IMAGE_MT_editor_menus" bl_label = "" def draw(self, context): @@ -950,6 +959,7 @@ class IMAGE_PT_view_display_uv_edit_overlays(Panel): col = layout.column() col.prop(uvedit, "show_smooth_edges", text="Smooth") col.prop(uvedit, "show_modified_edges", text="Modified") + col.prop(uvedit, "uv_opacity") class IMAGE_PT_view_display_uv_edit_overlays_stretch(Panel): @@ -1460,7 +1470,7 @@ classes = ( IMAGE_MT_uvs_snap_pie, IMAGE_HT_tool_header, IMAGE_HT_header, - MASK_MT_editor_menus, + IMAGE_MT_editor_menus, IMAGE_PT_active_tool, IMAGE_PT_mask, IMAGE_PT_mask_layers, diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 2ce81ba8359..b5926692324 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -76,7 +76,8 @@ class NODE_HT_header(Header): layout.separator_spacer() - types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'GPENCIL'} + types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', + 'GPENCIL', 'VOLUME', 'HAIR', 'POINTCLOUD'} # disable material slot buttons when pinned, cannot find correct slot within id_from (#36589) # disable also when the selected object does not support materials has_material_slots = not snode.pin and ob_type in types_that_support_material @@ -150,6 +151,14 @@ class NODE_HT_header(Header): if snode_id: layout.prop(snode_id, "use_nodes") + elif snode.tree_type == 'SimulationNodeTree': + row = layout.row(align=True) + row.prop(snode, "simulation", text="") + row.operator("simulation.new", text="", icon='ADD') + simulation = snode.simulation + if simulation: + row.prop(snode.simulation, "use_fake_user", text="") + else: # Custom node tree is edited as independent ID block NODE_MT_editor_menus.draw_collapsible(context, layout) @@ -530,7 +539,12 @@ class NODE_PT_active_node_properties(Panel): layout.label(text="Inputs:") for socket in value_inputs: row = layout.row() - socket.draw(context, row, node, iface_(socket.name, socket.bl_rna.translation_context)) + socket.draw( + context, + row, + node, + iface_(socket.label if socket.label else socket.name, socket.bl_rna.translation_context), + ) class NODE_PT_texture_mapping(Panel): diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 11fb20d8b38..ee8015df273 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -19,6 +19,10 @@ # <pep8 compliant> import bpy from bpy.types import Header, Menu, Panel +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_iface as iface_, +) class OUTLINER_HT_header(Header): @@ -100,20 +104,26 @@ class OUTLINER_MT_editor_menus(Menu): layout.menu("OUTLINER_MT_edit_datablocks") -class OUTLINER_MT_context(Menu): - bl_label = "Outliner" +class OUTLINER_MT_context_menu(Menu): + bl_label = "Outliner Context Menu" + + def draw(self, context): + space = context.space_data - def draw(self, _context): layout = self.layout - layout.menu("OUTLINER_MT_context_view") + if space.display_mode == 'VIEW_LAYER': + OUTLINER_MT_collection_new.draw_without_context_menu(context, layout) + layout.separator() + + layout.menu("OUTLINER_MT_context_menu_view") layout.separator() layout.menu("INFO_MT_area") -class OUTLINER_MT_context_view(Menu): +class OUTLINER_MT_context_menu_view(Menu): bl_label = "View" def draw(self, _context): @@ -202,8 +212,8 @@ class OUTLINER_MT_collection(Menu): layout.separator() - layout.operator("outliner.collection_delete", text="Delete", icon='X').hierarchy = False - layout.operator("outliner.collection_delete", text="Delete Hierarchy").hierarchy = True + layout.operator("outliner.delete", text="Delete", icon='X') + layout.operator("outliner.collection_hierarchy_delete") layout.separator() @@ -232,21 +242,25 @@ class OUTLINER_MT_collection(Menu): layout.separator() - OUTLINER_MT_context.draw(self, context) + OUTLINER_MT_context_menu.draw(self, context) class OUTLINER_MT_collection_new(Menu): bl_label = "Collection" + @staticmethod + def draw_without_context_menu(context, layout): + layout.operator("outliner.collection_new", text="New Collection").nested = False + layout.operator("outliner.id_paste", text="Paste Data-Blocks", icon='PASTEDOWN') + def draw(self, context): layout = self.layout - layout.operator("outliner.collection_new", text="New").nested = False - layout.operator("outliner.id_paste", text="Paste", icon='PASTEDOWN') + self.draw_without_context_menu(context, layout) layout.separator() - OUTLINER_MT_context.draw(self, context) + OUTLINER_MT_context_menu.draw(self, context) class OUTLINER_MT_object(Menu): @@ -264,7 +278,7 @@ class OUTLINER_MT_object(Menu): layout.separator() - layout.operator("outliner.object_operation", text="Delete", icon='X').type = 'DELETE' + layout.operator("outliner.delete", text="Delete", icon='X') if space.display_mode == 'VIEW_LAYER' and not space.use_filter_collection: layout.operator("outliner.object_operation", text="Delete Hierarchy").type = 'DELETE_HIERARCHY' @@ -279,8 +293,10 @@ class OUTLINER_MT_object(Menu): if object_mode in {'EDIT', 'POSE'}: name = bpy.types.Object.bl_rna.properties["mode"].enum_items[object_mode].name - layout.operator("outliner.object_operation", text=f"{name:s} Set").type = 'OBJECT_MODE_ENTER' - layout.operator("outliner.object_operation", text=f"{name:s} Clear").type = 'OBJECT_MODE_EXIT' + layout.operator("outliner.object_operation", + text=iface_("%s Set", i18n_contexts.operator_default) % name).type = 'OBJECT_MODE_ENTER' + layout.operator("outliner.object_operation", + text=iface_("%s Clear", i18n_contexts.operator_default) % name).type = 'OBJECT_MODE_EXIT' del name layout.separator() @@ -293,7 +309,7 @@ class OUTLINER_MT_object(Menu): layout.separator() - OUTLINER_MT_context.draw(self, context) + OUTLINER_MT_context_menu.draw(self, context) class OUTLINER_PT_filter(Panel): @@ -391,6 +407,9 @@ class OUTLINER_PT_filter(Panel): if ( bpy.data.curves or bpy.data.metaballs or + (hasattr(bpy.data, "hairs") and bpy.data.hairs) or + (hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds) or + bpy.data.volumes or bpy.data.lightprobes or bpy.data.lattices or bpy.data.fonts or @@ -410,8 +429,8 @@ classes = ( OUTLINER_MT_collection_visibility, OUTLINER_MT_collection_view_layer, OUTLINER_MT_object, - OUTLINER_MT_context, - OUTLINER_MT_context_view, + OUTLINER_MT_context_menu, + OUTLINER_MT_context_menu_view, OUTLINER_PT_filter, ) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index af0c23e7892..ca25c29960c 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -29,6 +29,7 @@ from bpy.app.translations import ( ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, + AnnotationOnionSkin, ) from bl_ui.space_toolsystem_common import ( ToolActivePanelHelper, @@ -275,7 +276,7 @@ class SEQUENCER_MT_view(Menu): if is_sequencer_view: layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("sequencer.view_selected", text="Frame Selected") - layout.operator("sequencer.view_all", text="Frame All") + layout.operator("sequencer.view_all") layout.operator("view2d.zoom_border", text="Zoom") if is_preview: @@ -309,6 +310,7 @@ class SEQUENCER_MT_view(Menu): layout.prop(st, "show_seconds") layout.prop(st, "show_locked_time") layout.prop(st, "show_strip_offset") + layout.prop(st, "show_fcurves") layout.separator() layout.prop(st, "show_markers") @@ -404,6 +406,8 @@ class SEQUENCER_MT_select(Menu): layout.separator() layout.operator("sequencer.select_box", text="Box Select") + props = layout.operator("sequencer.select_box", text="Box Select (Include Handles)") + props.include_handles = True layout.separator() @@ -716,8 +720,8 @@ class SEQUENCER_MT_strip(Menu): layout.menu("SEQUENCER_MT_strip_transform") layout.separator() - layout.operator("sequencer.cut", text="Cut").type = 'SOFT' - layout.operator("sequencer.cut", text="Hold Cut").type = 'HARD' + layout.operator("sequencer.split", text="Split").type = 'SOFT' + layout.operator("sequencer.split", text="Hold Split").type = 'HARD' layout.separator() layout.operator("sequencer.copy", text="Copy") @@ -781,7 +785,7 @@ class SEQUENCER_MT_context_menu(Menu): layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("sequencer.cut", text="Cut").type = 'SOFT' + layout.operator("sequencer.split", text="Split").type = 'SOFT' layout.separator() @@ -842,7 +846,7 @@ class SEQUENCER_MT_context_menu(Menu): }: layout.separator() layout.menu("SEQUENCER_MT_strip_effect") - elif strip_type in 'MOVIE': + elif strip_type == 'MOVIE': layout.separator() layout.menu("SEQUENCER_MT_strip_movie") elif strip_type == 'IMAGE': @@ -1113,11 +1117,11 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): if i == strip.multicam_source: sub = row.row(align=True) sub.enabled = False - sub.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i + sub.operator("sequencer.split_multicam", text="%d" % i).camera = i else: sub_1 = row.row(align=True) sub_1.enabled = True - sub_1.operator("sequencer.cut_multicam", text=f"{i:d}").camera = i + sub_1.operator("sequencer.split_multicam", text="%d" % i).camera = i if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW: for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)): @@ -1135,6 +1139,8 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): col = layout.column(align=True) if strip_type == 'SPEED': col.prop(strip, "multiply_speed") + col.prop(strip, "frame_interpolation_mode") + elif strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}: col.prop(strip, "use_default_fade", text="Default fade") if not strip.use_default_fade: @@ -1684,18 +1690,15 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): layout = self.layout strip = act_strip(context) - layout.active = not strip.mute - - split = layout.split() + layout.use_property_split = True + layout.use_property_decorate = False - col = split.column() - col.alignment = 'RIGHT' - col.label(text="Mirror") + layout.active = not strip.mute - col = split.column() - row = col.row(align=True) - row.prop(strip, "use_flip_x", text="X", toggle=True) - row.prop(strip, "use_flip_y", text="Y", toggle=True) + row = layout.row(heading="Mirror") + sub = row.row(align=True) + sub.prop(strip, "use_flip_x", text="X", toggle=True) + sub.prop(strip, "use_flip_y", text="Y", toggle=True) class SEQUENCER_PT_adjust_video(SequencerButtonsPanel, Panel): @@ -1799,12 +1802,12 @@ class SEQUENCER_PT_cache_settings(SequencerButtonsPanel, Panel): ed = context.scene.sequence_editor - col = layout.column() + col = layout.column(heading="Cache", align=True) - col.prop(ed, "use_cache_raw") - col.prop(ed, "use_cache_preprocessed") - col.prop(ed, "use_cache_composite") - col.prop(ed, "use_cache_final") + col.prop(ed, "use_cache_raw", text="Raw") + col.prop(ed, "use_cache_preprocessed", text="Pre-Processed") + col.prop(ed, "use_cache_composite", text="Composite") + col.prop(ed, "use_cache_final", text="Final") col.separator() col.prop(ed, "recycle_max_cost") @@ -1868,21 +1871,19 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel): flow = layout.column_flow() if ed.proxy_storage == 'PER_STRIP': - flow.prop(proxy, "use_proxy_custom_directory") - flow.prop(proxy, "use_proxy_custom_file") - + col = layout.column(heading="Custom Proxy") + col.prop(proxy, "use_proxy_custom_directory", text="Directory") if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file: - flow.prop(proxy, "directory") + col.prop(proxy, "directory") + col.prop(proxy, "use_proxy_custom_file", text="File") if proxy.use_proxy_custom_file: - flow.prop(proxy, "filepath") + col.prop(proxy, "filepath") - box = layout.box() - row = box.row(align=True) - row.prop(strip.proxy, "build_25") - row.prop(strip.proxy, "build_75") - row = box.row(align=True) - row.prop(strip.proxy, "build_50") - row.prop(strip.proxy, "build_100") + row = layout.row(heading="Resolutions", align=True) + row.prop(strip.proxy, "build_25", toggle=True) + row.prop(strip.proxy, "build_50", toggle=True) + row.prop(strip.proxy, "build_75", toggle=True) + row.prop(strip.proxy, "build_100", toggle=True) layout.use_property_split = True layout.use_property_decorate = False @@ -1923,10 +1924,10 @@ class SEQUENCER_PT_strip_cache(SequencerButtonsPanel, Panel): strip = act_strip(context) layout.active = strip.override_cache_settings - col = layout.column() - col.prop(strip, "use_cache_raw") - col.prop(strip, "use_cache_preprocessed") - col.prop(strip, "use_cache_composite") + col = layout.column(heading="Cache") + col.prop(strip, "use_cache_raw", text="Raw") + col.prop(strip, "use_cache_preprocessed", text="Pre-Processed") + col.prop(strip, "use_cache_composite", text="Composite") class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel): @@ -1972,7 +1973,9 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel): col.prop(st, "show_separate_color") col.prop(st, "proxy_render_size") - col.prop(ed, "use_prefetch") + + if ed: + col.prop(ed, "use_prefetch") class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): @@ -1980,6 +1983,12 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): bl_category = "View" bl_options = {'DEFAULT_CLOSED'} + @classmethod + def poll(cls, context): + if not context.scene.sequence_editor: + return False + return SequencerButtonsPanel_Output.poll(context) + def draw_header(self, context): scene = context.scene ed = scene.sequence_editor @@ -2084,10 +2093,12 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): box = layout.box() row = box.row() + row.use_property_decorate = False row.prop(mod, "show_expanded", text="", emboss=False) row.prop(mod, "name", text="") row.prop(mod, "mute", text="") + row.use_property_decorate = True sub = row.row(align=True) props = sub.operator("sequencer.strip_modifier_move", text="", icon='TRIA_UP') @@ -2159,6 +2170,33 @@ class SEQUENCER_PT_annotation(AnnotationDataPanel, SequencerButtonsPanel_Output, # But, it should only show up when there are images in the preview region +class SEQUENCER_PT_annotation_onion(AnnotationOnionSkin, SequencerButtonsPanel_Output, Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'UI' + bl_category = "View" + + @staticmethod + def has_preview(context): + st = context.space_data + return st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'} + + @classmethod + def poll(cls, context): + if context.annotation_data_owner is None: + return False + elif type(context.annotation_data_owner) is bpy.types.Object: + return False + else: + gpl = context.active_annotation_layer + if gpl is None: + return False + + return cls.has_preview(context) + + # 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_custom_props(SequencerButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} _context_path = "scene.sequence_editor.active_strip" @@ -2232,6 +2270,7 @@ classes = ( SEQUENCER_PT_view_safe_areas_center_cut, SEQUENCER_PT_annotation, + SEQUENCER_PT_annotation_onion, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index b7c5dcd5437..f6f22ad464f 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -129,18 +129,21 @@ class TEXT_PT_properties(Panel): layout.use_property_decorate = False st = context.space_data - flow = layout.column_flow() if not st.text: - flow.active = False - row = flow.row(align=True) + layout.active = False + st = context.space_data - row.prop(st, "show_margin", text="Margin") - rowsub = row.row() - rowsub.active = st.show_margin - rowsub.prop(st, "margin_column", text="") - flow.prop(st, "font_size") - flow.prop(st, "tab_width") + col = layout.column(align=False, heading="Margin") + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(st, "show_margin", text="") + sub = sub.row(align=True) + sub.active = st.show_margin + sub.prop(st, "margin_column", text="") + + layout.prop(st, "font_size") + layout.prop(st, "tab_width") text = st.text if text: diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 629958a783e..257e0c26a5d 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -142,6 +142,7 @@ class TIME_MT_view(Menu): layout.separator() layout.prop(scene, "show_keys_from_selected_only") + layout.prop(st.dopesheet, "show_only_errors") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 4dc724299f0..e0651dcac2b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -68,8 +68,8 @@ ToolDef = namedtuple( "idname", # The name to display in the interface. "label", - # Description (for tooltip), when not set, use the description of 'operator', - # may be a string or a 'function(context, item, keymap) -> string'. + # Description (for tool-tip), when not set, use the description of 'operator', + # may be a string or a 'function(context, item, key-map) -> string'. "description", # The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon. "icon", @@ -77,13 +77,34 @@ ToolDef = namedtuple( "cursor", # An optional gizmo group to activate when the tool is set or None for no gizmo. "widget", - # Optional keymap for tool, either: - # - A function that populates a keymaps passed in as an argument. + # Optional key-map for tool, possible values are: + # + # - ``None`` when the tool doesn't have a key-map. + # Also the default value when no key-map value is defined. + # + # - A string literal for the key-map name, the key-map items are located in the default key-map. + # + # - ``()`` an empty tuple for a default name. + # This is convenience functionality for generating a key-map name. + # So if a tool name is "Bone Size", in "Edit Armature" mode for the "3D View", + # All of these values are combined into an id, e.g: + # "3D View Tool: Edit Armature, Bone Envelope" + # + # Typically searching for a string ending with the tool name + # in the default key-map will lead you to the key-map for a tool. + # + # - A function that populates a key-maps passed in as an argument. + # # - A tuple filled with triple's of: # ``(operator_id, operator_properties, keymap_item_args)``. # + # Use this to define the key-map in-line. + # + # Note that this isn't used for Blender's built in tools which use the built-in key-map. + # Keep this functionality since it's likely useful for add-on key-maps. + # # Warning: currently 'from_dict' this is a list of one item, - # so internally we can swap the keymap function for the keymap it's self. + # so internally we can swap the key-map function for the key-map it's self. # This isn't very nice and may change, tool definitions shouldn't care about this. "keymap", # Optional data-block associated with this tool. @@ -216,29 +237,52 @@ class ToolSelectPanelHelper: else: return 0 + # tool flattening + # + # usually 'tools' is already expanded into ToolDef + # but when registering a tool, this can still be a function + # (_tools_flatten is usually called with cls.tools_from_context(context) + # [that already yields from the function]) + # so if item is still a function (e.g._defs_XXX.generate_from_brushes) + # seems like we cannot expand here (have no context yet) + # if we yield None here, this will risk running into duplicate tool bl_idname [in register_tool()] + # but still better than erroring out @staticmethod def _tools_flatten(tools): - for item in tools: - if type(item) is tuple: - yield from item - else: - # May be None. - yield item + for item_parent in tools: + if item_parent is None: + yield None + for item in item_parent if (type(item_parent) is tuple) else (item_parent,): + if item is None or _item_is_fn(item): + yield None + else: + yield item @staticmethod def _tools_flatten_with_tool_index(tools): - for item in tools: - if type(item) is tuple: - i = 0 - for sub_item in item: - if sub_item is None: - yield None, -1 - else: - yield sub_item, i - i += 1 - else: - # May be None. - yield item, -1 + for item_parent in tools: + if item_parent is None: + yield None, -1 + i = 0 + for item in item_parent if (type(item_parent) is tuple) else (item_parent,): + if item is None or _item_is_fn(item): + yield None, -1 + else: + yield item, i + i += 1 + + # Special internal function, gives use items that contain keymaps. + @staticmethod + def _tools_flatten_with_keymap(tools): + for item_parent in tools: + if item_parent is None: + continue + for item in item_parent if (type(item_parent) is tuple) else (item_parent,): + # skip None or generator function + if item is None or _item_is_fn(item): + continue + if item.keymap is not None: + yield item @classmethod def _tool_get_active(cls, context, space_type, mode, with_icon=False): @@ -405,32 +449,24 @@ class ToolSelectPanelHelper: return context.button_operator.name @classmethod - def _km_action_simple(cls, kc, context_descr, label, keymap_fn): + def _km_action_simple(cls, kc_default, kc, context_descr, label, keymap_fn): km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {label:s}" km = kc.keymaps.get(km_idname) + km_kwargs = dict(space_type=cls.bl_space_type, region_type='WINDOW', tool=True) if km is None: - km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW', tool=True) + km = kc.keymaps.new(km_idname, **km_kwargs) keymap_fn[0](km) keymap_fn[0] = km.name - # Special internal function, gives use items that contain keymaps. - @staticmethod - def _tools_flatten_with_keymap(tools): - for item_parent in tools: - if item_parent is None: - continue - for item in item_parent if (type(item_parent) is tuple) else (item_parent,): - # skip None or generator function - if item is None or _item_is_fn(item): - continue - if item.keymap is not None: - yield item + # Ensure we have a default key map, so the add-ons keymap is properly overlayed. + if kc_default is not kc: + kc_default.keymaps.new(km_idname, **km_kwargs) @classmethod def register(cls): wm = bpy.context.window_manager # Write into defaults, users may modify in preferences. - kc = wm.keyconfigs.default + kc_default = wm.keyconfigs.default # Track which tool-group was last used for non-active groups. # Blender stores the active tool-group index. @@ -439,7 +475,7 @@ class ToolSelectPanelHelper: cls._tool_group_active = {} # ignore in background mode - if kc is None: + if kc_default is None: return for context_mode, tools in cls.tools_all(): @@ -451,7 +487,7 @@ class ToolSelectPanelHelper: for item in cls._tools_flatten_with_keymap(tools): keymap_data = item.keymap if callable(keymap_data[0]): - cls._km_action_simple(kc, context_descr, item.label, keymap_data) + cls._km_action_simple(kc_default, kc_default, context_descr, item.label, keymap_data) @classmethod def keymap_ui_hierarchy(cls, context_mode): diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index c1ad196b555..e64a7c9731b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -18,6 +18,9 @@ # <pep8 compliant> +# For documentation on tool definitions: see "bl_ui.space_toolsystem_common.ToolDef" +# where there are comments for each field and their use. + # For now group all tools together # we may want to move these into per space-type files. # @@ -125,7 +128,7 @@ class _defs_view3d_generic: "\u2022 Drag ruler segment to measure an angle.\n" "\u2022 {} to remove the active ruler.\n" "\u2022 Ctrl while dragging to snap.\n" - "\u2022 Shift while dragging to measure surface thickness." + "\u2022 Shift while dragging to measure surface thickness" ).format( kmi_to_string_or_none(kmi_add), kmi_to_string_or_none(kmi_remove), @@ -144,10 +147,8 @@ class _defs_view3d_generic: class _defs_annotate: def draw_settings_common(context, layout, tool): - if type(context.gpencil_data_owner) is bpy.types.Object: - gpd = context.scene.grease_pencil - else: - gpd = context.gpencil_data + gpd = context.annotation_data + region_type = context.region.type if gpd is not None: if gpd.layers.active_note is not None: @@ -158,20 +159,27 @@ class _defs_annotate: else: text = "" - gpl = context.active_gpencil_layer + gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - sub = layout.row(align=True) - sub.ui_units_x = 8 - - sub.prop(gpl, "color", text="") - sub.popover( - panel="TOPBAR_PT_annotation_layers", - text=text, - ) + if context.space_data.type == 'VIEW_3D': + if region_type == 'TOOL_HEADER': + sub = layout.split(align=True, factor=0.5) + sub.ui_units_x = 6.5 + sub.prop(gpl, "color", text="") + else: + sub = layout.row(align=True) + sub.prop(gpl, "color", text="") + sub.popover( + panel="TOPBAR_PT_annotation_layers", + text=text, + ) + else: + layout.prop(gpl, "color", text="") - tool_settings = context.tool_settings space_type = tool.space_type + tool_settings = context.tool_settings + if space_type == 'VIEW_3D': layout.separator() @@ -182,6 +190,29 @@ class _defs_annotate: elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: row.prop(tool_settings, "use_gpencil_stroke_endpoints") + if tool.idname == "builtin.annotate_line": + layout.separator() + + props = tool.operator_properties("gpencil.annotate") + if region_type == 'TOOL_HEADER': + row = layout.row() + row.ui_units_x = 15 + row.prop(props, "arrowstyle_start", text="Start") + row.separator() + row.prop(props, "arrowstyle_end", text="End") + else: + col = layout.row().column(align=True) + col.prop(props, "arrowstyle_start", text="Style Start") + col.prop(props, "arrowstyle_end", text="End") + elif tool.idname == "builtin.annotate" and region_type != 'TOOL_HEADER': + layout.separator() + props = tool.operator_properties("gpencil.annotate") + layout.prop(props, "use_stabilizer", text="Stabilize Stroke") + col = layout.column(align=False) + col.active = props.use_stabilizer + col.prop(props, "stabilizer_radius", text="Radius", slider=True) + col.prop(props, "stabilizer_factor", text="Factor", slider=True) + @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) def scribble(*, draw_settings): return dict( @@ -460,6 +491,7 @@ class _defs_edit_armature: return dict( idname="builtin.extrude_to_cursor", label="Extrude to Cursor", + cursor='CROSSHAIR', icon="ops.armature.extrude_cursor", widget=None, keymap=(), @@ -611,15 +643,6 @@ class _defs_edit_mesh: region_type = context.region.type if not extra: - if props.offset_type == 'PERCENT': - layout.prop(props, "offset_pct") - else: - offset_text = "Width" - if props.offset_type == 'DEPTH': - offset_text = "Depth" - elif props.offset_type == 'OFFSET': - offset_text = "Offset" - layout.prop(props, "offset", text=offset_text) if region_type == 'TOOL_HEADER': layout.prop(props, "offset_type", text="") else: @@ -637,9 +660,10 @@ class _defs_edit_mesh: layout.prop(props, "vertex_only") layout.prop(props, "clamp_overlap") layout.prop(props, "loop_slide") - layout.prop(props, "mark_seam") - layout.prop(props, "mark_sharp") layout.prop(props, "harden_normals") + col = layout.column(heading="Mark") + col.prop(props, "mark_seam", text="Seam") + col.prop(props, "mark_sharp", text="Sharp") layout.prop(props, "material") @@ -680,6 +704,19 @@ class _defs_edit_mesh: ) @ToolDef.from_fn + def extrude_dissolve_and_intersect(): + return dict( + idname="builtin.extrude_dissolve_and_intersect", + label="Extrude Dissolve and Intersect", + description=( + "Extrude, dissolves edges whose faces form a flat surface and intersect new edges" + ), + icon="none", + widget="VIEW3D_GGT_tool_generic_handle_normal", + keymap=(), + ) + + @ToolDef.from_fn def extrude_normals(): def draw_settings(_context, layout, tool): props = tool.operator_properties("mesh.extrude_region_shrink_fatten") @@ -714,6 +751,7 @@ class _defs_edit_mesh: return dict( idname="builtin.extrude_to_cursor", label="Extrude to Cursor", + cursor='CROSSHAIR', icon="ops.mesh.dupli_extrude_cursor", widget=None, keymap=(), @@ -853,23 +891,61 @@ class _defs_edit_curve: @ToolDef.from_fn def draw(): - def draw_settings(context, layout, _tool): + def draw_settings(context, layout, tool, *, extra=False): # Tool settings initialize operator options. tool_settings = context.tool_settings cps = tool_settings.curve_paint_settings + region_type = context.region.type - col = layout.column() + if region_type == 'TOOL_HEADER': + if not extra: + layout.prop(cps, "curve_type", text="") + layout.prop(cps, "depth_mode", expand=True) + layout.popover("TOPBAR_PT_tool_settings_extra", text="...") + return - col.prop(cps, "curve_type") + layout.use_property_split = True + layout.use_property_decorate = False + if region_type != 'TOOL_HEADER': + layout.prop(cps, "curve_type") + layout.separator() if cps.curve_type == 'BEZIER': - col.prop(cps, "error_threshold") - col.prop(cps, "fit_method") - col.prop(cps, "use_corners_detect") + layout.prop(cps, "fit_method") + layout.prop(cps, "error_threshold") + if region_type != 'TOOL_HEADER': + row = layout.row(heading="Detect Corners", align=True) + else: + row = layout.row(heading="Corners", align=True) + row.prop(cps, "use_corners_detect", text="") + sub = row.row(align=True) + sub.active = cps.use_corners_detect + sub.prop(cps, "corner_angle", text="") + layout.separator() + + + col = layout.column(align=True) + col.prop(cps, "radius_taper_start", text="Taper Start", slider=True) + col.prop(cps, "radius_taper_end", text="End", slider=True) + col = layout.column(align=True) + col.prop(cps, "radius_min", text="Radius Min") + col.prop(cps, "radius_max", text="Max") + col.prop(cps, "use_pressure_radius") + + layout.separator() + + if region_type != 'TOOL_HEADER': + row = layout.row() + row.prop(cps, "depth_mode", expand=True) + if cps.depth_mode == 'SURFACE': + col = layout.column() + col.prop(cps, "surface_offset") + col.prop(cps, "use_offset_absolute") + col.prop(cps, "use_stroke_endpoints") + if cps.use_stroke_endpoints: + colsub = layout.column(align=True) + colsub.prop(cps, "surface_plane") - col = layout.row() - col.active = cps.use_corners_detect - col.prop(cps, "corner_angle") return dict( idname="builtin.draw", @@ -896,7 +972,8 @@ class _defs_edit_curve: def extrude_cursor(): return dict( idname="builtin.extrude_cursor", - label="Extrude Cursor", + label="Extrude to Cursor", + cursor='CROSSHAIR', icon="ops.curve.extrude_cursor", widget=None, keymap=(), @@ -1037,6 +1114,12 @@ class _defs_sculpt: layout.prop(props, "type", expand=False) layout.prop(props, "strength") layout.prop(props, "deform_axis") + layout.prop(props, "use_face_sets") + if (props.type == "SURFACE_SMOOTH"): + layout.prop(props, "surface_smooth_shape_preservation", expand=False) + layout.prop(props, "surface_smooth_current_vertex", expand=False) + if (props.type == "SHARPEN"): + layout.prop(props, "sharpen_smooth_ratio", expand=False) return dict( idname="builtin.mesh_filter", @@ -1137,8 +1220,25 @@ class _defs_weight_paint: def draw_settings(context, layout, tool): brush = context.tool_settings.weight_paint.brush if brush is not None: - layout.prop(brush, "weight", slider=True) - layout.prop(brush, "strength", slider=True) + from bl_ui.properties_paint_common import UnifiedPaintPanel + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "weight", + unified_name="use_unified_weight", + slider=True, + header=True + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + header=True + ) + props = tool.operator_properties("paint.weight_gradient") layout.prop(props, "type", expand=True) @@ -1444,6 +1544,11 @@ class _defs_gpencil_paint: @ToolDef.from_fn def eyedropper(): + def draw_settings(context, layout, tool): + props = tool.operator_properties("ui.eyedropper_gpencil_color") + row = layout.row() + row.use_property_split = False + row.prop(props, "mode", expand=True) return dict( idname="builtin.eyedropper", label="Eyedropper", @@ -1451,10 +1556,22 @@ class _defs_gpencil_paint: cursor='EYEDROPPER', widget=None, keymap=(), + draw_settings=draw_settings, ) class _defs_gpencil_edit: + def is_segment(context): + ts = context.scene.tool_settings + if context.mode == 'EDIT_GPENCIL': + return ts.gpencil_selectmode_edit == 'SEGMENT' + elif context.mode == 'SCULPT_GPENCIL': + return ts.use_gpencil_select_mask_segment + elif context.mode == 'VERTEX_GPENCIL': + return ts.use_gpencil_vertex_select_mask_segment + else: + return False + @ToolDef.from_fn def bend(): return dict( @@ -1468,7 +1585,8 @@ class _defs_gpencil_edit: @ToolDef.from_fn def select(): def draw_settings(context, layout, _tool): - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + if _defs_gpencil_edit.is_segment(context): + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return dict( idname="builtin.select", label="Tweak", @@ -1485,7 +1603,8 @@ class _defs_gpencil_edit: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + if _defs_gpencil_edit.is_segment(context): + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return dict( idname="builtin.select_box", label="Select Box", @@ -1502,7 +1621,8 @@ class _defs_gpencil_edit: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + if _defs_gpencil_edit.is_segment(context): + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -1520,7 +1640,8 @@ class _defs_gpencil_edit: row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) layout.prop(props, "radius") - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + if _defs_gpencil_edit.is_segment(context): + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d @@ -1583,6 +1704,23 @@ class _defs_gpencil_edit: draw_settings=_template_widget.VIEW3D_GGT_xform_extrude.draw_settings, ) + @ToolDef.from_fn + def transform_fill(): + def draw_settings(context, layout, tool): + props = tool.operator_properties("gpencil.transform_fill") + row = layout.row() + row.use_property_split = False + row.prop(props, "mode", expand=True) + + return dict( + idname="builtin.transform_fill", + label="Transform Fill", + icon="ops.gpencil.transform_fill", + cursor='DEFAULT', + widget=None, + keymap=(), + draw_settings=draw_settings, + ) class _defs_gpencil_sculpt: @@ -1602,11 +1740,10 @@ class _defs_gpencil_sculpt: context, idname_prefix="builtin_brush.", icon_prefix="ops.gpencil.sculpt_", - type=bpy.types.GPencilSculptSettings, - attr="sculpt_tool", + type=bpy.types.Brush, + attr="gpencil_sculpt_tool", tooldef_keywords=dict( operator="gpencil.sculpt_paint", - keymap="3D View Tool: Sculpt Gpencil, Paint", ), ) @@ -1619,11 +1756,37 @@ class _defs_gpencil_weight: context, idname_prefix="builtin_brush.", icon_prefix="ops.gpencil.sculpt_", - type=bpy.types.GPencilSculptSettings, - attr="weight_tool", + type=bpy.types.Brush, + attr="gpencil_weight_tool", tooldef_keywords=dict( - operator="gpencil.sculpt_paint", - keymap="3D View Tool: Sculpt Gpencil, Paint", + operator="gpencil.weight_paint", + ), + ) + + +class _defs_gpencil_vertex: + + @staticmethod + def poll_select_mask(context): + if context is None: + return True + ob = context.active_object + ts = context.scene.tool_settings + return ob and ob.type == 'GPENCIL' and (ts.use_gpencil_vertex_select_mask_point or + ts.use_gpencil_vertex_select_mask_stroke or + ts.use_gpencil_vertex_select_mask_segment) + + @staticmethod + def generate_from_brushes(context): + return generate_from_enum_ex( + context, + idname_prefix="builtin_brush.", + icon_prefix="brush.paint_vertex.", + type=bpy.types.Brush, + attr="gpencil_vertex_tool", + cursor='DOT', + tooldef_keywords=dict( + operator="gpencil.vertex_paint", ), ) @@ -1710,24 +1873,42 @@ class _defs_node_edit: keymap="Node Tool: Links Cut", ) + class _defs_sequencer_generic: @ToolDef.from_fn - def cut(): + def blade(): def draw_settings(_context, layout, tool): - props = tool.operator_properties("sequencer.cut") + props = tool.operator_properties("sequencer.split") row = layout.row() row.use_property_split = False row.prop(props, "type", expand=True) return dict( - idname="builtin.cut", - label="Cut", - icon="ops.mesh.knife_tool", + idname="builtin.blade", + label="Blade", + icon="ops.sequencer.blade", + cursor='CROSSHAIR', widget=None, - keymap="Sequencer Tool: Cut", + keymap="Sequencer Tool: Blade", + draw_settings=draw_settings, + ) + + @ToolDef.from_fn + def sample(): + def draw_settings(_context, layout, tool): + props = tool.operator_properties("sequencer.sample") + return dict( + idname="builtin.sample", + label="Sample", + description=( + "Sample pixel values under the cursor" + ), + icon="ops.paint.weight_sample", # XXX, needs own icon. + keymap="Sequencer Tool: Sample", draw_settings=draw_settings, ) + class _defs_sequencer_select: @ToolDef.from_fn def select(): @@ -1839,6 +2020,8 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'PAINT': [ _defs_texture_paint.generate_from_brushes, + None, + *_tools_annotate, ], } @@ -2015,6 +2198,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, ( _defs_edit_mesh.extrude, + _defs_edit_mesh.extrude_dissolve_and_intersect, _defs_edit_mesh.extrude_normals, _defs_edit_mesh.extrude_individual, _defs_edit_mesh.extrude_cursor, @@ -2188,6 +2372,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_gpencil_edit.tosphere, ), None, + _defs_gpencil_edit.transform_fill, + None, *_tools_annotate, ], 'SCULPT_GPENCIL': [ @@ -2205,6 +2391,17 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, ], + 'VERTEX_GPENCIL': [ + _defs_gpencil_vertex.generate_from_brushes, + None, + *_tools_annotate, + None, + lambda context: ( + VIEW3D_PT_tools_active._tools_gpencil_select + if _defs_gpencil_vertex.poll_select_mask(context) + else () + ), + ], } class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): bl_space_type = 'SEQUENCE_EDITOR' @@ -2253,19 +2450,22 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ ], 'PREVIEW': [ + _defs_sequencer_generic.sample, *_tools_annotate, ], 'SEQUENCER': [ *_tools_select, - _defs_sequencer_generic.cut, + _defs_sequencer_generic.blade, ], 'SEQUENCER_PREVIEW': [ + _defs_sequencer_generic.sample, *_tools_select, *_tools_annotate, - _defs_sequencer_generic.cut, + _defs_sequencer_generic.blade, ], } + classes = ( IMAGE_PT_tools_active, NODE_PT_tools_active, diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index fd46bd53cd2..9aedd7ef0b3 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -165,11 +165,11 @@ class TOPBAR_PT_gpencil_layers(Panel): srow = col.row(align=True) srow.prop(gpl, "opacity", text="Opacity", slider=True) - srow.prop(gpl, "mask_layer", text="", - icon='MOD_MASK' if gpl.mask_layer else 'LAYER_ACTIVE') + srow.prop(gpl, "use_mask_layer", text="", + icon='MOD_MASK' if gpl.use_mask_layer else 'LAYER_ACTIVE') srow = col.row(align=True) - srow.prop(gpl, "use_solo_mode", text="Show Only On Keyframed") + srow.prop(gpl, "use_lights") col = row.column() @@ -207,7 +207,8 @@ class TOPBAR_MT_editor_menus(Menu): def draw(self, context): layout = self.layout - if context.area.show_menus: + # Allow calling this menu directly (this might not be a header area). + if getattr(context.area, "show_menus", False): layout.menu("TOPBAR_MT_app", text="", icon='BLENDER') else: layout.menu("TOPBAR_MT_app", text="Blender") @@ -228,19 +229,16 @@ class TOPBAR_MT_app(Menu): layout = self.layout layout.operator("wm.splash") + layout.operator("wm.splash_about") layout.separator() - layout.menu("TOPBAR_MT_app_support") - - layout.separator() - - layout.menu("TOPBAR_MT_app_about") + layout.operator("preferences.app_template_install", + text="Install Application Template...") layout.separator() - layout.operator("preferences.app_template_install", - text="Install Application Template...") + layout.menu("TOPBAR_MT_app_system") class TOPBAR_MT_file_cleanup(Menu): @@ -402,43 +400,24 @@ class TOPBAR_MT_file_defaults(Menu): props.app_template = app_template -class TOPBAR_MT_app_about(Menu): - bl_label = "About" +# Include technical operators here which would otherwise have no way for users to access. +class TOPBAR_MT_app_system(Menu): + bl_label = "System" def draw(self, _context): layout = self.layout - layout.operator("wm.url_open_preset", text="Release Notes", - icon='URL').type = 'RELEASE_NOTES' + layout.operator("script.reload") layout.separator() - layout.operator("wm.url_open_preset", - text="Blender Website", icon='URL').type = 'BLENDER' - layout.operator("wm.url_open_preset", text="Credits", - icon='URL').type = 'CREDITS' + layout.operator("wm.memory_statistics") + layout.operator("wm.debug_menu") + layout.operator_menu_enum("wm.redraw_timer", "type") layout.separator() - layout.operator( - "wm.url_open", text="License", icon='URL', - ).url = "https://www.blender.org/about/license/" - - -class TOPBAR_MT_app_support(Menu): - bl_label = "Support Blender" - - def draw(self, _context): - layout = self.layout - - layout.operator("wm.url_open_preset", - text="Development Fund", icon='FUND').type = 'FUND' - - layout.separator() - - layout.operator( - "wm.url_open", text="Blender Store", icon='URL', - ).url = "https://store.blender.org" + layout.operator("screen.spacedata_cleanup") class TOPBAR_MT_templates_more(Menu): @@ -452,6 +431,7 @@ class TOPBAR_MT_templates_more(Menu): class TOPBAR_MT_file_import(Menu): bl_idname = "TOPBAR_MT_file_import" bl_label = "Import" + bl_owner_use_filter = False def draw(self, _context): if bpy.app.build_options.collada: @@ -464,6 +444,7 @@ class TOPBAR_MT_file_import(Menu): class TOPBAR_MT_file_export(Menu): bl_idname = "TOPBAR_MT_file_export" bl_label = "Export" + bl_owner_use_filter = False def draw(self, context): if bpy.app.build_options.collada: @@ -553,6 +534,8 @@ class TOPBAR_MT_edit(Menu): def draw(self, context): layout = self.layout + show_developer = context.preferences.view.show_developer_ui + layout.operator("ed.undo") layout.operator("ed.redo") @@ -571,8 +554,9 @@ class TOPBAR_MT_edit(Menu): layout.separator() - layout.operator("wm.search_menu", - text="Operator Search...", icon='VIEWZOOM') + layout.operator("wm.search_menu", text="Menu Search...", icon='VIEWZOOM') + if show_developer: + layout.operator("wm.search_operator", text="Operator Search...", icon='VIEWZOOM') layout.separator() @@ -824,8 +808,7 @@ classes = ( TOPBAR_MT_workspace_menu, TOPBAR_MT_editor_menus, TOPBAR_MT_app, - TOPBAR_MT_app_about, - TOPBAR_MT_app_support, + TOPBAR_MT_app_system, TOPBAR_MT_file, TOPBAR_MT_file_new, TOPBAR_MT_file_recover, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index ad5e7b5442c..153533dbde6 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -37,18 +37,13 @@ class USERPREF_HT_header(Header): def draw_buttons(layout, context): prefs = context.preferences - layout.scale_x = 1.0 - layout.scale_y = 1.0 layout.operator_context = 'EXEC_AREA' - row = layout.row() - row.menu("USERPREF_MT_save_load", text="", icon='COLLAPSEMENU') - if prefs.use_preferences_save and (not bpy.app.use_userpref_skip_save_on_exit): pass else: # Show '*' to let users know the preferences have been modified. - row.operator( + layout.operator( "wm.save_userpref", text="Save Preferences{:s}".format(" *" if prefs.is_dirty else ""), ) @@ -59,7 +54,10 @@ class USERPREF_HT_header(Header): layout.template_header() + USERPREF_MT_editor_menus.draw_collapsible(context, layout) + layout.separator_spacer() + self.draw_buttons(layout, context) @@ -84,6 +82,25 @@ class USERPREF_PT_navigation_bar(Panel): col.prop(prefs, "active_section", expand=True) +class USERPREF_MT_editor_menus(Menu): + bl_idname = "USERPREF_MT_editor_menus" + bl_label = "" + + def draw(self, _context): + layout = self.layout + layout.menu("USERPREF_MT_view") + layout.menu("USERPREF_MT_save_load", text="Preferences") + + +class USERPREF_MT_view(Menu): + bl_label = "View" + + def draw(self, context): + layout = self.layout + + layout.menu("INFO_MT_area") + + class USERPREF_MT_save_load(Menu): bl_label = "Save & Load" @@ -125,11 +142,10 @@ class USERPREF_PT_save_preferences(Panel): return False def draw(self, context): - layout = self.layout + layout = self.layout.row() layout.operator_context = 'EXEC_AREA' - layout.scale_x = 1.3 - layout.scale_y = 1.3 + layout.menu("USERPREF_MT_save_load", text="", icon='COLLAPSEMENU') USERPREF_HT_header.draw_buttons(layout, context) @@ -184,20 +200,16 @@ class USERPREF_PT_interface_display(InterfacePanel, CenterAlignMixIn, Panel): prefs = context.preferences view = prefs.view - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(view, "ui_scale", text="Resolution Scale") - flow.prop(view, "ui_line_width", text="Line Width") - - layout.separator() + col = layout.column() - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col.prop(view, "ui_scale", text="Resolution Scale") + col.prop(view, "ui_line_width", text="Line Width") + col.prop(view, "show_splash", text="Splash Screen") + col.prop(view, "show_developer_ui") - flow.prop(view, "show_splash", text="Splash Screen") - flow.prop(view, "show_tooltips") - flow.prop(view, "show_tooltips_python") - flow.prop(view, "show_developer_ui") - flow.prop(view, "show_large_cursors") + col = layout.column(heading="Tooltips") + col.prop(view, "show_tooltips") + col.prop(view, "show_tooltips_python") class USERPREF_PT_interface_text(InterfacePanel, CenterAlignMixIn, Panel): @@ -227,25 +239,17 @@ class USERPREF_PT_interface_translation(InterfacePanel, CenterAlignMixIn, Panel) def poll(cls, context): return bpy.app.build_options.international - def draw_header(self, context): - prefs = context.preferences - view = prefs.view - - self.layout.prop(view, "use_international_fonts", text="") - def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view - layout.active = view.use_international_fonts - layout.prop(view, "language") - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(view, "use_translate_tooltips", text="Tooltips") - flow.prop(view, "use_translate_interface", text="Interface") - flow.prop(view, "use_translate_new_dataname", text="New Data") + col = layout.column(heading="Affect") + col.active = (bpy.app.translations.locale != 'en_US') + col.prop(view, "use_translate_tooltips", text="Tooltips") + col.prop(view, "use_translate_interface", text="Interface") + col.prop(view, "use_translate_new_dataname", text="New Data") class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel): @@ -256,14 +260,13 @@ class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel): view = prefs.view system = prefs.system - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(system, "use_region_overlap") - flow.prop(view, "show_layout_ui", text="Corner Splitting") - flow.prop(view, "show_navigate_ui") - flow.prop(view, "color_picker_type") - flow.row().prop(view, "header_align") - flow.prop(view, "factor_display_type") + col = layout.column() + col.prop(system, "use_region_overlap") + col.prop(view, "show_layout_ui", text="Corner Splitting") + col.prop(view, "show_navigate_ui") + col.prop(view, "color_picker_type") + col.row().prop(view, "header_align") + col.prop(view, "factor_display_type") class USERPREF_PT_interface_temporary_windows(InterfacePanel, CenterAlignMixIn, Panel): @@ -275,10 +278,9 @@ class USERPREF_PT_interface_temporary_windows(InterfacePanel, CenterAlignMixIn, prefs = context.preferences view = prefs.view - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(view, "render_display_type", text="Render in") - flow.prop(view, "filebrowser_display_type", text="File Browser") + col = layout.column() + col.prop(view, "render_display_type", text="Render in") + col.prop(view, "filebrowser_display_type", text="File Browser") class USERPREF_PT_interface_menus(InterfacePanel, Panel): @@ -358,6 +360,7 @@ class USERPREF_PT_edit_objects_new(EditingPanel, CenterAlignMixIn, Panel): flow.prop(edit, "material_link", text="Link Materials to") flow.prop(edit, "object_align", text="Align to") flow.prop(edit, "use_enter_edit_mode", text="Enter Edit Mode") + flow.prop(edit, "collection_instance_empty_size", text="Instance Empty Size") class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Panel): @@ -368,6 +371,8 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa prefs = context.preferences edit = prefs.edit + layout.use_property_split = False + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) col = flow.column() @@ -375,18 +380,23 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa col.prop(edit, "use_duplicate_armature", text="Armature") col.prop(edit, "use_duplicate_curve", text="Curve") # col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented. + col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") + if hasattr(edit, "use_duplicate_hair"): + col.prop(edit, "use_duplicate_hair", text="Hair") col.prop(edit, "use_duplicate_light", text="Light") - col.prop(edit, "use_duplicate_lightprobe", text="Light Probe") col = flow.column() + col.prop(edit, "use_duplicate_lightprobe", text="Light Probe") col.prop(edit, "use_duplicate_material", text="Material") col.prop(edit, "use_duplicate_mesh", text="Mesh") col.prop(edit, "use_duplicate_metaball", text="Metaball") col.prop(edit, "use_duplicate_particle", text="Particle") col = flow.column() + if hasattr(edit, "use_duplicate_pointcloud"): + col.prop(edit, "use_duplicate_pointcloud", text="Point Cloud") col.prop(edit, "use_duplicate_surface", text="Surface") col.prop(edit, "use_duplicate_text", text="Text") # col.prop(edit, "use_duplicate_texture", text="Texture") # Not implemented. - col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") + col.prop(edit, "use_duplicate_volume", text="Volume") class USERPREF_PT_edit_cursor(EditingPanel, CenterAlignMixIn, Panel): @@ -396,10 +406,9 @@ class USERPREF_PT_edit_cursor(EditingPanel, CenterAlignMixIn, Panel): prefs = context.preferences edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(edit, "use_mouse_depth_cursor") - flow.prop(edit, "use_cursor_lock_adjust") + col = layout.column() + col.prop(edit, "use_mouse_depth_cursor") + col.prop(edit, "use_cursor_lock_adjust") class USERPREF_PT_edit_gpencil(EditingPanel, CenterAlignMixIn, Panel): @@ -410,10 +419,9 @@ class USERPREF_PT_edit_gpencil(EditingPanel, CenterAlignMixIn, Panel): prefs = context.preferences edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance") - flow.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") + col = layout.column(heading="Distance") + col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan") + col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean") class USERPREF_PT_edit_annotations(EditingPanel, CenterAlignMixIn, Panel): @@ -423,10 +431,9 @@ class USERPREF_PT_edit_annotations(EditingPanel, CenterAlignMixIn, Panel): prefs = context.preferences edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(edit, "grease_pencil_default_color", text="Default Color") - flow.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") + col = layout.column() + col.prop(edit, "grease_pencil_default_color", text="Default Color") + col.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") class USERPREF_PT_edit_weight_paint(EditingPanel, CenterAlignMixIn, Panel): @@ -437,6 +444,8 @@ class USERPREF_PT_edit_weight_paint(EditingPanel, CenterAlignMixIn, Panel): prefs = context.preferences view = prefs.view + layout.use_property_split = False + layout.prop(view, "use_weight_color_range", text="Use Custom Colors") col = layout.column() @@ -452,10 +461,9 @@ class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel): prefs = context.preferences edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color") - flow.prop(edit, "node_margin", text="Node Auto-offset Margin") + col = layout.column() + col.prop(edit, "sculpt_paint_overlay_color", text="Sculpt Overlay Color") + col.prop(edit, "node_margin", text="Node Auto-offset Margin") # ----------------------------------------------------------------------------- @@ -475,20 +483,16 @@ class USERPREF_PT_animation_timeline(AnimationPanel, CenterAlignMixIn, Panel): view = prefs.view edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - flow.prop(edit, "use_negative_frames") - - layout.separator() - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() + col.prop(edit, "use_negative_frames") - flow.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing") - flow.prop(view, "timecode_style") - flow.prop(view, "view_frame_type") + col.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing") + col.prop(view, "timecode_style") + col.prop(view, "view_frame_type") if view.view_frame_type == 'SECONDS': - flow.prop(view, "view_frame_seconds") + col.prop(view, "view_frame_seconds") elif view.view_frame_type == 'KEYFRAMES': - flow.prop(view, "view_frame_keyframes") + col.prop(view, "view_frame_keyframes") class USERPREF_PT_animation_keyframes(AnimationPanel, CenterAlignMixIn, Panel): @@ -498,25 +502,14 @@ class USERPREF_PT_animation_keyframes(AnimationPanel, CenterAlignMixIn, Panel): prefs = context.preferences edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(edit, "use_visual_keying") - flow.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed") - - -class USERPREF_PT_animation_autokey(AnimationPanel, CenterAlignMixIn, Panel): - bl_label = "Auto-Keyframing" - bl_parent_id = "USERPREF_PT_animation_keyframes" - - def draw_centered(self, context, layout): - prefs = context.preferences - edit = prefs.edit - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() + col.prop(edit, "use_visual_keying") + col.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed") - flow.prop(edit, "use_auto_keying_warning", text="Show Warning") - flow.prop(edit, "use_keyframe_insert_available", text="Only Insert Available") - flow.prop(edit, "use_auto_keying", text="Enable in New Scenes") + col = layout.column(heading="Auto-Keyframing") + col.prop(edit, "use_auto_keying_warning", text="Show Warning") + col.prop(edit, "use_keyframe_insert_available", text="Only Insert Available") + col.prop(edit, "use_auto_keying", text="Enable in New Scenes") class USERPREF_PT_animation_fcurves(AnimationPanel, CenterAlignMixIn, Panel): @@ -546,6 +539,7 @@ class SystemPanel: class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel): bl_label = "Sound" + bl_options = {'DEFAULT_CLOSED'} def draw_centered(self, context, layout): prefs = context.preferences @@ -591,32 +585,47 @@ class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel): system = prefs.system edit = prefs.edit - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(edit, "undo_steps", text="Undo Steps") - flow.prop(edit, "undo_memory_limit", text="Undo Memory Limit") - flow.prop(edit, "use_global_undo") + col = layout.column() + col.prop(edit, "undo_steps", text="Undo Steps") + col.prop(edit, "undo_memory_limit", text="Undo Memory Limit") + col.prop(edit, "use_global_undo") layout.separator() - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() + col.prop(system, "scrollback", text="Console Scrollback Lines") - flow.prop(system, "memory_cache_limit", text="Sequencer Cache Limit") - flow.prop(system, "scrollback", text="Console Scrollback Lines") + layout.separator() + + col = layout.column() + col.prop(system, "texture_time_out", text="Texture Time Out") + col.prop(system, "texture_collection_rate", text="Garbage Collection Rate") layout.separator() - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() + col.prop(system, "vbo_time_out", text="Vbo Time Out") + col.prop(system, "vbo_collection_rate", text="Garbage Collection Rate") - flow.prop(system, "texture_time_out", text="Texture Time Out") - flow.prop(system, "texture_collection_rate", text="Garbage Collection Rate") - layout.separator() +class USERPREF_PT_system_video_sequencer(SystemPanel, CenterAlignMixIn, Panel): + bl_label = "Video Sequencer" - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + def draw_centered(self, context, layout): + prefs = context.preferences + system = prefs.system + edit = prefs.edit + + layout.prop(system, "memory_cache_limit") + + layout.separator() - flow.prop(system, "vbo_time_out", text="Vbo Time Out") - flow.prop(system, "vbo_collection_rate", text="Garbage Collection Rate") + layout.prop(system, "use_sequencer_disk_cache") + col = layout.column() + col.active = system.use_sequencer_disk_cache + col.prop(system, "sequencer_disk_cache_dir", text="Directory") + col.prop(system, "sequencer_disk_cache_size_limit", text="Cache Limit") + col.prop(system, "sequencer_disk_cache_compression", text="Compression") # ----------------------------------------------------------------------------- @@ -635,23 +644,19 @@ class USERPREF_PT_viewport_display(ViewportPanel, CenterAlignMixIn, Panel): prefs = context.preferences view = prefs.view - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(view, "show_object_info", text="Object Info") - flow.prop(view, "show_view_name", text="View Name") - flow.prop(view, "show_playback_fps", text="Playback FPS") + col = layout.column(heading="Show") + col.prop(view, "show_object_info", text="Object Info") + col.prop(view, "show_view_name", text="View Name") + col.prop(view, "show_playback_fps", text="Playback FPS") layout.separator() - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() + col = layout.column() col.prop(view, "gizmo_size") col.prop(view, "lookdev_sphere_size") - flow.separator() + col.separator() - col = flow.column() col.prop(view, "mini_axis_type", text="3D Viewport Axis") if view.mini_axis_type == 'MINIMAL': @@ -666,12 +671,12 @@ class USERPREF_PT_viewport_quality(ViewportPanel, CenterAlignMixIn, Panel): prefs = context.preferences system = prefs.system - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() + col.prop(system, "viewport_aa") - flow.prop(system, "viewport_aa") - flow.prop(system, "gpencil_multi_sample", text="Grease Pencil Multisampling") - flow.prop(system, "use_overlay_smooth_wire") - flow.prop(system, "use_edit_mode_smooth_wire") + col = layout.column(heading="Smooth Wires") + col.prop(system, "use_overlay_smooth_wire", text="Overlay") + col.prop(system, "use_edit_mode_smooth_wire", text="Edit Mode") class USERPREF_PT_viewport_textures(ViewportPanel, CenterAlignMixIn, Panel): @@ -681,12 +686,11 @@ class USERPREF_PT_viewport_textures(ViewportPanel, CenterAlignMixIn, Panel): prefs = context.preferences system = prefs.system - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(system, "gl_texture_limit", text="Limit Size") - flow.prop(system, "anisotropic_filter") - flow.prop(system, "gl_clip_alpha", slider=True) - flow.prop(system, "image_draw_method", text="Image Display Method") + col = layout.column() + col.prop(system, "gl_texture_limit", text="Limit Size") + col.prop(system, "anisotropic_filter") + col.prop(system, "gl_clip_alpha", slider=True) + col.prop(system, "image_draw_method", text="Image Display Method") class USERPREF_PT_viewport_selection(ViewportPanel, CenterAlignMixIn, Panel): @@ -697,9 +701,7 @@ class USERPREF_PT_viewport_selection(ViewportPanel, CenterAlignMixIn, Panel): prefs = context.preferences system = prefs.system - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(system, "use_select_pick_depth") + layout.prop(system, "use_select_pick_depth") # ----------------------------------------------------------------------------- @@ -861,14 +863,31 @@ class USERPREF_PT_theme_interface_styles(ThemePanel, CenterAlignMixIn, Panel): flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) flow.prop(ui, "menu_shadow_fac") + flow.prop(ui, "menu_shadow_width") flow.prop(ui, "icon_alpha") flow.prop(ui, "icon_saturation") flow.prop(ui, "editor_outline") flow.prop(ui, "widget_text_cursor") - flow.prop(ui, "menu_shadow_width") flow.prop(ui, "widget_emboss") +class USERPREF_PT_theme_interface_transparent_checker(ThemePanel, CenterAlignMixIn, Panel): + bl_label = "Transparent Checkerboard" + bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "USERPREF_PT_theme_user_interface" + + def draw_centered(self, context, layout): + theme = context.preferences.themes[0] + ui = theme.user_interface + + flow = layout.grid_flow( + row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + + flow.prop(ui, "transparent_checker_primary") + flow.prop(ui, "transparent_checker_secondary") + flow.prop(ui, "transparent_checker_size") + + class USERPREF_PT_theme_interface_gizmos(ThemePanel, CenterAlignMixIn, Panel): bl_label = "Axis & Gizmo Colors" bl_options = {'DEFAULT_CLOSED'} @@ -888,6 +907,7 @@ class USERPREF_PT_theme_interface_gizmos(ThemePanel, CenterAlignMixIn, Panel): col = flow.column() col.prop(ui, "gizmo_primary") col.prop(ui, "gizmo_secondary") + col.prop(ui, "gizmo_view_align") col = flow.column() col.prop(ui, "gizmo_a") @@ -1289,37 +1309,40 @@ class USERPREF_PT_saveload_blend(SaveLoadPanel, CenterAlignMixIn, Panel): paths = prefs.filepaths view = prefs.view - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column(heading="Save") + col.prop(view, "use_save_prompt") + col.prop(paths, "use_save_preview_images") - flow.prop(paths, "use_relative_paths") - flow.prop(paths, "use_file_compression") - flow.prop(paths, "use_load_ui") - flow.prop(paths, "use_save_preview_images") - flow.prop(paths, "use_tabs_as_spaces") - flow.prop(view, "use_save_prompt") + col = layout.column(heading="Default to") + col.prop(paths, "use_relative_paths") + col.prop(paths, "use_file_compression") + col.prop(paths, "use_load_ui") - layout.separator() - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column(heading="Text Files") + col.prop(paths, "use_tabs_as_spaces") - flow.prop(paths, "save_version") - flow.prop(paths, "recent_files") + col = layout.column() + col.prop(paths, "save_version") + col.prop(paths, "recent_files") class USERPREF_PT_saveload_blend_autosave(SaveLoadPanel, CenterAlignMixIn, Panel): bl_label = "Auto Save" bl_parent_id = "USERPREF_PT_saveload_blend" - def draw_centered(self, context, layout): + def draw_header(self, context): prefs = context.preferences paths = prefs.filepaths - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + self.layout.prop(paths, "use_auto_save_temporary_files", text="") - flow.prop(paths, "use_auto_save_temporary_files") - sub = flow.column() - sub.active = paths.use_auto_save_temporary_files - sub.prop(paths, "auto_save_time", text="Timer (mins)") + def draw_centered(self, context, layout): + prefs = context.preferences + paths = prefs.filepaths + + col = layout.column() + col.active = paths.use_auto_save_temporary_files + col.prop(paths, "auto_save_time", text="Timer (mins)") class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel): @@ -1329,12 +1352,13 @@ class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel): prefs = context.preferences paths = prefs.filepaths - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() + col.prop(paths, "use_filter_files") - flow.prop(paths, "use_filter_files") - flow.prop(paths, "show_hidden_files_datablocks") - flow.prop(paths, "hide_recent_locations") - flow.prop(paths, "hide_system_bookmarks") + col = layout.column(heading="Hide") + col.prop(paths, "show_hidden_files_datablocks", text="Dot File & Datablocks") + col.prop(paths, "hide_recent_locations", text="Recent Locations") + col.prop(paths, "hide_system_bookmarks", text="System Bookmarks") # ----------------------------------------------------------------------------- @@ -1393,10 +1417,9 @@ class USERPREF_PT_input_tablet(InputPanel, CenterAlignMixIn, Panel): layout.prop(inputs, "tablet_api") layout.separator() - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(inputs, "pressure_threshold_max") - flow.prop(inputs, "pressure_softness") + col = layout.column() + col.prop(inputs, "pressure_threshold_max") + col.prop(inputs, "pressure_softness") class USERPREF_PT_input_ndof(InputPanel, CenterAlignMixIn, Panel): @@ -1443,24 +1466,27 @@ class USERPREF_PT_navigation_orbit(NavigationPanel, CenterAlignMixIn, Panel): inputs = prefs.inputs view = prefs.view - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() - flow.row().prop(inputs, "view_rotate_method", expand=True) + col.row().prop(inputs, "view_rotate_method", expand=True) if inputs.view_rotate_method == 'TURNTABLE': - flow.prop(inputs, "view_rotate_sensitivity_turntable") + col.prop(inputs, "view_rotate_sensitivity_turntable") else: - flow.prop(inputs, "view_rotate_sensitivity_trackball") + col.prop(inputs, "view_rotate_sensitivity_trackball") + col.prop(inputs, "use_rotate_around_active") + + col.separator() - flow.prop(inputs, "use_rotate_around_active") - flow.prop(inputs, "use_auto_perspective") - flow.prop(inputs, "use_mouse_depth_navigate") if sys.platform == "darwin": - flow.prop(inputs, "use_trackpad_natural", text="Natural Trackpad Direction") + col.prop(inputs, "use_trackpad_natural", text="Natural Trackpad Direction") - flow.separator() + col = layout.column(heading="Auto") + col.prop(inputs, "use_auto_perspective", text="Perspective") + col.prop(inputs, "use_mouse_depth_navigate", text="Depth") - flow.prop(view, "smooth_view") - flow.prop(view, "rotation_angle") + col = layout.column() + col.prop(view, "smooth_view") + col.prop(view, "rotation_angle") class USERPREF_PT_navigation_zoom(NavigationPanel, CenterAlignMixIn, Panel): @@ -1470,16 +1496,20 @@ class USERPREF_PT_navigation_zoom(NavigationPanel, CenterAlignMixIn, Panel): prefs = context.preferences inputs = prefs.inputs - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + col = layout.column() - flow.row().prop(inputs, "view_zoom_method", text="Zoom Method", expand=True) + col.row().prop(inputs, "view_zoom_method", text="Zoom Method") if inputs.view_zoom_method in {'DOLLY', 'CONTINUE'}: - flow.row().prop(inputs, "view_zoom_axis", expand=True) - flow.prop(inputs, "invert_mouse_zoom", text="Invert Mouse Zoom Direction") + col.row().prop(inputs, "view_zoom_axis") + col.prop(inputs, "use_zoom_to_mouse") + col = layout.column(heading="Invert Zoom Direction", align=True) + col.prop(inputs, "invert_mouse_zoom", text="Mouse") + col.prop(inputs, "invert_zoom_wheel", text="Wheel") + else: + col.prop(inputs, "use_zoom_to_mouse") + col.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction") - flow.prop(inputs, "invert_zoom_wheel", text="Invert Wheel Zoom Direction") # sub.prop(view, "wheel_scroll_lines", text="Scroll Lines") - flow.prop(inputs, "use_zoom_to_mouse") class USERPREF_PT_navigation_fly_walk(NavigationPanel, CenterAlignMixIn, Panel): @@ -1510,15 +1540,14 @@ class USERPREF_PT_navigation_fly_walk_navigation(NavigationPanel, CenterAlignMix inputs = prefs.inputs walk = inputs.walk_navigation - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(walk, "use_mouse_reverse") - flow.prop(walk, "mouse_speed") - flow.prop(walk, "teleport_time") + col = layout.column() + col.prop(walk, "use_mouse_reverse") + col.prop(walk, "mouse_speed") + col.prop(walk, "teleport_time") - sub = flow.column(align=True) - sub.prop(walk, "walk_speed") - sub.prop(walk, "walk_speed_factor") + col = layout.column(align=True) + col.prop(walk, "walk_speed") + col.prop(walk, "walk_speed_factor") class USERPREF_PT_navigation_fly_walk_gravity(NavigationPanel, CenterAlignMixIn, Panel): @@ -1545,10 +1574,9 @@ class USERPREF_PT_navigation_fly_walk_gravity(NavigationPanel, CenterAlignMixIn, layout.active = walk.use_gravity - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(walk, "view_height") - flow.prop(walk, "jump_height") + col = layout.column() + col.prop(walk, "view_height") + col.prop(walk, "jump_height") # Special case, this is only exposed as a popover. @@ -1837,7 +1865,7 @@ class USERPREF_PT_addons(AddOnPanel, Panel): # WARNING: 2.8x exception, may be removed # use disabled state for old add-ons, chances are they are broken. if is_addon_27x: - sub.label(text="upgrade to 2.8x required") + sub.label(text="Upgrade to 2.8x required") sub.label(icon='ERROR') # Remove code above after 2.8x migration is complete. elif info["warning"]: @@ -1874,16 +1902,16 @@ class USERPREF_PT_addons(AddOnPanel, Panel): split.label(text=" " + info["warning"], icon='ERROR') user_addon = USERPREF_PT_addons.is_user_addon(mod, user_addon_paths) - tot_row = bool(info["wiki_url"]) + bool(user_addon) + tot_row = bool(info["doc_url"]) + bool(user_addon) if tot_row: split = colsub.row().split(factor=0.15) split.label(text="Internet:") sub = split.row() - if info["wiki_url"]: + if info["doc_url"]: sub.operator( "wm.url_open", text="Documentation", icon='HELP', - ).url = info["wiki_url"] + ).url = info["doc_url"] # Only add "Report a Bug" button if tracker_url is set # or the add-on is bundled (use official tracker then). if info.get("tracker_url"): @@ -2088,6 +2116,21 @@ class ExperimentalPanel: url_prefix = "https://developer.blender.org/" + def _draw_items(self, context, items): + prefs = context.preferences + experimental = prefs.experimental + + layout = self.layout + layout.use_property_split = False + layout.use_property_decorate = False + + for prop_keywords, task in items: + split = layout.split(factor=0.66) + col = split.split() + col.prop(experimental, **prop_keywords) + col = split.split() + col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task + """ # Example panel, leave it here so we always have a template to follow even # after the features are gone from the experimental panel. @@ -2096,27 +2139,24 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): bl_label = "Virtual Reality" def draw(self, context): - prefs = context.preferences - experimental = prefs.experimental + self._draw_items( + context, ( + ({"property": "use_virtual_reality_scene_inspection"}, "T71347"), + ({"property": "use_virtual_reality_immersive_drawing"}, "T71348"), + ) + ) +""" - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - task = "T71347" - split = layout.split(factor=0.66) - col = split.split() - col.prop(experimental, "use_virtual_reality_scene_inspection", text="Scene Inspection") - col = split.split() - col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task - - task = "T71348" - split = layout.split(factor=0.66) - col = split.column() - col.prop(experimental, "use_virtual_reality_immersive_drawing", text="Continuous Immersive Drawing") - col = split.column() - col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task -""" +class USERPREF_PT_experimental_system(ExperimentalPanel, Panel): + bl_label = "System" + + def draw(self, context): + self._draw_items( + context, ( + ({"property": "use_undo_legacy"}, "T60695"), + ), + ) # ----------------------------------------------------------------------------- @@ -2130,6 +2170,8 @@ classes = ( USERPREF_HT_header, USERPREF_PT_navigation_bar, USERPREF_PT_save_preferences, + USERPREF_MT_editor_menus, + USERPREF_MT_view, USERPREF_MT_save_load, USERPREF_PT_interface_display, @@ -2157,11 +2199,11 @@ classes = ( USERPREF_PT_animation_timeline, USERPREF_PT_animation_keyframes, - USERPREF_PT_animation_autokey, USERPREF_PT_animation_fcurves, USERPREF_PT_system_cycles_devices, USERPREF_PT_system_memory, + USERPREF_PT_system_video_sequencer, USERPREF_PT_system_sound, USERPREF_MT_interface_theme_presets, @@ -2169,6 +2211,7 @@ classes = ( USERPREF_PT_theme_interface_state, USERPREF_PT_theme_interface_styles, USERPREF_PT_theme_interface_gizmos, + USERPREF_PT_theme_interface_transparent_checker, USERPREF_PT_theme_interface_icons, USERPREF_PT_theme_text_style, USERPREF_PT_theme_bone_color_sets, @@ -2206,6 +2249,8 @@ classes = ( # Popovers. USERPREF_PT_ndof_settings, + USERPREF_PT_experimental_system, + # Add dynamically generated editor theme panels last, # so they show up last in the theme section. *ThemeGenericClassGenerator.generate_panel_classes_from_theme_areas(), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index aa8f343e1a0..61d5d79473f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -31,6 +31,7 @@ from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, AnnotationOnionSkin, GreasePencilMaterialsPanel, + GreasePencilVertexcolorPanel, ) from bl_ui.space_toolsystem_common import ( ToolActivePanelHelper, @@ -119,21 +120,26 @@ class VIEW3D_HT_tool_header(Header): if is_valid_context: brush = context.tool_settings.gpencil_paint.brush if brush.gpencil_tool != 'ERASE': - layout.popover("VIEW3D_PT_tools_grease_pencil_brush_advanced") + if brush.gpencil_tool != 'TINT': + layout.popover("VIEW3D_PT_tools_grease_pencil_brush_advanced") - if brush.gpencil_tool != 'FILL': + if brush.gpencil_tool not in {'FILL', 'TINT'}: layout.popover("VIEW3D_PT_tools_grease_pencil_brush_stroke") - layout.popover("VIEW3D_PT_tools_grease_pencil_brushcurves") layout.popover("VIEW3D_PT_tools_grease_pencil_paint_appearance") elif tool_mode == 'SCULPT_GPENCIL': - settings = context.tool_settings.gpencil_sculpt - tool = settings.sculpt_tool - if tool in {'SMOOTH', 'RANDOMIZE', 'SMOOTH'}: - layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options") - layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") + if is_valid_context: + brush = context.tool_settings.gpencil_sculpt_paint.brush + tool = brush.gpencil_tool + if tool in ('SMOOTH', 'RANDOMIZE'): + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options") + layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': - layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance") + if is_valid_context: + layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance") + elif tool_mode == 'VERTEX_GPENCIL': + if is_valid_context: + layout.popover("VIEW3D_PT_tools_grease_pencil_vertex_appearance") def draw_mode_settings(self, context): layout = self.layout @@ -386,7 +392,7 @@ class _draw_tool_settings_context_mode: }: # is_paint = False pass - elif tool.idname == "Cutter": + elif tool.idname == "builtin.cutter": row = layout.row(align=True) row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return False @@ -425,6 +431,15 @@ class _draw_tool_settings_context_mode: row.prop(gp_settings, "use_material_pin", text="") + if brush.gpencil_tool in {'DRAW', 'FILL'} and ma: + row.separator(factor=1.0) + subrow = row.row(align=True) + row.prop_enum(settings, "color_mode", 'MATERIAL', text="", icon='MATERIAL') + row.prop_enum(settings, "color_mode", 'VERTEXCOLOR', text="", icon='VPAINT_HLT') + sub_row = row.row(align=True) + sub_row.enabled = settings.color_mode == 'VERTEXCOLOR' + sub_row.prop_with_popover(brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor") + row = layout.row(align=True) tool_settings = context.scene.tool_settings settings = tool_settings.gpencil_paint @@ -433,6 +448,10 @@ class _draw_tool_settings_context_mode: if context.object and brush.gpencil_tool in {'FILL', 'DRAW'}: draw_color_selector() + if context.object and brush.gpencil_tool == 'TINT': + row.separator(factor=0.4) + row.prop_with_popover(brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor") + from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, ) @@ -444,9 +463,8 @@ class _draw_tool_settings_context_mode: def SCULPT_GPENCIL(context, layout, tool): if (tool is None) or (not tool.has_datablock): return False - tool_settings = context.tool_settings - settings = tool_settings.gpencil_sculpt - brush = settings.brush + paint = context.tool_settings.gpencil_sculpt_paint + brush = paint.brush from bl_ui.properties_paint_common import ( brush_basic_gpencil_sculpt_settings, @@ -459,9 +477,8 @@ class _draw_tool_settings_context_mode: def WEIGHT_GPENCIL(context, layout, tool): if (tool is None) or (not tool.has_datablock): return False - tool_settings = context.tool_settings - settings = tool_settings.gpencil_sculpt - brush = settings.brush + paint = context.tool_settings.gpencil_weight_paint + brush = paint.brush from bl_ui.properties_paint_common import ( brush_basic_gpencil_weight_settings, @@ -471,6 +488,31 @@ class _draw_tool_settings_context_mode: return True @staticmethod + def VERTEX_GPENCIL(context, layout, tool): + if (tool is None) or (not tool.has_datablock): + return False + + paint = context.tool_settings.gpencil_vertex_paint + brush = paint.brush + + row = layout.row(align=True) + tool_settings = context.scene.tool_settings + settings = tool_settings.gpencil_vertex_paint + row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True) + + if brush.gpencil_vertex_tool not in {'BLUR', 'AVERAGE', 'SMEAR'}: + row.separator(factor=0.4) + row.prop_with_popover(brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor") + + from bl_ui.properties_paint_common import ( + brush_basic_gpencil_vertex_settings, + ) + + brush_basic_gpencil_vertex_settings(layout, context, brush, compact=True) + + return True + + @staticmethod def PARTICLE(context, layout, tool): if (tool is None) or (not tool.has_datablock): return False @@ -550,7 +592,7 @@ class VIEW3D_HT_header(Header): else: if (object_mode not in { 'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', - 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL' + 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL' }) or has_pose_mode: show_snap = True else: @@ -687,7 +729,14 @@ class VIEW3D_HT_header(Header): row.prop(tool_settings, "use_gpencil_select_mask_stroke", text="") row.prop(tool_settings, "use_gpencil_select_mask_segment", text="") - if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode: + # Select mode for Vertex Paint + if gpd.is_stroke_vertex_mode: + row = layout.row(align=True) + row.prop(tool_settings, "use_gpencil_vertex_select_mask_point", text="") + row.prop(tool_settings, "use_gpencil_vertex_select_mask_stroke", text="") + row.prop(tool_settings, "use_gpencil_vertex_select_mask_segment", text="") + + if gpd.use_stroke_edit_mode or gpd.is_stroke_sculpt_mode or gpd.is_stroke_weight_mode or gpd.is_stroke_vertex_mode: row = layout.row(align=True) row.prop(gpd, "use_multiedit", text="", icon='GP_MULTIFRAME_EDITING') @@ -780,18 +829,17 @@ class VIEW3D_HT_header(Header): # While exposing 'shading.show_xray(_wireframe)' is correct. # this hides the key shortcut from users: T70433. + if has_pose_mode: + draw_depressed = overlay.show_xray_bone + elif shading.type == 'WIREFRAME': + draw_depressed = shading.show_xray_wireframe + else: + draw_depressed = shading.show_xray row.operator( "view3d.toggle_xray", text="", icon='XRAY', - depress=( - overlay.show_xray_bone if has_pose_mode else - getattr( - shading, - "show_xray_wireframe" if shading.type == 'WIREFRAME' else - "show_xray" - ) - ), + depress=draw_depressed, ) row = layout.row(align=True) @@ -812,7 +860,8 @@ class VIEW3D_MT_editor_menus(Menu): obj = context.active_object mode_string = context.mode edit_object = context.edit_object - gp_edit = obj and obj.mode in {'EDIT_GPENCIL', 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'} + gp_edit = obj and obj.mode in {'EDIT_GPENCIL', 'PAINT_GPENCIL', 'SCULPT_GPENCIL', + 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL'} ts = context.scene.tool_settings layout.menu("VIEW3D_MT_view") @@ -827,6 +876,8 @@ class VIEW3D_MT_editor_menus(Menu): layout.menu("VIEW3D_MT_select_gpencil") elif mode_string == 'EDIT_GPENCIL': layout.menu("VIEW3D_MT_select_gpencil") + elif mode_string == 'VERTEX_GPENCIL': + layout.menu("VIEW3D_MT_select_gpencil") elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}: mesh = obj.data if mesh.use_paint_mask: @@ -878,6 +929,7 @@ class VIEW3D_MT_editor_menus(Menu): layout.menu("VIEW3D_MT_%s" % mode_string.lower()) if mode_string == 'SCULPT': layout.menu("VIEW3D_MT_mask") + layout.menu("VIEW3D_MT_face_sets") else: layout.menu("VIEW3D_MT_object") @@ -939,6 +991,7 @@ class VIEW3D_MT_transform(VIEW3D_MT_transform_base): layout = self.layout if context.mode == 'EDIT_MESH': layout.operator("transform.shrink_fatten", text="Shrink Fatten") + layout.operator("transform.skin_resize") elif context.mode == 'EDIT_CURVE': layout.operator("transform.transform", text="Radius").mode = 'CURVE_SHRINKFATTEN' @@ -1110,7 +1163,7 @@ class VIEW3D_MT_view(Menu): if view.region_quadviews: layout.operator("view3d.view_selected", text="Frame Selected (Quad View)").use_all_regions = True - layout.operator("view3d.view_all", text="Frame All").center = False + layout.operator("view3d.view_all").center = False layout.operator("view3d.view_persportho", text="Perspective/Orthographic") layout.menu("VIEW3D_MT_view_local") @@ -1241,7 +1294,7 @@ class VIEW3D_MT_view_align(Menu): layout.separator() - layout.operator("view3d.view_all", text="Center Cursor and View All").center = True + layout.operator("view3d.view_all", text="Center Cursor and Frame All").center = True layout.operator("view3d.view_center_cursor") layout.separator() @@ -1443,7 +1496,7 @@ class VIEW3D_MT_select_particle(Menu): layout.separator() - layout.operator("particle.select_linked") + layout.operator("particle.select_linked", text="Select Linked") layout.separator() @@ -1652,60 +1705,33 @@ class VIEW3D_MT_select_edit_surface(Menu): layout.operator("curve.select_less") -class VIEW3D_MT_edit_text_context_menu(Menu): - bl_label = "Text Context Menu" - - def draw(self, _context): - layout = self.layout - - layout.operator_context = 'INVOKE_DEFAULT' - - layout.operator("font.text_cut", text="Cut") - layout.operator("font.text_copy", text="Copy", icon='COPYDOWN') - layout.operator("font.text_paste", text="Paste", icon='PASTEDOWN') - - layout.separator() - - layout.operator("font.select_all") - - layout.separator() - - layout.menu("VIEW3D_MT_edit_font") - - class VIEW3D_MT_select_edit_text(Menu): - # intentional name mismatch - # select menu for 3d-text doesn't make sense - bl_label = "Edit" + bl_label = "Select" def draw(self, _context): layout = self.layout - layout.operator("ed.undo") - layout.operator("ed.redo") - - layout.separator() - - layout.operator("font.text_cut", text="Cut") - layout.operator("font.text_copy", text="Copy", icon='COPYDOWN') - layout.operator("font.text_paste", text="Paste", icon='PASTEDOWN') + layout.operator("font.select_all", text="All") layout.separator() - layout.operator("font.text_paste_from_file") + layout.operator("font.move_select", text="Previous Block").type = 'PREVIOUS_PAGE' + layout.operator("font.move_select", text="Next Block").type = 'NEXT_PAGE' layout.separator() - layout.operator("font.select_all") + layout.operator("font.move_select", text="Line Begin").type = 'LINE_BEGIN' + layout.operator("font.move_select", text="Line End").type = 'LINE_END' layout.separator() - layout.operator("font.case_set", text="To Uppercase").case = 'UPPER' - layout.operator("font.case_set", text="To Lowercase").case = 'LOWER' + layout.operator("font.move_select", text="Previous Line").type = 'PREVIOUS_LINE' + layout.operator("font.move_select", text="Next Line").type = 'NEXT_LINE' layout.separator() - layout.menu("VIEW3D_MT_edit_text_chars") + layout.operator("font.move_select", text="Previous Word").type = 'PREVIOUS_WORD' + layout.operator("font.move_select", text="Next Word").type = 'NEXT_WORD' class VIEW3D_MT_select_edit_metaball(Menu): @@ -1805,6 +1831,10 @@ class VIEW3D_MT_select_edit_armature(Menu): layout.separator() + layout.operator("armature.select_linked", text="Linked") + + layout.separator() + props = layout.operator("armature.select_hierarchy", text="Parent") props.extend = False props.direction = 'PARENT' @@ -1848,6 +1878,9 @@ class VIEW3D_MT_select_gpencil(Menu): layout.operator("gpencil.select_alternate") layout.operator_menu_enum("gpencil.select_grouped", "type", text="Grouped") + if _context.mode == 'VERTEX_GPENCIL': + layout.operator("gpencil.select_vertex_color", text="Vertex Color") + layout.separator() layout.operator("gpencil.select_first") @@ -2100,6 +2133,16 @@ class VIEW3D_MT_camera_add(Menu): layout.operator("object.camera_add", text="Camera", icon='OUTLINER_OB_CAMERA') +class VIEW3D_MT_volume_add(Menu): + bl_idname = "VIEW3D_MT_volume_add" + bl_label = "Volume" + + def draw(self, _context): + layout = self.layout + layout.operator("object.volume_import", text="Import OpenVDB...", icon='OUTLINER_DATA_VOLUME') + layout.operator("object.volume_add", text="Empty", icon='OUTLINER_DATA_VOLUME') + + class VIEW3D_MT_add(Menu): bl_label = "Add" bl_translation_context = i18n_contexts.operator_default @@ -2122,6 +2165,11 @@ class VIEW3D_MT_add(Menu): layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META') layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT') + if hasattr(bpy.data, "hairs"): + layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR') + if hasattr(bpy.data, "pointclouds"): + layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD') + layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME') layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL') layout.separator() @@ -2713,6 +2761,7 @@ class VIEW3D_MT_make_single_user(Menu): def draw(self, _context): layout = self.layout + layout.operator_context = 'EXEC_DEFAULT' props = layout.operator("object.make_single_user", text="Object") props.object = True @@ -2866,6 +2915,32 @@ class VIEW3D_MT_gpencil_vertex_group(Menu): layout.operator("gpencil.vertex_group_deselect", text="Deselect") +class VIEW3D_MT_paint_weight_lock(Menu): + bl_label = "Vertex Group Locks" + + def draw(self, _context): + layout = self.layout + + op = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All") + op.action, op.mask = 'LOCK', 'ALL' + op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All") + op.action, op.mask = 'UNLOCK', 'ALL' + op = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Selected") + op.action, op.mask = 'LOCK', 'SELECTED' + op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock Selected") + op.action, op.mask = 'UNLOCK', 'SELECTED' + op = layout.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Unselected") + op.action, op.mask = 'LOCK', 'UNSELECTED' + op = layout.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock Unselected") + op.action, op.mask = 'UNLOCK', 'UNSELECTED' + op = layout.operator("object.vertex_group_lock", text="Lock Only Selected") + op.action, op.mask = 'LOCK', 'INVERT_UNSELECTED' + op = layout.operator("object.vertex_group_lock", text="Lock Only Unselected") + op.action, op.mask = 'UNLOCK', 'INVERT_UNSELECTED' + op = layout.operator("object.vertex_group_lock", text="Invert Locks") + op.action, op.mask = 'INVERT', 'ALL' + + class VIEW3D_MT_paint_weight(Menu): bl_label = "Weights" @@ -2907,6 +2982,8 @@ class VIEW3D_MT_paint_weight(Menu): layout.operator("paint.weight_set") + layout.menu("VIEW3D_MT_paint_weight_lock", text="Locks") + def draw(self, _context): self.draw_generic(self.layout, is_editmode=False) @@ -2937,6 +3014,10 @@ class VIEW3D_MT_sculpt(Menu): layout.menu("VIEW3D_MT_sculpt_set_pivot", text="Set Pivot") + layout.separator() + + layout.operator("sculpt.optimize") + class VIEW3D_MT_mask(Menu): bl_label = "Mask" @@ -2991,12 +3072,14 @@ class VIEW3D_MT_mask(Menu): props.keep_previous_mask = False props.invert = True props.smooth_iterations = 2 + props.create_face_set = False props = layout.operator("sculpt.mask_expand", text="Expand Mask By Curvature") props.use_normals = True props.keep_previous_mask = True props.invert = False props.smooth_iterations = 0 + props.create_face_set = False layout.separator() @@ -3015,6 +3098,38 @@ class VIEW3D_MT_mask(Menu): props = layout.operator("sculpt.dirty_mask", text='Dirty Mask') +class VIEW3D_MT_face_sets(Menu): + bl_label = "Face Sets" + + def draw(self, _context): + layout = self.layout + + + op = layout.operator("sculpt.face_sets_create", text='Face Set From Masked') + op.mode = 'MASKED' + + op = layout.operator("sculpt.face_sets_create", text='Face Set From Visible') + op.mode = 'VISIBLE' + + op = layout.operator("sculpt.face_sets_create", text='Face Set From Edit Mode Selection') + op.mode = 'SELECTION' + + layout.separator() + + layout.menu("VIEW3D_MT_face_sets_init", text="Init Face Sets") + + layout.separator() + + op = layout.operator("sculpt.face_set_change_visibility", text='Invert Visible Face Sets') + op.mode = 'INVERT' + + op = layout.operator("sculpt.face_set_change_visibility", text='Show All Face Sets') + op.mode = 'SHOW_ALL' + + layout.separator() + + op = layout.operator("sculpt.face_sets_randomize_colors", text='Randomize Colors') + class VIEW3D_MT_sculpt_set_pivot(Menu): bl_label = "Sculpt Set Pivot" @@ -3037,6 +3152,37 @@ class VIEW3D_MT_sculpt_set_pivot(Menu): props = layout.operator("sculpt.set_pivot_position", text="Pivot to Surface Under Cursor") props.mode = 'SURFACE' +class VIEW3D_MT_face_sets_init(Menu): + bl_label = "Face Sets Init" + + def draw(self, _context): + layout = self.layout + + op = layout.operator("sculpt.face_sets_init", text='By Loose Parts') + op.mode = 'LOOSE_PARTS' + + op = layout.operator("sculpt.face_sets_init", text='By Materials') + op.mode = 'MATERIALS' + + op = layout.operator("sculpt.face_sets_init", text='By Normals') + op.mode = 'NORMALS' + + op = layout.operator("sculpt.face_sets_init", text='By UV Seams') + op.mode = 'UV_SEAMS' + + op = layout.operator("sculpt.face_sets_init", text='By Edge Creases') + op.mode = 'CREASES' + + op = layout.operator("sculpt.face_sets_init", text='By Edge Bevel Weight') + op.mode = 'BEVEL_WEIGHT' + + op = layout.operator("sculpt.face_sets_init", text='By Sharp Edges') + op.mode = 'SHARP_EDGES' + + op = layout.operator("sculpt.face_sets_init", text='By Face Maps') + op.mode = 'FACE_MAPS' + + class VIEW3D_MT_particle(Menu): bl_label = "Particle" @@ -3121,8 +3267,7 @@ class VIEW3D_MT_particle_context_menu(Menu): layout.separator() - layout.operator("particle.select_linked") - + layout.operator("particle.select_linked", text="Select Linked") class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu): _operator_name = "particle" @@ -3170,13 +3315,7 @@ class VIEW3D_MT_pose(Menu): layout.separator() - layout.operator_context = 'EXEC_AREA' - layout.operator("pose.autoside_names", text="AutoName Left/Right").axis = 'XAXIS' - layout.operator("pose.autoside_names", text="AutoName Front/Back").axis = 'YAXIS' - layout.operator("pose.autoside_names", text="AutoName Top/Bottom").axis = 'ZAXIS' - - layout.operator("pose.flip_names") - + layout.menu("VIEW3D_MT_pose_names") layout.operator("pose.quaternions_flip") layout.separator() @@ -3313,6 +3452,19 @@ class VIEW3D_MT_pose_constraints(Menu): layout.operator("pose.constraints_clear") +class VIEW3D_MT_pose_names(Menu): + bl_label = "Names" + + def draw(self, _context): + layout = self.layout + + layout.operator_context = 'EXEC_REGION_WIN' + layout.operator("pose.autoside_names", text="AutoName Left/Right").axis = 'XAXIS' + layout.operator("pose.autoside_names", text="AutoName Front/Back").axis = 'YAXIS' + layout.operator("pose.autoside_names", text="AutoName Top/Bottom").axis = 'ZAXIS' + layout.operator("pose.flip_names") + + class VIEW3D_MT_pose_showhide(ShowHideMenu, Menu): _operator_name = "pose" @@ -3385,7 +3537,6 @@ class BoneOptions: "use_deform", "use_envelope_multiply", "use_inherit_rotation", - "inherit_scale", ] if context.mode == 'EDIT_ARMATURE': @@ -3440,7 +3591,15 @@ class VIEW3D_MT_edit_mesh(Menu): layout.operator("mesh.duplicate_move", text="Duplicate") layout.menu("VIEW3D_MT_edit_mesh_extrude") - layout.operator("mesh.split") + + layout.separator() + + layout.menu("VIEW3D_MT_edit_mesh_merge", text="Merge") + layout.menu("VIEW3D_MT_edit_mesh_split", text="Split") + layout.operator_menu_enum("mesh.separate", "type") + + layout.separator() + layout.operator("mesh.bisect") layout.operator("mesh.knife_project") @@ -3462,7 +3621,6 @@ class VIEW3D_MT_edit_mesh(Menu): layout.separator() layout.menu("VIEW3D_MT_edit_mesh_showhide") - layout.operator_menu_enum("mesh.separate", "type") layout.menu("VIEW3D_MT_edit_mesh_clean") layout.separator() @@ -3580,7 +3738,8 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): col.separator() - col.operator("mesh.loopcut_slide") + props = col.operator("mesh.loopcut_slide") + props.TRANSFORM_OT_edge_slide.release_confirm = False col.operator("mesh.offset_edge_loops_slide") col.separator() @@ -3603,11 +3762,6 @@ class VIEW3D_MT_edit_mesh_context_menu(Menu): col.separator() - col.operator("mesh.mark_seam").clear = False - col.operator("mesh.mark_seam", text="Clear Seam").clear = True - - col.separator() - col.operator("mesh.mark_sharp") col.operator("mesh.mark_sharp", text="Clear Sharp").clear = True @@ -3719,12 +3873,19 @@ class VIEW3D_MT_edit_mesh_extrude(Menu): return menu def draw(self, context): + from math import pi + layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' for menu_id in self.extrude_options(context): self._extrude_funcs[menu_id](layout) + layout.separator() + + layout.operator("mesh.extrude_repeat") + layout.operator("mesh.spin").angle = pi * 2 + class VIEW3D_MT_edit_mesh_vertices(Menu): bl_label = "Vertex" @@ -3755,6 +3916,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.operator("transform.vert_slide", text="Slide Vertices") layout.operator_context = 'EXEC_DEFAULT' layout.operator("mesh.vertices_smooth", text="Smooth Vertices").factor = 0.5 + layout.operator("mesh.vertices_smooth_laplacian", text="Smooth Vertices (Laplacian)") layout.operator_context = 'INVOKE_REGION_WIN' layout.separator() @@ -3764,10 +3926,6 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.separator() - layout.menu("VIEW3D_MT_edit_mesh_merge", text="Merge Vertices") - - layout.separator() - layout.menu("VIEW3D_MT_vertex_group") layout.menu("VIEW3D_MT_hook") @@ -3823,6 +3981,7 @@ class VIEW3D_MT_edit_mesh_edges(Menu): layout.operator("mesh.extrude_edges_move", text="Extrude Edges") layout.operator("mesh.bevel", text="Bevel Edges").vertex_only = False layout.operator("mesh.bridge_edge_loops") + layout.operator("mesh.screw") layout.separator() @@ -3838,7 +3997,7 @@ class VIEW3D_MT_edit_mesh_edges(Menu): layout.separator() layout.operator("transform.edge_slide") - layout.operator("mesh.edge_split") + layout.operator("mesh.offset_edge_loops_slide") layout.separator() @@ -3847,11 +4006,6 @@ class VIEW3D_MT_edit_mesh_edges(Menu): layout.separator() - layout.operator("mesh.mark_seam").clear = False - layout.operator("mesh.mark_seam", text="Clear Seam").clear = True - - layout.separator() - layout.operator("mesh.mark_sharp") layout.operator("mesh.mark_sharp", text="Clear Sharp").clear = True @@ -4017,7 +4171,7 @@ class VIEW3D_MT_edit_mesh_normals(Menu): layout.operator("mesh.normals_tools", text="Copy Vectors").mode = 'COPY' layout.operator("mesh.normals_tools", text="Paste Vectors").mode = 'PASTE' - layout.operator("mesh.smoothen_normals", text="Smoothen Vectors") + layout.operator("mesh.smooth_normals", text="Smooth Vectors") layout.operator("mesh.normals_tools", text="Reset Vectors").mode = 'RESET' layout.separator() @@ -4116,6 +4270,19 @@ class VIEW3D_MT_edit_mesh_merge(Menu): layout.operator("mesh.remove_doubles", text="By Distance") +class VIEW3D_MT_edit_mesh_split(Menu): + bl_label = "Split" + + def draw(self, _context): + layout = self.layout + + layout.operator("mesh.split", text="Selection") + + layout.separator() + + layout.operator_enum("mesh.edge_split", "type") + + class VIEW3D_MT_edit_mesh_showhide(ShowHideMenu, Menu): _operator_name = "mesh" @@ -4157,6 +4324,9 @@ def draw_curve(self, _context): layout.operator("curve.split") layout.operator("curve.separate") + + layout.separator() + layout.operator("curve.cyclic_toggle") layout.operator_menu_enum("curve.spline_type_set", "type") @@ -4285,6 +4455,7 @@ class VIEW3D_MT_edit_curve_context_menu(Menu): # Remove layout.operator("curve.split") layout.operator("curve.decimate") + layout.operator("curve.separate") layout.operator("curve.dissolve_verts") layout.operator("curve.delete", text="Delete Segment").type = 'SEGMENT' layout.operator("curve.delete", text="Delete Point").type = 'VERT' @@ -4313,19 +4484,7 @@ class VIEW3D_MT_edit_surface(Menu): draw = draw_curve -class VIEW3D_MT_edit_font(Menu): - bl_label = "Font" - - def draw(self, _context): - layout = self.layout - - layout.operator("font.style_toggle", text="Toggle Bold", icon='BOLD').style = 'BOLD' - layout.operator("font.style_toggle", text="Toggle Italic", icon='ITALIC').style = 'ITALIC' - layout.operator("font.style_toggle", text="Toggle Underline", icon='UNDERLINE').style = 'UNDERLINE' - layout.operator("font.style_toggle", text="Toggle Small Caps", icon='SMALL_CAPS').style = 'SMALL_CAPS' - - -class VIEW3D_MT_edit_text_chars(Menu): +class VIEW3D_MT_edit_font_chars(Menu): bl_label = "Special Characters" def draw(self, _context): @@ -4365,6 +4524,91 @@ class VIEW3D_MT_edit_text_chars(Menu): layout.operator("font.text_insert", text="Spanish Exclamation Mark").text = "\u00A1" +class VIEW3D_MT_edit_font_kerning(Menu): + bl_label = "Kerning" + + def draw(self, context): + layout = self.layout + + ob = context.active_object + text = ob.data + kerning = text.edit_format.kerning + + layout.operator("font.change_spacing", text="Decrease Kerning").delta = -1 + layout.operator("font.change_spacing", text="Increase Kerning").delta = 1 + layout.operator("font.change_spacing", text="Reset Kerning").delta = -kerning + + +class VIEW3D_MT_edit_font_delete(Menu): + bl_label = "Delete" + + def draw(self, _context): + layout = self.layout + + layout.operator("font.delete", text="Previous Character").type = 'PREVIOUS_CHARACTER' + layout.operator("font.delete", text="Next Character").type = 'NEXT_CHARACTER' + layout.operator("font.delete", text="Previous Word").type = 'PREVIOUS_WORD' + layout.operator("font.delete", text="Next Word").type = 'NEXT_WORD' + + +class VIEW3D_MT_edit_font(Menu): + bl_label = "Text" + + def draw(self, _context): + layout = self.layout + + layout.operator("font.text_cut", text="Cut") + layout.operator("font.text_copy", text="Copy", icon='COPYDOWN') + layout.operator("font.text_paste", text="Paste", icon='PASTEDOWN') + + layout.separator() + + layout.operator("font.text_paste_from_file") + + layout.separator() + + layout.operator("font.case_set", text="To Uppercase").case = 'UPPER' + layout.operator("font.case_set", text="To Lowercase").case = 'LOWER' + + layout.separator() + + layout.menu("VIEW3D_MT_edit_font_chars") + + layout.separator() + + layout.operator("font.style_toggle", text="Toggle Bold", icon='BOLD').style = 'BOLD' + layout.operator("font.style_toggle", text="Toggle Italic", icon='ITALIC').style = 'ITALIC' + layout.operator("font.style_toggle", text="Toggle Underline", icon='UNDERLINE').style = 'UNDERLINE' + layout.operator("font.style_toggle", text="Toggle Small Caps", icon='SMALL_CAPS').style = 'SMALL_CAPS' + + layout.menu("VIEW3D_MT_edit_font_kerning") + + layout.separator() + + layout.menu("VIEW3D_MT_edit_font_delete") + + +class VIEW3D_MT_edit_font_context_menu(Menu): + bl_label = "Text Context Menu" + + def draw(self, _context): + layout = self.layout + + layout.operator_context = 'INVOKE_DEFAULT' + + layout.operator("font.text_cut", text="Cut") + layout.operator("font.text_copy", text="Copy", icon='COPYDOWN') + layout.operator("font.text_paste", text="Paste", icon='PASTEDOWN') + + layout.separator() + + layout.operator("font.select_all") + + layout.separator() + + layout.menu("VIEW3D_MT_edit_font") + + class VIEW3D_MT_edit_meta(Menu): bl_label = "Metaball" @@ -4442,8 +4686,10 @@ class VIEW3D_MT_edit_armature(Menu): layout.operator("armature.extrude_forked") layout.operator("armature.duplicate_move") - layout.operator("armature.merge") layout.operator("armature.fill") + + layout.separator() + layout.operator("armature.split") layout.operator("armature.separate") @@ -4515,7 +4761,7 @@ class VIEW3D_MT_armature_context_menu(Menu): # Remove layout.operator("armature.split") - layout.operator("armature.merge") + layout.operator("armature.separate") layout.operator("armature.dissolve") layout.operator("armature.delete") @@ -4598,6 +4844,10 @@ class VIEW3D_MT_paint_gpencil(Menu): layout = self.layout + layout.menu("GPENCIL_MT_layer_active", text="Active Layer") + + layout.separator() + layout.menu("VIEW3D_MT_gpencil_animation") layout.menu("VIEW3D_MT_edit_gpencil_interpolate") @@ -4656,6 +4906,10 @@ class VIEW3D_MT_edit_gpencil(Menu): layout.separator() + layout.menu("GPENCIL_MT_layer_active", text="Active Layer") + + layout.separator() + layout.menu("VIEW3D_MT_gpencil_animation") layout.menu("VIEW3D_MT_edit_gpencil_interpolate") @@ -4690,6 +4944,7 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): def draw(self, _context): layout = self.layout + settings = _context.tool_settings.gpencil_sculpt layout.operator("gpencil.stroke_subdivide", text="Subdivide").only_selected = False layout.menu("VIEW3D_MT_gpencil_simplify") @@ -4715,6 +4970,10 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' layout.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps", property="type") layout.operator("gpencil.stroke_flip", text="Switch Direction") + layout.prop(settings, "use_scale_thickness") + + layout.separator() + layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform") class VIEW3D_MT_edit_gpencil_point(Menu): @@ -4759,6 +5018,22 @@ class VIEW3D_MT_weight_gpencil(Menu): layout.menu("VIEW3D_MT_gpencil_autoweights") +class VIEW3D_MT_vertex_gpencil(Menu): + bl_label = "Paint" + + def draw(self, _context): + layout = self.layout + layout.operator("gpencil.vertex_color_set", text="Set Vertex Colors") + layout.separator() + layout.operator("gpencil.vertex_color_invert", text="Invert") + layout.operator("gpencil.vertex_color_levels", text="Levels") + layout.operator("gpencil.vertex_color_hsv", text="Hue Saturation Value") + layout.operator("gpencil.vertex_color_brightness_contrast", text="Bright/Contrast") + + layout.separator() + layout.menu("VIEW3D_MT_join_palette") + + class VIEW3D_MT_gpencil_animation(Menu): bl_label = "Animation" @@ -5013,6 +5288,58 @@ class VIEW3D_MT_sculpt_mask_edit_pie(Menu): op.filter_type = 'CONTRAST_DECREASE' op.auto_iteration_count = False +class VIEW3D_MT_sculpt_face_sets_edit_pie(Menu): + + bl_label = "Face Sets Edit" + + def draw(self, _context): + layout = self.layout + pie = layout.menu_pie() + + op = pie.operator("sculpt.face_sets_create", text='Face Set From Masked') + op.mode = 'MASKED' + + op = pie.operator("sculpt.face_sets_create", text='Face Set From Visible') + op.mode = 'VISIBLE' + + op = pie.operator("sculpt.face_set_change_visibility", text='Invert Visible') + op.mode = 'INVERT' + + op = pie.operator("sculpt.face_set_change_visibility", text='Show All') + op.mode = 'SHOW_ALL' + +class VIEW3D_MT_wpaint_vgroup_lock_pie(Menu): + bl_label = "Vertex Group Locks" + + def draw(self, _context): + layout = self.layout + pie = layout.menu_pie() + + # 1: Left + op = pie.operator("object.vertex_group_lock", icon='LOCKED', text="Lock All") + op.action, op.mask = 'LOCK', 'ALL' + # 2: Right + op = pie.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock All") + op.action, op.mask = 'UNLOCK', 'ALL' + # 3: Down + op = pie.operator("object.vertex_group_lock", icon='UNLOCKED', text="Unlock Selected") + op.action, op.mask = 'UNLOCK', 'SELECTED' + # 4: Up + op = pie.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Selected") + op.action, op.mask = 'LOCK', 'SELECTED' + # 5: Up/Left + op = pie.operator("object.vertex_group_lock", icon='LOCKED', text="Lock Unselected") + op.action, op.mask = 'LOCK', 'UNSELECTED' + # 6: Up/Right + op = pie.operator("object.vertex_group_lock", text="Lock Only Selected") + op.action, op.mask = 'LOCK', 'INVERT_UNSELECTED' + # 7: Down/Left + op = pie.operator("object.vertex_group_lock", text="Lock Only Unselected") + op.action, op.mask = 'UNLOCK', 'INVERT_UNSELECTED' + # 8: Down/Right + op = pie.operator("object.vertex_group_lock", text="Invert Locks") + op.action, op.mask = 'INVERT', 'ALL' + # ********** Panel ********** @@ -5058,8 +5385,8 @@ class VIEW3D_PT_view3d_properties(Panel): layout.use_property_split = True layout.use_property_decorate = False # No animation. - flow = layout.grid_flow(row_major=True, columns=0, even_columns=False, even_rows=False, align=True) - col = flow.column() + + col = layout.column() subcol = col.column() subcol.active = bool(view.region_3d.view_perspective != 'CAMERA' or view.region_quadviews) @@ -5069,20 +5396,22 @@ class VIEW3D_PT_view3d_properties(Panel): subcol.prop(view, "clip_start", text="Clip Start") subcol.prop(view, "clip_end", text="End") - subcol.separator() - - col = flow.column() + layout.separator() - subcol = col.column() - subcol.prop(view, "use_local_camera") + col = layout.column(align=False, heading="Local Camera") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(view, "use_local_camera", text="") + sub = sub.row(align=True) + sub.enabled = view.use_local_camera + sub.prop(view, "camera", text="") - subcol = col.column() - subcol.enabled = view.use_local_camera - subcol.prop(view, "camera", text="Local Camera") + layout.separator() - subcol = col.column(align=True) - subcol.prop(view, "use_render_border") - subcol.active = view.region_3d.view_perspective != 'CAMERA' + col = layout.column(align=True) + col.prop(view, "use_render_border") + col.active = view.region_3d.view_perspective != 'CAMERA' class VIEW3D_PT_view3d_lock(Panel): @@ -5101,23 +5430,24 @@ class VIEW3D_PT_view3d_lock(Panel): view = context.space_data col = layout.column(align=True) - subcol = col.column() - subcol.active = bool(view.region_3d.view_perspective != 'CAMERA' or view.region_quadviews) + sub = col.column() + sub.active = bool(view.region_3d.view_perspective != 'CAMERA' or view.region_quadviews) - subcol.prop(view, "lock_object") + sub.prop(view, "lock_object") lock_object = view.lock_object if lock_object: if lock_object.type == 'ARMATURE': - subcol.prop_search( + sub.prop_search( view, "lock_bone", lock_object.data, "edit_bones" if lock_object.mode == 'EDIT' else "bones", text="", ) else: - subcol.prop(view, "lock_cursor", text="Lock to 3D Cursor") + subcol = sub.column(heading="Lock") + subcol.prop(view, "lock_cursor", text="To 3D Cursor") - col.prop(view, "lock_camera") + col.prop(view, "lock_camera", text="Camera to View") class VIEW3D_PT_view3d_cursor(Panel): @@ -5240,6 +5570,9 @@ class VIEW3D_PT_object_type_visibility(Panel): ("surf", "Surface"), ("meta", "Meta"), ("font", "Text"), + ("hair", "Hair"), + ("pointcloud", "Point Cloud"), + ("volume", "Volume"), ("grease_pencil", "Grease Pencil"), (None, None), # Other @@ -5257,6 +5590,11 @@ class VIEW3D_PT_object_type_visibility(Panel): col.separator() continue + if attr == "hair" and not hasattr(bpy.data, "hairs"): + continue + elif attr == "pointcloud" and not hasattr(bpy.data, "pointclouds"): + continue + attr_v = "show_object_viewport_" f"{attr:s}" attr_s = "show_object_select_" f"{attr:s}" @@ -5377,6 +5715,7 @@ class VIEW3D_PT_shading_lighting(Panel): col.prop(shading, "studiolight_rotate_z", text="Rotation") col.prop(shading, "studiolight_intensity") col.prop(shading, "studiolight_background_alpha") + col.prop(shading, "studiolight_background_blur") col = split.column() # to align properly with above elif shading.type == 'RENDERED': @@ -5400,6 +5739,7 @@ class VIEW3D_PT_shading_lighting(Panel): col.prop(shading, "studiolight_rotate_z", text="Rotation") col.prop(shading, "studiolight_intensity") col.prop(shading, "studiolight_background_alpha") + col.prop(shading, "studiolight_background_blur") col = split.column() # to align properly with above @@ -5697,13 +6037,16 @@ class VIEW3D_PT_overlay_guides(Panel): split = col.split() sub = split.column() sub.prop(overlay, "show_text", text="Text Info") + sub.prop(overlay, "show_stats", text="Statistics") + sub = split.column() sub.prop(overlay, "show_cursor", text="3D Cursor") + sub.prop(overlay, "show_annotation", text="Annotations") if shading.type == 'MATERIAL': - col.prop(overlay, "show_look_dev") - - col.prop(overlay, "show_annotation", text="Annotations") + row = col.row() + row.active = shading.render_pass == 'COMBINED' + row.prop(overlay, "show_look_dev") class VIEW3D_PT_overlay_object(Panel): @@ -6062,6 +6405,12 @@ class VIEW3D_PT_overlay_sculpt(Panel): sub.active = sculpt.show_mask sub.prop(overlay, "sculpt_mode_mask_opacity", text="Mask") + row = layout.row(align=True) + row.prop(sculpt, "show_face_sets", text="") + sub = row.row() + sub.active = sculpt.show_face_sets + row.prop(overlay, "sculpt_mode_face_sets_opacity", text="Face Sets") + class VIEW3D_PT_overlay_pose(Panel): bl_space_type = 'VIEW_3D' @@ -6362,6 +6711,7 @@ class VIEW3D_PT_overlay_gpencil_options(Panel): 'EDIT_GPENCIL': "Edit Grease Pencil", 'SCULPT_GPENCIL': "Sculpt Grease Pencil", 'WEIGHT_GPENCIL': "Weight Grease Pencil", + 'VERTEX_GPENCIL': "Vertex Grease Pencil", 'OBJECT': "Grease Pencil", }[context.mode]) @@ -6375,9 +6725,10 @@ class VIEW3D_PT_overlay_gpencil_options(Panel): col = layout.column() row = col.row() row.prop(overlay, "use_gpencil_grid", text="") - sub = row.row() + sub = row.row(align=True) sub.active = overlay.use_gpencil_grid sub.prop(overlay, "gpencil_grid_opacity", text="Canvas", slider=True) + sub.prop(overlay, "use_gpencil_canvas_xray", text="", icon='XRAY') row = col.row() row.prop(overlay, "use_gpencil_fade_layers", text="") @@ -6386,17 +6737,32 @@ class VIEW3D_PT_overlay_gpencil_options(Panel): sub.prop(overlay, "gpencil_fade_layer", text="Fade Layers", slider=True) row = col.row() - row.prop(overlay, "use_gpencil_paper", text="") + row.prop(overlay, "use_gpencil_fade_objects", text="") sub = row.row(align=True) - sub.active = overlay.use_gpencil_paper - sub.prop(overlay, "gpencil_paper_opacity", text="Fade Objects", slider=True) - sub.prop(overlay, "use_gpencil_fade_objects", text="", icon='OUTLINER_OB_GREASEPENCIL') + sub.active = overlay.use_gpencil_fade_objects + sub.prop(overlay, "gpencil_fade_objects", text="Fade Objects", slider=True) + sub.prop(overlay, "use_gpencil_fade_gp_objects", text="", icon='OUTLINER_OB_GREASEPENCIL') + + if context.object.mode in {'EDIT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}: + split = layout.split() + col = split.column() + col.prop(overlay, "use_gpencil_edit_lines", text="Edit Lines") + col = split.column() + col.prop(overlay, "use_gpencil_multiedit_line_only", text="Only in Multiframe") + + if context.object.mode == 'EDIT_GPENCIL': + split = layout.split() + col = split.column() + col.prop(overlay, "use_gpencil_show_directions") + col = split.column() + col.prop(overlay, "use_gpencil_show_material_name", text="Material Name") - if context.object.mode in {'EDIT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}: - 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) + if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}: + layout.label(text="Vertex Paint") + layout.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True) + class VIEW3D_PT_quad_view(Panel): bl_space_type = 'VIEW_3D' @@ -6672,77 +7038,143 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.operator("gpencil.reproject", text="Reproject Strokes") +def draw_gpencil_layer_active(context, layout): + gpl = context.active_gpencil_layer + if gpl: + layout.label(text="Active Layer") + row = layout.row(align=True) + row.operator_context = 'EXEC_REGION_WIN' + row.operator_menu_enum("gpencil.layer_change", "layer", text="", icon='GREASEPENCIL') + row.prop(gpl, "info", text="") + row.operator("gpencil.layer_remove", text="", icon='X') + + +def draw_gpencil_material_active(context, layout): + ob = context.active_object + if ob and len(ob.material_slots) > 0 and ob.active_material_index >= 0: + ma = ob.material_slots[ob.active_material_index].material + if ma: + layout.label(text="Active Material") + row = layout.row(align=True) + row.operator_context = 'EXEC_REGION_WIN' + row.operator_menu_enum("gpencil.material_set", "slot", text="", icon='MATERIAL') + row.prop(ma, "name", text="") + + class VIEW3D_PT_gpencil_sculpt_context_menu(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'WINDOW' bl_label = "Sculpt Context Menu" + bl_ui_units_x = 12 def draw(self, context): - brush = context.tool_settings.gpencil_sculpt.brush + ts = context.tool_settings + settings = ts.gpencil_sculpt_paint + brush = settings.brush layout = self.layout - if context.mode == 'WEIGHT_GPENCIL': - layout.prop(brush, "weight") layout.prop(brush, "size", slider=True) layout.prop(brush, "strength") - layout.separator() - - # Frames - layout.label(text="Frames:") + # Layers + draw_gpencil_layer_active(context, layout) - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("gpencil.blank_frame_add", text="Insert Blank in Active Layer", icon='ADD') - layout.operator("gpencil.blank_frame_add", text="Insert Blank in All Layers", icon='ADD').all_layers = True +class VIEW3D_PT_gpencil_weight_context_menu(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_label = "Weight Paint Context Menu" + bl_ui_units_x = 12 - layout.separator() + def draw(self, context): + ts = context.tool_settings + settings = ts.gpencil_weight_paint + brush = settings.brush - layout.operator("gpencil.frame_duplicate", text="Duplicate Active Layer", icon='DUPLICATE') - layout.operator("gpencil.frame_duplicate", text="Duplicate All Layers", icon='DUPLICATE').mode = 'ALL' + layout = self.layout - layout.separator() + layout.prop(brush, "size", slider=True) + layout.prop(brush, "strength") + layout.prop(brush, "weight") - layout.operator("gpencil.delete", text="Delete Active Layer", icon='REMOVE').type = 'FRAME' - layout.operator("gpencil.active_frames_delete_all", text="Delete All Layers", icon='REMOVE') + # Layers + draw_gpencil_layer_active(context, layout) class VIEW3D_PT_gpencil_draw_context_menu(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'WINDOW' bl_label = "Draw Context Menu" + bl_ui_units_x = 12 def draw(self, context): - brush = context.tool_settings.gpencil_paint.brush + ts = context.tool_settings + settings = ts.gpencil_paint + brush = settings.brush gp_settings = brush.gpencil_settings layout = self.layout + is_vertex = settings.color_mode == 'VERTEXCOLOR' or brush.gpencil_tool == 'TINT' + + if brush.gpencil_tool not in {'ERASE', 'CUTTER', 'EYEDROPPER'} and is_vertex: + split = layout.split(factor=0.1) + split.prop(brush, "color", text="") + split.template_color_picker(brush, "color", value_slider=True) + + col = layout.column() + col.separator() + col.prop_menu_enum(gp_settings, "vertex_mode", text="Mode") + col.separator() if brush.gpencil_tool not in {'FILL', 'CUTTER'}: layout.prop(brush, "size", slider=True) if brush.gpencil_tool not in {'ERASE', 'FILL', 'CUTTER'}: layout.prop(gp_settings, "pen_strength") - layout.separator() + # Layers + draw_gpencil_layer_active(context, layout) + # Material + if not is_vertex: + draw_gpencil_material_active(context, layout) - # Frames - layout.label(text="Frames:") - layout.operator_context = 'INVOKE_REGION_WIN' +class VIEW3D_PT_gpencil_vertex_context_menu(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'WINDOW' + bl_label = "Vertex Paint Context Menu" + bl_ui_units_x = 12 - layout.operator("gpencil.blank_frame_add", text="Insert Blank in Active Layer", icon='ADD') - layout.operator("gpencil.blank_frame_add", text="Insert Blank in All Layers", icon='ADD').all_layers = True + def draw(self, context): + layout = self.layout + ts = context.tool_settings + settings = ts.gpencil_vertex_paint + brush = settings.brush + gp_settings = brush.gpencil_settings - layout.separator() + col = layout.column() - layout.operator("gpencil.frame_duplicate", text="Duplicate Active Layer", icon='DUPLICATE') - layout.operator("gpencil.frame_duplicate", text="Duplicate All Layers", icon='DUPLICATE').mode = 'ALL' + if brush.gpencil_vertex_tool in {'DRAW', 'REPLACE'}: + split = layout.split(factor=0.1) + split.prop(brush, "color", text="") + split.template_color_picker(brush, "color", value_slider=True) - layout.separator() + col = layout.column() + col.separator() + col.prop_menu_enum(gp_settings, "vertex_mode", text="Mode") + col.separator() - layout.operator("gpencil.delete", text="Delete Active Layer", icon='REMOVE').type = 'FRAME' - layout.operator("gpencil.active_frames_delete_all", text="Delete All Layers", icon='REMOVE') + row = col.row(align=True) + row.prop(brush, "size", text="Radius") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + + if brush.gpencil_vertex_tool in {'DRAW', 'BLUR', 'SMEAR'}: + row = layout.row(align=True) + row.prop(gp_settings, "pen_strength", slider=True) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + + # Layers + draw_gpencil_layer_active(context, layout) class VIEW3D_PT_paint_vertex_context_menu(Panel): @@ -6898,7 +7330,10 @@ class VIEW3D_PT_sculpt_context_menu(Panel): layout.prop(brush, "normal_weight", slider=True) if capabilities.has_pinch_factor: - layout.prop(brush, "crease_pinch_factor", slider=True, text="Pinch") + text = "Pinch" + if brush.sculpt_tool in {'BLOB', 'SNAKE_HOOK'}: + text = "Magnify" + layout.prop(brush, "crease_pinch_factor", slider=True, text=text) if capabilities.has_rake_factor: layout.prop(brush, "rake_factor", slider=True) @@ -6923,6 +7358,17 @@ class TOPBAR_PT_gpencil_materials(GreasePencilMaterialsPanel, Panel): return ob and ob.type == 'GPENCIL' +class TOPBAR_PT_gpencil_vertexcolor(GreasePencilVertexcolorPanel, Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Vertex Color" + bl_ui_units_x = 10 + + @classmethod + def poll(cls, context): + ob = context.object + return ob and ob.type == 'GPENCIL' + classes = ( VIEW3D_HT_header, VIEW3D_HT_tool_header, @@ -6954,7 +7400,6 @@ classes = ( VIEW3D_MT_select_edit_mesh, VIEW3D_MT_select_edit_curve, VIEW3D_MT_select_edit_surface, - VIEW3D_MT_edit_text_context_menu, VIEW3D_MT_select_edit_text, VIEW3D_MT_select_edit_metaball, VIEW3D_MT_edit_lattice_context_menu, @@ -6975,6 +7420,7 @@ classes = ( VIEW3D_MT_light_add, VIEW3D_MT_lightprobe_add, VIEW3D_MT_camera_add, + VIEW3D_MT_volume_add, VIEW3D_MT_add, VIEW3D_MT_image_add, VIEW3D_MT_object, @@ -6999,9 +7445,12 @@ classes = ( VIEW3D_MT_vertex_group, VIEW3D_MT_gpencil_vertex_group, VIEW3D_MT_paint_weight, + VIEW3D_MT_paint_weight_lock, VIEW3D_MT_sculpt, VIEW3D_MT_sculpt_set_pivot, VIEW3D_MT_mask, + VIEW3D_MT_face_sets, + VIEW3D_MT_face_sets_init, VIEW3D_MT_particle, VIEW3D_MT_particle_context_menu, VIEW3D_MT_particle_showhide, @@ -7014,6 +7463,7 @@ classes = ( VIEW3D_MT_pose_group, VIEW3D_MT_pose_ik, VIEW3D_MT_pose_constraints, + VIEW3D_MT_pose_names, VIEW3D_MT_pose_showhide, VIEW3D_MT_pose_apply, VIEW3D_MT_pose_context_menu, @@ -7039,6 +7489,7 @@ classes = ( VIEW3D_MT_edit_mesh_clean, VIEW3D_MT_edit_mesh_delete, VIEW3D_MT_edit_mesh_merge, + VIEW3D_MT_edit_mesh_split, VIEW3D_MT_edit_mesh_showhide, VIEW3D_MT_paint_gpencil, VIEW3D_MT_assign_material, @@ -7048,6 +7499,7 @@ classes = ( VIEW3D_MT_edit_gpencil_delete, VIEW3D_MT_edit_gpencil_showhide, VIEW3D_MT_weight_gpencil, + VIEW3D_MT_vertex_gpencil, VIEW3D_MT_gpencil_animation, VIEW3D_MT_gpencil_simplify, VIEW3D_MT_gpencil_copy_layer, @@ -7062,7 +7514,10 @@ classes = ( VIEW3D_MT_edit_curve_showhide, VIEW3D_MT_edit_surface, VIEW3D_MT_edit_font, - VIEW3D_MT_edit_text_chars, + VIEW3D_MT_edit_font_chars, + VIEW3D_MT_edit_font_kerning, + VIEW3D_MT_edit_font_delete, + VIEW3D_MT_edit_font_context_menu, VIEW3D_MT_edit_meta, VIEW3D_MT_edit_meta_showhide, VIEW3D_MT_edit_lattice, @@ -7084,6 +7539,8 @@ classes = ( VIEW3D_MT_orientations_pie, VIEW3D_MT_proportional_editing_falloff_pie, VIEW3D_MT_sculpt_mask_edit_pie, + VIEW3D_MT_wpaint_vgroup_lock_pie, + VIEW3D_MT_sculpt_face_sets_edit_pie, VIEW3D_PT_active_tool, VIEW3D_PT_active_tool_duplicate, VIEW3D_PT_view3d_properties, @@ -7131,10 +7588,13 @@ classes = ( VIEW3D_PT_paint_vertex_context_menu, VIEW3D_PT_paint_texture_context_menu, VIEW3D_PT_paint_weight_context_menu, + VIEW3D_PT_gpencil_vertex_context_menu, VIEW3D_PT_gpencil_sculpt_context_menu, + VIEW3D_PT_gpencil_weight_context_menu, VIEW3D_PT_gpencil_draw_context_menu, VIEW3D_PT_sculpt_context_menu, TOPBAR_PT_gpencil_materials, + TOPBAR_PT_gpencil_vertexcolor, TOPBAR_PT_annotation_layers, ) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 19d5e3da309..83144b33c67 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -21,6 +21,7 @@ from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( GreasePencilSculptOptionsPanel, GreasePencilDisplayPanel, + GreasePencilBrushFalloff, ) from bl_ui.properties_paint_common import ( UnifiedPaintPanel, @@ -71,6 +72,33 @@ class VIEW3D_MT_brush_context_menu(Menu): layout.operator("brush.reset") +class VIEW3D_MT_brush_gpencil_context_menu(Menu): + bl_label = "Brush Specials" + + def draw(self, context): + layout = self.layout + ts = context.tool_settings + + settings = None + if context.mode == 'PAINT_GPENCIL': + settings = ts.gpencil_paint + if context.mode == 'SCULPT_GPENCIL': + settings = ts.gpencil_sculpt_paint + elif context.mode == 'WEIGHT_GPENCIL': + settings = ts.gpencil_weight_paint + elif context.mode == 'VERTEX_GPENCIL': + settings = ts.gpencil_vertex_paint + + brush = getattr(settings, "brush", None) + # skip if no active brush + if not brush: + layout.label(text="No Brushes currently available", icon='INFO') + return + + layout.operator("gpencil.brush_reset") + layout.operator("gpencil.brush_reset_all") + + class VIEW3D_MT_brush_context_menu_paint_modes(Menu): bl_label = "Enabled Modes" @@ -97,21 +125,15 @@ class View3DPanel: # Used by vertex & weight paint def draw_vpaint_symmetry(layout, vpaint): - split = layout.split() - - col = split.column() - col.alignment = 'RIGHT' - col.label(text="Mirror") + col = layout.column() + col.use_property_split = True + col.use_property_decorate = False - col = split.column() - row = col.row(align=True) + row = col.row(heading="Mirror", align=True) row.prop(vpaint, "use_symmetry_x", text="X", toggle=True) row.prop(vpaint, "use_symmetry_y", text="Y", toggle=True) row.prop(vpaint, "use_symmetry_z", text="Z", toggle=True) - col = layout.column() - col.use_property_split = True - col.use_property_decorate = False col.prop(vpaint, "radial_symmetry", text="Radial") @@ -151,10 +173,10 @@ class VIEW3D_PT_tools_object_options_transform(View3DPanel, Panel): tool_settings = context.tool_settings - layout.label(text="Affect Only") - layout.prop(tool_settings, "use_transform_data_origin", text="Origins") - layout.prop(tool_settings, "use_transform_pivot_point_align", text="Locations") - layout.prop(tool_settings, "use_transform_skip_children", text="Parents") + col = layout.column(heading="Affect Only", align=True) + col.prop(tool_settings, "use_transform_data_origin", text="Origins") + col.prop(tool_settings, "use_transform_pivot_point_align", text="Locations") + col.prop(tool_settings, "use_transform_skip_children", text="Parents") # ********** default tools for editmode_mesh **************** @@ -181,16 +203,11 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): split = layout.split() - col = split.column() - col.alignment = 'RIGHT' - col.label(text="Mirror") - - col = split.column() - - row = col.row(align=True) - row.prop(mesh, "use_mirror_x", text="X", toggle=True) - row.prop(mesh, "use_mirror_y", text="Y", toggle=True) - row.prop(mesh, "use_mirror_z", text="Z", toggle=True) + row = layout.row(heading="Mirror") + sub = row.row(align=True) + sub.prop(mesh, "use_mirror_x", text="X", toggle=True) + sub.prop(mesh, "use_mirror_y", text="Y", toggle=True) + sub.prop(mesh, "use_mirror_z", text="Z", toggle=True) row = layout.row(align=True) row.active = ob.data.use_mirror_x or ob.data.use_mirror_y or ob.data.use_mirror_z @@ -226,62 +243,6 @@ class VIEW3D_PT_tools_meshedit_options_automerge(View3DPanel, Panel): col.prop(tool_settings, "use_mesh_automerge_and_split", toggle=False) col.prop(tool_settings, "double_threshold", text="Threshold") -# ********** default tools for editmode_curve **************** - - -class VIEW3D_PT_tools_curveedit_options_stroke(View3DPanel, Panel): - bl_category = "Tool" - bl_context = ".curve_edit" # dot on purpose (access from topbar) - bl_label = "Curve Stroke" - - def draw(self, context): - layout = self.layout - - tool_settings = context.tool_settings - cps = tool_settings.curve_paint_settings - - col = layout.column() - - col.prop(cps, "curve_type") - - if cps.curve_type == 'BEZIER': - col.label(text="Bezier Options:") - col.prop(cps, "error_threshold") - col.prop(cps, "fit_method") - col.prop(cps, "use_corners_detect") - - col = layout.column() - col.active = cps.use_corners_detect - col.prop(cps, "corner_angle") - - col.label(text="Pressure Radius:") - row = layout.row(align=True) - rowsub = row.row(align=True) - rowsub.prop(cps, "radius_min", text="Min") - rowsub.prop(cps, "radius_max", text="Max") - - row.prop(cps, "use_pressure_radius", text="", icon_only=True) - - col = layout.column() - col.label(text="Taper Radius:") - row = layout.row(align=True) - row.prop(cps, "radius_taper_start", text="Start") - row.prop(cps, "radius_taper_end", text="End") - - col = layout.column() - col.label(text="Projection Depth:") - row = layout.row(align=True) - row.prop(cps, "depth_mode", expand=True) - - col = layout.column() - if cps.depth_mode == 'SURFACE': - col.prop(cps, "surface_offset") - col.prop(cps, "use_offset_absolute") - col.prop(cps, "use_stroke_endpoints") - if cps.use_stroke_endpoints: - colsub = layout.column(align=True) - colsub.prop(cps, "surface_plane", expand=True) - # ********** default tools for editmode_armature **************** @@ -706,7 +667,7 @@ class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel): class VIEW3D_PT_tools_brush_falloff_frontface(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) - bl_label = "Frontface Falloff" + bl_label = "Front-face Falloff" bl_parent_id = "VIEW3D_PT_tools_brush_falloff" bl_options = {'DEFAULT_CLOSED'} @@ -810,38 +771,14 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): sub.prop(sculpt, "detail_refine_method", text="Refine Method") sub.prop(sculpt, "detail_type_method", text="Detailing") - col.prop(sculpt, "use_smooth_shading") - - -class VIEW3D_PT_sculpt_dyntopo_remesh(Panel, View3DPaintPanel): - bl_context = ".sculpt_mode" # dot on purpose (access from topbar) - bl_label = "Remesh" - bl_parent_id = "VIEW3D_PT_sculpt_dyntopo" - bl_options = {'DEFAULT_CLOSED'} - bl_ui_units_x = 12 - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False + if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: + col.operator("sculpt.detail_flood_fill") - tool_settings = context.tool_settings - sculpt = tool_settings.sculpt + col.prop(sculpt, "use_smooth_shading") - col = layout.column() - col.active = context.sculpt_object.use_dynamic_topology_sculpting - col.prop(sculpt, "symmetrize_direction") - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - col = flow.column() - col.operator("sculpt.symmetrize") - col = flow.column() - col.operator("sculpt.optimize") - if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: - col = flow.column() - col.operator("sculpt.detail_flood_fill") class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): @@ -868,9 +805,13 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): col.prop(mesh, "remesh_voxel_adaptivity") col.prop(mesh, "use_remesh_fix_poles") col.prop(mesh, "use_remesh_smooth_normals") - col.prop(mesh, "use_remesh_preserve_volume") - col.prop(mesh, "use_remesh_preserve_paint_mask") - col.operator("object.voxel_remesh", text="Remesh") + + col = layout.column(heading="Preserve", align=True) + col.prop(mesh, "use_remesh_preserve_volume", text="Volume") + col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") + col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") + + layout.operator("object.voxel_remesh", text="Remesh") # TODO, move to space_view3d.py @@ -891,15 +832,20 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): tool_settings = context.tool_settings sculpt = tool_settings.sculpt - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() + col = layout.column(heading="Display", align=True) col.prop(sculpt, "use_threaded", text="Threaded Sculpt") - col = flow.column() col.prop(sculpt, "show_low_resolution") - col = flow.column() + col.prop(sculpt, "use_sculpt_delay_updates") col.prop(sculpt, "use_deform_only") + col.separator() + + col = layout.column(heading="Auto-Masking", align=True) + col.prop(sculpt, "use_automasking_topology", text="Topology") + col.prop(sculpt, "use_automasking_face_sets", text="Face Sets") + col.prop(sculpt, "use_automasking_boundary_edges", text="Mesh Boundary") + col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary") + class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) @@ -941,54 +887,34 @@ class VIEW3D_PT_sculpt_symmetry(Panel, View3DPaintPanel): def draw(self, context): layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False sculpt = context.tool_settings.sculpt - split = layout.split() - - col = split.column() - col.alignment = 'RIGHT' - col.label(text="Mirror") - - col = split.column() - - row = col.row(align=True) + row = layout.row(align=True, heading="Mirror") row.prop(sculpt, "use_symmetry_x", text="X", toggle=True) row.prop(sculpt, "use_symmetry_y", text="Y", toggle=True) row.prop(sculpt, "use_symmetry_z", text="Z", toggle=True) - split = layout.split() - - col = split.column() - col.alignment = 'RIGHT' - col.label(text="Lock") - - col = split.column() - - row = col.row(align=True) + row = layout.row(align=True, heading="Lock") row.prop(sculpt, "lock_x", text="X", toggle=True) row.prop(sculpt, "lock_y", text="Y", toggle=True) row.prop(sculpt, "lock_z", text="Z", toggle=True) - split = layout.split() - - col = split.column() - col.alignment = 'RIGHT' - col.label(text="Tiling") - - col = split.column() - - row = col.row(align=True) + row = layout.row(align=True, heading="Tiling") row.prop(sculpt, "tile_x", text="X", toggle=True) row.prop(sculpt, "tile_y", text="Y", toggle=True) row.prop(sculpt, "tile_z", text="Z", toggle=True) - layout.use_property_split = True - layout.use_property_decorate = False - layout.prop(sculpt, "use_symmetry_feather", text="Feather") - layout.column().prop(sculpt, "radial_symmetry", text="Radial") - layout.column().prop(sculpt, "tile_offset", text="Tile Offset") + layout.prop(sculpt, "radial_symmetry", text="Radial") + layout.prop(sculpt, "tile_offset", text="Tile Offset") + + layout.separator() + + layout.prop(sculpt, "symmetrize_direction") + layout.operator("sculpt.symmetrize") class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel): @@ -1046,6 +972,7 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): col = layout.column() col.prop(tool_settings, "use_auto_normalize", text="Auto Normalize") + col.prop(tool_settings, "use_lock_relative", text="Lock-Relative") col.prop(tool_settings, "use_multipaint", text="Multi-Paint") col.prop(wpaint, "use_group_restrict") @@ -1190,19 +1117,14 @@ class VIEW3D_PT_tools_imagepaint_options(View3DPaintPanel, Panel): layout.prop(ipaint, "seam_bleed") layout.prop(ipaint, "dither", slider=True) - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() + col = layout.column() col.prop(ipaint, "use_occlude") - - col = flow.column() col.prop(ipaint, "use_backface_culling", text="Backface Culling") class VIEW3D_PT_tools_imagepaint_options_cavity(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) bl_label = "Cavity Mask" - bl_parent_id = "VIEW3D_PT_tools_imagepaint_options" bl_parent_id = "VIEW3D_PT_mask" bl_options = {'DEFAULT_CLOSED'} @@ -1356,7 +1278,7 @@ class VIEW3D_PT_tools_particlemode_options_display(View3DPanel, Panel): # Grease Pencil drawing brushes -class GreasePencilPanel: +class GreasePencilPaintPanel: bl_context = ".greasepencil_paint" bl_category = "Tool" @@ -1372,7 +1294,7 @@ class GreasePencilPanel: return True -class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPanel): +class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel): bl_label = "Brushes" def draw(self, context): @@ -1387,7 +1309,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePenci row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) col = row.column() - col.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="") + col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") if context.mode == 'PAINT_GPENCIL': brush = tool_settings.gpencil_paint.brush @@ -1400,8 +1322,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePenci layout.row().prop(brush, "icon_filepath", text="") -class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPanel): +class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPaintPanel): bl_label = "Brush Settings" + bl_options = {'DEFAULT_CLOSED'} # What is the point of brush presets? Seems to serve the exact same purpose as brushes themselves?? def draw_header_preset(self, _context): @@ -1448,7 +1371,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): @classmethod def poll(cls, context): brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool != 'ERASE' + return brush is not None and brush.gpencil_tool not in {'ERASE', 'TINT'} def draw(self, context): layout = self.layout @@ -1480,11 +1403,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): ma = brush.gpencil_settings.material col.separator() + col.prop(gp_settings, "hardness", slider=True) subcol = col.column(align=True) if ma and ma.grease_pencil.mode == 'LINE': subcol.enabled = False - subcol.prop(gp_settings, "gradient_factor", slider=True) - subcol.prop(gp_settings, "gradient_shape") + subcol.prop(gp_settings, "aspect") elif brush.gpencil_tool == 'FILL': row = col.row(align=True) @@ -1496,6 +1419,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): col.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") col.prop(gp_settings, "fill_threshold", text="Threshold") + class VIEW3D_PT_tools_grease_pencil_brush_stroke(Panel, View3DPanel): bl_context = ".greasepencil_paint" bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' @@ -1562,7 +1486,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_post_processing(View3DPanel, Panel): @classmethod def poll(cls, context): brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL'} + return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL', 'TINT'} def draw_header(self, context): if self.is_popover: @@ -1593,12 +1517,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_post_processing(View3DPanel, Panel): col1.prop(gp_settings, "pen_smooth_steps") col1 = col.column(align=True) - col1.prop(gp_settings, "pen_thick_smooth_factor") - col1.prop(gp_settings, "pen_thick_smooth_steps", text="Iterations") - - col1 = col.column(align=True) col1.prop(gp_settings, "pen_subdivision_steps") - col1.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) col1 = col.column(align=True) col1.prop(gp_settings, "simplify_factor") @@ -1617,7 +1536,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): @classmethod def poll(cls, context): brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL'} + return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL', 'TINT'} def draw_header(self, context): if self.is_popover: @@ -1632,7 +1551,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False - brush = context.tool_settings.gpencil_paint.brush + tool_settings = context.tool_settings + brush = tool_settings.gpencil_paint.brush + mode = tool_settings.gpencil_paint.color_mode gp_settings = brush.gpencil_settings if self.is_popover: @@ -1641,83 +1562,86 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): row.label(text=self.bl_label) col = layout.column() - col.active = gp_settings.use_settings_random - - col.prop(gp_settings, "random_pressure", text="Pressure", slider=True) - col.prop(gp_settings, "random_strength", text="Strength", slider=True) - col.prop(gp_settings, "uv_random", text="UV", slider=True) + col.enabled = gp_settings.use_settings_random row = col.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_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' - bl_label = "Curves" - bl_category = "Tool" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - brush = context.tool_settings.gpencil_paint.brush - return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL'} - - def draw(self, context): - pass - - -class VIEW3D_PT_tools_grease_pencil_brushcurves_sensitivity(View3DPanel, Panel): - bl_context = ".greasepencil_paint" - bl_label = "Sensitivity" - bl_category = "Tool" - bl_parent_id = "VIEW3D_PT_tools_grease_pencil_brushcurves" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True + row.prop(gp_settings, "random_pressure", text="Radius", slider=True) + row.prop(gp_settings, "use_stroke_random_radius", text="", icon='GP_SELECT_STROKES') + row.prop(gp_settings, "use_random_press_radius", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_random_press_radius and self.is_popover is False: + col.template_curve_mapping(gp_settings, "curve_random_pressure", brush=True, + use_negative_slope=True) - brush = context.tool_settings.gpencil_paint.brush - gp_settings = brush.gpencil_settings - - layout.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, - use_negative_slope=True) + row = col.row(align=True) + row.prop(gp_settings, "random_strength", text="Strength", slider=True) + row.prop(gp_settings, "use_stroke_random_strength", text="", icon='GP_SELECT_STROKES') + row.prop(gp_settings, "use_random_press_strength", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_random_press_strength and self.is_popover is False: + col.template_curve_mapping(gp_settings, "curve_random_strength", brush=True, + use_negative_slope=True) + row = col.row(align=True) + row.prop(gp_settings, "uv_random", text="UV", slider=True) + row.prop(gp_settings, "use_stroke_random_uv", text="", icon='GP_SELECT_STROKES') + row.prop(gp_settings, "use_random_press_uv", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_random_press_uv and self.is_popover is False: + col.template_curve_mapping(gp_settings, "curve_random_uv", brush=True, + use_negative_slope=True) -class VIEW3D_PT_tools_grease_pencil_brushcurves_strength(View3DPanel, Panel): - bl_context = ".greasepencil_paint" - bl_label = "Strength" - bl_category = "Tool" - bl_parent_id = "VIEW3D_PT_tools_grease_pencil_brushcurves" + col.separator() - def draw(self, context): - layout = self.layout - layout.use_property_split = True + col1 = col.column(align=True) + col1.enabled = mode == 'VERTEXCOLOR' and gp_settings.use_settings_random + row = col1.row(align=True) + row.prop(gp_settings, "random_hue_factor", slider=True) + row.prop(gp_settings, "use_stroke_random_hue", text="", icon='GP_SELECT_STROKES') + row.prop(gp_settings, "use_random_press_hue", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_random_press_hue and self.is_popover is False: + col1.template_curve_mapping(gp_settings, "curve_random_hue", brush=True, + use_negative_slope=True) + + row = col1.row(align=True) + row.prop(gp_settings, "random_saturation_factor", slider=True) + row.prop(gp_settings, "use_stroke_random_sat", text="", icon='GP_SELECT_STROKES') + row.prop(gp_settings, "use_random_press_sat", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_random_press_sat and self.is_popover is False: + col1.template_curve_mapping(gp_settings, "curve_random_saturation", brush=True, + use_negative_slope=True) + + row = col1.row(align=True) + row.prop(gp_settings, "random_value_factor", slider=True) + row.prop(gp_settings, "use_stroke_random_val", text="", icon='GP_SELECT_STROKES') + row.prop(gp_settings, "use_random_press_val", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_random_press_val and self.is_popover is False: + col1.template_curve_mapping(gp_settings, "curve_random_value", brush=True, + use_negative_slope=True) - brush = context.tool_settings.gpencil_paint.brush - gp_settings = brush.gpencil_settings + col.separator() - layout.template_curve_mapping(gp_settings, "curve_strength", brush=True, - use_negative_slope=True) + row = col.row(align=True) + row.prop(gp_settings, "pen_jitter", slider=True) + row.prop(gp_settings, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') + if gp_settings.use_jitter_pressure and self.is_popover is False: + col.template_curve_mapping(gp_settings, "curve_jitter", brush=True, + use_negative_slope=True) -class VIEW3D_PT_tools_grease_pencil_brushcurves_jitter(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_paint_falloff(GreasePencilBrushFalloff, Panel, View3DPaintPanel): bl_context = ".greasepencil_paint" - bl_label = "Jitter" - bl_category = "Tool" - bl_parent_id = "VIEW3D_PT_tools_grease_pencil_brushcurves" + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - layout.use_property_split = True + @classmethod + def poll(cls, context): + ts = context.tool_settings + settings = ts.gpencil_paint + brush = settings.brush + if brush is None: + return False - brush = context.tool_settings.gpencil_paint.brush - gp_settings = brush.gpencil_settings + tool = brush.gpencil_tool - layout.template_curve_mapping(gp_settings, "curve_jitter", brush=True, - use_negative_slope=True) + return (settings and settings.brush and settings.brush.curve and tool == 'TINT') # Grease Pencil stroke interpolation tools @@ -1768,25 +1692,49 @@ class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): # Grease Pencil stroke sculpting tools - -class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel): +class GreasePencilSculptPanel: bl_context = ".greasepencil_sculpt" - bl_label = "Brushes" bl_category = "Tool" + @classmethod + def poll(cls, context): + if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(gpd.is_stroke_sculpt_mode) + else: + return True + + +class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel): + bl_label = "Brushes" + 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 = context.scene.tool_settings + gpencil_paint = tool_settings.gpencil_sculpt_paint - layout.template_icon_view(settings, "sculpt_tool", show_labels=True) + row = layout.row() + row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) + col = row.column() + col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") -class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel): - bl_context = ".greasepencil_sculpt" - bl_category = "Tool" + if context.mode == 'SCULPT_GPENCIL': + brush = tool_settings.gpencil_sculpt_paint.brush + if brush is not None: + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if(brush.use_custom_icon): + layout.row().prop(brush, "icon_filepath", text="") + + +class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel, GreasePencilSculptPanel): bl_label = "Brush Settings" def draw(self, context): @@ -1794,7 +1742,8 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel): layout.use_property_split = True layout.use_property_decorate = False - settings = context.tool_settings.gpencil_sculpt + tool_settings = context.scene.tool_settings + settings = tool_settings.gpencil_sculpt_paint brush = settings.brush if not self.is_popover: @@ -1803,27 +1752,63 @@ class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel): ) brush_basic_gpencil_sculpt_settings(layout, context, brush) -# Grease Pencil weight painting tools +class VIEW3D_PT_tools_grease_pencil_brush_sculpt_falloff(GreasePencilBrushFalloff, Panel, View3DPaintPanel): + bl_context = ".greasepencil_sculpt" + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + ts = context.tool_settings + settings = ts.gpencil_sculpt_paint + return (settings and settings.brush and settings.brush.curve) -class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel): + +# Grease Pencil weight painting tools +class GreasePencilWeightPanel: bl_context = ".greasepencil_weight" - bl_label = "Brushes" bl_category = "Tool" + @classmethod + def poll(cls, context): + if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(gpd.is_stroke_weight_mode) + else: + return True + + +class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel): + bl_label = "Brushes" + 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 = context.scene.tool_settings + gpencil_paint = tool_settings.gpencil_weight_paint - layout.template_icon_view(settings, "weight_tool", show_labels=True) + row = layout.row() + row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) + col = row.column() + col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") -class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel): - bl_context = ".greasepencil_weight" - bl_category = "Tool" + if context.mode == 'WEIGHT_GPENCIL': + brush = tool_settings.gpencil_weight_paint.brush + if brush is not None: + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if(brush.use_custom_icon): + layout.row().prop(brush, "icon_filepath", text="") + + +class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel, GreasePencilWeightPanel): bl_label = "Brush Settings" def draw(self, context): @@ -1831,7 +1816,8 @@ class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel): layout.use_property_split = True layout.use_property_decorate = False - settings = context.tool_settings.gpencil_sculpt + tool_settings = context.scene.tool_settings + settings = tool_settings.gpencil_weight_paint brush = settings.brush if not self.is_popover: @@ -1841,6 +1827,268 @@ class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel): brush_basic_gpencil_weight_settings(layout, context, brush) +class VIEW3D_PT_tools_grease_pencil_brush_weight_falloff(GreasePencilBrushFalloff, Panel, View3DPaintPanel): + bl_context = ".greasepencil_weight" + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + ts = context.tool_settings + settings = ts.gpencil_weight_paint + brush = settings.brush + return (settings and settings.brush and settings.brush.curve) + + +# Grease Pencil vertex painting tools +class GreasePencilVertexPanel: + bl_context = ".greasepencil_vertex" + bl_category = "Tool" + + @classmethod + def poll(cls, context): + if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(gpd.is_stroke_vertex_mode) + else: + return True + + +class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel): + bl_label = "Brushes" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + tool_settings = context.scene.tool_settings + gpencil_paint = tool_settings.gpencil_vertex_paint + + row = layout.row() + row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) + + col = row.column() + col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="") + + if context.mode == 'VERTEX_GPENCIL': + brush = tool_settings.gpencil_vertex_paint.brush + if brush is not None: + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if(brush.use_custom_icon): + layout.row().prop(brush, "icon_filepath", text="") + + +class VIEW3D_PT_tools_grease_pencil_vertex_paint_settings(Panel, View3DPanel, GreasePencilVertexPanel): + bl_label = "Brush Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + tool_settings = context.scene.tool_settings + settings = tool_settings.gpencil_vertex_paint + brush = settings.brush + + if not self.is_popover: + from bl_ui.properties_paint_common import ( + brush_basic_gpencil_vertex_settings, + ) + brush_basic_gpencil_vertex_settings(layout, context, brush) + + +class VIEW3D_PT_tools_grease_pencil_brush_vertex_color(View3DPanel, Panel): + bl_context = ".greasepencil_vertex" + bl_label = "Color" + bl_category = "Tool" + + @classmethod + def poll(cls, context): + ob = context.object + ts = context.tool_settings + settings = ts.gpencil_vertex_paint + brush = settings.brush + + if ob is None or brush is None: + return False + + if context.region.type == 'TOOL_HEADER' or brush.gpencil_vertex_tool in {'BLUR', 'AVERAGE', 'SMEAR'}: + return False + + return True + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + ts = context.tool_settings + settings = ts.gpencil_vertex_paint + brush = settings.brush + gp_settings = brush.gpencil_settings + + col = layout.column() + + col.template_color_picker(brush, "color", value_slider=True) + + sub_row = col.row(align=True) + sub_row.prop(brush, "color", text="") + sub_row.prop(brush, "secondary_color", text="") + + sub_row.operator("gpencil.tint_flip", icon='FILE_REFRESH', text="") + + +class VIEW3D_PT_tools_grease_pencil_brush_vertex_falloff(GreasePencilBrushFalloff, Panel, View3DPaintPanel): + bl_context = ".greasepencil_vertex" + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + ts = context.tool_settings + settings = ts.gpencil_vertex_paint + return (settings and settings.brush and settings.brush.curve) + + +class VIEW3D_PT_tools_grease_pencil_brush_vertex_palette(View3DPanel, Panel): + bl_context = ".greasepencil_vertex" + bl_label = "Palette" + bl_category = "Tool" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_vertex_color' + + @classmethod + def poll(cls, context): + ob = context.object + ts = context.tool_settings + settings = ts.gpencil_vertex_paint + brush = settings.brush + + if ob is None or brush is None: + return False + + if brush.gpencil_vertex_tool in {'BLUR', 'AVERAGE', 'SMEAR'}: + return False + + return True + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + ts = context.tool_settings + settings = ts.gpencil_vertex_paint + + col = layout.column() + + row = col.row(align=True) + row.template_ID(settings, "palette", new="palette.new") + if settings.palette: + col.template_palette(settings, "palette", color=True) + + +class VIEW3D_PT_tools_grease_pencil_brush_mixcolor(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Color" + bl_category = "Tool" + + @classmethod + def poll(cls, context): + ob = context.object + ts = context.tool_settings + settings = ts.gpencil_paint + brush = settings.brush + + if ob is None or brush is None: + return False + + if context.region.type == 'TOOL_HEADER': + return False + + if brush.gpencil_tool == 'TINT': + return True + + if brush.gpencil_tool not in {'DRAW', 'FILL'}: + return False + + return True + + def draw(self, context): + layout = self.layout + ts = context.tool_settings + settings = ts.gpencil_paint + brush = settings.brush + gp_settings = brush.gpencil_settings + + if brush.gpencil_tool != 'TINT': + row = layout.row() + row.prop(settings, "color_mode", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = False + col = layout.column() + col.enabled = settings.color_mode == 'VERTEXCOLOR' or brush.gpencil_tool == 'TINT' + + col.template_color_picker(brush, "color", value_slider=True) + + sub_row = col.row(align=True) + sub_row.prop(brush, "color", text="") + sub_row.prop(brush, "secondary_color", text="") + + sub_row.operator("gpencil.tint_flip", icon='FILE_REFRESH', text="") + + if brush.gpencil_tool in {'DRAW', 'FILL'}: + col.prop(gp_settings, "vertex_mode", text="Mode") + col.prop(gp_settings, "vertex_color_factor", slider=True, text="Mix Factor") + + if brush.gpencil_tool == 'TINT': + col.prop(gp_settings, "vertex_mode", text="Mode") + + +class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel): + bl_context = ".greasepencil_paint" + bl_label = "Palette" + bl_category = "Tool" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_mixcolor' + + @classmethod + def poll(cls, context): + ob = context.object + ts = context.tool_settings + settings = ts.gpencil_paint + brush = settings.brush + + if ob is None or brush is None: + return False + + if brush.gpencil_tool == 'TINT': + return True + + if brush.gpencil_tool not in {'DRAW', 'FILL'}: + return False + + return True + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + ts = context.tool_settings + settings = ts.gpencil_paint + brush = settings.brush + + col = layout.column() + col.enabled = settings.color_mode == 'VERTEXCOLOR' or brush.gpencil_tool == 'TINT' + + row = col.row(align=True) + row.template_ID(settings, "palette", new="palette.new") + if settings.palette: + col.template_palette(settings, "palette", color=True) + + class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, Panel, View3DPanel): bl_context = ".greasepencil_sculpt" bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' @@ -1870,6 +2118,13 @@ class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilDisplayPanel, bl_label = "Cursor" +class VIEW3D_PT_tools_grease_pencil_vertex_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): + bl_context = ".greasepencil_vertex" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_vertex_paint_settings' + bl_category = "Tool" + bl_label = "Cursor" + + class VIEW3D_PT_gpencil_brush_presets(Panel, PresetPanel): """Brush settings""" bl_label = "Brush Presets" @@ -1880,15 +2135,15 @@ class VIEW3D_PT_gpencil_brush_presets(Panel, PresetPanel): classes = ( VIEW3D_MT_brush_context_menu, + VIEW3D_MT_brush_gpencil_context_menu, VIEW3D_MT_brush_context_menu_paint_modes, VIEW3D_PT_tools_object_options, VIEW3D_PT_tools_object_options_transform, VIEW3D_PT_tools_meshedit_options, VIEW3D_PT_tools_meshedit_options_automerge, - VIEW3D_PT_tools_curveedit_options_stroke, VIEW3D_PT_tools_armatureedit_options, VIEW3D_PT_tools_posemode_options, - + VIEW3D_PT_slots_projectpaint, VIEW3D_PT_tools_brush_select, VIEW3D_PT_tools_brush_settings, @@ -1908,7 +2163,6 @@ classes = ( VIEW3D_PT_tools_brush_display, VIEW3D_PT_sculpt_dyntopo, - VIEW3D_PT_sculpt_dyntopo_remesh, VIEW3D_PT_sculpt_voxel_remesh, VIEW3D_PT_sculpt_symmetry, VIEW3D_PT_sculpt_symmetry_for_topbar, @@ -1929,7 +2183,7 @@ classes = ( VIEW3D_PT_tools_imagepaint_symmetry, VIEW3D_PT_tools_imagepaint_options, - + VIEW3D_PT_tools_imagepaint_options_external, VIEW3D_MT_tools_projectpaint_stencil, @@ -1946,10 +2200,6 @@ classes = ( VIEW3D_PT_tools_grease_pencil_brush_post_processing, VIEW3D_PT_tools_grease_pencil_brush_random, VIEW3D_PT_tools_grease_pencil_brush_stabilizer, - VIEW3D_PT_tools_grease_pencil_brushcurves, - VIEW3D_PT_tools_grease_pencil_brushcurves_sensitivity, - VIEW3D_PT_tools_grease_pencil_brushcurves_strength, - VIEW3D_PT_tools_grease_pencil_brushcurves_jitter, VIEW3D_PT_tools_grease_pencil_paint_appearance, VIEW3D_PT_tools_grease_pencil_sculpt_select, VIEW3D_PT_tools_grease_pencil_sculpt_settings, @@ -1958,7 +2208,19 @@ classes = ( VIEW3D_PT_tools_grease_pencil_weight_paint_select, VIEW3D_PT_tools_grease_pencil_weight_paint_settings, VIEW3D_PT_tools_grease_pencil_weight_appearance, + VIEW3D_PT_tools_grease_pencil_vertex_paint_select, + VIEW3D_PT_tools_grease_pencil_vertex_paint_settings, + VIEW3D_PT_tools_grease_pencil_vertex_appearance, VIEW3D_PT_tools_grease_pencil_interpolate, + VIEW3D_PT_tools_grease_pencil_brush_mixcolor, + VIEW3D_PT_tools_grease_pencil_brush_mix_palette, + + VIEW3D_PT_tools_grease_pencil_brush_paint_falloff, + VIEW3D_PT_tools_grease_pencil_brush_sculpt_falloff, + VIEW3D_PT_tools_grease_pencil_brush_weight_falloff, + VIEW3D_PT_tools_grease_pencil_brush_vertex_color, + VIEW3D_PT_tools_grease_pencil_brush_vertex_palette, + VIEW3D_PT_tools_grease_pencil_brush_vertex_falloff, ) if __name__ == "__main__": # only for live edit. |