diff options
Diffstat (limited to 'release/scripts')
43 files changed, 1902 insertions, 1529 deletions
diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 8397b98b8cb6510869d28a84592b8d7213c3b6c +Subproject bc1262f4d61feeba235bb75046e65e0e8411241 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 7c36b48507f79ca62f8c038bad0fb3468c4f48e +Subproject 45aa940dabda64f7877c6d5dd843998a86f0a83 diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index a65ff15393a..8a074d23db9 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -172,8 +172,8 @@ def modules_refresh(module_cache=addons_fake_modules): if mod.__file__ != mod_path: print( "multiple addons with the same name:\n" - " " f"{mod.__file__!r}" "\n" - " " f"{mod_path!r}" + " %r\n" + " %r" % (mod.__file__, mod_path) ) error_duplicates.append((mod.bl_info["name"], mod.__file__, mod_path)) @@ -241,7 +241,7 @@ def check(module_name): if loaded_state is Ellipsis: print( - "Warning: addon-module " f"{module_name:s}" " found module " + "Warning: addon-module", module_name, "found module " "but without '__addon_enabled__' field, " "possible name collision from file:", repr(getattr(mod, "__file__", "<unknown>")), @@ -439,8 +439,9 @@ def disable(module_name, *, default_set=False, handle_error=None): handle_error(ex) else: print( - "addon_utils.disable: " f"{module_name:s}" " not", - ("disabled" if mod is None else "loaded") + "addon_utils.disable: %s not %s" % ( + module_name, + "disabled" if mod is None else "loaded") ) # could be in more than once, unlikely but better do this just in case. @@ -502,7 +503,7 @@ def _blender_manual_url_prefix(): else: manual_version = "dev" - return f"https://docs.blender.org/manual/en/{manual_version}" + return "https://docs.blender.org/manual/en/" + manual_version def module_bl_info(mod, info_basis=None): @@ -544,11 +545,11 @@ def module_bl_info(mod, info_basis=None): addon_info["doc_url"] = doc_url if _bpy.app.debug: print( - "Warning: add-on \"{addon_name}\": 'wiki_url' in 'bl_info' " + "Warning: add-on \"%s\": 'wiki_url' in 'bl_info' " "is deprecated please use 'doc_url' instead!\n" - " {addon_path}".format( - addon_name=addon_info['name'], - addon_path=getattr(mod, "__file__", None), + " %s" % ( + addon_info['name'], + getattr(mod, "__file__", None), ) ) diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py index b79c0b744d0..73004cee731 100644 --- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -505,7 +505,7 @@ def main(): if __name__ == "__main__": - print("\n\n *** Running {} *** \n".format(__file__)) - print(" *** Blend file {} *** \n".format(bpy.data.filepath)) + print("\n\n *** Running %s *** \n" % __file__) + print(" *** Blend file %s *** \n" % bpy.data.filepath) main() bpy.ops.wm.quit_blender() diff --git a/release/scripts/modules/bl_ui_utils/bug_report_url.py b/release/scripts/modules/bl_ui_utils/bug_report_url.py index 2adee70bc86..5676e0d6815 100644 --- a/release/scripts/modules/bl_ui_utils/bug_report_url.py +++ b/release/scripts/modules/bl_ui_utils/bug_report_url.py @@ -31,13 +31,13 @@ def url_prefill_from_blender(addon_info=None): fh.write("**System Information**\n") fh.write( - "Operating system: {!s} {!s} Bits\n".format( + "Operating system: %s %d Bits\n" % ( platform.platform(), struct.calcsize("P") * 8, ) ) fh.write( - "Graphics card: {!s} {!s} {!s}\n".format( + "Graphics card: %s %s %s\n" % ( bgl.glGetString(bgl.GL_RENDERER), bgl.glGetString(bgl.GL_VENDOR), bgl.glGetString(bgl.GL_VERSION), @@ -48,7 +48,7 @@ def url_prefill_from_blender(addon_info=None): "**Blender Version**\n" ) fh.write( - "Broken: version: {!s}, branch: {!s}, commit date: {!s} {!s}, hash: `rB{!s}`\n".format( + "Broken: version: %s, branch: %s, commit date: %s %s, hash: `rB%s`\n" % ( bpy.app.version_string, bpy.app.build_branch.decode('utf-8', 'replace'), bpy.app.build_commit_date.decode('utf-8', 'replace'), diff --git a/release/scripts/modules/bpy/ops.py b/release/scripts/modules/bpy/ops.py index 8f8f42bcd46..4e226f80f79 100644 --- a/release/scripts/modules/bpy/ops.py +++ b/release/scripts/modules/bpy/ops.py @@ -123,7 +123,7 @@ class BPyOpsSubModOp: # op_class = getattr(bpy.types, idname) op_class = op_get_rna_type(idname) descr = op_class.description - return f"{sig}\n{descr}" + return "%s\n%s" % (sig, descr) @staticmethod def _parse_args(args): diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py index bfdf28e0db4..7f337677635 100644 --- a/release/scripts/modules/bpy/utils/previews.py +++ b/release/scripts/modules/bpy/utils/previews.py @@ -76,8 +76,7 @@ class ImagePreviewCollection(dict): return raise ResourceWarning( - f"{self!r}: left open, remove with " - "'bpy.utils.previews.remove()'" + "%r: left open, remove with 'bpy.utils.previews.remove()'" % self ) self.close() @@ -116,7 +115,9 @@ class ImagePreviewCollection(dict): super().__delitem__(key) def __repr__(self): - return f"<{self.__class__.__name__:s} id={self._uuid:s}[{len(self):d}], {super()!r}>" + return "<%s id=%s[%d], %r>" % ( + self.__class__.__name__, self._uuid, len(self), super() + ) def new(): diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py index 844daded570..6076bf00063 100644 --- a/release/scripts/modules/rna_keymap_ui.py +++ b/release/scripts/modules/rna_keymap_ui.py @@ -240,6 +240,9 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout): "*": 'NUMPAD_ASTERIX', "/": 'NUMPAD_SLASH', '+': 'NUMPAD_PLUS', + "-": 'NUMPAD_MINUS', + ".": 'NUMPAD_PERIOD', + "'": 'QUOTE', "RMB": 'RIGHTMOUSE', "LMB": 'LEFTMOUSE', "MMB": 'MIDDLEMOUSE', diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 8dda8c90f85..662c1d908fc 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -130,7 +130,7 @@ def rna_idprop_ui_prop_default_set(item, prop, value): try: prop_type, is_array = rna_idprop_value_item_type(item[prop]) - if prop_type in {int, float}: + if prop_type in {int, float, str}: if is_array and isinstance(value, ARRAY_TYPES): value = [prop_type(item) for item in value] if any(value): diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index bbbe520441c..57b31175728 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -695,9 +695,12 @@ def km_user_interface(_params): ("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, {"properties": [("full_path", True)]}), # Keyframes and drivers - ("anim.keyframe_insert_button", {"type": 'I', "value": 'PRESS'}, None), - ("anim.keyframe_delete_button", {"type": 'I', "value": 'PRESS', "alt": True}, None), - ("anim.keyframe_clear_button", {"type": 'I', "value": 'PRESS', "shift": True, "alt": True}, None), + ("anim.keyframe_insert_button", {"type": 'I', "value": 'PRESS'}, + {"properties": [("all", True)]}), + ("anim.keyframe_delete_button", {"type": 'I', "value": 'PRESS', "alt": True}, + {"properties": [("all", True)]}), + ("anim.keyframe_clear_button", {"type": 'I', "value": 'PRESS', "shift": True, "alt": True}, + {"properties": [("all", True)]}), ("anim.driver_button_add", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None), @@ -725,6 +728,16 @@ def km_property_editor(_params): {"properties": [("direction", 'PREV'), ], },), ("screen.space_context_cycle", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True}, {"properties": [("direction", 'NEXT'), ], },), + # Modifier panels + ("object.modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("object.modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), + # Grease pencil modifier panels + ("object.gpencil_modifier_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.gpencil_modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("object.gpencil_modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), ]) return keymap @@ -1893,7 +1906,7 @@ def km_file_browser_main(params): {"properties": [("open", False), ("deselect_all", not params.legacy)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, - {"properties": [("extend", True)]}), + {"properties": [("extend", True), ("open", False)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("extend", True), ("fill", True), ("open", False)]}), ("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'}, @@ -2464,7 +2477,7 @@ def km_sequencer(params): {"properties": [("all", False)]}), ("sequencer.gap_remove", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, {"properties": [("all", True)]}), - ("sequencer.gap_insert", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None), + ("sequencer.gap_insert", {"type": 'EQUAL', "value": 'PRESS', "shift": True}, None), ("sequencer.snap", {"type": 'S', "value": 'PRESS', "shift": True}, None), ("sequencer.swap_inputs", {"type": 'S', "value": 'PRESS', "alt": True}, None), *( @@ -2520,14 +2533,6 @@ def km_sequencer(params): {"properties": [("side", 'LEFT')]}), ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("side", 'RIGHT')]}), - ("sequencer.select_side_of_frame", {"type": 'EQUAL', "value": 'PRESS'}, - {"properties": [("side", 'OVERLAP')]}), - ("sequencer.select_side_of_frame", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True}, - {"properties": [("side", 'LEFT'), ("extend", True)]}), - ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True}, - {"properties": [("side", 'RIGHT'), ("extend", True)]}), - ("sequencer.select_side_of_frame", {"type": 'EQUAL', "value": 'PRESS', "shift": True}, - {"properties": [("side", 'OVERLAP'), ("extend", True)]}), *_template_items_context_menu("SEQUENCER_MT_context_menu", params.context_menu_event), ]) @@ -4348,6 +4353,8 @@ def km_sculpt(params): ("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None), ("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None), + # Color + ("sculpt.sample_color", {"type": 'S', "value": 'PRESS'}, None), # Brush properties ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS'}, {"properties": [("scalar", 0.9)]}), @@ -4370,7 +4377,7 @@ def km_sculpt(params): # Tools ("paint.brush_select", {"type": 'X', "value": 'PRESS'}, {"properties": [("sculpt_tool", 'DRAW')]}), - ("paint.brush_select", {"type": 'S', "value": 'PRESS'}, + ("paint.brush_select", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("sculpt_tool", 'SMOOTH')]}), ("paint.brush_select", {"type": 'P', "value": 'PRESS'}, {"properties": [("sculpt_tool", 'PINCH')]}), @@ -5184,7 +5191,7 @@ def km_bevel_modal_map(_params): ("MARK_SHARP_TOGGLE", {"type": 'K', "value": 'PRESS', "any": True}, None), ("OUTER_MITER_CHANGE", {"type": 'O', "value": 'PRESS', "any": True}, None), ("INNER_MITER_CHANGE", {"type": 'I', "value": 'PRESS', "any": True}, None), - ("CUSTOM_PROFILE_TOGGLE", {"type": 'Z', "value": 'PRESS', "any": True}, None), + ("PROFILE_TYPE_CHANGE", {"type": 'Z', "value": 'PRESS', "any": True}, None), ("VERTEX_MESH_CHANGE", {"type": 'N', "value": 'PRESS', "any": True}, None), ]) @@ -5914,12 +5921,12 @@ def km_3d_view_tool_edit_mesh_extrude_region(params): ]}, ) -def km_3d_view_tool_edit_mesh_extrude_dissolve_and_intersect(params): +def km_3d_view_tool_edit_mesh_extrude_manifold(params): return ( - "3D View Tool: Edit Mesh, Extrude Dissolve and Intersect", + "3D View Tool: Edit Mesh, Extrude Manifold", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("mesh.extrude_region_dissolve_move_intersect", {"type": params.tool_tweak, "value": 'ANY'}, + ("mesh.extrude_manifold", {"type": params.tool_tweak, "value": 'ANY'}, {"properties": [ ("MESH_OT_extrude_region", [("use_dissolve_ortho_edges", True)]), ("TRANSFORM_OT_translate", [ @@ -6215,9 +6222,9 @@ def km_3d_view_tool_edit_curve_extrude(params): ) -def km_3d_view_tool_edit_curve_extrude_cursor(params): +def km_3d_view_tool_edit_curve_extrude_to_cursor(params): return ( - "3D View Tool: Edit Curve, Extrude Cursor", + "3D View Tool: Edit Curve, Extrude to Cursor", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ ("curve.vertex_add", {"type": params.tool_mouse, "value": 'PRESS'}, None), @@ -6285,6 +6292,16 @@ def km_3d_view_tool_sculpt_cloth_filter(params): ]}, ) +def km_3d_view_tool_sculpt_color_filter(params): + return ( + "3D View Tool: Sculpt, Color Filter", + {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, + {"items": [ + ("sculpt.color_filter", {"type": params.tool_tweak, "value": 'ANY'}, + None) + ]}, + ) + def km_3d_view_tool_paint_weight_sample_weight(params): return ( "3D View Tool: Paint Weight, Sample Weight", @@ -6792,7 +6809,7 @@ def generate_keymaps(params=None): km_3d_view_tool_edit_armature_extrude(params), km_3d_view_tool_edit_armature_extrude_to_cursor(params), km_3d_view_tool_edit_mesh_extrude_region(params), - km_3d_view_tool_edit_mesh_extrude_dissolve_and_intersect(params), + km_3d_view_tool_edit_mesh_extrude_manifold(params), km_3d_view_tool_edit_mesh_extrude_along_normals(params), km_3d_view_tool_edit_mesh_extrude_individual(params), km_3d_view_tool_edit_mesh_extrude_to_cursor(params), @@ -6819,12 +6836,13 @@ def generate_keymaps(params=None): km_3d_view_tool_edit_curve_tilt(params), km_3d_view_tool_edit_curve_randomize(params), km_3d_view_tool_edit_curve_extrude(params), - km_3d_view_tool_edit_curve_extrude_cursor(params), + km_3d_view_tool_edit_curve_extrude_to_cursor(params), km_3d_view_tool_sculpt_box_hide(params), km_3d_view_tool_sculpt_box_mask(params), km_3d_view_tool_sculpt_lasso_mask(params), km_3d_view_tool_sculpt_mesh_filter(params), km_3d_view_tool_sculpt_cloth_filter(params), + km_3d_view_tool_sculpt_color_filter(params), km_3d_view_tool_paint_weight_sample_weight(params), km_3d_view_tool_paint_weight_sample_vertex_group(params), km_3d_view_tool_paint_weight_gradient(params), 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 4cb6cefc960..f275d892abe 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1246,7 +1246,7 @@ def km_file_browser_main(params): ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, {"properties": [("open", False), ("deselect_all", True)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, - {"properties": [("extend", True)]}), + {"properties": [("extend", True), ("open", False)]}), ("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True,}, {"properties": [("extend", True), ("fill", True), ("open", False)]}), ("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK', "shift": True}, diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index d7df29f1769..c39a7afcff9 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -49,6 +49,7 @@ _modules = [ "uvcalc_smart_project", "vertexpaint_dirt", "view3d", + "gpencil_mesh_bake", "wm", ] diff --git a/release/scripts/startup/bl_operators/gpencil_mesh_bake.py b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py new file mode 100644 index 00000000000..ae75fa0e4d9 --- /dev/null +++ b/release/scripts/startup/bl_operators/gpencil_mesh_bake.py @@ -0,0 +1,162 @@ +# ##### 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> + +import bpy +from bpy.types import Operator +from bpy.props import ( + IntProperty, + FloatProperty, + BoolProperty, + EnumProperty, +) + +gp_object_items = [] + + +def my_objlist_callback(scene, context): + gp_object_items.clear() + gp_object_items.append(('*NEW', "New Object", "")) + for o in context.scene.objects: + if o.type == 'GPENCIL': + gp_object_items.append((o.name, o.name, "")) + + return gp_object_items + + +class GPENCIL_OT_mesh_bake(Operator): + """Bake all mesh animation into grease pencil strokes""" + bl_idname = "gpencil.mesh_bake" + bl_label = "Bake Mesh to Grease Pencil" + bl_options = {'REGISTER', 'UNDO'} + + frame_start: IntProperty( + name="Start Frame", + description="Start frame for baking", + min=0, max=300000, + default=1, + ) + frame_end: IntProperty( + name="End Frame", + description="End frame for baking", + min=1, max=300000, + default=250, + ) + step: IntProperty( + name="Frame Step", + description="Frame Step", + min=1, max=120, + default=1, + ) + thickness: IntProperty( + name="Thickness", + description="Thickness of the stroke lines", + min=1, max=100, + default=1, + ) + angle: FloatProperty( + name="Threshold Angle", + description="Threshold to determine ends of the strokes", + min=0, + max=+3.141592, + default=+1.22173, # 70 Degress + subtype='ANGLE', + ) + offset: FloatProperty( + name="Stroke Offset", + description="Offset strokes from fill", + soft_min=0.0, soft_max=100.0, + min=0.0, max=100.0, + default=0.001, + precision=3, + step=1, + subtype='DISTANCE', + unit='LENGTH', + ) + seams: BoolProperty( + name="Only Seam Edges", + description="Convert only seam edges", + default=False, + ) + faces: BoolProperty( + name="Export Faces", + description="Export faces as filled strokes", + default=True, + ) + target: EnumProperty( + name="Target Object", + description="Grease Pencil Object", + items=my_objlist_callback + ) + frame_target: IntProperty( + name="Target Frame", + description="Destination frame for the baked animation", + min=1, max=300000, + default=1, + ) + project_type: EnumProperty( + name="Reproject Type", + description="Type of projection", + items=( + ("KEEP", "No Reproject", ""), + ("FRONT", "Front", "Reproject the strokes using the X-Z plane"), + ("SIDE", "Side", "Reproject the strokes using the Y-Z plane"), + ("TOP", "Top", "Reproject the strokes using the X-Y plane"), + ("VIEW", "View", "Reproject the strokes to current viewpoint"), + ("CURSOR", "Cursor", "Reproject the strokes using the orientation of 3D cursor") + ) + ) + + @classmethod + def poll(self, context): + ob = context.active_object + return ((ob is not None) and + (ob.type in {'EMPTY', 'MESH'}) and + (context.mode == 'OBJECT')) + + def execute(self, context): + bpy.ops.gpencil.bake_mesh_animation( + frame_start=self.frame_start, + frame_end=self.frame_end, + step=self.step, + angle=self.angle, + thickness=self.thickness, + seams=self.seams, + faces=self.faces, + offset=self.offset, + target=self.target, + frame_target=self.frame_target, + project_type=self.project_type + ) + + return {'FINISHED'} + + def invoke(self, context, _event): + scene = context.scene + self.frame_start = scene.frame_start + self.frame_end = scene.frame_end + self.frame_target = scene.frame_start + + wm = context.window_manager + return wm.invoke_props_dialog(self) + + +classes = ( + GPENCIL_OT_mesh_bake, +) diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index 9fdaebc7540..1c5ebb4fa17 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -94,9 +94,16 @@ class NodeAddOperator: for setting in self.settings: # XXX catch exceptions here? value = eval(setting.value) + node_data = node + node_attr_name = setting.name + + # Support path to nested data. + if '.' in node_attr_name: + node_data_path, node_attr_name = node_attr_name.rsplit(".", 1) + node_data = node.path_resolve(node_data_path) try: - setattr(node, setting.name, value) + setattr(node_data, node_attr_name, value) except AttributeError as e: self.report( {'ERROR_INVALID_INPUT'}, diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 09a98100796..cf91cd3c961 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -194,7 +194,7 @@ class SelectHierarchy(Operator): for obj in selected_objects: parent = obj.parent - if parent: + if parent and parent.visible_get(): if obj_act == obj: act_new = parent @@ -202,12 +202,13 @@ class SelectHierarchy(Operator): else: for obj in selected_objects: - select_new.extend(obj.children) + select_new.extend([child for child in obj.children if child.visible_get()]) if select_new: select_new.sort(key=lambda obj_iter: obj_iter.name) act_new = select_new[0] + # don't edit any object settings above this if select_new: if not self.extend: diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 71153ba8b74..311631ac65f 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -399,8 +399,8 @@ class QuickSmoke(ObjectModeOperator, Operator): if self.style == 'FIRE' or self.style == 'BOTH': obj.modifiers[-1].domain_settings.use_noise = True - # set correct cache file format for smoke - obj.modifiers[-1].domain_settings.cache_data_format = 'UNI' + # ensure correct cache file format for smoke + obj.modifiers[-1].domain_settings.cache_data_format = 'OPENVDB' # Setup material @@ -514,7 +514,8 @@ class QuickLiquid(Operator): 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 + # ensure correct cache file formats for liquid + obj.modifiers[-1].domain_settings.cache_data_format = 'OPENVDB' obj.modifiers[-1].domain_settings.cache_mesh_format = 'BOBJECT' # change domain type, will also allocate and show particle system for FLIP diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index c83d0b9f4d8..2ea93a1aee9 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -80,7 +80,8 @@ class AddPresetBase: name = name.lower().strip() name = bpy.path.display_name_to_filepath(name) trans = maketrans_init() - return name.translate(trans) + # Strip surrounding "_" as they are displayed as spaces. + return name.translate(trans).strip("_") def execute(self, context): import os @@ -92,15 +93,16 @@ class AddPresetBase: preset_menu_class = getattr(bpy.types, self.preset_menu) is_xml = getattr(preset_menu_class, "preset_type", None) == 'XML' + is_preset_add = not (self.remove_name or self.remove_active) if is_xml: ext = ".xml" else: ext = ".py" - name = self.name.strip() - if not (self.remove_name or self.remove_active): + name = self.name.strip() if is_preset_add else self.name + if is_preset_add: if not name: return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py index 3900fc3f2c2..255852d3b26 100644 --- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py +++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py @@ -32,25 +32,11 @@ def guess_player_path(preset): if preset == 'INTERNAL': return bpy.app.binary_path - elif preset == 'BLENDER24': - player_path = "blender" - - if sys.platform == "darwin": - test_path = "/Applications/blender 2.49.app/Contents/MacOS/blender" - elif sys.platform[:3] == "win": - test_path = "/Program Files/Blender Foundation/Blender/blender.exe" - - if os.path.exists(test_path): - player_path = test_path elif preset == 'DJV': - player_path = "djv_view" - + player_path = "djv" if sys.platform == "darwin": - # TODO, crummy supporting only 1 version, - # could find the newest installed version - test_path = ("/Applications/djv-0.8.2.app" - "/Contents/Resources/bin/djv_view") + test_path = "/Applications/DJV2.app/Contents/Resources/bin/djv" if os.path.exists(test_path): player_path = test_path @@ -148,7 +134,13 @@ class PlayRenderedAnim(Operator): ] cmd.extend(opts) elif preset == 'DJV': - opts = [file, "-playback_speed", str(int(fps_final))] + opts = [ + file, + "-speed", str(fps_final), + "-in_out", str(frame_start), str(frame_end), + "-frame", str(scene.frame_current), + "-time_units", "Frames" + ] cmd.extend(opts) elif preset == 'FRAMECYCLER': opts = [file, f"{scene.frame_start:d}-{scene.frame_end:d}"] @@ -175,14 +167,8 @@ class PlayRenderedAnim(Operator): # launch it print("Executing command:\n ", " ".join(quote(c) for c in cmd)) - # workaround for boost 1.46, can be eventually removed. bug: [#32350] - env_copy = os.environ.copy() - if preset == 'INTERNAL': - env_copy["LC_ALL"] = "C" - # end workaround - try: - subprocess.Popen(cmd, env=env_copy) + subprocess.Popen(cmd) except Exception as e: err_msg = tip_("Couldn't run external animation player with command %r\n%s") % (cmd, e) self.report( diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py index af071561232..9ffb23287f8 100644 --- a/release/scripts/startup/bl_operators/sequencer.py +++ b/release/scripts/startup/bl_operators/sequencer.py @@ -164,6 +164,7 @@ class SequencerFadesClear(Operator): if curve: fcurves.remove(curve) setattr(sequence, animated_property, 1.0) + sequence.invalidate('COMPOSITE') return {'FINISHED'} @@ -184,8 +185,10 @@ class SequencerFadesAdd(Operator): ('IN_OUT', 'Fade In And Out', 'Fade selected strips in and out'), ('IN', 'Fade In', 'Fade in selected strips'), ('OUT', 'Fade Out', 'Fade out selected strips'), - ('CURSOR_FROM', 'From Playhead', 'Fade from the time cursor to the end of overlapping sequences'), - ('CURSOR_TO', 'To Playhead', 'Fade from the start of sequences under the time cursor to the current frame'), + ('CURSOR_FROM', 'From Current Frame', + 'Fade from the time cursor to the end of overlapping sequences'), + ('CURSOR_TO', 'To Current Frame', + 'Fade from the start of sequences under the time cursor to the current frame'), ), name="Fade type", description="Fade in, out, both in and out, to, or from the current frame. Default is both in and out", @@ -230,9 +233,10 @@ class SequencerFadesAdd(Operator): self.fade_animation_clear(fade_fcurve, fades) self.fade_animation_create(fade_fcurve, fades) faded_sequences.append(sequence) + sequence.invalidate('COMPOSITE') sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences" - self.report({'INFO'}, "Added fade animation to {} {}.".format(len(faded_sequences), sequence_string)) + self.report({'INFO'}, "Added fade animation to %d %s." % (len(faded_sequences), sequence_string)) return {'FINISHED'} def calculate_fade_duration(self, context, sequence): @@ -360,7 +364,7 @@ class Fade: return max_value if max_value > 0.0 else 1.0 def __repr__(self): - return "Fade {}: {} to {}".format(self.type, self.start, self.end) + return "Fade %r: %r to %r" % (self.type, self.start, self.end) def calculate_duration_frames(context, duration_seconds): diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py index 7024582ad30..62ef3cb34b5 100644 --- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py +++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py @@ -32,7 +32,7 @@ def get_vcolor_layer_data(me): return lay.data -def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only): +def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only, normalize): from mathutils import Vector from math import acos import array @@ -74,14 +74,14 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, tot_con = len(con[i]) if tot_con == 0: - continue + ang = pi / 2.0 # assume 90°, i. e. flat + else: + vec /= tot_con - vec /= tot_con - - # angle is the acos() of the dot product between normal and connected verts. - # > 90 degrees: convex - # < 90 degrees: concave - ang = acos(no.dot(vec)) + # angle is the acos() of the dot product between normal and connected verts. + # > 90 degrees: convex + # < 90 degrees: concave + ang = acos(no.dot(vec)) # enforce min/max ang = max(clamp_dirt, ang) @@ -104,8 +104,12 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, vert_tone[j] /= len(c) * blur_strength + 1 del orig_vert_tone - min_tone = min(vert_tone) - max_tone = max(vert_tone) + if normalize: + min_tone = min(vert_tone) + max_tone = max(vert_tone) + else: + min_tone = clamp_dirt + max_tone = clamp_clean tone_range = max_tone - min_tone @@ -181,6 +185,11 @@ class VertexPaintDirt(Operator): description="Don't calculate cleans for convex areas", default=False, ) + normalize: BoolProperty( + name="Normalize", + description="Normalize the colors, increasing the contrast", + default=True, + ) @classmethod def poll(cls, context): @@ -198,6 +207,7 @@ class VertexPaintDirt(Operator): self.dirt_angle, self.clean_angle, self.dirt_only, + self.normalize, ) return ret diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py index 88fa06a913f..a8e04eb2f24 100644 --- a/release/scripts/startup/bl_operators/view3d.py +++ b/release/scripts/startup/bl_operators/view3d.py @@ -98,7 +98,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator): TRANSFORM_OT_shrink_fatten={}, ) elif dissolve_and_intersect: - bpy.ops.mesh.extrude_region_dissolve_move_intersect( + bpy.ops.mesh.extrude_manifold( 'INVOKE_REGION_WIN', MESH_OT_extrude_region={ "use_dissolve_ortho_edges": True, diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 4c4736cd669..a543ea6685c 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1457,7 +1457,7 @@ class WM_OT_properties_edit(Operator): proptype, is_array = rna_idprop_value_item_type(value) row = layout.row() - row.enabled = proptype in {int, float} + row.enabled = proptype in {int, float, str} row.prop(self, "default") row = layout.row(align=True) @@ -1697,7 +1697,7 @@ class WM_OT_tool_set_by_id(Operator): tool_settings.workspace_tool_type = 'FALLBACK' return {'FINISHED'} else: - self.report({'WARNING'}, f"Tool {self.name!r:s} not found for space {space_type!r:s}.") + self.report({'WARNING'}, "Tool %r not found for space %r." % (self.name, space_type)) return {'CANCELLED'} @@ -2216,8 +2216,8 @@ class WM_OT_batch_rename(Operator): elif ty == 'STRIP': chars = action.strip_chars chars_strip = ( - "{:s}{:s}{:s}" - ).format( + "%s%s%s" + ) % ( string.punctuation if 'PUNCT' in chars else "", string.digits if 'DIGIT' in chars else "", " " if 'SPACE' in chars else "", @@ -2282,7 +2282,7 @@ class WM_OT_batch_rename(Operator): split.prop(self, "data_type", text="") split = layout.split(factor=0.5) - split.label(text="Rename {:d} {:s}:".format(len(self._data[0]), self._data[2])) + split.label(text="Rename %d %s:" % (len(self._data[0]), self._data[2])) split.row().prop(self, "data_source", expand=True) for action in self.actions: @@ -2397,7 +2397,7 @@ class WM_OT_batch_rename(Operator): change_len += 1 total_len += 1 - self.report({'INFO'}, "Renamed {:d} of {:d} {:s}".format(change_len, total_len, descr)) + self.report({'INFO'}, "Renamed %d of %d %s" % (change_len, total_len, descr)) return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 3fc54ff6d12..8bae32775a9 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -20,894 +20,762 @@ from bpy.types import Panel -class ConstraintButtonsPanel: +class ObjectConstraintPanel(Panel): + bl_context = "constraint" + + @classmethod + def poll(cls, context): + return (context.object) + + +class BoneConstraintPanel(Panel): + bl_context = "bone_constraint" + + @classmethod + def poll(cls, context): + return (context.pose_bone) + + +class OBJECT_PT_constraints(ObjectConstraintPanel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' - bl_context = "constraint" + bl_label = "Object Constraints" + bl_options = {'HIDE_HEADER'} - def draw_constraint(self, context, con): + def draw(self, context): layout = self.layout - box = layout.template_constraint(con) + layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint") - if box: - # match enum type to our functions, avoids a lookup table. - getattr(self, con.type)(context, box, con) + layout.template_constraints(use_bone_constraints=False) - if con.type in {'RIGID_BODY_JOINT', 'NULL'}: - return - if con.type in {'IK', 'SPLINE_IK'}: - # constraint.disable_keep_transform doesn't work well - # for these constraints. - box.prop(con, "influence") - else: - row = box.row(align=True) - row.prop(con, "influence") - row.operator("constraint.disable_keep_transform", text="", icon='CANCEL') +class BONE_PT_constraints(BoneConstraintPanel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_label = "Bone Constraints" + bl_options = {'HIDE_HEADER'} - @staticmethod - def space_template(layout, con, target=True, owner=True): - if target or owner: + def draw(self, context): + layout = self.layout - split = layout.split(factor=0.2) + layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint") - split.label(text="Space:") - row = split.row() + layout.template_constraints(use_bone_constraints=True) - if target: - row.prop(con, "target_space", text="") - if target and owner: - row.label(icon='ARROW_LEFTRIGHT') +# Parent class for constraint panels, with templates and drawing methods +# shared between the bone and object constraint panels +class ConstraintButtonsPanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_label = "" + bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND', 'DRAW_BOX'} + + @staticmethod + def draw_influence(layout, con): + layout.separator() + if con.type in {'IK', 'SPLINE_IK'}: + # constraint.disable_keep_transform doesn't work well + # for these constraints. + layout.prop(con, "influence") + else: + row = layout.row(align=True) + row.prop(con, "influence") + row.operator("constraint.disable_keep_transform", text="", icon='CANCEL') + @staticmethod + def space_template(layout, con, target=True, owner=True): + if target or owner: + layout.separator() + if target: + layout.prop(con, "target_space", text="Target") if owner: - row.prop(con, "owner_space", text="") + layout.prop(con, "owner_space", text="Owner") @staticmethod def target_template(layout, con, subtargets=True): - layout.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object + col = layout.column() + col.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object if con.target and subtargets: if con.target.type == 'ARMATURE': - layout.prop_search(con, "subtarget", con.target.data, "bones", text="Bone") + col.prop_search(con, "subtarget", con.target.data, "bones", text="Bone") - if hasattr(con, "head_tail"): - row = layout.row(align=True) - row.label(text="Head/Tail:") - row.prop(con, "head_tail", text="") + if con.subtarget and hasattr(con, "head_tail"): + row = col.row(align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "head_tail") # XXX icon, and only when bone has segments? - row.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER') + sub.prop(con, "use_bbone_shape", text="", icon='IPO_BEZIER') + row.prop_decorator(con, "head_tail") elif con.target.type in {'MESH', 'LATTICE'}: - layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group") + col.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group") - @staticmethod - def ik_template(layout, con): - # only used for iTaSC - layout.prop(con, "pole_target") + def get_constraint(self, context): + con = None + if context.pose_bone: + con = context.pose_bone.constraints[self.list_panel_index] + else: + con = context.object.constraints[self.list_panel_index] + self.layout.context_pointer_set("constraint", con) + return con - if con.pole_target and con.pole_target.type == 'ARMATURE': - layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") + def draw_header(self, context): + layout = self.layout + con = self.get_constraint(context) - if con.pole_target: - row = layout.row() - row.label() - row.prop(con, "pole_angle") + layout.template_constraint_header(con) - split = layout.split(factor=0.33) - col = split.column() - col.prop(con, "use_tail") - col.prop(con, "use_stretch") + # Drawing methods for specific constraints. (Shared by object and bone constraint panels) - col = split.column() - col.prop(con, "chain_count") + def draw_childof(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def CHILD_OF(self, _context, layout, con): self.target_template(layout, con) - split = layout.split() - - col = split.column() - col.label(text="Location:") - col.prop(con, "use_location_x", text="X") - col.prop(con, "use_location_y", text="Y") - col.prop(con, "use_location_z", text="Z") - - col = split.column() - col.label(text="Rotation:") - col.prop(con, "use_rotation_x", text="X") - col.prop(con, "use_rotation_y", text="Y") - col.prop(con, "use_rotation_z", text="Z") - - col = split.column() - col.label(text="Scale:") - col.prop(con, "use_scale_x", text="X") - col.prop(con, "use_scale_y", text="Y") - col.prop(con, "use_scale_z", text="Z") + row = layout.row(heading="Location") + row.use_property_decorate = False + row.prop(con, "use_location_x", text="X", toggle=True) + row.prop(con, "use_location_y", text="Y", toggle=True) + row.prop(con, "use_location_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Rotation") + row.use_property_decorate = False + row.prop(con, "use_rotation_x", text="X", toggle=True) + row.prop(con, "use_rotation_y", text="Y", toggle=True) + row.prop(con, "use_rotation_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Scale") + row.use_property_decorate = False + row.prop(con, "use_scale_x", text="X", toggle=True) + row.prop(con, "use_scale_y", text="Y", toggle=True) + row.prop(con, "use_scale_z", text="Z", toggle=True) + row.label(icon='BLANK1') row = layout.row() row.operator("constraint.childof_set_inverse") row.operator("constraint.childof_clear_inverse") - def TRACK_TO(self, _context, layout, con): - self.target_template(layout, con) - - row = layout.row() - row.label(text="To:") - row.prop(con, "track_axis", expand=True) - - row = layout.row() - row.prop(con, "up_axis", text="Up") - row.prop(con, "use_target_z") - - self.space_template(layout, con) - - def IK(self, context, layout, con): - if context.object.pose.ik_solver == 'ITASC': - layout.prop(con, "ik_type") - getattr(self, 'IK_' + con.ik_type)(context, layout, con) - else: - # Standard IK constraint - self.target_template(layout, con) - layout.prop(con, "pole_target") - - if con.pole_target and con.pole_target.type == 'ARMATURE': - layout.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") - - if con.pole_target: - row = layout.row() - row.prop(con, "pole_angle") - row.label() - - split = layout.split() - col = split.column() - col.prop(con, "iterations") - col.prop(con, "chain_count") - - col = split.column() - col.prop(con, "use_tail") - col.prop(con, "use_stretch") - - layout.label(text="Weight:") - - split = layout.split() - col = split.column() - row = col.row(align=True) - row.prop(con, "use_location", text="") - sub = row.row(align=True) - sub.active = con.use_location - sub.prop(con, "weight", text="Position", slider=True) - - col = split.column() - row = col.row(align=True) - row.prop(con, "use_rotation", text="") - sub = row.row(align=True) - sub.active = con.use_rotation - sub.prop(con, "orient_weight", text="Rotation", slider=True) + self.draw_influence(layout, con) - def IK_COPY_POSE(self, _context, layout, con): - self.target_template(layout, con) - self.ik_template(layout, con) + def draw_trackto(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - row = layout.row() - row.label(text="Axis Ref:") - row.prop(con, "reference_axis", expand=True) - split = layout.split(factor=0.33) - split.row().prop(con, "use_location") - row = split.row() - row.prop(con, "weight", text="Weight", slider=True) - row.active = con.use_location - split = layout.split(factor=0.33) - row = split.row() - row.label(text="Lock:") - row = split.row() - row.prop(con, "lock_location_x", text="X") - row.prop(con, "lock_location_y", text="Y") - row.prop(con, "lock_location_z", text="Z") - split.active = con.use_location - - split = layout.split(factor=0.33) - split.row().prop(con, "use_rotation") - row = split.row() - row.prop(con, "orient_weight", text="Weight", slider=True) - row.active = con.use_rotation - split = layout.split(factor=0.33) - row = split.row() - row.label(text="Lock:") - row = split.row() - row.prop(con, "lock_rotation_x", text="X") - row.prop(con, "lock_rotation_y", text="Y") - row.prop(con, "lock_rotation_z", text="Z") - split.active = con.use_rotation - - def IK_DISTANCE(self, _context, layout, con): self.target_template(layout, con) - self.ik_template(layout, con) - layout.prop(con, "limit_mode") + layout.prop(con, "track_axis", expand=True) + layout.prop(con, "up_axis", text="Up", expand=True) + layout.prop(con, "use_target_z") - row = layout.row() - row.prop(con, "weight", text="Weight", slider=True) - row.prop(con, "distance", text="Distance", slider=True) + self.space_template(layout, con) - def FOLLOW_PATH(self, _context, layout, con): - self.target_template(layout, con) - layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA') + self.draw_influence(layout, con) - split = layout.split() + def draw_follow_path(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - col = split.column() - col.prop(con, "use_curve_follow") - col.prop(con, "use_curve_radius") + self.target_template(layout, con) - col = split.column() - col.prop(con, "use_fixed_location") if con.use_fixed_location: - col.prop(con, "offset_factor", text="Offset") + layout.prop(con, "offset_factor", text="Offset Factor") else: - col.prop(con, "offset") + layout.prop(con, "offset") - row = layout.row() - row.label(text="Forward:") - row.prop(con, "forward_axis", expand=True) + layout.prop(con, "forward_axis", expand=True) + layout.prop(con, "up_axis", expand=True) - row = layout.row() - row.prop(con, "up_axis", text="Up") - row.label() + col = layout.column() + col.prop(con, "use_fixed_location") + col.prop(con, "use_curve_radius") + col.prop(con, "use_curve_follow") - def LIMIT_ROTATION(self, _context, layout, con): - split = layout.split() + layout.operator("constraint.followpath_path_animate", text="Animate Path", icon='ANIM_DATA') - col = split.column(align=True) - col.prop(con, "use_limit_x") - sub = col.column(align=True) + self.draw_influence(layout, con) + + def draw_rot_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + # Decorators and property split are really buggy with these properties + row = layout.row(heading="Limit X", align=True) + row.use_property_decorate = False + row.prop(con, "use_limit_x", text="") + sub = row.column(align=True) sub.active = con.use_limit_x sub.prop(con, "min_x", text="Min") sub.prop(con, "max_x", text="Max") + row.label(icon="BLANK1") - col = split.column(align=True) - col.prop(con, "use_limit_y") - sub = col.column(align=True) + row = layout.row(heading="Y", align=True) + row.use_property_decorate = False + row.prop(con, "use_limit_y", text="") + sub = row.column(align=True) sub.active = con.use_limit_y sub.prop(con, "min_y", text="Min") sub.prop(con, "max_y", text="Max") + row.label(icon="BLANK1") - col = split.column(align=True) - col.prop(con, "use_limit_z") - sub = col.column(align=True) + row = layout.row(heading="Z", align=True) + row.use_property_decorate = False + row.prop(con, "use_limit_z", text="") + sub = row.column(align=True) sub.active = con.use_limit_z sub.prop(con, "min_z", text="Min") sub.prop(con, "max_z", text="Max") + row.label(icon="BLANK1") layout.prop(con, "use_transform_limit") + layout.prop(con, "owner_space") - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") - - def LIMIT_LOCATION(self, _context, layout, con): - split = layout.split() - - col = split.column() - col.prop(con, "use_min_x") - sub = col.column() - sub.active = con.use_min_x - sub.prop(con, "min_x", text="") - col.prop(con, "use_max_x") - sub = col.column() - sub.active = con.use_max_x - sub.prop(con, "max_x", text="") - - col = split.column() - col.prop(con, "use_min_y") - sub = col.column() - sub.active = con.use_min_y - sub.prop(con, "min_y", text="") - col.prop(con, "use_max_y") - sub = col.column() - sub.active = con.use_max_y - sub.prop(con, "max_y", text="") - - col = split.column() - col.prop(con, "use_min_z") - sub = col.column() - sub.active = con.use_min_z - sub.prop(con, "min_z", text="") - col.prop(con, "use_max_z") - sub = col.column() - sub.active = con.use_max_z - sub.prop(con, "max_z", text="") + self.draw_influence(layout, con) - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + def draw_loc_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") - - def LIMIT_SCALE(self, _context, layout, con): - split = layout.split() - - col = split.column() - col.prop(con, "use_min_x") - sub = col.column() - sub.active = con.use_min_x - sub.prop(con, "min_x", text="") - col.prop(con, "use_max_x") - sub = col.column() - sub.active = con.use_max_x - sub.prop(con, "max_x", text="") - - col = split.column() - col.prop(con, "use_min_y") - sub = col.column() - sub.active = con.use_min_y - sub.prop(con, "min_y", text="") - col.prop(con, "use_max_y") - sub = col.column() - sub.active = con.use_max_y - sub.prop(con, "max_y", text="") - - col = split.column() - col.prop(con, "use_min_z") - sub = col.column() - sub.active = con.use_min_z - sub.prop(con, "min_z", text="") - col.prop(con, "use_max_z") - sub = col.column() - sub.active = con.use_max_z - sub.prop(con, "max_z", text="") + col = layout.column() - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + row = col.row(heading="Minimum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_x + subsub.prop(con, "min_x", text="") + row.prop_decorator(con, "min_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_y + subsub.prop(con, "min_y", text="") + row.prop_decorator(con, "min_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_z + subsub.prop(con, "min_z", text="") + row.prop_decorator(con, "min_z") + + col.separator() + + row = col.row(heading="Maximum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_x + subsub.prop(con, "max_x", text="") + row.prop_decorator(con, "max_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_y + subsub.prop(con, "max_y", text="") + row.prop_decorator(con, "max_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_z + subsub.prop(con, "max_z", text="") + row.prop_decorator(con, "max_z") - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") + layout.prop(con, "use_transform_limit") + layout.prop(con, "owner_space") - def COPY_ROTATION(self, _context, layout, con): - self.target_template(layout, con) + self.draw_influence(layout, con) - layout.prop(con, "euler_order", text="Order") + def draw_size_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - split = layout.split() + col = layout.column() - col = split.column() - col.prop(con, "use_x", text="X") - sub = col.column() - sub.active = con.use_x - sub.prop(con, "invert_x", text="Invert") + row = col.row(heading="Minimum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_x + subsub.prop(con, "min_x", text="") + row.prop_decorator(con, "min_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_y + subsub.prop(con, "min_y", text="") + row.prop_decorator(con, "min_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_min_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_min_z + subsub.prop(con, "min_z", text="") + row.prop_decorator(con, "min_z") + + col.separator() + + row = col.row(heading="Maximum X", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_x", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_x + subsub.prop(con, "max_x", text="") + row.prop_decorator(con, "max_x") + + row = col.row(heading="Y", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_y", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_y + subsub.prop(con, "max_y", text="") + row.prop_decorator(con, "max_y") + + row = col.row(heading="Z", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_max_z", text="") + subsub = sub.row(align=True) + subsub.active = con.use_max_z + subsub.prop(con, "max_z", text="") + row.prop_decorator(con, "max_z") - col = split.column() - col.prop(con, "use_y", text="Y") - sub = col.column() - sub.active = con.use_y - sub.prop(con, "invert_y", text="Invert") - col = split.column() - col.prop(con, "use_z", text="Z") - sub = col.column() - sub.active = con.use_z - sub.prop(con, "invert_z", text="Invert") + layout.prop(con, "use_transform_limit") + layout.prop(con, "owner_space") - layout.prop(con, "mix_mode", text="Mix") + self.draw_influence(layout, con) - self.space_template(layout, con) + def draw_rotate_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def COPY_LOCATION(self, _context, layout, con): self.target_template(layout, con) - split = layout.split() + layout.prop(con, "euler_order", text="Order") - col = split.column() - col.prop(con, "use_x", text="X") - sub = col.column() - sub.active = con.use_x - sub.prop(con, "invert_x", text="Invert") + row = layout.row(heading="Axis", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_x", text="X", toggle=True) + sub.prop(con, "use_y", text="Y", toggle=True) + sub.prop(con, "use_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Invert", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "invert_x", text="X", toggle=True) + sub.prop(con, "invert_y", text="Y", toggle=True) + sub.prop(con, "invert_z", text="Z", toggle=True) + row.label(icon='BLANK1') - col = split.column() - col.prop(con, "use_y", text="Y") - sub = col.column() - sub.active = con.use_y - sub.prop(con, "invert_y", text="Invert") + layout.prop(con, "mix_mode", text="Mix") - col = split.column() - col.prop(con, "use_z", text="Z") - sub = col.column() - sub.active = con.use_z - sub.prop(con, "invert_z", text="Invert") + self.space_template(layout, con) + + self.draw_influence(layout, con) + + def draw_locate_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + self.target_template(layout, con) + + row = layout.row(heading="Axis", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_x", text="X", toggle=True) + sub.prop(con, "use_y", text="Y", toggle=True) + sub.prop(con, "use_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + row = layout.row(heading="Invert", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "invert_x", text="X", toggle=True) + sub.prop(con, "invert_y", text="Y", toggle=True) + sub.prop(con, "invert_z", text="Z", toggle=True) + row.label(icon='BLANK1') layout.prop(con, "use_offset") self.space_template(layout, con) - def COPY_SCALE(self, _context, layout, con): + self.draw_influence(layout, con) + + def draw_size_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + self.target_template(layout, con) - row = layout.row(align=True) - row.prop(con, "use_x", text="X") - row.prop(con, "use_y", text="Y") - row.prop(con, "use_z", text="Z") + row = layout.row(heading="Axis", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_x", text="X", toggle=True) + sub.prop(con, "use_y", text="Y", toggle=True) + sub.prop(con, "use_z", text="Z", toggle=True) + row.label(icon='BLANK1') - layout.prop(con, "power") - layout.prop(con, "use_make_uniform") + col = layout.column() + col.prop(con, "power") + col.prop(con, "use_make_uniform") - row = layout.row() - row.prop(con, "use_offset") - row = row.row() + col.prop(con, "use_offset") + row = col.row() row.active = con.use_offset row.prop(con, "use_add") self.space_template(layout, con) - def MAINTAIN_VOLUME(self, _context, layout, con): + self.draw_influence(layout, con) + + def draw_same_volume(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True layout.prop(con, "mode") - row = layout.row() - row.label(text="Free:") + row = layout.row(heading="Free Axis") row.prop(con, "free_axis", expand=True) layout.prop(con, "volume") - row = layout.row() - row.label(text="Convert:") - row.prop(con, "owner_space", text="") - - def COPY_TRANSFORMS(self, _context, layout, con): - self.target_template(layout, con) - - layout.prop(con, "mix_mode", text="Mix") + layout.prop(con, "owner_space") - self.space_template(layout, con) + self.draw_influence(layout, con) - # def SCRIPT(self, context, layout, con): + def draw_trans_like(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def ACTION(self, _context, layout, con): self.target_template(layout, con) - split = layout.split() - - col = split.column() - col.label(text="From Target:") - col.prop(con, "transform_channel", text="") - col.prop(con, "target_space", text="") - - col = split.column() - col.label(text="To Action:") - col.prop(con, "action", text="") - col.prop(con, "use_bone_object_action") - - split = layout.split() - - col = split.column(align=True) - col.label(text="Target Range:") - col.prop(con, "min", text="Min") - col.prop(con, "max", text="Max") - - col = split.column(align=True) - col.label(text="Action Range:") - col.prop(con, "frame_start", text="Start") - col.prop(con, "frame_end", text="End") - layout.prop(con, "mix_mode", text="Mix") - def LOCKED_TRACK(self, _context, layout, con): - self.target_template(layout, con) + self.space_template(layout, con) - row = layout.row() - row.label(text="To:") - row.prop(con, "track_axis", expand=True) + self.draw_influence(layout, con) - row = layout.row() - row.label(text="Lock:") - row.prop(con, "lock_axis", expand=True) + def draw_action(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def LIMIT_DISTANCE(self, _context, layout, con): self.target_template(layout, con) - col = layout.column(align=True) - col.prop(con, "distance") - col.operator("constraint.limitdistance_reset") - - row = layout.row() - row.label(text="Clamp Region:") - row.prop(con, "limit_mode", text="") + layout.prop(con, "mix_mode", text="Mix") - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + self.draw_influence(layout, con) - self.space_template(layout, con) + def draw_lock_track(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def STRETCH_TO(self, _context, layout, con): self.target_template(layout, con) - row = layout.row() - row.prop(con, "rest_length", text="Rest Length") - row.operator("constraint.stretchto_reset", text="Reset") - - layout.prop(con, "bulge", text="Volume Variation") - split = layout.split() - col = split.column(align=True) - col.prop(con, "use_bulge_min", text="Volume Min") - sub = col.column() - sub.active = con.use_bulge_min - sub.prop(con, "bulge_min", text="") - col = split.column(align=True) - col.prop(con, "use_bulge_max", text="Volume Max") - sub = col.column() - sub.active = con.use_bulge_max - sub.prop(con, "bulge_max", text="") - col = layout.column() - col.active = con.use_bulge_min or con.use_bulge_max - col.prop(con, "bulge_smooth", text="Smooth") + layout.prop(con, "track_axis", expand=True) + layout.prop(con, "lock_axis", expand=True) - split = layout.split(factor=0.3) - split.label(text="Volume:") - row = split.row() - row.prop(con, "volume", expand=True) + self.draw_influence(layout, con) - split = layout.split(factor=0.3) - split.label(text="Rotation:") - row = split.row() - row.prop(con, "keep_axis", expand=True) + def draw_dist_limit(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def FLOOR(self, _context, layout, con): self.target_template(layout, con) - layout.prop(con, "use_rotation") - layout.prop(con, "offset") - row = layout.row() - row.label(text="Min/Max:") - row.prop(con, "floor_location", expand=True) + row.prop(con, "distance") + row.operator("constraint.limitdistance_reset", text="", icon="X") - self.space_template(layout, con) + layout.prop(con, "limit_mode", text="Clamp Region") - def RIGID_BODY_JOINT(self, _context, layout, con): - self.target_template(layout, con, subtargets=False) - - layout.prop(con, "pivot_type") - layout.prop(con, "child") + layout.prop(con, "use_transform_limit") - row = layout.row() - row.prop(con, "use_linked_collision", text="Linked Collision") - row.prop(con, "show_pivot", text="Display Pivot") - - split = layout.split() - - col = split.column(align=True) - col.label(text="Pivot:") - col.prop(con, "pivot_x", text="X") - col.prop(con, "pivot_y", text="Y") - col.prop(con, "pivot_z", text="Z") - - col = split.column(align=True) - col.label(text="Axis:") - col.prop(con, "axis_x", text="X") - col.prop(con, "axis_y", text="Y") - col.prop(con, "axis_z", text="Z") - - if con.pivot_type == 'CONE_TWIST': - layout.label(text="Limits:") - split = layout.split() - - col = split.column() - col.prop(con, "use_angular_limit_x", text="Angle X") - sub = col.column() - sub.active = con.use_angular_limit_x - sub.prop(con, "limit_angle_max_x", text="") - - col = split.column() - col.prop(con, "use_angular_limit_y", text="Angle Y") - sub = col.column() - sub.active = con.use_angular_limit_y - sub.prop(con, "limit_angle_max_y", text="") - - col = split.column() - col.prop(con, "use_angular_limit_z", text="Angle Z") - sub = col.column() - sub.active = con.use_angular_limit_z - sub.prop(con, "limit_angle_max_z", text="") - - elif con.pivot_type == 'GENERIC_6_DOF': - layout.label(text="Limits:") - split = layout.split() - - col = split.column(align=True) - col.prop(con, "use_limit_x", text="X") - sub = col.column(align=True) - sub.active = con.use_limit_x - sub.prop(con, "limit_min_x", text="Min") - sub.prop(con, "limit_max_x", text="Max") - - col = split.column(align=True) - col.prop(con, "use_limit_y", text="Y") - sub = col.column(align=True) - sub.active = con.use_limit_y - sub.prop(con, "limit_min_y", text="Min") - sub.prop(con, "limit_max_y", text="Max") - - col = split.column(align=True) - col.prop(con, "use_limit_z", text="Z") - sub = col.column(align=True) - sub.active = con.use_limit_z - sub.prop(con, "limit_min_z", text="Min") - sub.prop(con, "limit_max_z", text="Max") - - split = layout.split() - - col = split.column(align=True) - col.prop(con, "use_angular_limit_x", text="Angle X") - sub = col.column(align=True) - sub.active = con.use_angular_limit_x - sub.prop(con, "limit_angle_min_x", text="Min") - sub.prop(con, "limit_angle_max_x", text="Max") - - col = split.column(align=True) - col.prop(con, "use_angular_limit_y", text="Angle Y") - sub = col.column(align=True) - sub.active = con.use_angular_limit_y - sub.prop(con, "limit_angle_min_y", text="Min") - sub.prop(con, "limit_angle_max_y", text="Max") - - col = split.column(align=True) - col.prop(con, "use_angular_limit_z", text="Angle Z") - sub = col.column(align=True) - sub.active = con.use_angular_limit_z - sub.prop(con, "limit_angle_min_z", text="Min") - sub.prop(con, "limit_angle_max_z", text="Max") - - elif con.pivot_type == 'HINGE': - layout.label(text="Limits:") - split = layout.split() + self.space_template(layout, con) - row = split.row(align=True) - col = row.column() - col.prop(con, "use_angular_limit_x", text="Angle X") + self.draw_influence(layout, con) - col = row.column() - col.active = con.use_angular_limit_x - col.prop(con, "limit_angle_min_x", text="Min") - col = row.column() - col.active = con.use_angular_limit_x - col.prop(con, "limit_angle_max_x", text="Max") + def draw_stretch_to(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def CLAMP_TO(self, _context, layout, con): self.target_template(layout, con) row = layout.row() - row.label(text="Main Axis:") - row.prop(con, "main_axis", expand=True) - - layout.prop(con, "use_cyclic") - - def TRANSFORM(self, _context, layout, con): - self.target_template(layout, con) + row.prop(con, "rest_length") + row.operator("constraint.stretchto_reset", text="", icon="X") - layout.prop(con, "use_motion_extrapolate", text="Extrapolate") + layout.separator() col = layout.column() - col.row().label(text="Source:") - col.row().prop(con, "map_from", expand=True) - - if con.map_from == 'ROTATION': - layout.prop(con, "from_rotation_mode", text="Mode") - - split = layout.split() - ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale" + col.prop(con, "bulge", text="Volume Variation") + + row = col.row(heading="Volume Min", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_bulge_min", text="") + subsub = sub.row(align=True) + subsub.active = con.use_bulge_min + subsub.prop(con, "bulge_min", text="") + row.prop_decorator(con, "bulge_min") + + row = col.row(heading="Max", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_bulge_max", text="") + subsub = sub.row(align=True) + subsub.active = con.use_bulge_max + subsub.prop(con, "bulge_max", text="") + row.prop_decorator(con, "bulge_max") - sub = split.column(align=True) - sub.label(text="X:") - sub.prop(con, "from_min_x" + ext, text="Min") - sub.prop(con, "from_max_x" + ext, text="Max") + row = col.row() + row.active = con.use_bulge_min or con.use_bulge_max + row.prop(con, "bulge_smooth", text="Smooth") - sub = split.column(align=True) - sub.label(text="Y:") - sub.prop(con, "from_min_y" + ext, text="Min") - sub.prop(con, "from_max_y" + ext, text="Max") + layout.prop(con, "volume", expand=True) + layout.prop(con, "keep_axis", text="Rotation", expand=True) - sub = split.column(align=True) - sub.label(text="Z:") - sub.prop(con, "from_min_z" + ext, text="Min") - sub.prop(con, "from_max_z" + ext, text="Max") + self.draw_influence(layout, con) - col = layout.column() - row = col.row() - row.label(text="Source to Destination Mapping:") + def draw_min_max(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - # note: chr(187) is the ASCII arrow ( >> ). Blender Text Editor can't - # open it. Thus we are using the hard-coded value instead. - row = col.row() - row.prop(con, "map_to_x_from", expand=False, text="") - row.label(text=" %s X" % chr(187)) + self.target_template(layout, con) - row = col.row() - row.prop(con, "map_to_y_from", expand=False, text="") - row.label(text=" %s Y" % chr(187)) + layout.prop(con, "offset") + layout.prop(con, "floor_location", expand=True, text="Min/Max") + layout.prop(con, "use_rotation") - row = col.row() - row.prop(con, "map_to_z_from", expand=False, text="") - row.label(text=" %s Z" % chr(187)) + self.space_template(layout, con) - split = layout.split() + self.draw_influence(layout, con) - col = split.column() - col.label(text="Destination:") - col.row().prop(con, "map_to", expand=True) + def draw_clamp_to(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - if con.map_to == 'ROTATION': - layout.prop(con, "to_euler_order", text="Order") + self.target_template(layout, con) - split = layout.split() - ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale" + layout.prop(con, "main_axis", expand=True) - col = split.column() - col.label(text="X:") + layout.prop(con, "use_cyclic") - sub = col.column(align=True) - sub.prop(con, "to_min_x" + ext, text="Min") - sub.prop(con, "to_max_x" + ext, text="Max") + self.draw_influence(layout, con) - col = split.column() - col.label(text="Y:") + def draw_transform(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - sub = col.column(align=True) - sub.prop(con, "to_min_y" + ext, text="Min") - sub.prop(con, "to_max_y" + ext, text="Max") + self.target_template(layout, con) - col = split.column() - col.label(text="Z:") + layout.prop(con, "use_motion_extrapolate", text="Extrapolate") - sub = col.column(align=True) - sub.prop(con, "to_min_z" + ext, text="Min") - sub.prop(con, "to_max_z" + ext, text="Max") + self.space_template(layout, con) - layout.prop(con, "mix_mode" + ext, text="Mix") + self.draw_influence(layout, con) - self.space_template(layout, con) + def draw_shrinkwrap(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def SHRINKWRAP(self, _context, layout, con): self.target_template(layout, con, False) layout.prop(con, "distance") - layout.prop(con, "shrinkwrap_type") + layout.prop(con, "shrinkwrap_type", text="Mode") - if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}: - layout.prop(con, "wrap_mode", text="Snap Mode") + layout.separator() if con.shrinkwrap_type == 'PROJECT': - row = layout.row(align=True) - row.prop(con, "project_axis", expand=True) - split = layout.split(factor=0.4) - split.label(text="Axis Space:") - rowsub = split.row() - rowsub.prop(con, "project_axis_space", text="") - split = layout.split(factor=0.4) - split.label(text="Face Culling:") - rowsub = split.row() - rowsub.prop(con, "cull_face", expand=True) - row = layout.row() - row.prop(con, "use_project_opposite") - rowsub = row.row() - rowsub.active = con.use_project_opposite and con.cull_face != 'OFF' - rowsub.prop(con, "use_invert_cull") - layout.prop(con, "project_limit") + layout.prop(con, "project_axis", expand=True, text="Project Axis") + layout.prop(con, "project_axis_space", text="Space") + layout.prop(con, "project_limit", text="Distance") + layout.prop(con, "use_project_opposite") - if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}: - layout.prop(con, "use_track_normal") + layout.separator() - row = layout.row(align=True) - row.active = con.use_track_normal - row.prop(con, "track_axis", expand=True) + col = layout.column() + row = col.row() + row.prop(con, "cull_face", expand=True) + row = col.row() + row.active = con.use_project_opposite and con.cull_face != 'OFF' + row.prop(con, "use_invert_cull") - def DAMPED_TRACK(self, _context, layout, con): - self.target_template(layout, con) + layout.separator() - row = layout.row() - row.label(text="To:") - row.prop(con, "track_axis", expand=True) + if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE', 'TARGET_PROJECT'}: + layout.prop(con, "wrap_mode", text="Snap Mode") + row = layout.row(heading="Align to Normal", align=True) + row.use_property_decorate = False + sub = row.row(align=True) + sub.prop(con, "use_track_normal", text="") + subsub = sub.row(align=True) + subsub.active = con.use_track_normal + subsub.prop(con, "track_axis", text="") + row.prop_decorator(con, "track_axis") + + self.draw_influence(layout, con) + + def draw_damp_track(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def SPLINE_IK(self, _context, layout, con): self.target_template(layout, con) - col = layout.column() - col.label(text="Spline Fitting:") - col.prop(con, "chain_count") - col.prop(con, "use_even_divisions") - col.prop(con, "use_chain_offset") + layout.prop(con, "track_axis", expand=True) - col = layout.column() - col.label(text="Chain Scaling:") - col.prop(con, "use_curve_radius") + self.draw_influence(layout, con) - layout.prop(con, "y_scale_mode") - layout.prop(con, "xz_scale_mode") + def draw_spline_ik(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}: - layout.prop(con, "use_original_scale") + self.target_template(layout, con) - if con.xz_scale_mode == 'VOLUME_PRESERVE': - layout.prop(con, "bulge", text="Volume Variation") - split = layout.split() - col = split.column(align=True) - col.prop(con, "use_bulge_min", text="Volume Min") - sub = col.column() - sub.active = con.use_bulge_min - sub.prop(con, "bulge_min", text="") - col = split.column(align=True) - col.prop(con, "use_bulge_max", text="Volume Max") - sub = col.column() - sub.active = con.use_bulge_max - sub.prop(con, "bulge_max", text="") - col = layout.column() - col.active = con.use_bulge_min or con.use_bulge_max - col.prop(con, "bulge_smooth", text="Smooth") + self.draw_influence(layout, con) + + def draw_pivot(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - def PIVOT(self, _context, layout, con): self.target_template(layout, con) if con.target: - col = layout.column() - col.prop(con, "offset", text="Pivot Offset") + layout.prop(con, "offset", text="Pivot Offset") else: - col = layout.column() - col.prop(con, "use_relative_location") + layout.prop(con, "use_relative_location") if con.use_relative_location: - col.prop(con, "offset", text="Relative Pivot Point") + layout.prop(con, "offset", text="Pivot Point") else: - col.prop(con, "offset", text="Absolute Pivot Point") + layout.prop(con, "offset", text="Pivot Point") col = layout.column() - col.prop(con, "rotation_range", text="Pivot When") + col.prop(con, "rotation_range", text="Rotation Range") - @staticmethod - def _getConstraintClip(context, con): - if not con.use_active_clip: - return con.clip + self.draw_influence(layout, con) + + def draw_follow_track(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + clip = None + if con.use_active_clip: + clip = context.scene.active_clip else: - return context.scene.active_clip + clip = con.clip - def FOLLOW_TRACK(self, context, layout, con): - clip = self._getConstraintClip(context, con) + layout.prop(con, "use_active_clip") + layout.prop(con, "use_3d_position") row = layout.row() - row.prop(con, "use_active_clip") - row.prop(con, "use_3d_position") - - sub = row.column() - sub.active = not con.use_3d_position - sub.prop(con, "use_undistorted_position") + row.active = not con.use_3d_position + row.prop(con, "use_undistorted_position") - col = layout.column() if not con.use_active_clip: - col.prop(con, "clip") + layout.prop(con, "clip") - row = col.row() - row.prop(con, "frame_method", expand=True) + layout.prop(con, "frame_method") if clip: tracking = clip.tracking - col.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA') + layout.prop_search(con, "object", tracking, "objects", icon='OBJECT_DATA') tracking_object = tracking.objects.get(con.object, tracking.objects[0]) - col.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA') + layout.prop_search(con, "track", tracking_object, "tracks", icon='ANIM_DATA') - col.prop(con, "camera") + layout.prop(con, "camera") - row = col.row() + row = layout.row() row.active = not con.use_3d_position row.prop(con, "depth_object") layout.operator("clip.constraint_to_fcurve") - def CAMERA_SOLVER(self, _context, layout, con): + self.draw_influence(layout, con) + + def draw_camera_solver(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + layout.prop(con, "use_active_clip") if not con.use_active_clip: @@ -915,8 +783,19 @@ class ConstraintButtonsPanel: layout.operator("clip.constraint_to_fcurve") - def OBJECT_SOLVER(self, context, layout, con): - clip = self._getConstraintClip(context, con) + self.draw_influence(layout, con) + + def draw_object_solver(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + clip = None + if con.use_active_clip: + clip = context.scene.active_clip + else: + clip = con.clip layout.prop(con, "use_active_clip") @@ -934,36 +813,240 @@ class ConstraintButtonsPanel: layout.operator("clip.constraint_to_fcurve") - def TRANSFORM_CACHE(self, _context, layout, con): - layout.label(text="Cache File Properties:") - box = layout.box() - box.template_cache_file(con, "cache_file") + self.draw_influence(layout, con) - cache_file = con.cache_file + def draw_transform_cache(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + layout.template_cache_file(con, "cache_file") - layout.label(text="Constraint Properties:") - box = layout.box() + cache_file = con.cache_file if cache_file is not None: - box.prop_search(con, "object_path", cache_file, "object_paths") + layout.prop_search(con, "object_path", cache_file, "object_paths") + + self.draw_influence(layout, con) - def SCRIPT(self, _context, layout, _con): + def draw_python_constraint(self, context): + layout = self.layout layout.label(text="Blender 2.6 doesn't support python constraints yet") - def ARMATURE(self, context, layout, con): - topcol = layout.column() - topcol.use_property_split = True - topcol.operator("constraint.add_target", text="Add Target Bone") + def draw_armature(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + col = layout.column() + col.prop(con, "use_deform_preserve_volume") + col.prop(con, "use_bone_envelopes") + + if context.pose_bone: + col.prop(con, "use_current_location") + + layout.operator("constraint.add_target", text="Add Target Bone") + + layout.operator("constraint.normalize_target_weights") + + self.draw_influence(layout, con) if not con.targets: - box = topcol.box() - box.label(text="No target bones were added", icon='ERROR') + layout.label(text="No target bones added", icon='ERROR') - for i, tgt in enumerate(con.targets): - box = topcol.box() + def draw_kinematic(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + self.target_template(layout, con) + + if context.object.pose.ik_solver == 'ITASC': + layout.prop(con, "ik_type") + # This button gives itself too much padding, so put it in a column with the subtarget + col = layout.column() + col.prop(con, "pole_target") + + if con.pole_target and con.pole_target.type == 'ARMATURE': + col.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") + + col = layout.column() + if con.pole_target: + col.prop(con, "pole_angle") + col.prop(con, "use_tail") + col.prop(con, "use_stretch") + col.prop(con, "chain_count") + + if con.ik_type == 'COPY_POSE': + layout.prop(con, "reference_axis", expand=True) + + # Use separate rows and columns here to avoid an alignment issue with the lock buttons + loc_col = layout.column() + loc_col.prop(con, "use_location") + + row = loc_col.row() + row.active = con.use_location + row.prop(con, "weight", text="Weight", slider=True) + + row = loc_col.row(heading="Lock", align=True) + row.use_property_decorate = False + row.active = con.use_location + sub = row.row(align=True) + sub.prop(con, "lock_location_x", text="X", toggle=True) + sub.prop(con, "lock_location_y", text="Y", toggle=True) + sub.prop(con, "lock_location_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + rot_col = layout.column() + rot_col.prop(con, "use_rotation") + + row = rot_col.row() + row.active = con.use_rotation + row.prop(con, "orient_weight", text="Weight", slider=True) + + row = rot_col.row(heading="Lock", align=True) + row.use_property_decorate = False + row.active = con.use_rotation + sub = row.row(align=True) + sub.prop(con, "lock_rotation_x", text="X", toggle=True) + sub.prop(con, "lock_rotation_y", text="Y", toggle=True) + sub.prop(con, "lock_rotation_z", text="Z", toggle=True) + row.label(icon='BLANK1') + + elif con.ik_type == 'DISTANCE': + layout.prop(con, "limit_mode") + + col = layout.column() + col.prop(con, "weight", text="Weight", slider=True) + col.prop(con, "distance", text="Distance", slider=True) + else: + # Standard IK constraint + col = layout.column() + col.prop(con, "pole_target") + + if con.pole_target and con.pole_target.type == 'ARMATURE': + col.prop_search(con, "pole_subtarget", con.pole_target.data, "bones", text="Bone") + + col = layout.column() + if con.pole_target: + col.prop(con, "pole_angle") + col.prop(con, "iterations") + col.prop(con, "chain_count") + col.prop(con, "use_tail") + col.prop(con, "use_stretch") + + col = layout.column() + row = col.row(align=True, heading="Weight Position") + row.prop(con, "use_location", text="") + sub = row.row(align=True) + sub.active = con.use_location + sub.prop(con, "weight", text="", slider=True) + + row = col.row(align=True, heading="Rotation") + row.prop(con, "use_rotation", text="") + sub = row.row(align=True) + sub.active = con.use_rotation + sub.prop(con, "orient_weight", text="", slider=True) + + self.draw_influence(layout, con) + + +# Parent class for constraint subpanels +class ConstraintButtonsSubPanel(Panel): + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_label = "" + bl_options = {'DRAW_BOX'} + + def get_constraint(self, context): + con = None + if context.pose_bone: + con = context.pose_bone.constraints[self.list_panel_index] + else: + con = context.object.constraints[self.list_panel_index] + self.layout.context_pointer_set("constraint", con) + return con + + def draw_transform_source(self, context): + layout = self.layout + con = self.get_constraint(context) + + layout.prop(con, "map_from", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = True + + if con.map_from == 'ROTATION': + layout.prop(con, "from_rotation_mode", text="Mode") + + ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale" + + col = layout.column(align=True) + col.prop(con, "from_min_x" + ext, text="X Min") + col.prop(con, "from_max_x" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "from_min_y" + ext, text="Y Min") + col.prop(con, "from_max_y" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "from_min_z" + ext, text="Z Min") + col.prop(con, "from_max_z" + ext, text="Max") + + def draw_transform_mapping(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + layout.prop(con, "map_to_x_from", expand=False, text="Source Axis X") + + layout.prop(con, "map_to_y_from", expand=False, text="Y") + + layout.prop(con, "map_to_z_from", expand=False, text="Z") + + def draw_transform_destination(self, context): + layout = self.layout + con = self.get_constraint(context) + + layout.prop(con, "map_to", expand=True) + + layout.use_property_split = True + layout.use_property_decorate = True + + if con.map_to == 'ROTATION': + layout.prop(con, "to_euler_order", text="Order") + + ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale" + + col = layout.column(align=True) + col.prop(con, "to_min_x" + ext, text="X Min") + col.prop(con, "to_max_x" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "to_min_y" + ext, text="Y Min") + col.prop(con, "to_max_y" + ext, text="Max") + + col = layout.column(align=True) + col.prop(con, "to_min_z" + ext, text="Z Min") + col.prop(con, "to_max_z" + ext, text="Max") + + layout.prop(con, "mix_mode" + ext, text="Mix") + + def draw_armature_bones(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True + + for i, tgt in enumerate(con.targets): has_target = tgt.target is not None + box = layout.box() header = box.row() header.use_property_split = False @@ -977,61 +1060,615 @@ class ConstraintButtonsPanel: else: row.prop(tgt, "subtarget", text="", icon='BONE_DATA') - header.operator("constraint.remove_target", text="", icon='REMOVE').index = i + header.operator("constraint.remove_target", text="", icon='X').index = i - col = box.column() - col.active = has_target and tgt.subtarget != "" - col.prop(tgt, "weight", slider=True) + row = box.row() + row.active = has_target and tgt.subtarget != "" + row.prop(tgt, "weight", slider=True, text="Weight") - topcol.operator("constraint.normalize_target_weights") - topcol.prop(con, "use_deform_preserve_volume") - topcol.prop(con, "use_bone_envelopes") + def draw_spline_ik_fitting(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - if context.pose_bone: - topcol.prop(con, "use_current_location") + col = layout.column() + col.prop(con, "chain_count") + col.prop(con, "use_even_divisions") + col.prop(con, "use_chain_offset") + def draw_spline_ik_chain_scaling(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True -class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel): - bl_label = "Object Constraints" - bl_context = "constraint" - bl_options = {'HIDE_HEADER'} + layout.prop(con, "use_curve_radius") - @classmethod - def poll(cls, context): - return (context.object) + layout.prop(con, "y_scale_mode") + layout.prop(con, "xz_scale_mode") - def draw(self, context): + if con.xz_scale_mode in {'INVERSE_PRESERVE', 'VOLUME_PRESERVE'}: + layout.prop(con, "use_original_scale") + + if con.xz_scale_mode == 'VOLUME_PRESERVE': + col = layout.column() + col.prop(con, "bulge", text="Volume Variation") + + row = col.row(heading="Volume Min") + row.prop(con, "use_bulge_min", text="") + sub = row.row() + sub.active = con.use_bulge_min + sub.prop(con, "bulge_min", text="") + + row = col.row(heading="Max") + row.prop(con, "use_bulge_max", text="") + sub = row.row() + sub.active = con.use_bulge_max + sub.prop(con, "bulge_max", text="") + + row = layout.row() + row.active = con.use_bulge_min or con.use_bulge_max + row.prop(con, "bulge_smooth", text="Smooth") + + def draw_action_target(self, context): layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True - obj = context.object + layout.prop(con, "transform_channel", text="Channel") + layout.prop(con, "target_space") - layout.operator_menu_enum("object.constraint_add", "type", text="Add Object Constraint") + col = layout.column(align=True) + col.prop(con, "min", text="Range Min") + col.prop(con, "max", text="Max") - for con in obj.constraints: - self.draw_constraint(context, con) + def draw_action_action(self, context): + layout = self.layout + con = self.get_constraint(context) + layout.use_property_split = True + layout.use_property_decorate = True -class BONE_PT_constraints(ConstraintButtonsPanel, Panel): - bl_label = "Bone Constraints" - bl_context = "bone_constraint" - bl_options = {'HIDE_HEADER'} + layout.prop(con, "action") + layout.prop(con, "use_bone_object_action") + + col = layout.column(align=True) + col.prop(con, "frame_start", text="Frame Start") + col.prop(con, "frame_end", text="End") - @classmethod - def poll(cls, context): - return (context.pose_bone) +# Child Of Constraint + +class OBJECT_PT_bChildOfConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): def draw(self, context): - layout = self.layout + self.draw_childof(context) - layout.operator_menu_enum("pose.constraint_add", "type", text="Add Bone Constraint") - for con in context.pose_bone.constraints: - self.draw_constraint(context, con) +class BONE_PT_bChildOfConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_childof(context) + +# Track To Constraint + +class OBJECT_PT_bTrackToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trackto(context) + + +class BONE_PT_bTrackToConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trackto(context) + +# Follow Path Constraint + +class OBJECT_PT_bFollowPathConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_path(context) + + +class BONE_PT_bFollowPathConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_path(context) + + +# Rotation Limit Constraint + +class OBJECT_PT_bRotLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rot_limit(context) + + +class BONE_PT_bRotLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rot_limit(context) + + +# Location Limit Constraint + +class OBJECT_PT_bLocLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_loc_limit(context) + + +class BONE_PT_bLocLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_loc_limit(context) + + +# Size Limit Constraint + +class OBJECT_PT_bSizeLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_limit(context) + + +class BONE_PT_bSizeLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_limit(context) + + +# Rotate Like Constraint + +class OBJECT_PT_bRotateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rotate_like(context) + + +class BONE_PT_bRotateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_rotate_like(context) + + +# Locate Like Constraint + +class OBJECT_PT_bLocateLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_locate_like(context) + + +class BONE_PT_bLocateLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_locate_like(context) + + +# Size Like Constraint + +class OBJECT_PT_bSizeLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_like(context) + + +class BONE_PT_bSizeLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_size_like(context) + + +# Same Volume Constraint + +class OBJECT_PT_bSameVolumeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_same_volume(context) + + +class BONE_PT_bSameVolumeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_same_volume(context) + + +# Trans Like Constraint + +class OBJECT_PT_bTransLikeConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trans_like(context) + + +class BONE_PT_bTransLikeConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_trans_like(context) + + +# Action Constraint + +class OBJECT_PT_bActionConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_action(context) + + +class BONE_PT_bActionConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_action(context) + + +class OBJECT_PT_bActionConstraint_target(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bActionConstraint" + bl_label = "Target" + + def draw(self, context): + self.draw_action_target(context) + + +class BONE_PT_bActionConstraint_target(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bActionConstraint" + bl_label = "Target" + + def draw(self, context): + self.draw_action_target(context) + + +class OBJECT_PT_bActionConstraint_action(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bActionConstraint" + bl_label = "Action" + + def draw(self, context): + self.draw_action_action(context) + + +class BONE_PT_bActionConstraint_action(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bActionConstraint" + bl_label = "Action" + + def draw(self, context): + self.draw_action_action(context) + + +# Lock Track Constraint + +class OBJECT_PT_bLockTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_lock_track(context) + + +class BONE_PT_bLockTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_lock_track(context) + + +# Disance Limit Constraint + +class OBJECT_PT_bDistLimitConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_dist_limit(context) + + +class BONE_PT_bDistLimitConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_dist_limit(context) + + +# Stretch To Constraint + +class OBJECT_PT_bStretchToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_stretch_to(context) + + +class BONE_PT_bStretchToConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_stretch_to(context) + + +# Min Max Constraint + +class OBJECT_PT_bMinMaxConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_min_max(context) + + +class BONE_PT_bMinMaxConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_min_max(context) + + +# Clamp To Constraint + +class OBJECT_PT_bClampToConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_clamp_to(context) + + +class BONE_PT_bClampToConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_clamp_to(context) + + +# Transform Constraint + +class OBJECT_PT_bTransformConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform(context) + + +class BONE_PT_bTransformConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform(context) + + +class OBJECT_PT_bTransformConstraint_source(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bTransformConstraint" + bl_label = "Source" + + def draw(self, context): + self.draw_transform_source(context) + + +class BONE_PT_bTransformConstraint_source(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bTransformConstraint" + bl_label = "Source" + + def draw(self, context): + self.draw_transform_source(context) + + +class OBJECT_PT_bTransformConstraint_mapping(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bTransformConstraint" + bl_label = "Mapping" + + def draw(self, context): + self.draw_transform_mapping(context) + + +class BONE_PT_bTransformConstraint_mapping(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bTransformConstraint" + bl_label = "Mapping" + + def draw(self, context): + self.draw_transform_mapping(context) + + +class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bTransformConstraint" + bl_label = "Destination" + + def draw(self, context): + self.draw_transform_destination(context) + + +class BONE_PT_bTransformConstraint_destination(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bTransformConstraint" + bl_label = "Destination" + + def draw(self, context): + self.draw_transform_destination(context) + + +# Shrinkwrap Constraint + +class OBJECT_PT_bShrinkwrapConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_shrinkwrap(context) + + +class BONE_PT_bShrinkwrapConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_shrinkwrap(context) + + +# Damp Track Constraint + +class OBJECT_PT_bDampTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_damp_track(context) + + +class BONE_PT_bDampTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_damp_track(context) + + +# Spline IK Constraint + +class BONE_PT_bSplineIKConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_spline_ik(context) + + +class BONE_PT_bSplineIKConstraint_fitting(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bSplineIKConstraint" + bl_label = "Fitting" + + def draw(self, context): + self.draw_spline_ik_fitting(context) + + +class BONE_PT_bSplineIKConstraint_chain_scaling(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bSplineIKConstraint" + bl_label = "Chain Scaling" + + def draw(self, context): + self.draw_spline_ik_chain_scaling(context) + + +# Pivot Constraint + +class OBJECT_PT_bPivotConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_pivot(context) + + +class BONE_PT_bPivotConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_pivot(context) + + +# Follow Track Constraint + +class OBJECT_PT_bFollowTrackConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_track(context) + + +class BONE_PT_bFollowTrackConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_follow_track(context) + + +# Camera Solver Constraint + +class OBJECT_PT_bCameraSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_camera_solver(context) + + +class BONE_PT_bCameraSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_camera_solver(context) + + +# Object Solver Constraint + +class OBJECT_PT_bObjectSolverConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_object_solver(context) + + +class BONE_PT_bObjectSolverConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_object_solver(context) + + +# Transform Cache Constraint + +class OBJECT_PT_bTransformCacheConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform_cache(context) + + +class BONE_PT_bTransformCacheConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_transform_cache(context) + + +# Python Constraint + +class OBJECT_PT_bPythonConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_python_constraint(context) + +class BONE_PT_bPythonConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_python_constraint(context) + + + +# Armature Constraint + +class OBJECT_PT_bArmatureConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_armature(context) + + +class BONE_PT_bArmatureConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_armature(context) + + +class OBJECT_PT_bArmatureConstraint_bones(ObjectConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "OBJECT_PT_bArmatureConstraint" + bl_label = "Bones" + + def draw(self, context): + self.draw_armature_bones(context) + + +class BONE_PT_bArmatureConstraint_bones(BoneConstraintPanel, ConstraintButtonsSubPanel): + bl_parent_id = "BONE_PT_bArmatureConstraint" + bl_label = "Bones" + + def draw(self, context): + self.draw_armature_bones(context) + + +# Inverse Kinematic Constraint + +class OBJECT_PT_bKinematicConstraint(ObjectConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_kinematic(context) + + +class BONE_PT_bKinematicConstraint(BoneConstraintPanel, ConstraintButtonsPanel): + def draw(self, context): + self.draw_kinematic(context) + classes = ( + # Object Panels OBJECT_PT_constraints, BONE_PT_constraints, + OBJECT_PT_bChildOfConstraint, + OBJECT_PT_bTrackToConstraint, + OBJECT_PT_bKinematicConstraint, + OBJECT_PT_bFollowPathConstraint, + OBJECT_PT_bRotLimitConstraint, + OBJECT_PT_bLocLimitConstraint, + OBJECT_PT_bSizeLimitConstraint, + OBJECT_PT_bRotateLikeConstraint, + OBJECT_PT_bLocateLikeConstraint, + OBJECT_PT_bSizeLikeConstraint, + OBJECT_PT_bSameVolumeConstraint, + OBJECT_PT_bTransLikeConstraint, + OBJECT_PT_bActionConstraint, + OBJECT_PT_bActionConstraint_target, + OBJECT_PT_bActionConstraint_action, + OBJECT_PT_bLockTrackConstraint, + OBJECT_PT_bDistLimitConstraint, + OBJECT_PT_bStretchToConstraint, + OBJECT_PT_bMinMaxConstraint, + OBJECT_PT_bClampToConstraint, + OBJECT_PT_bTransformConstraint, + OBJECT_PT_bTransformConstraint_source, + OBJECT_PT_bTransformConstraint_mapping, + OBJECT_PT_bTransformConstraint_destination, + OBJECT_PT_bShrinkwrapConstraint, + OBJECT_PT_bDampTrackConstraint, + OBJECT_PT_bPivotConstraint, + OBJECT_PT_bFollowTrackConstraint, + OBJECT_PT_bCameraSolverConstraint, + OBJECT_PT_bObjectSolverConstraint, + OBJECT_PT_bTransformCacheConstraint, + OBJECT_PT_bPythonConstraint, + OBJECT_PT_bArmatureConstraint, + OBJECT_PT_bArmatureConstraint_bones, + # Bone panels + BONE_PT_bChildOfConstraint, + BONE_PT_bTrackToConstraint, + BONE_PT_bKinematicConstraint, + BONE_PT_bFollowPathConstraint, + BONE_PT_bRotLimitConstraint, + BONE_PT_bLocLimitConstraint, + BONE_PT_bSizeLimitConstraint, + BONE_PT_bRotateLikeConstraint, + BONE_PT_bLocateLikeConstraint, + BONE_PT_bSizeLikeConstraint, + BONE_PT_bSameVolumeConstraint, + BONE_PT_bTransLikeConstraint, + BONE_PT_bActionConstraint, + BONE_PT_bActionConstraint_target, + BONE_PT_bActionConstraint_action, + BONE_PT_bLockTrackConstraint, + BONE_PT_bDistLimitConstraint, + BONE_PT_bStretchToConstraint, + BONE_PT_bMinMaxConstraint, + BONE_PT_bClampToConstraint, + BONE_PT_bTransformConstraint, + BONE_PT_bTransformConstraint_source, + BONE_PT_bTransformConstraint_mapping, + BONE_PT_bTransformConstraint_destination, + BONE_PT_bShrinkwrapConstraint, + BONE_PT_bDampTrackConstraint, + BONE_PT_bSplineIKConstraint, + BONE_PT_bSplineIKConstraint_fitting, + BONE_PT_bSplineIKConstraint_chain_scaling, + BONE_PT_bPivotConstraint, + BONE_PT_bFollowTrackConstraint, + BONE_PT_bCameraSolverConstraint, + BONE_PT_bObjectSolverConstraint, + BONE_PT_bTransformCacheConstraint, + BONE_PT_bPythonConstraint, + BONE_PT_bArmatureConstraint, + BONE_PT_bArmatureConstraint_bones, ) if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 62dffa3b6ba..3de144c5a15 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -84,7 +84,6 @@ class DATA_PT_lens(CameraButtonsPanel, Panel): col.separator() if cam.type == 'PERSP': - col = layout.column() if cam.lens_unit == 'MILLIMETERS': col.prop(cam, "lens") elif cam.lens_unit == 'FOV': diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py index 4ed5264549f..946578937bb 100644 --- a/release/scripts/startup/bl_ui/properties_data_gpencil.py +++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py @@ -214,10 +214,6 @@ class DATA_PT_gpencil_onion_skinning(DataButtonsPanel, Panel): layout = self.layout layout.use_property_split = True - layout.enabled = gpd.users <= 1 - - if gpd.users > 1: - layout.label(text="Multiuser datablock not supported", icon='ERROR') col = layout.column() col.prop(gpd, "onion_mode") diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 425c94dfdcd..fbd8e2d7cff 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -459,6 +459,31 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, Panel): col.operator("mesh.vertex_color_remove", icon='REMOVE', text="") +class DATA_PT_sculpt_vertex_colors(MeshButtonsPanel, Panel): + bl_label = "Sculpt Vertex Colors" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + + me = context.mesh + + row = layout.row() + col = row.column() + + col.template_list("MESH_UL_vcols", "svcols", me, "sculpt_vertex_colors", me.sculpt_vertex_colors, "active_index", rows=2) + + col = row.column(align=True) + col.operator("mesh.sculpt_vertex_color_add", icon='ADD', text="") + col.operator("mesh.sculpt_vertex_color_remove", icon='REMOVE', text="") + + row = layout.row() + col = row.column() + col.operator("sculpt.vertex_to_loop_colors", text="Store Sculpt Vertex Color") + col.operator("sculpt.loop_to_vertex_colors", text="Load Sculpt Vertex Color") + + class DATA_PT_remesh(MeshButtonsPanel, Panel): bl_label = "Remesh" bl_options = {'DEFAULT_CLOSED'} @@ -483,6 +508,8 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): col.prop(mesh, "use_remesh_preserve_volume", text="Volume") col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") + col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") + col.operator("object.voxel_remesh", text="Voxel Remesh") else: col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh") @@ -537,6 +564,7 @@ classes = ( DATA_PT_shape_keys, DATA_PT_uv_texture, DATA_PT_vertex_colors, + DATA_PT_sculpt_vertex_colors, DATA_PT_face_maps, DATA_PT_normals, DATA_PT_texture_space, diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index e7536838199..d464a3ffc6b 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -46,13 +46,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): bl_label = "Modifiers" - def check_conflicts(self, layout, ob): - for md in ob.grease_pencil_modifiers: - if md.type == 'GP_TIME': - row = layout.row() - row.label(text="Build and Time Offset modifier not compatible", icon='ERROR') - break - @classmethod def poll(cls, context): ob = context.object @@ -60,483 +53,8 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): def draw(self, context): layout = self.layout - - ob = context.object - layout.operator_menu_enum("object.gpencil_modifier_add", "type") - - for md in ob.grease_pencil_modifiers: - box = layout.template_greasepencil_modifier(md) - if box: - # match enum type to our functions, avoids a lookup table. - getattr(self, md.type)(box, ob, md) - - # the mt.type enum is (ab)used for a lookup on function names - # ...to avoid lengthy if statements - # so each type must have a function here. - - def gpencil_masking(self, layout, ob, md, use_vertex, use_curve=False): - gpd = ob.data - layout.separator() - layout.label(text="Influence Filters:") - - split = layout.split(factor=0.25) - - col1 = split.column() - - col1.label(text="Layer:") - col1.label(text="Material:") - if use_vertex: - col1.label(text="Vertex Group:") - - col2 = split.column() - - split = col2.split(factor=0.6) - row = split.row(align=True) - row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') - row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT') - - row = split.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') - - split = col2.split(factor=0.6) - - row = split.row(align=True) - - valid = md.material in (slot.material for slot in ob.material_slots) or md.material is None - if valid: - icon = 'SHADING_TEXTURE' - else: - icon = 'ERROR' - - row.alert = not valid - row.prop_search(md, "material", gpd, "materials", text="", icon=icon) - row.prop(md, "invert_materials", text="", icon='ARROW_LEFTRIGHT') - - row = split.row(align=True) - row.prop(md, "pass_index", text="Pass") - row.prop(md, "invert_material_pass", text="", icon='ARROW_LEFTRIGHT') - - if use_vertex: - row = col2.row(align=True) - row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") - row.prop(md, "invert_vertex", text="", icon='ARROW_LEFTRIGHT') - - if use_curve: - col = layout.column() - col.separator() - col.prop(md, "use_custom_curve") - if md.use_custom_curve: - col.template_curve_mapping(md, "curve") - - def GP_NOISE(self, layout, ob, md): - split = layout.split() - - col = split.column() - row = col.row(align=True) - row.prop(md, "factor", text="Position") - row = col.row(align=True) - row.prop(md, "factor_strength", text="Strength") - row = col.row(align=True) - row.prop(md, "factor_thickness", text="Thickness") - row = col.row(align=True) - row.prop(md, "factor_uvs", text="UV") - - col.separator() - row = col.row(align=True) - row.prop(md, "random", text="", icon='TIME', toggle=True) - - subrow = row.row(align=True) - subrow.enabled = md.random - subrow.prop(md, "step") - subrow.prop(md, "seed") - - col.separator() - col.prop(md, "noise_scale") - - self.gpencil_masking(layout, ob, md, True, True) - - def GP_SMOOTH(self, layout, ob, md): - col = layout.column() - col.prop(md, "factor") - col.prop(md, "step", text="Repeat") - - col.label(text="Affect:") - row = col.row(align=True) - row.prop(md, "use_edit_position", text="Position", toggle=True) - row.prop(md, "use_edit_strength", text="Strength", toggle=True) - row.prop(md, "use_edit_thickness", text="Thickness", toggle=True) - row.prop(md, "use_edit_uv", text="UV", toggle=True) - - self.gpencil_masking(layout, ob, md, True, True) - - def GP_SUBDIV(self, layout, ob, md): - layout.row().prop(md, "subdivision_type", expand=True) - split = layout.split() - col = split.column() - row = col.row(align=True) - row.prop(md, "level", text="Subdivisions") - - self.gpencil_masking(layout, ob, md, False) - - def GP_SIMPLIFY(self, layout, ob, md): - gpd = ob.data - - row = layout.row() - row.prop(md, "mode") - - split = layout.split() - - col = split.column() - col.label(text="Settings:") - - if md.mode == 'FIXED': - col.prop(md, "step") - elif md.mode == 'ADAPTIVE': - col.prop(md, "factor") - elif md.mode == 'SAMPLE': - col.prop(md, "length") - elif md.mode == 'MERGE': - col.prop(md, "distance") - - self.gpencil_masking(layout, ob, md, False) - - def GP_THICK(self, layout, ob, md): - col = layout.column() - - col.prop(md, "normalize_thickness") - - if md.normalize_thickness: - col.prop(md, "thickness") - else: - col.prop(md, "thickness_factor") - - self.gpencil_masking(layout, ob, md, True, True) - - def GP_TEXTURE(self, layout, ob, md): - col = layout.column() - - col.prop(md, "mode") - if md.mode in {'STROKE', 'STROKE_AND_FILL'}: - col.label(text="Stroke Texture:") - col.prop(md, "fit_method") - col.prop(md, "uv_offset") - col.prop(md, "uv_scale") - - if md.mode == 'STROKE_AND_FILL': - col.separator() - - if md.mode in {'FILL', 'STROKE_AND_FILL'}: - col.label(text="Fill Texture:") - col.prop(md, "fill_rotation", text="Rotation") - col.prop(md, "fill_offset", text="Location") - col.prop(md, "fill_scale", text="Scale") - - self.gpencil_masking(layout, ob, md, True) - - def GP_TINT(self, layout, ob, md): - layout.row().prop(md, "tint_type", expand=True) - - if md.tint_type == 'UNIFORM': - col = layout.column() - col.prop(md, "color") - - col.separator() - col.prop(md, "factor") - - if md.tint_type == 'GRADIENT': - col = layout.column() - col.label(text="Colors:") - col.template_color_ramp(md, "colors") - - col.separator() - - col.label(text="Object:") - col.prop(md, "object", text="") - - col.separator() - row = col.row(align=True) - row.prop(md, "radius") - row.prop(md, "factor") - - col.separator() - col.prop(md, "vertex_mode") - - self.gpencil_masking(layout, ob, md, True, True) - - def GP_TIME(self, layout, ob, md): - gpd = ob.data - - row = layout.row() - row.prop(md, "mode", text="Mode") - - row = layout.row() - if md.mode == 'FIX': - txt = "Frame" - else: - txt = "Frame Offset" - row.prop(md, "offset", text=txt) - - row = layout.row() - row.enabled = md.mode != 'FIX' - row.prop(md, "frame_scale") - - row = layout.row() - row.separator() - - row = layout.row() - row.enabled = md.mode != 'FIX' - row.prop(md, "use_custom_frame_range") - - row = layout.row(align=True) - row.enabled = md.mode != 'FIX' and md.use_custom_frame_range is True - row.prop(md, "frame_start") - row.prop(md, "frame_end") - - row = layout.row() - row.enabled = md.mode != 'FIX' - row.prop(md, "use_keep_loop") - - row = layout.row() - row.label(text="Layer:") - row = layout.row(align=True) - row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') - row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT') - - row = layout.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') - - def GP_COLOR(self, layout, ob, md): - split = layout.split() - - col = split.column() - col.label(text="Color:") - col.prop(md, "hue", text="H", slider=True) - col.prop(md, "saturation", text="S", slider=True) - col.prop(md, "value", text="V", slider=True) - - row = layout.row() - row.prop(md, "modify_color") - - self.gpencil_masking(layout, ob, md, False, True) - - def GP_OPACITY(self, layout, ob, md): - split = layout.split() - - col = split.column() - col.prop(md, "modify_color") - - if md.modify_color == 'HARDNESS': - col.prop(md, "hardness") - show = False - else: - col.prop(md, "normalize_opacity") - if md.normalize_opacity is True: - text="Strength" - else: - text="Opacity Factor" - - col.prop(md, "factor", text=text) - show = True - self.gpencil_masking(layout, ob, md, show, show) - - def GP_ARRAY(self, layout, ob, md): - col = layout.column() - col.prop(md, "count") - - split = layout.split() - col = split.column() - col.prop(md, "use_constant_offset", text="Constant Offset") - subcol = col.column() - subcol.enabled = md.use_constant_offset - subcol.prop(md, "constant_offset", text="") - - col.prop(md, "use_object_offset") - subcol = col.column() - subcol.enabled = md.use_object_offset - subcol.prop(md, "offset_object", text="") - - col = split.column() - col.prop(md, "use_relative_offset", text="Relative Offset") - subcol = col.column() - subcol.enabled = md.use_relative_offset - subcol.prop(md, "relative_offset", text="") - - split = layout.split() - col = split.column() - col.label(text="Random Offset:") - col.prop(md, "random_offset", text="") - - col = split.column() - col.label(text="Random Rotation:") - col.prop(md, "random_rotation", text="") - - col = split.column() - col.label(text="Random Scale:") - col.prop(md, "random_scale", text="") - - col = layout.column() - col.prop(md, "seed") - col.separator() - col.prop(md, "replace_material", text="Material Override") - - self.gpencil_masking(layout, ob, md, False) - - def GP_BUILD(self, layout, ob, md): - gpd = ob.data - - split = layout.split() - - col = split.column() - self.check_conflicts(col, ob) - - col.prop(md, "mode") - if md.mode == 'CONCURRENT': - col.prop(md, "concurrent_time_alignment") - - col.separator() - col.prop(md, "transition") - sub = col.column(align=True) - sub.prop(md, "start_delay") - sub.prop(md, "length") - - col = layout.column(align=True) - col.prop(md, "use_restrict_frame_range") - sub = col.column(align=True) - sub.active = md.use_restrict_frame_range - sub.prop(md, "frame_start", text="Start") - sub.prop(md, "frame_end", text="End") - - col.prop(md, "use_percentage") - sub = col.column(align=True) - sub.active = md.use_percentage - sub.prop(md, "percentage_factor") - - layout.label(text="Influence Filters:") - - split = layout.split(factor=0.25) - - col1 = split.column() - - col1.label(text="Layer:") - - col2 = split.column() - - split = col2.split(factor=0.6) - row = split.row(align=True) - row.prop_search(md, "layer", gpd, "layers", text="", icon='GREASEPENCIL') - row.prop(md, "invert_layers", text="", icon='ARROW_LEFTRIGHT') - - row = split.row(align=True) - row.prop(md, "layer_pass", text="Pass") - row.prop(md, "invert_layer_pass", text="", icon='ARROW_LEFTRIGHT') - - def GP_LATTICE(self, layout, ob, md): - split = layout.split() - - col = split.column() - col.label(text="Object:") - col.prop(md, "object", text="") - - layout.prop(md, "strength", slider=True) - - self.gpencil_masking(layout, ob, md, True) - - def GP_MIRROR(self, layout, ob, md): - row = layout.row(align=True) - row.prop(md, "x_axis") - row.prop(md, "y_axis") - row.prop(md, "z_axis") - - layout.label(text="Mirror Object:") - layout.prop(md, "object", text="") - - self.gpencil_masking(layout, ob, md, False) - - def GP_HOOK(self, layout, ob, md): - split = layout.split() - - col = split.column() - col.label(text="Object:") - col.prop(md, "object", text="") - if md.object and md.object.type == 'ARMATURE': - col.label(text="Bone:") - col.prop_search(md, "subtarget", md.object.data, "bones", text="") - - use_falloff = (md.falloff_type != 'NONE') - - layout.separator() - - row = layout.row(align=True) - if use_falloff: - row.prop(md, "falloff_radius") - row.prop(md, "strength", slider=True) - layout.prop(md, "falloff_type") - - col = layout.column() - if use_falloff: - if md.falloff_type == 'CURVE': - col.template_curve_mapping(md, "falloff_curve") - - split = layout.split() - - col = split.column() - col.prop(md, "use_falloff_uniform") - - self.gpencil_masking(layout, ob, md, True) - - def GP_OFFSET(self, layout, ob, md): - split = layout.split() - - split.column().prop(md, "location") - split.column().prop(md, "rotation") - split.column().prop(md, "scale") - - self.gpencil_masking(layout, ob, md, True) - - def GP_ARMATURE(self, layout, ob, md): - split = layout.split() - - col = split.column() - col.label(text="Object:") - col.prop(md, "object", text="") - # col.prop(md, "use_deform_preserve_volume") - - col = split.column() - col.label(text="Bind To:") - col.prop(md, "use_vertex_groups", text="Vertex Groups") - col.prop(md, "use_bone_envelopes", text="Bone Envelopes") - - layout.separator() - - row = layout.row(align=True) - row.label(text="Vertex Group:") - row = layout.row(align=True) - row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") - sub = row.row(align=True) - sub.active = bool(md.vertex_group) - sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') - - def GP_MULTIPLY(self, layout, ob, md): - col = layout.column() - - col.prop(md, "duplicates") - subcol = col.column() - subcol.enabled = md.duplicates > 0 - subcol.prop(md, "distance") - subcol.prop(md, "offset", slider=True) - - subcol.separator() - - subcol.prop(md, "use_fade") - if md.use_fade: - subcol.prop(md, "fading_center") - subcol.prop(md, "fading_thickness", slider=True) - subcol.prop(md, "fading_opacity", slider=True) - - self.gpencil_masking(layout, ob, md, False) + layout.template_grease_pencil_modifiers() classes = ( diff --git a/release/scripts/startup/bl_ui/properties_data_shaderfx.py b/release/scripts/startup/bl_ui/properties_data_shaderfx.py index 1d4bf37b282..a96fef018c7 100644 --- a/release/scripts/startup/bl_ui/properties_data_shaderfx.py +++ b/release/scripts/startup/bl_ui/properties_data_shaderfx.py @@ -24,11 +24,11 @@ class ShaderFxButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "shaderfx" - bl_options = {'HIDE_HEADER'} class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): bl_label = "Effects" + bl_options = {'HIDE_HEADER'} # Unused: always show for now. @@ -39,122 +39,8 @@ class DATA_PT_shader_fx(ShaderFxButtonsPanel, Panel): def draw(self, context): layout = self.layout - - ob = context.object - layout.operator_menu_enum("object.shaderfx_add", "type") - - for fx in ob.shader_effects: - box = layout.template_shaderfx(fx) - if box: - # match enum type to our functions, avoids a lookup table. - getattr(self, fx.type)(box, fx) - - # the mt.type enum is (ab)used for a lookup on function names - # ...to avoid lengthy if statements - # so each type must have a function here. - - def FX_BLUR(self, layout, fx): - - layout.prop(fx, "use_dof_mode", text="Use Depth of Field") - layout.separator() - - col = layout.column() - col.enabled = not fx.use_dof_mode - col.prop(fx, "size", text="Size") - col.separator() - col.prop(fx, "rotation") - - layout.prop(fx, "samples", text="Samples") - - - def FX_COLORIZE(self, layout, fx): - layout.prop(fx, "mode", text="Mode") - - if fx.mode == 'DUOTONE': - layout.prop(fx, "low_color", text="Low Color") - if fx.mode == 'CUSTOM': - layout.prop(fx, "low_color", text="Color") - - if fx.mode == 'DUOTONE': - layout.prop(fx, "high_color", text="High Color") - - layout.prop(fx, "factor") - - def FX_WAVE(self, layout, fx): - row = layout.row(align=True) - row.prop(fx, "orientation", expand=True) - - layout.separator() - layout.prop(fx, "amplitude") - layout.prop(fx, "period") - layout.prop(fx, "phase") - - def FX_PIXEL(self, layout, fx): - layout.prop(fx, "size", text="Size") - - def FX_RIM(self, layout, fx): - layout.prop(fx, "offset", text="Offset") - - layout.prop(fx, "rim_color") - layout.prop(fx, "mask_color") - layout.prop(fx, "mode", text="Blend") - layout.prop(fx, "blur") - layout.prop(fx, "samples") - - def FX_SHADOW(self, layout, fx): - layout.prop(fx, "offset", text="Offset") - - layout.prop(fx, "shadow_color") - layout.prop(fx, "scale") - layout.prop(fx, "rotation") - - layout.separator() - layout.prop(fx, "blur") - layout.prop(fx, "samples") - - layout.separator() - layout.prop(fx, "use_object", text="Use Object as Pivot") - if fx.use_object: - row = layout.row() - row.prop(fx, "object", text="Object") - - layout.separator() - layout.prop(fx, "use_wave", text="Use Wave Effect") - if fx.use_wave is True: - row = layout.row(align=True) - row.prop(fx, "orientation", expand=True) - layout.prop(fx, "amplitude") - layout.prop(fx, "period") - layout.prop(fx, "phase") - - def FX_GLOW(self, layout, fx): - layout.prop(fx, "mode") - if fx.mode == 'LUMINANCE': - layout.prop(fx, "threshold") - else: - layout.prop(fx, "select_color") - - layout.prop(fx, "glow_color") - layout.separator() - layout.prop(fx, "blend_mode", text="Blend") - layout.prop(fx, "opacity") - - layout.prop(fx, "size") - layout.prop(fx, "rotation") - layout.prop(fx, "samples") - - layout.prop(fx, "use_glow_under", text="Glow Under") - - def FX_SWIRL(self, layout, fx): - layout.prop(fx, "object", text="Object") - - layout.prop(fx, "radius") - layout.prop(fx, "angle") - - def FX_FLIP(self, layout, fx): - layout.prop(fx, "flip_horizontal") - layout.prop(fx, "flip_vertical") + layout.template_shaderfx() classes = ( diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py index 5f0fd3374d2..69c557d336f 100644 --- a/release/scripts/startup/bl_ui/properties_output.py +++ b/release/scripts/startup/bl_ui/properties_output.py @@ -411,6 +411,8 @@ class RENDER_PT_encoding_audio(RenderOutputButtonsPanel, Panel): layout.prop(ffmpeg, "audio_codec", text="Audio Codec") if ffmpeg.audio_codec != 'NONE': + layout.prop(ffmpeg, "audio_channels") + layout.prop(ffmpeg, "audio_mixrate", text="Sample Rate") layout.prop(ffmpeg, "audio_bitrate") layout.prop(ffmpeg, "audio_volume", slider=True) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 1612cce3c51..209231cacb0 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -607,6 +607,16 @@ def brush_settings(layout, context, brush, popover=False): layout.operator("sculpt.set_persistent_base") layout.separator() + if capabilities.has_color: + ups = context.scene.tool_settings.unified_paint_settings + 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='BRUSHES_ALL') + layout.prop(brush, "blend", text="Blend Mode") + if brush.sculpt_tool == 'CLAY_STRIPS': row = layout.row() row.prop(brush, "tip_roundness") @@ -623,9 +633,13 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush, "pose_origin_type") layout.prop(brush, "pose_offset") layout.prop(brush, "pose_smooth_iterations") - if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in ('TOPOLOGY','FACE_SETS'): + if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in {'TOPOLOGY', 'FACE_SETS'}: layout.prop(brush, "pose_ik_segments") layout.prop(brush, "use_pose_ik_anchored") + layout.prop(brush, "use_connected_only") + layout.prop(brush, "disconnected_distance_max") + + layout.separator() if brush.sculpt_tool == 'CLOTH': @@ -655,6 +669,15 @@ def brush_settings(layout, context, brush, popover=False): if brush.sculpt_tool == 'GRAB': layout.prop(brush, "use_grab_active_vertex") + if brush.sculpt_tool == 'PAINT': + col = layout.column() + col.prop(brush, "flow") + col.prop(brush, "wet_mix") + col.prop(brush, "wet_persistence") + col.prop(brush, "density") + col.prop(brush, "tip_roundness") + col.prop(brush, "tip_scale_x") + if brush.sculpt_tool == 'MULTIPLANE_SCRAPE': col = layout.column() col.prop(brush, "multiplane_scrape_angle") diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index 210d850ad06..db33fda3b17 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -330,6 +330,7 @@ def basic_force_field_settings_ui(self, field): col.prop(field, "use_gravity_falloff", text="Gravitation") col.prop(field, "use_absorption") + col.prop(field, "wind_factor") def basic_force_field_falloff_ui(self, field): diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 833cd05dd81..0bfa70b008b 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -210,10 +210,10 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): note.label(icon='INFO', text="Unbaked Guides: Bake Guides or disable them") split = layout.split() - split.enabled = note_flag + split.enabled = note_flag and ob.mode == 'OBJECT' 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: + if domain.cache_resumable and 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() @@ -705,6 +705,7 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): layout = self.layout layout.use_property_split = True + ob = context.object domain = context.fluid.domain_settings layout.active = domain.use_noise @@ -736,7 +737,7 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): note.label(icon='INFO', text="Unbaked Data: Bake Data first") split = layout.split() - split.enabled = domain.has_cache_baked_data and note_flag + split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT' 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: @@ -777,6 +778,7 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): layout = self.layout layout.use_property_split = True + ob = context.object domain = context.fluid.domain_settings layout.active = domain.use_mesh @@ -821,7 +823,7 @@ class PHYSICS_PT_mesh(PhysicButtonsPanel, Panel): note.label(icon='INFO', text="Unbaked Data: Bake Data first") split = layout.split() - split.enabled = domain.has_cache_baked_data and note_flag + split.enabled = domain.has_cache_baked_data and note_flag and ob.mode == 'OBJECT' 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: @@ -855,6 +857,7 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): layout = self.layout layout.use_property_split = True + ob = context.object domain = context.fluid.domain_settings is_baking_any = domain.is_cache_baking_any @@ -936,6 +939,7 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): split = layout.split() split.enabled = ( note_flag and + ob.mode == 'OBJECT' and domain.has_cache_baked_data and (domain.use_spray_particles or domain.use_bubble_particles or @@ -1114,14 +1118,13 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): def draw(self, context): layout = self.layout + ob = context.object md = context.fluid domain = context.fluid.domain_settings is_baking_any = domain.is_cache_baking_any has_baked_data = domain.has_cache_baked_data - has_baked_noise = domain.has_cache_baked_noise has_baked_mesh = domain.has_cache_baked_mesh - has_baked_particles = domain.has_cache_baked_particles col = layout.column() col.prop(domain, "cache_directory", text="") @@ -1146,35 +1149,33 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): col.separator() col = flow.column() + col.enabled = not is_baking_any and not has_baked_data + col.prop(domain, "cache_resumable", text="Is Resumable") + row = col.row() row.enabled = not is_baking_any and not has_baked_data - row.prop(domain, "cache_data_format", text="Data File Format") - - if md.domain_settings.domain_type in {'GAS'}: - if domain.use_noise: - row = col.row() - row.enabled = not is_baking_any and not has_baked_noise - row.prop(domain, "cache_noise_format", text="Noise File Format") + row.prop(domain, "cache_data_format", text="Format Volumes") - if md.domain_settings.domain_type in {'LIQUID'}: - # File format for all particle systemes (FLIP and secondary) + if md.domain_settings.domain_type in {'LIQUID'} and domain.use_mesh: row = col.row() - row.enabled = not is_baking_any and not has_baked_particles and not has_baked_data - row.prop(domain, "cache_particle_format", text="Particle File Format") + row.enabled = not is_baking_any and not has_baked_mesh + row.prop(domain, "cache_mesh_format", text="Meshes") - if domain.use_mesh: - row = col.row() - row.enabled = not is_baking_any and not has_baked_mesh - row.prop(domain, "cache_mesh_format", text="Mesh File Format") - - if domain.cache_type == 'FINAL': + if domain.cache_type == 'ALL': col.separator() split = layout.split() + split.enabled = ob.mode == 'OBJECT' - if domain.is_cache_baking_data and not domain.has_cache_baked_data: + bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) + if domain.cache_resumable and 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 cancel") + 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: @@ -1189,8 +1190,8 @@ class PHYSICS_PT_export(PhysicButtonsPanel, Panel): @classmethod def poll(cls, context): - # Only show the advanced panel to advanced users who know Mantaflow's birthday :) - if not PhysicButtonsPanel.poll_fluid_domain(context) or bpy.app.debug_value != 3001: + domain = context.fluid.domain_settings + if not PhysicButtonsPanel.poll_fluid_domain(context) or (domain.cache_data_format != 'OPENVDB' and bpy.app.debug_value != 3001): return False return (context.engine in cls.COMPAT_ENGINES) @@ -1203,12 +1204,24 @@ class PHYSICS_PT_export(PhysicButtonsPanel, Panel): 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 col = flow.column() - col.prop(domain, "export_manta_script", text="Export Mantaflow Script") + + if domain.cache_data_format == 'OPENVDB': + col.enabled = not is_baking_any and not has_baked_data + col.prop(domain, "openvdb_cache_compress_type", text="Compression Volumes") + + col = flow.column() + col.prop(domain, "openvdb_data_depth", text="Precision Volumes") + + # Only show the advanced panel to advanced users who know Mantaflow's birthday :) + if bpy.app.debug_value == 3001: + col = flow.column() + col.prop(domain, "export_manta_script", text="Export Mantaflow Script") class PHYSICS_PT_field_weights(PhysicButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 11b02cfb552..fcaa715cfd8 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -173,8 +173,10 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel): layout.active = props.use_motion_blur col = layout.column() - col.prop(props, "motion_blur_samples") col.prop(props, "motion_blur_shutter") + col.prop(props, "motion_blur_depth_scale") + col.prop(props, "motion_blur_max") + col.prop(props, "motion_blur_steps", text="Steps") class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index 0b4ca2902c1..22f455fe5be 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -289,8 +289,6 @@ class SCENE_PT_audio(SceneButtonsPanel, Panel): layout.use_property_split = True scene = context.scene - rd = context.scene.render - ffmpeg = rd.ffmpeg flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True) @@ -299,17 +297,8 @@ class SCENE_PT_audio(SceneButtonsPanel, Panel): col.separator() - col.prop(scene, "audio_distance_model") - col.prop(ffmpeg, "audio_channels") - - col.separator() - - col = flow.column() - col.prop(ffmpeg, "audio_mixrate", text="Sample Rate") - - col.separator() - col = col.column(align=True) + col.prop(scene, "audio_distance_model") col.prop(scene, "audio_doppler_speed", text="Doppler Speed") col.prop(scene, "audio_doppler_factor", text="Doppler Factor") diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index ee8015df273..aa4d0b94b7f 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -213,7 +213,7 @@ class OUTLINER_MT_collection(Menu): layout.separator() layout.operator("outliner.delete", text="Delete", icon='X') - layout.operator("outliner.collection_hierarchy_delete") + layout.operator("outliner.delete", text="Delete Hierarchy").hierarchy = True layout.separator() @@ -279,9 +279,7 @@ class OUTLINER_MT_object(Menu): layout.separator() layout.operator("outliner.delete", text="Delete", icon='X') - - if space.display_mode == 'VIEW_LAYER' and not space.use_filter_collection: - layout.operator("outliner.object_operation", text="Delete Hierarchy").type = 'DELETE_HIERARCHY' + layout.operator("outliner.delete", text="Delete Hierarchy").hierarchy = True layout.separator() diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index abe6f614f28..8dfa182f52d 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -474,7 +474,7 @@ class SEQUENCER_MT_navigation(Menu): layout.separator() - layout.operator("sequencer.view_frame", text="Go to Playhead") + layout.operator("sequencer.view_frame") layout.separator() @@ -635,7 +635,7 @@ class SEQUENCER_MT_strip_transform(Menu): layout = self.layout layout.operator("transform.seq_slide", text="Move") - layout.operator("transform.transform", text="Move/Extend from Playhead").mode = 'TIME_EXTEND' + layout.operator("transform.transform", text="Move/Extend from Current Frame").mode = 'TIME_EXTEND' layout.operator("sequencer.slip", text="Slip Strip Contents") layout.separator() @@ -731,7 +731,7 @@ class SEQUENCER_MT_strip(Menu): layout.operator("sequencer.copy", text="Copy") layout.operator("sequencer.paste", text="Paste") layout.operator("sequencer.duplicate_move") - layout.operator("sequencer.delete", text="Delete...") + layout.operator("sequencer.delete", text="Delete") strip = act_strip(context) @@ -1528,7 +1528,7 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel): split.alignment = 'RIGHT' split.label(text="End") split = split.split(factor=0.8 + max_factor, align=True) - split.label(text="{:>14}".format(smpte_from_frame(frame_final_end))) + split.label(text="%14s" % smpte_from_frame(frame_final_end)) split.alignment = 'RIGHT' split.label(text=str(frame_final_end) + " ") @@ -1569,10 +1569,10 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel): split = col.split(factor=0.5 + max_factor, align=True) split.alignment = 'RIGHT' - split.label(text="Playhead") + split.label(text="Current Frame") split = split.split(factor=0.8 + max_factor, align=True) frame_display = frame_current - frame_final_start - split.label(text="{:>14}".format(smpte_from_frame(frame_display))) + split.label(text="%14s" % smpte_from_frame(frame_display)) split.alignment = 'RIGHT' split.label(text=str(frame_display) + " ") diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index 553d79fb349..4f518d8e2d4 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -174,7 +174,10 @@ class TEXT_PT_find(Panel): row = col.row(align=True) row.prop(st, "replace_text", icon='DECORATE_OVERRIDE', text="") row.operator("text.replace_set_selected", text="", icon='EYEDROPPER') - col.operator("text.replace") + + row = col.row(align=True) + row.operator("text.replace") + row.operator("text.replace", text = "Replace all").all = True layout.separator() diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 257e0c26a5d..1a9244a3051 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -243,8 +243,8 @@ class TIME_PT_playback(TimelinePanelButtons, Panel): layout.prop(scene, "show_subframe", text="Subframes") - layout.prop(scene, "lock_frame_selection_to_range", text="Limit Playhead to Frame Range") - layout.prop(screen, "use_follow", text="Follow Playhead") + layout.prop(scene, "lock_frame_selection_to_range", text="Limit Playback to Frame Range") + layout.prop(screen, "use_follow", text="Follow Current Frame") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index e0651dcac2b..080e66b59e7 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -36,7 +36,7 @@ __all__ = ( # Support reloading icons. if "_icon_cache" in locals(): release = bpy.app.icons.release - for icon_value in _icon_cache.values(): + for icon_value in set(_icon_cache.values()): if icon_value != 0: release(icon_value) del release @@ -450,7 +450,7 @@ class ToolSelectPanelHelper: @classmethod def _km_action_simple(cls, kc_default, kc, context_descr, label, keymap_fn): - km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {label:s}" + km_idname = "%s %s, %s" % (cls.keymap_prefix, context_descr, label) km = kc.keymaps.get(km_idname) km_kwargs = dict(space_type=cls.bl_space_type, region_type='WINDOW', tool=True) if km is None: diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 2c3108e0a11..2391678b99b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -124,12 +124,12 @@ class _defs_view3d_generic: kmi_remove = None return tip_( "Measure distance and angles.\n" - "\u2022 {} anywhere for new measurement.\n" + "\u2022 %s anywhere for new measurement.\n" "\u2022 Drag ruler segment to measure an angle.\n" - "\u2022 {} to remove the active ruler.\n" + "\u2022 %s to remove the active ruler.\n" "\u2022 Ctrl while dragging to snap.\n" "\u2022 Shift while dragging to measure surface thickness" - ).format( + ) % ( kmi_to_string_or_none(kmi_add), kmi_to_string_or_none(kmi_remove), ) @@ -482,7 +482,7 @@ class _defs_view3d_add: return dict( idname="builtin.primitive_cone_add", label="Add Cone", - icon="ops.mesh.primitive_cube_add_gizmo", + icon="ops.mesh.primitive_cone_add_gizmo", description=( "Add cone to mesh interactively" ), @@ -738,27 +738,40 @@ class _defs_edit_mesh: def bevel(): def draw_settings(context, layout, tool, *, extra=False): props = tool.operator_properties("mesh.bevel") - region_type = context.region.type + + region_is_header = context.region.type == 'TOOL_HEADER' if not extra: - if region_type == 'TOOL_HEADER': + if region_is_header: layout.prop(props, "offset_type", text="") else: layout.prop(props, "offset_type") layout.prop(props, "segments") - layout.prop(props, "profile", slider=True) - if region_type == 'TOOL_HEADER': + row = layout.row() + row.prop(props, "profile_type", text="" if region_is_header else None) + if props.profile_type == 'SUPERELLIPSE': + layout.prop(props, "profile", text="Shape", slider=True) + + if region_is_header: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") else: extra = True - if extra or region_type != 'TOOL_HEADER': - layout.prop(props, "vertex_only") - layout.prop(props, "clamp_overlap") - layout.prop(props, "loop_slide") - layout.prop(props, "harden_normals") + if extra: + layout.use_property_split = True + layout.use_property_decorate = False + + if props.profile_type == 'CUSTOM': + layout.prop(props, "profile", text="Miter Shape", slider=True) + + col = layout.column() + col.prop(props, "vertex_only") + col.prop(props, "clamp_overlap") + col.prop(props, "loop_slide") + col.prop(props, "harden_normals") + col = layout.column(heading="Mark") col.prop(props, "mark_seam", text="Seam") col.prop(props, "mark_sharp", text="Sharp") @@ -770,8 +783,7 @@ class _defs_edit_mesh: if props.miter_inner == 'ARC': layout.prop(props, "spread") - layout.prop(props, "use_custom_profile") - if props.use_custom_profile: + if props.profile_type == 'CUSTOM': tool_settings = context.tool_settings layout.template_curveprofile(tool_settings, "custom_bevel_profile_preset") @@ -802,14 +814,14 @@ class _defs_edit_mesh: ) @ToolDef.from_fn - def extrude_dissolve_and_intersect(): + def extrude_manifold(): return dict( - idname="builtin.extrude_dissolve_and_intersect", - label="Extrude Dissolve and Intersect", + idname="builtin.extrude_manifold", + label="Extrude Manifold", description=( "Extrude, dissolves edges whose faces form a flat surface and intersect new edges" ), - icon="none", + icon="ops.mesh.extrude_manifold", widget="VIEW3D_GGT_tool_generic_handle_normal", keymap=(), ) @@ -1212,10 +1224,10 @@ class _defs_sculpt: layout.prop(props, "strength") layout.prop(props, "deform_axis") layout.prop(props, "use_face_sets") - if (props.type == "SURFACE_SMOOTH"): + if props.type == 'SURFACE_SMOOTH': layout.prop(props, "surface_smooth_shape_preservation", expand=False) layout.prop(props, "surface_smooth_current_vertex", expand=False) - if (props.type == "SHARPEN"): + elif props.type == 'SHARPEN': layout.prop(props, "sharpen_smooth_ratio", expand=False) return dict( @@ -1246,6 +1258,24 @@ class _defs_sculpt: draw_settings=draw_settings, ) + @ToolDef.from_fn + def color_filter(): + def draw_settings(_context, layout, tool): + props = tool.operator_properties("sculpt.color_filter") + layout.prop(props, "type", expand=False) + if props.type == 'FILL': + layout.prop(props, "fill_color", expand=False) + layout.prop(props, "strength") + + return dict( + idname="builtin.color_filter", + label="Color Filter", + icon="ops.sculpt.color_filter", + widget=None, + keymap=(), + draw_settings=draw_settings, + ) + class _defs_vertex_paint: @@ -2327,7 +2357,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, ( _defs_edit_mesh.extrude, - _defs_edit_mesh.extrude_dissolve_and_intersect, + _defs_edit_mesh.extrude_manifold, _defs_edit_mesh.extrude_normals, _defs_edit_mesh.extrude_individual, _defs_edit_mesh.extrude_cursor, @@ -2421,6 +2451,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, _defs_sculpt.mesh_filter, _defs_sculpt.cloth_filter, + _defs_sculpt.color_filter, None, _defs_transform.translate, _defs_transform.rotate, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 436d866886b..e5171df597a 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -45,7 +45,7 @@ class USERPREF_HT_header(Header): # Show '*' to let users know the preferences have been modified. layout.operator( "wm.save_userpref", - text="Save Preferences{:s}".format(" *" if prefs.is_dirty else ""), + text="Save Preferences" + (" *" if prefs.is_dirty else ""), ) def draw(self, context): @@ -1903,8 +1903,10 @@ class USERPREF_PT_addons(AddOnPanel, Panel): "wm.url_open", text="Report a Bug", icon='URL', ).url = info["tracker_url"] elif not user_addon: - addon_info = ("Name: {} {}\nAuthor: {}\n").format( - info["name"], info["version"], info["author"]) + addon_info = ( + "Name: %s %s\n" + "Author: %s\n" + ) % (info["name"], str(info["version"]), info["author"]) props = sub.operator( "wm.url_open_preset", text="Report a Bug", icon='URL', ) @@ -1987,7 +1989,7 @@ class StudioLightPanelMixin: for studio_light in lights: self.draw_studio_light(flow, studio_light) else: - layout.label(text="No custom {} configured".format(self.bl_label)) + layout.label(text="No custom %s configured" % self.bl_label) def draw_studio_light(self, layout, studio_light): box = layout.box() @@ -2112,8 +2114,10 @@ class ExperimentalPanel: split = layout.split(factor=0.66) col = split.split() col.prop(experimental, **prop_keywords) - col = split.split() - col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task + + if task: + col = split.split() + col.operator("wm.url_open", text=task, icon='URL').url = self.url_prefix + task """ # Example panel, leave it here so we always have a template to follow even @@ -2132,13 +2136,36 @@ class USERPREF_PT_experimental_virtual_reality(ExperimentalPanel, Panel): """ -class USERPREF_PT_experimental_system(ExperimentalPanel, Panel): - bl_label = "System" +class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): + bl_label = "New Features" + + def draw(self, context): + self._draw_items( + context, ( + ({"property": "use_new_particle_system"}, "T73324"), + ), + ) + + +class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): + bl_label = "Prototypes" + + def draw(self, context): + self._draw_items( + context, ( + ({"property": "use_new_hair_type"}, "T68981"), + ), + ) + + +class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): + bl_label = "Debugging" def draw(self, context): self._draw_items( context, ( ({"property": "use_undo_legacy"}, "T60695"), + ({"property": "use_cycles_debug"}, None), ), ) @@ -2233,7 +2260,9 @@ classes = ( # Popovers. USERPREF_PT_ndof_settings, - USERPREF_PT_experimental_system, + USERPREF_PT_experimental_new_features, + USERPREF_PT_experimental_prototypes, + USERPREF_PT_experimental_debugging, # Add dynamically generated editor theme panels last, # so they show up last in the theme section. diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index c982d8e93a9..1403e43a8af 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -131,7 +131,7 @@ class VIEW3D_HT_tool_header(Header): if is_valid_context: brush = context.tool_settings.gpencil_sculpt_paint.brush tool = brush.gpencil_tool - if tool in ('SMOOTH', 'RANDOMIZE'): + if tool in {'SMOOTH', 'RANDOMIZE'}: layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_options") layout.popover("VIEW3D_PT_tools_grease_pencil_sculpt_appearance") elif tool_mode == 'WEIGHT_GPENCIL': @@ -295,6 +295,11 @@ class _draw_tool_settings_context_mode: if not capabilities.has_direction: layout.row().prop(brush, "direction", expand=True, text="") + if capabilities.has_color: + UnifiedPaintPanel.prop_unified_color(layout, context, brush, "color", text = "") + layout.prop(brush, "blend", text="", expand = False) + + return True @staticmethod @@ -2171,9 +2176,9 @@ class VIEW3D_MT_add(Menu): layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE') layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META') layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT') - if hasattr(bpy.data, "hairs"): + if context.preferences.experimental.use_new_hair_type: layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR') - if hasattr(bpy.data, "pointclouds"): + if context.preferences.experimental.use_new_particle_system: layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD') layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME') layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL') @@ -2348,6 +2353,7 @@ class VIEW3D_MT_object_animation(Menu): layout.separator() layout.operator("nla.bake", text="Bake Action...") + layout.operator("gpencil.mesh_bake", text="Bake Mesh to Grease Pencil...") class VIEW3D_MT_object_rigid_body(Menu): @@ -5620,8 +5626,8 @@ class VIEW3D_PT_object_type_visibility(Panel): elif attr == "pointcloud" and not hasattr(bpy.data, "pointclouds"): continue - attr_v = "show_object_viewport_" f"{attr:s}" - attr_s = "show_object_select_" f"{attr:s}" + attr_v = "show_object_viewport_" + attr + attr_s = "show_object_select_" + attr icon_v = 'HIDE_OFF' if getattr(view, attr_v) else 'HIDE_ON' icon_s = 'RESTRICT_SELECT_OFF' if getattr(view, attr_s) else 'RESTRICT_SELECT_ON' @@ -6786,7 +6792,10 @@ class VIEW3D_PT_overlay_gpencil_options(Panel): if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}: layout.label(text="Vertex Paint") - layout.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True) + row = layout.row() + shading = VIEW3D_PT_shading.get_shading(context) + row.enabled = shading.type not in {'WIREFRAME', 'RENDERED'} + row.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True) class VIEW3D_PT_quad_view(Panel): @@ -7329,6 +7338,12 @@ class VIEW3D_PT_sculpt_context_menu(Panel): brush = context.tool_settings.sculpt.brush capabilities = brush.sculpt_capabilities + if capabilities.has_color: + split = layout.split(factor=0.1) + UnifiedPaintPanel.prop_unified_color(split, context, brush, "color", text="") + UnifiedPaintPanel.prop_unified_color_picker(split, context, brush, "color", value_slider=True) + layout.prop(brush, "blend", text="") + UnifiedPaintPanel.prop_unified( layout, context, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 3d72a2a588c..549b89938e0 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -187,6 +187,7 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): bl_context = ".mesh_edit" # dot on purpose (access from topbar) bl_label = "Options" bl_options = {'DEFAULT_CLOSED'} + bl_ui_units_x = 12 @classmethod def poll(cls, context): @@ -198,11 +199,15 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False + tool_settings = context.tool_settings ob = context.active_object mesh = ob.data split = layout.split() + row = layout.row(align=True, heading="Transform") + row.prop(tool_settings, "use_transform_correct_face_attributes") + row = layout.row(heading="Mirror") sub = row.row(align=True) sub.prop(mesh, "use_mirror_x", text="X", toggle=True) @@ -806,6 +811,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): col.prop(mesh, "use_remesh_preserve_volume", text="Volume") col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") + col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") + layout.operator("object.voxel_remesh", text="Remesh") @@ -829,7 +836,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): sculpt = tool_settings.sculpt col = layout.column(heading="Display", align=True) - col.prop(sculpt, "use_threaded", text="Threaded Sculpt") col.prop(sculpt, "show_low_resolution") col.prop(sculpt, "use_sculpt_delay_updates") col.prop(sculpt, "use_deform_only") @@ -1694,7 +1700,7 @@ class GreasePencilSculptPanel: @classmethod def poll(cls, context): - if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): + if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}: if context.gpencil_data is None: return False @@ -1768,7 +1774,7 @@ class GreasePencilWeightPanel: @classmethod def poll(cls, context): - if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): + if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}: if context.gpencil_data is None: return False @@ -1843,7 +1849,7 @@ class GreasePencilVertexPanel: @classmethod def poll(cls, context): - if context.space_data.type in ('VIEW_3D', 'PROPERTIES'): + if context.space_data.type in {'VIEW_3D', 'PROPERTIES'}: if context.gpencil_data is None: return False |