From 137b888c2a41ade0f99cce491f29d808e09ae18f Mon Sep 17 00:00:00 2001 From: NevilArt Date: Mon, 27 May 2024 09:28:37 +0300 Subject: [PATCH] U20240527 Backburner repository option, better auto naming, increment on repository file name. data cleaner add. Freeze on issue fix. --- CHANGELOG.md | 6 + CONTRIBUTORS.md | 3 + README.md | 11 +- __init__.py | 36 +- tools/internal/animation/animation_key.py | 455 ++++++++-------- tools/internal/animation/character_lister.py | 115 ++-- tools/internal/animation/follow_path.py | 99 ++++ tools/internal/animation/frame_update.py | 31 +- tools/internal/animation/selection_set.py | 48 +- tools/internal/animation/selection_set_v2.py | 34 +- tools/internal/animation/time.py | 41 +- tools/internal/object/__init__.py | 3 + tools/internal/object/cleanup.py | 149 +++++ tools/internal/object/instancer.py | 4 +- tools/internal/render/backburner.py | 540 ++++++++++++------- tools/internal/render/light_lister.py | 196 +++---- 16 files changed, 1116 insertions(+), 655 deletions(-) create mode 100644 tools/internal/animation/follow_path.py create mode 100644 tools/internal/object/cleanup.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c64f43..65a4017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0, 1, 2, 20240523 +* Create Primitive naming issue fixed. +* Smart loop bug fixed. +* Batch rename issue fixed. +* Instancer bug fixed. + # 0, 1, 2, 20240520 * Align Object issues from last update fixed. * Mesh to hair guid pre setup operator added diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bbc767f..835e24a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -3,6 +3,7 @@ # Thanks for Support * Stephen Lebed + * jonas grigonis * nildo essa * gary jay smith @@ -14,8 +15,10 @@ * d_phantom70@hotmail.com * kuroiluna@hotmail.com * solenekinkielele@yahoo.fr + * revovl@gmail.com * shane@studiopiper.com.au +* tmdals1825@gmail.com # Note * Unfortunately, I don't have all the names. If you're reading this, please send me your name and webpage if you'd like to be added to the list. diff --git a/README.md b/README.md index 42ff808..bd262c3 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,13 @@ If you found this product useful and want to support this project you can Donate * [Download Older Version (2.80-2.92)(2.93-3.2)](https://github.com/NevilArt/BsMax_2_80) ## Recent Updates and Changes -* Create Primitive naming issue fixed. -* Smart loop bug fixed. -* Batch rename issue fixed. -* Instancer bug fixed. +* Backburner submiter has some updates +* 1. custom Repository directory option. +* 2. Incremental number to repository file rather than random number. +* 3. Use jobname as repository file name optional. +* 4. Automaticaly add scene and viewlayer name to jobname if was more then one. +* Geometry data clean stuff add that work on multiple object rather then only active object.(View3D/Object/Clean Up/ ...) +* "Freeze on" repeat calculation issue fixed. (Tools/Animation/...) * [Change log ...](https://github.com/NevilArt/BsMax/blob/master/CHANGELOG.md) ## Special Thanks diff --git a/__init__.py b/__init__.py index 725b3b5..d8dc4b5 100644 --- a/__init__.py +++ b/__init__.py @@ -15,19 +15,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/05/23 +# 2024/05/27 bl_info = { - 'name': 'BsMax', - 'description': 'BsMax UI simulations and Tool pack (Blender 3.3LTS ~ 4.1)', - 'author': 'Naser Merati (Nevil)', - 'version': (0, 1, 2, 20240523), + 'name': "BsMax", + 'description': "BsMax UI simulations and Tool pack (Blender 3.3LTS ~ 4.1)", + 'author': "Naser Merati (Nevil)", + 'version': (0, 1, 2, 20240527), 'blender': (3, 3, 0), - 'location': 'Almost Everywhere in Blender', + 'location': "Almost Everywhere in Blender", 'wiki_url': 'https://github.com/NevilArt/BsMax/wiki', 'doc_url': 'https://github.com/NevilArt/BsMax/wiki', 'tracker_url': 'https://github.com/NevilArt/BsMax/issues', - 'category': 'Interface' + 'category': "Interface" } @@ -133,21 +133,21 @@ def row_prop(cls, col, name, page): def draw_simple_panel(cls, layout): row = layout.row() col = row.column() - col.label(text='Select packages parts separately') + col.label(text="Select packages parts separately") row_prop(cls, col, 'navigation', 'Navigation') row_prop(cls, col, 'keymaps', 'Keymaps-' + cls.keymaps) row_prop(cls, col, 'floatmenus', 'floatmenus-' + cls.floatmenus) #TODO update wiki page row_prop(cls, col, 'side_panel', 'SidePanel-' + cls.floatmenus) col.label( - text='Note: Sometimes need to restart Blender to addon work properly' + text="Note: Sometimes need to restart Blender to addon work properly" ) def draw_custom_panel(cls, layout): row = layout.row() col = row.column() - col.label(text='Select packages parts customly') + col.label(text="Select packages parts customly") row_prop(cls, col, 'navigation_3d', 'navigation_3d-' + cls.navigation_3d) row_prop(cls, col, 'navigation_2d', 'navigation_2d-' + cls.navigation_2d) @@ -159,7 +159,7 @@ def draw_custom_panel(cls, layout): row_prop(cls, col, 'graph_editor', 'graph_editor-' + cls.graph_editor) row_prop(cls, col, 'clip_editor', 'clip_editor-' + cls.clip_editor) row_prop(cls, - col, 'video_sequencer','video_sequencer-' + cls.video_sequencer + col, 'video_sequencer', 'video_sequencer-' + cls.video_sequencer ) row_prop(cls, col, 'file_browser', 'file_browser-' + cls.file_browser) @@ -167,7 +167,7 @@ def draw_custom_panel(cls, layout): row_prop(cls, col, 'side_panel', 'SidePanel-' + cls.floatmenus) col.label( - text='Note: Sometimes need to restart Blender to addon work properly' + text="Note: Sometimes need to restart Blender to addon work properly" ) @@ -584,11 +584,11 @@ def draw(self, _): box = layout.box() row = box.row() icon = 'DOWNARROW_HLT' if self.options else 'RIGHTARROW' - row.prop(self, 'options', text='Options', icon=icon) + row.prop(self, 'options', text="Options", icon=icon) row.operator( 'bsmax.save_preferences', - text='Save Preferences Setting', + text="Save Preferences Setting", icon='FILE_TICK' ) @@ -624,8 +624,8 @@ def register_delay(preferences): def register(): global classes, addons - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) preferences = addons[__name__].preferences load_preferences(preferences) @@ -651,8 +651,8 @@ def unregister(): unregister_startup() unregister_bsmax() - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) # templates.unregister() if path in sys.path: diff --git a/tools/internal/animation/animation_key.py b/tools/internal/animation/animation_key.py index 001bfc8..d771ee7 100644 --- a/tools/internal/animation/animation_key.py +++ b/tools/internal/animation/animation_key.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/03/27 +# 2024/05/26 import bpy @@ -39,26 +39,209 @@ def __init__(self): key_data = KeyData() +def update_freeze_on(cls, _): + """ Get from calculate fields """ + cls.frames = cls.relase - cls.push + cls.next_step = cls.next_push - cls.push + 1 + cls.repeat = int((cls.end - cls.push) / cls.next_step) + + +def update_freeze_on_option(cls, _): + """ Reset extera infor if hide """ + if not cls.more: + cls.next_step = 1 + cls.repeat = 1 + + +def draw_freeze_on_panel(cls, ctx): + layout = cls.layout + freeze_on = ctx.scene.freeze_on + + box = layout.box() + box.enabled = not freeze_on.calculator # disable if calc is on + + row = box.row() + row.label(text="Fix position") + row.prop(freeze_on, 'more', icon='HAND') + row.prop(freeze_on, 'panel', icon='NODE_SEL') + + row = box.row() + row = box.row(align=True) + row.prop(freeze_on, 'frames', icon='TEMP') + + if freeze_on.more: + row.prop(freeze_on, 'next_step', icon='TRACKING_FORWARDS_SINGLE') + row.prop(freeze_on, 'repeat', icon='FILE_REFRESH') + + box = layout.box() + box.label(text="Set Key For") + row = box.row(align=True) + row.prop(freeze_on, 'key_location', text="Location", icon='BLANK1') + row.prop(freeze_on, 'key_rotation', text="Rotation", icon='BLANK1') + row.prop(freeze_on, 'key_scale', text="Scale",icon='BLANK1') + + box = layout.box() + box.prop(freeze_on, 'calculator', icon='ALIGN_TOP') + if freeze_on.calculator: + box.prop(freeze_on, 'push', icon='TEMP') + box.prop(freeze_on, 'relase', icon='TEMP') + box.prop(freeze_on, 'next_push', icon='TEMP') + box.prop(freeze_on, 'end', icon='TEMP') + # else: + # freeze_on.next_step = 1 + # freeze_on.repeat = 1 + + +def fix_object_in_location(ctx, frame_current): + # get info from scene + freeze_on = ctx.scene.freeze_on + + # at each frame return object to first position and set key + for obj in ctx.selected_objects: + worldlocation = obj.matrix_world + for frame in range(frame_current, frame_current + freeze_on.frames): + obj.matrix_world = worldlocation + insert_key_to_current_state( + obj, frame, + freeze_on.key_location, + freeze_on.key_rotation, + freeze_on.key_scale + ) + + +def fix_bone_in_location(ctx, frame_current): + # get info from scene + freeze_on = ctx.scene.freeze_on + + # at each frame return bone to first position and set key + armature = ctx.active_object + for bone in ctx.selected_pose_bones: + # Conver Pose bone space to world space + bone_matrix = armature.convert_space(pose_bone=bone, + matrix=bone.matrix, from_space='POSE', to_space='WORLD') + + for frame in range(frame_current, frame_current + freeze_on.frames): + ctx.scene.frame_current = frame + ctx.view_layer.update() + # Convert World to pose bone space + bone.matrix = armature.convert_space( + pose_bone=bone, + matrix=bone_matrix, + from_space='WORLD', + to_space='POSE' + ) + + insert_key_to_current_state( + bone, frame, + freeze_on.key_location, + freeze_on.key_rotation, + freeze_on.key_scale + ) + + +def apply_freeze_on(ctx): + pass + + +class Freeze_on_Property(PropertyGroup): + """ Data method """ + frames: IntProperty( + name="Fix", + min=1, + default=1, + description="Number of frames object has to fixed" + ) # type: ignore + + next_step: IntProperty( + name="Cycle", + min=1, + default=1, + description="Length of walk/run cycle" + ) # type: ignore + + repeat: IntProperty( + name="Repeat", + min=1, + default=1, + description="Repeat same action for next steps" + ) # type: ignore + + """ Key chanels """ + key_location: BoolProperty( + name="Key Location", + default=True, + description="Set Key for Location" + ) # type: ignore + + key_rotation: BoolProperty( + name="Key Rotation", + default=True, + description="Set key for Rotation" + ) # type: ignore + + key_scale: BoolProperty( + name="Key Scale", + default=False, + description="Set key for Scale" + ) # type: ignore + + """ Calculator """ + calculator: BoolProperty(name="Calculator", default=False) # type: ignore + + push: IntProperty( + name="Frame that first time foot touch the floor", + min=0, + default=1, + update=update_freeze_on + ) # type: ignore + + relase: IntProperty( + name="Frame that foot untouch floor", + min=0, default=1, + update=update_freeze_on + ) # type: ignore + + next_push: IntProperty( + name="Second time foot touch floor", + min=0, default=1, + update=update_freeze_on + ) # type: ignore + + end: IntProperty( + name="End of Walk/Run cycle", + min=0, default=1, + update=update_freeze_on + ) # type: ignore + + """ Simple """ + more: BoolProperty( + name="More Option", default=False, + update=update_freeze_on_option + ) # type: ignore + + panel: BoolProperty(name="On Panel", default=False) # type: ignore + + class Anim_OT_Set_Key_Filters(Operator): bl_idname = 'anim.set_key_filters' - bl_label = 'Set Key Filters' - bl_description = 'Set Key Filter' - - available: BoolProperty(name='Avalable') - location: BoolProperty(name='Location') - rotation: BoolProperty(name='Rotation') - scale: BoolProperty(name='Scale') - visual_location: BoolProperty(name='Visual Location') - visual_rotation: BoolProperty(name='Visual Rotation') - visual_scale: BoolProperty(name='Viasual Scale') - bbone_shape: BoolProperty(name='BBone Shape') - whole_character: BoolProperty(name='Whole Character') + bl_label = "Set Key Filters" + bl_description = "Set Key Filter" + + available: BoolProperty(name="Avalable") # type: ignore + location: BoolProperty(name="Location") # type: ignore + rotation: BoolProperty(name="Rotation") # type: ignore + scale: BoolProperty(name="Scale") # type: ignore + visual_location: BoolProperty(name="Visual Location") # type: ignore + visual_rotation: BoolProperty(name="Visual Rotation") # type: ignore + visual_scale: BoolProperty(name="Viasual Scale") # type: ignore + bbone_shape: BoolProperty(name="BBone Shape") # type: ignore + whole_character: BoolProperty(name="Whole Character") # type: ignore whole_character_selected: BoolProperty( - name='Whole Character (Selected only)' - ) + name="Whole Character (Selected only)" + ) # type: ignore - def draw(self, ctx): + def draw(self, _): layout = self.layout box = layout.box() box.prop(self, 'available') @@ -89,14 +272,14 @@ def set(self): key_data.whole_character = self.whole_character key_data.whole_character_selected = self.whole_character_selected - def execute(self, ctx): + def execute(self, _): self.set() return {'FINISHED'} - def cancel(self, ctx): + def cancel(self, _): self.set() - def invoke(self, ctx, event): + def invoke(self, ctx, _): global key_data self.available = key_data.available self.location = key_data.location @@ -111,15 +294,14 @@ def invoke(self, ctx, event): return ctx.window_manager.invoke_props_dialog(self, width=200) - class Anim_OT_Set_Key(Operator): """ Set keyframe on selected objects specific chanels """ - bl_idname = "anim.set_key" + bl_idname = 'anim.set_key' bl_label = "Set Keys" @classmethod def poll(self, ctx): - return ctx.mode in ['OBJECT', 'POSE'] + return ctx.mode in {'OBJECT', 'POSE'} def execute(self, ctx): global key_data @@ -140,22 +322,28 @@ def execute(self, ctx): if key_data.location: anim.keyframe_insert_menu(type='Location') + if key_data.rotation: anim.keyframe_insert_menu(type='Rotation') + if key_data.scale: anim.keyframe_insert_menu(type='Scaling') if key_data.visual_location: anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualLoc') + if key_data.visual_rotation: anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualRot') + if key_data.visual_scale: anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualScaling') if key_data.bbone_shape: anim.keyframe_insert_menu(type='BUILTIN_KSI_VisualScaling') + if key_data.whole_character: anim.keyframe_insert_menu(type='WholeCharacter') + if key_data.whole_character_selected: anim.keyframe_insert_menu(type='WholeCharacterSelected') @@ -166,16 +354,18 @@ class Anim_OT_Frame_Set(Operator): """ Set time slider curent time value """ bl_idname = 'anim.frame_set' bl_label = 'Set Frame' + bl_description = "" frame: EnumProperty( - name='Frame', default='Next', + name="Frame", items =[ - ('Next', 'Next', ''), - ('Previous', 'Previous', ''), - ('First', 'First', ''), - ('Last', 'Last', '') - ] - ) + ('Next', "Next", ""), + ('Previous', "Previous", ""), + ('First', "First", ""), + ('Last', "Last", "") + ], + default='Next' + ) # type: ignore def execute(self, ctx): scene = ctx.scene @@ -203,7 +393,8 @@ def execute(self, ctx): class Dopesheet_OT_Zoom_Extended(Operator): """ Zoom on selected or all keys """ bl_idname = 'action.zoom_extended' - bl_label = 'Zoom Extended' + bl_label = "Zoom Extended" + bl_description = "" @classmethod def poll(self, ctx): @@ -218,7 +409,8 @@ def execute(self, ctx): class Anim_OT_Delete_Key(Operator): """ Delete key withotu permisions """ bl_idname = 'anim.delete_key' - bl_label = 'Delete Key' + bl_label = "Delete Key" + bl_description = "" bl_options={'REGISTER', 'UNDO'} @classmethod @@ -235,181 +427,10 @@ def execute(self, ctx): return{'FINISHED'} -def update_freeze_on(self, ctx): - """ Get from calculate fields """ - self.frames = self.relase - self.push - self.next_step = self.next_push - self.push + 1 - self.repeat = (self.end - self.push) / self.next_step - - -def update_freeze_on_option(self, ctx): - """ Reset extera infor if hide """ - if not self.more: - self.next_step = 1 - self.repeat = 1 - - -class Freeze_on_Property(PropertyGroup): - """ Data method """ - frames: IntProperty( - name='Fix', min=1, default=1, - description='Number of frames object has to fixed' - ) - - next_step: IntProperty( - name='Cycle', min=1, default=1, - description='Length of walk/run cycle' - ) - - repeat: IntProperty( - name='Repeat', min=1, default=1, - description='Repeat same action for next steps' - ) - - """ Key chanels """ - key_location: BoolProperty( - name='Key Location', default=True, - description='Set Key for Location' - ) - - key_rotation: BoolProperty( - name='Key Rotation', default=True, - description='Set key for Rotation' - ) - - key_scale: BoolProperty( - name='Key Scale', default=False, - description='Set key for Scale' - ) - - """ Calculator """ - calculator: BoolProperty(name='Calculator', default=False) - - push: IntProperty( - name='Frame that first time foot touch the floor', - min=0, default=1, update=update_freeze_on - ) - - relase: IntProperty( - name='Frame that foot untouch floor', - min=0, default=1, update=update_freeze_on - ) - - next_push: IntProperty( - name='Second time foot touch floor', - min=0, default=1, update=update_freeze_on - ) - - end: IntProperty( - name='End of Walk/Run cycle', - min=0, default=1, update=update_freeze_on - ) - - """ Simple """ - more: BoolProperty( - name='More Option', default=False, - update=update_freeze_on_option - ) - - panel: BoolProperty(name='On Panel', default=False) - - - -def draw_freeze_on_panel(self, ctx): - layout = self.layout - freeze_on = ctx.scene.freeze_on - - box = layout.box() - box.enabled = not freeze_on.calculator # disable if calc is on - - row = box.row() - row.label(text='Fix position') - row.prop(freeze_on, 'more', icon='HAND') - row.prop(freeze_on, 'panel', icon='NODE_SEL') - - row = box.row() - row = box.row(align=True) - row.prop(freeze_on, 'frames', icon='TEMP') - - if freeze_on.more: - row.prop(freeze_on, 'next_step', icon='TRACKING_FORWARDS_SINGLE') - row.prop(freeze_on, 'repeat', icon='FILE_REFRESH') - - box = layout.box() - box.label(text='Set Key For') - row = box.row(align=True) - row.prop(freeze_on, 'key_location', text='Location', icon='BLANK1') - row.prop(freeze_on, 'key_rotation', text='Rotation', icon='BLANK1') - row.prop(freeze_on, 'key_scale', text='Scale',icon='BLANK1') - - box = layout.box() - box.prop(freeze_on, 'calculator', icon='ALIGN_TOP') - if freeze_on.calculator: - box.prop(freeze_on, 'push', icon='TEMP') - box.prop(freeze_on, 'relase', icon='TEMP') - box.prop(freeze_on, 'next_push', icon='TEMP') - box.prop(freeze_on, 'end', icon='TEMP') - # else: - # freeze_on.next_step = 1 - # freeze_on.repeat = 1 - - - -def fix_object_in_location(ctx, frame_current): - # get info from scene - freeze_on = ctx.scene.freeze_on - - # at each frame return object to first position and set key - for obj in ctx.selected_objects: - worldlocation = obj.matrix_world - for frame in range(frame_current, frame_current + freeze_on.frames): - obj.matrix_world = worldlocation - insert_key_to_current_state( - obj, frame, - freeze_on.key_location, - freeze_on.key_rotation, - freeze_on.key_scale - ) - - - -def fix_bone_in_location(ctx, frame_current): - # get info from scene - freeze_on = ctx.scene.freeze_on - - # at each frame return bone to first position and set key - armature = ctx.active_object - for bone in ctx.selected_pose_bones: - # Conver Pose bone space to world space - bone_matrix = armature.convert_space(pose_bone=bone, - matrix=bone.matrix, from_space='POSE', to_space='WORLD') - - for frame in range(frame_current, frame_current + freeze_on.frames): - ctx.scene.frame_current = frame - ctx.view_layer.update() - # Convert World to pose bone space - bone.matrix = armature.convert_space( - pose_bone=bone, - matrix=bone_matrix, - from_space='WORLD', - to_space='POSE' - ) - - insert_key_to_current_state( - bone, frame, - freeze_on.key_location, - freeze_on.key_rotation, - freeze_on.key_scale - ) - - -def apply_freeze_on(ctx): - pass - - class Anim_OT_Freeze_on(Operator): bl_idname = 'anim.freeze_on' - bl_label = 'Freeze On' + bl_label = "Freeze On" + bl_description = "" bl_options={'REGISTER', 'UNDO'} @classmethod @@ -448,17 +469,17 @@ def execute(self, ctx): ctx.scene.frame_current = start_time return{'FINISHED'} - def invoke(self, ctx, event): + def invoke(self, ctx, _): return ctx.window_manager.invoke_props_dialog(self) - class Anim_OP_Freeze_on(Panel): + bl_idname = 'VIEW3D_PT_freeze_on' bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_label = 'Freeze On' - bl_idname = 'VIEW3D_PT_freeze_on' - bl_category = 'Tool' + bl_label = "Freeze On" + bl_category = "Tool" + bl_description = "" @classmethod def poll(self, ctx): @@ -466,7 +487,7 @@ def poll(self, ctx): def draw(self, ctx): self.layout.operator_context = 'EXEC_DEFAULT' - self.layout.operator('anim.freeze_on', text='Apply') + self.layout.operator('anim.freeze_on', text="Apply") draw_freeze_on_panel(self, ctx) @@ -474,7 +495,7 @@ def draw(self, ctx): # graph.select_linked -classes = ( +classes = { Freeze_on_Property, Anim_OT_Set_Key_Filters, Anim_OT_Set_Key, @@ -483,23 +504,23 @@ def draw(self, ctx): Anim_OT_Freeze_on, Anim_OP_Freeze_on, Dopesheet_OT_Zoom_Extended -) +} def register_animation_key(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.Scene.freeze_on = PointerProperty( - type=Freeze_on_Property, name='Freeze On' + type=Freeze_on_Property, name="Freeze On" ) def unregister_animation_key(): del bpy.types.Scene.freeze_on - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) if __name__ == '__main__': diff --git a/tools/internal/animation/character_lister.py b/tools/internal/animation/character_lister.py index c27c221..3344ed2 100644 --- a/tools/internal/animation/character_lister.py +++ b/tools/internal/animation/character_lister.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/03/27 +# 2024/05/26 import bpy @@ -38,38 +38,42 @@ def get_character_by_name(self, name): return char return None -cs = CharacterSet() +character_set = CharacterSet() class Armature_TO_Character_Hide(Operator): """ Hide/Unhide Character (for now only Armature) """ - bl_idname = "anim.character_hide" + bl_idname = 'anim.character_hide' bl_label = "Character Hide" + bl_description = "" bl_options={'REGISTER', 'INTERNAL'} - name: StringProperty(default="") - def execute(self,ctx): - character = cs.get_character_by_name(self.name) + name: StringProperty(default="") # type: ignore + + def execute(self, _): + character = character_set.get_character_by_name(self.name) if character: state = not character.hide_viewport character.hide_viewport = state character.hide_render = False character.hide_select = False - return{"FINISHED"} + return{'FINISHED'} class Armature_TO_Character_Isolate(Operator): """ Isolate only this Character (for now only Armature) """ - bl_idname = "anim.character_isolate" + bl_idname = 'anim.character_isolate' bl_label = "Character Isolate" + bl_description = "" bl_options={'REGISTER', 'INTERNAL'} - name: StringProperty(default="") - def execute(self,ctx): - character = cs.get_character_by_name(self.name) + name: StringProperty(default="") # type: ignore + + def execute(self, _): + character = character_set.get_character_by_name(self.name) if character: - for char in cs.characters: + for char in character_set.characters: if char != character: char.hide_viewport = True char.hide_render = True @@ -79,72 +83,87 @@ def execute(self,ctx): char.hide_render = False char.hide_select = False - return{"FINISHED"} + return{'FINISHED'} class Armature_TO_Character_Rest(Operator): """ Rest/Pose Switch """ - bl_idname = "anim.character_rest" + bl_idname = 'anim.character_rest' bl_label = "Character Rest/Pose" + bl_description = "" bl_options={'REGISTER', 'INTERNAL'} - name: StringProperty(default="") - def execute(self,ctx): - character = cs.get_character_by_name(self.name) + name: StringProperty(default="") # type: ignore + + def execute(self, _): + character = character_set.get_character_by_name(self.name) if character: state = character.data.pose_position state = 'POSE' if state == 'REST' else 'REST' character.data.pose_position = state - return{"FINISHED"} + return{'FINISHED'} class Anim_TO_Character_Lister(Operator): """ List of Character for quick managment """ - bl_idname = "anim.character_lister" + bl_idname = 'anim.character_lister' bl_label = "Character lister" + bl_description = "" + bl_options={'REGISTER'} - def get_field(self,row,character): + def get_field(self, row, character): name = character.name - row.operator("object.select_by_name", icon='ARMATURE_DATA', text=character.name).name = name - + row.operator( + 'object.select_by_name', icon='ARMATURE_DATA', text=character.name + ).name = name + hide_icon = 'HIDE_ON' if character.hide_viewport else 'HIDE_OFF' - row.operator("anim.character_hide", icon=hide_icon, text='').name = name - - isolate_icon = 'RADIOBUT_OFF' if character.hide_viewport else 'RADIOBUT_ON' - row.operator("anim.character_isolate", icon=isolate_icon, text='').name = name - - rest_icon = 'ARMATURE_DATA' if character.data.pose_position == 'REST' else 'EVENT_T' - row.operator("anim.character_rest", icon=rest_icon, text='').name = name - - def draw(self,ctx): + row.operator( + 'anim.character_hide', icon=hide_icon, text="" + ).name = name + + hide_viewport = character.hide_viewport + isolate_icon = 'RADIOBUT_OFF' if hide_viewport else 'RADIOBUT_ON' + row.operator( + 'anim.character_isolate', icon=isolate_icon, text="" + ).name = name + + pose_position = character.data.pose_position + rest_icon = 'ARMATURE_DATA' if pose_position == 'REST' else 'EVENT_T' + row.operator( + 'anim.character_rest', icon=rest_icon, text="" + ).name = name + + def draw(self, _): box = self.layout.box() col = box.column() row = col.row() - row.label(text='') - for character in cs.characters: + row.label(text="") + for character in character_set.characters: self.get_field(col.row(align=True), character) - def execute(self,ctx): - return{"FINISHED"} + def execute(self, _): + return{'FINISHED'} - def invoke(self,ctx,event): + def invoke(self, ctx, _): """ collect armature objects in scene """ - cs.get_scene_characters() + character_set.get_scene_characters() return ctx.window_manager.invoke_props_dialog(self,width=200) class Object_TO_Make_Override_Library_plus(Operator): """ Convert Multiple selection to library overide """ - bl_idname = "object.make_override_library_multi" + bl_idname = 'object.make_override_library_multi' bl_label = "Make Library Override (Multi)" + bl_description = "" bl_options={'REGISTER'} @classmethod - def poll(self, ctx): + def poll(self, _): return bpy.ops.object.make_override_library.poll() - def execute(self,ctx): + def execute(self, ctx): objs = [] for obj in ctx.selected_objects: if obj.type == 'EMPTY': @@ -158,25 +177,25 @@ def execute(self,ctx): if bpy.ops.object.make_override_library.poll(): bpy.ops.object.make_override_library() - return{"FINISHED"} + return{'FINISHED'} -def library_override_menu(self, ctx): +def library_override_menu(self, _): self.layout.operator('object.make_override_library_multi') -classes = ( +classes = { Armature_TO_Character_Hide, Armature_TO_Character_Isolate, Armature_TO_Character_Rest, Anim_TO_Character_Lister, Object_TO_Make_Override_Library_plus -) +} def register_character_lister(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.VIEW3D_MT_object_relations.prepend(library_override_menu) @@ -184,8 +203,8 @@ def register_character_lister(): def unregister_character_lister(): bpy.types.VIEW3D_MT_object_relations.remove(library_override_menu) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) if __name__ == "__main__": register_character_lister() \ No newline at end of file diff --git a/tools/internal/animation/follow_path.py b/tools/internal/animation/follow_path.py new file mode 100644 index 0000000..d4aeca1 --- /dev/null +++ b/tools/internal/animation/follow_path.py @@ -0,0 +1,99 @@ +############################################################################ +# 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 3 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, see . +############################################################################ +# 2024/05/26 + +import bpy + +from bpy.types import Operator +from bpy.utils import register_class, unregister_class +from bpy.props import BoolProperty, EnumProperty + + +class FollowerData: + def __init__(self): + self.owner = None + self.path = None + self.floor = [] + self.bones = [] + self.iks = [] + + def reset(self): + self.owner = None + self.path = None + self.floor = [] + self.bones = [] + self.iks = [] + +followre_data = FollowerData() + + +def get_parts(cls, ctx): + global followre_data + if cls.type == 'PATH': + if ctx.object.type == 'CURVE': + followre_data.path = ctx.object + + if cls.typee == 'FLOOR': + pass + + if cls.type == 'IKS': + pass + + if cls.type == 'BONES': + pass + + +class Anim_OT_Follower_pick(Operator): + bl_idname = 'anim.followr_part_pickre' + bl_label = 'Follower Parts Pickr' + bl_options = {'REGISTER', 'UNDO'} + + type: EnumProperty( + name="", + items=[ + ('PATH', "Path", "Curve Object as following path"), + ('FLOOR', "Ground", "Get as touching surface"), + ('IKS', "IKs", "get selected IK bons for feet on ground"), + ('BONES', "Bonees", "Get selected bone has take effect") + ], + default="", + description="" + ) # type: ignore + + @classmethod + def poll(self, ctx): + True + + def execute(self, ctx): + get_parts(self, ctx) + return{'FINISHED'} + + +classes ={ + Anim_OT_Follower_pick +} + +def register_fallow_path(): + for cls in classes: + register_class(cls) + + +def unregister_follow_path(): + for cls in classes: + unregister_class(cls) + + +if __name__ == '__main__': + register_fallow_path() \ No newline at end of file diff --git a/tools/internal/animation/frame_update.py b/tools/internal/animation/frame_update.py index 9af7777..d406415 100644 --- a/tools/internal/animation/frame_update.py +++ b/tools/internal/animation/frame_update.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/03/27 +# 2024/05/26 import bpy @@ -23,18 +23,18 @@ from bsmax.graphic import get_header_color -def mode_updated(self, ctx): - if self.active_auto_use_select_pick_depth: +def mode_updated(cls, ctx): + if cls.active_auto_use_select_pick_depth: """ uspds use_select_pick_depth State """ - uspds = False if (ctx.mode == 'POSE') else self.use_select_pick_depth + uspds = False if (ctx.mode == 'POSE') else cls.use_select_pick_depth ctx.preferences.system.use_select_pick_depth = uspds -def autokey_state_updated(self, ctx): +def autokey_state_updated(cls, ctx): autoKey = ctx.scene.tool_settings.use_keyframe_insert_auto color = (0.5, 0.0, 0.0, 1.0) if autoKey else get_header_color() # allow to update if affect_theme active in preference - if color and self.preferences.affect_theme: + if color and cls.preferences.affect_theme: ctx.preferences.themes['Default'].dopesheet_editor.space.header = color @@ -100,7 +100,7 @@ def depsgraph_update(scene): # this operator need to see scene_state class # class Anim_OT_Auto_Key_Toggle(Operator): bl_idname = 'anim.auto_key_toggle' - bl_label = 'Auto Key Toggle' + bl_label = "Auto Key Toggle" bl_options = {'REGISTER', 'INTERNAL'} def execute(self, ctx): @@ -114,24 +114,25 @@ def execute(self, ctx): # this operator need to see scene_state class # class Anim_OT_Auto_Use_Select_Pick_Depth_Toggle(Operator): bl_idname = 'anim.auto_use_select_pick_depth_toggle' - bl_label = 'Auto Use Select Pick Depth Toggle' + bl_label = "Auto Use Select Pick Depth Toggle" bl_options = {'REGISTER'} def execute(self, ctx): global scene_state - scene_state.active_auto_use_select_pick_depth = not scene_state.active_auto_use_select_pick_depth + scene_state.active_auto_use_select_pick_depth = \ + not scene_state.active_auto_use_select_pick_depth return{'FINISHED'} -classes = ( +classes = { Anim_OT_Auto_Key_Toggle, Anim_OT_Auto_Use_Select_Pick_Depth_Toggle -) +} def register_frame_update(preferences): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) scene_state.store(bpy.context, preferences) # bpy.app.handlers.render_init.append(render_init) @@ -143,8 +144,8 @@ def unregister_frame_update(): # bpy.app.handlers.render_init.remove(render_init) bpy.app.handlers.depsgraph_update_pre.remove(depsgraph_update) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) if __name__ == '__main__': diff --git a/tools/internal/animation/selection_set.py b/tools/internal/animation/selection_set.py index 5ae2241..ad00f02 100644 --- a/tools/internal/animation/selection_set.py +++ b/tools/internal/animation/selection_set.py @@ -51,7 +51,7 @@ def get_names_from_armature_ss(self, armature, reload=False): """ Create name for new ganarated buttons """ if len(names) < buttons_count: - for i in range(len(names), buttons_count): + for _ in range(len(names), buttons_count): names.append('') self.button_names = names @@ -198,18 +198,18 @@ class Selection_Set_Scene(PropertyGroup): ('SET', 'Set', 'Set Selection groups'), ('EDIT', 'Edit', 'Edit buttons layout') ] - ) + ) # type: ignore - autohide: BoolProperty(name='Auto hide Non selected', default=False) - unselect: BoolProperty(name='Remove From Selection', default=False) - name: StringProperty(name="", update=rename_selection_set) - active: IntProperty(name="", default=0) + autohide: BoolProperty(name='Auto hide Non selected', default=False) # type: ignore + unselect: BoolProperty(name='Remove From Selection', default=False) # type: ignore + name: StringProperty(name="", update=rename_selection_set) # type: ignore + active: IntProperty(name="", default=0) # type: ignore class Selection_Set_Armature(PropertyGroup): - columns: IntProperty(name='columns', min=1, max=100, default=3) - rows: IntProperty(name='row', min=1, max=100, default=10) - names: StringProperty(name='') + columns: IntProperty(name='columns', min=1, max=100, default=3) # type: ignore + rows: IntProperty(name='row', min=1, max=100, default=10) # type: ignore + names: StringProperty(name='') # type: ignore class ARMATURE_OT_Selection_Set_Transfer(Operator): @@ -218,7 +218,7 @@ class ARMATURE_OT_Selection_Set_Transfer(Operator): bl_description = 'Copy/Past Selection setting from a Armature/Blender to other one' bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - action: StringProperty() + action: StringProperty() # type: ignore def execute(self, ctx): if self.action == 'COPY': @@ -234,7 +234,7 @@ class ARMATURE_OT_Selection_Set_Dimension_Resize(Operator): bl_label = 'Selection set Dimension Resize' # bl_description = '' bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - action: StringProperty() + action: StringProperty() # type: ignore def execute(self, ctx): selection_set = ctx.active_object.data.selection_set @@ -276,7 +276,7 @@ class ARMATURE_OT_Selection_Set(Operator): # bl_description = '' bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - index: IntProperty(name='index') + index: IntProperty(name='index') # type: ignore ctrl, shift, alt = False, False, False @@ -452,34 +452,40 @@ def draw(self, ctx): row.operator('pose.selection_set', text=name).index=index -classes = ( +classes = { Selection_Set_Scene, Selection_Set_Armature, Armature_OP_Selection_Set, ARMATURE_OT_Selection_Set, ARMATURE_OT_Selection_Set_Transfer, ARMATURE_OT_Selection_Set_Dimension_Resize -) +} def register_selection_set(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.Scene.selection_set = PointerProperty(type=Selection_Set_Scene) - bpy.types.Armature.selection_set = PointerProperty(type=Selection_Set_Armature) - bpy.types.PoseBone.selection_groups = StringProperty(name='Selection Groups', default='') + + bpy.types.Armature.selection_set = PointerProperty( + type=Selection_Set_Armature + ) + + bpy.types.PoseBone.selection_groups = StringProperty( + name="Selection Groups", default="" + ) def unregister_selection_set(): - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) del bpy.types.Scene.selection_set del bpy.types.Armature.selection_set del bpy.types.PoseBone.selection_groups -if __name__ == "__main__": +if __name__ == '__main__': # unregister_selection_set() register_selection_set() \ No newline at end of file diff --git a/tools/internal/animation/selection_set_v2.py b/tools/internal/animation/selection_set_v2.py index 2fe8e9f..cf1e30b 100644 --- a/tools/internal/animation/selection_set_v2.py +++ b/tools/internal/animation/selection_set_v2.py @@ -564,33 +564,35 @@ class SceneSelectionSet(PropertyGroup): ('SET', 'Set', 'Set Selection groups'), ('EDIT', 'Edit', 'Edit buttons layout') ] - ) + ) # type: ignore + # Shown on select mode - autohide: BoolProperty(name='Auto hide Non selected', default=False) - unselect: BoolProperty(name='Remove From Selection', default=False) + autohide: BoolProperty(name='Auto hide Non selected', default=False) # type: ignore + unselect: BoolProperty(name='Remove From Selection', default=False) # type: ignore # shown on Edit mode buttonName: StringProperty( name="", update=rename_active_button - ) + ) # type: ignore + tabName: StringProperty( name="", update=scene_selection_set_tab_name_update - ) + ) # type: ignore columns: IntProperty( name='columns', min=1, max=30, default=3, update=scene_selection_set_columns_update - ) + ) # type: ignore rows: IntProperty( name='rows', min=1, max=30, default=10, update=scene_selection_set_rows_update - ) + ) # type: ignore - activeRow: IntProperty(name="", default=0) - activeColamn: IntProperty(name="", default=0) + activeRow: IntProperty(name="", default=0) # type: ignore + activeColamn: IntProperty(name="", default=0) # type: ignore @@ -600,7 +602,7 @@ class ArmatureSelectionSet(PropertyGroup): name='Tab', items=armature_selection_set_tab_items, update=armature_selection_set_tab_update - ) + ) # type: ignore # main data for save in file tabs = {} @@ -635,7 +637,7 @@ class ARMATURE_OT_SelectionSetTab(Operator): ('ADD', 'Add', 'Add as new Tab'), ('REMOVE', 'Remove', 'Remove Current Tab') ] - ) + ) # type: ignore def execute(self, ctx): return tab_operator_execute(self, ctx) @@ -648,7 +650,7 @@ class ARMATURE_OT_SelectionSetIcon(Operator): bl_description = "" bl_options = {'REGISTER', 'INTERNAL'} - icon: StringProperty("") + icon: StringProperty("") # type: ignore def draw(self, ctx): armature_ot_selectionset_icon_draw(self, ctx) @@ -686,10 +688,10 @@ class ARMATURE_OT_SelectionSet(Operator): bl_description = 'Select Bones by selection set ID' bl_options = {'REGISTER', 'INTERNAL'} - name: StringProperty(name="Name") - id: IntProperty(name='id') - column: IntProperty(name='Column') - row: IntProperty(name='Row') + name: StringProperty(name="Name") # type: ignore + id: IntProperty(name='id') # type: ignore + column: IntProperty(name='Column') # type: ignore + row: IntProperty(name='Row') # type: ignore ctrl, shift, alt = False, False, False diff --git a/tools/internal/animation/time.py b/tools/internal/animation/time.py index 0abbbdd..9465da9 100644 --- a/tools/internal/animation/time.py +++ b/tools/internal/animation/time.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/04/15 +# 2024/05/27 import bpy from bpy.types import Operator @@ -22,20 +22,21 @@ class Anim_OT_Set_TimeLine_Range(Operator): bl_idname = 'anim.set_timeline_range' - bl_label = 'Set TimeLine Range' + bl_label = "Set TimeLine Range" + bl_description = "" start = False mouse_x = 0 - + #TODO change keys to upper case mode: EnumProperty( - name='Mode', + name="Mode", items =[ - ('Shift','Shift',''), - ('First','First',''), - ('End','End','') + ('Shift', "Shift", ""), + ('First', "First", ""), + ('End', "End", "") ], default='Shift' - ) + ) # type: ignore def modal(self, ctx, event): if not self.start: @@ -81,17 +82,18 @@ def modal(self, ctx, event): return {'RUNNING_MODAL'} - def invoke(self, ctx, event): + def invoke(self, ctx, _): ctx.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} class Anim_OT_Keys_Range_Set(Operator): bl_idname = 'anim.keys_range_set' - bl_label = 'Set Keys Time Raneg' + bl_label = "Set Keys Time Raneg" + bl_description = "" bl_options = {'REGISTER', 'INTERNAL'} - selection: BoolProperty(name='Selection', default=True) + selection: BoolProperty(name='Selection', default=True) # type: ignore # @classmethod # def poll(self, ctx): @@ -116,7 +118,7 @@ def execute(self, ctx): -def time_context_menu(self, ctx): +def time_context_menu(self, _): layout = self.layout layout.separator() layout.operator('anim.start_frame_set') @@ -124,16 +126,15 @@ def time_context_menu(self, ctx): # layout.operator('anim.keys_range_set') - -classes = ( +classes = { Anim_OT_Set_TimeLine_Range, Anim_OT_Keys_Range_Set -) +} def register_time(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.DOPESHEET_MT_context_menu.append(time_context_menu) bpy.types.SEQUENCER_MT_context_menu.append(time_context_menu) @@ -143,9 +144,9 @@ def unregister_time(): bpy.types.DOPESHEET_MT_context_menu.remove(time_context_menu) bpy.types.SEQUENCER_MT_context_menu.remove(time_context_menu) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_time() \ No newline at end of file diff --git a/tools/internal/object/__init__.py b/tools/internal/object/__init__.py index 07d9096..fa50cbe 100644 --- a/tools/internal/object/__init__.py +++ b/tools/internal/object/__init__.py @@ -20,6 +20,7 @@ from .clone_array_objects import register_clone_object, unregister_clone_object from .collection import register_collection, unregister_collection from .convert import register_convert, unregister_convert +from .cleanup import register_cleanup, unregister_cleanup from .create import register_create, unregister_create from .display import register_display, unregister_dispaly from .instancer import register_instancer, unregister_instancer @@ -39,6 +40,7 @@ def register_object(preferences): register_clone_object() register_collection() register_convert() + register_cleanup() register_create() register_display() register_instancer() @@ -58,6 +60,7 @@ def unregister_object(): unregister_clone_object() unregister_collection() unregister_convert() + unregister_cleanup() unregister_create() unregister_dispaly() unregister_instancer() diff --git a/tools/internal/object/cleanup.py b/tools/internal/object/cleanup.py new file mode 100644 index 0000000..b759514 --- /dev/null +++ b/tools/internal/object/cleanup.py @@ -0,0 +1,149 @@ +############################################################################ +# 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 3 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, see . +############################################################################ + +import bpy + +from bpy.utils import register_class, unregister_class +from bpy.types import Operator +from bpy.props import EnumProperty + +#TODO add mesh validate operator too + + +def clear_sculp_mask_data(ctx, objects): + active_object = ctx.active_object + customdata_mask_clear = bpy.ops.mesh.customdata_mask_clear + + bpy.ops.object.select_all(action='DESELECT') + + for object in objects: + object.select_set(state=True) + ctx.view_layer.objects.active = object + + if customdata_mask_clear.poll(): + customdata_mask_clear() + + object.select_set(state=False) + + for object in objects: + object.select_set(state=True) + ctx.view_layer.objects.active = active_object + + +def clear_skin_data(ctx, objects): + active_object = ctx.active_object + customdata_skin_clear = bpy.ops.mesh.customdata_skin_clear + + bpy.ops.object.select_all(action='DESELECT') + + for object in objects: + object.select_set(state=True) + ctx.view_layer.objects.active = object + + if customdata_skin_clear.poll(): + customdata_skin_clear() + + object.select_set(state=False) + + for object in objects: + object.select_set(state=True) + ctx.view_layer.objects.active = active_object + + +def clear_custom_split_normals_data(ctx, objects): + active_object = ctx.active_object + bpy.ops.object.select_all(action='DESELECT') + for object in objects: + if object.data.has_custom_normals: + object.select_set(state=True) + ctx.view_layer.objects.active = object + bpy.ops.mesh.customdata_custom_splitnormals_clear() + object.select_set(state=False) + + for object in objects: + object.select_set(state=True) + ctx.view_layer.objects.active = active_object + + +class Mesh_OT_Clear(Operator): + bl_idname = "mesh.clear" + bl_label = "Clear" + bl_options = {'REGISTER', 'UNDO'} + + action: EnumProperty( + name="Clear", + items=[ + ('MASK', "Scalpt Mask Data", ""), + ('SKIN', "Skin Data", ""), + ('NORMAL', "Custom Split Normals Data", "") + ] + ) # type: ignore + + @classmethod + def poll(self, ctx): + if ctx.area.type == 'VIEW_3D': + return ctx.mode == 'OBJECT' + return False + + def execute(self, ctx): + if self.action == 'MASK': + clear_sculp_mask_data(ctx, ctx.selected_objects) + + elif self.action == 'SKIN': + clear_skin_data(ctx, ctx.selected_objects) + + elif self.action == 'NORMAL': + clear_custom_split_normals_data(ctx, ctx.selected_objects) + + return{"FINISHED"} + + +def cleanup_additional_menu(self, _): + layout = self.layout + layout.separator() + layout.operator( + 'mesh.clear', text="Clear Sculpt Mask Data" + ).action='MASK' + + layout.operator( + 'mesh.clear', text="Clear Skin Data" + ).action='SKIN' + + layout.operator( + 'mesh.clear', text="Clear Custom Split Normals Data" + ).action='NORMAL' + + +classes = { + Mesh_OT_Clear +} + + +def register_cleanup(): + for cls in classes: + register_class(cls) + + bpy.types.VIEW3D_MT_object_cleanup.append(cleanup_additional_menu) + + +def unregister_cleanup(): + bpy.types.VIEW3D_MT_object_cleanup.remove(cleanup_additional_menu) + + for cls in classes: + unregister_class(cls) + + +if __name__ == '__main__': + register_cleanup() \ No newline at end of file diff --git a/tools/internal/object/instancer.py b/tools/internal/object/instancer.py index f02b396..f9aff16 100644 --- a/tools/internal/object/instancer.py +++ b/tools/internal/object/instancer.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/05/22 +# 2024/05/26 import bpy @@ -49,7 +49,7 @@ class Object_TO_Make_Unique(Operator): group: BoolProperty( name="Keep In group with each other", default=True, description="Keep selected groupe with each others" - ) + ) # type: ignore @classmethod def poll(self, ctx): diff --git a/tools/internal/render/backburner.py b/tools/internal/render/backburner.py index 2619fb1..0048166 100644 --- a/tools/internal/render/backburner.py +++ b/tools/internal/render/backburner.py @@ -17,11 +17,10 @@ # https://www.dropbox.com/s/ivschytl309jm2n/render_backburner.zip?dl=0 # # Update and Modified for Blender 2.8x to 4.x by Nevil # -# 2024/02/02 +# 2024/05/26 #TODO List # out of range warning -# gount out of range warning # backburner frames bit array arrenge # Frames Invert range # Frames refine @@ -37,19 +36,18 @@ "name": "BsMax-Backburner", "description": "Backburner for Blender 2.93 ~ 3.6", "author": "Matt Ebb | Blaize | Anthony Hunt | Spirou4D | Nevil", - "version": (0, 2, 1, 0),# Updated on 2023-11-26 - "blender": (3, 6, 0),# to 4.0 + "version": (0, 2, 2, 0), + "blender": (3, 6, 0),# ~ 4.1 "location": "Properties/ Output/ Backbrner", - "wiki_url": "https://github.com/NevilArt/BsMax_2_80/wiki", - "doc_url": "https://github.com/NevilArt/BsMax_2_80/wiki", - "tracker_url": "https://github.com/NevilArt/BsMax_2_80/issues", + "wiki_url": "https://github.com/NevilArt/BsMax/wiki", + "doc_url": "https://github.com/NevilArt/BsMax/wiki", + "tracker_url": "https://github.com/NevilArt/BsMax/issues", "category": "Render" } import bpy import os import subprocess -import random import platform from bpy.props import ( @@ -58,6 +56,9 @@ ) from bpy.types import Panel, Operator, PropertyGroup from bpy.utils import register_class, unregister_class +from bpy.app import version + +last_acceptable_repository_path = "" def backburner_path(): @@ -78,6 +79,21 @@ def blender_path(): return bpy.app.binary_path +def get_blender_file_name(ctx): + name = bpy.path.basename(bpy.data.filepath) + name = (name.split('.'))[0] + + # Add scene name if there more than one scenes + if len(bpy.data.scenes) > 1: + name += '_' + ctx.scene.name + + # Add viewlayer name if there more than one view layer + if len(ctx.scene.view_layers) > 1: + name += '_' + ctx.window.view_layer.name + + return name + + def string_to_integer_array(frames): """ Get string in BitArray format and conver to IntegerArray """ string, ints = '', [] @@ -89,8 +105,8 @@ def string_to_integer_array(frames): """ convert strings to integers """ string = string.strip() - ranges = string.split(",") - numstr = [r.split("-") for r in ranges] + ranges = string.split(',') + numstr = [r.split('-') for r in ranges] for n in numstr: if len(n) == 1: @@ -110,38 +126,75 @@ def string_to_integer_array(frames): def integer_array_to_bitarray_string(ints): return "" -#TODO check previees file and add +1 filename -def create_new_file_name(file_name): - random_id = random.randint(1000000, 9999999) - append_text = '_BACKBURNERTEMPFILE_' + str(random_id) - return os.path.splitext(file_name)[0] + append_text + '.blend' + +def convertable_to_integer(string): + for digit in string: + if not digit in '0123456789': + return False + return True + + +def get_max_file_name_digit(file_path, file_name, extention): + files = [file for file in os.listdir(file_path) + if file.lower().endswith(extention) + ] + file_name_length = len(file_name) + extention_length = len(extention) + files = [name for name in files if name.startswith(file_name)] + + digit_parts = [name[file_name_length:-extention_length] + for name in files + ] + + numbers = [int(number) for number in digit_parts + if convertable_to_integer(number) + ] + + return max(numbers)+1 if numbers else 0 -def check_start_frame(self, ctx): +def create_new_unigue_file_name(ctx): + backburner = ctx.scene.backburner + + file_path = backburner.repository_path \ + if backburner.use_repository else \ + os.path.dirname(bpy.data.filepath) + + # create a file name + file_name = backburner.job_name \ + if backburner.use_job_name else get_blender_file_name(ctx) + + file_name += "_BACKBURNERTEMPFILE_" + number = get_max_file_name_digit(file_path, file_name, '.blend') + full_name = file_path + '\\' + file_name + str(number) + '.blend' + return full_name + + +def check_start_frame(_, ctx): """ Make sure star frame allways smaller then end frame """ - csbb = ctx.scene.backburner - if csbb.frame_start > csbb.frame_end: - csbb.frame_end = csbb.frame_start + backburner = ctx.scene.backburner + if backburner.frame_start > backburner.frame_end: + backburner.frame_end = backburner.frame_start -def check_end_frame(self, ctx): +def check_end_frame(_, ctx): """ Make sure end frame allways bigger then start frame """ - csbb = ctx.scene.backburner - if csbb.frame_end < csbb.frame_start: - csbb.frame_start = csbb.frame_end + backburner = ctx.scene.backburner + if backburner.frame_end < backburner.frame_start: + backburner.frame_start = backburner.frame_end -def filter_frames_bitarray(self, ctx): +def filter_frames_bitarray(_, ctx): """ Remove illeagle character from Frames field """ - csbb = ctx.scene.backburner + backburner = ctx.scene.backburner string = '' - for l in csbb.frames_bitarray: + for l in backburner.frames_bitarray: if l in '0123456789,-': string += l - if csbb.frames_bitarray != string: - csbb.frames_bitarray = string + if backburner.frames_bitarray != string: + backburner.frames_bitarray = string def get_render_frames_count(ctx): @@ -152,8 +205,11 @@ def get_render_frames_count(ctx): if mode == 'FRAMES': return len(string_to_integer_array(backburner.frames_bitarray)) - start_frame = backburner.frame_start if mode == 'RANGE' else scene.frame_start + start_frame = backburner.frame_start \ + if mode == 'RANGE'else scene.frame_start + end_frame = backburner.frame_end if mode == 'RANGE' else scene.frame_end + return end_frame - start_frame + 1 @@ -169,8 +225,11 @@ def task_fild(start, end): return field -def create_task_list_file(scene, filename): - """ Combine all taskes and write in file for submit to Backburner manager """ +def create_task_list_file(ctx, filename): + """ Combine all taskes and write in + file for submit to Backburner manager """ + + scene = ctx.scene backburner = scene.backburner mode = backburner.override_frame_range @@ -183,8 +242,11 @@ def create_task_list_file(scene, filename): frames = string_to_integer_array(backburner.frames_bitarray) else: - start_frame = backburner.frame_start if mode == 'RANGE' else scene.frame_start - end_frame = backburner.frame_end if mode == 'RANGE' else scene.frame_end + start_frame = backburner.frame_start \ + if mode == 'RANGE' else scene.frame_start + + end_frame = backburner.frame_end \ + if mode == 'RANGE' else scene.frame_end for frame in range(start_frame, end_frame+1): frames.append(frame) @@ -219,7 +281,7 @@ def create_task_list_file(scene, filename): """ Write task to file """ dir = os.path.dirname(filename) if os.access(dir, os.W_OK): - task_list_file_name = os.path.join(dir,"submit_temp_cmd") + task_list_file_name = os.path.join(dir, 'submit_temp_cmd') file = open(task_list_file_name, 'w') file.write(task) file.close() @@ -227,12 +289,12 @@ def create_task_list_file(scene, filename): return task_list_file_name -def create_cmd_command(scene): - cbb = scene.backburner +def create_cmd_command(ctx): + backburner = ctx.scene.backburner - file_name = create_new_file_name(bpy.data.filepath) + file_name = create_new_unigue_file_name(ctx) bpy.ops.wm.save_as_mainfile(filepath=file_name, copy=True) - task_list_file = create_task_list_file(scene, file_name) + task_list_file = create_task_list_file(ctx, file_name) # cmdjob.exe -jobname "testJob" # -description "job de test" @@ -249,35 +311,35 @@ def create_cmd_command(scene): # -q -mip -silent -U MAXScript %tp2 """ Create Backburner CMD Text """ - cmd = cbb.path_backburner - cmd += ' -jobName:"' + cbb.job_name + '"' - cmd += ' -manager: ' + cbb.manager + cmd = backburner.path_backburner + cmd += ' -jobName:"' + backburner.job_name + '"' + cmd += ' -manager: ' + backburner.manager - if cbb.port != 3234: - cmd += ' -port: '+ str(cbb.port) + if backburner.port != 3234: + cmd += ' -port: '+ str(backburner.port) - if cbb.group != '': - cmd += ' -group:"' + cbb.group + '"' + if backburner.group != '': + cmd += ' -group:"' + backburner.group + '"' - if cbb.job_details != '': - cmd += ' -description:"' + cbb.job_details + '"' + if backburner.job_details != '': + cmd += ' -description:"' + backburner.job_details + '"' - cmd += ' -priority:' + str(cbb.priority) - cmd += ' -timeout:' + str(cbb.timeout) + cmd += ' -priority:' + str(backburner.priority) + cmd += ' -timeout:' + str(backburner.timeout) - if cbb.suspended: + if backburner.suspended: cmd += ' -suspended' cmd += ' -taskList:"' + task_list_file + '"' cmd += ' -taskName: 1' - if cbb.use_custom_path: - cmd += ' "' + cbb.blender_path + '"' + if backburner.use_custom_path: + cmd += ' "' + backburner.blender_path + '"' else: cmd += ' "' + blender_path() + '"' # cmd += ' -submit: "'+ bpy.data.filepath + '"' - if cbb.background_render: + if backburner.background_render: cmd += ' --background' cmd += ' "' + file_name + '"' @@ -288,8 +350,8 @@ def create_cmd_command(scene): return task_list_file, cmd -def submit(scene): - task_list_file, cmd = create_cmd_command(scene) +def submit(ctx): + task_list_file, cmd = create_cmd_command(ctx) succeed = True """ Try to Submit job to backburner """ try: @@ -304,16 +366,16 @@ def submit(scene): def get_preset_file_path(): - ''' Return the pathand file name of preset file ''' + """ Return the pathand file name of preset file """ preset_path = bpy.utils.user_resource('CONFIG') + '/BsMax/' - ''' Creat preset directory if not exist ''' + """ Creat preset directory if not exist """ if not os.path.isdir(preset_path): os.mkdir(preset_path) file_name = 'Backburner.ini' - ''' if Backburner.ini file not exist create an empty one ''' + """ if Backburner.ini file not exist create an empty one """ backburner_file = preset_path + file_name if not os.path.exists(backburner_file): @@ -333,107 +395,125 @@ def create_script_text(ctx): text += 'backburner.timeout = ' + str(backburner.timeout) + '\n' text += 'backburner.priority = ' + str(backburner.priority) + '\n' text += 'backburner.suspended = ' + str(backburner.suspended) + '\n' + text += 'backburner.frames_per_task = ' text += str(backburner.frames_per_task) + '\n' text += 'backburner.manager = "' + backburner.manager + '"\n' text += 'backburner.port = ' + str(backburner.port) + '\n' text += 'backburner.group = "' + backburner.group + '"\n' + text += 'backburner.background_render = ' text += str(backburner.background_render) + '\n' + text += 'backburner.use_job_name = ' + str(backburner.use_job_name) +'\n' + text += 'backburner.use_custom_path = ' text += str(backburner.use_custom_path) + '\n' - text += 'backburner.blender_path = r"' + backburner.blender_path +'"' + text += 'backburner.blender_path = r"' + backburner.blender_path +'"\n' + text += 'backburner.use_repository = ' + str(backburner.use_repository) +'\n' + text += 'backburner.repository_path = r"' + backburner.repository_path + '"' + return text -def draw_backburner_panel(self, ctx): - csbb = ctx.scene.backburner +def draw_backburner_panel(cls, ctx): + backburner = ctx.scene.backburner - layout = self.layout + layout = cls.layout row = layout.row() row.operator( 'wm.url_open', icon='HELP' ).url= "https://github.com/NevilArt/BsMax/wiki/Render-Tools" + button_text = "Submit " + str(get_render_frames_count(ctx)) + button_text += " frames to Backburner" row.operator( 'render.backburner_action', - text='Submit ' + str(get_render_frames_count(ctx)) + ' frames to Backburner', + text=button_text, icon='RENDER_ANIMATION' ).action='SUBMIT' - - row.operator('render.backburner_action', text='', icon='ADD').action='SAVE' + + save_icon = 'ADD' if version < (4, 0, 1) else 'FILE_TICK' + row.operator('render.backburner_action', text="", icon=save_icon).action='SAVE' row.operator( 'render.backburner_action', - text='', + text="", icon='RECOVER_LAST' ).action='LOAD' - + layout.separator() - + row = layout.row() - row.prop(csbb, 'job_name') + row.prop(backburner, 'job_name') + use_name_icon = 'CHECKBOX_HLT' if backburner.use_job_name else 'CHECKBOX_DEHLT' + row.prop(backburner, 'use_job_name', text="", icon=use_name_icon) row.operator( 'render.backburner_action', - text='', + text="", icon='FILE_REFRESH' ).action='GETNAME' - + row = layout.row() - row.prop(csbb, 'job_details') - row.label(text='',icon='BLANK1') + row.prop(backburner, 'job_details') + row.label(text="", icon='BLANK1') layout.separator() row = layout.row() - row.prop(csbb, 'timeout') - row.prop(csbb, 'priority') - row.prop(csbb, 'suspended', text='', icon='EVENT_S') + row.prop(backburner, 'timeout') + row.prop(backburner, 'priority') + row.prop(backburner, 'suspended', text="", icon='EVENT_S') layout.separator() - + row = layout.row() - row.prop(csbb, 'override_frame_range') - row.prop(csbb, 'frames_per_task') - + row.prop(backburner, 'override_frame_range') + row.prop(backburner, 'frames_per_task') + row = layout.row() - if csbb.override_frame_range == 'RANGE': - row.prop(csbb, 'frame_start') - row.prop(csbb, 'frame_end') + if backburner.override_frame_range == 'RANGE': + row.prop(backburner, 'frame_start') + row.prop(backburner, 'frame_end') + + elif backburner.override_frame_range == 'FRAMES': + row = layout.row() + row.prop(backburner, 'frames_bitarray', text="") + # row.prop( + # backburner, 'invert_bitarray', text="", icon = 'ARROW_LEFTRIGHT' + # ) - elif csbb.override_frame_range == 'FRAMES': - layout.prop(csbb, 'frames_bitarray', text='') - layout.separator() row = layout.row() - row.prop(csbb, 'manager') - row.prop(csbb, 'port') - row.prop(csbb, 'group') - + row.prop(backburner, 'manager') + row.prop(backburner, 'port') + row.prop(backburner, 'group') + row = layout.row() - row.prop(csbb, 'use_custom_path') - row.prop(csbb, 'background_render') - - if csbb.use_custom_path: - row = layout.row() - row.prop(csbb, 'blender_path') + row.prop(backburner, 'use_custom_path') + row.prop(backburner, 'background_render') + if backburner.use_custom_path: + row = layout.row() + row.prop(backburner, 'blender_path') -def subcation_get_name(ctx): - blend_file_name = ctx.blend_data.filepath + row = layout.row() + row.prop(backburner, 'use_repository') + if backburner.use_repository: + row = layout.row() + row.prop(backburner, 'repository_path') - if blend_file_name != "": - file_name = bpy.path.basename(blend_file_name) - the_name = (file_name.split('.'))[0] - else: - the_name = "New Job" +def subcation_get_name(ctx): + job_name = "New Job" + + if bpy.data.filepath: + job_name = get_blender_file_name(ctx) - ctx.scene.backburner.job_name = the_name + ctx.scene.backburner.job_name = job_name def subaction_save(ctx): @@ -468,149 +548,215 @@ def subaction_riverce_frames(ctx): pass +def repository_path_check(cls, _): + global last_acceptable_repository_path + repository_path = cls.repository_path + if os.access(repository_path, os.W_OK): + if repository_path[-1] == '\\': + cls.repository_path = repository_path[:-1] + last_acceptable_repository_path = cls.repository_path + else: + cls.repository_path = last_acceptable_repository_path + + def subaction_submit(self, ctx): if ctx.blend_data.filepath == '': - self.report({'WARNING'},'Save File Befor Submit') - return{"FINISHED"} + self.report({'WARNING'}, "Save File Before Submit") + return{'FINISHED'} - csbb = ctx.scene.backburner + backburner = ctx.scene.backburner - if csbb.blender_path == '': + if backburner.blender_path == '': self.report({'ERROR'}, "Network path to Blender hasn't been set") return {'CANCELLED'} - if csbb.path_backburner == '': + if backburner.path_backburner == '': self.report({'ERROR'}, "Path to Backburner cmdjob.exe hasn't been set") return {'CANCELLED'} - self.report({'OPERATOR'},'Submitting...') + self.report({'OPERATOR'}, "Submitting...") - if submit(ctx.scene): - self.report({'OPERATOR'},'Job Submited to backburner manager') + if submit(ctx): + self.report({'OPERATOR'}, "Job Submited to backburner manager") else: self.report( - {'WARNING'},'Backburner manager not found. Failed to submission.' + {'WARNING'}, "Backburner manager not found. Failed to submission." ) #TODO delete temp render file if fails class Backburner_Property(PropertyGroup): job_name: StringProperty( - name='Job Name', maxlen=256, default='New Job', - description='Name of the job to be shown in Backburner' - ) + name="Job Name", + maxlen=256, + default="New Job", + description="Name of the Job to be shown in Backburner Monitor" + ) # type: ignore + + use_job_name: BoolProperty( + name="", + default=False, + description="Use Job Name as Repository File Name" + ) # type: ignore job_details: StringProperty( - name='Description', maxlen=400, default='', - description='Add aditional information to render task' - ) + name="Description", + maxlen=400, default="", + description="Add aditional information to render task" + ) # type: ignore frames_per_task: IntProperty( - name='Frames per Task', - min=1, max=9999, soft_min=1, soft_max=1000, default=1, - description='Number of frames to give each render node' - ) + name="Frames per Task", + min=1, max=9999, + soft_min=1, soft_max=1000, + default=1, + description="Number of frames to give each render node" + ) # type: ignore timeout: IntProperty( - name='Timeout', - min=1, max=99999, soft_min=1, soft_max=1440, default=120, - description='Timeout per task' - ) + name="Timeout", + min=1, max=99999, + soft_min=1, soft_max=1440, + default=120, + description="Timeout per task" + ) # type: ignore priority: IntProperty( - name='Priority', - min=0, max=99, soft_min=0, soft_max=99, default=50, - description='Priority of this job (0 is Critical)' - ) - - suspended: BoolProperty ( - name='Suspended', default=True, - description='Submit Job as Suspended' - ) + name="Priority", + min=0, max=99, + soft_min=0, soft_max=99, + default=50, + description="Priority of this job (0 is Critical)" + ) # type: ignore + + suspended: BoolProperty( + name="Suspended", + default=True, + description="Submit Job as Suspended" + ) # type: ignore override_frame_range: EnumProperty( - name='Frames', - description='Override Render frames Range', - default='ACTIVE', + name="Frames", items=[ - ('ACTIVE', 'Active Time', ''), - ('RANGE', 'Range', ''), - ('FRAMES', 'Specific Frames', '') - ] - ) + ('ACTIVE', "Active Time", ""), + ('RANGE', "Range", ""), + ('FRAMES', "Specific Frames", "") + ], + default='ACTIVE', + description="Override Render frames Range" + ) # type: ignore frame_start: IntProperty( - name='Start Frame', - min=1, max=50000, default=1, update=check_start_frame, - description='Start frame of animation sequence to render' - ) + name="Start Frame", + min=1, max=50000, + default=1, + update=check_start_frame, + description="Start frame of animation sequence to render" + ) # type: ignore frame_end: IntProperty( - name='End Frame', - min=1, max=50000, default=250, update=check_end_frame, - description='End frame of animation sequence to render' - ) + name="End Frame", + min=1, max=50000, + default=250, + update=check_end_frame, + description="End frame of animation sequence to render" + ) # type: ignore frames_bitarray: StringProperty( - name='Frames', maxlen=400, default='1,3,5-7', + name="Frames", + maxlen=400, + default="1,3,5-7", update=filter_frames_bitarray, - description='Custom frames' - ) + description="Custom frames" + ) # type: ignore + + invert_bitarray: BoolProperty( + name="", default=False, + description="Submit invert range of frames" + ) # type: ignore manager: StringProperty( - name='Manager', maxlen=400, default='localhost', - description='Name of render manager' - ) + name="Manager", + maxlen=400, + default="localhost", + description="Name of render manager" + ) # type: ignore port: IntProperty( - name='Port', - min=0, max=999999, default=3234, - description='Manager Port' - ) + name="Port", + min=0, max=999999, + default=3234, + description="Manager Port" + ) # type: ignore group: StringProperty( - name='Groups', maxlen=400, default='', - description='Name of Render Group' - ) + name="Groups", + maxlen=400, + default="", + description="Name of Render Group" + ) # type: ignore path_backburner: StringProperty( - name='Backburner Path', - maxlen=400, subtype='FILE_PATH', default=backburner_path(), - description='Path to Backburner cmdjob.exe' - ) - - use_custom_path: BoolProperty (name='Use Custom Blender', default=False) + name="Backburner Path", + maxlen=400, + subtype='FILE_PATH', + default=backburner_path(), + description="Path to Backburner cmdjob.exe" + ) # type: ignore + + use_custom_path: BoolProperty( + name="Use Custom Blender", + default=False + ) # type: ignore blender_path: StringProperty( - name='Blender Path', - maxlen=400, subtype='FILE_PATH', default=blender_path(), - description='Path to blender.exe' - ) + name="Blender Path", + maxlen=400, + subtype='FILE_PATH', + default=blender_path(), + description="Path of blender.exe" + ) # type: ignore # options: BoolProperty (name='More', default=False) background_render: BoolProperty ( - name='Render in Background', default=True - ) + name="Render in Background", default=True + ) # type: ignore submit_file: BoolProperty ( - name='Submit file to Manager', default=False - ) - + name="Submit file to Manager", default=False + ) # type: ignore + + servers_description = "Render this job only with the servers specified " + servers_description += "(semi-colon separated list - ignored if group is used)" servers: StringProperty( - name='Servers', maxlen=400, default='', - description='Render this job only with the servers specified (semi-colon separated list - ignored if group is used)' - ) + name="Servers", + maxlen=400, + default="", + description=servers_description + ) # type: ignore + + use_repository: BoolProperty( + name="Use Custom Repository", + default=False + ) # type: ignore + + repository_path: StringProperty( + name="Repository Path", + subtype='DIR_PATH', + update=repository_path_check, + description="Custom Repository Directory" + ) # type: ignore class Render_OT_Backburner_Action(Operator): """ All Backburner sub action """ - bl_idname = "render.backburner_action" + bl_idname = 'render.backburner_action' bl_label = "Backburner Sub action" bl_options = {'REGISTER', 'INTERNAL'} - action: StringProperty(name='Action') + action: StringProperty(name='Action') # type: ignore def execute(self, ctx): @@ -638,7 +784,7 @@ def execute(self, ctx): class Render_OT_Backburner(Operator): """ Submit scene to Backburner as render job. """ bl_idname = 'render.backburner' - bl_label = 'Backburner V0.2.1.0' + bl_label = "Backburner V0.2.2.0" bl_options = {'REGISTER'} def draw(self, ctx): @@ -655,36 +801,36 @@ class RENDER_PT_Backburner(Panel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'output' - bl_label = 'Backburner' + bl_label = "Backburner" bl_options = {'DEFAULT_CLOSED'} def draw(self, ctx): draw_backburner_panel(self, ctx) -def backburner_menu(self, ctx): +def backburner_menu(self, _): self.layout.operator( 'render.backburner', - text='Submit to Backburner', + text="Submit to Backburner", icon='NETWORK_DRIVE' ) -classes = ( +classes = { Backburner_Property, Render_OT_Backburner, Render_OT_Backburner_Action, RENDER_PT_Backburner -) +} def register_backburner(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.Scene.backburner = PointerProperty( type=Backburner_Property, - name='Backburner Submission' + name="Backburner Submission" ) bpy.types.TOPBAR_MT_render.prepend(backburner_menu) @@ -694,8 +840,8 @@ def unregister_backburner(): bpy.types.TOPBAR_MT_render.remove(backburner_menu) del bpy.types.Scene.backburner - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) """ Calls when installed as stand alone add-on """ @@ -707,5 +853,5 @@ def unregister(): unregister_backburner() -if __name__ == "__main__": +if __name__ == '__main__': register_backburner() \ No newline at end of file diff --git a/tools/internal/render/light_lister.py b/tools/internal/render/light_lister.py index 4b6d543..7d30908 100644 --- a/tools/internal/render/light_lister.py +++ b/tools/internal/render/light_lister.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/02/07 +# 2024/05/26 import bpy @@ -32,28 +32,28 @@ def get_light_field(col, light): """ Create Icone name from type name """ icon = 'LIGHT_' + data.type row.operator( - 'object.select_by_name', text='', icon=icon + 'object.select_by_name', text="", icon=icon ).name = light.name - row.prop(light, 'name', text='') - row.prop(data, 'type', text='') - row.prop(data, 'color', text='', icon='BLANK1') - row.prop(data, 'energy', text='') + row.prop(light, 'name', text="") + row.prop(data, 'type', text="") + row.prop(data, 'color', text="", icon='BLANK1') + row.prop(data, 'energy', text="") if data.type == 'POINT': - row.prop(data, 'shadow_soft_size', text='') + row.prop(data, 'shadow_soft_size', text="") elif data.type == 'SUN': - row.prop(data, 'angle', text='') + row.prop(data, 'angle', text="") elif data.type == 'SPOT': - row.prop(data, 'shadow_soft_size', text='') + row.prop(data, 'shadow_soft_size', text="") elif data.type == 'AREA': # row.prop(data, 'shape', text='') - row.prop(data, 'size', text='') + row.prop(data, 'size', text="") - row.prop(data, 'use_shadow', text='', icon='COMMUNITY') + row.prop(data, 'use_shadow', text="", icon='COMMUNITY') def get_active_light_field(col, light): @@ -65,109 +65,109 @@ def get_active_light_field(col, light): if data.type == 'POINT': row = col.row() - row.prop(data, 'use_custom_distance', text='Custom Distance') + row.prop(data, 'use_custom_distance', text="Custom Distance") if data.use_custom_distance: - row.prop(data, 'cutoff_distance', text='') - row.label(text='') - row.label(text='') + row.prop(data, 'cutoff_distance', text="") + row.label(text="") + row.label(text="") row = col.row() - row.prop(data, 'use_shadow', text='Shadow') + row.prop(data, 'use_shadow', text="Shadow") if data.use_shadow: - row.prop(data, 'shadow_buffer_clip_start', text='Clip Start') - row.prop(data, 'shadow_buffer_bias', text='Bias') - row.label(text='') + row.prop(data, 'shadow_buffer_clip_start', text="Clip Start") + row.prop(data, 'shadow_buffer_bias', text="Bias") + row.label(text="") row = col.row() - row.prop(data, 'use_contact_shadow', text='Contact Shadow') + row.prop(data, 'use_contact_shadow', text="Contact Shadow") if data.use_contact_shadow: - row.prop(data, 'contact_shadow_distance', text='Distance') - row.prop(data, 'contact_shadow_bias', text='Bias') - row.prop(data, 'contact_shadow_thickness', text='Thickness') + row.prop(data, 'contact_shadow_distance', text="Distance") + row.prop(data, 'contact_shadow_bias', text="Bias") + row.prop(data, 'contact_shadow_thickness', text="Thickness") if data.type == 'SUN': row = col.row() - row.prop(data, 'use_shadow', text='Shadow') + row.prop(data, 'use_shadow', text="Shadow") if data.use_shadow: - row.prop(data, 'shadow_buffer_bias', text='Bias') + row.prop(data, 'shadow_buffer_bias', text="Bias") row.label(text='') row.label(text='') row = col.row() - row.prop(data, 'shadow_cascade_count', text='Count') - row.prop(data, 'shadow_cascade_fade', text='Fade') - row.prop(data, 'shadow_cascade_max_distance', text='Max Distance') - row.prop(data, 'shadow_cascade_exponent', text='Distribution') + row.prop(data, 'shadow_cascade_count', text="Count") + row.prop(data, 'shadow_cascade_fade', text="Fade") + row.prop(data, 'shadow_cascade_max_distance', text="Max Distance") + row.prop(data, 'shadow_cascade_exponent', text="Distribution") row = col.row() if data.use_shadow: - row.prop(data, 'use_contact_shadow', text='Contact Shadow') + row.prop(data, 'use_contact_shadow', text="Contact Shadow") if data.use_contact_shadow: - row.prop(data, 'contact_shadow_distance', text='Distance') - row.prop(data, 'contact_shadow_bias', text='Bias') - row.prop(data, 'contact_shadow_thickness', text='Thikness') + row.prop(data, 'contact_shadow_distance', text="Distance") + row.prop(data, 'contact_shadow_bias', text="Bias") + row.prop(data, 'contact_shadow_thickness', text="Thikness") if data.type == 'SPOT': row = col.row() - row.prop(data, 'use_custom_distance', text='Custom Distance') + row.prop(data, 'use_custom_distance', text="Custom Distance") if data.use_custom_distance: - row.prop(data, 'cutoff_distance', text='Distance') + row.prop(data, 'cutoff_distance', text="Distance") row.label(text='') row.label(text='') row = col.row() - row.prop(data, 'show_cone', text='Show Cone') - row.prop(data, 'spot_size', text='Size') - row.prop(data, 'spot_blend', text='Blend') + row.prop(data, 'show_cone', text="Show Cone") + row.prop(data, 'spot_size', text="Size") + row.prop(data, 'spot_blend', text="Blend") row.label(text='') row = col.row() - row.prop(data, 'use_shadow', text='Shadow') + row.prop(data, 'use_shadow', text="Shadow") if data.use_shadow: - row.prop(data, 'shadow_buffer_clip_start', text='Clip Start') - row.prop(data, 'shadow_buffer_bias', text='Bias') + row.prop(data, 'shadow_buffer_clip_start', text="Clip Start") + row.prop(data, 'shadow_buffer_bias', text="Bias") row.label(text='') row = col.row() - row.prop(data, 'use_contact_shadow', text='Contact Shadow') + row.prop(data, 'use_contact_shadow', text="Contact Shadow") if data.use_contact_shadow: - row.prop(data, 'contact_shadow_distance', text='Distance') - row.prop(data, 'contact_shadow_bias', text='Bias') - row.prop(data, 'contact_shadow_thickness', text='Thikness') + row.prop(data, 'contact_shadow_distance', text="Distance") + row.prop(data, 'contact_shadow_bias', text="Bias") + row.prop(data, 'contact_shadow_thickness', text="Thikness") if data.type == 'AREA': row = col.row() row.prop(data, 'shape', text='') if data.shape in {'SQUARE', 'DISK'}: - row.prop(data, 'size', text='Size') + row.prop(data, 'size', text="Size") row.label(text='') if data.shape in {'RECTANGLE', 'ELLIPSE'}: - row.prop(data, 'size', text='Size X') - row.prop(data, 'size_y', text='Size') + row.prop(data, 'size', text="Size X") + row.prop(data, 'size_y', text="Size") - row.label(text='') + row.label(text="") row = col.row() - row.prop(data, 'use_custom_distance', text='Custom Distance') + row.prop(data, 'use_custom_distance', text="Custom Distance") if data.use_custom_distance: - row.prop(data, 'cutoff_distance', text='Distance') - row.label(text='') - row.label(text='') + row.prop(data, 'cutoff_distance', text="Distance") + row.label(text="") + row.label(text="") row = col.row() - row.prop(data, 'use_shadow', text='Shadow') + row.prop(data, 'use_shadow', text="Shadow") if data.use_shadow: - row.prop(data, 'shadow_buffer_clip_start', text='Clip Start') - row.prop(data, 'shadow_buffer_bias', text='Bias') - row.label(text='') + row.prop(data, 'shadow_buffer_clip_start', text="Clip Start") + row.prop(data, 'shadow_buffer_bias', text="Bias") + row.label(text="") row = col.row() - row.prop(data, 'use_contact_shadow', text='Contact Shadow') + row.prop(data, 'use_contact_shadow', text="Contact Shadow") if data.use_contact_shadow: - row.prop(data, 'contact_shadow_distance', text='Distance') - row.prop(data, 'contact_shadow_bias', text='Bias') - row.prop(data, 'contact_shadow_thickness', text='Thikness') + row.prop(data, 'contact_shadow_distance', text="Distance") + row.prop(data, 'contact_shadow_bias', text="Bias") + row.prop(data, 'contact_shadow_thickness', text="Thikness") def get_material_field(col, material, node): @@ -177,43 +177,43 @@ def get_material_field(col, material, node): color_slot = node.inputs['Color'] if is_free_node_input(color_slot): - row.prop(color_slot, 'default_value', text='', icon='BLANK1') + row.prop(color_slot, 'default_value', text="", icon='BLANK1') else: - row.label(text='', icon='LOCKED') + row.label(text="", icon='LOCKED') strength_slot = node.inputs['Strength'] if is_free_node_input(strength_slot): - row.prop(strength_slot, 'default_value', text='Strength') + row.prop(strength_slot, 'default_value', text="Strength") else: - row.label(text=' ', icon='LOCKED') + row.label(text=" ", icon='LOCKED') if node.type == 'BSDF_PRINCIPLED': emission_slot = node.inputs['Emission'] if is_free_node_input(emission_slot): - row.prop(emission_slot, 'default_value', text='', icon='BLANK1') + row.prop(emission_slot, 'default_value', text="", icon='BLANK1') else: row.label(text='', icon='LOCKED') strength_slot = node.inputs['Emission Strength'] if is_free_node_input(strength_slot): - row.prop(strength_slot, 'default_value', text='Strength') + row.prop(strength_slot, 'default_value', text="Strength") else: - row.label(text=' ', icon='LOCKED') + row.label(text=" ", icon='LOCKED') - row.label(text='') - row.label(text='') + row.label(text="") + row.label(text="") def get_camera_field(row, camera, active): srow = row.row(align=True) icon = 'OUTLINER_OB_CAMERA' if active else 'CAMERA_DATA' srow.operator( - 'camera.active_by_name', icon=icon, text='' + 'camera.active_by_name', icon=icon, text="" ).name = camera.name srow.operator( @@ -222,21 +222,21 @@ def get_camera_field(row, camera, active): srow = row.row(align=True) srow.label(text='', icon='DRIVER_ROTATIONAL_DIFFERENCE') - srow.prop(camera.data, 'type', text='') - srow.prop(camera.data, 'lens', text='') + srow.prop(camera.data, 'type', text="") + srow.prop(camera.data, 'lens', text="") srow = row.row(align=True) srow.label(text='', icon='CURVE_PATH') - srow.prop(camera.data, 'clip_start', text='') - srow.prop(camera.data, 'clip_end', text='') + srow.prop(camera.data, 'clip_start', text="") + srow.prop(camera.data, 'clip_end', text="") srow = row.row(align=True) srow.label(text='', icon='ZOOM_SELECTED') - srow.prop(camera.data.dof, 'use_dof', text='') - srow.prop(camera.data.dof, 'focus_distance', text='') + srow.prop(camera.data.dof, 'use_dof', text="") + srow.prop(camera.data.dof, 'focus_distance', text="") srow = row.row(align=True) - srow.prop(camera.data, 'display_size', text='') + srow.prop(camera.data, 'display_size', text="") def get_lights(): @@ -306,7 +306,8 @@ def get_emission_matts(): class Render_OT_Light_Lister(Operator): bl_idname = 'render.light_lister' - bl_label = 'Light lister' + bl_label = "Light lister" + bl_description = "" bl_options = {'REGISTER', 'INTERNAL'} lights, materials = [], [] @@ -334,11 +335,10 @@ def draw(self, ctx): for material, node in self.materials: get_material_field(col, material, node) - def execute(self, ctx): - # self.report({'OPERATOR'}, 'bpy.ops.render.light_lister()') + def execute(self, _): return{'FINISHED'} - def invoke(self, ctx, event): + def invoke(self, ctx, _): self.lights = get_lights() self.materials = get_emission_matts() return ctx.window_manager.invoke_props_dialog(self, width=500) @@ -347,16 +347,17 @@ def invoke(self, ctx, event): class Camera_OT_Actve_By_Name(Operator): """ Set as active camera """ bl_idname = 'camera.active_by_name' - bl_label = 'Active Camera by name' + bl_label = "Active Camera by name" + bl_description = "" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - name: bpy.props.StringProperty(default='') + name: bpy.props.StringProperty(default="") # type: ignore @classmethod def poll(self, ctx): return ctx.mode == 'OBJECT' - def execute(self,ctx): + def execute(self, ctx): bpy.ops.object.select_all(action='DESELECT') if self.name != '': @@ -368,7 +369,8 @@ def execute(self,ctx): class Render_OT_Camera_Lister(Operator): bl_idname = 'render.camera_lister' - bl_label = 'Camera lister' + bl_label = "Camera lister" + bl_description = "" bl_options = {'REGISTER', 'INTERNAL'} lights = [] @@ -381,36 +383,36 @@ def draw(self,ctx): is_active = (cam == ctx.scene.camera) get_camera_field(col.row(align=False), cam, is_active) - def execute(self, ctx): + def execute(self, _): return{'FINISHED'} - def invoke(self, ctx, event): + def invoke(self, ctx, _): self.cameras = get_cameras() return ctx.window_manager.invoke_props_dialog(self, width=700) -def render_menu(self, ctx): +def render_menu(self, _): layout = self.layout layout.separator() layout.operator( - 'render.light_lister', text='Light Lister', icon='LIGHT_SUN' + 'render.light_lister', text="Light Lister", icon='LIGHT_SUN' ) layout.operator( - 'render.camera_lister', text='Camera Lister', icon='CAMERA_DATA' + 'render.camera_lister', text="Camera Lister", icon='CAMERA_DATA' ) -classes = ( +classes = { Render_OT_Light_Lister, Camera_OT_Actve_By_Name, Render_OT_Camera_Lister -) +} def register_light_lister(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.TOPBAR_MT_render.append(render_menu) @@ -418,8 +420,8 @@ def register_light_lister(): def unregister_light_lister(): bpy.types.TOPBAR_MT_render.remove(render_menu) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) if __name__ == '__main__':