diff options
author | YimingWu <xp8110@outlook.com> | 2020-02-01 05:25:32 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2020-02-01 05:25:32 +0300 |
commit | b47883a990ee68e659a8a8b44729be9b8e0d002f (patch) | |
tree | 24a7733807992fc84445d30b63deaedfe1ab40a1 /release/scripts | |
parent | b5abbc40a07041af91dca5d0a4acd8e5f1518c91 (diff) | |
parent | d9ec25844b4ac3143775615469fe69b27105c108 (diff) |
Merge remote-tracking branch 'origin/master' into temp-lanpr-review
Diffstat (limited to 'release/scripts')
36 files changed, 3339 insertions, 3288 deletions
diff --git a/release/scripts/freestyle/modules/parameter_editor.py b/release/scripts/freestyle/modules/parameter_editor.py index e74b61ced94..534ee7d65be 100644 --- a/release/scripts/freestyle/modules/parameter_editor.py +++ b/release/scripts/freestyle/modules/parameter_editor.py @@ -188,7 +188,7 @@ class CurveMappingModifier(ScalarBlendModifier): # deprecated: return evaluateCurveMappingF(self.curve, 0, t) curve = self.curve curve.initialize() - result = curve.curves[0].evaluate(t) + result = curve.evaluate(curve=curve.curves[0], position=t) # float precision errors in t can give a very weird result for evaluate. # therefore, bound the result by the curve's min and max values return bound(curve.clip_min_y, result, curve.clip_max_y) diff --git a/release/scripts/presets/fluid/honey.py b/release/scripts/presets/fluid/honey.py index fbeb7f2b286..0ed3658470a 100644 --- a/release/scripts/presets/fluid/honey.py +++ b/release/scripts/presets/fluid/honey.py @@ -1,3 +1,3 @@ import bpy -bpy.context.fluid.settings.viscosity_base = 2.0 -bpy.context.fluid.settings.viscosity_exponent = 3 +bpy.context.fluid.domain_settings.viscosity_base = 2.0 +bpy.context.fluid.domain_settings.viscosity_exponent = 3 diff --git a/release/scripts/presets/fluid/oil.py b/release/scripts/presets/fluid/oil.py index 3d73de9303a..c33e5d4b6cf 100644 --- a/release/scripts/presets/fluid/oil.py +++ b/release/scripts/presets/fluid/oil.py @@ -1,3 +1,3 @@ import bpy -bpy.context.fluid.settings.viscosity_base = 5.0 -bpy.context.fluid.settings.viscosity_exponent = 5 +bpy.context.fluid.domain_settings.viscosity_base = 5.0 +bpy.context.fluid.domain_settings.viscosity_exponent = 5 diff --git a/release/scripts/presets/fluid/water.py b/release/scripts/presets/fluid/water.py index 0b68ad28c98..f34ecc834ff 100644 --- a/release/scripts/presets/fluid/water.py +++ b/release/scripts/presets/fluid/water.py @@ -1,3 +1,3 @@ import bpy -bpy.context.fluid.settings.viscosity_base = 1.0 -bpy.context.fluid.settings.viscosity_exponent = 6 +bpy.context.fluid.domain_settings.viscosity_base = 1.0 +bpy.context.fluid.domain_settings.viscosity_exponent = 6 diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 9ae5dbe071d..e6d68510312 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -80,6 +80,17 @@ def op_tool_cycle(tool, kmi_args): # ------------------------------------------------------------------------------ # Keymap Templates +def _template_items_context_menu(menu, key_args_primary): + return [ + op_menu(menu, kmi_args) + for kmi_args in (key_args_primary, {"type": 'APP', "value": 'PRESS'}) + ] + +def _template_items_context_panel(menu, key_args_primary): + return [ + op_panel(menu, kmi_args) + for kmi_args in (key_args_primary, {"type": 'APP', "value": 'PRESS'}) + ] def _template_items_object_subdivision_set(): return [ @@ -541,7 +552,7 @@ def km_uv_editor(params): ("uv.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, {"properties": [("extend", False), ("deselect_all", True)]}), - ("uv.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, + ("uv.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("extend", True), ("deselect_all", False)]}), ("transform.translate", {"type": "EVT_TWEAK_L", "value": 'ANY'}, None), ("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, @@ -560,7 +571,7 @@ def km_uv_editor(params): {"properties": [("unselected", True)]}), ("uv.reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), op_menu_pie("IMAGE_MT_uvs_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), - op_menu("IMAGE_MT_uvs_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("IMAGE_MT_uvs_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), ("wm.context_toggle", {"type": 'X', "value": 'PRESS'}, @@ -887,7 +898,7 @@ def km_graph_editor(params): ("graph.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None), op_menu("GRAPH_MT_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}), op_menu("GRAPH_MT_delete", {"type": 'DEL', "value": 'PRESS'}), - op_menu("GRAPH_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("GRAPH_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("graph.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ("graph.keyframe_insert", {"type": 'S', "value": 'PRESS'}, None), ("graph.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), @@ -1081,7 +1092,7 @@ def km_node_editor(params): ("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("node.backimage_fit", {"type": 'A', "value": 'PRESS', "alt": True}, None), ("node.backimage_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None), - op_menu("NODE_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("NODE_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("node.link_make", {"type": 'L', "value": 'PRESS'}, {"properties": [("replace", False)]}), ("node.link_make", {"type": 'L', "value": 'PRESS', "shift": True}, @@ -1166,7 +1177,7 @@ def km_info(params): ("info.report_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("info.report_delete", {"type": 'DEL', "value": 'PRESS'}, None), ("info.report_copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), - op_menu("INFO_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("INFO_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -1212,7 +1223,7 @@ def km_file_browser(params): {"properties": [("increment", -10)]}), ("file.filenum", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, {"properties": [("increment", -100)]}), - op_menu("FILEBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("FILEBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -1379,7 +1390,7 @@ def km_dopesheet(params): ("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'}, {"properties": [("data_path", 'space_data.auto_snap')]}), op_menu_pie("DOPESHEET_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), - op_menu("DOPESHEET_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("DOPESHEET_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), op_menu("DOPESHEET_MT_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}), op_menu("DOPESHEET_MT_delete", {"type": 'DEL', "value": 'PRESS'}), ("action.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), @@ -1453,7 +1464,7 @@ def km_nla_channels(params): {"properties": [("extend", True)]}), ("nla.tracks_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), ("nla.tracks_delete", {"type": 'DEL', "value": 'PRESS'}, None), - op_menu("NLA_MT_channel_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("NLA_MT_channel_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -1517,7 +1528,7 @@ def km_nla_editor(params): {"properties": [("mode", 'TIME_EXTEND')]}), ("transform.transform", {"type": 'R', "value": 'PRESS'}, {"properties": [("mode", 'TIME_SCALE')]}), - op_menu("NLA_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("NLA_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), op_menu_pie("NLA_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), @@ -1676,7 +1687,7 @@ def km_text(params): {"properties": [("lines", 1)]}), ("text.line_break", {"type": 'RET', "value": 'PRESS'}, None), ("text.line_break", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None), - op_menu("TEXT_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}), + *_template_items_context_menu("TEXT_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}), ("text.line_number", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None), ("text.insert", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None), ]) @@ -1808,7 +1819,7 @@ def km_sequencer(params): ("transform.seq_slide", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), - op_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None), ]) @@ -1895,7 +1906,7 @@ def km_console(params): {"properties": [("text", '\t')]}), ("console.indent_or_autocomplete", {"type": 'TAB', "value": 'PRESS'}, None), ("console.unindent", {"type": 'TAB', "value": 'PRESS', "shift": True}, None), - op_menu("CONSOLE_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("CONSOLE_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("console.insert", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None), ]) @@ -2016,7 +2027,7 @@ def km_clip_editor(params): ("clip.keyframe_insert", {"type": 'S', "value": 'PRESS'}, None), ("clip.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("clip.join_tracks", {"type": 'J', "value": 'PRESS', "ctrl": True}, None), - op_menu("CLIP_MT_tracking_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("CLIP_MT_tracking_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("wm.context_toggle", {"type": 'L', "value": 'PRESS'}, {"properties": [("data_path", 'space_data.lock_selection')]}), ("wm.context_toggle", {"type": 'D', "value": 'PRESS', "alt": True}, @@ -2209,7 +2220,7 @@ def km_animation_channels(params): ("anim.channels_group", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("anim.channels_ungroup", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None), # Menus. - op_menu("DOPESHEET_MT_channel_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("DOPESHEET_MT_channel_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -2292,7 +2303,7 @@ def km_grease_pencil_stroke_edit_mode(params): ("gpencil.active_frames_delete_all", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, None), ("gpencil.active_frames_delete_all", {"type": 'DEL', "value": 'PRESS', "shift": True}, None), # Context menu - op_menu("VIEW3D_MT_gpencil_edit_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_gpencil_edit_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Separate op_menu("GPENCIL_MT_separate", {"type": 'P', "value": 'PRESS'}), # Split and joint strokes @@ -2354,7 +2365,7 @@ def km_grease_pencil_stroke_paint_mode(params): ("wm.radial_control", {"type": 'S', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_paint.brush.size')]}), # Draw context menu - op_panel("VIEW3D_PT_gpencil_draw_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_panel("VIEW3D_PT_gpencil_draw_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Draw delete menu op_menu("GPENCIL_MT_gpencil_draw_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}), op_menu("GPENCIL_MT_gpencil_draw_delete", {"type": 'DEL', "value": 'PRESS'}), @@ -2495,7 +2506,7 @@ def km_grease_pencil_stroke_sculpt_mode(params): ("wm.radial_control", {"type": 'S', "value": 'PRESS'}, {"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt.brush.size')]}), # Context menu - op_panel("VIEW3D_PT_gpencil_sculpt_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_panel("VIEW3D_PT_gpencil_sculpt_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Display *_grease_pencil_display(), ]) @@ -2624,7 +2635,7 @@ def km_pose(params): ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), - op_menu("VIEW3D_MT_pose_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_pose_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools op_tool_cycle("builtin.select_box", {"type": 'Q', "value": 'PRESS'}), op_tool_cycle("builtin.move", {"type": 'W', "value": 'PRESS'}), @@ -2695,7 +2706,7 @@ def km_object_mode(params): {"properties": [("type", 'Scaling')]}), ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), - op_menu("VIEW3D_MT_object_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_object_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("object.link_to_collection", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("object.hide_view_clear", {"type": 'H', "value": 'PRESS', "alt": True}, None), @@ -2773,7 +2784,7 @@ def km_curve(params): {"properties": [("unselected", False)]}), ("curve.hide", {"type": 'H', "value": 'PRESS', "shift": True}, {"properties": [("unselected", True)]}), - op_menu("VIEW3D_MT_edit_curve_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_edit_curve_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), # Tools @@ -2875,7 +2886,7 @@ def km_image_paint(params): ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.image_paint.brush.use_smooth_stroke')]}), op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), - op_panel("VIEW3D_PT_paint_texture_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_panel("VIEW3D_PT_paint_texture_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools ("paint.brush_select", {"type": 'D', "value": 'PRESS'}, {"properties": [("image_tool", 'DRAW')]}), @@ -2925,7 +2936,7 @@ def km_vertex_paint(params): ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.vertex_paint.brush.use_smooth_stroke')]}), op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), - op_panel("VIEW3D_PT_paint_vertex_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools ("paint.brush_select", {"type": 'D', "value": 'PRESS'}, {"properties": [("vertex_tool", 'DRAW')]}), @@ -2959,7 +2970,7 @@ def km_weight_paint(params): {"properties": [("data_path", 'weight_paint_object.data.use_paint_mask_vertex')]}), ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}), - op_panel("VIEW3D_PT_paint_weight_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Bone selection for combined weight paint + pose mode. ("view3d.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), # Tools @@ -3071,7 +3082,7 @@ def km_sculpt(params): {"properties": [("sculpt_tool", 'DRAW')]}), # Menus - op_panel("VIEW3D_PT_sculpt_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -3126,7 +3137,7 @@ def km_mesh(params): ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), # Menus. - op_menu("VIEW3D_MT_edit_mesh_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_edit_mesh_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), #Tools *_template_items_basic_tools(), op_tool_cycle("builtin.bevel", {"type": 'B', "value": 'PRESS', "ctrl": True}), @@ -3189,7 +3200,7 @@ def km_armature(params): ("armature.dissolve", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None), ("armature.dissolve", {"type": 'DEL', "value": 'PRESS', "ctrl": True}, None), # Menus. - op_menu("VIEW3D_MT_armature_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_armature_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools. *_template_items_basic_tools(), op_tool_cycle("builtin.roll", {"type": 'Y', "value": 'PRESS'}), @@ -3223,7 +3234,7 @@ def km_metaball(params): ("mball.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}), ("mball.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}), ("mball.select_similar", {"type": 'G', "value": 'PRESS', "shift": True}, None), - op_menu("VIEW3D_MT_edit_metaball_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_edit_metaball_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), # Tools @@ -3249,7 +3260,7 @@ def km_lattice(params): ("lattice.select_more", {"type": 'UP_ARROW', "value": 'PRESS'}, None), ("lattice.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS'}, None), ("object.vertex_parent_set", {"type": 'P', "value": 'PRESS', "ctrl": True}, None), - op_menu("VIEW3D_MT_edit_lattice_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_edit_lattice_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), # Tools @@ -3298,7 +3309,7 @@ def km_particle(params): {"properties": [("data_path_primary", 'tool_settings.particle_edit.brush.strength')]}), ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), - op_menu("VIEW3D_MT_particle_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_particle_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) return keymap @@ -3388,7 +3399,7 @@ def km_font(params): ("font.text_insert", {"type": 'TEXTINPUT', "value": 'ANY', "any": True}, None), ("font.text_insert", {"type": 'BACK_SPACE', "value": 'PRESS', "alt": True}, {"properties": [("accent", True)]}), - op_menu("VIEW3D_MT_edit_text_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), + *_template_items_context_menu("VIEW3D_MT_edit_text_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), ]) diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index bb92e070d00..5af2bd22222 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -32,7 +32,6 @@ _modules = [ "constraint", "file", "image", - "mask", "mesh", "node", "object", diff --git a/release/scripts/startup/bl_operators/freestyle.py b/release/scripts/startup/bl_operators/freestyle.py index baac3556fb2..393dd467e88 100644 --- a/release/scripts/startup/bl_operators/freestyle.py +++ b/release/scripts/startup/bl_operators/freestyle.py @@ -89,16 +89,16 @@ class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator): min_dist = sys.float_info.max max_dist = -min_dist if m.type == 'DISTANCE_FROM_CAMERA': - ob_to_cam = matrix_to_camera * ob.matrix_world + ob_to_cam = matrix_to_camera @ ob.matrix_world for vert in selected_verts: # dist in the camera space - dist = (ob_to_cam * vert.co).length + dist = (ob_to_cam @ vert.co).length min_dist = min(dist, min_dist) max_dist = max(dist, max_dist) elif m.type == 'DISTANCE_FROM_OBJECT': for vert in selected_verts: # dist in the world space - dist = (ob.matrix_world * vert.co - target_location).length + dist = (ob.matrix_world @ vert.co - target_location).length min_dist = min(dist, min_dist) max_dist = max(dist, max_dist) # Fill the Range Min/Max entries with the computed distances diff --git a/release/scripts/startup/bl_operators/mask.py b/release/scripts/startup/bl_operators/mask.py deleted file mode 100644 index 2635f535b0b..00000000000 --- a/release/scripts/startup/bl_operators/mask.py +++ /dev/null @@ -1,40 +0,0 @@ -# ##### 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-80 compliant> - -from bpy.types import Menu -from bpy.app.translations import contexts as i18n_contexts - - -class MASK_MT_add(Menu): - bl_idname = "MASK_MT_add" - bl_label = "Add" - bl_translation_context = i18n_contexts.operator_default - - def draw(self, _context): - layout = self.layout - - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("mask.primitive_circle_add", text="Circle", icon='MESH_CIRCLE') - layout.operator("mask.primitive_square_add", text="Square", icon='MESH_PLANE') - - -classes = ( - MASK_MT_add, -) diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 0f6deea71eb..e49ca0320c7 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -349,8 +349,8 @@ class QuickSmoke(ObjectModeOperator, Operator): ) def execute(self, context): - if not bpy.app.build_options.mod_smoke: - self.report({'ERROR'}, "Built without Smoke modifier support") + if not bpy.app.build_options.fluid: + self.report({'ERROR'}, "Built without Fluid modifier") return {'CANCELLED'} fake_context = context.copy() @@ -366,11 +366,17 @@ class QuickSmoke(ObjectModeOperator, Operator): for obj in mesh_objects: fake_context["object"] = obj # make each selected object a smoke flow - bpy.ops.object.modifier_add(fake_context, type='SMOKE') - obj.modifiers[-1].smoke_type = 'FLOW' + bpy.ops.object.modifier_add(fake_context, type='FLUID') + obj.modifiers[-1].fluid_type = 'FLOW' # set type - obj.modifiers[-1].flow_settings.smoke_flow_type = self.style + obj.modifiers[-1].flow_settings.flow_type = self.style + + # set flow behavior + obj.modifiers[-1].flow_settings.flow_behavior = 'INFLOW' + + # use some surface distance for smoke emission + obj.modifiers[-1].flow_settings.surface_distance = 1.5 if not self.show_flows: obj.display_type = 'WIRE' @@ -388,10 +394,13 @@ class QuickSmoke(ObjectModeOperator, Operator): obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0)) # setup smoke domain - bpy.ops.object.modifier_add(type='SMOKE') - obj.modifiers[-1].smoke_type = 'DOMAIN' + bpy.ops.object.modifier_add(type='FLUID') + obj.modifiers[-1].fluid_type = 'DOMAIN' if self.style == 'FIRE' or self.style == 'BOTH': - obj.modifiers[-1].domain_settings.use_high_resolution = True + obj.modifiers[-1].domain_settings.use_noise = True + + # set correct cache file format for smoke + obj.modifiers[-1].domain_settings.cache_data_format = 'UNI' # Setup material @@ -431,47 +440,25 @@ class QuickSmoke(ObjectModeOperator, Operator): return {'FINISHED'} -class QuickFluid(ObjectModeOperator, Operator): - """Use selected objects in a fluid simulation""" - bl_idname = "object.quick_fluid" - bl_label = "Quick Fluid" +class QuickLiquid(Operator): + bl_idname = "object.quick_liquid" + bl_label = "Quick Liquid" bl_options = {'REGISTER', 'UNDO'} - style: EnumProperty( - name="Fluid Style", - items=( - ('INFLOW', "Inflow", ""), - ('BASIC', "Basic", ""), - ), - default='BASIC', - ) - initial_velocity: FloatVectorProperty( - name="Initial Velocity", - description="Initial velocity of the fluid", - min=-100.0, max=100.0, - default=(0.0, 0.0, 0.0), - subtype='VELOCITY', - ) show_flows: BoolProperty( - name="Render Fluid Objects", - description="Keep the fluid objects visible during rendering", - default=False, - ) - start_baking: BoolProperty( - name="Start Fluid Bake", - description=("Start baking the fluid immediately " - "after creating the domain object"), - default=False, - ) + name="Render Liquid Objects", + description="Keep the liquid objects visible during rendering", + default=False, + ) def execute(self, context): - if not bpy.app.build_options.mod_fluid: - self.report({'ERROR'}, "Built without Fluid modifier support") + if not bpy.app.build_options.fluid: + self.report({'ERROR'}, "Built without Fluid modifier") return {'CANCELLED'} fake_context = context.copy() mesh_objects = [obj for obj in context.selected_objects - if (obj.type == 'MESH' and 0.0 not in obj.dimensions)] + if obj.type == 'MESH'] min_co = Vector((100000.0, 100000.0, 100000.0)) max_co = -min_co @@ -481,47 +468,51 @@ class QuickFluid(ObjectModeOperator, Operator): for obj in mesh_objects: fake_context["object"] = obj - # make each selected object a fluid - bpy.ops.object.modifier_add(fake_context, type='FLUID_SIMULATION') - - # fluid has to be before constructive modifiers, - # so it might not be the last modifier - for mod in obj.modifiers: - if mod.type == 'FLUID_SIMULATION': - break - - if self.style == 'INFLOW': - mod.settings.type = 'INFLOW' - mod.settings.inflow_velocity = self.initial_velocity - else: - mod.settings.type = 'FLUID' - mod.settings.initial_velocity = self.initial_velocity + # make each selected object a liquid flow + bpy.ops.object.modifier_add(fake_context, type='FLUID') + obj.modifiers[-1].fluid_type = 'FLOW' + + # set type + obj.modifiers[-1].flow_settings.flow_type = 'LIQUID' + + # set flow behavior + obj.modifiers[-1].flow_settings.flow_behavior = 'GEOMETRY' + + # use some surface distance for smoke emission + obj.modifiers[-1].flow_settings.surface_distance = 0.0 - obj.hide_render = not self.show_flows if not self.show_flows: obj.display_type = 'WIRE' # store bounding box min/max for the domain object obj_bb_minmax(obj, min_co, max_co) - # add the fluid domain object + # add the liquid domain object bpy.ops.mesh.primitive_cube_add() obj = context.active_object - obj.name = "Fluid Domain" - - # give the fluid some room below the flows - # and scale with initial velocity - v = 0.5 * self.initial_velocity - obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + v - obj.scale = ( - 0.5 * (max_co - min_co) + - Vector((1.0, 1.0, 2.0)) + - Vector((abs(v[0]), abs(v[1]), abs(v[2]))) - ) + obj.name = "Liquid Domain" - # setup smoke domain - bpy.ops.object.modifier_add(type='FLUID_SIMULATION') - obj.modifiers[-1].settings.type = 'DOMAIN' + # give the liquid some room above the flows + obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0)) + + # setup liquid domain + bpy.ops.object.modifier_add(type='FLUID') + obj.modifiers[-1].fluid_type = 'DOMAIN' + obj.modifiers[-1].domain_settings.domain_type = 'LIQUID' + # set all domain borders to obstacle + obj.modifiers[-1].domain_settings.use_collision_border_front = True + obj.modifiers[-1].domain_settings.use_collision_border_back = True + obj.modifiers[-1].domain_settings.use_collision_border_right = True + obj.modifiers[-1].domain_settings.use_collision_border_left = True + obj.modifiers[-1].domain_settings.use_collision_border_top = True + obj.modifiers[-1].domain_settings.use_collision_border_bottom = True + + # set correct cache file format for liquid + obj.modifiers[-1].domain_settings.cache_mesh_format = 'BOBJECT' + + # allocate and show particle system for FLIP + obj.modifiers[-1].domain_settings.use_flip_particles = True # make the domain smooth so it renders nicely bpy.ops.object.shade_smooth() @@ -529,7 +520,7 @@ class QuickFluid(ObjectModeOperator, Operator): # create a ray-transparent material for the domain bpy.ops.object.material_slot_add() - mat = bpy.data.materials.new("Fluid Domain Material") + mat = bpy.data.materials.new("Liquid Domain Material") obj.material_slots[0].material = mat # Make sure we use nodes @@ -560,15 +551,12 @@ class QuickFluid(ObjectModeOperator, Operator): links.new(node_absorption.outputs["Volume"], node_out.inputs["Volume"]) node_absorption.inputs["Color"].default_value = (0.8, 0.9, 1.0, 1.0) - if self.start_baking: - bpy.ops.fluid.bake('INVOKE_DEFAULT') - return {'FINISHED'} classes = ( QuickExplode, - QuickFluid, QuickFur, QuickSmoke, + QuickLiquid, ) diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index deb33f77050..c811f542a3a 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -382,16 +382,16 @@ class AddPresetFluid(AddPresetBase, Operator): """Add or remove a Fluid Preset""" bl_idname = "fluid.preset_add" bl_label = "Add Fluid Preset" - preset_menu = "FLUID_PT_presets" + preset_menu = "FLUID_MT_presets" preset_defines = [ "fluid = bpy.context.fluid" - ] + ] preset_values = [ - "fluid.settings.viscosity_base", - "fluid.settings.viscosity_exponent", - ] + "fluid.domain_settings.viscosity_base", + "fluidanta.domain_settings.viscosity_exponent", + ] preset_subdir = "fluid" diff --git a/release/scripts/startup/bl_operators/rigidbody.py b/release/scripts/startup/bl_operators/rigidbody.py index 97149133dec..bc80500c888 100644 --- a/release/scripts/startup/bl_operators/rigidbody.py +++ b/release/scripts/startup/bl_operators/rigidbody.py @@ -241,7 +241,8 @@ class ConnectRigidBodies(Operator): description="Pattern used to connect objects", items=( ('SELECTED_TO_ACTIVE', "Selected to Active", "Connect selected objects to the active object"), - ('CHAIN_DISTANCE', "Chain by Distance", "Connect objects as a chain based on distance, starting at the active object"), + ('CHAIN_DISTANCE', "Chain by Distance", "Connect objects as a chain based on distance, " + "starting at the active object"), ), default='SELECTED_TO_ACTIVE', ) diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py index 4e61874b440..1f56cbe6d57 100644 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py @@ -283,7 +283,16 @@ def mergeUvIslands(islandList): # UV Edge list used for intersections as well as unique points. edges, uniqueEdgePoints = island2Edge(islandList[islandIdx]) - decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w, h, edges, uniqueEdgePoints]) + decoratedIslandList.append([ + islandList[islandIdx], + totFaceArea, + efficiency, + islandBoundsArea, + w, + h, + edges, + uniqueEdgePoints, + ]) # Sort by island bounding box area, smallest face area first. # no.. chance that to most simple edge loop first. @@ -389,7 +398,8 @@ def mergeUvIslands(islandList): # testcount+=1 # print 'Testing intersect' - Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector((boxLeft, boxBottom))) + Intersect = islandIntersectUvIsland( + sourceIsland, targetIsland, Vector((boxLeft, boxBottom))) # print 'Done', Intersect if Intersect == 1: # Line intersect, don't bother with this any more pass diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py index 1f982c331fb..39d792bd557 100644 --- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py +++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py @@ -176,7 +176,14 @@ class VertexPaintDirt(Operator): obj = context.object mesh = obj.data - ret = applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, self.dirt_angle, self.clean_angle, self.dirt_only) + ret = applyVertexDirt( + mesh, + self.blur_iterations, + self.blur_strength, + self.dirt_angle, + self.clean_angle, + self.dirt_only, + ) return ret diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index ac5d1a7ac30..8fc59ca493a 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -54,10 +54,9 @@ _modules = [ "properties_physics_common", "properties_physics_dynamicpaint", "properties_physics_field", - "properties_physics_fluid", "properties_physics_rigidbody", "properties_physics_rigidbody_constraint", - "properties_physics_smoke", + "properties_physics_fluid", "properties_physics_softbody", "properties_render", "properties_output", diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index c872b9acd4a..d4b2c39bd5e 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -945,7 +945,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "angle") col.prop(md, "limits", slider=True) - def SMOKE(self, layout, _ob, _md): + def FLUID(self, layout, _ob, _md): layout.label(text="Settings are inside the Physics tab") def SMOOTH(self, layout, ob, md): @@ -2414,6 +2414,7 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): row.prop(md, "layer_pass", text="Pass") row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') + classes = ( DATA_PT_modifiers, DATA_PT_gpencil_modifiers, 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 5fa98c533c3..45cb10bb3bd 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -131,122 +131,6 @@ class AnnotationDrawingToolsPanel: gpencil_stroke_placement_settings(context, col) -class GreasePencilStrokeEditPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Edit Strokes" - bl_category = "Tools" - bl_region_type = 'TOOLS' - - @classmethod - def poll(cls, context): - if context.gpencil_data is None: - return False - - gpd = context.gpencil_data - return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) - - def draw(self, context): - layout = self.layout - - is_3d_view = context.space_data.type == 'VIEW_3D' - - if not is_3d_view: - layout.label(text="Select:") - col = layout.column(align=True) - col.operator("gpencil.select_all", text="Select All") - col.operator("gpencil.select_box") - col.operator("gpencil.select_circle") - - layout.separator() - - col = layout.column(align=True) - col.operator("gpencil.select_linked") - col.operator("gpencil.select_more") - col.operator("gpencil.select_less") - col.operator("gpencil.select_alternate") - - layout.label(text="Edit:") - row = layout.row(align=True) - row.operator("gpencil.copy", text="Copy") - row.operator("gpencil.paste", text="Paste").type = 'ACTIVE' - row.operator("gpencil.paste", text="Paste by Layer").type = 'LAYER' - - col = layout.column(align=True) - col.operator("gpencil.delete") - col.operator("gpencil.duplicate_move", text="Duplicate") - if is_3d_view: - col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' - col.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps...", property="type") - - layout.separator() - - if not is_3d_view: - col = layout.column(align=True) - col.operator("transform.translate") # icon='MAN_TRANS' - col.operator("transform.rotate") # icon='MAN_ROT' - col.operator("transform.resize", text="Scale") # icon='MAN_SCALE' - - layout.separator() - - layout.separator() - col = layout.column(align=True) - col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction") - col.operator("gpencil.stroke_change_color", text="Assign Material") - - layout.separator() - col = layout.column(align=True) - col.operator("gpencil.stroke_subdivide", text="Subdivide") - row = col.row(align=True) - row.operator("gpencil.stroke_simplify_fixed", text="Simplify") - row.operator("gpencil.stroke_simplify", text="Adaptive") - row.operator("gpencil.stroke_trim", text="Trim") - - col.separator() - - row = col.row(align=True) - row.operator("gpencil.stroke_merge", text="Merge") - row.operator("gpencil.stroke_join", text="Join").type = 'JOIN' - row.operator("gpencil.stroke_join", text="& Copy").type = 'JOINCOPY' - - col.operator("gpencil.stroke_flip", text="Flip Direction") - - if is_3d_view: - layout.separator() - - col = layout.column(align=True) - col.operator_menu_enum("gpencil.stroke_separate", text="Separate...", property="mode") - col.operator("gpencil.stroke_split", text="Split") - - col = layout.column(align=True) - col.label(text="Cleanup:") - col.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") - col.operator_menu_enum("gpencil.frame_clean_fill", text="Clean Boundary Strokes...", property="mode") - - -class GreasePencilStrokeSculptPanel: - # subclass must set - # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Sculpt Strokes" - bl_category = "Tools" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.gpencil_sculpt - brush = settings.brush - - layout.template_icon_view(settings, "sculpt_tool", show_labels=True) - - if not self.is_popover: - from bl_ui.properties_paint_common import ( - brush_basic_gpencil_sculpt_settings, - ) - brush_basic_gpencil_sculpt_settings(layout, context, brush) - - class GreasePencilSculptOptionsPanel: bl_label = "Sculpt Strokes" @@ -278,14 +162,36 @@ class GreasePencilSculptOptionsPanel: # GP Object Tool Settings -class GreasePencilAppearancePanel: - bl_label = "Brush Appearance" +class GreasePencilDisplayPanel: + bl_label = "Brush Tip" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): ob = context.active_object - return ob and ob.type == 'GPENCIL' + brush = context.tool_settings.gpencil_paint.brush + if ob and ob.type == 'GPENCIL' and brush: + if context.mode == 'PAINT_GPENCIL': + return brush.gpencil_tool != 'ERASE' + else: + # GP Sculpt and Weight Paint always have Brush Tip panel. + return True + return False + + def draw_header(self, context): + if self.is_popover: + return + + 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="") def draw(self, context): layout = self.layout @@ -299,42 +205,36 @@ class GreasePencilAppearancePanel: brush = tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - sub = layout.column(align=True) - sub.enabled = not brush.use_custom_icon - sub.prop(gp_settings, "gp_icon", text="Icon") + if self.is_popover: + row = layout.row(align=True) + row.prop(gp_settings, "use_cursor", text="") + row.label(text="Display Cursor") - layout.prop(brush, "use_custom_icon") - sub = layout.column() - sub.active = brush.use_custom_icon - sub.prop(brush, "icon_filepath", text="") - - layout.prop(gp_settings, "use_cursor", text="Show Brush") + col = layout.column(align=True) + col.active = gp_settings.use_cursor if brush.gpencil_tool == 'DRAW': - layout.prop(gp_settings, "show_lasso", text="Show Fill Color While Drawing") + col.prop(gp_settings, "show_lasso", text="Show Fill Color While Drawing") if brush.gpencil_tool == 'FILL': - layout.prop(brush, "cursor_color_add", text="Color") + 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") + col = layout.column(align=True) - col.prop(brush, "use_cursor", text="Show Brush") - - if tool in {'THICKNESS', 'STRENGTH'}: - col.prop(brush, "cursor_color_add", text="Add") - col.prop(brush, "cursor_color_sub", text="Subtract") - elif tool == 'PINCH': - col.prop(brush, "cursor_color_add", text="Pinch") - col.prop(brush, "cursor_color_sub", text="Inflate") - elif tool == 'TWIST': - col.prop(brush, "cursor_color_add", text="CCW") - col.prop(brush, "cursor_color_sub", text="CW") - else: - col.prop(brush, "cursor_color_add", text="") + col.active = brush.use_cursor + + 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): @@ -601,7 +501,7 @@ class GPENCIL_MT_move_to_layer(Menu): gpl_active = context.active_gpencil_layer tot_layers = len(gpd.layers) i = tot_layers - 1 - while(i >= 0): + while i >= 0: gpl = gpd.layers[i] if gpl.info == gpl_active.info: icon = 'GREASEPENCIL' @@ -811,12 +711,11 @@ class GreasePencilToolsPanel: bl_options = {'DEFAULT_CLOSED'} @classmethod - def poll(cls, context): + def poll(cls, _context): # XXX - disabled in 2.8 branch. + # return (context.gpencil_data is not None) return False - return (context.gpencil_data is not None) - def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_mask_common.py b/release/scripts/startup/bl_ui/properties_mask_common.py index 94d7ac2c91e..3342fe1985a 100644 --- a/release/scripts/startup/bl_ui/properties_mask_common.py +++ b/release/scripts/startup/bl_ui/properties_mask_common.py @@ -22,6 +22,33 @@ # menus are referenced `as is` from bpy.types import Menu, UIList +from bpy.app.translations import contexts as i18n_contexts + + +# Use by both image & clip context menus. +def draw_mask_context_menu(layout, context): + layout.operator_menu_enum("mask.handle_type_set", "type") + layout.operator("mask.switch_direction") + layout.operator("mask.cyclic_toggle") + + layout.separator() + layout.operator("mask.copy_splines", icon='COPYDOWN') + layout.operator("mask.paste_splines", icon='PASTEDOWN') + + layout.separator() + + layout.operator("mask.shape_key_rekey", text="Re-key Shape Points") + layout.operator("mask.feather_weight_clear") + layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation") + + layout.separator() + + layout.operator("mask.parent_set") + layout.operator("mask.parent_clear") + + layout.separator() + + layout.operator("mask.delete") class MASK_UL_layers(UIList): @@ -320,6 +347,19 @@ class MASK_MT_mask(Menu): layout.menu("MASK_MT_animation") +class MASK_MT_add(Menu): + bl_idname = "MASK_MT_add" + bl_label = "Add" + bl_translation_context = i18n_contexts.operator_default + + def draw(self, _context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("mask.primitive_circle_add", text="Circle", icon='MESH_CIRCLE') + layout.operator("mask.primitive_square_add", text="Square", icon='MESH_PLANE') + + class MASK_MT_visibility(Menu): bl_label = "Show/Hide" @@ -383,6 +423,7 @@ class MASK_MT_select(Menu): classes = ( MASK_UL_layers, MASK_MT_mask, + MASK_MT_add, MASK_MT_visibility, MASK_MT_transform, MASK_MT_animation, diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index d8d90fec583..327df079d3b 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -26,74 +26,487 @@ class UnifiedPaintPanel: # bl_region_type = 'UI' @staticmethod + def get_brush_mode(context): + """ Get the correct mode for this context. For any context where this returns None, + no brush options should be displayed.""" + mode = context.mode + + if mode == 'PARTICLE': + # Particle brush settings currently completely do their own thing. + return None + + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) + + if not tool: + # If there is no active tool, then there can't be an active brush. + return None + + if not tool.has_datablock: + # tool.has_datablock is always true for tools that use brushes. + return None + + space_data = context.space_data + tool_settings = context.tool_settings + + if space_data: + space_type = space_data.type + if space_type == 'IMAGE_EDITOR': + if space_data.show_uvedit: + return 'UV_SCULPT' + return 'PAINT_2D' + elif space_type in {'VIEW_3D', 'PROPERTIES'}: + if mode == 'PAINT_TEXTURE': + if tool_settings.image_paint: + return mode + else: + return None + return mode + return None + + @staticmethod def paint_settings(context): tool_settings = context.tool_settings - if context.sculpt_object: + mode = UnifiedPaintPanel.get_brush_mode(context) + + # 3D paint settings + if mode == 'SCULPT': return tool_settings.sculpt - elif context.vertex_paint_object: + elif mode == 'PAINT_VERTEX': return tool_settings.vertex_paint - elif context.weight_paint_object: + elif mode == 'PAINT_WEIGHT': return tool_settings.weight_paint - elif context.image_paint_object: - if (tool_settings.image_paint and tool_settings.image_paint.detect_data()): - return tool_settings.image_paint - - return None - elif context.particle_edit_object: + elif mode == 'PAINT_TEXTURE': + return tool_settings.image_paint + elif mode == 'PARTICLE': return tool_settings.particle_edit - + # 2D paint settings + elif mode == 'PAINT_2D': + return tool_settings.image_paint + elif mode == 'UV_SCULPT': + return tool_settings.uv_sculpt + # Grease Pencil settings + elif mode == 'PAINT_GPENCIL': + return tool_settings.gpencil_paint + elif mode in {'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}: + return tool_settings.gpencil_sculpt return None @staticmethod - def unified_paint_settings(parent, context): + def prop_unified( + layout, + context, + brush, + prop_name, + unified_name=None, + pressure_name=None, + icon='NONE', + text=None, + slider=False, + header=False, + ): + """ Generalized way of adding brush options to the UI, + along with their pen pressure setting and global toggle, if they exist. """ + row = layout.row(align=True) ups = context.tool_settings.unified_paint_settings + prop_owner = brush + if unified_name and getattr(ups, unified_name): + prop_owner = ups - flow = parent.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() - col.prop(ups, "use_unified_size", text="Size") - col = flow.column() - col.prop(ups, "use_unified_strength", text="Strength") - if context.weight_paint_object: - col = flow.column() - col.prop(ups, "use_unified_weight", text="Weight") - elif context.vertex_paint_object or context.image_paint_object: - col = flow.column() - col.prop(ups, "use_unified_color", text="Color") - else: - col = flow.column() - col.prop(ups, "use_unified_color", text="Color") + row.prop(prop_owner, prop_name, icon=icon, text=text, slider=slider) - @staticmethod - def prop_unified_size(parent, context, brush, prop_name, *, icon='NONE', text=None, slider=False): - ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_size else brush - parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + if pressure_name: + row.prop(brush, pressure_name, text="") - @staticmethod - def prop_unified_strength(parent, context, brush, prop_name, *, icon='NONE', text=None, slider=False): - ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_strength else brush - parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + if unified_name and not header: + # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 + row.prop(ups, unified_name, text="", icon="WORLD") - @staticmethod - def prop_unified_weight(parent, context, brush, prop_name, *, icon='NONE', text=None, slider=False): - ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_weight else brush - parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + return row @staticmethod def prop_unified_color(parent, context, brush, prop_name, *, text=None): ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_color else brush - parent.prop(ptr, prop_name, text=text) + prop_owner = ups if ups.use_unified_color else brush + parent.prop(prop_owner, prop_name, text=text) @staticmethod def prop_unified_color_picker(parent, context, brush, prop_name, value_slider=True): ups = context.tool_settings.unified_paint_settings - ptr = ups if ups.use_unified_color else brush - parent.template_color_picker(ptr, prop_name, value_slider=value_slider) + prop_owner = ups if ups.use_unified_color else brush + parent.template_color_picker(prop_owner, prop_name, value_slider=value_slider) + + +### Classes to let various paint modes' panels share code, by sub-classing these classes. ### +class BrushPanel(UnifiedPaintPanel): + @classmethod + def poll(cls, context): + return cls.get_brush_mode(context) is not None + + +class BrushSelectPanel(BrushPanel): + bl_label = "Brushes" + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + brush = settings.brush + + row = layout.row() + large_preview = True + if large_preview: + row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False) + else: + row.column().template_ID(settings, "brush", new="brush.add") + col = row.column() + col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") + + if brush is not None: + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if brush.use_custom_icon: + layout.prop(brush, "icon_filepath", text="") + + +class ColorPalettePanel(BrushPanel): + bl_label = "Color Palette" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + + settings = cls.paint_settings(context) + brush = settings.brush + + if context.space_data.type == 'IMAGE_EDITOR' or context.image_paint_object: + capabilities = brush.image_paint_capabilities + return capabilities.has_color + + elif context.vertex_paint_object: + capabilities = brush.vertex_paint_capabilities + return capabilities.has_color + return False + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + + layout.template_ID(settings, "palette", new="palette.new") + if settings.palette: + layout.template_palette(settings, "palette", color=True) + + +class ClonePanel(BrushPanel): + bl_label = "Clone" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + + settings = cls.paint_settings(context) + + mode = cls.get_brush_mode(context) + if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + brush = settings.brush + return brush.image_tool == 'CLONE' + return False + + def draw_header(self, context): + settings = self.paint_settings(context) + self.layout.prop(settings, "use_clone_layer", text="") + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + + layout.active = settings.use_clone_layer + + ob = context.active_object + col = layout.column() + + if settings.mode == 'MATERIAL': + if len(ob.material_slots) > 1: + col.label(text="Materials") + col.template_list( + "MATERIAL_UL_matslots", "", + ob, "material_slots", + ob, "active_material_index", + rows=2, + ) + + mat = ob.active_material + if mat: + col.label(text="Source Clone Slot") + col.template_list( + "TEXTURE_UL_texpaintslots", "", + mat, "texture_paint_images", + mat, "paint_clone_slot", + rows=2, + ) + + elif settings.mode == 'IMAGE': + mesh = ob.data + + clone_text = mesh.uv_layer_clone.name if mesh.uv_layer_clone else "" + col.label(text="Source Clone Image") + col.template_ID(settings, "clone_image") + col.label(text="Source Clone UV Map") + col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) + + +class TextureMaskPanel(BrushPanel): + bl_label = "Texture Mask" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + brush = context.tool_settings.image_paint.brush + + col = layout.column() + col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8) + + mask_tex_slot = brush.mask_texture_slot + + # map_mode + layout.row().prop(mask_tex_slot, "mask_map_mode", text="Mask Mapping") + + if mask_tex_slot.map_mode == 'STENCIL': + if brush.mask_texture and brush.mask_texture.type == 'IMAGE': + layout.operator("brush.stencil_fit_image_aspect").mask = True + layout.operator("brush.stencil_reset_transform").mask = True + + col = layout.column() + col.prop(brush, "use_pressure_masking", text="Pressure Masking") + # angle and texture_angle_source + if mask_tex_slot.has_texture_angle: + col = layout.column() + col.prop(mask_tex_slot, "angle", text="Angle") + if mask_tex_slot.has_texture_angle_source: + col.prop(mask_tex_slot, "use_rake", text="Rake") + + if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle: + col.prop(mask_tex_slot, "use_random", text="Random") + if mask_tex_slot.use_random: + col.prop(mask_tex_slot, "random_angle", text="Random Angle") + + # scale and offset + col.prop(mask_tex_slot, "offset") + col.prop(mask_tex_slot, "scale") + + +class StrokePanel(BrushPanel): + bl_label = "Stroke" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + mode = self.get_brush_mode(context) + settings = self.paint_settings(context) + brush = settings.brush + + col = layout.column() + + col.prop(brush, "stroke_method") + col.separator() + + if brush.use_anchor: + col.prop(brush, "use_edge_to_edge", text="Edge To Edge") + + if brush.use_airbrush: + col.prop(brush, "rate", text="Rate", slider=True) + + if brush.use_space: + row = col.row(align=True) + row.prop(brush, "spacing", text="Spacing") + row.prop(brush, "use_pressure_spacing", toggle=True, text="") + + if brush.use_line or brush.use_curve: + row = col.row(align=True) + row.prop(brush, "spacing", text="Spacing") + + if mode == 'SCULPT': + col.row().prop(brush, "use_scene_spacing", text="Spacing Distance", expand=True) + + if mode in {'PAINT_TEXTURE', 'PAINT_2D', 'SCULPT'}: + if brush.image_paint_capabilities.has_space_attenuation or brush.sculpt_capabilities.has_space_attenuation: + col.prop(brush, "use_space_attenuation") + + if brush.use_curve: + col.separator() + col.template_ID(brush, "paint_curve", new="paintcurve.new") + col.operator("paintcurve.draw") + col.separator() + + if brush.use_space: + col.separator() + row = col.row(align=True) + col.prop(brush, "dash_ratio", text="Dash Ratio") + col.prop(brush, "dash_samples", text="Dash Length") + + if (mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter) or mode != 'SCULPT': + col.separator() + row = col.row(align=True) + if brush.jitter_unit == 'BRUSH': + row.prop(brush, "jitter", slider=True) + else: + row.prop(brush, "jitter_absolute") + row.prop(brush, "use_pressure_jitter", toggle=True, text="") + col.row().prop(brush, "jitter_unit", expand=True) + + col.separator() + col.prop(settings, "input_samples") + + +class SmoothStrokePanel(BrushPanel): + bl_label = "Stabilize Stroke" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + settings = cls.paint_settings(context) + brush = settings.brush + if brush.brush_capabilities.has_smooth_stroke: + return True + return False + + def draw_header(self, context): + settings = self.paint_settings(context) + brush = settings.brush + + self.layout.prop(brush, "use_smooth_stroke", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = self.paint_settings(context) + brush = settings.brush + + col = layout.column() + col.active = brush.use_smooth_stroke + col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + + +class FalloffPanel(BrushPanel): + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + settings = cls.paint_settings(context) + return (settings and settings.brush and settings.brush.curve) + + def draw(self, context): + layout = self.layout + settings = self.paint_settings(context) + mode = self.get_brush_mode(context) + brush = settings.brush + + if brush is None: + return + + col = layout.column(align=True) + row = col.row(align=True) + row.prop(brush, "curve_preset", text="") + + if brush.curve_preset == 'CUSTOM': + layout.template_curve_mapping(brush, "curve", brush=True) + + 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' + + if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT'}: + col.separator() + row = col.row(align=True) + row.use_property_split = True + row.use_property_decorate = False + row.prop(brush, "falloff_shape", expand=True) + + +class DisplayPanel(BrushPanel): + bl_label = "Brush Cursor" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + settings = self.paint_settings(context) + if settings and not self.is_popover: + self.layout.prop(settings, "show_brush", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + mode = self.get_brush_mode(context) + settings = self.paint_settings(context) + brush = settings.brush + tex_slot = brush.texture_slot + tex_slot_mask = brush.mask_texture_slot + + if self.is_popover: + row = layout.row(align=True) + row.prop(settings, "show_brush", text="") + row.label(text="Display Cursor") + + col = layout.column() + col.active = brush.brush_capabilities.has_overlay and settings.show_brush + + col.prop(brush, "cursor_color_add", text="Cursor Color") + if mode == 'SCULPT' and brush.sculpt_capabilities.has_secondary_color: + col.prop(brush, "cursor_color_subtract", text="Inverse Cursor Color") + + col.separator() + + row = col.row(align=True) + row.prop(brush, "cursor_overlay_alpha", text="Falloff Opacity") + row.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + row.prop( + brush, "use_cursor_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', + ) + + if mode in ['PAINT_2D', 'PAINT_TEXTURE', 'PAINT_VERTEX', 'SCULPT']: + row = col.row(align=True) + row.prop(brush, "texture_overlay_alpha", text="Texture Opacity") + row.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + if tex_slot.map_mode != 'STENCIL': + row.prop( + brush, "use_primary_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', + ) + + if mode in ['PAINT_TEXTURE', 'PAINT_2D']: + row = col.row(align=True) + row.prop(brush, "mask_overlay_alpha", text="Mask Texture Opacity") + row.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + if tex_slot_mask.map_mode != 'STENCIL': + row.prop( + brush, "use_secondary_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', + ) class VIEW3D_MT_tools_projectpaint_clone(Menu): @@ -108,99 +521,363 @@ class VIEW3D_MT_tools_projectpaint_clone(Menu): props.value = i -def brush_texpaint_common(panel, context, layout, brush, _settings, *, projpaint=False): - col = layout.column() +def brush_settings(layout, context, brush, popover=False): + """ Draw simple brush settings for Sculpt, + Texture/Vertex/Weight Paint modes, or skip certain settings for the popover """ - if brush.image_tool == 'FILL' and not projpaint: - col.prop(brush, "fill_threshold", text="Gradient Type", slider=True) + mode = UnifiedPaintPanel.get_brush_mode(context) - elif brush.image_tool == 'SOFTEN': - col.row().prop(brush, "direction", expand=True) - col.prop(brush, "sharp_threshold") - if not projpaint: - col.prop(brush, "blur_kernel_radius") - col.prop(brush, "blur_mode") - elif brush.image_tool == 'MASK': - col.prop(brush, "weight", text="Mask Value", slider=True) + ### Draw simple settings unique to each paint mode. ### + brush_shared_settings(layout, context, brush, popover) - elif brush.image_tool == 'CLONE': - if not projpaint: - col.prop(brush, "clone_image", text="Image") - col.prop(brush, "clone_alpha", text="Alpha") + # Sculpt Mode # + if mode == 'SCULPT': + capabilities = brush.sculpt_capabilities - if not panel.is_popover: - brush_basic_texpaint_settings(col, context, brush) + # normal_radius_factor + layout.prop(brush, "normal_radius_factor", slider=True) + # auto_smooth_factor and use_inverse_smooth_pressure + if capabilities.has_auto_smooth: + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "auto_smooth_factor", + pressure_name="use_inverse_smooth_pressure", + slider=True, + ) -def brush_texpaint_common_clone(_panel, context, layout, _brush, settings, *, projpaint=False): - ob = context.active_object - col = layout.column() + # topology_rake_factor + if ( + capabilities.has_topology_rake and + context.sculpt_object.use_dynamic_topology_sculpting + ): + layout.prop(brush, "topology_rake_factor", slider=True) + + # normal_weight + if capabilities.has_normal_weight: + layout.prop(brush, "normal_weight", slider=True) + + # crease_pinch_factor + if capabilities.has_pinch_factor: + text = "Pinch" + if brush.sculpt_tool in {'BLOB', 'SNAKE_HOOK'}: + text = "Magnify" + layout.prop(brush, "crease_pinch_factor", slider=True, text=text) + + # rake_factor + if capabilities.has_rake_factor: + layout.prop(brush, "rake_factor", slider=True) + + # plane_offset, use_offset_pressure, use_plane_trim, plane_trim + if capabilities.has_plane_offset: + layout.separator() + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "plane_offset", + pressure_name="use_offset_pressure", + slider=True, + ) - if settings.mode == 'MATERIAL': - if len(ob.material_slots) > 1: - col.label(text="Materials") - col.template_list("MATERIAL_UL_matslots", "", - ob, "material_slots", - ob, "active_material_index", rows=2) + 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") + layout.separator() + + # height + if capabilities.has_height: + layout.prop(brush, "height", slider=True, text="Height") + + # 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 + + if do_persistent: + layout.separator() + layout.prop(brush, "use_persistent") + layout.operator("sculpt.set_persistent_base") + layout.separator() + + if brush.sculpt_tool == 'ELASTIC_DEFORM': + layout.separator() + layout.prop(brush, "elastic_deform_type") + layout.prop(brush, "elastic_deform_volume_preservation", slider=True) + layout.separator() + + if brush.sculpt_tool == 'POSE': + row = layout.row() + row.prop(brush, "pose_offset") + + if brush.sculpt_tool == 'SCRAPE': + 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, "invert_to_scrape_fill", text="Invert to Scrape") + + if brush.sculpt_tool == 'GRAB': + layout.prop(brush, "use_grab_active_vertex") + + if brush.sculpt_tool == 'MULTIPLANE_SCRAPE': + col = layout.column() + col.prop(brush, "multiplane_scrape_angle") + col.prop(brush, "use_multiplane_scrape_dynamic") + col.prop(brush, "show_multiplane_scrape_planes_preview") + + if brush.sculpt_tool == 'MASK': + layout.row().prop(brush, "mask_tool", expand=True) + + # 3D and 2D Texture Paint Mode. + elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + capabilities = brush.image_paint_capabilities + + if brush.image_tool == 'FILL': + # For some reason fill threshold only appears to be implemented in 2D paint. + if brush.color_type == 'COLOR': + if mode == 'PAINT_2D': + layout.prop(brush, "fill_threshold", text="Fill Threshold", slider=True) + elif brush.color_type == 'GRADIENT': + layout.row().prop(brush, "gradient_fill_mode", expand=True) + + +def brush_shared_settings(layout, context, brush, popover=False): + """ Draw simple brush settings that are shared between different paint modes. """ + + mode = UnifiedPaintPanel.get_brush_mode(context) + + ### Determine which settings to draw. ### + blend_mode = False + size = False + size_mode = False + strength = False + strength_pressure = False + weight = False + direction = False + + # 3D and 2D Texture Paint # + if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + if not popover: + blend_mode = brush.image_paint_capabilities.has_color + size = brush.image_paint_capabilities.has_radius + strength = strength_pressure = True + + # Sculpt # + if mode == 'SCULPT': + size_mode = True + if not popover: + size = True + strength = True + strength_pressure = brush.sculpt_capabilities.has_strength_pressure + direction = not brush.sculpt_capabilities.has_direction + + # Vertex Paint # + if mode == 'PAINT_VERTEX': + if not popover: + blend_mode = True + size = True + strength = True + strength_pressure = True + + # Weight Paint # + if mode == 'PAINT_WEIGHT': + if not popover: + size = True + weight = brush.weight_paint_capabilities.has_weight + strength = strength_pressure = True + # Only draw blend mode for the Draw tool, because for other tools it is pointless. D5928#137944 + if brush.weight_tool == 'DRAW': + blend_mode = True + + # UV Sculpt # + if mode == 'UV_SCULPT': + size = True + strength = True + + ### Draw settings. ### + ups = context.scene.tool_settings.unified_paint_settings + + if blend_mode: + layout.prop(brush, "blend", text="Blend") + layout.separator() + + if weight: + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "weight", + unified_name="use_unified_weight", + slider=True, + ) + + size_owner = ups if ups.use_unified_size else brush + size_prop = "size" + if size_mode and (size_owner.use_locked_size == 'SCENE'): + size_prop = "unprojected_radius" + if size or size_mode: + if size: + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + size_prop, + unified_name="use_unified_size", + pressure_name="use_pressure_size", + text="Radius", + slider=True, + ) + if size_mode: + layout.row().prop(size_owner, "use_locked_size", expand=True) + layout.separator() + + if strength: + pressure_name = "use_pressure_strength" if strength_pressure else None + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + pressure_name=pressure_name, + slider=True, + ) + layout.separator() + + if direction: + layout.row().prop(brush, "direction", expand=True) + + +def brush_settings_advanced(layout, context, brush, popover=False): + """Draw advanced brush settings for Sculpt, Texture/Vertex/Weight Paint modes.""" + + mode = UnifiedPaintPanel.get_brush_mode(context) + + # In the popover we want to combine advanced brush settings with non-advanced brush settings. + if popover: + brush_settings(layout, context, brush, popover=True) + layout.separator() + layout.label(text="Advanced:") + + # These options are shared across many modes. + use_accumulate = False + use_frontface = False + + if mode == 'SCULPT': + capabilities = brush.sculpt_capabilities + use_accumulate = capabilities.has_accumulate + use_frontface = True + + # topology automasking + layout.prop(brush, "use_automasking_topology") + + # 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") + layout.separator() + + # 3D and 2D Texture Paint. + elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + capabilities = brush.image_paint_capabilities + use_accumulate = capabilities.has_accumulate + + if mode == 'PAINT_2D': + layout.prop(brush, "use_paint_antialiasing") + else: + layout.prop(brush, "use_alpha") - mat = ob.active_material - if mat: - col.label(text="Source Clone Slot") - col.template_list("TEXTURE_UL_texpaintslots", "", - mat, "texture_paint_images", - mat, "paint_clone_slot", rows=2) + # Tool specific settings + if brush.image_tool == 'SOFTEN': + layout.separator() + layout.row().prop(brush, "direction", expand=True) + layout.prop(brush, "sharp_threshold") + if mode == 'PAINT_2D': + layout.prop(brush, "blur_kernel_radius") + layout.prop(brush, "blur_mode") - elif settings.mode == 'IMAGE': - mesh = ob.data + elif brush.image_tool == 'MASK': + layout.prop(brush, "weight", text="Mask Value", slider=True) - clone_text = mesh.uv_layer_clone.name if mesh.uv_layer_clone else "" - col.label(text="Source Clone Image") - col.template_ID(settings, "clone_image") - col.label(text="Source Clone UV Map") - col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) + elif brush.image_tool == 'CLONE': + if mode == 'PAINT_2D': + layout.prop(brush, "clone_image", text="Image") + layout.prop(brush, "clone_alpha", text="Alpha") + # Vertex Paint # + elif mode == 'PAINT_VERTEX': + layout.prop(brush, "use_alpha") + if brush.vertex_tool != 'SMEAR': + use_accumulate = True + use_frontface = True -def brush_texpaint_common_color(_panel, context, layout, brush, _settings, *, projpaint=False): - UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) + # Weight Paint + elif mode == 'PAINT_WEIGHT': + if brush.weight_tool != 'SMEAR': + use_accumulate = True + use_frontface = True - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") - row.separator() - row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) + # Draw shared settings. + if use_accumulate: + layout.prop(brush, "use_accumulate") + if use_frontface: + layout.prop(brush, "use_frontface", text="Front Faces Only") -def brush_texpaint_common_gradient(_panel, context, layout, brush, _settings, *, projpaint=False): - layout.template_color_ramp(brush, "gradient", expand=True) - layout.use_property_split = True +def draw_color_settings(context, layout, brush, color_type=False): + """Draw color wheel and gradient settings.""" + ups = context.scene.tool_settings.unified_paint_settings - col = layout.column() + if color_type: + row = layout.row() + row.use_property_split = False + row.prop(brush, "color_type", expand=True) - if brush.image_tool == 'DRAW': - UnifiedPaintPanel.prop_unified_color(col, context, brush, "secondary_color", text="Background Color") - col.prop(brush, "gradient_stroke_mode", text="Gradient Mapping") - if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: - col.prop(brush, "grad_spacing") - else: # if brush.image_tool == 'FILL': - col.prop(brush, "gradient_fill_mode") + # Color wheel + if brush.color_type == 'COLOR': + UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) + row = layout.row(align=True) + UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") + 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') + # Gradient + elif brush.color_type == 'GRADIENT': + layout.template_color_ramp(brush, "gradient", expand=True) -def brush_texpaint_common_options(_panel, _context, layout, brush, _settings, *, projpaint=False): - capabilities = brush.image_paint_capabilities + layout.use_property_split = True - col = layout.column() + col = layout.column() - if capabilities.has_accumulate: - col.prop(brush, "use_accumulate") + if brush.image_tool == 'DRAW': + UnifiedPaintPanel.prop_unified( + col, + context, + brush, + "secondary_color", + unified_name="use_unified_color", + text="Background Color", + header=True, + ) - if capabilities.has_space_attenuation: - col.prop(brush, "use_space_attenuation") - - if projpaint: - col.prop(brush, "use_alpha") - else: - col.prop(brush, "use_paint_antialiasing") + col.prop(brush, "gradient_stroke_mode", text="Gradient Mapping") + if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: + col.prop(brush, "grad_spacing") # Used in both the View3D toolbar and texture properties @@ -282,93 +959,40 @@ def brush_mask_texture_settings(layout, brush): col.prop(mask_tex_slot, "offset") col.prop(mask_tex_slot, "scale") -# Basic Brush Options -# -# Share between topbar and brush panel. - - -def brush_basic_wpaint_settings(layout, context, brush, *, compact=False): - capabilities = brush.weight_paint_capabilities - - if capabilities.has_weight: - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_weight(row, context, brush, "weight", slider=True) - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - row.prop(brush, "use_pressure_strength", text="") - - layout.prop(brush, "blend", text="" if compact else "Blend") - - -def brush_basic_vpaint_settings(layout, context, brush, *, compact=False): - capabilities = brush.vertex_paint_capabilities - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - row.prop(brush, "use_pressure_strength", text="") - - if capabilities.has_color: - layout.prop(brush, "blend", text="" if compact else "Blend") - def brush_basic_texpaint_settings(layout, context, brush, *, compact=False): + """Draw Tool Settings header for Vertex Paint and 2D and 3D Texture Paint modes.""" capabilities = brush.image_paint_capabilities - if capabilities.has_radius: - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - row.prop(brush, "use_pressure_size", text="") - - row = layout.row(align=True) - - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - row.prop(brush, "use_pressure_strength", text="") - if capabilities.has_color: + UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") layout.prop(brush, "blend", text="" if compact else "Blend") - -def brush_basic_sculpt_settings(layout, context, brush, *, compact=False): - tool_settings = context.tool_settings - capabilities = brush.sculpt_capabilities - - row = layout.row(align=True) - - ups = tool_settings.unified_paint_settings - if ( - (ups.use_unified_size and ups.use_locked_size == 'SCENE') or - ((not ups.use_unified_size) and brush.use_locked_size == 'SCENE') - ): - UnifiedPaintPanel.prop_unified_size(row, context, brush, "unprojected_radius", slider=True, text="Radius") - else: - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - - row.prop(brush, "use_pressure_size", text="") - - # strength, use_strength_pressure, and use_strength_attenuation - row = layout.row(align=True) - - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength") - - if capabilities.has_strength_pressure: - row.prop(brush, "use_pressure_strength", text="") - - # direction - if not capabilities.has_direction: - layout.row().prop(brush, "direction", expand=True, **({"text": ""} if compact else {})) - - -def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact=True, is_toolbar=False): + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + pressure_name="use_pressure_size", + unified_name="use_unified_size", + slider=True, + text="Radius", + header=True + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + pressure_name="use_pressure_strength", + unified_name="use_unified_strength", + header=True + ) + + +def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False): gp_settings = brush.gpencil_settings + tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False) # Brush details if brush.gpencil_tool == 'ERASE': @@ -377,6 +1001,8 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY') + row = layout.row(align=True) + row.prop(gp_settings, "eraser_mode", expand=True) if gp_settings.eraser_mode == 'SOFT': row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) @@ -385,6 +1011,11 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= row.prop(gp_settings, "eraser_strength_factor") row = layout.row(align=True) row.prop(gp_settings, "eraser_thickness_factor") + + row = layout.row(align=True) + row.prop(gp_settings, "use_cursor", text="Display Cursor") + + # FIXME: tools must use their own UI drawing! elif brush.gpencil_tool == 'FILL': row = layout.row(align=True) row.prop(gp_settings, "fill_leak", text="Leak Size") @@ -392,26 +1023,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= row.prop(brush, "size", text="Thickness") row = layout.row(align=True) row.prop(gp_settings, "fill_simplify_level", text="Simplify") - row = layout.row(align=True) - row.prop(gp_settings, "fill_draw_mode", text="Boundary") - row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID') - # Fill options - if is_toolbar: - settings = context.tool_settings.gpencil_sculpt - row = layout.row(align=True) - sub = row.row(align=True) - sub.popover( - panel="TOPBAR_PT_gpencil_fill", - text="Fill Options", - ) - else: - row = layout.row(align=True) - row.prop(gp_settings, "fill_factor", text="Resolution") - if gp_settings.fill_draw_mode != 'STROKE': - row = layout.row(align=True) - row.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") - row = layout.row(align=True) - row.prop(gp_settings, "fill_threshold", text="Threshold") + else: # brush.gpencil_tool == 'DRAW': row = layout.row(align=True) row.prop(brush, "size", text="Radius") @@ -427,10 +1039,10 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, tool, *, compact= "builtin.line", "builtin.box", "builtin.circle", - "builtin.polyline", + "builtin.polyline" }: settings = context.tool_settings.gpencil_sculpt - if is_toolbar: + if compact: row = layout.row(align=True) row.prop(settings, "use_thickness_curve", text="", icon='CURVE_DATA') sub = row.row(align=True) @@ -491,10 +1103,8 @@ def brush_basic_gpencil_weight_settings(layout, _context, brush, *, compact=Fals row = layout.row(align=True) row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - - layout.prop(brush, "use_falloff") - layout.prop(brush, "weight", slider=True) + layout.prop(brush, "use_falloff") classes = ( diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 076b1f2592c..21abf8bb34c 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -55,7 +55,7 @@ def particle_panel_poll(cls, context): if not settings: return False - return settings.is_fluid is False and (engine in cls.COMPAT_ENGINES) + return (settings.is_fluid is False) and (engine in cls.COMPAT_ENGINES) def particle_get_settings(context): @@ -119,6 +119,7 @@ def find_modifier(ob, psys): if md.type == 'PARTICLE_SYSTEM': if md.particle_system == psys: return md + return None class PARTICLE_UL_particle_systems(bpy.types.UIList): @@ -159,7 +160,10 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel): @classmethod def poll(cls, context): engine = context.engine - return (context.particle_system or context.object or context.space_data.pin_id) and (engine in cls.COMPAT_ENGINES) + return ( + (context.particle_system or context.object or context.space_data.pin_id) and + (engine in cls.COMPAT_ENGINES) + ) def draw(self, context): layout = self.layout @@ -203,7 +207,7 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, Panel): col = layout.column() - if part.is_fluid is False: + if (part.is_fluid is False): row = col.row() row.enabled = particle_panel_enabled(context, psys) row.template_ID(psys, "settings", new="particle.new") diff --git a/release/scripts/startup/bl_ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py index cd7e99f255c..d9713cb8608 100644 --- a/release/scripts/startup/bl_ui/properties_physics_cloth.py +++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py @@ -160,6 +160,7 @@ class PHYSICS_PT_cloth_damping(PhysicButtonsPanel, Panel): col = flow.column() col.prop(cloth, "bending_damping", text="Bending") + class PHYSICS_PT_cloth_internal_springs(PhysicButtonsPanel, Panel): bl_label = "Internal Springs" bl_parent_id = 'PHYSICS_PT_cloth_physical_properties' @@ -201,6 +202,7 @@ class PHYSICS_PT_cloth_internal_springs(PhysicButtonsPanel, Panel): col = flow.column() col.prop(cloth, "internal_compression_stiffness_max", text="Max Compression") + class PHYSICS_PT_cloth_pressure(PhysicButtonsPanel, Panel): bl_label = "Pressure" bl_parent_id = 'PHYSICS_PT_cloth_physical_properties' diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index b69f2233035..5397020a521 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -99,8 +99,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel): physics_add(col, context.soft_body, "Soft Body", 'SOFT_BODY', 'MOD_SOFT', True) if obj.type == 'MESH': - physics_add(col, context.fluid, "Fluid", 'FLUID_SIMULATION', 'MOD_FLUIDSIM', True) - physics_add(col, context.smoke, "Smoke", 'SMOKE', 'MOD_SMOKE', True) + physics_add(col, context.fluid, "Fluid", 'FLUID', 'MOD_FLUIDSIM', True) physics_add_special( col, obj.rigid_body, "Rigid Body", @@ -118,7 +117,7 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel): ) -# cache-type can be 'PSYS' 'HAIR' 'SMOKE' etc. +# cache-type can be 'PSYS' 'HAIR' 'FLUID' etc. def point_cache_ui(self, cache, enabled, cachetype): layout = self.layout @@ -141,10 +140,10 @@ def point_cache_ui(self, cache, enabled, cachetype): col.operator("ptcache.add", icon='ADD', text="") col.operator("ptcache.remove", icon='REMOVE', text="") - if cachetype in {'PSYS', 'HAIR', 'SMOKE'}: + if cachetype in {'PSYS', 'HAIR', 'FLUID'}: col = layout.column() - if cachetype == 'SMOKE': + if cachetype == 'FLUID': col.prop(cache, "use_library_path", text="Use Library Path") col.prop(cache, "use_external") @@ -160,14 +159,14 @@ def point_cache_ui(self, cache, enabled, cachetype): col.alignment = 'RIGHT' col.label(text=cache_info) else: - if cachetype in {'SMOKE', 'DYNAMIC_PAINT'}: + if cachetype in {'FLUID', 'DYNAMIC_PAINT'}: if not is_saved: col = layout.column(align=True) col.alignment = 'RIGHT' col.label(text="Cache is disabled until the file is saved") layout.enabled = False - if not cache.use_external or cachetype == 'SMOKE': + if not cache.use_external or cachetype == 'FLUID': col = layout.column(align=True) if cachetype not in {'PSYS', 'DYNAMIC_PAINT'}: @@ -175,18 +174,18 @@ def point_cache_ui(self, cache, enabled, cachetype): col.prop(cache, "frame_start", text="Simulation Start") col.prop(cache, "frame_end") - if cachetype not in {'SMOKE', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}: + if cachetype not in {'FLUID', 'CLOTH', 'DYNAMIC_PAINT', 'RIGID_BODY'}: col.prop(cache, "frame_step") cache_info = cache.info - if cachetype != 'SMOKE' and cache_info: # avoid empty space. + if cachetype != 'FLUID' and cache_info: # avoid empty space. col = layout.column(align=True) col.alignment = 'RIGHT' col.label(text=cache_info) can_bake = True - if cachetype not in {'SMOKE', 'DYNAMIC_PAINT', 'RIGID_BODY'}: + if cachetype not in {'FLUID', 'DYNAMIC_PAINT', 'RIGID_BODY'}: if not is_saved: col = layout.column(align=True) col.alignment = 'RIGHT' @@ -269,7 +268,7 @@ def effector_weights_ui(self, weights, weight_type): col.prop(weights, "curve_guide", slider=True) col.prop(weights, "texture", slider=True) - if weight_type != 'SMOKE': + if weight_type != 'FLUID': col.prop(weights, "smokeflow", slider=True) col = flow.column() diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index b9e690629d1..6b0dd7ac36f 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -21,16 +21,18 @@ import bpy from bpy.types import ( Panel, + Menu, +) +from .properties_physics_common import ( + effector_weights_ui, ) -from bpy.app.translations import pgettext_iface as iface_ -from bl_ui.utils import PresetPanel -class FLUID_PT_presets(PresetPanel, Panel): +class FLUID_MT_presets(Menu): bl_label = "Fluid Presets" preset_subdir = "fluid" preset_operator = "script.execute_preset" - preset_add_operator = "fluid.preset_add" + draw = Menu.draw_preset class PhysicButtonsPanel: @@ -39,28 +41,59 @@ class PhysicButtonsPanel: bl_context = "physics" @staticmethod + def check_domain_has_unbaked_guide(domain): + return ( + domain.use_guide and not domain.has_cache_baked_guide and + ((domain.guide_source == 'EFFECTOR') or + (domain.guide_source == 'DOMAIN' and not domain.guide_parent)) + ) + + @staticmethod def poll_fluid(context): ob = context.object if not ((ob and ob.type == 'MESH') and (context.fluid)): return False - return (bpy.app.build_options.mod_fluid) + md = context.fluid + return md and (context.fluid.fluid_type != 'NONE') + + @staticmethod + def poll_fluid_domain(context): + if not PhysicButtonsPanel.poll_fluid(context): + return False + + md = context.fluid + return md and (md.fluid_type == 'DOMAIN') @staticmethod - def poll_fluid_settings(context): - if not (PhysicButtonsPanel.poll_fluid(context)): + def poll_gas_domain(context): + if not PhysicButtonsPanel.poll_fluid(context): return False md = context.fluid - return md and md.settings and (md.settings.type != 'NONE') + if md and (md.fluid_type == 'DOMAIN'): + domain = md.domain_settings + return domain.domain_type in {'GAS'} + return False @staticmethod - def poll_fluid_domain(context): + def poll_liquid_domain(context): if not PhysicButtonsPanel.poll_fluid(context): return False md = context.fluid - return md and md.settings and (md.settings.type == 'DOMAIN') + if md and (md.fluid_type == 'DOMAIN'): + domain = md.domain_settings + return domain.domain_type in {'LIQUID'} + return False + + @staticmethod + def poll_fluid_flow(context): + if not PhysicButtonsPanel.poll_fluid(context): + return False + + md = context.fluid + return md and (md.fluid_type == 'FLOW') class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel): @@ -70,325 +103,903 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel, Panel): @classmethod def poll(cls, context): ob = context.object - return (ob and ob.type == 'MESH') and context.engine in cls.COMPAT_ENGINES and (context.fluid) + return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.fluid) def draw(self, context): layout = self.layout layout.use_property_split = True - if not bpy.app.build_options.mod_fluid: - col = layout.column() + if not bpy.app.build_options.fluid: + col = layout.column(align=True) col.alignment = 'RIGHT' - col.label(text="Built without fluids") + col.label(text="Built without Fluid modifier") return - md = context.fluid - fluid = md.settings - col = layout.column() - col.prop(fluid, "type") + layout.prop(md, "fluid_type") -class PHYSICS_PT_fluid_flow(PhysicButtonsPanel, Panel): - bl_label = "Flow" - bl_parent_id = "PHYSICS_PT_fluid" +class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): + bl_label = "Settings" + bl_parent_id = 'PHYSICS_PT_fluid' COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): - md = context.fluid - fluid = md.settings - - if not PhysicButtonsPanel.poll_fluid_settings(context): + if not PhysicButtonsPanel.poll_fluid(context): return False - return fluid.type in {'INFLOW', 'OUTFLOW', 'CONTROL'} and (context.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - md = context.fluid - fluid = md.settings - self.layout.prop(fluid, "use", text="") + return (context.engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout layout.use_property_split = True md = context.fluid - fluid = md.settings + ob = context.object + scene = context.scene - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) + if md.fluid_type == 'DOMAIN': + domain = md.domain_settings - flow.active = fluid.use + # Deactivate UI if guides are enabled but not baked yet. + layout.active = not self.check_domain_has_unbaked_guide(domain) - if fluid.type == 'INFLOW': - col = flow.column() - col.prop(fluid, "volume_initialization", text="Volume Initialization") - col.prop(fluid, "use_animated_mesh") + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_baked_data - row = col.row() - row.active = not fluid.use_animated_mesh - row.prop(fluid, "use_local_coords") + row = layout.row() + row.enabled = not is_baking_any and not has_baked_data + row.prop(domain, "domain_type", expand=False) - col = flow.column() - col.prop(fluid, "inflow_velocity", text="Inflow Velocity") + 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_data - elif fluid.type == 'OUTFLOW': col = flow.column() - col.prop(fluid, "volume_initialization", text="Volume Initialization") + 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(fluid, "use_animated_mesh") + 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") - elif fluid.type == 'CONTROL': - col = flow.column() - col.prop(fluid, "quality", slider=True) - col.prop(fluid, "use_reverse_frames") + col.separator() col = flow.column() - col.prop(fluid, "start_time", text="Time Start") - col.prop(fluid, "end_time", text="End") + if scene.use_gravity: + sub = col.column() + sub.enabled = False + 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") + + if domain.cache_type == 'MODULAR': + col.separator() + split = layout.split() - col.separator() + 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: + col = split.column() + col.operator("fluid.bake_data", text="Resume") + col = split.column() + col.operator("fluid.free_data", text="Free") + elif domain.is_cache_baking_data and not domain.has_cache_baked_data: + split.enabled = False + split.operator("fluid.pause_bake", text="Baking Data - ESC to pause") + elif not domain.has_cache_baked_data and not domain.is_cache_baking_data: + split.operator("fluid.bake_data", text="Bake Data") + else: + split.operator("fluid.free_data", text="Free Data") + + elif md.fluid_type == 'FLOW': + flow = md.flow_settings + + row = layout.row() + row.prop(flow, "flow_type", expand=False) + + grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + + col = grid.column() + col.prop(flow, "flow_behavior", expand=False) + if flow.flow_behavior in {'INFLOW'}: + col.prop(flow, "use_inflow", text="Use Inflow") + + col.prop(flow, "subframes", text="Sampling Substeps") + + if not flow.flow_behavior == 'OUTFLOW' and flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}: + + if flow.flow_type in {'SMOKE', 'BOTH'}: + col.prop(flow, "smoke_color", text="Smoke Color") + + col = grid.column(align=True) + col.prop(flow, "use_absolute", text="Absolute Density") + + if flow.flow_type in {'SMOKE', 'BOTH'}: + col.prop(flow, "temperature", text="Initial Temperature") + col.prop(flow, "density", text="Density") + + if flow.flow_type in {'FIRE', 'BOTH'}: + col.prop(flow, "fuel_amount", text="Fuel") + + col.separator() + col.prop_search(flow, "density_vertex_group", ob, "vertex_groups", text="Vertex Group") + + elif md.fluid_type == 'EFFECTOR': + effector_settings = md.effector_settings + + 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) col = flow.column() - col.prop(fluid, "attraction_strength", text="Attraction Strength") - col.prop(fluid, "attraction_radius", text="Radius") - col.separator() + col.prop(effector_settings, "use_plane_init", text="Is Planar") + col.prop(effector_settings, "surface_distance", text="Surface Thickness") - col = flow.column(align=True) - col.prop(fluid, "velocity_strength", text="Velocity Strength") - col.prop(fluid, "velocity_radius", text="Radius") + 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") -class PHYSICS_PT_fluid_settings(PhysicButtonsPanel, Panel): - bl_label = "Settings" - bl_parent_id = "PHYSICS_PT_fluid" +class PHYSICS_PT_borders(PhysicButtonsPanel, Panel): + bl_label = "Border Collisions" + bl_parent_id = 'PHYSICS_PT_settings' COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): + if not PhysicButtonsPanel.poll_fluid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + md = context.fluid - fluid = md.settings + domain = md.domain_settings + + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_baked_data + + 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_data + + col = flow.column() + col.prop(domain, "use_collision_border_front", text="Front") + col = flow.column() + col.prop(domain, "use_collision_border_back", text="Back") + col = flow.column() + col.prop(domain, "use_collision_border_right", text="Right") + col = flow.column() + col.prop(domain, "use_collision_border_left", text="Left") + col = flow.column() + col.prop(domain, "use_collision_border_top", text="Top") + col = flow.column() + col.prop(domain, "use_collision_border_bottom", text="Bottom") + - if not PhysicButtonsPanel.poll_fluid_settings(context): +class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel): + bl_label = "Smoke" + bl_parent_id = 'PHYSICS_PT_settings' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_gas_domain(context): return False - return fluid.type in {'DOMAIN', 'FLUID', 'OBSTACLE', 'PARTICLE'} and (context.engine in cls.COMPAT_ENGINES) + + return (context.engine in cls.COMPAT_ENGINES) def draw(self, context): layout = self.layout layout.use_property_split = True md = context.fluid - fluid = md.settings + domain = md.domain_settings - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_baked_data - if fluid.type not in {'NONE', 'DOMAIN', 'PARTICLE', 'FLUID', 'OBSTACLE'}: - flow.active = fluid.use + 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_data - if fluid.type == 'DOMAIN': - col = flow.column() + col = flow.column() + col.prop(domain, "alpha") + col.prop(domain, "beta", text="Temperature Diff.") + col = flow.column() + col.prop(domain, "vorticity") - if bpy.app.build_options.openmp: - col.prop(fluid, "threads", text="Simulation Threads") - col.separator() - col.prop(fluid, "resolution", text="Final Resolution") - col.prop(fluid, "preview_resolution", text="Preview") +class PHYSICS_PT_smoke_dissolve(PhysicButtonsPanel, Panel): + bl_label = "Dissolve" + bl_parent_id = 'PHYSICS_PT_smoke' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - col.separator() + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_gas_domain(context): + return False - col = flow.column() - col.prop(fluid, "render_display_mode", text="Render Display") - col.prop(fluid, "viewport_display_mode", text="Viewport") + return (context.engine in cls.COMPAT_ENGINES) - col.separator() + def draw_header(self, context): + md = context.fluid + domain = md.domain_settings - col = flow.column() - sub = col.column(align=True) - sub.prop(fluid, "start_time", text="Time Start") - sub.prop(fluid, "end_time", text="End") - col.prop(fluid, "simulation_rate", text="Speed") + self.layout.prop(domain, "use_dissolve_smoke", text="") - col = flow.column() - col.prop(fluid, "use_speed_vectors") - col.prop(fluid, "use_reverse_frames") - col.prop(fluid, "frame_offset", text="Offset") + def draw(self, context): + layout = self.layout + layout.use_property_split = True - elif fluid.type == 'FLUID': - col = flow.column() - col.prop(fluid, "volume_initialization", text="Volume Initialization") - col.prop(fluid, "use_animated_mesh") + md = context.fluid + domain = md.domain_settings - col = flow.column() - col.prop(fluid, "initial_velocity", text="Initial Velocity") + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_baked_data - elif fluid.type == 'OBSTACLE': - col = flow.column() - col.prop(fluid, "volume_initialization", text="Volume Initialization") - col.prop(fluid, "use_animated_mesh") + 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_data - col = flow.column() - subcol = col.column() - subcol.enabled = not fluid.use_animated_mesh - subcol.prop(fluid, "slip_type", text="Slip Type") + layout.active = domain.use_dissolve_smoke - if fluid.slip_type == 'PARTIALSLIP': - subcol.prop(fluid, "partial_slip_factor", text="Amount", slider=True) + col = flow.column() + col.prop(domain, "dissolve_speed", text="Time") - col.prop(fluid, "impact_factor", text="Impact Factor") + col = flow.column() + col.prop(domain, "use_dissolve_smoke_log", text="Slow") - elif fluid.type == 'PARTICLE': - col = flow.column() - col.prop(fluid, "particle_influence", text="Influence Size") - col.prop(fluid, "alpha_influence", text="Alpha") - col = flow.column() - col.prop(fluid, "use_drops") - col.prop(fluid, "use_floats") - col.prop(fluid, "show_tracer") +class PHYSICS_PT_fire(PhysicButtonsPanel, Panel): + bl_label = "Fire" + bl_parent_id = 'PHYSICS_PT_settings' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_gas_domain(context): + return False -class PHYSICS_PT_fluid_particle_cache(PhysicButtonsPanel, Panel): - bl_label = "Cache" - bl_parent_id = "PHYSICS_PT_fluid" + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + md = context.fluid + domain = md.domain_settings + + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_baked_data + + 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_data + + col = flow.column() + col.prop(domain, "burning_rate", text="Reaction Speed") + col = flow.column() + col.prop(domain, "flame_smoke", text="Flame Smoke") + col = flow.column() + col.prop(domain, "flame_vorticity", text="Flame Vorticity") + col = flow.column() + col.prop(domain, "flame_ignition", text="Temperature Ignition") + col = flow.column() + col.prop(domain, "flame_max_temp", text="Maximum Temperature") + col = flow.column() + col.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'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_liquid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + md = context.fluid.domain_settings + self.layout.prop(md, "use_flip_particles", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + md = context.fluid + domain = md.domain_settings + + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_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") + + +class PHYSICS_PT_flow_source(PhysicButtonsPanel, Panel): + bl_label = "Flow Source" + bl_parent_id = 'PHYSICS_PT_settings' + bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): - if not PhysicButtonsPanel.poll_fluid_settings(context): + if not PhysicButtonsPanel.poll_fluid_flow(context): return False + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + ob = context.object + flow = context.fluid.flow_settings + + col = layout.column() + col.prop(flow, "flow_source", expand=False, text="Flow Source") + if flow.flow_source == 'PARTICLES': + col.prop_search(flow, "particle_system", ob, "particle_systems", text="Particle System") + + grid = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + + col = grid.column() + if flow.flow_source == 'MESH': + col.prop(flow, "use_plane_init", text="Is Planar") + col.prop(flow, "surface_distance", text="Surface Thickness") + if flow.flow_type in {'SMOKE', 'BOTH', 'FIRE'}: + col = grid.column() + col.prop(flow, "volume_density", text="Volume Density") + + if flow.flow_source == 'PARTICLES': + col.prop(flow, "use_particle_size", text="Set Size") + sub = col.column() + sub.active = flow.use_particle_size + sub.prop(flow, "particle_size") + + +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'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_fluid_flow(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): md = context.fluid - return md and md.settings and (md.settings.type == 'PARTICLE') and (context.engine in cls.COMPAT_ENGINES) + flow_smoke = md.flow_settings + + self.layout.prop(flow_smoke, "use_initial_velocity", text="") def draw(self, context): layout = self.layout + layout.use_property_split = True + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) + + md = context.fluid + flow_smoke = md.flow_settings + + flow.active = flow_smoke.use_initial_velocity + + col = flow.column() + col.prop(flow_smoke, "velocity_factor") + + if flow_smoke.flow_source == 'MESH': + col.prop(flow_smoke, "velocity_normal") + # col.prop(flow_smoke, "velocity_random") + col = flow.column() + col.prop(flow_smoke, "velocity_coord") + + +class PHYSICS_PT_flow_texture(PhysicButtonsPanel, Panel): + bl_label = "Texture" + bl_parent_id = 'PHYSICS_PT_settings' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_fluid_flow(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): md = context.fluid - fluid = md.settings + flow_smoke = md.flow_settings + + self.layout.prop(flow_smoke, "use_texture", text="") + + def draw(self, context): + 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) + + ob = context.object + flow_smoke = context.fluid.flow_settings + + sub = flow.column() + sub.active = flow_smoke.use_texture + sub.prop(flow_smoke, "noise_texture") + sub.prop(flow_smoke, "texture_map_type", text="Mapping") + + col = flow.column() + sub = col.column() + sub.active = flow_smoke.use_texture + + if flow_smoke.texture_map_type == 'UV': + sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers") + + if flow_smoke.texture_map_type == 'AUTO': + sub.prop(flow_smoke, "texture_size") + + sub.prop(flow_smoke, "texture_offset") + + +class PHYSICS_PT_adaptive_domain(PhysicButtonsPanel, Panel): + bl_label = "Adaptive Domain" + bl_parent_id = 'PHYSICS_PT_fluid' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_gas_domain(context): + return False + + 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 - layout.prop(fluid, "filepath", text="") + self.layout.enabled = not is_baking_any and not has_baked_any + self.layout.prop(md, "use_adaptive_domain", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + domain = context.fluid.domain_settings + layout.active = domain.use_adaptive_domain + + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) + flow.enabled = not is_baking_any and not has_baked_any + + col = flow.column() + col.prop(domain, "additional_res", text="Add Resolution") + col.prop(domain, "adapt_margin") + + col.separator() + + col = flow.column() + col.prop(domain, "adapt_threshold", text="Threshold") -class PHYSICS_PT_domain_bake(PhysicButtonsPanel, Panel): - bl_label = "Bake" +class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): + bl_label = "Noise" bl_parent_id = 'PHYSICS_PT_fluid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): - if not PhysicButtonsPanel.poll_fluid_domain(context): + if not PhysicButtonsPanel.poll_gas_domain(context): return False 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 + self.layout.enabled = not is_baking_any + self.layout.prop(md, "use_noise", text="") + def draw(self, context): layout = self.layout + layout.use_property_split = True - md = context.fluid - fluid = md.settings + domain = context.fluid.domain_settings - row = layout.row(align=True) - row.alignment = 'RIGHT' - row.label(text="Cache Path") + # 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.prop(fluid, "filepath", text="") + is_baking_any = domain.is_cache_baking_any + has_baked_noise = domain.has_cache_baked_noise - # odd formatting here so translation script can extract string - layout.operator( - "fluid.bake", text=iface_("Bake (Req. Memory: %s)") % fluid.memory_estimate, - translate=False, icon='MOD_FLUIDSIM' - ) + 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_noise + + col = flow.column() + col.prop(domain, "noise_scale", text="Upres Factor") + # TODO (sebbas): Mantaflow only supports wavelet noise. Maybe get rid of noise type field. + col.prop(domain, "noise_type", text="Noise Method") + + col = flow.column() + col.prop(domain, "noise_strength", text="Strength") + col.prop(domain, "noise_pos_scale", text="Scale") + col.prop(domain, "noise_time_anim", text="Time") + if domain.cache_type == 'MODULAR': + col.separator() -class PHYSICS_PT_domain_gravity(PhysicButtonsPanel, Panel): - bl_label = "World" + split = layout.split() + split.enabled = domain.has_cache_baked_data + + 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: + col = split.column() + col.operator("fluid.bake_noise", text="Resume") + col = split.column() + col.operator("fluid.free_noise", text="Free") + elif not domain.has_cache_baked_noise and domain.is_cache_baking_noise: + split.enabled = False + split.operator("fluid.pause_bake", text="Baking Noise - ESC to pause") + elif not domain.has_cache_baked_noise and not domain.is_cache_baking_noise: + split.operator("fluid.bake_noise", text="Bake Noise") + else: + split.operator("fluid.free_noise", text="Free Noise") + + +class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): + bl_label = "Mesh" bl_parent_id = 'PHYSICS_PT_fluid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): - if not PhysicButtonsPanel.poll_fluid_domain(context): + if not PhysicButtonsPanel.poll_liquid_domain(context): return False 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 + self.layout.enabled = not is_baking_any + self.layout.prop(md, "use_mesh", text="") + def draw(self, context): layout = self.layout layout.use_property_split = True - fluid = context.fluid.settings - scene = context.scene + domain = context.fluid.domain_settings - col = layout.column() + # Deactivate UI if guides are enabled but not baked yet. + layout.active = domain.use_mesh and not self.check_domain_has_unbaked_guide(domain) - use_gravity = scene.use_gravity - use_units = scene.unit_settings.system != 'NONE' + is_baking_any = domain.is_cache_baking_any + has_baked_mesh = domain.has_cache_baked_mesh - if use_gravity or use_units: - s_gravity = " Gravity" if use_gravity else "" - s_units = " Units" if use_units else "" - s_and = " and " if use_gravity and use_units else "" - warn = f"Using {s_gravity}{s_and}{s_units} from Scene" + 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_mesh - sub = col.column() - sub.alignment = 'RIGHT' - sub.label(text=warn) + col = flow.column() - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) + col.prop(domain, "mesh_scale", text="Upres Factor") + col.prop(domain, "mesh_particle_radius", text="Particle Radius") col = flow.column() - sub = col.column() - sub.enabled = not use_gravity - sub.prop(fluid, "gravity", text="Gravity") + col.prop(domain, "use_speed_vectors", text="Use Speed Vectors") - sub = col.column() - sub.enabled = not use_units - sub.prop(fluid, "simulation_scale", text="Scene Size Meters" if use_units else "World Size Meters") + col.separator() + col.prop(domain, "mesh_generator", text="Mesh Generator") + + if domain.mesh_generator in {'IMPROVED'}: + col = flow.column(align=True) + col.prop(domain, "mesh_smoothen_pos", text="Smoothing Positive") + col.prop(domain, "mesh_smoothen_neg", text="Negative") + + col = flow.column(align=True) + col.prop(domain, "mesh_concave_upper", text="Concavity Upper") + col.prop(domain, "mesh_concave_lower", text="Lower") + + # TODO (sebbas): for now just interpolate any upres grids, ie not sampling highres grids + #col.prop(domain, "highres_sampling", text="Flow Sampling:") + + if domain.cache_type == 'MODULAR': + col.separator() + + split = layout.split() + split.enabled = domain.has_cache_baked_data + + 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: + col = split.column() + col.operator("fluid.bake_mesh", text="Resume") + col = split.column() + col.operator("fluid.free_mesh", text="Free") + elif not domain.has_cache_baked_mesh and domain.is_cache_baking_mesh: + split.enabled = False + split.operator("fluid.pause_bake", text="Baking Mesh - ESC to pause") + elif not domain.has_cache_baked_mesh and not domain.is_cache_baking_mesh: + split.operator("fluid.bake_mesh", text="Bake Mesh") + else: + split.operator("fluid.free_mesh", text="Free Mesh") + + +class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): + bl_label = "Particles" + bl_parent_id = 'PHYSICS_PT_fluid' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_liquid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + 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) + + 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 + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + flow.enabled = not is_baking_any + + 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") + + 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 + + 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.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.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.separator() + + col = flow.column(align=True) + col.prop(domain, "sndparticle_potential_radius", text="Potential Radius") + col.prop(domain, "sndparticle_update_radius", text="Particle Update Radius") + 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.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.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.separator() col = flow.column() - col.prop(fluid, "grid_levels", text="Optimization", slider=True) - col.prop(fluid, "compressibility", slider=True) + col.prop(domain, "sndparticle_boundary", text="Particles in Boundary:") + if domain.cache_type == 'MODULAR': + col.separator() -class PHYSICS_PT_domain_viscosity(PhysicButtonsPanel, Panel): - bl_label = "Viscosity" + split = layout.split() + split.enabled = ( + domain.has_cache_baked_data and + (domain.use_spray_particles or + domain.use_bubble_particles or + domain.use_foam_particles or + domain.use_tracer_particles) + ) + + bake_incomplete = (domain.cache_frame_pause_particles < domain.cache_frame_end) + if domain.has_cache_baked_particles and not domain.is_cache_baking_particles and bake_incomplete: + col = split.column() + col.operator("fluid.bake_particles", text="Resume") + col = split.column() + col.operator("fluid.free_particles", text="Free") + elif not domain.has_cache_baked_particles and domain.is_cache_baking_particles: + split.enabled = False + split.operator("fluid.pause_bake", text="Baking Particles - ESC to pause") + elif not domain.has_cache_baked_particles and not domain.is_cache_baking_particles: + split.operator("fluid.bake_particles", text="Bake Particles") + else: + split.operator("fluid.free_particles", text="Free Particles") + + +class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): + bl_label = "Diffusion" bl_parent_id = 'PHYSICS_PT_fluid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): + # Fluid diffusion only enabled for liquids (surface tension and viscosity not relevant for smoke) + if not PhysicButtonsPanel.poll_liquid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + 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) + + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + has_baked_data = domain.has_cache_baked_data + + 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") + + +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'} + + @classmethod + def poll(cls, context): if not PhysicButtonsPanel.poll_fluid_domain(context): return False return (context.engine in cls.COMPAT_ENGINES) - def draw_header_preset(self, _context): - FLUID_PT_presets.draw_panel_header(self.layout) + 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_guide", text="") def draw(self, context): layout = self.layout layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - fluid = context.fluid.settings + domain = context.fluid.domain_settings + + layout.active = domain.use_guide + + is_baking_any = domain.is_cache_baking_any + has_baked_data = domain.has_cache_baked_data + + 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_data col = flow.column() - col.prop(fluid, "viscosity_base", text="Base") + col.prop(domain, "guide_alpha", text="Weight") + col.prop(domain, "guide_beta", text="Size") + col.prop(domain, "guide_vel_factor", text="Velocity Factor") col = flow.column() - col.prop(fluid, "viscosity_exponent", text="Exponent", slider=True) + col.prop(domain, "guide_source", text="Velocity Source") + if domain.guide_source == 'DOMAIN': + col.prop(domain, "guide_parent", text="Guide Parent") + if domain.cache_type == 'MODULAR': + col.separator() -class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, Panel): - bl_label = "Boundary" + if domain.guide_source == 'EFFECTOR': + split = layout.split() + bake_incomplete = (domain.cache_frame_pause_guide < domain.cache_frame_end) + if domain.has_cache_baked_guide and not domain.is_cache_baking_guide and bake_incomplete: + col = split.column() + col.operator("fluid.bake_guides", text="Resume") + 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") + elif not domain.has_cache_baked_guide and not domain.is_cache_baking_guide: + split.operator("fluid.bake_guides", text="Bake Guides") + else: + split.operator("fluid.free_guides", text="Free Guides") + + +class PHYSICS_PT_collections(PhysicButtonsPanel, Panel): + bl_label = "Collections" bl_parent_id = 'PHYSICS_PT_fluid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -403,26 +1014,126 @@ class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, Panel): def draw(self, context): layout = self.layout layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - fluid = context.fluid.settings + domain = context.fluid.domain_settings + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + + col = flow.column() + col.prop(domain, "fluid_group", text="Flow") + + # col.prop(domain, "effector_group", text="Forces") + col.prop(domain, "effector_group", text="Effector") + + +class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): + bl_label = "Cache" + bl_parent_id = 'PHYSICS_PT_fluid' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_fluid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + md = context.fluid + domain = context.fluid.domain_settings + + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + + col = layout.column() + col.prop(domain, "cache_directory", text="") + col.enabled = not is_baking_any + + 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.prop(fluid, "slip_type", text="Type") + col.prop(domain, "cache_type", expand=False) + col.enabled = not is_baking_any + col = flow.column(align=True) col.separator() - if fluid.slip_type == 'PARTIALSLIP': - col.prop(fluid, "partial_slip_factor", slider=True, text="Amount") + col.prop(domain, "cache_frame_start", text="Frame Start") + col.prop(domain, "cache_frame_end", text="End") + col.enabled = not is_baking_any + + col.separator() col = flow.column() - col.prop(fluid, "surface_smooth", text="Surface Smoothing") - col.prop(fluid, "surface_subdivisions", text="Subdivisions") - col.prop(fluid, "use_surface_noobs") + col.enabled = not is_baking_any and not has_baked_any + col.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") -class PHYSICS_PT_domain_particles(PhysicButtonsPanel, Panel): - bl_label = "Particles" + 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") + + if domain.use_mesh: + col.prop(domain, "cache_mesh_format", text="Mesh File Format") + + if domain.cache_type == 'FINAL': + + col.separator() + split = layout.split() + + 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: + col = split.column() + col.operator("fluid.bake_all", text="Resume") + col = split.column() + col.operator("fluid.free_all", text="Free") + elif domain.is_cache_baking_data and not domain.has_cache_baked_data: + split.enabled = False + split.operator("fluid.pause_bake", text="Baking All - ESC to pause") + elif not domain.has_cache_baked_data and not domain.is_cache_baking_data: + split.operator("fluid.bake_all", text="Bake All") + else: + split.operator("fluid.free_all", text="Free All") + + +class PHYSICS_PT_export(PhysicButtonsPanel, Panel): + bl_label = "Advanced" + bl_parent_id = 'PHYSICS_PT_cache' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + if not PhysicButtonsPanel.poll_fluid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + domain = context.fluid.domain_settings + + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + + 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 + + col = flow.column() + col.prop(domain, "export_manta_script", text="Export Mantaflow Script") + + +class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel): + bl_label = "Field Weights" bl_parent_id = 'PHYSICS_PT_fluid' bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -435,32 +1146,141 @@ class PHYSICS_PT_domain_particles(PhysicButtonsPanel, Panel): return (context.engine in cls.COMPAT_ENGINES) def draw(self, context): + domain = context.fluid.domain_settings + effector_weights_ui(self, domain.effector_weights, 'SMOKE') + + +class PHYSICS_PT_viewport_display(PhysicButtonsPanel, Panel): + bl_label = "Viewport Display" + bl_parent_id = 'PHYSICS_PT_fluid' + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return (PhysicButtonsPanel.poll_gas_domain(context)) + + def draw(self, context): layout = self.layout layout.use_property_split = True flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - fluid = context.fluid.settings + domain = context.fluid.domain_settings col = flow.column() - col.prop(fluid, "tracer_particles", text="Tracer") + 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() - col.prop(fluid, "generate_particles", text="Generate") + sub = col.column() + sub.enabled = not do_full_slicing and do_axis_slicing + sub.prop(domain, "slice_axis") + sub.prop(domain, "slice_depth") + + row = col.row() + row.enabled = do_full_slicing or not do_axis_slicing + row.prop(domain, "slice_per_voxel") + + col.prop(domain, "display_interpolation") + + +class PHYSICS_PT_viewport_display_color(PhysicButtonsPanel, Panel): + bl_label = "Color Mapping" + bl_parent_id = 'PHYSICS_PT_viewport_display' + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return (PhysicButtonsPanel.poll_gas_domain(context)) + + def draw_header(self, context): + md = context.fluid.domain_settings + + self.layout.prop(md, "use_color_ramp", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + domain = context.fluid.domain_settings + col = layout.column() + col.enabled = domain.use_color_ramp + + col.prop(domain, "coba_field") + + col.use_property_split = False + + col = col.column() + col.template_color_ramp(domain, "color_ramp", expand=True) + + +class PHYSICS_PT_viewport_display_debug(PhysicButtonsPanel, Panel): + bl_label = "Debug Velocity" + bl_parent_id = 'PHYSICS_PT_viewport_display' + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + return (PhysicButtonsPanel.poll_gas_domain(context)) + + def draw_header(self, context): + md = context.fluid.domain_settings + + self.layout.prop(md, "show_velocity", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + 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.enabled = domain.show_velocity + col.prop(domain, "vector_display_type", text="Display As") + col.prop(domain, "vector_scale") classes = ( - FLUID_PT_presets, + FLUID_MT_presets, PHYSICS_PT_fluid, - PHYSICS_PT_fluid_settings, - PHYSICS_PT_fluid_flow, - PHYSICS_PT_fluid_particle_cache, - PHYSICS_PT_domain_bake, - PHYSICS_PT_domain_boundary, - PHYSICS_PT_domain_particles, - PHYSICS_PT_domain_gravity, - PHYSICS_PT_domain_viscosity, + PHYSICS_PT_settings, + PHYSICS_PT_borders, + PHYSICS_PT_smoke, + PHYSICS_PT_smoke_dissolve, + 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_guide, + PHYSICS_PT_collections, + PHYSICS_PT_cache, + PHYSICS_PT_export, + PHYSICS_PT_field_weights, + PHYSICS_PT_viewport_display, + PHYSICS_PT_viewport_display_color, + PHYSICS_PT_viewport_display_debug, ) + if __name__ == "__main__": # only for live edit. from bpy.utils import register_class for cls in classes: diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py deleted file mode 100644 index 057e7ddf211..00000000000 --- a/release/scripts/startup/bl_ui/properties_physics_smoke.py +++ /dev/null @@ -1,692 +0,0 @@ -# ##### 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, -) -from bl_ui.properties_physics_common import ( - point_cache_ui, - effector_weights_ui, -) - - -class PhysicButtonsPanel: - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "physics" - - @staticmethod - def poll_smoke(context): - ob = context.object - if not ((ob and ob.type == 'MESH') and (context.smoke)): - return False - - md = context.smoke - return md and (context.smoke.smoke_type != 'NONE') and (bpy.app.build_options.mod_smoke) - - @staticmethod - def poll_smoke_domain(context): - if not PhysicButtonsPanel.poll_smoke(context): - return False - - md = context.smoke - return md and (md.smoke_type == 'DOMAIN') - - -class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel): - bl_label = "Smoke" - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - ob = context.object - return (ob and ob.type == 'MESH') and (context.engine in cls.COMPAT_ENGINES) and (context.smoke) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - if not bpy.app.build_options.mod_smoke: - col = layout.column(align=True) - col.alignment = 'RIGHT' - col.label(text="Built without Smoke modifier") - return - - md = context.smoke - - layout.prop(md, "smoke_type") - - -class PHYSICS_PT_smoke_settings(PhysicButtonsPanel, Panel): - bl_label = "Settings" - bl_parent_id = 'PHYSICS_PT_smoke' - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - md = context.smoke - ob = context.object - - if md.smoke_type == 'DOMAIN': - domain = md.domain_settings - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - - col = flow.column() - col.enabled = (not domain.point_cache.is_baked) - col.prop(domain, "resolution_max", text="Resolution Divisions") - col.prop(domain, "time_scale", text="Time Scale") - - col.separator() - - col = flow.column() - sub = col.row() - sub.enabled = (not domain.point_cache.is_baked) - sub.prop(domain, "collision_extents", text="Border Collisions") - - # This can be tweaked after baking, for render. - col.prop(domain, "clipping", text="Empty Space") - - elif md.smoke_type == 'FLOW': - flow_smoke = md.flow_settings - - col = layout.column() - col.prop(flow_smoke, "smoke_flow_type", expand=False) - - col.separator() - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) - col = flow.column() - - if flow_smoke.smoke_flow_type != 'OUTFLOW': - col.prop(flow_smoke, "smoke_flow_source", expand=False, text="Flow Source") - - if flow_smoke.smoke_flow_source == 'PARTICLES': - col.prop_search( - flow_smoke, "particle_system", ob, "particle_systems", - text="Particle System" - ) - else: - col.prop(flow_smoke, "surface_distance") - col.prop(flow_smoke, "volume_density") - - col = flow.column() - col.prop(flow_smoke, "use_absolute") - - if flow_smoke.smoke_flow_type in {'SMOKE', 'BOTH'}: - col.prop(flow_smoke, "density") - col.prop(flow_smoke, "temperature", text="Temperature Diff.") - - col.separator() - - col = flow.column() - col.prop(flow_smoke, "smoke_color") - - if flow_smoke.smoke_flow_type in {'FIRE', 'BOTH'}: - col.prop(flow_smoke, "fuel_amount") - - col.prop(flow_smoke, "subframes", text="Sampling Subframes") - - col.separator() - - col.prop_search(flow_smoke, "density_vertex_group", ob, "vertex_groups", text="Vertex Group") - - elif md.smoke_type == 'COLLISION': - coll = md.coll_settings - - col = layout.column() - col.prop(coll, "collision_type") - - -class PHYSICS_PT_smoke_settings_initial_velocity(PhysicButtonsPanel, Panel): - bl_label = "Initial Velocity" - bl_parent_id = 'PHYSICS_PT_smoke_settings' - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke(context): - return False - - md = context.smoke - return (md and (md.smoke_type == 'FLOW') - and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW' - and context.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - md = context.smoke - flow_smoke = md.flow_settings - - self.layout.prop(flow_smoke, "use_initial_velocity", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) - - md = context.smoke - flow_smoke = md.flow_settings - - flow.active = flow_smoke.use_initial_velocity - - col = flow.column(align=True) - col.prop(flow_smoke, "velocity_factor") - - if flow_smoke.smoke_flow_source == 'MESH': - col = flow.column() - col.prop(flow_smoke, "velocity_normal") - # sub.prop(flow_smoke, "velocity_random") - - -class PHYSICS_PT_smoke_settings_particle_size(PhysicButtonsPanel, Panel): - bl_label = "Particle Size" - bl_parent_id = 'PHYSICS_PT_smoke_settings' - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke(context): - return False - - md = context.smoke - return (md and (md.smoke_type == 'FLOW') - and md.flow_settings and md.flow_settings.smoke_flow_type != 'OUTFLOW' - and md.flow_settings.smoke_flow_source == 'PARTICLES' - and context.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - md = context.smoke - flow_smoke = md.flow_settings - - self.layout.prop(flow_smoke, "use_particle_size", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - md = context.smoke - flow_smoke = md.flow_settings - - layout.active = flow_smoke.use_particle_size - - layout.prop(flow_smoke, "particle_size") - - -class PHYSICS_PT_smoke_behavior(PhysicButtonsPanel, Panel): - bl_label = "Behavior" - bl_parent_id = 'PHYSICS_PT_smoke_settings' - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - md = context.smoke - domain = md.domain_settings - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - flow.enabled = (not domain.point_cache.is_baked) - - col = flow.column() - col.prop(domain, "alpha") - col.prop(domain, "beta", text="Temperature Diff.") - col = flow.column() - col.prop(domain, "vorticity") - - -class PHYSICS_PT_smoke_behavior_dissolve(PhysicButtonsPanel, Panel): - bl_label = "Dissolve" - bl_parent_id = 'PHYSICS_PT_smoke_behavior' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - md = context.smoke - domain = md.domain_settings - - self.layout.prop(domain, "use_dissolve_smoke", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - md = context.smoke - domain = md.domain_settings - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - flow.enabled = (not domain.point_cache.is_baked) - - layout.active = domain.use_dissolve_smoke - - col = flow.column() - col.prop(domain, "dissolve_speed", text="Time") - - col = flow.column() - col.prop(domain, "use_dissolve_smoke_log", text="Slow") - - -class PHYSICS_PT_smoke_flow_texture(PhysicButtonsPanel, Panel): - bl_label = "Texture" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke(context): - return False - - md = context.smoke - return (md and (md.smoke_type == 'FLOW') - and (md.flow_settings.smoke_flow_source == 'MESH') - and (context.engine in cls.COMPAT_ENGINES)) - - def draw_header(self, context): - md = context.smoke - flow_smoke = md.flow_settings - - self.layout.prop(flow_smoke, "use_texture", text="") - - def draw(self, context): - 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) - - ob = context.object - flow_smoke = context.smoke.flow_settings - - sub = flow.column() - sub.active = flow_smoke.use_texture - sub.prop(flow_smoke, "noise_texture") - sub.prop(flow_smoke, "texture_map_type", text="Mapping") - - col = flow.column() - sub = col.column() - sub.active = flow_smoke.use_texture - - if flow_smoke.texture_map_type == 'UV': - sub.prop_search(flow_smoke, "uv_layer", ob.data, "uv_layers") - - if flow_smoke.texture_map_type == 'AUTO': - sub.prop(flow_smoke, "texture_size") - - sub.prop(flow_smoke, "texture_offset") - - -class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel): - bl_label = "Flames" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - domain = context.smoke.domain_settings - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - flow.enabled = (not domain.point_cache.is_baked) - - col = flow.column() - col.prop(domain, "burning_rate", text="Reaction Speed") - col.prop(domain, "flame_smoke") - col.prop(domain, "flame_vorticity") - - col.separator() - - col = flow.column(align=True) - col.prop(domain, "flame_ignition", text="Temperature Ignition") - col.prop(domain, "flame_max_temp") - - col.separator() - - sub = col.column() - sub.prop(domain, "flame_smoke_color") - - -class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel): - bl_label = "Adaptive Domain" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - md = context.smoke.domain_settings - - self.layout.prop(md, "use_adaptive_domain", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - domain = context.smoke.domain_settings - layout.active = domain.use_adaptive_domain - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - flow.enabled = (not domain.point_cache.is_baked) - - col = flow.column() - col.prop(domain, "additional_res", text="Add Resolution") - col.prop(domain, "adapt_margin") - - col.separator() - - col = flow.column() - col.prop(domain, "adapt_threshold", text="Threshold") - - -class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel): - bl_label = "High Resolution" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - md = context.smoke.domain_settings - - self.layout.prop(md, "use_high_resolution", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - md = context.smoke.domain_settings - layout.active = md.use_high_resolution - - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - - col = flow.column() - col.enabled = not md.point_cache.is_baked - col.prop(md, "amplify", text="Resolution Divisions") - col.prop(md, "highres_sampling", text="Flow Sampling") - - col.separator() - - col = flow.column() - col.enabled = not md.point_cache.is_baked - col.prop(md, "noise_type", text="Noise Method") - col.prop(md, "strength") - - layout.prop(md, "show_high_resolution") - - -class PHYSICS_PT_smoke_collections(PhysicButtonsPanel, Panel): - bl_label = "Collections" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - domain = context.smoke.domain_settings - - col = layout.column() - col.prop(domain, "fluid_collection", text="Flow") - - # col = layout.column() - # col.prop(domain, "effector_collection", text="Effector") - col.prop(domain, "collision_collection", text="Collision") - - -class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel): - bl_label = "Cache" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - - domain = context.smoke.domain_settings - cache_file_format = domain.cache_file_format - - col = flow.column() - col.prop(domain, "cache_file_format") - - if cache_file_format == 'POINTCACHE': - col = flow.column() - col.prop(domain, "point_cache_compress_type", text="Compression") - col.separator() - - elif cache_file_format == 'OPENVDB': - if not bpy.app.build_options.openvdb: - row = layout.row(align=True) - row.alignment = 'RIGHT' - row.label(text="Built without OpenVDB support") - return - - col = flow.column() - col.prop(domain, "openvdb_cache_compress_type", text="Compression") - col.prop(domain, "data_depth", text="Data Depth") - col.separator() - - cache = domain.point_cache - point_cache_ui(self, cache, (cache.is_baked is False), 'SMOKE') - - -class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel): - bl_label = "Field Weights" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - @classmethod - def poll(cls, context): - if not PhysicButtonsPanel.poll_smoke_domain(context): - return False - - return (context.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - domain = context.smoke.domain_settings - effector_weights_ui(self, domain.effector_weights, 'SMOKE') - - -class PHYSICS_PT_smoke_viewport_display(PhysicButtonsPanel, Panel): - bl_label = "Viewport Display" - bl_parent_id = 'PHYSICS_PT_smoke' - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return (PhysicButtonsPanel.poll_smoke_domain(context)) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - - domain = context.smoke.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() - sub = col.column() - sub.enabled = not do_full_slicing and do_axis_slicing - sub.prop(domain, "slice_axis") - sub.prop(domain, "slice_depth") - - row = col.row() - row.enabled = do_full_slicing or not do_axis_slicing - row.prop(domain, "slice_per_voxel") - - col.prop(domain, "display_interpolation") - - -class PHYSICS_PT_smoke_viewport_display_color(PhysicButtonsPanel, Panel): - bl_label = "Color Mapping" - bl_parent_id = 'PHYSICS_PT_smoke_viewport_display' - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return (PhysicButtonsPanel.poll_smoke_domain(context)) - - def draw_header(self, context): - md = context.smoke.domain_settings - - self.layout.prop(md, "use_color_ramp", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - domain = context.smoke.domain_settings - col = layout.column() - col.enabled = domain.use_color_ramp - - col.prop(domain, "coba_field") - - col.use_property_split = False - - col = col.column() - col.template_color_ramp(domain, "color_ramp", expand=True) - - -class PHYSICS_PT_smoke_viewport_display_debug(PhysicButtonsPanel, Panel): - bl_label = "Debug Velocity" - bl_parent_id = 'PHYSICS_PT_smoke_viewport_display' - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return (PhysicButtonsPanel.poll_smoke_domain(context)) - - def draw_header(self, context): - md = context.smoke.domain_settings - - self.layout.prop(md, "show_velocity", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) - - domain = context.smoke.domain_settings - - col = flow.column() - col.enabled = domain.show_velocity - col.prop(domain, "vector_display_type", text="Display As") - col.prop(domain, "vector_scale") - - -classes = ( - PHYSICS_PT_smoke, - PHYSICS_PT_smoke_settings, - PHYSICS_PT_smoke_settings_initial_velocity, - PHYSICS_PT_smoke_settings_particle_size, - PHYSICS_PT_smoke_behavior, - PHYSICS_PT_smoke_behavior_dissolve, - PHYSICS_PT_smoke_adaptive_domain, - PHYSICS_PT_smoke_cache, - PHYSICS_PT_smoke_field_weights, - PHYSICS_PT_smoke_fire, - PHYSICS_PT_smoke_flow_texture, - PHYSICS_PT_smoke_collections, - PHYSICS_PT_smoke_highres, - PHYSICS_PT_smoke_viewport_display, - PHYSICS_PT_smoke_viewport_display_color, - PHYSICS_PT_smoke_viewport_display_debug, -) - - -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/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 9afdcdff9a5..f93629a4f03 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -1445,15 +1445,6 @@ class CLIP_MT_select_grouped(Menu): layout.operator_enum("clip.select_grouped", "group") -class CLIP_MT_mask_handle_type_menu(Menu): - bl_label = "Set Handle Type" - - def draw(self, _context): - layout = self.layout - - layout.operator_enum("mask.handle_type_set", "type") - - class CLIP_MT_tracking_context_menu(Menu): bl_label = "Context Menu" @@ -1507,30 +1498,8 @@ class CLIP_MT_tracking_context_menu(Menu): layout.operator("clip.delete_track") elif mode == 'MASK': - - layout.menu("CLIP_MT_mask_handle_type_menu") - layout.operator("mask.switch_direction") - layout.operator("mask.cyclic_toggle") - - layout.separator() - - layout.operator("mask.copy_splines", icon='COPYDOWN') - layout.operator("mask.paste_splines", icon='PASTEDOWN') - - layout.separator() - - layout.operator("mask.shape_key_rekey", text="Re-key Shape Points") - layout.operator("mask.feather_weight_clear") - layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation") - - layout.separator() - - layout.operator("mask.parent_set") - layout.operator("mask.parent_clear") - - layout.separator() - - layout.operator("mask.delete") + from .properties_mask_common import draw_mask_context_menu + draw_mask_context_menu(layout, context) class CLIP_PT_camera_presets(PresetPanel, Panel): @@ -1801,7 +1770,6 @@ classes = ( CLIP_MT_tracking_pie, CLIP_MT_reconstruction_pie, CLIP_MT_solving_pie, - CLIP_MT_mask_handle_type_menu ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index b8f86788b4c..5a73ff094a2 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -657,6 +657,7 @@ class DOPESHEET_MT_snap_pie(Menu): pie.operator("action.snap", text="Nearest Second").type = 'NEAREST_SECOND' pie.operator("action.snap", text="Nearest Marker").type = 'NEAREST_MARKER' + class LayersDopeSheetPanel: bl_space_type = 'DOPESHEET_EDITOR' bl_region_type = 'UI' diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 543a45e85c2..8dd0eaf5445 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -162,7 +162,7 @@ def panel_poll_is_upper_region(region): class FILEBROWSER_UL_dir(UIList): - def draw_item(self, _context, layout, _data, item, icon, _active_data, active_propname, _index): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): direntry = item # space = context.space_data diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index 968492db374..2e853a287ea 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -294,7 +294,9 @@ class GRAPH_MT_key(Menu): operator_context = layout.operator_context layout.operator("graph.decimate", text="Decimate (Ratio)").mode = 'RATIO' - # Using the modal operation doesn't make sense for this variant as we do not have a modal mode for it, so just execute it. + + # Using the modal operation doesn't make sense for this variant + # as we do not have a modal mode for it, so just execute it. layout.operator_context = 'EXEC_DEFAULT' layout.operator("graph.decimate", text="Decimate (Allowed Change)").mode = 'ERROR' layout.operator_context = operator_context diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 5af9fed83f8..c6f490f9d26 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -27,12 +27,18 @@ from bpy.types import ( from bl_ui.properties_paint_common import ( UnifiedPaintPanel, brush_texture_settings, - brush_texpaint_common, - brush_texpaint_common_color, - brush_texpaint_common_gradient, - brush_texpaint_common_clone, - brush_texpaint_common_options, - brush_mask_texture_settings, + brush_basic_texpaint_settings, + brush_settings, + brush_settings_advanced, + draw_color_settings, + ClonePanel, + BrushSelectPanel, + TextureMaskPanel, + ColorPalettePanel, + StrokePanel, + SmoothStrokePanel, + FalloffPanel, + DisplayPanel, ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, @@ -44,7 +50,7 @@ from bl_ui.space_toolsystem_common import ( from bpy.app.translations import pgettext_iface as iface_ -class ImagePaintPanel(UnifiedPaintPanel): +class ImagePaintPanel: bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'UI' @@ -59,7 +65,7 @@ class BrushButtonsPanel(UnifiedPaintPanel): return tool_settings.brush -class IMAGE_PT_active_tool(ToolActivePanelHelper, Panel): +class IMAGE_PT_active_tool(Panel, ToolActivePanelHelper): bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'UI' bl_category = "Tool" @@ -181,25 +187,6 @@ class IMAGE_MT_select(Menu): layout.operator("uv.select_overlap") -class IMAGE_MT_brush(Menu): - bl_label = "Brush" - - def draw(self, context): - layout = self.layout - tool_settings = context.tool_settings - settings = tool_settings.image_paint - brush = settings.brush - - ups = context.tool_settings.unified_paint_settings - layout.prop(ups, "use_unified_size", text="Unified Size") - layout.prop(ups, "use_unified_strength", text="Unified Strength") - layout.prop(ups, "use_unified_color", text="Unified Color") - layout.separator() - - # Brush tool. - layout.prop_menu_enum(brush, "image_tool") - - class IMAGE_MT_image(Menu): bl_label = "Image" @@ -244,7 +231,7 @@ class IMAGE_MT_image(Menu): if ima and not show_render: if ima.packed_file: - if len(ima.filepath): + if ima.filepath: layout.separator() layout.operator("image.unpack", text="Unpack") else: @@ -569,15 +556,16 @@ class IMAGE_HT_tool_header(Header): if tool_mode == 'PAINT': if (tool is not None) and tool.has_datablock: - layout.popover_group( - space_type='IMAGE_EDITOR', - region_type='UI', - context=".paint_common_2d", - category="", - ) + layout.popover("IMAGE_PT_paint_settings_advanced") + layout.popover("IMAGE_PT_paint_stroke") + layout.popover("IMAGE_PT_paint_curve") + layout.popover("IMAGE_PT_tools_brush_display") + layout.popover("IMAGE_PT_tools_brush_texture") + layout.popover("IMAGE_PT_tools_mask_texture") elif tool_mode == 'UV': if (tool is not None) and tool.has_datablock: - layout.popover_group(space_type='IMAGE_EDITOR', region_type='UI', context=".uv_sculpt", category="") + layout.popover("IMAGE_PT_uv_sculpt_curve") + layout.popover("IMAGE_PT_uv_sculpt_options") def draw_mode_settings(self, context): layout = self.layout @@ -601,15 +589,26 @@ class _draw_tool_settings_context_mode: uv_sculpt = tool_settings.uv_sculpt brush = uv_sculpt.brush if brush: - from bl_ui.properties_paint_common import UnifiedPaintPanel - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="") - - row = layout.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="") + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + pressure_name="use_pressure_size", + unified_name="use_unified_size", + slider=True, + header=True + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + pressure_name="use_pressure_strength", + unified_name="use_unified_strength", + slider=True, + header=True + ) @staticmethod def PAINT(context, layout, tool): @@ -623,13 +622,6 @@ class _draw_tool_settings_context_mode: if brush is None: return - from bl_ui.properties_paint_common import ( - UnifiedPaintPanel, - brush_basic_texpaint_settings, - ) - capabilities = brush.image_paint_capabilities - if capabilities.has_color: - UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") brush_basic_texpaint_settings(layout, context, brush, compact=True) @@ -763,7 +755,6 @@ class MASK_MT_editor_menus(Menu): show_uvedit = sima.show_uvedit show_maskedit = sima.show_maskedit - show_paint = sima.show_paint layout.menu("IMAGE_MT_view") @@ -771,8 +762,6 @@ class MASK_MT_editor_menus(Menu): layout.menu("IMAGE_MT_select") if show_maskedit: layout.menu("MASK_MT_select") - if show_paint: - layout.menu("IMAGE_MT_brush") if ima and ima.is_dirty: layout.menu("IMAGE_MT_image", text="Image*") @@ -792,44 +781,13 @@ class IMAGE_MT_mask_context_menu(Menu): @classmethod def poll(cls, context): sima = context.space_data - return (sima.show_maskedit) + return sima.show_maskedit def draw(self, context): layout = self.layout - sima = context.space_data - - if not sima.mask: - layout.operator("mask.new") - layout.separator() - layout.operator("mask.primitive_circle_add", icon='MESH_CIRCLE') - layout.operator("mask.primitive_square_add", icon='MESH_PLANE') - else: - layout.operator_menu_enum("mask.handle_type_set", "type") - layout.operator("mask.switch_direction") - layout.operator("mask.cyclic_toggle") - - layout.separator() - layout.operator("mask.primitive_circle_add", icon='MESH_CIRCLE') - layout.operator("mask.primitive_square_add", icon='MESH_PLANE') - - layout.separator() - layout.operator("mask.copy_splines", icon='COPYDOWN') - layout.operator("mask.paste_splines", icon='PASTEDOWN') + from .properties_mask_common import draw_mask_context_menu + draw_mask_context_menu(layout, context) - layout.separator() - - layout.operator("mask.shape_key_rekey", text="Re-key Shape Points") - layout.operator("mask.feather_weight_clear") - layout.operator("mask.shape_key_feather_reset", text="Reset Feather Animation") - - layout.separator() - - layout.operator("mask.parent_set") - layout.operator("mask.parent_clear") - - layout.separator() - - layout.operator("mask.delete") # ----------------------------------------------------------------------------- # Mask (similar code in space_clip.py, keep in sync) @@ -1064,7 +1022,7 @@ class IMAGE_PT_render_slots(Panel): class IMAGE_UL_udim_tiles(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + def draw_item(self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index): tile = item layout.prop(tile, "label", text="", emboss=False) @@ -1101,11 +1059,17 @@ class IMAGE_PT_udim_tiles(Panel): col.operator("image.tile_fill") -class IMAGE_PT_paint(Panel, ImagePaintPanel): - bl_label = "Brush" +class IMAGE_PT_paint_select(Panel, ImagePaintPanel, BrushSelectPanel): + bl_label = "Brushes" bl_context = ".paint_common_2d" bl_category = "Tool" + +class IMAGE_PT_paint_settings(Panel, ImagePaintPanel): + bl_context = ".paint_common_2d" + bl_category = "Tool" + bl_label = "Brush Settings" + def draw(self, context): layout = self.layout @@ -1115,98 +1079,33 @@ class IMAGE_PT_paint(Panel, ImagePaintPanel): settings = context.tool_settings.image_paint brush = settings.brush - col = layout.column() - col.template_ID_preview(settings, "brush", new="brush.add", rows=2, cols=6) - if brush: - brush_texpaint_common(self, context, layout, brush, settings) + brush_settings(layout.column(), context, brush, popover=self.is_popover) -class IMAGE_PT_paint_color(Panel, ImagePaintPanel): - bl_category = "Tool" +class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel): bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" - bl_label = "Color Picker" - - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - capabilities = brush.image_paint_capabilities - - return capabilities.has_color - - def draw(self, context): - layout = self.layout - settings = context.tool_settings.image_paint - brush = settings.brush - - layout.prop(brush, "color_type", expand=True) - - if brush.color_type == 'COLOR': - brush_texpaint_common_color(self, context, layout, brush, settings) - elif brush.color_type == 'GRADIENT': - brush_texpaint_common_gradient(self, context, layout, brush, settings) - - -class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel): + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" - bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" - bl_label = "Color Palette" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - capabilities = brush.image_paint_capabilities - - return capabilities.has_color + bl_label = "Advanced" def draw(self, context): layout = self.layout - settings = context.tool_settings.image_paint - - layout.template_ID(settings, "palette", new="palette.new") - if settings.palette: - layout.template_palette(settings, "palette", color=True) - - -class IMAGE_PT_paint_clone(Panel, ImagePaintPanel): - bl_category = "Tool" - bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" - bl_label = "Clone from Image/UV Map" - bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - - return brush.image_tool == 'CLONE' - - def draw_header(self, context): - settings = context.tool_settings.image_paint - self.layout.prop(settings, "use_clone_layer", text="") + layout.use_property_split = True + layout.use_property_decorate = False # No animation. - def draw(self, context): - layout = self.layout settings = context.tool_settings.image_paint brush = settings.brush - layout.active = settings.use_clone_layer + brush_settings_advanced(layout.column(), context, brush, self.is_popover) - brush_texpaint_common_clone(self, context, layout, brush, settings) - -class IMAGE_PT_paint_options(Panel, ImagePaintPanel): - bl_category = "Tool" +class IMAGE_PT_paint_color(Panel, ImagePaintPanel): bl_context = ".paint_common_2d" - bl_parent_id = "IMAGE_PT_paint" - bl_label = "Options" - bl_options = {'DEFAULT_CLOSED'} + bl_parent_id = "IMAGE_PT_paint_settings" + bl_category = "Tool" + bl_label = "Color Picker" @classmethod def poll(cls, context): @@ -1221,130 +1120,36 @@ class IMAGE_PT_paint_options(Panel, ImagePaintPanel): settings = context.tool_settings.image_paint brush = settings.brush - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - brush_texpaint_common_options(self, context, layout, brush, settings) + draw_color_settings(context, layout, brush, color_type=True) -class IMAGE_PT_tools_brush_display(BrushButtonsPanel, Panel): - bl_label = "Display" +class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel, ColorPalettePanel): + bl_category = "Tool" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" + bl_label = "Color Palette" bl_options = {'DEFAULT_CLOSED'} - bl_category = "Tool" - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - tool_settings = context.tool_settings.image_paint - brush = tool_settings.brush - tex_slot = brush.texture_slot - tex_slot_mask = brush.mask_texture_slot - - col = layout.column() - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "cursor_overlay_alpha", text="Curve Alpha") - sub.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - row.prop( - brush, "use_cursor_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', - ) - col.active = brush.brush_capabilities.has_overlay - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "texture_overlay_alpha", text="Texture Alpha") - sub.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot.map_mode != 'STENCIL': - row.prop( - brush, "use_primary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', - ) - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "mask_overlay_alpha", text="Mask Texture Alpha") - sub.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot_mask.map_mode != 'STENCIL': - row.prop( - brush, "use_secondary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', - ) - - -class IMAGE_PT_tools_brush_display_show_brush(BrushButtonsPanel, Panel): - bl_context = ".paint_common_2d" # dot on purpose (access from topbar) - bl_label = "Show Brush" - bl_parent_id = "IMAGE_PT_tools_brush_display" +class IMAGE_PT_paint_clone(Panel, ImagePaintPanel, ClonePanel): bl_category = "Tool" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - settings = context.tool_settings.image_paint - - self.layout.prop(settings, "show_brush", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.image_paint - brush = settings.brush - - col = layout.column() - col.active = settings.show_brush - - if context.sculpt_object and context.tool_settings.sculpt: - if brush.sculpt_capabilities.has_secondary_color: - col.prop(brush, "cursor_color_add", text="Add") - col.prop(brush, "cursor_color_subtract", text="Subtract") - else: - col.prop(brush, "cursor_color_add", text="Color") - else: - col.prop(brush, "cursor_color_add", text="Color") + bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" + bl_label = "Clone from Image/UV Map" -class IMAGE_PT_tools_brush_display_custom_icon(BrushButtonsPanel, Panel): - bl_context = ".paint_common_2d" # dot on purpose (access from topbar) - bl_label = "Custom Icon" - bl_parent_id = "IMAGE_PT_tools_brush_display" +class IMAGE_PT_tools_brush_display(Panel, BrushButtonsPanel, DisplayPanel): + bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" + bl_label = "Brush Tip" bl_options = {'DEFAULT_CLOSED'} - def draw_header(self, context): - settings = context.tool_settings.image_paint - brush = settings.brush - - self.layout.prop(brush, "use_custom_icon", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.image_paint - brush = settings.brush - - col = layout.column() - col.active = brush.use_custom_icon - col.prop(brush, "icon_filepath", text="") - class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel): bl_label = "Texture" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -1360,135 +1165,36 @@ class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, Panel): brush_texture_settings(col, brush, 0) -class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel): - bl_label = "Texture Mask" +class IMAGE_PT_tools_mask_texture(Panel, BrushButtonsPanel, TextureMaskPanel): bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - - brush = context.tool_settings.image_paint.brush - - col = layout.column() - - col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8) - - brush_mask_texture_settings(col, brush) + bl_label = "Texture Mask" -class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel): +class IMAGE_PT_paint_stroke(BrushButtonsPanel, Panel, StrokePanel): bl_label = "Stroke" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - - tool_settings = context.tool_settings.image_paint - brush = tool_settings.brush - layout.use_property_split = True - layout.use_property_decorate = False - - col = layout.column() - - col.prop(brush, "stroke_method") - - if brush.use_anchor: - col.prop(brush, "use_edge_to_edge", text="Edge To Edge") - - if brush.use_airbrush: - col.prop(brush, "rate", text="Rate", slider=True) - - if brush.use_space: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - row.prop(brush, "use_pressure_spacing", toggle=True, text="") - - if brush.use_line or brush.use_curve: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - - if brush.use_curve: - col.template_ID(brush, "paint_curve", new="paintcurve.new") - col.operator("paintcurve.draw") - - row = col.row(align=True) - if brush.use_relative_jitter: - row.prop(brush, "jitter", slider=True) - else: - row.prop(brush, "jitter_absolute") - row.prop(brush, "use_relative_jitter", icon_only=True) - row.prop(brush, "use_pressure_jitter", toggle=True, text="") - - col.prop(tool_settings, "input_samples") - - -class IMAGE_PT_paint_stroke_smooth_stroke(BrushButtonsPanel, Panel): - bl_context = ".paint_common_2d" # dot on purpose (access from topbar) - bl_label = "Smooth Stroke" +class IMAGE_PT_paint_stroke_smooth_stroke(Panel, BrushButtonsPanel, SmoothStrokePanel): + bl_context = ".paint_common_2d" + bl_label = "Stabilize Stroke" bl_parent_id = "IMAGE_PT_paint_stroke" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = context.tool_settings.image_paint - brush = settings.brush - if brush.brush_capabilities.has_smooth_stroke: - return True - def draw_header(self, context): - settings = context.tool_settings.image_paint - brush = settings.brush - - self.layout.prop(brush, "use_smooth_stroke", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = context.tool_settings.image_paint - brush = settings.brush - - col = layout.column() - col.active = brush.use_smooth_stroke - col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) - - -class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel): +class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel, FalloffPanel): bl_label = "Falloff" bl_context = ".paint_common_2d" + bl_parent_id = "IMAGE_PT_paint_settings" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - - tool_settings = context.tool_settings.image_paint - brush = tool_settings.brush - - col = layout.column(align=True) - row = col.row(align=True) - row.prop(brush, "curve_preset", text="") - - if brush.curve_preset == 'CUSTOM': - layout.template_curve_mapping(brush, "curve") - - 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 IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): bl_context = ".imagepaint_2d" @@ -1508,90 +1214,64 @@ class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel): row.prop(ipaint, "tile_y", text="Y", toggle=True) -class IMAGE_PT_uv_sculpt_brush(Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' - bl_context = ".uv_sculpt" # dot on purpose (access from topbar) - bl_category = "Tool" - bl_label = "Brush" - +class UVSculptPanel(UnifiedPaintPanel): @classmethod def poll(cls, context): - sima = context.space_data - # TODO(campbell): nicer way to check if we're in uv sculpt mode. - if sima and sima.show_uvedit: - from bl_ui.space_toolsystem_common import ToolSelectPanelHelper - tool = ToolSelectPanelHelper.tool_active_from_context(context) - if tool.has_datablock: - return True - return False + return cls.get_brush_mode(context) == 'UV_SCULPT' + + +class IMAGE_PT_uv_sculpt_brush_select(Panel, BrushSelectPanel, ImagePaintPanel, UVSculptPanel): + bl_context = ".uv_sculpt" + bl_category = "Tool" + bl_label = "Brushes" + + +class IMAGE_PT_uv_sculpt_brush_settings(Panel, ImagePaintPanel, UVSculptPanel): + bl_context = ".uv_sculpt" + bl_category = "Tool" + bl_label = "Brush Settings" def draw(self, context): - from bl_ui.properties_paint_common import UnifiedPaintPanel layout = self.layout tool_settings = context.tool_settings uvsculpt = tool_settings.uv_sculpt - layout.template_ID(uvsculpt, "brush") - brush = uvsculpt.brush - if not self.is_popover: - if brush: - col = layout.column() - - row = col.row(align=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_size(row, context, brush, "use_pressure_size", text="") - - row = col.row(align=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "strength", slider=True) - UnifiedPaintPanel.prop_unified_strength(row, context, brush, "use_pressure_strength", text="") - - col = layout.column() - col.prop(tool_settings, "uv_sculpt_lock_borders") - col.prop(tool_settings, "uv_sculpt_all_islands") + brush_settings(layout.column(), context, brush) if brush: if brush.uv_sculpt_tool == 'RELAX': - col.prop(tool_settings, "uv_relax_method") - - col.prop(uvsculpt, "show_brush") + # Although this settings is stored in the scene, + # it is only used by a single tool, + # so it doesn't make sense from a user perspective to move it to the Options panel. + layout.prop(tool_settings, "uv_relax_method") -class IMAGE_PT_uv_sculpt_curve(Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'UI' +class IMAGE_PT_uv_sculpt_curve(Panel, FalloffPanel, ImagePaintPanel, UVSculptPanel): bl_context = ".uv_sculpt" # dot on purpose (access from topbar) + bl_parent_id = "IMAGE_PT_uv_sculpt_brush_settings" bl_category = "Tool" bl_label = "Falloff" bl_options = {'DEFAULT_CLOSED'} - poll = IMAGE_PT_uv_sculpt_brush.poll + +class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel, UVSculptPanel): + bl_context = ".uv_sculpt" # dot on purpose (access from topbar) + bl_category = "Tool" + bl_label = "Options" def draw(self, context): layout = self.layout tool_settings = context.tool_settings uvsculpt = tool_settings.uv_sculpt - brush = uvsculpt.brush - - if brush is not None: - col = layout.column(align=True) - row = col.row(align=True) - row.prop(brush, "curve_preset", text="") - if brush.curve_preset == 'CUSTOM': - layout.template_curve_mapping(brush, "curve") - - row = layout.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' + col = layout.column() + col.prop(tool_settings, "uv_sculpt_lock_borders") + col.prop(tool_settings, "uv_sculpt_all_islands") + col.prop(uvsculpt, "show_brush", text="Display Cursor") class ImageScopesPanel: @@ -1765,7 +1445,6 @@ classes = ( IMAGE_MT_view, IMAGE_MT_view_zoom, IMAGE_MT_select, - IMAGE_MT_brush, IMAGE_MT_image, IMAGE_MT_image_invert, IMAGE_MT_uvs, @@ -1797,21 +1476,22 @@ classes = ( IMAGE_PT_view_display, IMAGE_PT_view_display_uv_edit_overlays, IMAGE_PT_view_display_uv_edit_overlays_stretch, - IMAGE_PT_paint, + IMAGE_PT_paint_select, + IMAGE_PT_paint_settings, IMAGE_PT_paint_color, IMAGE_PT_paint_swatches, + IMAGE_PT_paint_settings_advanced, IMAGE_PT_paint_clone, - IMAGE_PT_paint_options, IMAGE_PT_tools_brush_texture, IMAGE_PT_tools_mask_texture, IMAGE_PT_paint_stroke, IMAGE_PT_paint_stroke_smooth_stroke, IMAGE_PT_paint_curve, IMAGE_PT_tools_brush_display, - IMAGE_PT_tools_brush_display_show_brush, - IMAGE_PT_tools_brush_display_custom_icon, IMAGE_PT_tools_imagepaint_symmetry, - IMAGE_PT_uv_sculpt_brush, + IMAGE_PT_uv_sculpt_brush_select, + IMAGE_PT_uv_sculpt_brush_settings, + IMAGE_PT_uv_sculpt_options, IMAGE_PT_uv_sculpt_curve, IMAGE_PT_view_histogram, IMAGE_PT_view_waveform, diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 7bf203d8e39..11fb20d8b38 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -48,7 +48,7 @@ class OUTLINER_HT_header(Header): if display_mode == 'SEQUENCE': row = layout.row(align=True) - row.prop(space, "use_sync_select", icon="UV_SYNC_SELECT", text="") + row.prop(space, "use_sync_select", icon='UV_SYNC_SELECT', text="") row = layout.row(align=True) if display_mode in {'SCENES', 'VIEW_LAYER'}: diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index b4f841d2eb8..097564444d0 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -510,7 +510,7 @@ class SEQUENCER_MT_add(Menu): col.enabled = selected_sequences_len(context) >= 2 col = layout.column() - col.operator_menu_enum("sequencer.fades_add", "type", text="Fade", icon="IPO_EASE_IN_OUT") + col.operator_menu_enum("sequencer.fades_add", "type", text="Fade", icon='IPO_EASE_IN_OUT') col.enabled = selected_sequences_len(context) >= 1 diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 532f5e023b6..7b0a769ae62 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -218,49 +218,27 @@ class ToolSelectPanelHelper: @staticmethod def _tools_flatten(tools): - """ - Flattens, skips None and calls generators. - """ for item in tools: - if item is None: - yield None - elif type(item) is tuple: - for sub_item in item: - if sub_item is None: - yield None - elif _item_is_fn(sub_item): - yield from sub_item(context) - else: - yield sub_item + if type(item) is tuple: + yield from item else: - if _item_is_fn(item): - yield from item(context) - else: - yield item + # May be None. + yield item @staticmethod def _tools_flatten_with_tool_index(tools): for item in tools: - if item is None: - yield None, -1 - elif type(item) is tuple: + if type(item) is tuple: i = 0 for sub_item in item: if sub_item is None: yield None, -1 - elif _item_is_fn(sub_item): - for item_dyn in sub_item(context): - yield item_dyn, i - i += 1 else: yield sub_item, i i += 1 else: - if _item_is_fn(item): - for item_dyn in item(context): - yield item_dyn, -1 - else: - yield item, -1 + # May be None. + yield item, -1 @classmethod def _tool_get_active(cls, context, space_type, mode, with_icon=False): @@ -378,7 +356,7 @@ class ToolSelectPanelHelper: @staticmethod def _tool_active_from_context(context, space_type, mode=None, create=False): - if space_type == 'VIEW_3D': + if space_type in {'VIEW_3D', 'PROPERTIES'}: if mode is None: mode = context.mode tool = context.workspace.tools.from_space_view3d_mode(mode, create=create) @@ -676,7 +654,6 @@ class ToolSelectPanelHelper: space_type = context.space_data.type return ToolSelectPanelHelper._tool_active_from_context(context, space_type) - @staticmethod def draw_active_tool_fallback( context, layout, tool, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 639fb2a31a4..5f017e61db7 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -594,7 +594,7 @@ class _defs_edit_mesh: props = tool.operator_properties("mesh.bevel") region_type = context.region.type - if extra == False: + if not extra: if props.offset_type == 'PERCENT': layout.prop(props, "offset_pct") else: @@ -1135,11 +1135,10 @@ class _defs_weight_paint: def draw_settings(context, layout, tool): brush = context.tool_settings.weight_paint.brush if brush is not None: - from bl_ui.properties_paint_common import UnifiedPaintPanel - UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength", slider=True) + layout.prop(brush, "weight", slider=True) + layout.prop(brush, "strength", slider=True) props = tool.operator_properties("paint.weight_gradient") - layout.prop(props, "type") + layout.prop(props, "type", expand=True) return dict( idname="builtin.gradient", @@ -1203,7 +1202,7 @@ class _defs_image_uv_transform: idname="builtin.move", label="Move", icon="ops.transform.translate", - # widget="VIEW3D_GGT_xform_gizmo", + widget="IMAGE_GGT_gizmo2d_translate", operator="transform.translate", keymap="Image Editor Tool: Uv, Move", ) @@ -1214,7 +1213,7 @@ class _defs_image_uv_transform: idname="builtin.rotate", label="Rotate", icon="ops.transform.rotate", - # widget="VIEW3D_GGT_xform_gizmo", + widget="IMAGE_GGT_gizmo2d_rotate", operator="transform.rotate", keymap="Image Editor Tool: Uv, Rotate", ) @@ -1225,7 +1224,7 @@ class _defs_image_uv_transform: idname="builtin.scale", label="Scale", icon="ops.transform.resize", - # widget="VIEW3D_GGT_xform_gizmo", + widget="IMAGE_GGT_gizmo2d_resize", operator="transform.resize", keymap="Image Editor Tool: Uv, Scale", ) diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 2e2c5adb970..7dbf49d01e3 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -20,6 +20,7 @@ import bpy from bpy.types import Header, Menu, Panel + class TOPBAR_HT_upper_bar(Header): bl_space_type = 'TOPBAR' @@ -67,7 +68,8 @@ class TOPBAR_HT_upper_bar(Header): layout.template_running_jobs() # Active workspace view-layer is retrieved through window, not through workspace. - layout.template_ID(window, "scene", new="scene.new", unlink="scene.delete") + layout.template_ID(window, "scene", new="scene.new", + unlink="scene.delete") row = layout.row(align=True) row.template_search( @@ -91,9 +93,11 @@ class TOPBAR_PT_tool_settings_extra(Panel): layout = self.layout # Get the active tool - space_type, mode = ToolSelectPanelHelper._tool_key_from_context(context) + space_type, mode = ToolSelectPanelHelper._tool_key_from_context( + context) cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type) - item, tool, _ = cls._tool_get_active(context, space_type, mode, with_icon=True) + item, tool, _ = cls._tool_get_active( + context, space_type, mode, with_icon=True) if item is None: return @@ -115,7 +119,8 @@ class TOPBAR_PT_tool_fallback(Panel): ToolSelectPanelHelper.draw_fallback_tool_items(layout, context) if tool_settings.workspace_tool_type == 'FALLBACK': tool = context.tool - ToolSelectPanelHelper.draw_active_tool_fallback(context, layout, tool) + ToolSelectPanelHelper.draw_active_tool_fallback( + context, layout, tool) class TOPBAR_PT_gpencil_layers(Panel): @@ -174,20 +179,25 @@ class TOPBAR_PT_gpencil_layers(Panel): gpl = context.active_gpencil_layer if gpl: - sub.menu("GPENCIL_MT_layer_context_menu", icon='DOWNARROW_HLT', text="") + sub.menu("GPENCIL_MT_layer_context_menu", + icon='DOWNARROW_HLT', text="") if len(gpd.layers) > 1: col.separator() sub = col.column(align=True) - sub.operator("gpencil.layer_move", icon='TRIA_UP', text="").type = 'UP' - sub.operator("gpencil.layer_move", icon='TRIA_DOWN', text="").type = 'DOWN' + sub.operator("gpencil.layer_move", + icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.layer_move", + icon='TRIA_DOWN', text="").type = 'DOWN' col.separator() sub = col.column(align=True) - sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', text="").affect_visibility = True - sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.layer_isolate", icon='HIDE_OFF', + text="").affect_visibility = True + sub.operator("gpencil.layer_isolate", icon='LOCKED', + text="").affect_visibility = False class TOPBAR_MT_editor_menus(Menu): @@ -229,7 +239,18 @@ class TOPBAR_MT_app(Menu): layout.separator() - layout.operator("preferences.app_template_install", text="Install Application Template...") + layout.operator("preferences.app_template_install", + text="Install Application Template...") + + +class TOPBAR_MT_file_cleanup(Menu): + bl_label = "Clean Up" + + def draw(self, context): + layout = self.layout + layout.separator() + + layout.operator("outliner.orphans_purge") class TOPBAR_MT_file(Menu): @@ -270,6 +291,7 @@ class TOPBAR_MT_file(Menu): layout.separator() layout.menu("TOPBAR_MT_file_external_data") + layout.menu("TOPBAR_MT_file_cleanup") layout.separator() @@ -325,7 +347,8 @@ class TOPBAR_MT_file_new(Menu): # Draw application templates. if not use_more: - props = layout.operator("wm.read_homefile", text="General", icon=icon) + props = layout.operator( + "wm.read_homefile", text="General", icon=icon) props.app_template = "" for d in paths: @@ -370,7 +393,8 @@ class TOPBAR_MT_file_defaults(Menu): app_template = None if app_template: - layout.label(text=bpy.path.display_name(app_template, has_ext=False)) + layout.label(text=bpy.path.display_name( + app_template, has_ext=False)) layout.operator("wm.save_homefile") props = layout.operator("wm.read_factory_settings") @@ -384,12 +408,15 @@ class TOPBAR_MT_app_about(Menu): def draw(self, _context): layout = self.layout - layout.operator("wm.url_open_preset", text="Release Notes", icon='URL').type = 'RELEASE_NOTES' + layout.operator("wm.url_open_preset", text="Release Notes", + icon='URL').type = 'RELEASE_NOTES' 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.url_open_preset", + text="Blender Website", icon='URL').type = 'BLENDER' + layout.operator("wm.url_open_preset", text="Credits", + icon='URL').type = 'CREDITS' layout.separator() @@ -404,7 +431,8 @@ class TOPBAR_MT_app_support(Menu): def draw(self, _context): layout = self.layout - layout.operator("wm.url_open_preset", text="Development Fund", icon='FUND').type = 'FUND' + layout.operator("wm.url_open_preset", + text="Development Fund", icon='FUND').type = 'FUND' layout.separator() @@ -417,7 +445,8 @@ class TOPBAR_MT_templates_more(Menu): bl_label = "Templates" def draw(self, context): - bpy.types.TOPBAR_MT_file_new.draw_ex(self.layout, context, use_more=True) + bpy.types.TOPBAR_MT_file_new.draw_ex( + self.layout, context, use_more=True) class TOPBAR_MT_file_import(Menu): @@ -426,7 +455,8 @@ class TOPBAR_MT_file_import(Menu): def draw(self, _context): if bpy.app.build_options.collada: - self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)") + self.layout.operator("wm.collada_import", + text="Collada (Default) (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_import", text="Alembic (.abc)") @@ -435,11 +465,15 @@ class TOPBAR_MT_file_export(Menu): bl_idname = "TOPBAR_MT_file_export" bl_label = "Export" - def draw(self, _context): + def draw(self, context): if bpy.app.build_options.collada: - self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)") + self.layout.operator("wm.collada_export", + text="Collada (Default) (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_export", text="Alembic (.abc)") + if bpy.app.build_options.usd and context.preferences.experimental.use_usd_exporter: + self.layout.operator( + "wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)") class TOPBAR_MT_file_external_data(Menu): @@ -492,8 +526,10 @@ class TOPBAR_MT_render(Menu): rd = context.scene.render - layout.operator("render.render", text="Render Image", icon='RENDER_STILL').use_viewport = True - props = layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION') + layout.operator("render.render", text="Render Image", + icon='RENDER_STILL').use_viewport = True + props = layout.operator( + "render.render", text="Render Animation", icon='RENDER_ANIMATION') props.animation = True props.use_viewport = True @@ -535,7 +571,8 @@ class TOPBAR_MT_edit(Menu): layout.separator() - layout.operator("wm.search_menu", text="Operator Search...", icon='VIEWZOOM') + layout.operator("wm.search_menu", + text="Operator Search...", icon='VIEWZOOM') layout.separator() @@ -554,7 +591,8 @@ class TOPBAR_MT_edit(Menu): layout.separator() - layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES') + layout.operator("screen.userpref_show", + text="Preferences...", icon='PREFERENCES') class TOPBAR_MT_window(Menu): @@ -574,8 +612,10 @@ class TOPBAR_MT_window(Menu): layout.separator() - layout.operator("screen.workspace_cycle", text="Next Workspace").direction = 'NEXT' - layout.operator("screen.workspace_cycle", text="Previous Workspace").direction = 'PREV' + layout.operator("screen.workspace_cycle", + text="Next Workspace").direction = 'NEXT' + layout.operator("screen.workspace_cycle", + text="Previous Workspace").direction = 'PREV' layout.separator() @@ -602,7 +642,8 @@ class TOPBAR_MT_help(Menu): show_developer = context.preferences.view.show_developer_ui - layout.operator("wm.url_open_preset", text="Manual", icon='HELP').type = 'MANUAL' + layout.operator("wm.url_open_preset", text="Manual", + icon='HELP').type = 'MANUAL' layout.operator( "wm.url_open", text="Tutorials", icon='URL', @@ -635,7 +676,8 @@ class TOPBAR_MT_help(Menu): layout.separator() - layout.operator("wm.url_open_preset", text="Report a Bug", icon='URL').type = 'BUG' + layout.operator("wm.url_open_preset", + text="Report a Bug", icon='URL').type = 'BUG' layout.separator() @@ -664,7 +706,8 @@ class TOPBAR_MT_file_context_menu(Menu): layout.separator() - layout.operator("screen.userpref_show", text="Preferences...", icon='PREFERENCES') + layout.operator("screen.userpref_show", + text="Preferences...", icon='PREFERENCES') class TOPBAR_MT_workspace_menu(Menu): @@ -673,21 +716,26 @@ class TOPBAR_MT_workspace_menu(Menu): def draw(self, _context): layout = self.layout - layout.operator("workspace.duplicate", text="Duplicate", icon='DUPLICATE') + layout.operator("workspace.duplicate", + text="Duplicate", icon='DUPLICATE') if len(bpy.data.workspaces) > 1: layout.operator("workspace.delete", text="Delete", icon='REMOVE') layout.separator() - layout.operator("workspace.reorder_to_front", text="Reorder to Front", icon='TRIA_LEFT_BAR') - layout.operator("workspace.reorder_to_back", text="Reorder to Back", icon='TRIA_RIGHT_BAR') + layout.operator("workspace.reorder_to_front", + text="Reorder to Front", icon='TRIA_LEFT_BAR') + layout.operator("workspace.reorder_to_back", + text="Reorder to Back", icon='TRIA_RIGHT_BAR') layout.separator() # For key binding discoverability. - props = layout.operator("screen.workspace_cycle", text="Previous Workspace") + props = layout.operator("screen.workspace_cycle", + text="Previous Workspace") props.direction = 'PREV' - props = layout.operator("screen.workspace_cycle", text="Next Workspace") + props = layout.operator( + "screen.workspace_cycle", text="Next Workspace") props.direction = 'NEXT' @@ -702,29 +750,8 @@ class TOPBAR_PT_gpencil_primitive(Panel): layout = self.layout # Curve - layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True) - - -# Grease Pencil Fill -class TOPBAR_PT_gpencil_fill(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'HEADER' - bl_label = "Advanced" - - def draw(self, context): - paint = context.tool_settings.gpencil_paint - brush = paint.brush - gp_settings = brush.gpencil_settings - - layout = self.layout - # Fill - row = layout.row(align=True) - row.prop(gp_settings, "fill_factor", text="Resolution") - if gp_settings.fill_draw_mode != 'STROKE': - row = layout.row(align=True) - row.prop(gp_settings, "show_fill", text="Ignore Transparent Strokes") - row = layout.row(align=True) - row.prop(gp_settings, "fill_threshold", text="Threshold") + layout.template_curve_mapping( + settings, "thickness_primitive_curve", brush=True) # Only a popover @@ -807,6 +834,7 @@ classes = ( TOPBAR_MT_file_import, TOPBAR_MT_file_export, TOPBAR_MT_file_external_data, + TOPBAR_MT_file_cleanup, TOPBAR_MT_file_previews, TOPBAR_MT_edit, TOPBAR_MT_render, @@ -816,7 +844,6 @@ classes = ( TOPBAR_PT_tool_settings_extra, TOPBAR_PT_gpencil_layers, TOPBAR_PT_gpencil_primitive, - TOPBAR_PT_gpencil_fill, TOPBAR_PT_name, ) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index bf39cbda391..9527c7f4de8 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -27,6 +27,9 @@ from bpy.app.translations import pgettext_iface as iface_ from bpy.app.translations import contexts as i18n_contexts +# ----------------------------------------------------------------------------- +# Main Header + class USERPREF_HT_header(Header): bl_space_type = 'PREFERENCES' @@ -60,6 +63,9 @@ class USERPREF_HT_header(Header): self.draw_buttons(layout, context) +# ----------------------------------------------------------------------------- +# Main Navigation Bar + class USERPREF_PT_navigation_bar(Panel): bl_label = "Preferences Navigation" bl_space_type = 'PREFERENCES' @@ -128,47 +134,53 @@ class USERPREF_PT_save_preferences(Panel): USERPREF_HT_header.draw_buttons(layout, context) +# ----------------------------------------------------------------------------- +# Min-In Helpers + # Panel mix-in. -class PreferencePanel: +class CenterAlignMixIn: """ Base class for panels to center align contents with some horizontal margin. - Deriving classes need to implement a ``draw_props(context, layout)`` function. + Deriving classes need to implement a ``draw_centered(context, layout)`` function. """ - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' - def draw(self, context): layout = self.layout width = context.region.width ui_scale = context.preferences.system.ui_scale + # No horizontal margin if region is rather small. + is_wide = width > (350 * ui_scale) layout.use_property_split = True layout.use_property_decorate = False # No animation. row = layout.row() - if width > (350 * ui_scale): # No horizontal margin if region is rather small. + if is_wide: row.label() # Needed so col below is centered. col = row.column() col.ui_units_x = 50 - # draw_props implemented by deriving classes. - self.draw_props(context, col) + # Implemented by sub-classes. + self.draw_centered(context, col) - if width > (350 * ui_scale): # No horizontal margin if region is rather small. + if is_wide: row.label() # Needed so col above is centered. -class USERPREF_PT_interface_display(PreferencePanel, Panel): - bl_label = "Display" +# ----------------------------------------------------------------------------- +# Interface Panels + +class InterfacePanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "interface" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INTERFACE') - def draw_props(self, context, layout): +class USERPREF_PT_interface_display(InterfacePanel, CenterAlignMixIn, Panel): + bl_label = "Display" + + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -188,16 +200,11 @@ class USERPREF_PT_interface_display(PreferencePanel, Panel): flow.prop(view, "show_large_cursors") -class USERPREF_PT_interface_text(PreferencePanel, Panel): +class USERPREF_PT_interface_text(InterfacePanel, CenterAlignMixIn, Panel): bl_label = "Text Rendering" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INTERFACE') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -212,14 +219,13 @@ class USERPREF_PT_interface_text(PreferencePanel, Panel): flow.prop(view, "font_path_ui_mono") -class USERPREF_PT_interface_translation(PreferencePanel, Panel): +class USERPREF_PT_interface_translation(InterfacePanel, CenterAlignMixIn, Panel): bl_label = "Translation" bl_translation_context = i18n_contexts.id_windowmanager @classmethod def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INTERFACE') and bpy.app.build_options.international + return bpy.app.build_options.international def draw_header(self, context): prefs = context.preferences @@ -227,7 +233,7 @@ class USERPREF_PT_interface_translation(PreferencePanel, Panel): self.layout.prop(view, "use_international_fonts", text="") - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -242,15 +248,10 @@ class USERPREF_PT_interface_translation(PreferencePanel, Panel): flow.prop(view, "use_translate_new_dataname", text="New Data") -class USERPREF_PT_interface_editors(PreferencePanel, Panel): +class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel): bl_label = "Editors" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INTERFACE') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view system = prefs.system @@ -265,17 +266,12 @@ class USERPREF_PT_interface_editors(PreferencePanel, Panel): flow.prop(view, "factor_display_type") -class USERPREF_PT_interface_temporary_windows(PreferencePanel, Panel): +class USERPREF_PT_interface_temporary_windows(InterfacePanel, CenterAlignMixIn, Panel): bl_label = "Temporary Windows" bl_parent_id = "USERPREF_PT_interface_editors" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INTERFACE') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -285,22 +281,15 @@ class USERPREF_PT_interface_temporary_windows(PreferencePanel, Panel): flow.prop(view, "filebrowser_display_type", text="File Browser") -class USERPREF_PT_interface_menus(Panel): - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' +class USERPREF_PT_interface_menus(InterfacePanel, Panel): bl_label = "Menus" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INTERFACE') - def draw(self, context): pass -class USERPREF_PT_interface_menus_mouse_over(PreferencePanel, Panel): +class USERPREF_PT_interface_menus_mouse_over(InterfacePanel, CenterAlignMixIn, Panel): bl_label = "Open on Mouse Over" bl_parent_id = "USERPREF_PT_interface_menus" @@ -310,7 +299,7 @@ class USERPREF_PT_interface_menus_mouse_over(PreferencePanel, Panel): self.layout.prop(view, "use_mouse_over_open", text="") - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -322,11 +311,11 @@ class USERPREF_PT_interface_menus_mouse_over(PreferencePanel, Panel): flow.prop(view, "open_sublevel_delay", text="Sub Level") -class USERPREF_PT_interface_menus_pie(PreferencePanel, Panel): +class USERPREF_PT_interface_menus_pie(InterfacePanel, CenterAlignMixIn, Panel): bl_label = "Pie Menus" bl_parent_id = "USERPREF_PT_interface_menus" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -340,25 +329,27 @@ class USERPREF_PT_interface_menus_pie(PreferencePanel, Panel): flow.prop(view, "pie_menu_confirm") -class USERPREF_PT_edit_objects(Panel): - bl_label = "Objects" +# ----------------------------------------------------------------------------- +# Editing Panels + +class EditingPanel: bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' + bl_context = "editing" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EDITING') + +class USERPREF_PT_edit_objects(EditingPanel, Panel): + bl_label = "Objects" def draw(self, context): pass -class USERPREF_PT_edit_objects_new(PreferencePanel, Panel): +class USERPREF_PT_edit_objects_new(EditingPanel, CenterAlignMixIn, Panel): bl_label = "New Objects" bl_parent_id = "USERPREF_PT_edit_objects" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -369,11 +360,11 @@ class USERPREF_PT_edit_objects_new(PreferencePanel, Panel): flow.prop(edit, "use_enter_edit_mode", text="Enter Edit Mode") -class USERPREF_PT_edit_objects_duplicate_data(PreferencePanel, Panel): +class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Panel): bl_label = "Duplicate Data" bl_parent_id = "USERPREF_PT_edit_objects" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -398,15 +389,10 @@ class USERPREF_PT_edit_objects_duplicate_data(PreferencePanel, Panel): col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") -class USERPREF_PT_edit_cursor(PreferencePanel, Panel): +class USERPREF_PT_edit_cursor(EditingPanel, CenterAlignMixIn, Panel): bl_label = "3D Cursor" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EDITING') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -416,16 +402,11 @@ class USERPREF_PT_edit_cursor(PreferencePanel, Panel): flow.prop(edit, "use_cursor_lock_adjust") -class USERPREF_PT_edit_gpencil(PreferencePanel, Panel): +class USERPREF_PT_edit_gpencil(EditingPanel, CenterAlignMixIn, Panel): bl_label = "Grease Pencil" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EDITING') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -435,15 +416,10 @@ class USERPREF_PT_edit_gpencil(PreferencePanel, Panel): flow.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") -class USERPREF_PT_edit_annotations(PreferencePanel, Panel): +class USERPREF_PT_edit_annotations(EditingPanel, CenterAlignMixIn, Panel): bl_label = "Annotations" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EDITING') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -453,16 +429,11 @@ class USERPREF_PT_edit_annotations(PreferencePanel, Panel): flow.prop(edit, "grease_pencil_eraser_radius", text="Eraser Radius") -class USERPREF_PT_edit_weight_paint(PreferencePanel, Panel): +class USERPREF_PT_edit_weight_paint(EditingPanel, CenterAlignMixIn, Panel): bl_label = "Weight Paint" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EDITING') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -473,16 +444,11 @@ class USERPREF_PT_edit_weight_paint(PreferencePanel, Panel): col.template_color_ramp(view, "weight_color_range", expand=True) -class USERPREF_PT_edit_misc(PreferencePanel, Panel): +class USERPREF_PT_edit_misc(EditingPanel, CenterAlignMixIn, Panel): bl_label = "Miscellaneous" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EDITING') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -492,15 +458,19 @@ class USERPREF_PT_edit_misc(PreferencePanel, Panel): flow.prop(edit, "node_margin", text="Node Auto-offset Margin") -class USERPREF_PT_animation_timeline(PreferencePanel, Panel): - bl_label = "Timeline" +# ----------------------------------------------------------------------------- +# Animation Panels + +class AnimationPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "animation" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'ANIMATION') - def draw_props(self, context, layout): +class USERPREF_PT_animation_timeline(AnimationPanel, CenterAlignMixIn, Panel): + bl_label = "Timeline" + + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view edit = prefs.edit @@ -521,15 +491,10 @@ class USERPREF_PT_animation_timeline(PreferencePanel, Panel): flow.prop(view, "view_frame_keyframes") -class USERPREF_PT_animation_keyframes(PreferencePanel, Panel): +class USERPREF_PT_animation_keyframes(AnimationPanel, CenterAlignMixIn, Panel): bl_label = "Keyframes" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'ANIMATION') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -539,11 +504,11 @@ class USERPREF_PT_animation_keyframes(PreferencePanel, Panel): flow.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed") -class USERPREF_PT_animation_autokey(PreferencePanel, Panel): +class USERPREF_PT_animation_autokey(AnimationPanel, CenterAlignMixIn, Panel): bl_label = "Auto-Keyframing" bl_parent_id = "USERPREF_PT_animation_keyframes" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -554,15 +519,10 @@ class USERPREF_PT_animation_autokey(PreferencePanel, Panel): flow.prop(edit, "use_auto_keying", text="Enable in New Scenes") -class USERPREF_PT_animation_fcurves(PreferencePanel, Panel): +class USERPREF_PT_animation_fcurves(AnimationPanel, CenterAlignMixIn, Panel): bl_label = "F-Curves" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'ANIMATION') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences edit = prefs.edit @@ -575,15 +535,19 @@ class USERPREF_PT_animation_fcurves(PreferencePanel, Panel): flow.prop(edit, "use_insertkey_xyz_to_rgb", text="XYZ to RGB") -class USERPREF_PT_system_sound(PreferencePanel, Panel): - bl_label = "Sound" +# ----------------------------------------------------------------------------- +# System Panels - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'SYSTEM') +class SystemPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "system" + + +class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel): + bl_label = "Sound" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences system = prefs.system @@ -597,15 +561,10 @@ class USERPREF_PT_system_sound(PreferencePanel, Panel): sub.prop(system, "audio_sample_format", text="Sample Format") -class USERPREF_PT_system_cycles_devices(PreferencePanel, Panel): +class USERPREF_PT_system_cycles_devices(SystemPanel, CenterAlignMixIn, Panel): bl_label = "Cycles Render Devices" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'SYSTEM') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences col = layout.column() @@ -624,15 +583,55 @@ class USERPREF_PT_system_cycles_devices(PreferencePanel, Panel): # col.row().prop(system, "opensubdiv_compute_type", text="") -class USERPREF_PT_viewport_display(PreferencePanel, Panel): - bl_label = "Display" +class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel): + bl_label = "Memory & Limits" - @classmethod - def poll(cls, context): + def draw_centered(self, context, layout): prefs = context.preferences - return (prefs.active_section == 'VIEWPORT') + 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") + + layout.separator() + + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + + flow.prop(system, "memory_cache_limit", text="Sequencer Cache Limit") + flow.prop(system, "scrollback", text="Console Scrollback Lines") + + layout.separator() + + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + + flow.prop(system, "texture_time_out", text="Texture Time Out") + flow.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) + + flow.prop(system, "vbo_time_out", text="Vbo Time Out") + flow.prop(system, "vbo_collection_rate", text="Garbage Collection Rate") + + +# ----------------------------------------------------------------------------- +# Viewport Panels + +class ViewportPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "viewport" + + +class USERPREF_PT_viewport_display(ViewportPanel, CenterAlignMixIn, Panel): + bl_label = "Display" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences view = prefs.view @@ -660,15 +659,10 @@ class USERPREF_PT_viewport_display(PreferencePanel, Panel): col.prop(view, "mini_axis_brightness", text="Brightness") -class USERPREF_PT_viewport_quality(PreferencePanel, Panel): +class USERPREF_PT_viewport_quality(ViewportPanel, CenterAlignMixIn, Panel): bl_label = "Quality" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'VIEWPORT') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences system = prefs.system @@ -680,15 +674,10 @@ class USERPREF_PT_viewport_quality(PreferencePanel, Panel): flow.prop(system, "use_edit_mode_smooth_wire") -class USERPREF_PT_viewport_textures(PreferencePanel, Panel): +class USERPREF_PT_viewport_textures(ViewportPanel, CenterAlignMixIn, Panel): bl_label = "Textures" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'VIEWPORT') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences system = prefs.system @@ -700,16 +689,11 @@ class USERPREF_PT_viewport_textures(PreferencePanel, Panel): flow.prop(system, "image_draw_method", text="Image Display Method") -class USERPREF_PT_viewport_selection(PreferencePanel, Panel): +class USERPREF_PT_viewport_selection(ViewportPanel, CenterAlignMixIn, Panel): bl_label = "Selection" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'VIEWPORT') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences system = prefs.system @@ -718,45 +702,13 @@ class USERPREF_PT_viewport_selection(PreferencePanel, Panel): flow.prop(system, "use_select_pick_depth") -class USERPREF_PT_system_memory(PreferencePanel, Panel): - bl_label = "Memory & Limits" - - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'SYSTEM') - - def draw_props(self, context, layout): - prefs = context.preferences - 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") - - layout.separator() - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) - - flow.prop(system, "memory_cache_limit", text="Sequencer Cache Limit") - flow.prop(system, "scrollback", text="Console Scrollback Lines") - - layout.separator() - - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) +# ----------------------------------------------------------------------------- +# Theme Panels - flow.prop(system, "texture_time_out", text="Texture Time Out") - flow.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) - - flow.prop(system, "vbo_time_out", text="Vbo Time Out") - flow.prop(system, "vbo_collection_rate", text="Garbage Collection Rate") +class ThemePanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "themes" class USERPREF_MT_interface_theme_presets(Menu): @@ -775,17 +727,10 @@ class USERPREF_MT_interface_theme_presets(Menu): bpy.ops.preferences.reset_default_theme() -class USERPREF_PT_theme(Panel): - bl_space_type = 'PREFERENCES' +class USERPREF_PT_theme(ThemePanel, Panel): bl_label = "Themes" - bl_region_type = 'WINDOW' bl_options = {'HIDE_HEADER'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'THEMES') - def draw(self, _context): layout = self.layout @@ -801,17 +746,10 @@ class USERPREF_PT_theme(Panel): row.operator("preferences.reset_default_theme", text="Reset", icon='LOOP_BACK') -class USERPREF_PT_theme_user_interface(PreferencePanel, Panel): - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' +class USERPREF_PT_theme_user_interface(ThemePanel, CenterAlignMixIn, Panel): bl_label = "User Interface" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'THEMES') - def draw_header(self, _context): layout = self.layout @@ -822,9 +760,8 @@ class USERPREF_PT_theme_user_interface(PreferencePanel, Panel): # Base class for dynamically defined widget color panels. +# This is not registered. class PreferenceThemeWidgetColorPanel: - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' bl_parent_id = "USERPREF_PT_theme_user_interface" def draw(self, context): @@ -851,15 +788,10 @@ class PreferenceThemeWidgetColorPanel: col.prop(widget_style, "roundness") - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'THEMES') - +# Base class for dynamically defined widget color panels. +# This is not registered. class PreferenceThemeWidgetShadePanel: - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' def draw(self, context): theme = context.preferences.themes[0] @@ -882,12 +814,12 @@ class PreferenceThemeWidgetShadePanel: self.layout.prop(widget_style, "show_shaded", text="") -class USERPREF_PT_theme_interface_state(PreferencePanel, Panel): +class USERPREF_PT_theme_interface_state(ThemePanel, CenterAlignMixIn, Panel): bl_label = "State" bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "USERPREF_PT_theme_user_interface" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): theme = context.preferences.themes[0] ui_state = theme.user_interface.wcol_state @@ -917,12 +849,12 @@ class USERPREF_PT_theme_interface_state(PreferencePanel, Panel): col.prop(ui_state, "blend") -class USERPREF_PT_theme_interface_styles(PreferencePanel, Panel): +class USERPREF_PT_theme_interface_styles(ThemePanel, CenterAlignMixIn, Panel): bl_label = "Styles" bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "USERPREF_PT_theme_user_interface" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): theme = context.preferences.themes[0] ui = theme.user_interface @@ -937,12 +869,12 @@ class USERPREF_PT_theme_interface_styles(PreferencePanel, Panel): flow.prop(ui, "widget_emboss") -class USERPREF_PT_theme_interface_gizmos(PreferencePanel, Panel): +class USERPREF_PT_theme_interface_gizmos(ThemePanel, CenterAlignMixIn, Panel): bl_label = "Axis & Gizmo Colors" bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "USERPREF_PT_theme_user_interface" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): theme = context.preferences.themes[0] ui = theme.user_interface @@ -962,12 +894,12 @@ class USERPREF_PT_theme_interface_gizmos(PreferencePanel, Panel): col.prop(ui, "gizmo_b") -class USERPREF_PT_theme_interface_icons(PreferencePanel, Panel): +class USERPREF_PT_theme_interface_icons(ThemePanel, CenterAlignMixIn, Panel): bl_label = "Icon Colors" bl_options = {'DEFAULT_CLOSED'} bl_parent_id = "USERPREF_PT_theme_user_interface" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): theme = context.preferences.themes[0] ui = theme.user_interface @@ -983,15 +915,10 @@ class USERPREF_PT_theme_interface_icons(PreferencePanel, Panel): flow.prop(ui, "icon_border_intensity") -class USERPREF_PT_theme_text_style(PreferencePanel, Panel): +class USERPREF_PT_theme_text_style(ThemePanel, CenterAlignMixIn, Panel): bl_label = "Text Style" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'THEMES') - @staticmethod def _ui_font_style(layout, font_style): layout.use_property_split = True @@ -1015,7 +942,7 @@ class USERPREF_PT_theme_text_style(PreferencePanel, Panel): layout.label(icon='FONTPREVIEW') - def draw_props(self, context, layout): + def draw_centered(self, context, layout): style = context.preferences.ui_styles[0] layout.label(text="Panel Title") @@ -1032,21 +959,16 @@ class USERPREF_PT_theme_text_style(PreferencePanel, Panel): self._ui_font_style(layout, style.widget_label) -class USERPREF_PT_theme_bone_color_sets(PreferencePanel, Panel): +class USERPREF_PT_theme_bone_color_sets(ThemePanel, CenterAlignMixIn, Panel): bl_label = "Bone Color Sets" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'THEMES') - def draw_header(self, _context): layout = self.layout layout.label(icon='COLOR') - def draw_props(self, context, layout): + def draw_centered(self, context, layout): theme = context.preferences.themes[0] layout.use_property_split = True @@ -1063,9 +985,8 @@ class USERPREF_PT_theme_bone_color_sets(PreferencePanel, Panel): # Base class for dynamically defined theme-space panels. +# This is not registered. class PreferenceThemeSpacePanel: - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' # not essential, hard-coded UI delimiters for the theme layout ui_delimiters = { @@ -1137,11 +1058,6 @@ class PreferenceThemeSpacePanel: data = getattr(data, datapath_item) PreferenceThemeSpacePanel._theme_generic(layout, data, self.theme_area) - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'THEMES') - class ThemeGenericClassGenerator(): @@ -1172,7 +1088,7 @@ class ThemeGenericClassGenerator(): for (name, wcol) in wcols: panel_id = "USERPREF_PT_theme_interface_" + wcol - yield type(panel_id, (PreferenceThemeWidgetColorPanel, Panel), { + yield type(panel_id, (PreferenceThemeWidgetColorPanel, ThemePanel, Panel), { "bl_label": name, "bl_options": {'DEFAULT_CLOSED'}, "draw": PreferenceThemeWidgetColorPanel.draw, @@ -1180,7 +1096,7 @@ class ThemeGenericClassGenerator(): }) panel_shade_id = "USERPREF_PT_theme_interface_shade_" + wcol - yield type(panel_shade_id, (PreferenceThemeWidgetShadePanel, Panel), { + yield type(panel_shade_id, (PreferenceThemeWidgetShadePanel, ThemePanel, Panel), { "bl_label": "Shaded", "bl_options": {'DEFAULT_CLOSED'}, "bl_parent_id": panel_id, @@ -1204,7 +1120,7 @@ class ThemeGenericClassGenerator(): for prop in props_ls: new_datapath = datapath + "." + prop.identifier if datapath else prop.identifier panel_id = parent_id + "_" + prop.identifier - yield type(panel_id, (PreferenceThemeSpacePanel, Panel), { + yield type(panel_id, (PreferenceThemeSpacePanel, ThemePanel, Panel), { "bl_label": rna_type.properties[prop.identifier].name, "bl_parent_id": parent_id, "bl_options": {'DEFAULT_CLOSED'}, @@ -1213,7 +1129,12 @@ class ThemeGenericClassGenerator(): "datapath": new_datapath, }) - yield from generate_child_panel_classes_recurse(panel_id, prop.fixed_type, theme_area, new_datapath) + yield from generate_child_panel_classes_recurse( + panel_id, + prop.fixed_type, + theme_area, + new_datapath, + ) yield from generate_child_panel_classes_recurse(parent_id, rna_type, theme_area, datapath) @@ -1227,7 +1148,7 @@ class ThemeGenericClassGenerator(): panel_id = "USERPREF_PT_theme_" + theme_area.identifier.lower() # Generate panel-class from theme_area - yield type(panel_id, (PreferenceThemeSpacePanel, Panel), { + yield type(panel_id, (PreferenceThemeSpacePanel, ThemePanel, Panel), { "bl_label": theme_area.name, "bl_options": {'DEFAULT_CLOSED'}, "draw_header": PreferenceThemeSpacePanel.draw_header, @@ -1242,29 +1163,24 @@ class ThemeGenericClassGenerator(): theme_area, theme_area.identifier.lower()) +# ----------------------------------------------------------------------------- +# File Paths Panels + # Panel mix-in. class FilePathsPanel: bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' + bl_context = "file_paths" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'FILE_PATHS') + +class USERPREF_PT_file_paths_data(FilePathsPanel, Panel): + bl_label = "Data" def draw(self, context): layout = self.layout - layout.use_property_split = True layout.use_property_decorate = False - self.draw_props(context, layout) - - -class USERPREF_PT_file_paths_data(FilePathsPanel, Panel): - bl_label = "Data" - - def draw_props(self, context, _layout): paths = context.preferences.filepaths col = self.layout.column() @@ -1278,7 +1194,11 @@ class USERPREF_PT_file_paths_data(FilePathsPanel, Panel): class USERPREF_PT_file_paths_render(FilePathsPanel, Panel): bl_label = "Render" - def draw_props(self, context, _layout): + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + paths = context.preferences.filepaths col = self.layout.column() @@ -1289,7 +1209,11 @@ class USERPREF_PT_file_paths_render(FilePathsPanel, Panel): class USERPREF_PT_file_paths_applications(FilePathsPanel, Panel): bl_label = "Applications" - def draw_props(self, context, layout): + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + paths = context.preferences.filepaths col = layout.column() @@ -1305,14 +1229,18 @@ class USERPREF_PT_file_paths_development(FilePathsPanel, Panel): @classmethod def poll(cls, context): prefs = context.preferences - return (prefs.active_section == 'FILE_PATHS') and prefs.view.show_developer_ui + return prefs.view.show_developer_ui + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False - def draw_props(self, context, layout): paths = context.preferences.filepaths layout.prop(paths, "i18n_branches_directory", text="I18n Branches") -class USERPREF_PT_saveload_autorun(PreferencePanel, Panel): +class USERPREF_PT_saveload_autorun(FilePathsPanel, Panel): bl_label = "Auto Run Python Scripts" bl_parent_id = "USERPREF_PT_saveload_blend" @@ -1343,15 +1271,19 @@ class USERPREF_PT_saveload_autorun(PreferencePanel, Panel): row.operator("wm.userpref_autoexec_path_remove", text="", icon='X', emboss=False).index = i -class USERPREF_PT_saveload_blend(PreferencePanel, Panel): - bl_label = "Blend Files" +# ----------------------------------------------------------------------------- +# Save/Load Panels - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'SAVE_LOAD') +class SaveLoadPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "save_load" + + +class USERPREF_PT_saveload_blend(SaveLoadPanel, CenterAlignMixIn, Panel): + bl_label = "Blend Files" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences paths = prefs.filepaths view = prefs.view @@ -1373,11 +1305,11 @@ class USERPREF_PT_saveload_blend(PreferencePanel, Panel): flow.prop(paths, "recent_files") -class USERPREF_PT_saveload_blend_autosave(PreferencePanel, Panel): +class USERPREF_PT_saveload_blend_autosave(SaveLoadPanel, CenterAlignMixIn, Panel): bl_label = "Auto Save" bl_parent_id = "USERPREF_PT_saveload_blend" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences paths = prefs.filepaths @@ -1389,15 +1321,10 @@ class USERPREF_PT_saveload_blend_autosave(PreferencePanel, Panel): sub.prop(paths, "auto_save_time", text="Timer (mins)") -class USERPREF_PT_saveload_file_browser(PreferencePanel, Panel): +class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel): bl_label = "File Browser" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'SAVE_LOAD') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences paths = prefs.filepaths @@ -1409,85 +1336,19 @@ class USERPREF_PT_saveload_file_browser(PreferencePanel, Panel): flow.prop(paths, "hide_system_bookmarks") -class USERPREF_PT_ndof_settings(Panel): - bl_label = "3D Mouse Settings" - bl_space_type = 'TOPBAR' # dummy. - bl_region_type = 'HEADER' - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - input_prefs = context.preferences.inputs - - is_view3d = context.space_data.type == 'VIEW_3D' - - col = layout.column(align=True) - col.prop(input_prefs, "ndof_sensitivity") - col.prop(input_prefs, "ndof_orbit_sensitivity") - col.prop(input_prefs, "ndof_deadzone") - - if is_view3d: - layout.separator() - layout.prop(input_prefs, "ndof_show_guide") - - layout.separator() - layout.label(text="Orbit Style") - layout.row().prop(input_prefs, "ndof_view_navigate_method", text="Navigate") - layout.row().prop(input_prefs, "ndof_view_rotate_method", text="Orbit") - layout.separator() - - layout.label(text="Orbit Options") - split = layout.split(factor=0.6) - row = split.row() - row.alignment = 'RIGHT' - row.label(text="Invert Axis") - row = split.row(align=True) - for text, attr in ( - ("X", "ndof_rotx_invert_axis"), - ("Y", "ndof_roty_invert_axis"), - ("Z", "ndof_rotz_invert_axis"), - ): - row.prop(input_prefs, attr, text=text, toggle=True) - - # view2d use pan/zoom - layout.separator() - layout.label(text="Pan Options") - - split = layout.split(factor=0.6) - row = split.row() - row.alignment = 'RIGHT' - row.label(text="Invert Axis") - row = split.row(align=True) - for text, attr in ( - ("X", "ndof_panx_invert_axis"), - ("Y", "ndof_pany_invert_axis"), - ("Z", "ndof_panz_invert_axis"), - ): - row.prop(input_prefs, attr, text=text, toggle=True) +# ----------------------------------------------------------------------------- +# Input Panels - layout.prop(input_prefs, "ndof_pan_yz_swap_axis") - - layout.label(text="Zoom Options") - layout.prop(input_prefs, "ndof_zoom_invert") - - if is_view3d: - layout.separator() - layout.label(text="Fly/Walk Options") - layout.prop(input_prefs, "ndof_fly_helicopter") - layout.prop(input_prefs, "ndof_lock_horizon") +class InputPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "input" -class USERPREF_PT_input_keyboard(PreferencePanel, Panel): +class USERPREF_PT_input_keyboard(InputPanel, CenterAlignMixIn, Panel): bl_label = "Keyboard" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INPUT') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences inputs = prefs.inputs @@ -1495,15 +1356,10 @@ class USERPREF_PT_input_keyboard(PreferencePanel, Panel): layout.prop(inputs, "use_numeric_input_advanced") -class USERPREF_PT_input_mouse(PreferencePanel, Panel): +class USERPREF_PT_input_mouse(InputPanel, CenterAlignMixIn, Panel): bl_label = "Mouse" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'INPUT') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): import sys prefs = context.preferences inputs = prefs.inputs @@ -1524,15 +1380,63 @@ class USERPREF_PT_input_mouse(PreferencePanel, Panel): flow.prop(inputs, "move_threshold") -class USERPREF_PT_navigation_orbit(PreferencePanel, Panel): - bl_label = "Orbit & Pan" +class USERPREF_PT_input_tablet(InputPanel, CenterAlignMixIn, Panel): + bl_label = "Tablet" + + def draw_centered(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + import sys + if sys.platform[:3] == "win": + 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") + + +class USERPREF_PT_input_ndof(InputPanel, CenterAlignMixIn, Panel): + bl_label = "NDOF" + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): prefs = context.preferences - return (prefs.active_section == 'NAVIGATION') + inputs = prefs.inputs + return inputs.use_ndof + + def draw_centered(self, context, layout): + prefs = context.preferences + inputs = prefs.inputs + + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + + flow.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity") + flow.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity") + flow.prop(inputs, "ndof_deadzone", text="Deadzone") - def draw_props(self, context, layout): + layout.separator() + + flow.row().prop(inputs, "ndof_view_navigate_method", expand=True) + flow.row().prop(inputs, "ndof_view_rotate_method", expand=True) + + +# ----------------------------------------------------------------------------- +# Navigation Panels + +class NavigationPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "navigation" + + +class USERPREF_PT_navigation_orbit(NavigationPanel, CenterAlignMixIn, Panel): + bl_label = "Orbit & Pan" + + def draw_centered(self, context, layout): import sys prefs = context.preferences inputs = prefs.inputs @@ -1558,15 +1462,10 @@ class USERPREF_PT_navigation_orbit(PreferencePanel, Panel): flow.prop(view, "rotation_angle") -class USERPREF_PT_navigation_zoom(PreferencePanel, Panel): +class USERPREF_PT_navigation_zoom(NavigationPanel, CenterAlignMixIn, Panel): bl_label = "Zoom" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'NAVIGATION') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences inputs = prefs.inputs @@ -1582,15 +1481,10 @@ class USERPREF_PT_navigation_zoom(PreferencePanel, Panel): flow.prop(inputs, "use_zoom_to_mouse") -class USERPREF_PT_navigation_fly_walk(PreferencePanel, Panel): +class USERPREF_PT_navigation_fly_walk(NavigationPanel, CenterAlignMixIn, Panel): bl_label = "Fly & Walk" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'NAVIGATION') - - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences inputs = prefs.inputs @@ -1600,7 +1494,7 @@ class USERPREF_PT_navigation_fly_walk(PreferencePanel, Panel): flow.prop(inputs, "use_camera_lock_parent") -class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel, Panel): +class USERPREF_PT_navigation_fly_walk_navigation(NavigationPanel, CenterAlignMixIn, Panel): bl_label = "Walk" bl_parent_id = "USERPREF_PT_navigation_fly_walk" bl_options = {'DEFAULT_CLOSED'} @@ -1610,7 +1504,7 @@ class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel, Panel): prefs = context.preferences return prefs.inputs.navigation_mode == 'WALK' - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences inputs = prefs.inputs walk = inputs.walk_navigation @@ -1626,7 +1520,7 @@ class USERPREF_PT_navigation_fly_walk_navigation(PreferencePanel, Panel): sub.prop(walk, "walk_speed_factor") -class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel, Panel): +class USERPREF_PT_navigation_fly_walk_gravity(NavigationPanel, CenterAlignMixIn, Panel): bl_label = "Gravity" bl_parent_id = "USERPREF_PT_navigation_fly_walk" bl_options = {'DEFAULT_CLOSED'} @@ -1643,7 +1537,7 @@ class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel, Panel): self.layout.prop(walk, "use_gravity", text="") - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences inputs = prefs.inputs walk = inputs.walk_navigation @@ -1656,53 +1550,84 @@ class USERPREF_PT_navigation_fly_walk_gravity(PreferencePanel, Panel): flow.prop(walk, "jump_height") -class USERPREF_PT_input_tablet(PreferencePanel, Panel): - bl_label = "Tablet" +# Special case, this is only exposed as a popover. +class USERPREF_PT_ndof_settings(Panel): + bl_label = "3D Mouse Settings" + bl_space_type = 'TOPBAR' # dummy. + bl_region_type = 'HEADER' - @classmethod - def poll(cls, context): - prefs = context.preferences - return prefs.active_section == 'INPUT' + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. - def draw_props(self, context, layout): - prefs = context.preferences - inputs = prefs.inputs + input_prefs = context.preferences.inputs - import sys - if sys.platform[:3] == "win": - layout.prop(inputs, "tablet_api") + is_view3d = context.space_data.type == 'VIEW_3D' + + col = layout.column(align=True) + col.prop(input_prefs, "ndof_sensitivity") + col.prop(input_prefs, "ndof_orbit_sensitivity") + col.prop(input_prefs, "ndof_deadzone") + + if is_view3d: layout.separator() + layout.prop(input_prefs, "ndof_show_guide") - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + layout.separator() + layout.label(text="Orbit Style") + layout.row().prop(input_prefs, "ndof_view_navigate_method", text="Navigate") + layout.row().prop(input_prefs, "ndof_view_rotate_method", text="Orbit") + layout.separator() - flow.prop(inputs, "pressure_threshold_max") - flow.prop(inputs, "pressure_softness") + layout.label(text="Orbit Options") + split = layout.split(factor=0.6) + row = split.row() + row.alignment = 'RIGHT' + row.label(text="Invert Axis") + row = split.row(align=True) + for text, attr in ( + ("X", "ndof_rotx_invert_axis"), + ("Y", "ndof_roty_invert_axis"), + ("Z", "ndof_rotz_invert_axis"), + ): + row.prop(input_prefs, attr, text=text, toggle=True) + # view2d use pan/zoom + layout.separator() + layout.label(text="Pan Options") -class USERPREF_PT_input_ndof(PreferencePanel, Panel): - bl_label = "NDOF" - bl_options = {'DEFAULT_CLOSED'} + split = layout.split(factor=0.6) + row = split.row() + row.alignment = 'RIGHT' + row.label(text="Invert Axis") + row = split.row(align=True) + for text, attr in ( + ("X", "ndof_panx_invert_axis"), + ("Y", "ndof_pany_invert_axis"), + ("Z", "ndof_panz_invert_axis"), + ): + row.prop(input_prefs, attr, text=text, toggle=True) - @classmethod - def poll(cls, context): - prefs = context.preferences - inputs = prefs.inputs - return prefs.active_section == 'INPUT' and inputs.use_ndof + layout.prop(input_prefs, "ndof_pan_yz_swap_axis") - def draw_props(self, context, layout): - prefs = context.preferences - inputs = prefs.inputs + layout.label(text="Zoom Options") + layout.prop(input_prefs, "ndof_zoom_invert") - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + if is_view3d: + layout.separator() + layout.label(text="Fly/Walk Options") + layout.prop(input_prefs, "ndof_fly_helicopter") + layout.prop(input_prefs, "ndof_lock_horizon") - flow.prop(inputs, "ndof_sensitivity", text="Pan Sensitivity") - flow.prop(inputs, "ndof_orbit_sensitivity", text="Orbit Sensitivity") - flow.prop(inputs, "ndof_deadzone", text="Deadzone") - layout.separator() +# ----------------------------------------------------------------------------- +# Key-Map Editor Panels - flow.row().prop(inputs, "ndof_view_navigate_method", expand=True) - flow.row().prop(inputs, "ndof_view_rotate_method", expand=True) +class KeymapPanel: + bl_space_type = 'PREFERENCES' + bl_region_type = 'WINDOW' + bl_context = "keymap" class USERPREF_MT_keyconfigs(Menu): @@ -1714,17 +1639,10 @@ class USERPREF_MT_keyconfigs(Menu): Menu.draw_preset(self, context) -class USERPREF_PT_keymap(Panel): - bl_space_type = 'PREFERENCES' +class USERPREF_PT_keymap(KeymapPanel, Panel): bl_label = "Keymap" - bl_region_type = 'WINDOW' bl_options = {'HIDE_HEADER'} - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'KEYMAP') - def draw(self, context): from rna_keymap_ui import draw_keymaps @@ -1740,10 +1658,17 @@ class USERPREF_PT_keymap(Panel): # print("runtime", time.time() - start) -class USERPREF_PT_addons(Panel): +# ----------------------------------------------------------------------------- +# Add-On Panels + +class AddOnPanel: bl_space_type = 'PREFERENCES' - bl_label = "Add-ons" bl_region_type = 'WINDOW' + bl_context = "addons" + + +class USERPREF_PT_addons(AddOnPanel, Panel): + bl_label = "Add-ons" bl_options = {'HIDE_HEADER'} _support_icon_mapping = { @@ -1752,11 +1677,6 @@ class USERPREF_PT_addons(Panel): 'TESTING': 'EXPERIMENTAL', } - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'ADDONS') - @staticmethod def is_user_addon(mod, user_addon_paths): import os @@ -2027,14 +1947,16 @@ class USERPREF_PT_addons(Panel): row.label(text=module_name, translate=False) -class StudioLightPanelMixin(): +# ----------------------------------------------------------------------------- +# Studio Light Panels + +class StudioLightPanel: bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' + bl_context = "lights" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'LIGHTS') + +class StudioLightPanelMixin: def _get_lights(self, prefs): return [light for light in prefs.studio_lights if light.is_user_defined and light.type == self.sl_type] @@ -2070,7 +1992,7 @@ class StudioLightPanelMixin(): box.label(text=studio_light.name) -class USERPREF_PT_studiolight_matcaps(Panel, StudioLightPanelMixin): +class USERPREF_PT_studiolight_matcaps(StudioLightPanel, StudioLightPanelMixin, Panel): bl_label = "MatCaps" sl_type = 'MATCAP' @@ -2080,7 +2002,7 @@ class USERPREF_PT_studiolight_matcaps(Panel, StudioLightPanelMixin): layout.separator() -class USERPREF_PT_studiolight_world(Panel, StudioLightPanelMixin): +class USERPREF_PT_studiolight_world(StudioLightPanel, StudioLightPanelMixin, Panel): bl_label = "LookDev HDRIs" sl_type = 'WORLD' @@ -2090,7 +2012,7 @@ class USERPREF_PT_studiolight_world(Panel, StudioLightPanelMixin): layout.separator() -class USERPREF_PT_studiolight_lights(Panel, StudioLightPanelMixin): +class USERPREF_PT_studiolight_lights(StudioLightPanel, StudioLightPanelMixin, Panel): bl_label = "Studio Lights" sl_type = 'STUDIO' @@ -2102,14 +2024,13 @@ class USERPREF_PT_studiolight_lights(Panel, StudioLightPanelMixin): layout.separator() -class USERPREF_PT_studiolight_light_editor(Panel): +class USERPREF_PT_studiolight_light_editor(StudioLightPanel, Panel): bl_label = "Editor" bl_parent_id = "USERPREF_PT_studiolight_lights" - bl_space_type = 'PREFERENCES' - bl_region_type = 'WINDOW' bl_options = {'DEFAULT_CLOSED'} - def opengl_light_buttons(self, layout, light): + @staticmethod + def opengl_light_buttons(layout, light): col = layout.column() col.active = light.use @@ -2156,33 +2077,28 @@ class USERPREF_PT_studiolight_light_editor(Panel): layout.prop(system, "light_ambient") +# ----------------------------------------------------------------------------- +# Experimental Panels + class ExperimentalPanel: bl_space_type = 'PREFERENCES' bl_region_type = 'WINDOW' + bl_context = "experimental" url_prefix = "https://developer.blender.org/" - @classmethod - def poll(cls, context): - prefs = context.preferences - return (prefs.active_section == 'EXPERIMENTAL') - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - self.draw_props(context, layout) - class USERPREF_PT_experimental_ui(ExperimentalPanel, Panel): bl_label = "User Interface" - def draw_props(self, context, layout): + def draw(self, context): prefs = context.preferences experimental = prefs.experimental + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + task = "T66304" split = layout.split(factor=0.66) col = split.column() @@ -2198,7 +2114,7 @@ class USERPREF_PT_experimental_ui(ExperimentalPanel, Panel): class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): bl_label = "Virtual Reality" - def draw_props(self, context, layout): + def draw_centered(self, context, layout): prefs = context.preferences experimental = prefs.experimental @@ -2218,6 +2134,33 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): """ +class USERPREF_PT_experimental_usd(ExperimentalPanel, Panel): + bl_label = "Universal Scene Description" + + @classmethod + def poll(cls, context): + # Only show the panel if Blender was actually built with USD support. + return getattr(bpy.app.build_options, "usd", False) + + def draw(self, context): + prefs = context.preferences + experimental = prefs.experimental + + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + split = layout.split(factor=0.66) + col = split.split() + col.prop(experimental, "use_usd_exporter", text="USD Exporter") + col = split.split() + url = "https://devtalk.blender.org/t/universal-scene-description-usd-exporter-feedback/10920" + col.operator("wm.url_open", text='Give Feedback', icon='URL').url = url + + +# ----------------------------------------------------------------------------- +# Class Registration + # Order of registration defines order in UI, # so dynamically generated classes are 'injected' in the intended order. classes = ( @@ -2300,6 +2243,7 @@ classes = ( USERPREF_PT_studiolight_world, USERPREF_PT_experimental_ui, + USERPREF_PT_experimental_usd, # Popovers. USERPREF_PT_ndof_settings, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index cebfdf9196c..48e40554cfb 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -25,6 +25,7 @@ from bpy.types import ( ) from bl_ui.properties_paint_common import ( UnifiedPaintPanel, + brush_basic_texpaint_settings, ) from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, @@ -67,7 +68,6 @@ class VIEW3D_HT_tool_header(Header): context, layout, tool_key=('VIEW_3D', tool_mode), ) - # Object Mode Options # ------------------- @@ -77,23 +77,31 @@ class VIEW3D_HT_tool_header(Header): # (obviously separated for from the users POV) draw_fn = getattr(_draw_tool_settings_context_mode, tool_mode, None) if draw_fn is not None: - draw_fn(context, layout, tool) - - popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"} + is_valid_context = draw_fn(context, layout, tool) + + def draw_3d_brush_settings(layout, tool_mode): + layout.popover("VIEW3D_PT_tools_brush_settings_advanced", text="Brush") + if tool_mode != 'PAINT_WEIGHT': + layout.popover("VIEW3D_PT_tools_brush_texture") + if tool_mode == 'PAINT_TEXTURE': + layout.popover("VIEW3D_PT_tools_mask_texture") + layout.popover("VIEW3D_PT_tools_brush_stroke") + layout.popover("VIEW3D_PT_tools_brush_falloff") + layout.popover("VIEW3D_PT_tools_brush_display") # Note: general mode options should be added to 'draw_mode_settings'. if tool_mode == 'SCULPT': - if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + if is_valid_context: + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'PAINT_VERTEX': - if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + if is_valid_context: + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'PAINT_WEIGHT': - if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + if is_valid_context: + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'PAINT_TEXTURE': - if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".paint_common", **popover_kw) + if is_valid_context: + draw_3d_brush_settings(layout, tool_mode) elif tool_mode == 'EDIT_ARMATURE': pass elif tool_mode == 'EDIT_CURVE': @@ -108,12 +116,24 @@ class VIEW3D_HT_tool_header(Header): # layout.popover_group(context=".paint_common", **popover_kw) pass elif tool_mode == 'PAINT_GPENCIL': - if (tool is not None) and tool.has_datablock: - layout.popover_group(context=".greasepencil_paint", **popover_kw) + 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 != 'FILL': + 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': - layout.popover_group(context=".greasepencil_sculpt", **popover_kw) + 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") elif tool_mode == 'WEIGHT_GPENCIL': - layout.popover_group(context=".greasepencil_weight", **popover_kw) + layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance") def draw_mode_settings(self, context): layout = self.layout @@ -221,80 +241,138 @@ class _draw_tool_settings_context_mode: @staticmethod def SCULPT(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False paint = context.tool_settings.sculpt layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush if brush is None: - return + return False - from bl_ui.properties_paint_common import ( - brush_basic_sculpt_settings, + tool_settings = context.tool_settings + capabilities = brush.sculpt_capabilities + + ups = tool_settings.unified_paint_settings + + size = "size" + size_owner = ups if ups.use_unified_size else brush + if size_owner.use_locked_size == 'SCENE': + size = "unprojected_radius" + + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + size, + pressure_name="use_pressure_size", + unified_name="use_unified_size", + text="Radius", + slider=True, + header=True + ) + + # strength, use_strength_pressure + pressure_name = "use_pressure_strength" if capabilities.has_strength_pressure else None + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + pressure_name=pressure_name, + unified_name="use_unified_strength", + text="Strength", + header=True ) - brush_basic_sculpt_settings(layout, context, brush, compact=True) + + # direction + if not capabilities.has_direction: + layout.row().prop(brush, "direction", expand=True, text="") + + return True @staticmethod def PAINT_TEXTURE(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False paint = context.tool_settings.image_paint layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush if brush is None: - return + return False - from bl_ui.properties_paint_common import ( - UnifiedPaintPanel, - brush_basic_texpaint_settings, - ) - capabilities = brush.image_paint_capabilities - if capabilities.has_color: - UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") brush_basic_texpaint_settings(layout, context, brush, compact=True) + return True + @staticmethod def PAINT_VERTEX(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False paint = context.tool_settings.vertex_paint layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush if brush is None: - return + return False - from bl_ui.properties_paint_common import ( - UnifiedPaintPanel, - brush_basic_vpaint_settings, - ) - capabilities = brush.vertex_paint_capabilities - if capabilities.has_color: - UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text="") - brush_basic_vpaint_settings(layout, context, brush, compact=True) + brush_basic_texpaint_settings(layout, context, brush, compact=True) + + return True @staticmethod def PAINT_WEIGHT(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False paint = context.tool_settings.weight_paint layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) brush = paint.brush if brush is None: - return + return False + + capabilities = brush.weight_paint_capabilities + if capabilities.has_weight: + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "weight", + unified_name="use_unified_weight", + slider=True, + header=True + ) - from bl_ui.properties_paint_common import brush_basic_wpaint_settings - brush_basic_wpaint_settings(layout, context, brush, compact=True) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + pressure_name="use_pressure_size", + unified_name="use_unified_size", + slider=True, + text="Radius", + header=True + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + pressure_name="use_pressure_strength", + unified_name="use_unified_strength", + header=True + ) + + return True @staticmethod def PAINT_GPENCIL(context, layout, tool): if tool is None: - return + return False # is_paint = True # FIXME: tools must use their own UI drawing! @@ -311,14 +389,14 @@ class _draw_tool_settings_context_mode: elif tool.idname == "Cutter": row = layout.row(align=True) row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") - return + return False elif not tool.has_datablock: - return + return False paint = context.tool_settings.gpencil_paint brush = paint.brush if brush is None: - return + return False gp_settings = brush.gpencil_settings @@ -358,12 +436,14 @@ class _draw_tool_settings_context_mode: from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, ) - brush_basic_gpencil_paint_settings(layout, context, brush, tool, compact=True, is_toolbar=True) + brush_basic_gpencil_paint_settings(layout, context, brush, compact=True) + + return True @staticmethod def SCULPT_GPENCIL(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False tool_settings = context.tool_settings settings = tool_settings.gpencil_sculpt brush = settings.brush @@ -373,10 +453,12 @@ class _draw_tool_settings_context_mode: ) brush_basic_gpencil_sculpt_settings(layout, context, brush, compact=True) + return True + @staticmethod def WEIGHT_GPENCIL(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False tool_settings = context.tool_settings settings = tool_settings.gpencil_sculpt brush = settings.brush @@ -386,38 +468,44 @@ class _draw_tool_settings_context_mode: ) brush_basic_gpencil_weight_settings(layout, context, brush, compact=True) + return True + @staticmethod def PARTICLE(context, layout, tool): if (tool is None) or (not tool.has_datablock): - return + return False # See: 'VIEW3D_PT_tools_brush', basically a duplicate settings = context.tool_settings.particle_edit brush = settings.brush tool = settings.tool - if tool != 'NONE': - layout.prop(brush, "size", slider=True) - if tool == 'ADD': - layout.prop(brush, "count") + if tool == 'NONE': + return False - layout.prop(settings, "use_default_interpolate") - layout.prop(brush, "steps", slider=True) - layout.prop(settings, "default_key_count", slider=True) - else: - layout.prop(brush, "strength", slider=True) - - if tool == 'LENGTH': - layout.row().prop(brush, "length_mode", expand=True) - elif tool == 'PUFF': - layout.row().prop(brush, "puff_mode", expand=True) - layout.prop(brush, "use_puff_volume") - elif tool == 'COMB': - row = layout.row() - row.active = settings.is_editable - row.prop(settings, "use_emitter_deflect", text="Deflect Emitter") - sub = row.row(align=True) - sub.active = settings.use_emitter_deflect - sub.prop(settings, "emitter_distance", text="Distance") + layout.prop(brush, "size", slider=True) + if tool == 'ADD': + layout.prop(brush, "count") + + layout.prop(settings, "use_default_interpolate") + layout.prop(brush, "steps", slider=True) + layout.prop(settings, "default_key_count", slider=True) + else: + layout.prop(brush, "strength", slider=True) + + if tool == 'LENGTH': + layout.row().prop(brush, "length_mode", expand=True) + elif tool == 'PUFF': + layout.row().prop(brush, "puff_mode", expand=True) + layout.prop(brush, "use_puff_volume") + elif tool == 'COMB': + row = layout.row() + row.active = settings.is_editable + row.prop(settings, "use_emitter_deflect", text="Deflect Emitter") + sub = row.row(align=True) + sub.active = settings.use_emitter_deflect + sub.prop(settings, "emitter_distance", text="Distance") + + return True class VIEW3D_HT_header(Header): @@ -467,12 +555,11 @@ class VIEW3D_HT_header(Header): show_snap = True else: - from bl_ui.properties_paint_common import UnifiedPaintPanel paint_settings = UnifiedPaintPanel.paint_settings(context) if paint_settings: brush = paint_settings.brush - if brush and brush.stroke_method == 'CURVE': + if brush and hasattr(brush, "stroke_method") and brush.stroke_method == 'CURVE': show_snap = True if show_snap: @@ -1042,6 +1129,12 @@ class VIEW3D_MT_view(Menu): layout.operator("render.opengl", text="Viewport Render Image", icon='RENDER_STILL') layout.operator("render.opengl", text="Viewport Render Animation", icon='RENDER_ANIMATION').animation = True + props = layout.operator("render.opengl", + text="Viewport Render Keyframes", + icon='RENDER_ANIMATION', + ) + props.animation = True + props.render_keyed_only = True layout.separator() @@ -2591,7 +2684,7 @@ class VIEW3D_MT_object_quick_effects(Menu): layout.operator("object.quick_fur") layout.operator("object.quick_explode") layout.operator("object.quick_smoke") - layout.operator("object.quick_fluid") + layout.operator("object.quick_liquid") class VIEW3D_MT_object_showhide(Menu): @@ -5474,9 +5567,10 @@ class VIEW3D_PT_shading_render_pass(Panel): @classmethod def poll(cls, context): - return (context.space_data.shading.type == 'MATERIAL' - or (context.engine in cls.COMPAT_ENGINES - and context.space_data.shading.type == 'RENDERED')) + return ( + (context.space_data.shading.type == 'MATERIAL') or + (context.engine in cls.COMPAT_ENGINES and context.space_data.shading.type == 'RENDERED') + ) def draw(self, context): shading = context.space_data.shading @@ -6652,6 +6746,7 @@ class VIEW3D_PT_paint_vertex_context_menu(Panel): def draw(self, context): layout = self.layout + brush = context.tool_settings.vertex_paint.brush capabilities = brush.vertex_paint_capabilities @@ -6661,8 +6756,24 @@ class VIEW3D_PT_paint_vertex_context_menu(Panel): UnifiedPaintPanel.prop_unified_color_picker(split, context, brush, "color", value_slider=True) layout.prop(brush, "blend", text="") - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + unified_name="use_unified_size", + pressure_name="use_pressure_size", + slider=True, + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + pressure_name="use_pressure_strength", + slider=True, + ) class VIEW3D_PT_paint_texture_context_menu(Panel): @@ -6673,6 +6784,7 @@ class VIEW3D_PT_paint_texture_context_menu(Panel): def draw(self, context): layout = self.layout + brush = context.tool_settings.image_paint.brush capabilities = brush.image_paint_capabilities @@ -6683,8 +6795,24 @@ class VIEW3D_PT_paint_texture_context_menu(Panel): layout.prop(brush, "blend", text="") if capabilities.has_radius: - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + unified_name="use_unified_size", + pressure_name="use_pressure_size", + slider=True, + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + pressure_name="use_pressure_strength", + slider=True, + ) class VIEW3D_PT_paint_weight_context_menu(Panel): @@ -6697,9 +6825,32 @@ class VIEW3D_PT_paint_weight_context_menu(Panel): layout = self.layout brush = context.tool_settings.weight_paint.brush - UnifiedPaintPanel.prop_unified_weight(layout, context, brush, "weight", slider=True) - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "weight", + unified_name="use_unified_weight", + slider=True, + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + unified_name="use_unified_size", + pressure_name="use_pressure_size", + slider=True, + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + pressure_name="use_pressure_strength", + slider=True, + ) class VIEW3D_PT_sculpt_context_menu(Panel): @@ -6714,8 +6865,24 @@ class VIEW3D_PT_sculpt_context_menu(Panel): brush = context.tool_settings.sculpt.brush capabilities = brush.sculpt_capabilities - UnifiedPaintPanel.prop_unified_size(layout, context, brush, "size", slider=True) - UnifiedPaintPanel.prop_unified_strength(layout, context, brush, "strength") + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + unified_name="use_unified_size", + pressure_name="use_pressure_size", + slider=True, + ) + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + pressure_name="use_pressure_strength", + slider=True, + ) if capabilities.has_auto_smooth: layout.prop(brush, "auto_smooth_factor", slider=True) diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 718365ec99d..f0c4aaa9344 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -19,26 +19,30 @@ # <pep8 compliant> from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( - GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel, GreasePencilSculptOptionsPanel, - GreasePencilAppearancePanel, + GreasePencilDisplayPanel, ) from bl_ui.properties_paint_common import ( UnifiedPaintPanel, - brush_mask_texture_settings, - brush_texpaint_common, - brush_texpaint_common_color, - brush_texpaint_common_gradient, - brush_texpaint_common_clone, - brush_texpaint_common_options, + BrushSelectPanel, + ClonePanel, + TextureMaskPanel, + ColorPalettePanel, + StrokePanel, + SmoothStrokePanel, + FalloffPanel, + DisplayPanel, brush_texture_settings, + brush_mask_texture_settings, + brush_settings, + brush_settings_advanced, + draw_color_settings, ) from bl_ui.utils import PresetPanel class VIEW3D_MT_brush_context_menu(Menu): - bl_label = "Material Specials" + bl_label = "Brush Specials" def draw(self, context): layout = self.layout @@ -110,9 +114,8 @@ def draw_vpaint_symmetry(layout, vpaint): col.use_property_decorate = False col.prop(vpaint, "radial_symmetry", text="Radial") -# Most of these panels should not be visible in GP edit modes - +# Most of these panels should not be visible in GP edit modes def is_not_gpencil_edit_mode(context): is_gpmode = ( context.active_object and @@ -319,26 +322,42 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, Panel): # ********** default tools for paint modes **************** -class View3DPaintPanel(UnifiedPaintPanel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' +class TEXTURE_UL_texpaintslots(UIList): + def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): + # mat = data + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(item, "name", text="", emboss=False, icon_value=icon) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="") + + +class View3DPaintPanel(View3DPanel, UnifiedPaintPanel): bl_category = "Tool" +class View3DPaintBrushPanel(View3DPaintPanel): + @classmethod + def poll(cls, context): + mode = cls.get_brush_mode(context) + return mode is not None + + class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Particle tools" + bl_label = "Particle Tool" bl_options = {'HIDE_HEADER'} @classmethod def poll(cls, context): - settings = cls.paint_settings(context) + settings = context.tool_settings.particle_edit return (settings and settings.brush and context.particle_edit_object) def draw(self, context): layout = self.layout - settings = self.paint_settings(context) + settings = context.tool_settings.particle_edit brush = settings.brush tool = settings.tool @@ -371,19 +390,20 @@ class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Brush" +class VIEW3D_PT_tools_brush_select(Panel, View3DPaintBrushPanel, BrushSelectPanel): + bl_context = ".paint_common" + bl_label = "Brushes" + + +# TODO, move to space_view3d.py +class VIEW3D_PT_tools_brush_settings(Panel, View3DPaintBrushPanel): + bl_context = ".paint_common" + bl_label = "Brush Settings" @classmethod def poll(cls, context): settings = cls.paint_settings(context) - return (settings and - settings.brush and - (context.sculpt_object or - context.vertex_paint_object or - context.weight_paint_object or - context.image_paint_object)) + return settings and settings.brush is not None def draw(self, context): layout = self.layout @@ -394,309 +414,67 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): settings = self.paint_settings(context) brush = settings.brush - if not self.is_popover: - row = layout.row() - row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8) - row.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") - - # Sculpt Mode # - if context.sculpt_object and brush: - from bl_ui.properties_paint_common import ( - brush_basic_sculpt_settings, - ) - - capabilities = brush.sculpt_capabilities - - col = layout.column() - - if not self.is_popover: - brush_basic_sculpt_settings(col, context, brush) - - # normal_radius_factor - col.separator() - row = col.row() - row.prop(brush, "normal_radius_factor", slider=True) - - if brush.sculpt_tool == 'ELASTIC_DEFORM': - col.separator() - row = col.row() - row.prop(brush, "elastic_deform_type") - row = col.row() - row.prop(brush, "elastic_deform_volume_preservation", slider=True) - elif brush.sculpt_tool == 'POSE': - row = col.row() - row.prop(brush, "pose_offset") - row = col.row() - row.prop(brush, "pose_smooth_iterations") - elif brush.sculpt_tool == 'SCRAPE': - row = col.row() - row.prop(brush, "invert_to_scrape_fill", text = "Invert to Fill") - elif brush.sculpt_tool == 'FILL': - row = col.row() - row.prop(brush, "invert_to_scrape_fill", text = "Invert to Scrape") - elif brush.sculpt_tool == 'GRAB': - col.separator() - row = col.row() - row.prop(brush, "use_grab_active_vertex") - elif brush.sculpt_tool == 'MULTIPLANE_SCRAPE': - row = col.row() - row.prop(brush, "multiplane_scrape_angle") - row = col.row() - row.prop(brush, "use_multiplane_scrape_dynamic") - row = col.row() - row.prop(brush, "show_multiplane_scrape_planes_preview") - - # topology_rake_factor - if ( - capabilities.has_topology_rake and - context.sculpt_object.use_dynamic_topology_sculpting - ): - row = col.row() - row.prop(brush, "topology_rake_factor", slider=True) - - # auto_smooth_factor and use_inverse_smooth_pressure - if capabilities.has_auto_smooth: - row = col.row(align=True) - row.prop(brush, "auto_smooth_factor", slider=True) - row.prop(brush, "use_inverse_smooth_pressure", toggle=True, text="") - - # normal_weight - if capabilities.has_normal_weight: - row = col.row(align=True) - row.prop(brush, "normal_weight", slider=True) - - # crease_pinch_factor - if capabilities.has_pinch_factor: - row = col.row(align=True) - if brush.sculpt_tool in {'BLOB', 'SNAKE_HOOK'}: - row.prop(brush, "crease_pinch_factor", slider=True, text="Magnify") - else: - row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch") - - # rake_factor - if capabilities.has_rake_factor: - row = col.row(align=True) - row.prop(brush, "rake_factor", slider=True) - - if brush.sculpt_tool == 'MASK': - col.prop(brush, "mask_tool") - - # plane_offset, use_offset_pressure, use_plane_trim, plane_trim - if capabilities.has_plane_offset: - row = col.row(align=True) - row.prop(brush, "plane_offset", slider=True) - row.prop(brush, "use_offset_pressure", text="") - - col.separator() - - row = col.row() - row.prop(brush, "use_plane_trim", text="Plane Trim") - row = col.row() - row.active = brush.use_plane_trim - row.prop(brush, "plane_trim", slider=True, text="Distance") - - # height - if capabilities.has_height: - row = col.row() - row.prop(brush, "height", slider=True, text="Height") - - # 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 - - if do_persistent: - col.prop(brush, "use_persistent") - col.operator("sculpt.set_persistent_base") - - # Texture Paint Mode # + brush_settings(layout.column(), context, brush, popover=self.is_popover) - elif context.image_paint_object and brush: - brush_texpaint_common(self, context, layout, brush, settings, projpaint=True) - # Weight Paint Mode # - elif context.weight_paint_object and brush: - from bl_ui.properties_paint_common import ( - brush_basic_wpaint_settings, - ) - - col = layout.column() +class VIEW3D_PT_tools_brush_settings_advanced(Panel, View3DPaintBrushPanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" + bl_label = "Advanced" + bl_options = {'DEFAULT_CLOSED'} - if not self.is_popover: - brush_basic_wpaint_settings(col, context, brush) + def draw(self, context): + layout = self.layout - # Vertex Paint Mode # - elif context.vertex_paint_object and brush: - from bl_ui.properties_paint_common import ( - brush_basic_vpaint_settings, - ) + layout.use_property_split = True + layout.use_property_decorate = False # No animation. - col = layout.column() + settings = UnifiedPaintPanel.paint_settings(context) + brush = settings.brush - if not self.is_popover: - brush_basic_vpaint_settings(col, context, brush) + brush_settings_advanced(layout.column(), context, brush, self.is_popover) class VIEW3D_PT_tools_brush_color(Panel, View3DPaintPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Color Picker" @classmethod def poll(cls, context): settings = cls.paint_settings(context) brush = settings.brush + if context.image_paint_object: capabilities = brush.image_paint_capabilities return capabilities.has_color - elif context.vertex_paint_object: capabilities = brush.vertex_paint_capabilities return capabilities.has_color + return False + def draw(self, context): layout = self.layout settings = self.paint_settings(context) brush = settings.brush - if context.vertex_paint_object: - brush_texpaint_common_color(self, context, layout, brush, settings, projpaint=True) - - else: - layout.prop(brush, "color_type", expand=True) - - if brush.color_type == 'COLOR': - brush_texpaint_common_color(self, context, layout, brush, settings, projpaint=True) - elif brush.color_type == 'GRADIENT': - brush_texpaint_common_gradient(self, context, layout, brush, settings, projpaint=True) + draw_color_settings(context, layout, brush, color_type=not context.vertex_paint_object) -class VIEW3D_PT_tools_brush_swatches(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" +class VIEW3D_PT_tools_brush_swatches(Panel, View3DPaintPanel, ColorPalettePanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Color Palette" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - brush = settings.brush - if context.image_paint_object: - capabilities = brush.image_paint_capabilities - return capabilities.has_color - - elif context.vertex_paint_object: - capabilities = brush.vertex_paint_capabilities - return capabilities.has_color - - def draw(self, context): - layout = self.layout - settings = self.paint_settings(context) - - layout.template_ID(settings, "palette", new="palette.new") - if settings.palette: - layout.template_palette(settings, "palette", color=True) - -class VIEW3D_PT_tools_brush_clone(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" +class VIEW3D_PT_tools_brush_clone(Panel, View3DPaintPanel, ClonePanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Clone from Paint Slot" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - brush = settings.brush - - return brush.image_tool == 'CLONE' - - def draw_header(self, context): - settings = self.paint_settings(context) - self.layout.prop(settings, "use_clone_layer", text="") - - def draw(self, context): - layout = self.layout - settings = self.paint_settings(context) - brush = settings.brush - - layout.active = settings.use_clone_layer - - brush_texpaint_common_clone(self, context, layout, brush, settings, projpaint=True) - - -class VIEW3D_PT_tools_brush_options(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_brush" - bl_label = "Options" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - tool_settings = context.tool_settings - settings = self.paint_settings(context) - brush = settings.brush - capabilities = brush.sculpt_capabilities - - layout.use_property_split = True - layout.use_property_decorate = False # No animation. - - col = layout.column() - - if context.image_paint_object and brush: - brush_texpaint_common_options(self, context, layout, brush, settings, projpaint=True) - - elif context.sculpt_object and brush: - col.prop(brush, "use_automasking_topology") - if capabilities.has_accumulate: - col.prop(brush, "use_accumulate") - - UnifiedPaintPanel.prop_unified_size(col, context, brush, "use_locked_size") - - if capabilities.has_sculpt_plane: - col.prop(brush, "sculpt_plane") - col.prop(brush, "use_original_normal") - col.prop(brush, "use_original_plane") - - col.prop(brush, "use_frontface", text="Front Faces Only") - col.prop(brush, "use_projected") - - elif context.weight_paint_object and brush: - - if brush.weight_tool != 'SMEAR': - col.prop(brush, "use_accumulate") - - col.prop(brush, "use_frontface", text="Front Faces Only") - col.prop(brush, "use_projected") - col.prop(tool_settings, "use_auto_normalize", text="Auto Normalize") - col.prop(tool_settings, "use_multipaint", text="Multi-Paint") - - elif context.vertex_paint_object and brush: - - if brush.vertex_tool != 'SMEAR': - col.prop(brush, "use_accumulate") - - col.prop(brush, "use_alpha") - col.prop(brush, "use_frontface", text="Front Faces Only") - col.prop(brush, "use_projected") - - -class TEXTURE_UL_texpaintslots(UIList): - def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): - # mat = data - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.prop(item, "name", text="", emboss=False, icon_value=icon) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.label(text="") - class VIEW3D_MT_tools_projectpaint_uvlayer(Menu): bl_label = "Clone Layer" @@ -718,8 +496,7 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel): @classmethod def poll(cls, context): brush = context.tool_settings.image_paint.brush - ob = context.active_object - return (brush is not None and ob is not None) + return (brush is not None and context.active_object is not None) def draw(self, context): layout = self.layout @@ -782,9 +559,8 @@ class VIEW3D_PT_slots_projectpaint(View3DPanel, Panel): layout.separator() layout.operator("image.save_all_modified", text="Save All Images", icon='FILE_TICK') -# TODO, move to space_view3d.py - +# TODO, move to space_view3d.py class VIEW3D_PT_stencil_projectpaint(View3DPanel, Panel): bl_category = "Tool" bl_context = ".imagepaint" # dot on purpose (access from topbar) @@ -836,73 +612,17 @@ class VIEW3D_PT_stencil_projectpaint(View3DPanel, Panel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush_display(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Display" +class VIEW3D_PT_tools_brush_display(Panel, View3DPaintBrushPanel, DisplayPanel): + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" + bl_label = "Cursor" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and - settings.brush and - (context.sculpt_object or - context.vertex_paint_object or - context.weight_paint_object or - context.image_paint_object)) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - tex_slot = brush.texture_slot - tex_slot_mask = brush.mask_texture_slot - - col = layout.column() - - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "cursor_overlay_alpha", text="Curve Alpha") - sub.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - row.prop( - brush, "use_cursor_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', - ) - - col.active = brush.brush_capabilities.has_overlay - - if context.image_paint_object or context.sculpt_object or context.vertex_paint_object: - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "texture_overlay_alpha", text="Texture Alpha") - sub.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot.map_mode != 'STENCIL': - row.prop( - brush, "use_primary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', - ) - - if context.image_paint_object: - row = col.row(align=True) - - sub = row.row(align=True) - sub.prop(brush, "mask_overlay_alpha", text="Mask Texture Alpha") - sub.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot_mask.map_mode != 'STENCIL': - row.prop( - brush, "use_secondary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', - ) - # TODO, move to space_view3d.py class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) + bl_context = ".paint_common" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Texture" bl_options = {'DEFAULT_CLOSED'} @@ -926,9 +646,10 @@ class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_mask_texture(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_mask_texture(Panel, View3DPaintPanel, TextureMaskPanel): bl_category = "Tool" bl_context = ".imagepaint" # dot on purpose (access from topbar) + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Texture Mask" bl_options = {'DEFAULT_CLOSED'} @@ -950,151 +671,27 @@ class VIEW3D_PT_tools_mask_texture(Panel, View3DPaintPanel): # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush_stroke(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_brush_stroke(Panel, View3DPaintPanel, StrokePanel): bl_context = ".paint_common" # dot on purpose (access from topbar) bl_label = "Stroke" + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and - settings.brush and - (context.sculpt_object or - context.vertex_paint_object or - context.weight_paint_object or - context.image_paint_object)) - - def draw(self, context): - layout = self.layout - - settings = self.paint_settings(context) - brush = settings.brush - layout.use_property_split = True - layout.use_property_decorate = False - - col = layout.column() - - col.prop(brush, "stroke_method") - - if brush.use_anchor: - col.prop(brush, "use_edge_to_edge", text="Edge To Edge") - - if brush.use_airbrush: - col.prop(brush, "rate", text="Rate", slider=True) - - if brush.use_space: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - row.prop(brush, "use_pressure_spacing", toggle=True, text="") - col.prop(brush, "dash_ratio") - col.prop(brush, "dash_samples") - - if brush.use_line or brush.use_curve: - row = col.row(align=True) - row.prop(brush, "spacing", text="Spacing") - col.prop(brush, "dash_ratio") - col.prop(brush, "dash_samples") - - if brush.use_curve: - col.template_ID(brush, "paint_curve", new="paintcurve.new") - col.operator("paintcurve.draw") - - if context.sculpt_object: - - if brush.sculpt_capabilities.has_space_attenuation: - col.prop(brush, "use_space_attenuation") - - col.prop(brush, "use_scene_spacing") - - if brush.sculpt_capabilities.has_jitter: - - row = col.row(align=True) - if brush.use_relative_jitter: - row.prop(brush, "jitter", slider=True) - else: - row.prop(brush, "jitter_absolute") - row.prop(brush, "use_relative_jitter", icon_only=True) - row.prop(brush, "use_pressure_jitter", toggle=True, text="") - - else: - - row = col.row(align=True) - if brush.use_relative_jitter: - row.prop(brush, "jitter", slider=True) - else: - row.prop(brush, "jitter_absolute") - row.prop(brush, "use_relative_jitter", icon_only=True) - row.prop(brush, "use_pressure_jitter", toggle=True, text="") - - col.prop(settings, "input_samples") - -class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, SmoothStrokePanel): bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Smooth Stroke" + bl_label = "Stabilize Stroke" bl_parent_id = "VIEW3D_PT_tools_brush_stroke" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - brush = settings.brush - if brush.brush_capabilities.has_smooth_stroke: - return True - - def draw_header(self, context): - settings = self.paint_settings(context) - brush = settings.brush - - self.layout.prop(brush, "use_smooth_stroke", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column() - col.active = brush.use_smooth_stroke - col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) - # TODO, move to space_view3d.py -class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel): +class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) + bl_parent_id = "VIEW3D_PT_tools_brush_settings" bl_label = "Falloff" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - settings = cls.paint_settings(context) - return (settings and settings.brush and settings.brush.curve) - - def draw(self, context): - layout = self.layout - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column(align=True) - row = col.row(align=True) - row.prop(brush, "curve_preset", text="") - - if brush.curve_preset == 'CUSTOM': - layout.template_curve_mapping(brush, "curve", brush=True) - - 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 VIEW3D_PT_tools_brush_falloff_frontface(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) @@ -1264,9 +861,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): col.prop(mesh, "use_remesh_preserve_paint_mask") col.operator("object.voxel_remesh", text="Remesh") -# TODO, move to space_view3d.py - +# TODO, move to space_view3d.py class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_label = "Options" @@ -1294,23 +890,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "use_deform_only") -class VIEW3D_PT_sculpt_options_unified(Panel, View3DPaintPanel): - bl_context = ".sculpt_mode" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_sculpt_options" - bl_label = "Unified Brush" - - @classmethod - def poll(cls, context): - return (context.sculpt_object and context.tool_settings.sculpt) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - self.unified_paint_settings(layout, context) - - class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_parent_id = "VIEW3D_PT_sculpt_options" @@ -1409,64 +988,6 @@ class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel): draw = VIEW3D_PT_sculpt_symmetry.draw -class VIEW3D_PT_tools_brush_display_show_brush(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Show Brush" - bl_parent_id = "VIEW3D_PT_tools_brush_display" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - settings = self.paint_settings(context) - - self.layout.prop(settings, "show_brush", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column() - col.active = settings.show_brush - - if context.sculpt_object and context.tool_settings.sculpt: - if brush.sculpt_capabilities.has_secondary_color: - col.prop(brush, "cursor_color_add", text="Add") - col.prop(brush, "cursor_color_subtract", text="Subtract") - else: - col.prop(brush, "cursor_color_add", text="Color") - else: - col.prop(brush, "cursor_color_add", text="Color") - - -class VIEW3D_PT_tools_brush_display_custom_icon(Panel, View3DPaintPanel): - bl_context = ".paint_common" # dot on purpose (access from topbar) - bl_label = "Custom Icon" - bl_parent_id = "VIEW3D_PT_tools_brush_display" - bl_options = {'DEFAULT_CLOSED'} - - def draw_header(self, context): - settings = self.paint_settings(context) - brush = settings.brush - - self.layout.prop(brush, "use_custom_icon", text="") - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - settings = self.paint_settings(context) - brush = settings.brush - - col = layout.column() - col.active = brush.use_custom_icon - col.prop(brush, "icon_filepath", text="") - # ********** default tools for weight-paint **************** @@ -1512,6 +1033,10 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): wpaint = tool_settings.weight_paint col = layout.column() + + col.prop(tool_settings, "use_auto_normalize", text="Auto Normalize") + col.prop(tool_settings, "use_multipaint", text="Multi-Paint") + col.prop(wpaint, "use_group_restrict") obj = context.weight_paint_object @@ -1523,19 +1048,6 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): row.prop(mesh, "use_mirror_topology") -class VIEW3D_PT_tools_weightpaint_options_unified(Panel, View3DPaintPanel): - bl_context = ".weightpaint" - bl_label = "Unified Brush" - bl_parent_id = "VIEW3D_PT_tools_weightpaint_options" - - def draw(self, context): - layout = self.layout - - layout.use_property_split = True - layout.use_property_decorate = False - - self.unified_paint_settings(layout, context) - # ********** default tools for vertex-paint **************** @@ -1545,16 +1057,16 @@ class VIEW3D_PT_tools_vertexpaint_options(Panel, View3DPaintPanel): bl_label = "Options" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - layout = self.layout - - layout.label(text="Unified Brush") + @classmethod + def poll(self, _context): + # This is currently unused, since there aren't any Vertex Paint mode specific options. + return False + def draw(self, _context): + layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - self.unified_paint_settings(layout, context) - # TODO, move to space_view3d.py class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel): @@ -1676,19 +1188,6 @@ class VIEW3D_PT_tools_imagepaint_options(View3DPaintPanel, Panel): col.prop(ipaint, "use_backface_culling", text="Backface Culling") -class VIEW3D_PT_tools_imagepaint_options_unified(Panel, View3DPaintPanel): - bl_context = ".imagepaint" # dot on purpose (access from topbar) - bl_parent_id = "VIEW3D_PT_tools_imagepaint_options" - bl_label = "Unified Brush" - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - self.unified_paint_settings(layout, context) - - class VIEW3D_PT_tools_imagepaint_options_cavity(View3DPaintPanel, Panel): bl_context = ".imagepaint" # dot on purpose (access from topbar) bl_label = "Cavity Mask" @@ -1718,14 +1217,15 @@ class VIEW3D_PT_imagepaint_options(View3DPaintPanel): bl_label = "Options" @classmethod - def poll(cls, context): - return (context.image_paint_object and context.tool_settings.image_paint) + def poll(cls, _context): + # This is currently unused, since there aren't any Vertex Paint mode specific options. + return False + # return (context.image_paint_object and context.tool_settings.image_paint) - def draw(self, context): + def draw(self, _context): layout = self.layout - - col = layout.column() - self.unified_paint_settings(col, context) + layout.use_property_split = True + layout.use_property_decorate = False class VIEW3D_MT_tools_projectpaint_stencil(Menu): @@ -1783,6 +1283,8 @@ class VIEW3D_PT_tools_particlemode_options(View3DPanel, Panel): col = layout.column(align=True) col.active = pe.is_editable col.prop(ob.data, "use_mirror_x") + if pe.tool == 'ADD': + col.prop(ob.data, "use_mirror_topology") col.separator() col.prop(pe, "use_preserve_length", text="Preserve Strand Lengths") col.prop(pe, "use_preserve_root", text="Preserve Root Positions") @@ -1842,15 +1344,13 @@ class VIEW3D_PT_tools_particlemode_options_display(View3DPanel, Panel): # Grease Pencil drawing brushes -class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): +class GreasePencilPanel: bl_context = ".greasepencil_paint" - bl_label = "Brush" bl_category = "Tool" @classmethod def poll(cls, context): - is_3d_view = context.space_data.type == 'VIEW_3D' - if is_3d_view: + if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}: if context.gpencil_data is None: return False @@ -1859,6 +1359,10 @@ class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): else: return True + +class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPanel): + bl_label = "Brushes" + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -1868,14 +1372,38 @@ class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): gpencil_paint = tool_settings.gpencil_paint row = layout.row() - col = row.column() - col.template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) + row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8) col = row.column() - brush = gpencil_paint.brush + col.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="") + + if context.mode == 'PAINT_GPENCIL': + brush = tool_settings.gpencil_paint.brush + if brush is not None: + gp_settings = brush.gpencil_settings + + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + + if brush.use_custom_icon: + layout.row().prop(brush, "icon_filepath", text="") + - sub = col.column(align=True) - sub.operator("gpencil.brush_presets_create", icon='PRESET_NEW', text="") +class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPanel): + bl_label = "Brush Settings" + + # What is the point of brush presets? Seems to serve the exact same purpose as brushes themselves?? + def draw_header_preset(self, _context): + VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout) + + 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_paint + + brush = gpencil_paint.brush if brush is not None: gp_settings = brush.gpencil_settings @@ -1895,63 +1423,87 @@ class VIEW3D_PT_tools_grease_pencil_brush(View3DPanel, Panel): from bl_ui.properties_paint_common import ( brush_basic_gpencil_paint_settings, ) - tool = context.workspace.tools.from_space_view3d_mode(context.mode, create=False) - brush_basic_gpencil_paint_settings(layout, context, brush, tool, compact=True, is_toolbar=False) + brush_basic_gpencil_paint_settings(layout, context, brush, compact=False) -# Grease Pencil drawing brushes options -class VIEW3D_PT_tools_grease_pencil_brush_option(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel): bl_context = ".greasepencil_paint" - bl_label = "Options" + bl_label = "Advanced" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' 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_header_preset(self, _context): - VIEW3D_PT_gpencil_brush_presets.draw_panel_header(self.layout) + return brush is not None and brush.gpencil_tool != 'ERASE' def draw(self, context): layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - brush = context.tool_settings.gpencil_paint.brush + tool_settings = context.scene.tool_settings + gpencil_paint = tool_settings.gpencil_paint + brush = gpencil_paint.brush + gp_settings = brush.gpencil_settings + col = layout.column(align=True) if brush is not None: - gp_settings = brush.gpencil_settings - col = layout.column(align=True) - col.prop(gp_settings, "input_samples") - col.separator() + if brush.gpencil_tool != 'FILL': + col.prop(gp_settings, "input_samples") + col.separator() - col.prop(gp_settings, "active_smooth_factor") - col.separator() + col.prop(gp_settings, "active_smooth_factor") + col.separator() - col.prop(gp_settings, "angle", slider=True) - col.prop(gp_settings, "angle_factor", text="Factor", slider=True) + col.prop(gp_settings, "angle", slider=True) + col.prop(gp_settings, "angle_factor", text="Factor", slider=True) - ob = context.object - if ob and brush.gpencil_settings.use_material_pin is False: - ma = ob.active_material - elif brush.gpencil_settings.material: - ma = brush.gpencil_settings.material - else: + ob = context.object ma = None + if ob and brush.gpencil_settings.use_material_pin is False: + ma = ob.active_material + elif brush.gpencil_settings.material: + ma = brush.gpencil_settings.material - col.separator() - 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") + col.separator() + 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") + + elif brush.gpencil_tool == 'FILL': + row = col.row(align=True) + row.prop(gp_settings, "fill_draw_mode", text="Boundary") + row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID') + col.separator() + col.prop(gp_settings, "fill_factor", text="Resolution") + if gp_settings.fill_draw_mode != 'STROKE': + 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' + bl_label = "Stroke" + 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 == 'DRAW' + + def draw(self, context): + layout = self.layout -class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(Panel, View3DPanel): bl_context = ".greasepencil_paint" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' - bl_label = "Stabilize" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_stroke' + bl_label = "Stabilize Stroke" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -1961,6 +1513,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): return brush is not None and brush.gpencil_tool == 'DRAW' def draw_header(self, context): + if self.is_popover: + return + brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings self.layout.prop(gp_settings, "use_settings_stabilizer", text="") @@ -1972,24 +1527,35 @@ class VIEW3D_PT_tools_grease_pencil_brush_stabilizer(View3DPanel, Panel): brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - layout.active = gp_settings.use_settings_stabilizer - layout.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - layout.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + if self.is_popover: + row = layout.row() + row.prop(gp_settings, "use_settings_stabilizer", text="") + row.label(text=self.bl_label) + + col = layout.column() + col.active = gp_settings.use_settings_stabilizer + + col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) -class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_brush_post_processing(View3DPanel, Panel): bl_context = ".greasepencil_paint" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_stroke' bl_label = "Post-Processing" 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 != 'ERASE' + return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL'} def draw_header(self, context): + if self.is_popover: + return + brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings self.layout.prop(gp_settings, "use_settings_postprocess", text="") @@ -2001,30 +1567,37 @@ class VIEW3D_PT_tools_grease_pencil_brush_settings(View3DPanel, Panel): brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - layout.active = gp_settings.use_settings_postprocess - col = layout.column(align=True) - col.prop(gp_settings, "pen_smooth_factor") - col.prop(gp_settings, "pen_smooth_steps") + if self.is_popover: + row = layout.row() + row.prop(gp_settings, "use_settings_postprocess", text="") + row.label(text=self.bl_label) - col = layout.column(align=True) - col.prop(gp_settings, "pen_thick_smooth_factor") - col.prop(gp_settings, "pen_thick_smooth_steps", text="Iterations") + col = layout.column() + col.active = gp_settings.use_settings_postprocess - col = layout.column(align=True) - col.prop(gp_settings, "pen_subdivision_steps") - col.prop(gp_settings, "random_subdiv", text="Randomness", slider=True) + col1 = col.column(align=True) + col1.prop(gp_settings, "pen_smooth_factor") + col1.prop(gp_settings, "pen_smooth_steps") - col = layout.column(align=True) - col.prop(gp_settings, "simplify_factor") + col1 = col.column(align=True) + col1.prop(gp_settings, "pen_thick_smooth_factor") + col1.prop(gp_settings, "pen_thick_smooth_steps", text="Iterations") - col = layout.column(align=True) - col.prop(gp_settings, "trim") + 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") + + col1 = col.column(align=True) + col1.prop(gp_settings, "trim") class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): bl_context = ".greasepencil_paint" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_option' + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_stroke' bl_label = "Randomize" bl_category = "Tool" bl_options = {'DEFAULT_CLOSED'} @@ -2032,9 +1605,12 @@ 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 != 'ERASE' + return brush is not None and brush.gpencil_tool not in {'ERASE', 'FILL'} def draw_header(self, context): + if self.is_popover: + return + brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings self.layout.prop(gp_settings, "use_settings_random", text="") @@ -2046,13 +1622,20 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): brush = context.tool_settings.gpencil_paint.brush gp_settings = brush.gpencil_settings - layout.active = gp_settings.use_settings_random - layout.prop(gp_settings, "random_pressure", text="Pressure", slider=True) - layout.prop(gp_settings, "random_strength", text="Strength", slider=True) - layout.prop(gp_settings, "uv_random", text="UV", slider=True) + if self.is_popover: + row = layout.row() + row.prop(gp_settings, "use_settings_random", text="") + row.label(text=self.bl_label) - row = layout.row(align=True) + 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) + + row = col.row(align=True) row.prop(gp_settings, "pen_jitter", slider=True) row.prop(gp_settings, "use_jitter_pressure", text="", icon='STYLUS_PRESSURE') @@ -2060,6 +1643,7 @@ class VIEW3D_PT_tools_grease_pencil_brush_random(View3DPanel, Panel): # 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'} @@ -2124,12 +1708,6 @@ class VIEW3D_PT_tools_grease_pencil_brushcurves_jitter(View3DPanel, Panel): use_negative_slope=True) -# Grease Pencil stroke editing tools -class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): - bl_space_type = 'VIEW_3D' - bl_category = "Tool" - - # Grease Pencil stroke interpolation tools class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): bl_space_type = 'VIEW_3D' @@ -2178,18 +1756,47 @@ class VIEW3D_PT_tools_grease_pencil_interpolate(Panel): # Grease Pencil stroke sculpting tools -class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, View3DPanel, Panel): + +class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel): bl_context = ".greasepencil_sculpt" - bl_category = "Tools" - bl_label = "Brush" + bl_label = "Brushes" bl_category = "Tool" + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.tool_settings.gpencil_sculpt + + layout.template_icon_view(settings, "sculpt_tool", show_labels=True) + + +class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel): + bl_context = ".greasepencil_sculpt" + bl_category = "Tool" + bl_label = "Brush Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush + + if not self.is_popover: + from bl_ui.properties_paint_common import ( + brush_basic_gpencil_sculpt_settings, + ) + brush_basic_gpencil_sculpt_settings(layout, context, brush) # Grease Pencil weight painting tools -class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel): + + +class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel): bl_context = ".greasepencil_weight" - bl_category = "Tools" - bl_label = "Brush" + bl_label = "Brushes" bl_category = "Tool" def draw(self, context): @@ -2198,46 +1805,60 @@ class VIEW3D_PT_tools_grease_pencil_weight_paint(View3DPanel, Panel): layout.use_property_decorate = False settings = context.tool_settings.gpencil_sculpt - brush = settings.brush layout.template_icon_view(settings, "weight_tool", show_labels=True) - col = layout.column() + +class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel): + bl_context = ".greasepencil_weight" + bl_category = "Tool" + bl_label = "Brush Settings" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + settings = context.tool_settings.gpencil_sculpt + brush = settings.brush if not self.is_popover: from bl_ui.properties_paint_common import ( brush_basic_gpencil_weight_settings, ) - brush_basic_gpencil_weight_settings(col, context, brush) + brush_basic_gpencil_weight_settings(layout, context, brush) -# Grease Pencil Brush Appearance (one for each mode) -class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): - bl_context = ".greasepencil_paint" - bl_label = "Display" +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' bl_category = "Tool" + bl_label = "Sculpt Strokes" -class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): - bl_context = ".greasepencil_sculpt" - bl_label = "Display" +# Grease Pencil Brush Appearance (one for each mode) +class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): + bl_context = ".greasepencil_paint" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_brush_settings' + bl_label = "Cursor" bl_category = "Tool" -class VIEW3D_PT_tools_grease_pencil_sculpt_options(GreasePencilSculptOptionsPanel, View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_sculpt_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_sculpt" - bl_label = "Sculpt Strokes" - bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt' + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_sculpt_settings' + bl_label = "Cursor" bl_category = "Tool" -class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilAppearancePanel, View3DPanel, Panel): +class VIEW3D_PT_tools_grease_pencil_weight_appearance(GreasePencilDisplayPanel, Panel, View3DPanel): bl_context = ".greasepencil_weight" - bl_label = "Display" + bl_parent_id = 'VIEW3D_PT_tools_grease_pencil_weight_paint_settings' bl_category = "Tool" + bl_label = "Cursor" -class VIEW3D_PT_gpencil_brush_presets(PresetPanel, Panel): +class VIEW3D_PT_gpencil_brush_presets(Panel, PresetPanel): """Brush settings""" bl_label = "Brush Presets" preset_subdir = "gpencil_brush" @@ -2255,12 +1876,14 @@ classes = ( VIEW3D_PT_tools_curveedit_options_stroke, VIEW3D_PT_tools_armatureedit_options, VIEW3D_PT_tools_posemode_options, + VIEW3D_PT_slots_projectpaint, - VIEW3D_PT_tools_brush, + VIEW3D_PT_tools_brush_select, + VIEW3D_PT_tools_brush_settings, VIEW3D_PT_tools_brush_color, VIEW3D_PT_tools_brush_swatches, + VIEW3D_PT_tools_brush_settings_advanced, VIEW3D_PT_tools_brush_clone, - VIEW3D_PT_tools_brush_options, TEXTURE_UL_texpaintslots, VIEW3D_MT_tools_projectpaint_uvlayer, VIEW3D_PT_stencil_projectpaint, @@ -2272,49 +1895,53 @@ classes = ( VIEW3D_PT_tools_brush_falloff_frontface, VIEW3D_PT_tools_brush_falloff_normal, VIEW3D_PT_tools_brush_display, - VIEW3D_PT_tools_brush_display_show_brush, - VIEW3D_PT_tools_brush_display_custom_icon, + VIEW3D_PT_sculpt_dyntopo, VIEW3D_PT_sculpt_dyntopo_remesh, VIEW3D_PT_sculpt_voxel_remesh, VIEW3D_PT_sculpt_symmetry, VIEW3D_PT_sculpt_symmetry_for_topbar, VIEW3D_PT_sculpt_options, - VIEW3D_PT_sculpt_options_unified, VIEW3D_PT_sculpt_options_gravity, + VIEW3D_PT_tools_weightpaint_symmetry, VIEW3D_PT_tools_weightpaint_symmetry_for_topbar, VIEW3D_PT_tools_weightpaint_options, - VIEW3D_PT_tools_weightpaint_options_unified, + VIEW3D_PT_tools_vertexpaint_symmetry, VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar, VIEW3D_PT_tools_vertexpaint_options, + VIEW3D_PT_tools_imagepaint_symmetry, VIEW3D_PT_tools_imagepaint_options, VIEW3D_PT_tools_imagepaint_options_cavity, - VIEW3D_PT_tools_imagepaint_options_unified, VIEW3D_PT_tools_imagepaint_options_external, VIEW3D_MT_tools_projectpaint_stencil, + VIEW3D_PT_tools_particlemode, VIEW3D_PT_tools_particlemode_options, VIEW3D_PT_tools_particlemode_options_shapecut, VIEW3D_PT_tools_particlemode_options_display, VIEW3D_PT_gpencil_brush_presets, - VIEW3D_PT_tools_grease_pencil_brush, - VIEW3D_PT_tools_grease_pencil_brush_option, + VIEW3D_PT_tools_grease_pencil_brush_select, VIEW3D_PT_tools_grease_pencil_brush_settings, - VIEW3D_PT_tools_grease_pencil_brush_stabilizer, + VIEW3D_PT_tools_grease_pencil_brush_advanced, + VIEW3D_PT_tools_grease_pencil_brush_stroke, + 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_sculpt, - VIEW3D_PT_tools_grease_pencil_weight_paint, VIEW3D_PT_tools_grease_pencil_paint_appearance, + VIEW3D_PT_tools_grease_pencil_sculpt_select, + VIEW3D_PT_tools_grease_pencil_sculpt_settings, VIEW3D_PT_tools_grease_pencil_sculpt_options, VIEW3D_PT_tools_grease_pencil_sculpt_appearance, + 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_interpolate, ) |