From c7718fb80276720b4004128f9c16fface9e6914b Mon Sep 17 00:00:00 2001 From: NevilArt Date: Wed, 25 Dec 2024 08:46:28 +0300 Subject: [PATCH] U241225 Marker Copy/Paste add Render presets manager update public code cleanup --- CHANGELOG.md | 12 +- CONTRIBUTORS.md | 2 + README.md | 10 +- __init__.py | 4 +- tools/internal/armature/attach.py | 5 +- tools/internal/armature/bone.py | 8 +- tools/internal/camera/__init__.py | 3 + tools/internal/camera/cameras.py | 168 +++--- tools/internal/camera/frame_catcher.py | 25 +- tools/internal/camera/motion_blur.py | 223 ++++++++ tools/internal/graph_editor/marker.py | 84 ++- tools/internal/nodes/copy_past.py | 152 ++++++ tools/internal/object/modifier.py | 18 +- tools/internal/render/preset.py | 478 +++++++++--------- tools/internal/rigg/joystick.py | 102 ++-- tools/internal/rigg/naming.py | 20 +- tools/internal/rigg/shapekey.py | 87 ++-- tools/internal/rigg/wire_parameter.py | 31 +- tools/internal/scene/crowds.py | 13 +- tools/internal/scene/file.py | 47 +- tools/internal/text/arabic.py | 2 + tools/internal/text/console.py | 5 +- tools/internal/text/info.py | 13 +- tools/internal/text/text_editor.py | 12 +- tools/internal/transform/align_objects.py | 42 +- tools/internal/transform/coordinate.py | 31 +- tools/internal/transform/transform_type_in.py | 300 ++++++----- tools/internal/uv/edit.py | 156 +++--- tools/internal/uv/panel.py | 164 +++--- tools/internal/view/__init__.py | 2 - tools/internal/view/copy_past.py | 87 ++-- tools/internal/view/droptool.py | 11 +- tools/internal/view/floate_ditor.py | 206 ++++---- tools/internal/view/gride.py | 12 +- tools/internal/view/viewport.py | 5 +- tools/internal/view/viewportbg.py | 15 +- tools/internal/vse/__init__.py | 5 +- tools/internal/vse/videp_sequence_ediotor.py | 203 ++++---- tools/pipeline/__init__.py | 6 + tools/pipeline/maya_abc_cleaner.py | 423 ++++++++++++++++ tools/special/max/modifier_panel.py | 21 +- 41 files changed, 2117 insertions(+), 1096 deletions(-) create mode 100644 tools/internal/camera/motion_blur.py create mode 100644 tools/internal/nodes/copy_past.py create mode 100644 tools/pipeline/maya_abc_cleaner.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cd26f8b..3fc71d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -# 0, 1, 2, 20240714 +# 0, 1, 3, 20240823 +* Topo Symmetrize tool that keep UV data (under the EditMesh/Mesh menu) +* Camera target select search from last constraint and ignore disable ones this makes better result. +* Alingt Target to camera matchs the target to camera direction (Camera/Target/Align). +* Command panel Instancer warning issue fixed. +* Batch Rename for VSE issue fixed. +* Place for Farsi arabic type corrector. + +# 0, 1, 3, 20240714 * Multi object Connector for geometry node (select objects in viewport in node editor active node that has object input sokets and run Auto object picker). * Farsi and Arabic typing tool (only apears in seach menu "Farsi & Arabic Corrector") (type text press OK and paste your text any where you need). * Auto rename shifted markers to frame number (timeline marker/auto rename). @@ -6,7 +14,7 @@ * Codes for older than Blender 3.6 removed. * blender_manifest.toml file removed from github version and became a normal addon again. -# 0, 1, 2, 20240701 +# 0, 1, 3, 20240701 * BsMax on Blender 4.2 is not shown as legesy any more. * Render preset Save/Load update, bugfix and adapted for new EEVEE. * Add object Validate to Cleanup tools list too and bug fixed. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 46249b7..9983983 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -19,10 +19,12 @@ * solenekinkielele@yahoo.fr * Nattapong Khwanpray +* Florian Schreiber * revovl@gmail.com * shane@studiopiper.com.au * tmdals1825@gmail.com * ddr3653@gmail.com +* jackpot0006@yahoo.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 cd8c8ce..8b216e8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# BsMax Blender Addon 3.6LTS ~ 4.2LTS +# BsMax Blender Addon 3.6LTS ~ 4.3 BsMax is package of Modeling, Rig & Animation, Render tools and UI mimic for [Blender 3D](https://www.blender.org/). @@ -36,12 +36,8 @@ If you found this product useful and want to support this project you can Donate * [Download Older Version for Blender 2.80 ~ 3.5](https://github.com/NevilArt/BsMax_2_80) ## Recent Updates and Changes -* Topo Symmetrize tool that keep UV data (under the EditMesh/Mesh menu) -* Camera target select search from last constraint and ignore disable ones this makes better result. -* Alingt Target to camera matchs the target to camera direction (Camera/Target/Align). -* Command panel Instancer warning issue fixed. -* Batch Rename for VSE issue fixed. -* Place for Farsi arabic type corrector. +* Marker Copy/Paste added. +* Render preset tool updated. * [Change log ...](https://github.com/NevilArt/BsMax/blob/master/CHANGELOG.md) ## Special Thanks diff --git a/__init__.py b/__init__.py index 86005e5..7c46950 100644 --- a/__init__.py +++ b/__init__.py @@ -31,15 +31,15 @@ 'name': "BsMax", 'description': "BsMax UI simulations and Tool pack (Blender 3.6LTS ~ 4.2LTS)", 'author': "Naser Merati (Nevil)", - 'version': (0, 1, 3, 20240823), + 'version': (0, 1, 3, 20241225), 'blender': (3, 6, 0), # Minimum Version '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" } +# TODO switch prefrence saving data to jason # Add public classes, variables and functions path if not in list. path = os.path.dirname(os.path.realpath(__file__)) if path not in sys.path: diff --git a/tools/internal/armature/attach.py b/tools/internal/armature/attach.py index 042c521..9586147 100644 --- a/tools/internal/armature/attach.py +++ b/tools/internal/armature/attach.py @@ -92,9 +92,10 @@ def picked_target(ctx, source, subsource, target, subtarget): class Armature_OT_Attach(PickOperator): - bl_idname = "armature.attach" + bl_idname = 'armature.attach' bl_label = "Attach" bl_options = {'REGISTER', 'UNDO'} + filters = ['ARMATURE', 'MESH', 'FONT', 'CURVE'] @classmethod @@ -115,5 +116,5 @@ def unregister_attach(): bpy.utils.unregister_class(Armature_OT_Attach) -if __name__ == "__main__": +if __name__ == '__main__': register_attach() \ No newline at end of file diff --git a/tools/internal/armature/bone.py b/tools/internal/armature/bone.py index d511bc5..b24ddcf 100644 --- a/tools/internal/armature/bone.py +++ b/tools/internal/armature/bone.py @@ -194,8 +194,8 @@ def select_keyed_menu(self, ctx): def register_bone(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.VIEW3D_MT_select_pose.append(select_keyed_menu) @@ -203,8 +203,8 @@ def register_bone(): def unregister_bone(): bpy.types.VIEW3D_MT_select_pose.remove(select_keyed_menu) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) if __name__ == '__main__': diff --git a/tools/internal/camera/__init__.py b/tools/internal/camera/__init__.py index fa87cb2..9d2d55b 100644 --- a/tools/internal/camera/__init__.py +++ b/tools/internal/camera/__init__.py @@ -15,16 +15,19 @@ from .cameras import register_cameras, unregister_cameras from .copy_paste import register_copy_past, unregister_copy_past +from .motion_blur import register_motion_blur, unregister_motion_blur from .target_camera import register_terget_camera, unregister_terget_camera def register_camera(): register_cameras() register_copy_past() + register_motion_blur() register_terget_camera() def unregister_camera(): unregister_cameras() unregister_copy_past() + unregister_motion_blur() unregister_terget_camera() \ No newline at end of file diff --git a/tools/internal/camera/cameras.py b/tools/internal/camera/cameras.py index 5073789..723849a 100644 --- a/tools/internal/camera/cameras.py +++ b/tools/internal/camera/cameras.py @@ -12,10 +12,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/08/27 + import bpy from bpy.props import EnumProperty, BoolProperty from bpy.types import Operator + from bsmax.state import has_constraint @@ -24,33 +27,37 @@ def get_view3d(ctx): return ctx.area.spaces.active areas = [area for area in ctx.screen.areas if area.type == 'VIEW_3D'] - spaces = [space for area in areas - for space in area.spaces - if space.type == 'VIEW_3D'] + spaces = [ + space for area in areas for space in area.spaces + if space.type == 'VIEW_3D' + ] + #TODO try to find biggest view 3d if spaces: return spaces[0] return None - def get_cameras(ctx): if ctx.active_object: if ctx.active_object.type == 'CAMERA': if ctx.active_object.select_get(): return [ctx.active_object] else: - return [obj for obj in bpy.data.objects if obj.type == 'CAMERA'] + return [ + obj for obj in bpy.data.objects if obj.type == 'CAMERA' + ] else: return [obj for obj in bpy.data.objects if obj.type == 'CAMERA'] else: if ctx.selected_objects: - return [obj for obj in ctx.selected_objects if obj.type == 'CAMERA'] + return [ + obj for obj in ctx.selected_objects if obj.type == 'CAMERA' + ] else: return [obj for obj in bpy.data.objects if obj.type == 'CAMERA'] - def set_camera(ctx, camera, view3d): ctx.scene.camera = camera view3d.region_3d.view_perspective = 'CAMERA' @@ -59,13 +66,10 @@ def set_camera(ctx, camera, view3d): bpy.ops.view3d.view_center_camera('INVOKE_DEFAULT') - -# create a camera from view class Camera_OT_Create_From_View(Operator): - """ Create a new camera and align it to view """ bl_idname = 'camera.create_from_view' bl_label = 'Create Camera From View' - bl_description = 'Create New Camera From View' + bl_description = "Create a new camera and align it to view" bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -95,45 +99,52 @@ def execute(self, ctx): return{'FINISHED'} - class Camera_OT_Set_Active(Operator): bl_idname = 'camera.set_active' bl_label = 'Set as Active Camera' - bl_description = 'Set Selected Camera As Active Camera' + bl_description = "Set Selected Camera As Active Camera" bl_options = {'REGISTER', 'UNDO'} - + + @classmethod + def poll(self, ctx): + if ctx.active_object: + return ctx.active_object.type == 'CAMERA' + return False + def execute(self, ctx): - if ctx.active_object != None: - if ctx.active_object.type == 'CAMERA': - ctx.scene.camera = ctx.active_object - """ If Autokey on, Bind Marker To Active Camera """ - if ctx.scene.tool_settings.use_keyframe_insert_auto: - cur_frame = ctx.scene.frame_current - name = 'F_' + str(cur_frame) - marker = ctx.scene.timeline_markers.new(name, frame = cur_frame) - marker.camera = ctx.active_object - return{'FINISHED'} + ctx.scene.camera = ctx.active_object + """ If Autokey on, Bind Marker To Active Camera """ + if ctx.scene.tool_settings.use_keyframe_insert_auto: + cur_frame = ctx.scene.frame_current + name = 'F_' + str(cur_frame) + marker = ctx.scene.timeline_markers.new(name, frame = cur_frame) + marker.camera = ctx.active_object + + return{'FINISHED'} class Camera_OT_Select(Operator): - """ List camera and make active picked one """ bl_idname = 'camera.select' - bl_label = 'Select Camera' + bl_label = "Select Camera" + bl_description = "List camera and make active picked one" bl_property = 'search' - bl_description = '' @classmethod def poll(self, ctx): - return ctx.area.type in {'VIEW_3D', 'DOPESHEET_EDITOR', - 'GRAPH_EDITOR', 'NLA_EDITOR'} + return ctx.area.type in { + 'VIEW_3D', 'DOPESHEET_EDITOR', 'GRAPH_EDITOR', 'NLA_EDITOR' + } def scene_cameras(self, ctx): - return [(cam.name, cam.name, '') for cam in bpy.data.objects - if cam.type == 'CAMERA'] - - search: EnumProperty(name='Select Camera', items=scene_cameras) - + return [ + (cam.name, cam.name, '') + for cam in bpy.data.objects if cam.type == 'CAMERA' + ] + + search: EnumProperty( + name='Select Camera', items=scene_cameras + ) # type: ignore def execute(self, ctx): camera = bpy.data.objects[self.search] @@ -152,7 +163,6 @@ def invoke(self, ctx, event): return{'RUNNING_MODAL'} - class Camera_OT_Lock_To_View_Toggle(Operator): bl_idname = 'camera.lock_to_view_toggle' bl_label = 'Lock Camera to view (Toggle)' @@ -167,11 +177,10 @@ def execute(self, ctx): return {'FINISHED'} - class Camera_OT_Lock_Transform(Operator): bl_idname = 'camera.lock_transform' - bl_label = 'Lock Camera Transform' - bl_description = 'Lock active cameras transform' + bl_label = "Lock Camera Transform" + bl_description = "Lock active cameras transform" bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -188,14 +197,14 @@ def execute(self, ctx): return {'FINISHED'} - class Camera_OT_Select_Active_Camera(Operator): bl_idname = 'camera.select_active_camera' - bl_label = 'Select Active Camera/Target' - bl_description = 'Select Acitve Camera/Target' + bl_label = "Select Active Camera/Target" + bl_description = "Select Acitve Camera/Target" + bl_options = {'REGISTER', 'UNDO'} - selcam: BoolProperty(name='Select Camera') - seltrg: BoolProperty(name='Select Target') + selcam: BoolProperty(name='Select Camera') # type: ignore + seltrg: BoolProperty(name='Select Target') # type: ignore @classmethod def poll(self, ctx): @@ -203,23 +212,25 @@ def poll(self, ctx): def execute(self, ctx): cam = ctx.scene.camera - if cam != None: - bpy.ops.object.select_all(action='DESELECT') - if self.selcam: - cam.select_set(state=True) - if self.seltrg: - if has_constraint(cam, 'TRACK_TO'): - targ = cam.constraints["Track To"].target - targ.select_set(state=True) + if not cam: + return {'FINISHED'} - return {'FINISHED'} + bpy.ops.object.select_all(action='DESELECT') + if self.selcam: + cam.select_set(state=True) + if self.seltrg: + if has_constraint(cam, 'TRACK_TO'): + targ = cam.constraints["Track To"].target + targ.select_set(state=True) + + return {'FINISHED'} class Camera_OT_Show_Safe_Area(Operator): bl_idname = 'camera.show_safe_areas' - bl_label = 'Show Safe Area' - bl_description = 'Show Safe Area' + bl_label = "Show Safe Area" + bl_description = "Show Safe Area" @classmethod def poll(self, ctx): @@ -227,21 +238,29 @@ def poll(self, ctx): def execute(self, ctx): camera = ctx.scene.camera - if camera: - camera.data.show_safe_areas = not camera.data.show_safe_areas - camera.data.show_passepartout = True - camera.data.passepartout_alpha = 1 - return {'FINISHED'} + if not camera: + return {'FINISHED'} + + camera.data.show_safe_areas = not camera.data.show_safe_areas + camera.data.show_passepartout = True + camera.data.passepartout_alpha = 1 + return {'FINISHED'} class Camera_OT_Lock_Toggle(Operator): bl_idname = 'camera.lock_toggle' - bl_label = 'Show Safe Area' - bl_description = 'Show Safe Area' - - name: EnumProperty(name='Name', default='CAMERA', - items=[('CAMERA', 'Camera',''), ('CURSOR', 'Cursor','')]) + bl_label = "Show Safe Area" + bl_description = "Show Safe Area" + + name: EnumProperty( + name='Name', + items=[ + ('CAMERA', "Camera", ""), + ('CURSOR', "Cursor", "") + ], + default='CAMERA' + ) # type: ignore @classmethod def poll(self, ctx): @@ -250,21 +269,21 @@ def poll(self, ctx): def execute(self, ctx): if self.name == 'CAMERA': ctx.space_data.lock_camera = not ctx.space_data.lock_camera + elif self.name == 'CURSOR': ctx.space_data.lock_cursor = not ctx.space_data.lock_cursor - return {'FINISHED'} + return {'FINISHED'} -def camera_menu(self, ctx): +def camera_menu(self, _): layout = self.layout layout.separator() layout.operator('camera.create_from_view') layout.operator('camera.select') - -classes = ( +classes = { Camera_OT_Set_Active, Camera_OT_Create_From_View, Camera_OT_Select, @@ -273,22 +292,21 @@ def camera_menu(self, ctx): Camera_OT_Select_Active_Camera, Camera_OT_Show_Safe_Area, Camera_OT_Lock_Toggle -) - +} def register_cameras(): - for c in classes: - bpy.utils.register_class(c) - bpy.types.VIEW3D_MT_view_cameras.append(camera_menu) + for cls in classes: + bpy.utils.register_class(cls) + bpy.types.VIEW3D_MT_view_cameras.append(camera_menu) def unregister_cameras(): bpy.types.VIEW3D_MT_view_cameras.remove(camera_menu) - for c in classes: - bpy.utils.unregister_class(c) + for cls in classes: + bpy.utils.unregister_class(cls) if __name__ == '__main__': diff --git a/tools/internal/camera/frame_catcher.py b/tools/internal/camera/frame_catcher.py index f167257..401d591 100644 --- a/tools/internal/camera/frame_catcher.py +++ b/tools/internal/camera/frame_catcher.py @@ -16,7 +16,6 @@ from bpy.types import Operator - def fill_sequence_gaps(seq, size): ret = [] for i in range(len(seq)): @@ -33,7 +32,6 @@ def fill_sequence_gaps(seq, size): return ret - def remove_tinys(seq, size): ret,i = [], 1 while i < len(seq): @@ -59,7 +57,6 @@ def remove_tinys(seq, size): return ret - def frames_to_string(frames): print("-Frames> ",frames) string = '' @@ -79,7 +76,6 @@ def frames_to_string(frames): return string - def has_right(frames, index): if index < len(frames): if frames[index] + 1 == frames[index + 1]: @@ -87,7 +83,6 @@ def has_right(frames, index): return False - def has_left(frames, index): if index > 0: if frames[index] - 1 == frames[index - 1]: @@ -95,7 +90,6 @@ def has_left(frames, index): return False - def is_right_stand(frames, index, count): if index + 1 < len(frames): if frames[index] + count < frames[index + 1]: @@ -103,7 +97,6 @@ def is_right_stand(frames, index, count): return False - def is_left_stand(frames, index, count): if index - 1 > 0: if frames[index - 1] + count < frames[index]: @@ -111,7 +104,6 @@ def is_left_stand(frames, index, count): return False - def is_right_seq(frames, index, count): ret = True for i in range(index, index + count): @@ -122,7 +114,6 @@ def is_right_seq(frames, index, count): return ret - def is_left_seq(frames, index, count): ret = True for i in range(index - count, index): @@ -135,7 +126,6 @@ def is_left_seq(frames, index, count): return ret - class Transform_Data: def __init__(self): self.matrix_world = None @@ -151,7 +141,6 @@ def rotation(self): return self.matrix_world.to_euler() - def collect_transform_data(ctx, obj, first, last): transform_data = [] """ store time line condition """ @@ -176,7 +165,6 @@ def collect_transform_data(ctx, obj, first, last): return transform_data - def compar_params(cam, time1, time2): """ this function checs the FOV changes """ # Ret = True @@ -190,7 +178,6 @@ def compar_params(cam, time1, time2): return True - def compar_f(f1, f2, tol): if f1 != f2: heigher, lower = [f1, f2] if f1 >= f2 else [f2, f1] @@ -198,7 +185,6 @@ def compar_f(f1, f2, tol): return True - def compar_p3(p1, p2, tol): x = compar_f(p1.x, p2.x, tol) y = compar_f(p1.y, p2.y, tol) @@ -206,7 +192,6 @@ def compar_p3(p1, p2, tol): return x and y and z - def compar_transform(tr1, tr2, tol): if tr1 != None and tr2 != None: cp = tr1.location == tr2.location @@ -217,7 +202,6 @@ def compar_transform(tr1, tr2, tol): return False - def collect_move_frames(ctx, cam): first, last = ctx.scene.frame_start, ctx.scene.frame_end transform_datas = collect_transform_data(ctx, cam, first, last) @@ -243,7 +227,6 @@ def collect_move_frames(ctx, cam): return frames - def catch_camera_frames(ctx): cam = ctx.scene.camera if cam != None: @@ -260,9 +243,8 @@ def catch_camera_frames(ctx): return string - class Camera_OT_Catch_Frames(Operator): - bl_idname = "camera.catch_frames" + bl_idname = 'camera.catch_frames' bl_label = "Catch Frames" bl_description = "" bl_options = {'REGISTER', 'UNDO'} @@ -277,16 +259,13 @@ def execute(self, ctx): return{"FINISHED"} - def register_camera_catcher(): bpy.utils.register_class(Camera_OT_Catch_Frames) - def unregister_camera_catcher(): bpy.utils.unregister_class(Camera_OT_Catch_Frames) - -if __name__ == "__main__": +if __name__ == '__main__': register_camera_catcher() \ No newline at end of file diff --git a/tools/internal/camera/motion_blur.py b/tools/internal/camera/motion_blur.py new file mode 100644 index 0000000..9cebb40 --- /dev/null +++ b/tools/internal/camera/motion_blur.py @@ -0,0 +1,223 @@ +############################################################################ +# 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/09/20 + +from typing import Any +import bpy + +from bpy.types import Context, Operator +from bpy.props import IntProperty +from bpy.utils import register_class, unregister_class + + +def is_digits(string): + for char in string: + if not char in '0123456789': + return False + return True + + +def clone_camera(ctx, camera): + camera_even = camera.copy() + camera_odd = camera.copy() + camera_even.name = camera.name + "_Even" + camera_odd.name = camera.name + "_Odd" + + if camera.users_collection: + for collection in camera.users_collection: + collection.objects.link(camera_even) + collection.objects.link(camera_odd) + else: + ctx.collection.objects.link(camera_even) + ctx.collection.objects.link() + + if camera.animation_data: + action = camera.animation_data.action + camera_even.animation_data.action = action.copy() + camera_odd.animation_data.action = action.copy() + + ctx.view_layer.update() + + return camera_even, camera_odd + + +def get_camera_key_times(camera): + if not camera.animation_data or not camera.animation_data.action: + return [] + + channels_to_check = {'location', 'rotation_euler', 'rotation_quaternion'} + keyframe_frames = set() + + for fcurve in camera.animation_data.action.fcurves: + if any(channel in fcurve.data_path for channel in channels_to_check): + keyframe_frames.update( + [int(kf.co[0]) for kf in fcurve.keyframe_points] + ) + + return sorted(keyframe_frames) + + +def collect_cut_morakers(ctx, camera): + markers = ctx.scene.timeline_markers + markers = sorted(markers, key=lambda marker: marker.frame) + key_times = get_camera_key_times(camera) + return [marker for marker in markers if marker.frame in key_times] + + +def clone_transform_keyframe_to_time(camera, key_time, new_time): + if camera.animation_data and camera.animation_data.action: + action = camera.animation_data.action + for fcurve in action.fcurves: + # Find the keyframe at keytime + for keyframe in fcurve.keyframe_points: + if keyframe.co[0] == key_time: + # Clone the keyframe to newframe + fcurve.keyframe_points.insert(new_time, keyframe.co[1]) + fcurve.update() + + +def bind_camera_to_markers(camera, markers, key_offset): + for marker in markers: + key_time = marker.frame + new_time = key_time + key_offset + clone_transform_keyframe_to_time(camera, key_time, new_time) + marker.camera = camera + + +class Camera_OT_Motion_Blur_Solver(Operator): + bl_idname = 'camera.motion_blur_solver' + bl_label = 'Motion Blure Solver' + bl_description = "Add Marker at camera jumping frame with out Motion blur issue" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, ctx): + if ctx.object: + return ctx.object.type == 'CAMERA' + return False + + def execute(self, ctx): + camera_even, camera_odd = clone_camera(ctx, ctx.object) + cut_markers = collect_cut_morakers(ctx, ctx.object) + even_markers = cut_markers[0::2] + odd_markers = cut_markers[1::2] + + bind_camera_to_markers(camera_even, even_markers, -5) + bind_camera_to_markers(camera_odd, odd_markers, -5) + + return{'FINISHED'} + + +class Camera_OT_Jump_Frame_Lister(Operator): + bl_idname = 'camera.jump_frame_lister' + bl_label = "Jump Frames Lister" + bl_description = "Return Bitarray list for camera jumps" + + before: IntProperty( + name="Before", + min=0, default= 1, + description="Number of frame most pack befor jump marker" + ) # type: ignore + after: IntProperty( + name="After", + min=0, default= 0, + description="Number of frame most pack After jump marker" + ) # type: ignore + + @classmethod + def poll(self, ctx): + if ctx.object: + return ctx.object.type == 'CAMERA' + return False + + def draw(self, ctx): + layout = self.layout + row = layout.row() + row.prop(self, 'before', text="Before") + row.prop(self, 'after', text="After") + + def execute(self, ctx): + cut_markers = collect_cut_morakers(ctx, ctx.object) + length = self.before + self.after + string = "" + + if length == 0: + for marker in cut_markers: + frame = marker.frame + if frame > 0: + string += str(frame) + "," + + elif length == 1: + for marker in cut_markers: + frame = marker.frame + + if frame <= 0: + continue + + start = frame - self.before + end = frame + self.after + string += str(start) + "," + str(end) + "," + + elif length > 1: + for marker in cut_markers: + frame = marker.frame + + if frame <= 0: + continue + + start = frame - self.before + end = frame + self.after + string += str(start) + "-" + str(end) + "," + + ctx.window_manager.clipboard = string + + return{'FINISHED'} + + def invoke(self, ctx, event): + ctx.window_manager.invoke_props_dialog(self) + return {'RUNNING_MODAL'} + + +classes = { + Camera_OT_Motion_Blur_Solver, + Camera_OT_Jump_Frame_Lister +} + + +def motion_blur_solver_manu(self, _): + layout = self.layout + layout.separator() + # layout.operator('camera.motion_blur_solver') + layout.operator('camera.jump_frame_lister') + + +def register_motion_blur(): + for cls in classes: + register_class(cls) + + bpy.types.VIEW3D_MT_view_cameras.append(motion_blur_solver_manu) + + +def unregister_motion_blur(): + bpy.types.VIEW3D_MT_view_cameras.remove(motion_blur_solver_manu) + + for cls in classes: + unregister_class(cls) + + +if __name__ == '__main__': + # bpy.types.VIEW3D_MT_view_cameras.append(motion_blur_solver_manu) + for cls in classes: + register_class(cls) \ No newline at end of file diff --git a/tools/internal/graph_editor/marker.py b/tools/internal/graph_editor/marker.py index 7d0c161..3db60a4 100644 --- a/tools/internal/graph_editor/marker.py +++ b/tools/internal/graph_editor/marker.py @@ -12,11 +12,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not,see . ############################################################################ -# 2024/07/12 +# 2024/10/21 import bpy from bpy.types import Operator +from bpy.props import BoolProperty from bpy.utils import register_class, unregister_class @@ -35,6 +36,37 @@ def fix_marker_name(marker): marker.name = "F_" + str(marker.frame) +def copy_markers_to_clipboard(ctx, copy_all): + markers =[ + marker for marker in ctx.scene.timeline_markers + if marker.select or copy_all + ] + text = "BSMAXCOPYMARKERKEYWORD\n" + for marker in markers: + text += marker.name + ',' + text += str(marker.frame) + '\n' + + ctx.window_manager.clipboard = text + + +def paste_markers_from_clipboard(ctx): + keys = ctx.window_manager.clipboard.split('\n') + + if not keys: + return + + if keys[0] != "BSMAXCOPYMARKERKEYWORD": + return + + for key in keys[1:]: + parts = key.split(',') + if len(parts) < 2: + continue + name = parts[0] + frame = int(parts[1]) + ctx.scene.timeline_markers.new(name, frame=frame) + + class Marker_OT_Auto_Rename(Operator): bl_idname = 'marker.auto_rename' bl_label = "Auto Rename" @@ -51,18 +83,62 @@ def execute(self, ctx): return{'FINISHED'} +class Marker_OT_Copy(Operator): + bl_idname = 'marker.copy' + bl_label = "Marker Copy" + bl_description = "Copy Marker to other Scene or Blender" + bl_options = {'REGISTER', 'UNDO'} + + all: BoolProperty( + default=True, + description="Copy All Markers" + ) # type: ignore + + @classmethod + def poll(self, ctx): + return True + + def draw(self, ctx): + self.layout.prop( + self, 'all', text="Copy All Markers" + ) + + def execute(self, ctx): + copy_markers_to_clipboard(ctx, self.all) + return{'FINISHED'} + + +class Marker_OT_Paste(Operator): + bl_idname = 'marker.paste' + bl_label = "Marker Paste" + bl_description = "Paste Marker in other scene or blender" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, ctx): + return True + + def execute(self, ctx): + paste_markers_from_clipboard(ctx) + return{'FINISHED'} + + class Marker_OT_shift(Operator): bl_idname = 'marker.shiftm' def marker_rename_menu(self, _): layout = self.layout + layout.operator('marker.copy', text="Copy") + layout.operator('marker.paste', text="Paste") layout.separator() layout.operator('marker.auto_rename') classes = { - Marker_OT_Auto_Rename + Marker_OT_Auto_Rename, + Marker_OT_Copy, + Marker_OT_Paste } @@ -89,4 +165,6 @@ def unregister_marker(): if __name__ == '__main__': - register_marker() \ No newline at end of file + # register_marker() + for cls in classes: + register_class(cls) \ No newline at end of file diff --git a/tools/internal/nodes/copy_past.py b/tools/internal/nodes/copy_past.py new file mode 100644 index 0000000..dc42acd --- /dev/null +++ b/tools/internal/nodes/copy_past.py @@ -0,0 +1,152 @@ +############################################################################ +# 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.types import Operator, Menu +from bpy.utils import register_class, unregister_class + + +def get_active_node_editor_details(ctx): + area = ctx.area + if area.type == 'NODE_EDITOR': + space = area.spaces.active + space_data = ctx.space_data + + if space.tree_type == 'ShaderNodeTree': + if space_data.shader_type in {'OBJECT', 'WORLD', 'LINESTYLE'}: + return 'SHADER_' + space_data.shader_type + else: + return 'SHADER' + + elif space.tree_type == 'GeometryNodeTree': + if space_data.geometry_nodes_type in {'MODIFIER', 'TOOL'}: + return 'GEONODE_' + space_data.geometry_nodes_type + else: + return 'GEONODE' + + elif space.tree_type == 'CompositorNodeTree': + return 'COMPOSITOR' + + elif space.tree_type == 'TextureNodeTree': + if space_data.texture_type in {'WORLD', 'BRUSH', 'LINESTYLE'}: + return 'TEXTURE_' + space_data.texture_type + else: + return 'TEXTURE' + + return None + + +def node_to_create_script(index, node): + node_handler_name = "node" + str(index) + + script = " " + node_handler_name + " = node_tree.nodes.new(type=" + script += node.bl_idname + ")\n" + + script += " " + node_handler_name + ".location = " + str(node.location) + "\n" + + for input_socket in node.inputs: + if input_socket.is_linked or not hasattr(input_socket, 'default_value'): + continue + + input_value = str(input_socket.default_value) + script += " " + node_handler_name + script += ".inputs['" + input_socket.name + "'].default_value = " + script += input_value + "\n" + + return script + + +def convert_selected_nodes_to_script(node_tree): + selected_nodes = [node for node in node_tree.nodes if node.select] + selected_node_names = {node.name for node in selected_nodes} + + if not selected_nodes: + return '' + + script = "" + for index, node in enumerate(selected_nodes): + script += node_to_create_script(index, node) + "\n" + + for link in node_tree.links: + from_node = link.from_node + to_node = link.to_node + + # if (from_node.name in selected_node_names and to_node.name not in selected_node_names) or \ + # (from_node.name not in selected_node_names and to_node.name in selected_node_names): + + from_node_name = from_node.name.replace(' ', '_') + to_node_name = to_node.name.replace(' ', '_') + from_socket = link.from_socket.name + to_socket = link.to_socket.name + + script += " " + "node_tree.links.new(" + script += from_node_name + script += ".outputs['" + script += from_socket + script += "'], " + script += to_node_name + script += ".inputs['" + script += to_socket + script += "'])\n" + + return script + + +def get_script_header(current_editor): + script = "\n----------------------------------------------\n" + script += "import bpy\n" + script += "editor_type = '" + current_editor + "'\n" + script += "def recreate_nodes():\n" + script += " " + "node_tree = bpy.context.space_data.node_tree\n" + return script + + +def get_script_footer(): + return "# Script Footer" + + +class Nodes_OT_Copy_To_python(Operator): + bl_idname = 'nodes.copy_to_python' + bl_label = "copy selected nodes" + bl_description = "" + bl_options = {'REGISTER', 'UNDO'} + + # @classmethod + # def poll(self, ctx): + # if ctx.space_data.type == 'NODE_EDITOR': + # return ctx.space_data.shader_type == 'OBJECT' + # return False + + def execute(self, ctx): + if ctx.space_data.node_tree is None: + return + editor = get_active_node_editor_details(ctx) + script = get_script_header(editor) + script += convert_selected_nodes_to_script(ctx.space_data.node_tree) + script += get_script_footer() + + print(script) + return{'FINISHED'} + + +# def def register(): +# bpy.utils.register_module(__name__) + +# def unregister(): +# bpy.utils.unregister_module(__name__) + +if __name__ == '__main__': + register_class(Nodes_OT_Copy_To_python) diff --git a/tools/internal/object/modifier.py b/tools/internal/object/modifier.py index abcb73f..537c174 100644 --- a/tools/internal/object/modifier.py +++ b/tools/internal/object/modifier.py @@ -19,9 +19,8 @@ from bpy.props import EnumProperty - class Modifier_OT_Copy_Selected(Operator): - bl_idname = "modifier.copy_selected" + bl_idname = 'modifier.copy_selected' bl_label = "Copy Selected Modifiers" bl_options = {'REGISTER'} @@ -34,10 +33,10 @@ def get_modifiers(self, ctx): modifiers: EnumProperty( name='Modifiers', - description='List of modifiers to copy', + description="List of modifiers to copy", items=get_modifiers, - options={"ENUM_FLAG"} - ) + options={'ENUM_FLAG'} + ) # type: ignore def draw(self,ctx): layout = self.layout @@ -67,22 +66,19 @@ def copy_modifier(self, ctx, name): def execute(self, ctx): for name in self.modifiers: self.copy_modifier(ctx, name) - return{"FINISHED"} + return{'FINISHED'} - def invoke(self,ctx,event): + def invoke(self, ctx, event): return ctx.window_manager.invoke_props_dialog(self) - def register_modifier(): bpy.utils.register_class(Modifier_OT_Copy_Selected) - def unregister_modifier(): bpy.utils.unregister_class(Modifier_OT_Copy_Selected) - -if __name__ == "__main__": +if __name__ == '__main__': register_modifier() \ No newline at end of file diff --git a/tools/internal/render/preset.py b/tools/internal/render/preset.py index b15ded4..26ae7a6 100644 --- a/tools/internal/render/preset.py +++ b/tools/internal/render/preset.py @@ -12,84 +12,101 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/06/09 +# 2024/12/10 import bpy import os import subprocess import platform +import json -from bpy.types import Panel, Operator, PropertyGroup +from bpy.types import Panel, Operator, PropertyGroup, UIList from bpy.utils import register_class, unregister_class -from bpy.app.handlers import persistent from bpy.app import version from bpy.props import ( - PointerProperty, StringProperty, EnumProperty, BoolProperty + StringProperty, EnumProperty, BoolProperty, + CollectionProperty, IntProperty ) -from os import path, mkdir, access, W_OK +from os import path, mkdir from glob import glob -preset_kay = '""" BsMax Render Preset File """' +def get_datafiles_path(): + user_resource_path = bpy.utils.resource_path('USER') + datafiles_path = os.path.join(user_resource_path, "datafiles") + datafiles_path += os.sep + 'BsMax' + if not path.isdir(datafiles_path): + mkdir(datafiles_path) -def refine_value(value): - """ get value and return an executable pyton string """ - if type(value).__name__ == 'Color': - return (value[0], value[1], value[2]) + datafiles_path += os.sep + 'Render_presets' + if not path.isdir(datafiles_path): + mkdir(datafiles_path) - if type(value).__name__ == 'str': - return '"' + value + '"' + return datafiles_path - if type(value).__name__ in {'float', 'bool', 'int'}: - return value - return None +def write_dictionary_to_json_file(data, file_path): + try: + with open(file_path, 'w', encoding='utf-8') as json_file: + json.dump(data, json_file, ensure_ascii=False, indent=4) + return True + except: + return False + + +def read_json_file_to_dictionary(file_path): + try: + with open(file_path, 'r', encoding='utf-8') as json_file: + data = json.load(json_file) + return data + except: + return {} -# not a good method but for now is trusted +# not a good method but for now works fine def str_data_path(ctx, data_path): scene = ctx.scene if data_path == scene: - return 'bpy.context.scene.' + return 'scene.' if data_path == scene.render: - return 'bpy.context.scene.render.' + return 'scene.render.' if data_path == scene.cycles: - return 'bpy.context.scene.cycles.' + return 'scene.cycles.' if data_path == scene.eevee: - return 'bpy.context.scene.eevee.' + return 'scene.eevee.' if data_path == scene.world.light_settings: - return 'bpy.context.scene.world.light_settings.' + return 'scene.world.light_settings.' if data_path == scene.view_settings: - return 'bpy.context.scene.view_settings.' + return 'scene.view_settings.' if data_path == scene.display_settings: - return 'bpy.context.scene.display_settings.' + return 'scene.display_settings.' if data_path == scene.sequencer_colorspace_settings: - return 'bpy.context.scene.sequencer_colorspace_settings.' + return 'scene.sequencer_colorspace_settings.' if data_path == scene.grease_pencil_settings: - return 'bpy.context.scene.grease_pencil_settings.' + return 'scene.grease_pencil_settings.' if data_path == scene.cycles_curves: - return 'bpy.context.scene.cycles_curves.' + return 'scene.cycles_curves.' return '' -def pystr_attribute(ctx, data_path, attribute): - if not hasattr(data_path, attribute): - return "" - - script = str_data_path(ctx, data_path) + attribute + " = " - value = getattr(data_path, attribute) - script += '"' + value + '"' if type(value) == str else str(value) - script += '\n' - return script +def attribute_list_to_dictionery(ctx, data_path, attribute_list): + new_dictionary = {} + for attribute in attribute_list: + if not hasattr(data_path, attribute): + continue + data_path_name = str_data_path(ctx, data_path) + attribute + new_dictionary[data_path_name] = getattr(data_path, attribute) + + return new_dictionary -def get_manual_part(ctx, engin_name): +def get_manual_part_of_dict(ctx, engin_name): """ the mising parametes inside the bl_rna.properties adding manualy """ - script = "" + new_dictionary = {} if engin_name == 'RENDER': attributes = [ @@ -110,8 +127,9 @@ def get_manual_part(ctx, engin_name): ] render = ctx.scene.render - for attribute in attributes: - script += pystr_attribute(ctx, render, attribute) + new_dictionary.update( + attribute_list_to_dictionery(ctx, render, attributes) + ) if engin_name == 'EEVEE': attributes = [ @@ -130,8 +148,9 @@ def get_manual_part(ctx, engin_name): ] eevee = ctx.scene.eevee - for attribute in attributes: - script += pystr_attribute(ctx, eevee, attribute) + new_dictionary.update( + attribute_list_to_dictionery(ctx, eevee, attributes) + ) if engin_name == 'EEVEENEXT': # TODO check this list again after 4.2LTS releasee @@ -155,8 +174,9 @@ def get_manual_part(ctx, engin_name): ] eevee = ctx.scene.eevee - for attribute in attributes: - script += pystr_attribute(ctx, eevee, attribute) + new_dictionary.update( + attribute_list_to_dictionery(ctx, eevee, attributes) + ) if engin_name == 'CYCLES': attributes = [ @@ -183,8 +203,9 @@ def get_manual_part(ctx, engin_name): ] cycles = ctx.scene.cycles - for attribute in attributes: - script += pystr_attribute(ctx, cycles, attribute) + new_dictionary.update( + attribute_list_to_dictionery(ctx, cycles, attributes) + ) if engin_name == 'SCENE': scene = ctx.scene @@ -204,36 +225,29 @@ def get_manual_part(ctx, engin_name): ] for data_path, attribute in attributes: - script += pystr_attribute(ctx, data_path, attribute) + new_dictionary.update( + attribute_list_to_dictionery(ctx, data_path, [attribute]) + ) - return script + return new_dictionary -def get_preset_path(): - preset_path = bpy.utils.user_resource('CONFIG') + '\\BsMax\\' - if not path.isdir(preset_path): - mkdir(preset_path) - - preset_path += 'render_presets\\' - if not path.isdir(preset_path): - mkdir(preset_path) - - return preset_path - - -def get_script(engin, engin_name): +def get_dictionary(ctx, engin, engin_name): """ * get render engin and render engin name - * collect all change able attributes - * convert the attribute and data to line of python code - * e.g. bpy.context.scene.eevee.motion_blur_max = 32 + * collect all editable attributes + * return the list as a dictionary """ + ignore_list = { + 'bl_rna', 'rna_type', 'filepath', '', + 'stamp_background', 'stamp_foreground', 'stamp_note_text' + } + + dictionary = {} - illegals = {'bl_rna', 'rna_type', 'filepath', ''} - script = "" for name in dir(engin): """ filter none relateds """ - if name in illegals: + if name in ignore_list: continue if (name.startswith('__') and name.endswith('__')) : @@ -248,60 +262,45 @@ def get_script(engin, engin_name): continue value = getattr(engin, name) - value = refine_value(value) if value: - script += 'bpy.context.scene.' - script += engin_name + '.' + name - script += ' = ' + str(value) + '\n' + key = 'scene.' + engin_name + '.' + name + dictionary[key] = value - return script + return dictionary -def create_preset_script(ctx): - global preset_kay - script = preset_kay + '\n' - script += 'import bpy \n' - script += get_script(ctx.scene.render, 'render') - script += get_manual_part(ctx, 'RENDER') +def create_preset_dictionary(ctx): + dictionary = {} + + dictionary.update(get_dictionary(ctx, ctx.scene.render, 'render')) + dictionary.update(get_manual_part_of_dict(ctx, 'RENDER')) engine = ctx.scene.render.engine if engine == 'BLENDER_EEVEE': - script += get_script(ctx.scene.eevee, 'eevee') - script += get_manual_part(ctx, 'EEVEE') - + dictionary.update(get_dictionary(ctx, ctx.scene.eevee, 'eevee')) + dictionary.update(get_manual_part_of_dict(ctx, 'EEVEE')) + if engine == 'BLENDER_EEVEE_NEXT': - script += get_script(ctx.scene.eevee, 'eevee') - script += get_manual_part(ctx, 'EEVEENEXT') + dictionary.update(get_dictionary(ctx, ctx.scene.eevee, 'eevee')) + dictionary.update(get_manual_part_of_dict(ctx, 'EEVEENEXT')) elif engine == 'CYCLES': - script += get_script(ctx.scene.cycles, 'cycles') - script += get_manual_part(ctx, 'CYCLES') - - elif engine == 'BLENDER_WORKBENCH': - script += get_script(ctx.scene, 'scene') - - script += get_manual_part(ctx, 'SCENE') - - #TODO other render engined has to add here - return script + dictionary.update(get_dictionary(ctx, ctx.scene.cycles, 'cycles')) + dictionary.update(get_manual_part_of_dict(ctx, 'CYCLES')) + elif engine == 'BLENDER_WORKBENCH': + dictionary.update(get_dictionary(ctx, ctx.scene, 'scene')) -def is_script_preset(script): - global preset_kay - if type(script).__name__ == 'str': - lines = script.split('\n') - if len(lines) == 0: - return False - return lines[0] == preset_kay - else: - return False + dictionary.update(get_manual_part_of_dict(ctx, 'SCENE')) + #TODO other render engined has to add here + # Octane + # Redshift + # Vray -def execute_is_python(script): - if is_script_preset(script): - exec(script) + return dictionary def open_folder_in_explorer(path): @@ -318,30 +317,77 @@ def open_folder_in_explorer(path): subprocess.call(["xdg-open", path]) -def save_file(presetName, string): - presetPath = get_preset_path() - if not path.exists(presetPath): - if access(presetPath, W_OK): - mkdir(presetPath) +def get_list_of_presets(): + preset_path = get_datafiles_path() + return [ + path.splitext(path.basename(f))[0] + for f in glob(preset_path+"/*.json") + ] + - fileName = presetPath + presetName + '.py' - presetFile = open(fileName, "w") - presetFile.write(string) - presetFile.close() +def render_preset_reload(ctx): + ctx.scene.render_presets.clear() + for name in get_list_of_presets(): + ps = ctx.scene.render_presets.add() + ps.name = name -def get_presets_list(): - preset_path = get_preset_path() - preset_list = [ - path.splitext(path.basename(f))[0] - for f in glob(preset_path+"/*.py") - ] - return [(pl, pl, "") for pl in preset_list] +def render_preset_add(ctx): + name = ctx.scene.render_preset_name + file_name = get_datafiles_path() + os.sep + name + ".json" + + if os.path.exists(file_name): + pass + #TODO check is name avalible warn or use auto digit + + write_dictionary_to_json_file( + create_preset_dictionary(ctx), file_name + ) + + render_preset_reload(ctx) + +def render_preset_remove(ctx): + index = ctx.scene.render_presets_index + name = ctx.scene.render_presets[index].name + file_name = get_datafiles_path() + os.sep + name + ".json" + #TODO ask for permition + os.remove(file_name) + render_preset_reload(ctx) -def read_preset_file(presetName): - fileName = get_preset_path() + presetName + '.py' - return open(fileName).read() + +def render_preset_name_update(ctx): + index = ctx.scene.render_presets_index + new_name = ctx.scene.render_presets[index].name + old_name = ctx.scene.render_preset_name + file_path = get_datafiles_path() + os.sep + + os.rename( + file_path + new_name + ".json", + file_path + old_name + ".json" + ) + + render_preset_reload(ctx) + + +def render_preset_load(ctx): + index = ctx.scene.render_presets_index + name = ctx.scene.render_presets[index].name + file_name = get_datafiles_path() + os.sep + name + ".json" + dictionary = read_json_file_to_dictionary(file_name) + scope = {'ctx': ctx} + for key, value in dictionary.items(): + expression = 'ctx.' + key + try: + exec(f"{expression} = {value}", {"__builtins__": None}, scope) + except: + pass + + +class Render_Preset(PropertyGroup): + name: StringProperty( + name="Preset Name", default="New Preset" + ) # type: ignore class Render_OT_Preset(Operator): @@ -350,58 +396,50 @@ class Render_OT_Preset(Operator): bl_description = "Save/Load or Copy/Past render settings" bl_options = {'REGISTER', 'INTERNAL'} - def get_presets(self, _): - return get_presets_list() - - name: StringProperty( - name='Preset Name', default='New Preset' - ) # type: ignore - - names: EnumProperty( - name='Preset Name', items=get_presets - ) # type: ignore - action: EnumProperty( items=[ - ('COPY', 'Copy', 'Copy Render Setting to Clipboard'), - ('PASTE', 'Paste', 'Paste Render Setting from Clipboard'), - ('SAVE', 'Save', 'Save Render Setting to File'), + ('RELOAD', 'Reload', "Save Render Setting to File"), + ('ADD', 'Add', "Save Render Setting to File"), + ('REMOVE', 'Remove', "Remove Active Preset"), + ('UPDATE', 'Update', "Update active preset"), ('LOAD', 'Load', 'Load Rendert Setting from File'), ('REVEAL', 'Reveal', 'Reveal Render Presets Folder') ], - default='SAVE' + default='RELOAD' ) # type: ignore - def draw(self, ctx): - layout = self.layout - if self.action == 'SAVE': - layout.prop(self, 'name') - elif self.action == 'LOAD': - layout.prop(self, 'names') - def execute(self, ctx): - if self.action == 'COPY': - ctx.window_manager.clipboard = create_preset_script(ctx) + if self.action == 'RELOAD': + render_preset_reload(ctx) + + elif self.action == 'ADD': + render_preset_add(ctx) - elif self.action == 'PASTE': - execute_is_python(ctx.window_manager.clipboard) + elif self.action == 'REMOVE': + render_preset_remove(ctx) - elif self.action == 'SAVE': - save_file(self.name, create_preset_script(ctx)) + elif self.action == 'UPDATE': + render_preset_name_update(ctx) elif self.action == 'LOAD': - exec(read_preset_file(self.names)) + render_preset_load(ctx) elif self.action == 'REVEAL': - open_folder_in_explorer(get_preset_path()) + open_folder_in_explorer(get_datafiles_path()) - return{"FINISHED"} - - def invoke(self, ctx, event): - if self.action in ('SAVE', 'LOAD'): - return ctx.window_manager.invoke_props_dialog(self) - else: - return self.execute(ctx) + return{'FINISHED'} + + +class RENDER_UI_Preset(UIList): + def draw_item(self, ctx, layout, data, item, icon, + active_data, active_property, index = 0, flt_flag = 0): + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.label(text=item.name) + + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.prop(item, "name", text="", emboss=False) class RENDER_PT_Preset(Panel): @@ -413,27 +451,49 @@ class RENDER_PT_Preset(Panel): def draw(self, ctx): layout = self.layout - row = layout.row() - row.operator( - 'render.preset', text='Save', icon='ADD' - ).action='SAVE' - - row.operator( - 'render.preset', text='Load', icon='FILE_TICK' - ).action='LOAD' - - row.operator( - 'render.preset', text='', icon='COPYDOWN' - ).action='COPY' - - row.operator( - 'render.preset', text='', icon='PASTEDOWN' - ).action='PASTE' + scene = ctx.scene - row.operator( + row = layout.row() + col = row.column() + sub_col = col.column(align=True) + sub_col.template_list( + "RENDER_UI_Preset", "", + scene, "render_presets", + scene, "render_presets_index" + ) + sub_col.prop(ctx.scene, 'render_preset_name') + + col = row.column() + + sub_col = col.column(align=True) + + sub_col.operator( + 'render.preset', text='', icon='FILE_REFRESH' + ).action='RELOAD' + + sub_col = col.column(align=True) + sub_col.operator( + 'render.preset', text='', icon='ADD' + ).action='ADD' + + sub_col.operator( + 'render.preset', text='', icon='REMOVE' + ).action='REMOVE' + + sub_col.operator( + 'render.preset', text='', icon='GREASEPENCIL' + ).action='UPDATE' + + sub_col = col.column() + sub_col.operator( 'render.preset', text='', icon='FILEBROWSER' ).action='REVEAL' + sub_col = col.column() + sub_col.operator( + 'render.preset', text='', icon='PLAY' + ).action='LOAD' + class Render_PrePostScript(PropertyGroup): pre_render_active: BoolProperty( @@ -459,55 +519,11 @@ class Render_PrePostScript(PropertyGroup): ) # type: ignore -class RENDER_PT_Script(Panel): - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = 'render' - bl_label = "Script" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, ctx): - layout = self.layout - rpps = ctx.scene.render_prepost_script - - row = layout.row() - row.prop(rpps, 'pre_render_active', text="", icon='PLAY') - row.prop(rpps, 'pre_render_script') - - row = layout.row() - row.prop(rpps, 'post_render_active', text="", icon='PLAY') - row.prop(rpps, 'post_render_script') - - -@persistent -def pre_render(scene): - if scene.render_prepost_script.pre_render_active: - script_name = scene.render_prepost_script.pre_render_script - if script_name in bpy.data.texts: - script = bpy.data.texts[script_name].as_string() - try: - exec(script) - except: - pass - - -@persistent -def post_render(scene): - if scene.render_prepost_script.post_render_active: - script_name = scene.render_prepost_script.post_render_script - if script_name in bpy.data.texts: - script = bpy.data.texts[script_name].as_string() - try: - exec(script) - except: - pass - - classes = { - Render_PrePostScript, + Render_Preset, + RENDER_UI_Preset, Render_OT_Preset, - RENDER_PT_Preset, - RENDER_PT_Script + RENDER_PT_Preset } @@ -515,22 +531,16 @@ def register_preset(): for cls in classes: register_class(cls) - bpy.types.Scene.render_prepost_script = PointerProperty( - type=Render_PrePostScript, - name="Pre/Post Render Script" - ) - - bpy.app.handlers.render_init.append(pre_render) - bpy.app.handlers.render_complete.append(post_render) + Scene = bpy.types.Scene + Scene.render_presets = CollectionProperty(type=Render_Preset) + Scene.render_presets_index = IntProperty(name="Index", default=0) + Scene.render_preset_name = StringProperty(name="", default="New Preset") def unregister_preset(): for cls in classes: unregister_class(cls) - bpy.app.handlers.render_init.remove(pre_render) - bpy.app.handlers.render_complete.remove(post_render) - if __name__ == '__main__': register_preset() \ No newline at end of file diff --git a/tools/internal/rigg/joystick.py b/tools/internal/rigg/joystick.py index 2d9963b..3156b3d 100644 --- a/tools/internal/rigg/joystick.py +++ b/tools/internal/rigg/joystick.py @@ -12,12 +12,16 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +#2024/09/11 + +#TODO need to mode simplify and cleanup import bpy from bpy.types import Operator from bpy.props import IntProperty, EnumProperty from mathutils import Vector +from bpy.utils import register_class, unregister_class from bsmax.state import get_obj_class @@ -26,7 +30,6 @@ ) - def get_joystic_class(obj): obj_class = get_obj_class(obj) if obj_class == 'Rectangle': @@ -44,23 +47,24 @@ def get_joystic_class(obj): if obj_class == 'Circle': return 'CIRCLE_JOYSTICK' - return "" - + return '' def get_handle_radius(obj, joy_type): width = obj.data.primitivedata.width length = obj.data.primitivedata.length radius = obj.data.primitivedata.radius1 + if joy_type == 'RECTANGLE_JOYSTICK': return (width + length) / 15.0 + elif joy_type in {'VERTICAL_SLIDER', 'HORIZONTAL_SLIDER'}: return (min(width, length) / 2) + elif joy_type == 'CIRCLE_JOYSTICK': return radius / 5 - def create_rectangle_frame_Mesh(ctx, mode, rectangle): width = round(rectangle.data.primitivedata.width, 3) @@ -75,17 +79,17 @@ def create_rectangle_frame_Mesh(ctx, mode, rectangle): rectangle.name = name - """ fix orient """ + # fix orient bpy.ops.transform.rotate( value=-1.5708, orient_axis='X', orient_type='LOCAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='LOCAL', - constraint_axis=(True, False, False), + constraint_axis=(True, False, False) ) - """ convert (curve) frame to mesh """ + # convert (curve) frame to mesh bpy.ops.object.mode_set(mode='OBJECT', toggle=False) bpy.ops.object.select_all(action='DESELECT') rectangle.select_set(state=True) @@ -97,7 +101,6 @@ def create_rectangle_frame_Mesh(ctx, mode, rectangle): return rectangle - def catch_circilar_frame(ctx): """ Find or Create a mesh for joystic Frame bone args: @@ -115,8 +118,8 @@ def catch_circilar_frame(ctx): joy_mesh.name = name collection = catche_collection(ctx, "Joystick_Meshes") move_to_collection(joy_mesh, collection) - return joy_mesh + return joy_mesh def catche_joy_mesh(ctx): @@ -147,11 +150,10 @@ def catche_joy_mesh(ctx): return joy_mesh +def create_armature( + ctx, name, frame_mesh, joy_mesh, frame_radius, joy_radius): -def create_armature(ctx, name, frame_mesh, joy_mesh, - frame_radius, joy_radius): - """ Create armature - """ + # Create armature bpy.ops.object.armature_add(location=(0, 0, 0)) joystick = ctx.active_object joystick.name = name @@ -188,16 +190,14 @@ def create_armature(ctx, name, frame_mesh, joy_mesh, return joystick - def match_transform(source, target): - """ Set Transform """ + # Set Transform source.scale = target.scale source.rotation_euler.x = target.rotation_euler.x source.rotation_euler.y = target.rotation_euler.y source.rotation_euler.z = target.rotation_euler.z - def create_circle_joystick(ctx, circle, joy_type): global joystick_data matrix_world = circle.matrix_world.copy() @@ -221,7 +221,7 @@ def create_circle_joystick(ctx, circle, joy_type): match_transform(joystick, circle) - """ setup constraints """ + # setup constraints bpy.ops.object.mode_set(mode='POSE', toggle=False) distance = frame_radius - joy_radius @@ -248,7 +248,6 @@ def create_circle_joystick(ctx, circle, joy_type): bpy.ops.object.mode_set(mode='OBJECT', toggle=False) - def create_rectangle_joystick(ctx, rectangle, joy_type): global joystick_data mode = joystick_data.mode @@ -260,14 +259,19 @@ def create_rectangle_joystick(ctx, rectangle, joy_type): if mode in [1,4,7]: x_offset = width / 2 - radius + elif mode in [3,6,9]: x_offset = -(width / 2 - radius) + else: x_offset = 0 + if mode in [1,2,3]: y_offset = -(length / 2 - radius) + elif mode in [7,8,9]: y_offset = length / 2 - radius + else: y_offset = 0 @@ -286,7 +290,7 @@ def create_rectangle_joystick(ctx, rectangle, joy_type): if mode == 10: return - """ setup constraints """ + # setup constraints bpy.ops.object.mode_set(mode='POSE', toggle=False) cons = joystick.pose.bones['Joy'].constraints.new('LIMIT_LOCATION') @@ -300,6 +304,7 @@ def create_rectangle_joystick(ctx, rectangle, joy_type): if joy_type in ['RECTANGLE_JOYSTICK','HORIZONTAL_SLIDER']: cons.min_x = -(width / 2 - radius) + x_offset cons.max_x = width / 2 - radius + x_offset + if joy_type in ['RECTANGLE_JOYSTICK','VERTICAL_SLIDER']: cons.min_y = -(length / 2 - radius) + y_offset cons.max_y = length / 2 - radius + y_offset @@ -307,7 +312,6 @@ def create_rectangle_joystick(ctx, rectangle, joy_type): bpy.ops.object.mode_set(mode='OBJECT', toggle=False) - class Joystick_Creator_Data: def __init__(self): # pass value from an operator to silent run @@ -382,14 +386,13 @@ def get_arrow_panel(op, self): col.label(text=text) - class Rigg_TO_Joystick_Creator(Operator): bl_idname = 'rigg.joystick_creator' - bl_label = 'Joystick Creator' - bl_description = 'Convert Selected Rectangle or Circle to Joystick' + bl_label = "Joystick Creator" + bl_description = "Convert Selected Rectangle or Circle to Joystick" bl_options = {'REGISTER', 'UNDO'} - mode: IntProperty() + mode: IntProperty() # type: ignore type = '' @classmethod @@ -424,9 +427,8 @@ def invoke(self, ctx, event): elif self.mode > 0: global joystick_data joystick_data.mode = self.mode - return {'CANCELLED'} - + return {'CANCELLED'} class Rigg_TO_Joystick_Shapekey_Connector(Operator): @@ -434,8 +436,8 @@ class Rigg_TO_Joystick_Shapekey_Connector(Operator): Shape keys and run this operator """ bl_idname = 'rigg.joystick_shapekey_connector' - bl_label = 'Joystick Connecotr' - bl_description = 'Connect Joystick to Shapekey' + bl_label = "Joystick Connecotr" + bl_description = "Connect Joystick to Shapekey" bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -474,7 +476,7 @@ def coolect_joys(self, ctx): else: return [('None', 'None', '')] - joystick: EnumProperty(items=coolect_joys) + joystick: EnumProperty(items=coolect_joys) # type: ignore items = [] def collect_shapekeys(self, ctx): @@ -483,20 +485,25 @@ def collect_shapekeys(self, ctx): if obj.type in {'MESH', 'CURVE'}: shell = obj items = [('None', '', '')] + if shell != None: - names = [n.name for n in shell.data.shape_keys.key_blocks - if n.name != 'Basis'] + names = [ + n.name for n in shell.data.shape_keys.key_blocks + if n.name != 'Basis' + ] + items += [(n, n, '') for n in names] + return items - up: EnumProperty(items=collect_shapekeys) - upright: EnumProperty(items=collect_shapekeys) - right: EnumProperty(items=collect_shapekeys) - downright: EnumProperty(items=collect_shapekeys) - down: EnumProperty(items=collect_shapekeys) - downleft: EnumProperty(items=collect_shapekeys) - left: EnumProperty(items=collect_shapekeys) - upleft: EnumProperty(items=collect_shapekeys) + up: EnumProperty(items=collect_shapekeys) # type: ignore + upright: EnumProperty(items=collect_shapekeys) # type: ignore + right: EnumProperty(items=collect_shapekeys) # type: ignore + downright: EnumProperty(items=collect_shapekeys) # type: ignore + down: EnumProperty(items=collect_shapekeys) # type: ignore + downleft: EnumProperty(items=collect_shapekeys) # type: ignore + left: EnumProperty(items=collect_shapekeys) # type: ignore + upleft: EnumProperty(items=collect_shapekeys) # type: ignore def get_expration(self, direction, joy): # rectangular joystic @@ -650,7 +657,6 @@ def invoke(self, ctx, event): return ctx.window_manager.invoke_props_dialog(self, width=400) - def joystick_connectore_menu(self, ctx): layout = self.layout layout.separator() @@ -659,26 +665,22 @@ def joystick_connectore_menu(self, ctx): # layout.operator('modifier.copy_selected') - -classes = ( +classes = { Rigg_TO_Joystick_Creator, Rigg_TO_Joystick_Shapekey_Connector -) - +} def register_joystic(): - for c in classes: - bpy.utils.register_class(c) + for cls in classes: + register_class(cls) bpy.types.VIEW3D_MT_make_links.append(joystick_connectore_menu) - def unregister_joystic(): bpy.types.VIEW3D_MT_make_links.remove(joystick_connectore_menu) - for c in classes: - bpy.utils.unregister_class(c) - + for cls in classes: + unregister_class(cls) if __name__ == '__main__': diff --git a/tools/internal/rigg/naming.py b/tools/internal/rigg/naming.py index a7a2aad..96f7cf9 100644 --- a/tools/internal/rigg/naming.py +++ b/tools/internal/rigg/naming.py @@ -12,20 +12,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +#2024/09/11 import bpy from bpy.types import Operator - def is_blender_name(name): if len(name) < 2: return False return name[-2] == '.' and name[-1].lower() in ('r', 'l') - def is_daz3d_name(name): if len(name) < 2: return False @@ -38,22 +37,18 @@ def is_daz3d_name(name): return True - def is_unnamed(name): return False - def get_blender_bone_name_direction(name): return name[-1] - def get_blender_bone_name_base(name): return name[:-2] - def get_daz3d_bone_name_direction(name): if name[0] in ('r', 'l') and name[-2] != '.': return name[0] @@ -68,7 +63,6 @@ def get_daz3d_bone_name_direction(name): return "" - def get_das3d_bone_name_base(name): if name[0] in ('r', 'l') and name[-2] != '.': return name[1:] @@ -84,7 +78,6 @@ def get_das3d_bone_name_base(name): return "" - class BoneName: def __init__(self, name): self.name = name @@ -113,10 +106,10 @@ def get_blender_name(self): return self.name - class Armature_TO_auto_side_rename(Operator): bl_idname = 'armature.auto_direction_rename' - bl_label = 'Armature Auto Rename' + bl_label = "Armature Auto Rename" + bl_description = "" bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -127,19 +120,16 @@ def execute(self, ctx): for bone in ctx.object.data.bones: boneName = BoneName(name=bone.name) bone.name = boneName.get_blender_name() - return{"FINISHED"} - + return{'FINISHED'} def register_naming(): bpy.utils.register_class(Armature_TO_auto_side_rename) - def unregister_naming(): bpy.utils.unregister_class(Armature_TO_auto_side_rename) - -if __name__ == "__main__": +if __name__ == '__main__': register_naming() \ No newline at end of file diff --git a/tools/internal/rigg/shapekey.py b/tools/internal/rigg/shapekey.py index 3d35b21..0144e3f 100644 --- a/tools/internal/rigg/shapekey.py +++ b/tools/internal/rigg/shapekey.py @@ -12,26 +12,26 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ - +# 2024/09/10 """ This file can be instaled as an stand alone add-on too """ bl_info = { "name": "BsMax-Shapekey", - "description": "Drive multiple Shapekey (Blender 2.93LTS ~ 3.6LTS)", + "description": "Drive multiple Shapekey (Blender 2.93LTS ~ 4.2LTS)", "author": "Matt Ebb | Blaize | Anthony Hunt | Spirou4D | Nevil", "version": (0, 1, 0, 2),# 2023-06-11 "blender": (2, 93, 0),# to 3.6 - "location": "Properties/ Output/ Backbrner", + "location": "Properties/ Data/ Shape Key", "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", "category": "Render" } - import bpy from bpy.types import Operator +from bpy.utils import register_class, unregister_class # a-----------v----------b----------v------c # p = (v-a)/(b-a) if v < b else (c-v)/(c-b) #zerout @@ -51,11 +51,10 @@ def sort(self): self.values.sort() - class Mesh_TO_Shapekeys_Sort_by_name(Operator): - """ Sort shape key by name order """ - bl_idname = "mesh.shapekeys_sort_by_name" + bl_idname = 'mesh.shapekeys_sort_by_name' bl_label = "Sort By Name" + bl_description = "Sort shape key by name order" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} @classmethod @@ -83,18 +82,18 @@ def execute(self, ctx): step_count = current_index - index -1 ctx.object.active_shape_key_index = current_index for i in range(step_count): - bpy.ops.object.shape_key_move(type="UP") + bpy.ops.object.shape_key_move(type='UP') - return{"FINISHED"} + return{'FINISHED'} def has_integer_sufix(string, key): - """ get underline position """ + # get underline position index = string.rfind(key) - """ seprate name from sufix """ + # seprate name from sufix """ integer = string[index + 1:] name = string[:index] - """ check if sufix integer and in range 0~100 """ + # check if sufix integer and in range 0~100 if integer.isdigit(): val = int(integer) @@ -104,14 +103,12 @@ def has_integer_sufix(string, key): return None - def remove_shapekey_by_name(obj, shapekey_name): key_blocks = obj.data.shape_keys.key_blocks obj.active_shape_key_index = key_blocks.keys().index(shapekey_name) bpy.ops.object.shape_key_remove() - def get_shapekeys(ctx): allowedShapekeys = [] for n in ctx.object.data.shape_keys.key_blocks: @@ -126,7 +123,6 @@ def get_shapekeys(ctx): return allowedShapekeys - def get_groups_by(shapekeys, key): multi_shapekeys = [] for n in shapekeys: @@ -151,38 +147,46 @@ def devide_numeric_shapekeys_to_sub_groups(shapekeys, seprator): return groups - def create_multi_shapekey_driver(ctx): - """ Collect names whit underline """ + # Collect names whit underline shapekeys = get_shapekeys(ctx) - """ Groupe the shape keys """ - groups = devide_numeric_shapekeys_to_sub_groups(get_groups_by(shapekeys, '_'), '_') - groups += devide_numeric_shapekeys_to_sub_groups(get_groups_by(shapekeys, '%'), '%') - groups += devide_numeric_shapekeys_to_sub_groups(get_groups_by(shapekeys, '+'), '+') + # Groupe the shape keys + groups = devide_numeric_shapekeys_to_sub_groups( + get_groups_by(shapekeys, '_'), '_' + ) + + groups += devide_numeric_shapekeys_to_sub_groups( + get_groups_by(shapekeys, '%'), '%' + ) + + groups += devide_numeric_shapekeys_to_sub_groups( + get_groups_by(shapekeys, '+'), '+' + ) - """ remove groups with single value """ + # remove groups with single value for group in groups: if len(group.values) < 2: groups.remove(group) - """ sort all group values """ + # sort all group values for group in groups: group.sort() - """ setup drivers """ + # setup drivers shell = ctx.object - names = [n.name for n in shell.data.shape_keys.key_blocks - if n.name != 'Basis'] + names = [ + n.name for n in shell.data.shape_keys.key_blocks if n.name != 'Basis' + ] for group in groups: - """ Create empty shapekey for driving """ + # Create empty shapekey for driving if not group.name in names: shell.shape_key_add(name=group.name, from_mix=False) for index, val in enumerate(group.values): - """ Set up driver to shape keys """ + # Set up driver to shape keys shape_key = group.name + group.seprator + str(val) key_block = shell.data.shape_keys.key_blocks[shape_key] key_block.driver_remove('value') @@ -197,7 +201,7 @@ def create_multi_shapekey_driver(ctx): target.id = shell.data.shape_keys target.data_path = 'key_blocks["' + group.name + '"].value' - """ Create driver script """ + ### Create driver script ### # previes shapekey start value start = 0 if index == 0 else float(group.values[index-1]) / 100.0 # current shapkey satrt value @@ -249,7 +253,7 @@ class Mesh_TO_Create_Multi_Target_Shapekeys(Operator): target_75\n The digit are percentage of the each target complet on """ - bl_idname = "mesh.create_multi_target_shapekeys" + bl_idname = 'mesh.create_multi_target_shapekeys' bl_label = "Create Multi Target Shapekeys" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} @@ -259,8 +263,7 @@ def poll(self, ctx): def execute(self, ctx): create_multi_shapekey_driver(ctx) - return{"FINISHED"} - + return{'FINISHED'} def shapekey_tools(self, ctx): @@ -272,37 +275,33 @@ def shapekey_tools(self, ctx): row.operator('mesh.shapekeys_sort_by_name') - -classes = ( +classes = { Mesh_TO_Shapekeys_Sort_by_name, Mesh_TO_Create_Multi_Target_Shapekeys -) - +} def register_shapekey(): - for c in classes: - bpy.utils.register_class(c) - bpy.types.DATA_PT_shape_keys.append(shapekey_tools) + for cls in classes: + register_class(cls) + bpy.types.DATA_PT_shape_keys.append(shapekey_tools) def unregister_shapekey(): bpy.types.DATA_PT_shape_keys.remove(shapekey_tools) - for c in classes: - bpy.utils.unregister_class(c) + for cls in classes: + unregister_class(cls) def register(): register_shapekey() - def unregister(): unregister_shapekey() - -if __name__ == "__main__": +if __name__ == '__main__': register_shapekey() \ No newline at end of file diff --git a/tools/internal/rigg/wire_parameter.py b/tools/internal/rigg/wire_parameter.py index 79e87e8..f07ebb0 100644 --- a/tools/internal/rigg/wire_parameter.py +++ b/tools/internal/rigg/wire_parameter.py @@ -12,6 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/09/10 import bpy @@ -19,7 +20,6 @@ from bpy.props import EnumProperty, FloatProperty - def create_obj_driver(obj, chanel, subchanel, start, end): driver = obj.data.dof.driver_add('aperture_fstop') bpy.data.driver_add('aperture_fstop') @@ -44,36 +44,34 @@ def create_obj_driver(obj, chanel, subchanel, start, end): return "" - def create_bone_driver(bone, chanel, subchanel, start, end): return "" - class Rigg_TO_Wire_Parameter(Operator): bl_idname = 'rigg.wire_parameter' - bl_label = 'Wire Parameter' - bl_description = '' + bl_label = "Wire Parameter" + bl_description = "" bl_options = {'REGISTER', 'UNDO'} chanel: EnumProperty( items=[ - ("LOCATION", "Location", ""), - ("ROTATION", "Rotation", ""), - ("SCALE", "Scale", "") + ('LOCATION', "Location", ""), + ('ROTATION', "Rotation", ""), + ('SCALE', "Scale", "") ] - ) + ) # type: ignore axis: EnumProperty( items=[ - ("X", "X", ""), - ("Y", "Y", ""), - ("Z", "Z", "") + ('X', "X", ""), + ('Y', "Y", ""), + ('Z', "Z", "") ] - ) + ) # type: ignore - start: FloatProperty() - end: FloatProperty() + start: FloatProperty() # type: ignore + end: FloatProperty() # type: ignore @classmethod def poll(self, ctx): @@ -102,16 +100,13 @@ def invoke(self, ctx, event): return ctx.window_manager.invoke_props_dialog(self) - def register_wire_parameter(): bpy.utils.register_class(Rigg_TO_Wire_Parameter) - def unregister_wire_parameter(): bpy.utils.unregister_class(Rigg_TO_Wire_Parameter) - if __name__ == '__main__': register_wire_parameter() diff --git a/tools/internal/scene/crowds.py b/tools/internal/scene/crowds.py index 22f2534..aaa13ef 100644 --- a/tools/internal/scene/crowds.py +++ b/tools/internal/scene/crowds.py @@ -462,7 +462,7 @@ def draw(self, ctx): def execute(self, ctx): random_and_loop_refrenses(self, ctx) - return{"FINISHED"} + return{'FINISHED'} def invoke(self, ctx, event): return ctx.window_manager.invoke_props_dialog(self) @@ -505,7 +505,7 @@ def draw(self, ctx): def execute(self, ctx): stuff_variation(self, ctx) - return{"FINISHED"} + return{'FINISHED'} def invoke(self, ctx, event): return ctx.window_manager.invoke_props_dialog(self) @@ -513,7 +513,8 @@ def invoke(self, ctx, event): class BsMax_MT_Crowds_Tools(Menu): bl_idname = 'BSMAX_MT_crowdstools' - bl_label = 'Crowds' + bl_label = "Crowds" + bl_description = "" # bl_context = 'objectmode' @classmethod @@ -525,19 +526,19 @@ def draw(self, ctx): icon='LIGHTPROBE_GRID' if version < (4, 1, 0) else 'LIGHTPROBE_VOLUME' layout.operator( 'crowds.clone_refrenses', - text='Clone Instanses', + text="Clone Instanses", icon=icon ) layout.operator( 'crowds.loop_refrenses', - text='Loop Instanses', + text="Loop Instanses", icon='ORIENTATION_GIMBAL' ) layout.operator( 'crowds.stuff_variation', - text='Variate Instanses', + text="Variate Instanses", icon='GEOMETRY_NODES' ) diff --git a/tools/internal/scene/file.py b/tools/internal/scene/file.py index 57c9332..fe959c6 100644 --- a/tools/internal/scene/file.py +++ b/tools/internal/scene/file.py @@ -22,9 +22,10 @@ class File_OT_Scale_Icons(Operator): - bl_idname = "filebrowser.scaleicons" + bl_idname = 'filebrowser.scaleicons' bl_label = "Scale Icons" - up: bpy.props.BoolProperty(name="scaleup", default=True) + bl_description = "" + up: bpy.props.BoolProperty(name="scaleup", default=True) # type: ignore @classmethod def poll(self, ctx): @@ -35,6 +36,7 @@ def execute(self, ctx): small = 'LIST_VERTICAL' medium = 'LIST_HORIZONTAL' large = 'THUMBNAIL' + if self.up: if params.display_type == large: params.display_type = small @@ -51,12 +53,13 @@ def execute(self, ctx): elif params.display_type == small: params.display_type = large - return{"FINISHED"} + return{'FINISHED'} class File_OT_Version(Operator): - bl_idname = "file.version" + bl_idname = 'file.version' bl_label = "Version" + bl_description = "" @classmethod def poll(self, ctx): @@ -67,7 +70,7 @@ def draw(self, ctx): self.layout.label(text="App: " + self.app_version) def execute(self, ctx): - return{"FINISHED"} + return{'FINISHED'} def invoke(self, ctx, event): self.date_version = str(bpy.data.version) @@ -76,8 +79,9 @@ def invoke(self, ctx, event): class File_OT_Save_Check(Operator): - bl_idname = "file.save_check" + bl_idname = 'file.save_check' bl_label = "Save Check" + bl_description = "" def compar_versions(self, file_ver, app_ver): print(file_ver[0], app_ver[0], file_ver[1], app_ver[1]) @@ -90,7 +94,7 @@ def draw(self, ctx): def execute(self, ctx): bpy.ops.wm.save_mainfile() - return{"FINISHED"} + return{'FINISHED'} def invoke(self, ctx, event): # check if file already saved @@ -98,7 +102,7 @@ def invoke(self, ctx, event): # save normaly if file and blender version are same if self.compar_versions(bpy.data.version, bpy.app.version): bpy.ops.wm.save_mainfile() - return{"FINISHED"} + return{'FINISHED'} # open dialog if versions not same self.date_version = str(bpy.data.version) @@ -107,12 +111,14 @@ def invoke(self, ctx, event): # just saved unsaved files bpy.ops.wm.save_as_mainfile('INVOKE_DEFAULT') - return{"FINISHED"} + + return{'FINISHED'} class Scene_OT_Reset(Operator): - bl_idname = "scene.reset" + bl_idname = 'scene.reset' bl_label = "Reset" + bl_description = "" bl_options = {'REGISTER', 'INTERNAL'} def draw(self, ctx): @@ -137,36 +143,37 @@ def execute(self, ctx): else: pass - return{"FINISHED"} + return{'FINISHED'} def invoke(self, ctx, event): if bpy.data.is_dirty: return ctx.window_manager.invoke_props_dialog(self) + return self.execute(ctx) def version_menu(self, ctx): layout = self.layout layout.separator() - layout.operator("file.version", text='File Version', icon='BLENDER') + layout.operator('file.version', text="File Version", icon='BLENDER') def reset_menu(self, ctx): - self.layout.operator("scene.reset", text='Reset', icon='FILE') + self.layout.operator('scene.reset', text="Reset", icon='FILE') -classes = ( +classes = { File_OT_Scale_Icons, File_OT_Version, File_OT_Save_Check, Scene_OT_Reset -) +} def register_file(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) bpy.types.TOPBAR_MT_file.prepend(reset_menu) bpy.types.TOPBAR_MT_file.append(version_menu) @@ -176,9 +183,9 @@ def unregister_file(): bpy.types.TOPBAR_MT_file.remove(reset_menu) bpy.types.TOPBAR_MT_file.remove(version_menu) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_file() \ No newline at end of file diff --git a/tools/internal/text/arabic.py b/tools/internal/text/arabic.py index 3f860ff..49b6f75 100644 --- a/tools/internal/text/arabic.py +++ b/tools/internal/text/arabic.py @@ -14,6 +14,8 @@ ############################################################################ # 2024/07/14 +#TODO seprator like dot makes pereview character apear as Initial or Medial + import bpy from bpy.types import Operator diff --git a/tools/internal/text/console.py b/tools/internal/text/console.py index 2a41752..98ac21e 100644 --- a/tools/internal/text/console.py +++ b/tools/internal/text/console.py @@ -12,6 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/09/10 import bpy @@ -19,8 +20,10 @@ class Console_OT_Cut(Operator): - bl_idname = "console.cut" + bl_idname = 'console.cut' bl_label = "Cut" + bl_description = "" + def execute(self, ctx): bpy.ops.console.copy('INVOKE_DEFAULT') bpy.ops.console.delete('INVOKE_DEFAULT') diff --git a/tools/internal/text/info.py b/tools/internal/text/info.py index 9d56d9e..14f7c16 100644 --- a/tools/internal/text/info.py +++ b/tools/internal/text/info.py @@ -12,26 +12,28 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/09/10 import bpy from bpy.types import Operator - class Info_OT_Clear(Operator): - bl_idname = "info.clear" + bl_idname = 'info.clear' bl_label = "Clear" + bl_description = "" + def execute(self, ctx): bpy.ops.info.select_all(action='SELECT') bpy.ops.info.report_delete('INVOKE_DEFAULT') return {'FINISHED'} - class Info_OT_NewScript(Operator): - bl_idname = "info.new_script" + bl_idname = 'info.new_script' bl_label = "New Script" + def execute(self, ctx): # bpy.ops.info.report_copy() # ctx.area.type = 'TEXT_EDITOR' @@ -44,14 +46,13 @@ def execute(self, ctx): # bpy.ops.text.new() # bpy.data.texts[-1].name = "New Script" # bpy.ops.text.paste() - return {'FINISHED'} + return {'FINISHED'} def register_info(): bpy.utils.register_class(Info_OT_Clear) - def unregister_info(): bpy.utils.unregister_class(Info_OT_Clear) diff --git a/tools/internal/text/text_editor.py b/tools/internal/text/text_editor.py index b825cf0..476afa7 100644 --- a/tools/internal/text/text_editor.py +++ b/tools/internal/text/text_editor.py @@ -12,36 +12,36 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/09/10 import bpy from bpy.types import Operator - class Text_OT_Smart_Save(Operator): - bl_idname = "text.smart_save" + bl_idname = 'text.smart_save' bl_label = "Smart Save" bl_description = "Save Text if external Save Blend file if internal" + def execute(self, ctx): name = ctx.area.spaces.active.text.name + if bpy.data.texts[name].filepath == '': bpy.ops.wm.save_mainfile('INVOKE_DEFAULT') else: bpy.ops.text.save('INVOKE_DEFAULT') - return {'FINISHED'} + return {'FINISHED'} def register_text_editor(): bpy.utils.register_class(Text_OT_Smart_Save) - def unregister_text_editor(): bpy.utils.unregister_class(Text_OT_Smart_Save) - -if __name__ == "__main__": +if __name__ == '__main__': register_text_editor() \ No newline at end of file diff --git a/tools/internal/transform/align_objects.py b/tools/internal/transform/align_objects.py index 117fbb2..6d244c7 100644 --- a/tools/internal/transform/align_objects.py +++ b/tools/internal/transform/align_objects.py @@ -420,37 +420,37 @@ def get_target_options(self, _): ('CURSOR', "Cursor", "") ] - ready: BoolProperty(default=False) + ready: BoolProperty(default=False) # type: ignore """ Props """ - pos_x: BoolProperty(update=update) - pos_y: BoolProperty(update=update) - pos_z: BoolProperty(update=update) + pos_x: BoolProperty(update=update) # type: ignore + pos_y: BoolProperty(update=update) # type: ignore + pos_z: BoolProperty(update=update) # type: ignore current: EnumProperty( items= get_source_options, update=update, default=2 - ) + ) # type: ignore target: EnumProperty( items= get_target_options, update=update, default=2 - ) + ) # type: ignore - target_type: EnumProperty(update=update, items=sub_target) + target_type: EnumProperty(update=update, items=sub_target) # type: ignore - rot_x: BoolProperty(update=update) - rot_y: BoolProperty(update=update) - rot_z: BoolProperty(update=update) + rot_x: BoolProperty(update=update) # type: ignore + rot_y: BoolProperty(update=update) # type: ignore + rot_z: BoolProperty(update=update) # type: ignore - scl_x: BoolProperty(update=update) - scl_y: BoolProperty(update=update) - scl_z: BoolProperty(update=update) + scl_x: BoolProperty(update=update) # type: ignore + scl_y: BoolProperty(update=update) # type: ignore + scl_z: BoolProperty(update=update) # type: ignore percent: FloatProperty( name="Percent", update=update, soft_min=0, soft_max=1, default=1, step=0.1 - ) + ) # type: ignore @classmethod def poll(self, ctx): @@ -477,7 +477,7 @@ def invoke(self, ctx, _): class Object_OT_Align_Selected_to_Active(Operator): - bl_idname = "object.align_selected_to_active" + bl_idname = 'object.align_selected_to_active' bl_label = "Align Selected to Active object" bl_options = {'REGISTER', 'UNDO'} @@ -495,7 +495,7 @@ def execute(self, ctx): class Object_OT_Align_Selected_to_Target(PickOperator): - bl_idname = "object.align_selected_to_target" + bl_idname = 'object.align_selected_to_target' bl_label = "Align Selected to Target" bl_options = {'REGISTER', 'UNDO'} @@ -516,14 +516,14 @@ def picked(self, ctx, source, subsource, target, subtarget): def register_align_objects(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) def unregister_align_objects(): - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_align_objects() \ No newline at end of file diff --git a/tools/internal/transform/coordinate.py b/tools/internal/transform/coordinate.py index 585f6d6..0b308e6 100644 --- a/tools/internal/transform/coordinate.py +++ b/tools/internal/transform/coordinate.py @@ -12,51 +12,46 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ - -import bpy +# 2024/09/10 from bpy.types import Operator from bpy.props import StringProperty - +from bpy.utils import register_class, unregister_class # Coordinate class Object_OT_Coord_System(Operator): - bl_idname = "object.coordinate_system" + bl_idname = 'object.coordinate_system' bl_label = "Coordinate System" - coordsys: StringProperty(default = 'GLOBAL') + coordsys: StringProperty(default = 'GLOBAL') # type: ignore def execute(self, ctx): # NORMAL, GIMBAL, LOCAL, VIEW, GLOBAL, CURSOR ctx.window.scene.transform_orientation_slots[0].type = self.coordsys - return{"FINISHED"} - + return{'FINISHED'} class Object_OT_Set_Local_Coord_in_Pose_Mode(Operator): - bl_idname = "object.set_local_coord_in_pose_mode" + bl_idname = 'object.set_local_coord_in_pose_mode' bl_label = "Local (Pose)" def execute(self, ctx): ctx.window.scene.transform_orientation_slots[0].type = 'LOCAL' ctx.scene.tool_settings.transform_pivot_point = 'INDIVIDUAL_ORIGINS' - return{"FINISHED"} + return{'FINISHED'} - -classes = ( +classes = { Object_OT_Coord_System, Object_OT_Set_Local_Coord_in_Pose_Mode -) - +} def register_coordinate(): - for c in classes: - bpy.utils.register_class(c) - + for cls in classes: + register_class(cls) def unregister_coordinate(): - for c in classes: - bpy.utils.unregister_class(c) \ No newline at end of file + for cls in classes: + unregister_class(cls) \ No newline at end of file diff --git a/tools/internal/transform/transform_type_in.py b/tools/internal/transform/transform_type_in.py index 4a1250c..b04cd78 100644 --- a/tools/internal/transform/transform_type_in.py +++ b/tools/internal/transform/transform_type_in.py @@ -12,134 +12,131 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/09/09 import bpy from bpy.types import Operator from bpy.props import FloatProperty, BoolProperty, EnumProperty +from bpy.utils import register_class, unregister_class - -def pos_abs_x(self, ctx): +def pos_abs_x(cls, ctx): for obj in ctx.selected_objects: - obj.location[0] = self.pos_abs_x + obj.location[0] = cls.pos_abs_x -def pos_abs_y(self, ctx): +def pos_abs_y(cls, ctx): for obj in ctx.selected_objects: - obj.location[1] = self.pos_abs_y + obj.location[1] = cls.pos_abs_y -def pos_abs_z(self, ctx): +def pos_abs_z(cls, ctx): for obj in ctx.selected_objects: - obj.location[2] = self.pos_abs_z - + obj.location[2] = cls.pos_abs_z -def rot_abs_x(self, ctx): +def rot_abs_x(cls, ctx): for obj in ctx.selected_objects: - obj.rotation_euler[0] = self.rot_abs_x + obj.rotation_euler[0] = cls.rot_abs_x -def rot_abs_y(self, ctx): +def rot_abs_y(cls, ctx): for obj in ctx.selected_objects: - obj.rotation_euler[1] = self.rot_abs_y + obj.rotation_euler[1] = cls.rot_abs_y -def rot_abs_z(self, ctx): +def rot_abs_z(cls, ctx): for obj in ctx.selected_objects: - obj.rotation_euler[2] = self.rot_abs_z - + obj.rotation_euler[2] = cls.rot_abs_z -def scl_abs_x(self, ctx): +def scl_abs_x(cls, ctx): for obj in ctx.selected_objects: - obj.scale[0] = self.scl_abs_x / 100.0 + obj.scale[0] = cls.scl_abs_x / 100.0 -def scl_abs_y(self, ctx): +def scl_abs_y(cls, ctx): for obj in ctx.selected_objects: - obj.scale[1] = self.scl_abs_y / 100.0 + obj.scale[1] = cls.scl_abs_y / 100.0 -def scl_abs_z(self, ctx): +def scl_abs_z(cls, ctx): for obj in ctx.selected_objects: - obj.scale[2] = self.scl_abs_z / 100.0 + obj.scale[2] = cls.scl_abs_z / 100.0 -pos,rot,scl = [],[],[] +pos, rot, scl = [], [], [] -def pos_off_x(self, ctx): +def pos_off_x(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.location[0] = pos[index].x+self.pos_off_x + obj.location[0] = pos[index].x + cls.pos_off_x -def pos_off_y(self, ctx): +def pos_off_y(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.location[1] = pos[index].y+self.pos_off_y + obj.location[1] = pos[index].y + cls.pos_off_y -def pos_off_z(self, ctx): +def pos_off_z(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.location[2] = pos[index].z+self.pos_off_z + obj.location[2] = pos[index].z + cls.pos_off_z - -def rot_off_x(self, ctx): +def rot_off_x(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.rotation_euler[0] = rot[index].x + self.rot_off_x + obj.rotation_euler[0] = rot[index].x + cls.rot_off_x -def rot_off_y(self, ctx): +def rot_off_y(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.rotation_euler[1] = rot[index].y + self.rot_off_y + obj.rotation_euler[1] = rot[index].y + cls.rot_off_y -def rot_off_z(self, ctx): +def rot_off_z(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.rotation_euler[2] = rot[index].z + self.rot_off_z - + obj.rotation_euler[2] = rot[index].z + cls.rot_off_z -def scl_off_x(self, ctx): +def scl_off_x(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.scale[0] = scl[index].x*self.scl_off_x/100 + obj.scale[0] = scl[index].x * cls.scl_off_x/100 -def scl_off_y(self, ctx): +def scl_off_y(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.scale[1] = scl[index].y*self.scl_off_y/100 + obj.scale[1] = scl[index].y * cls.scl_off_y/100 -def scl_off_z(self, ctx): +def scl_off_z(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.scale[2] = scl[index].z*self.scl_off_z/100 - + obj.scale[2] = scl[index].z * cls.scl_off_z/100 -def percent(self, ctx): +def percent(cls, ctx): if len(ctx.selected_objects) == len(scl): for index, obj in enumerate(ctx.selected_objects): - obj.scale[0] = scl[index][0] * self.percent/100 - obj.scale[1] = scl[index][1] * self.percent/100 - obj.scale[2] = scl[index][2] * self.percent/100 + obj.scale[0] = scl[index][0] * cls.percent/100 + obj.scale[1] = scl[index][1] * cls.percent/100 + obj.scale[2] = scl[index][2] * cls.percent/100 - -def read_objects_values(self, ctx): +def read_objects_values(cls, ctx): selection = ctx.selected_objects if len(selection) == 1: location = selection[0].location - self.pos_abs_x = location.x - self.pos_abs_y = location.y - self.pos_abs_z = location.z + cls.pos_abs_x = location.x + cls.pos_abs_y = location.y + cls.pos_abs_z = location.z + sre = selection[0].rotation_euler - self.rot_abs_x = sre.x - self.rot_abs_y = sre.y - self.rot_abs_z = sre.z + cls.rot_abs_x = sre.x + cls.rot_abs_y = sre.y + cls.rot_abs_z = sre.z + scale = selection[0].scale - self.scl_abs_x = scale.x * 100 - self.scl_abs_y = scale.y * 100 - self.scl_abs_z = scale.z * 100 - self.percent = 100 + cls.scl_abs_x = scale.x * 100 + cls.scl_abs_y = scale.y * 100 + cls.scl_abs_z = scale.z * 100 + cls.percent = 100 pos.clear() rot.clear() @@ -155,38 +152,93 @@ def read_objects_values(self, ctx): class Object_OT_Transform_Type_In(Operator): bl_idname = "object.transform_type_in" bl_label = "Transform Type-in" + bl_description = "" bl_options = {'REGISTER', 'UNDO'} - pos_abs_x: FloatProperty(name="X",unit='LENGTH',update=pos_abs_x) - pos_abs_y: FloatProperty(name="Y",unit='LENGTH',update=pos_abs_y) - pos_abs_z: FloatProperty(name="Z",unit='LENGTH',update=pos_abs_z) - - pos_off_x: FloatProperty(name="X",unit='LENGTH',update=pos_off_x) - pos_off_y: FloatProperty(name="Y",unit='LENGTH',update=pos_off_y) - pos_off_z: FloatProperty(name="Z",unit='LENGTH',update=pos_off_z) - - rot_abs_x: FloatProperty(name="X",unit='ROTATION',update=rot_abs_x) - rot_abs_y: FloatProperty(name="Y",unit='ROTATION',update=rot_abs_y) - rot_abs_z: FloatProperty(name="Z",unit='ROTATION',update=rot_abs_z) - - rot_off_x: FloatProperty(name="X",unit='ROTATION',update=rot_off_x) - rot_off_y: FloatProperty(name="Y",unit='ROTATION',update=rot_off_y) - rot_off_z: FloatProperty(name="Z",unit='ROTATION',update=rot_off_z) - - scl_abs_x: FloatProperty(name="X",default=100,update=scl_abs_x) - scl_abs_y: FloatProperty(name="Y",default=100,update=scl_abs_y) - scl_abs_z: FloatProperty(name="Z",default=100,update=scl_abs_z) - - scl_off_x: FloatProperty(name="X",default=100,update=scl_off_x) - scl_off_y: FloatProperty(name="Y",default=100,update=scl_off_y) - scl_off_z: FloatProperty(name="Z",default=100,update=scl_off_z) - - percent: FloatProperty(name="%",default=100,update=percent) - - squash: BoolProperty(name="Squash") + pos_abs_x: FloatProperty( + name="X", unit='LENGTH', update=pos_abs_x + ) # type: ignore + + pos_abs_y: FloatProperty( + name="Y", unit='LENGTH', update=pos_abs_y + ) # type: ignore + + pos_abs_z: FloatProperty( + name="Z", unit='LENGTH', update=pos_abs_z + ) # type: ignore + + pos_off_x: FloatProperty( + name="X", unit='LENGTH', update=pos_off_x + ) # type: ignore + + pos_off_y: FloatProperty( + name="Y", unit='LENGTH', update=pos_off_y + ) # type: ignore + + pos_off_z: FloatProperty( + name="Z", unit='LENGTH', update=pos_off_z + ) # type: ignore + + rot_abs_x: FloatProperty( + name="X", unit='ROTATION', update=rot_abs_x + ) # type: ignore + + rot_abs_y: FloatProperty( + name="Y", unit='ROTATION', update=rot_abs_y + ) # type: ignore + + rot_abs_z: FloatProperty( + name="Z", unit='ROTATION', update=rot_abs_z + ) # type: ignore + + rot_off_x: FloatProperty( + name="X", unit='ROTATION', update=rot_off_x + ) # type: ignore + + rot_off_y: FloatProperty( + name="Y", unit='ROTATION', update=rot_off_y + ) # type: ignore + + rot_off_z: FloatProperty( + name="Z", unit='ROTATION', update=rot_off_z + ) # type: ignore + + scl_abs_x: FloatProperty( + name="X", default=100, update=scl_abs_x + ) # type: ignore + + scl_abs_y: FloatProperty( + name="Y", default=100, update=scl_abs_y + ) # type: ignore + + scl_abs_z: FloatProperty( + name="Z", default=100, update=scl_abs_z + ) # type: ignore + + scl_off_x: FloatProperty( + name="X", default=100, update=scl_off_x + ) # type: ignore + + scl_off_y: FloatProperty( + name="Y", default=100, update=scl_off_y + ) # type: ignore + + scl_off_z: FloatProperty( + name="Z", default=100, update=scl_off_z + ) # type: ignore + + percent: FloatProperty( + name="%", default=100, update=percent + ) # type: ignore + + squash: BoolProperty(name="Squash") # type: ignore def draw(self, ctx): - tool = ctx.workspace.tools.from_space_view3d_mode(ctx.mode,create=False).idname + + tool = ctx.workspace.tools.from_space_view3d_mode( + ctx.mode,create=False + ).idname + layout = self.layout row = layout.row() box = row.box() @@ -194,40 +246,40 @@ def draw(self, ctx): col.label(text="Absolute:") if tool == 'builtin.move': - col.prop(self,"pos_abs_x") - col.prop(self,"pos_abs_y") - col.prop(self,"pos_abs_z") + col.prop(self, 'pos_abs_x') + col.prop(self, 'pos_abs_y') + col.prop(self, 'pos_abs_z') elif tool == 'builtin.rotate': - col.prop(self,"rot_abs_x") - col.prop(self,"rot_abs_y") - col.prop(self,"rot_abs_z") + col.prop(self, 'rot_abs_x') + col.prop(self, 'rot_abs_y') + col.prop(self, 'rot_abs_z') elif tool == 'builtin.scale': - col.prop(self,"scl_abs_x") - col.prop(self,"scl_abs_y") - col.prop(self,"scl_abs_z") - box.prop(self,"squash") + col.prop(self, 'scl_abs_x') + col.prop(self, 'scl_abs_y') + col.prop(self, 'scl_abs_z') + box.prop(self, 'squash') box = row.box() col = box.column(align=True) - col.label(text="Offset:") + col.label(text= 'Offset:') if tool == 'builtin.move': - col.prop(self,"pos_off_x") - col.prop(self,"pos_off_y") - col.prop(self,"pos_off_z") + col.prop(self, 'pos_off_x') + col.prop(self, 'pos_off_y') + col.prop(self, 'pos_off_z') elif tool == 'builtin.rotate': - col.prop(self,"rot_off_x") - col.prop(self,"rot_off_y") - col.prop(self,"rot_off_z") + col.prop(self, 'rot_off_x') + col.prop(self, 'rot_off_y') + col.prop(self, 'rot_off_z') elif tool == 'builtin.scale': - col.prop(self,"scl_off_x") - col.prop(self,"scl_off_y") - col.prop(self,"scl_off_z") - col.prop(self,"percent") + col.prop(self, 'scl_off_x') + col.prop(self, 'scl_off_y') + col.prop(self, 'scl_off_z') + col.prop(self, 'percent') read_objects_values(self, ctx) self.pos_off_x, self.pos_off_y, self.pos_off_z = 0, 0, 0 @@ -236,7 +288,6 @@ def draw(self, ctx): self.percent = 100 def execute(self, ctx): - self.report({'OPERATOR'},'bpy.ops.object.transform_type_in()') return {'FINISHED'} def cancel(self, ctx): @@ -251,34 +302,39 @@ def invoke(self, ctx, event): class Object_OT_TTI_Call(Operator): bl_idname = "object.tti_call" bl_label = "TTI Call" - switch: EnumProperty(default='none', items=[('none',"None",''),('move','Move',''),('rotate','Rotate',''),('scale','Scale','')]) + bl_description = "" + switch: EnumProperty( + items=[ + ('none', "None", ""), + ('move', "Move", ""), + ('rotate', "Rotate", ""), + ('scale', "Scale", "") + ], + default='none' + ) # type: ignore def execute(self, ctx): - if self.switch in {'move','rotate','scale'}: + if self.switch in {'move', 'rotate', 'scale'}: bpy.ops.wm.tool_set_by_id(name='builtin.' + self.switch) bpy.ops.object.transform_type_in('INVOKE_DEFAULT') return {'FINISHED'} - -classes = ( +classes = { Object_OT_Transform_Type_In, Object_OT_TTI_Call -) - +} def register_transform_type_in(): - for c in classes: - bpy.utils.register_class(c) - + for cls in classes: + register_class(cls) def unregister_transform_type_in(): - for c in classes: - bpy.utils.unregister_class(c) - + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_transform_type_in() \ No newline at end of file diff --git a/tools/internal/uv/edit.py b/tools/internal/uv/edit.py index 84f6933..5ce11b4 100644 --- a/tools/internal/uv/edit.py +++ b/tools/internal/uv/edit.py @@ -12,6 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/08/30 import bpy @@ -19,20 +20,24 @@ from bpy.types import Operator from bpy.props import BoolProperty, EnumProperty - +from bpy.utils import register_class, unregister_class # this operator works smoother then the original one in panel class UV_OT_Mirror_Cover(Operator): - """ Mirror the selected UV """ - bl_idname = "uv.mirror_cover" + bl_idname = 'uv.mirror_cover' bl_label = "Mirror (Cover)" + bl_description = "Mirror the selected UV" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} axis: EnumProperty( - name="Axis", default='X', - items=[('X', 'X', ''), ('Y', 'Y', '')] - ) + name="Axis", + items=[ + ('X', "X", "X Axis"), + ('Y', "Y", "Y Axis") + ], + default='X' + ) # type: ignore @classmethod def poll(self, ctx): @@ -45,17 +50,16 @@ def execute(self, ctx): if self.axis == 'Y': bpy.ops.transform.mirror(constraint_axis=(False, True, False)) - return{"FINISHED"} - + return{'FINISHED'} class UV_OT_Turn(Operator): - """ Rotate Selected UV by given degere """ - bl_idname = "uv.turn" + bl_idname = 'uv.turn' bl_label = "Turn" + bl_description = "Rotate Selected UV by given degere" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - ccw: BoolProperty(name="CCW") + ccw: BoolProperty(name="CCW") # type: ignore @classmethod def poll(self, ctx): @@ -72,14 +76,13 @@ def execute(self, ctx): orient_matrix_type='VIEW' ) - return{"FINISHED"} - + return{'FINISHED'} class UV_OT_Snap_Toggle(Operator): - """ Rotate Selected UV by given degere """ - bl_idname = "uv.snap_toggle" + bl_idname = 'uv.snap_toggle' bl_label = "Snap Toggle" + bl_description = "Rotate Selected UV by Given Degere" bl_options = {'REGISTER', 'INTERNAL'} @classmethod @@ -89,14 +92,13 @@ def poll(self, ctx): def execute(self, ctx): tool_settings = ctx.scene.tool_settings tool_settings.use_snap_uv = not tool_settings.use_snap_uv - return{"FINISHED"} - + return{'FINISHED'} class UV_OT_Split_To_Island(Operator): - """ Split Selected to Island with seam border """ - bl_idname = "uv.split_to_island" + bl_idname = 'uv.split_to_island' bl_label = "Split to Island" + bl_description = "Split Selected to Island with seam border" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} @classmethod @@ -114,69 +116,84 @@ def execute(self, ctx): bpy.ops.uv.select_split() # scale down to seprate from rest to let next operator works - bpy.ops.transform.resize(value=(0.5, 0.5, 0.5), - orient_type='GLOBAL', - orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), - orient_matrix_type='GLOBAL' - ) + bpy.ops.transform.resize( + value=(0.5, 0.5, 0.5), + orient_type='GLOBAL', + orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), + orient_matrix_type='GLOBAL' + ) # conver edges to seam bpy.ops.uv.seams_from_islands() # reset scale to original size - bpy.ops.transform.resize(value=(2, 2, 2), - orient_type='GLOBAL', - orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), - orient_matrix_type='GLOBAL' - ) + bpy.ops.transform.resize( + value=(2, 2, 2), + orient_type='GLOBAL', + orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), + orient_matrix_type='GLOBAL' + ) # reset sync mode ctx.scene.tool_settings.use_uv_select_sync = use_uv_select_sync - return{"FINISHED"} + return{'FINISHED'} + + +######################################################################## + +# def get_active_uv_face(mesh): +# uv_layer = mesh.uv_layers.active +# if not uv_layer: +# return None + +# uv_data = uv_layer.data +# selected_faces = [] + +# # Find the selected UV face in the UV Editor +# for poly in mesh.polygons: +# face_uv = [uv_data[loop_index].uv for loop_index in poly.loop_indices] +# if all(uv_data[loop_index].select for loop_index in poly.loop_indices): +# selected_faces.append(face_uv) +# if len(selected_faces) == 0: +# return None + +# if len(selected_faces) > 1: +# return None + +# return selected_faces[0] class UV_OT_Rectangulate_Active_Face(Operator): - """ Make active face perfect rectangle """ - bl_idname = "uv.rectangulate_active_face" + bl_idname = 'uv.rectangulate_active_face' bl_label = "Rectangulate Active Face" + bl_description = "Make active face perfect rectangle" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(self, ctx): return ctx.scene.tool_settings.uv_select_mode == 'FACE' - def execute(self, ctx): - # uv = ctx.object.data.uv_layers.active + # face = get_active_uv_face(ctx.object.data) + # print(">>>", face) + return{'FINISHED'} - # bm = bmesh.from_edit_mesh(ctx.object.data) - # uv_layer = bm.verts.layers.uv.verify() +######################################################################## - # for face in bm.faces: - # print(face.index) - # for loop in face.loops: - # uv = loop[uv_layer] - # print(uv.co, uv.select, loop.vert.index) - # #TODO --- - return{"FINISHED"} +# Original Author 'Simon Lusenc' +# Algorithm stands on the thesis that order of polygon loop is defining direction of face normal +# and that same loop order is used in uv data. +# With this knowladge we can easily say that cross product: +# (v2.uv-v1.uv)x(v3.uv-v2.uv) gives us uv normal direction of part of the polygon. Further +# this normal has to be used in dot product with up vector (0,0,1) and result smaller than zero +# means uv normal is pointed in opposite direction than it should be (partial polygon v1,v2,v3 is flipped). - - -""" Original Author 'Simon Lusenc' """ class UV_OT_Select_Flipped_UVs(Operator): - """Select polygons with flipped UV mapping.""" - - # Algorithm stands on the thesis that order of polygon loop is defining direction of face normal - # and that same loop order is used in uv data. - # With this knowladge we can easily say that cross product: - # (v2.uv-v1.uv)x(v3.uv-v2.uv) gives us uv normal direction of part of the polygon. Further - # this normal has to be used in dot product with up vector (0,0,1) and result smaller than zero - # means uv normal is pointed in opposite direction than it should be (partial polygon v1,v2,v3 is flipped). - - bl_idname = "uv.select_flipped" + bl_idname = 'uv.select_flipped' bl_label = "Select Flipped UVs" + bl_description = "Select polygons with flipped UV Mapping" @classmethod def poll(self, ctx): @@ -185,11 +202,10 @@ def poll(self, ctx): def execute(self, ctx): obj = ctx.object - bpy.ops.mesh.select_all(action="DESELECT") - bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.mesh.select_all(action='DESELECT') + bpy.ops.object.mode_set(mode='OBJECT') for poly in obj.data.polygons: - # calculate uv differences between current and next face vertex for # whole polygon diffs = [] @@ -212,7 +228,8 @@ def execute(self, ctx): if i == len(diffs)-1: break - # as soon as we find partial flipped polygon we select it and finish search + # as soon as we find partial flipped polygon we + # select it and finish search if diffs[i].cross(diffs[i+1]) @ Vector((0,0,1)) <= 0: poly.select = True break @@ -222,37 +239,34 @@ def execute(self, ctx): return {'FINISHED'} - def uv_select_menu(self, ctx): self.layout.operator("uv.select_flipped") - -classes = ( +classes = { UV_OT_Mirror_Cover, UV_OT_Turn, UV_OT_Select_Flipped_UVs, UV_OT_Snap_Toggle, UV_OT_Split_To_Island, UV_OT_Rectangulate_Active_Face -) +} def register_edit(): - for c in classes: - bpy.utils.register_class(c) + for cls in classes: + register_class(cls) bpy.types.IMAGE_MT_select.append(uv_select_menu) - def unregister_edit(): bpy.types.IMAGE_MT_select.remove(uv_select_menu) - for c in classes: - bpy.utils.unregister_class(c) - + for cls in classes: + unregister_class(cls) if __name__ == '__main__': - register_edit() \ No newline at end of file + # register_edit() + register_class(UV_OT_Rectangulate_Active_Face) \ No newline at end of file diff --git a/tools/internal/uv/panel.py b/tools/internal/uv/panel.py index bd4a5ef..07f6016 100644 --- a/tools/internal/uv/panel.py +++ b/tools/internal/uv/panel.py @@ -12,16 +12,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/09/09 import bpy from bpy.types import Panel class UV_OP_Property_Panel(Panel): + bl_idname = 'UV_PT_Tools_Sheet' + bl_label = "UV Tools" bl_space_type = 'IMAGE_EDITOR' bl_region_type = 'UI' - bl_label = 'UV Tools' - bl_idname = 'UV_PT_Tools_Sheet' bl_category = 'UV Tools' @classmethod @@ -34,113 +35,160 @@ def draw(self, ctx): # Pin box = layout.box() row = box.row() - row.operator('uv.pin', text='', icon='PINNED').clear=False - row.operator('uv.pin', text='', icon='UNPINNED').clear=True + row.operator('uv.pin', text="", icon='PINNED').clear=False + row.operator('uv.pin', text="", icon='UNPINNED').clear=True # Mirror box = layout.box() box.operator('mesh.faces_mirror_uv') row = box.row(align=True) - row.operator('uv.mirror_cover', text='X', icon='MOD_MIRROR').axis='X' - row.operator('uv.mirror_cover', text='Y', icon='MOD_MIRROR').axis='Y' - row.operator('uv.turn',text='-90',icon='LOOP_BACK').ccw = False - row.operator('uv.turn',text='+90',icon='LOOP_FORWARDS').ccw = True + row.operator('uv.mirror_cover', text="X", icon='MOD_MIRROR').axis='X' + row.operator('uv.mirror_cover', text="Y", icon='MOD_MIRROR').axis='Y' + row.operator('uv.turn', text="-90", icon='LOOP_BACK').ccw = False + row.operator('uv.turn', text="+90", icon='LOOP_FORWARDS').ccw = True # Snap box = layout.box() - box.label(text='Snap') + box.label(text="Snap") row = box.row(align=True) row.prop(ctx.space_data.uv_editor, 'pixel_snap_mode', expand=True) row = box.row(align=True) - row.operator('uv.snap_selected', text=' ', - icon='SNAP_VERTEX').target='PIXELS' - row.operator('uv.snap_selected', text=' ', - icon='PIVOT_CURSOR').target='CURSOR' - row.operator('uv.snap_selected', text=' ', - icon='ORIENTATION_GIMBAL').target='CURSOR_OFFSET' - row.operator('uv.snap_selected', text=' ', - icon='SELECT_SET').target='ADJACENT_UNSELECTED' + row.operator( + 'uv.snap_selected', text=" ", icon='SNAP_VERTEX' + ).target='PIXELS' + + row.operator( + 'uv.snap_selected', text=" ", icon='PIVOT_CURSOR' + ).target='CURSOR' + + row.operator( + 'uv.snap_selected', text=" ", icon='ORIENTATION_GIMBAL' + ).target='CURSOR_OFFSET' + + row.operator( + 'uv.snap_selected', text=" ", icon='SELECT_SET' + ).target='ADJACENT_UNSELECTED' row = box.row(align=True) - row.operator('uv.snap_cursor', text='Cursor', - icon='ORIENTATION_CURSOR').target='PIXELS' - row.operator('uv.snap_cursor', text='Vertex', - icon='SNAP_VERTEX').target='SELECTED' - box.prop(ctx.space_data.uv_editor, 'lock_bounds', - text='Lock Bound', icon='VIEW_ORTHO') + row.operator( + 'uv.snap_cursor', text="Cursor", icon='ORIENTATION_CURSOR' + ).target='PIXELS' + + row.operator( + 'uv.snap_cursor', text="Vertex", icon='SNAP_VERTEX' + ).target='SELECTED' + + box.prop( + ctx.space_data.uv_editor, 'lock_bounds', + text="Lock Bound", icon='VIEW_ORTHO' + ) # Merge / Split box = layout.box() box.label(text='Merge / Split') row = box.row(align=True) - row.operator('uv.weld', text=' Weld', icon='FULLSCREEN_EXIT') - row.operator('uv.snap_selected', text=' To Cursor', - icon='ORIENTATION_CURSOR').target='CURSOR' - row.operator('uv.remove_doubles', text=' Merge', - icon='DRIVER_DISTANCE') + row.operator('uv.weld', text=" Weld", icon='FULLSCREEN_EXIT') + row.operator( + 'uv.snap_selected', text=" To Cursor", icon='ORIENTATION_CURSOR' + ).target='CURSOR' + + row.operator( + 'uv.remove_doubles', text=" Merge", icon='DRIVER_DISTANCE' + ) + row = box.row(align=True) - row.operator('uv.split_to_island', text='Split') - row.operator('uv.stitch', text='Stitch') + row.operator('uv.split_to_island', text="Split") + row.operator('uv.stitch', text="Stitch") # UNwarap box = layout.box() - box.label(text='Unwrap') + box.label(text="Unwrap") row = box.row(align=True) - row.operator('uv.mark_seam', text=' Mark Seam', icon='MESH_PLANE').clear=False - row.operator('uv.mark_seam', text=' Clear Seam', icon='SELECT_SET').clear=True - row.operator('uv.seams_from_islands', text=' Seam From Island', icon='MOD_BOOLEAN') + row.operator( + 'uv.mark_seam', text=" Mark Seam", icon='MESH_PLANE' + ).clear=False + + row.operator( + 'uv.mark_seam', text=" Clear Seam", icon='SELECT_SET' + ).clear=True + + row.operator( + 'uv.seams_from_islands', text=" Seam From Island", + icon='MOD_BOOLEAN' + ) + row = box.row(align=True) - row.operator('uv.unwrap', text=' Unwarp', icon='MATCLOTH') - row.operator('uv.cube_project', text=' Cube Projection', - icon='MESH_CUBE') - row.operator('uv.cylinder_project', text=' Cylinder Projection', - icon='MESH_CYLINDER') - row.operator('uv.sphere_project', text=' Sphere Projection', - icon='MESH_UVSPHERE') + row.operator('uv.unwrap', text=" Unwarp", icon='MATCLOTH') + + row.operator( + 'uv.cube_project', text=" Cube Projection", icon='MESH_CUBE' + ) + + row.operator( + 'uv.cylinder_project', text=" Cylinder Projection", + icon='MESH_CYLINDER' + ) + + row.operator( + 'uv.sphere_project', text=" Sphere Projection", + icon='MESH_UVSPHERE' + ) + row = box.row(align=True) - row.operator('uv.smart_project', text=' Smart Project', icon='MONKEY') - row.operator('uv.lightmap_pack', text=' Ligtmap Pack', icon='MOD_MULTIRES') - row.operator('uv.follow_active_quads', text=' Fallow Active Quad', - icon='VIEW_PERSPECTIVE') + row.operator('uv.smart_project', text=" Smart Project", icon='MONKEY') + row.operator( + 'uv.lightmap_pack', text=" Ligtmap Pack", icon='MOD_MULTIRES' + ) + + row.operator( + 'uv.follow_active_quads', text=" Fallow Active Quad", + icon='VIEW_PERSPECTIVE' + ) # Align box = layout.box() box.label(text='Align') row = box.row(align=True) - row.operator('uv.align', text=' Straighten').axis='ALIGN_S' - row.operator('uv.align', text='X Straighten').axis='ALIGN_T' - row.operator('uv.align', text='Y Straighten').axis='ALIGN_U' + row.operator('uv.align', text=" Straighten").axis='ALIGN_S' + row.operator('uv.align', text="X Straighten").axis='ALIGN_T' + row.operator('uv.align', text="Y Straighten").axis='ALIGN_U' row = box.row(align=True) - row.operator('uv.align', text='Flatten', - icon='NODE_CORNER').axis='ALIGN_AUTO' - row.operator('uv.align', text='X', - icon='NODE_SIDE').axis='ALIGN_X' - row.operator('uv.align', text='Y', - icon='NODE_TOP').axis='ALIGN_Y' + + row.operator( + 'uv.align', text="Flatten", icon='NODE_CORNER' + ).axis='ALIGN_AUTO' + + row.operator( + 'uv.align', text="X", icon='NODE_SIDE' + ).axis='ALIGN_X' + + row.operator( + 'uv.align', text="Y", icon='NODE_TOP' + ).axis='ALIGN_Y' + row = box.row(align=True) # Pack box = layout.box() box.label(text='Pack') + row = box.row() row.operator('uv.pack_islands', icon='SEQ_STRIP_META') row.operator('uv.average_islands_scale', icon='OBJECT_HIDDEN') + row = box.row() row.operator('uv.minimize_stretch') layout.operator('uv.reset') - def register_panel(): bpy.utils.register_class(UV_OP_Property_Panel) - def unregister_panel(): bpy.utils.unregister_class(UV_OP_Property_Panel) - -if __name__ == "__main__": +if __name__ == '__main__': register_panel() \ No newline at end of file diff --git a/tools/internal/view/__init__.py b/tools/internal/view/__init__.py index 5c88ee4..de182cc 100644 --- a/tools/internal/view/__init__.py +++ b/tools/internal/view/__init__.py @@ -23,7 +23,6 @@ from .viewportbg import register_viewportbg, unregister_viewportbg - def register_view(preferences): register_droptool(preferences) register_float_editor() @@ -34,7 +33,6 @@ def register_view(preferences): register_viewportbg() - def unregister_view(): unregister_droptool() unregister_float_editor() diff --git a/tools/internal/view/copy_past.py b/tools/internal/view/copy_past.py index a137e41..012e8a1 100644 --- a/tools/internal/view/copy_past.py +++ b/tools/internal/view/copy_past.py @@ -12,6 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/08/30 import bpy @@ -19,21 +20,41 @@ from bpy.props import BoolProperty +def create_buffer_object(ctx, name): + source = ctx.object + ''' Create buffer object ''' + bpy.ops.object.select_all(action='DESELECT') + bpy.ops.mesh.primitive_cube_add() + ctx.object.name = name + target = ctx.active_object + ''' Active Source Object ''' + source.select_set(state=True) + ctx.view_layer.objects.active = source + return target + + +def send_to_buffer(ctx, target): + bpy.ops.object.select_all(action='DESELECT') + target.select_set(state=True) + ctx.view_layer.objects.active = target + bpy.ops.view3d.copybuffer() + bpy.ops.object.delete_plus() + class View3D_OT_Copy_Data(Operator): - bl_idname = "view3d.copy_data" - bl_label = "Copy Object/Data" + bl_idname = 'view3d.copy_data' + bl_label = "Copy Object / Data" bl_options = {'REGISTER'} - object: BoolProperty(name='Object', default=True) - material: BoolProperty(name='Material', default=False) - modifier: BoolProperty(name='Modifier', default=False) - animation: BoolProperty(name='Animation', default=False) + object: BoolProperty(name='Object', default=True) # type: ignore + material: BoolProperty(name='Material', default=False) # type: ignore + modifier: BoolProperty(name='Modifier', default=False) # type: ignore + animation: BoolProperty(name='Animation', default=False) # type: ignore @classmethod def poll(self, ctx): if ctx.area.type == 'VIEW_3D': - return len(ctx.selected_objects) > 0 + return ctx.selected_objects return False def draw(self,ctx): @@ -44,71 +65,43 @@ def draw(self,ctx): box.prop(self, 'material') box.prop(self, 'modifier') box.prop(self, 'animation') - - def create_buffer_object(self, ctx, name): - source = ctx.active_object - ''' Create buffer object ''' - bpy.ops.object.select_all(action='DESELECT') - bpy.ops.mesh.primitive_cube_add() - ctx.active_object.name = name - target = ctx.active_object - ''' Active Source Object ''' - source.select_set(state=True) - ctx.view_layer.objects.active = source - return target - - def send_to_buffer(self, ctx, target): - bpy.ops.object.select_all(action='DESELECT') - target.select_set(state=True) - ctx.view_layer.objects.active = target - bpy.ops.view3d.copybuffer() - bpy.ops.object.delete_plus() def execute(self, ctx): if self.object: bpy.ops.view3d.copybuffer() if self.material: - target = self.create_buffer_object(ctx, - 'BsMax_Copy_Past_Material_Temprary_Object' - ) - + name = "BsMax_Copy_Past_Material_Temprary_Object" + target = create_buffer_object(ctx, name) bpy.ops.object.make_links_data(type='MATERIAL') - # self.send_to_buffer(target) + # send_to_buffer(target) if self.modifier: - target = self.create_buffer_object(ctx, - 'BsMax_Copy_Past_Modifier_Temprary_Object' - ) - + name = "BsMax_Copy_Past_Modifier_Temprary_Object" + target = create_buffer_object(ctx, name) bpy.ops.object.make_links_data(type='MODIFIERS') - self.send_to_buffer(target) + send_to_buffer(target) if self.animation: - target = self.create_buffer_object(ctx, - 'BsMax_Copy_Past_Animation_Temprary_Object' - ) + name = "BsMax_Copy_Past_Animation_Temprary_Object" + target = create_buffer_object(ctx, name) bpy.ops.object.make_links_data(type='ANIMATION') - self.send_to_buffer(target) + send_to_buffer(target) - return{"FINISHED"} + return{'FINISHED'} - def invoke(self, ctx, event): + def invoke(self, ctx, _): return ctx.window_manager.invoke_props_dialog(self, width=100) - - def register_copy_past(): bpy.utils.register_class(View3D_OT_Copy_Data) - def unregister_copy_past(): bpy.utils.unregister_class(View3D_OT_Copy_Data) - -if __name__ == "__main__": +if __name__ == '__main__': register_copy_past() \ No newline at end of file diff --git a/tools/internal/view/droptool.py b/tools/internal/view/droptool.py index 2d1dedf..48794b3 100644 --- a/tools/internal/view/droptool.py +++ b/tools/internal/view/droptool.py @@ -18,13 +18,14 @@ from bpy.types import Operator from bpy.props import EnumProperty +from bpy.utils import register_class, unregister_class # This Operator makes free 'X' button in EDIT_TEXT mode class WM_OT_Search_Operator_Cover(Operator): - """ Call the regular search operator """ bl_idname = 'wm.search_operator_cover' bl_label = "Search Operator Cover" + bl_description = "Call the regular search operator" bl_options = {'REGISTER', 'INTERNAL'} @classmethod @@ -38,9 +39,9 @@ def execute(self, ctx): # This Operator makes free '[' ']' buttons in EDIT_TEXT mode class WM_OT_Side_Toolbar_Toggle(Operator): - """ Call the regular search operator """ - bl_idname = "wm.side_toolbar_toggle" + bl_idname = 'wm.side_toolbar_toggle' bl_label = "Side Toolbar Toggle" + bl_description = "Call the regular search operator" bl_options = {'REGISTER', 'INTERNAL'} side: EnumProperty( @@ -169,9 +170,9 @@ def register_droptool(preferences): View3D_OT_Drop_Tool.preferences = preferences for cls in classes: - bpy.utils.register_class(cls) + register_class(cls) def unregister_droptool(): for cls in classes: - bpy.utils.unregister_class(cls) \ No newline at end of file + unregister_class(cls) \ No newline at end of file diff --git a/tools/internal/view/floate_ditor.py b/tools/internal/view/floate_ditor.py index 8a5c76c..e337a98 100644 --- a/tools/internal/view/floate_ditor.py +++ b/tools/internal/view/floate_ditor.py @@ -12,73 +12,78 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/08/30 import bpy from bpy.types import Operator, Menu from bpy.props import StringProperty, BoolProperty - +from bpy.utils import register_class, unregister_class class Editor_OT_Open_As_Float_Window(Operator): bl_idname = 'editor.float' - bl_label = 'Open As Float Window' + bl_label = "Open As Float Window" bl_options = {'REGISTER', 'INTERNAL'} - ui_type: StringProperty(default='VIEW_3D') - shader_type: StringProperty(default='') - multiple: BoolProperty(default=True) + ui_type: StringProperty(default='VIEW_3D') # type: ignore + shader_type: StringProperty(default='') # type: ignore + multiple: BoolProperty(default=True) # type: ignore def execute(self,ctx): - """ New Method for Blender 2.93 and Newer """ windows = ctx.window_manager.windows - + """ Pass if exist and single mode""" if not self.multiple: for window in windows: for area in window.screen.areas: - if area.ui_type == self.ui_type: - if self.shader_type != '': - for space in area.spaces: - if hasattr(space, 'shader_type'): - try: - if space.shader_type == self.shader_type: - return{'FINISHED'} - except: - pass - else: - return{'FINISHED'} + if area.ui_type != self.ui_type: + continue + + if self.shader_type == '': + return{'FINISHED'} + + for space in area.spaces: + if hasattr(space, 'shader_type'): + try: + if space.shader_type == self.shader_type: + return{'FINISHED'} + except: + pass """ Create New Window """ bpy.ops.wm.window_new() area = windows[-1].screen.areas[0] area.ui_type = self.ui_type - if self.shader_type != '': - #TODO check has attribute - try: - ctx.space_data.shader_type = self.shader_type - except: - pass - return{'FINISHED'} + if self.shader_type == '': + return{'FINISHED'} + + #TODO check has attribute + try: + ctx.space_data.shader_type = self.shader_type + except: + pass + return{'FINISHED'} class Editor_OT_Script_Listener_Open(Operator): bl_idname = 'editor.script_listener' - bl_label = 'Script Listener(Float)' + bl_label = "Script Listener(Float)" bl_options = {'REGISTER', 'INTERNAL'} def execute(self, ctx): - """ New Method for Blender 2.93 and Newer """ windows = ctx.window_manager.windows """ Pass if exist """ for window in windows: - if len(window.screen.areas) == 2: - areas = window.screen.areas - if areas[0].ui_type == 'INFO' and areas[1].ui_type == 'CONSOLE': - return{'FINISHED'} + if len(window.screen.areas) != 2: + continue + + areas = window.screen.areas + if areas[0].ui_type == 'INFO' and areas[1].ui_type == 'CONSOLE': + return{'FINISHED'} """ Create New Window """ bpy.ops.wm.window_new() @@ -91,112 +96,135 @@ def execute(self, ctx): return{'FINISHED'} - class BsMax_MT_New_Editor(Menu): bl_idname = 'BSMAX_MT_new_editor' - bl_label = 'New Editor' + bl_label = "New Editor" + def draw(self, ctx): layout=self.layout - layout.operator("editor.float", text='3D Viewport', - icon='VIEW3D').ui_type='VIEW_3D' + layout.operator( + 'editor.float', text="3D Viewport", icon='VIEW3D' + ).ui_type='VIEW_3D' - layout.operator("editor.float", text='Image Editor', - icon='IMAGE').ui_type='IMAGE_EDITOR' + layout.operator( + 'editor.float', text="Image Editor", icon='IMAGE' + ).ui_type='IMAGE_EDITOR' - layout.operator("editor.float", text='UV Editor', - icon='UV').ui_type='UV' + layout.operator( + 'editor.float', text='UV Editor', icon='UV' + ).ui_type='UV' - layout.operator("editor.float", text='Compositor', - icon='NODE_COMPOSITING').ui_type='CompositorNodeTree' + layout.operator( + 'editor.float', text="Compositor", icon='NODE_COMPOSITING' + ).ui_type='CompositorNodeTree' - layout.operator("editor.float", text='Texture Node Editor', - icon='TEXTURE').ui_type='TextureNodeTree' + layout.operator( + 'editor.float', text="Texture Node Editor", icon='TEXTURE' + ).ui_type='TextureNodeTree' - layout.operator("editor.float", text='Geometry Node Editor', - icon='NODETREE').ui_type='GeometryNodeTree' + layout.operator( + 'editor.float', text="Geometry Node Editor", icon='NODETREE' + ).ui_type='GeometryNodeTree' - layout.operator("editor.float", text='Shader Node Editor', - icon='NODE_MATERIAL').ui_type='ShaderNodeTree' + layout.operator( + 'editor.float', text='Shader Node Editor', icon='NODE_MATERIAL' + ).ui_type='ShaderNodeTree' - layout.operator("editor.float", text='Video Sequencer', - icon='SEQUENCE').ui_type='SEQUENCE_EDITOR' + layout.operator( + 'editor.float', text='Video Sequencer', icon='SEQUENCE' + ).ui_type='SEQUENCE_EDITOR' - layout.operator("editor.float", text='Movie Clip Editor', - icon='TRACKER').ui_type='CLIP_EDITOR' + layout.operator( + 'editor.float', text='Movie Clip Editor', icon='TRACKER' + ).ui_type='CLIP_EDITOR' layout.separator() - layout.operator('editor.float', text='Dope Sheet', - icon='ACTION').ui_type='DOPESHEET' + layout.operator( + 'editor.float', text="Dope Sheet", icon='ACTION' + ).ui_type='DOPESHEET' - layout.operator("editor.float", text='Time Line', - icon='TIME').ui_type='TIMELINE' + layout.operator( + 'editor.float', text="Time Line", icon='TIME' + ).ui_type='TIMELINE' - layout.operator("editor.float", text='Graph Editor', - icon='GRAPH').ui_type='FCURVES' + layout.operator( + 'editor.float', text="Graph Editor", icon='GRAPH' + ).ui_type='FCURVES' - layout.operator("editor.float", text='Drivers', - icon='DRIVER').ui_type='DRIVERS' + layout.operator( + 'editor.float', text="Drivers", icon='DRIVER' + ).ui_type='DRIVERS' - layout.operator("editor.float", text='Nonlinear Animation', - icon='NLA').ui_type='NLA_EDITOR' + layout.operator( + 'editor.float', text="Nonlinear Animation", icon='NLA' + ).ui_type='NLA_EDITOR' layout.separator() - layout.operator('editor.float', text='Text Editor', - icon='TEXT').ui_type='TEXT_EDITOR' + layout.operator( + 'editor.float', text="Text Editor", icon='TEXT' + ).ui_type='TEXT_EDITOR' - layout.operator("editor.float", text='Python Console', - icon='CONSOLE').ui_type='CONSOLE' + layout.operator( + 'editor.float', text="Python Console", icon='CONSOLE' + ).ui_type='CONSOLE' - layout.operator("editor.float", text='Info', - icon='INFO').ui_type='INFO' + layout.operator( + 'editor.float', text="Info", icon='INFO' + ).ui_type='INFO' layout.separator() - layout.operator('editor.float', text='Outliner', - icon='OUTLINER').ui_type='OUTLINER' + layout.operator( + 'editor.float', text="Outliner", icon='OUTLINER' + ).ui_type='OUTLINER' - layout.operator("editor.float", text='Properties', - icon='PROPERTIES').ui_type='PROPERTIES' + layout.operator( + 'editor.float', text="Properties", icon='PROPERTIES' + ).ui_type='PROPERTIES' - layout.operator("editor.float", text='File Browser', - icon='FILE_FOLDER').ui_type='FILES' + layout.operator( + 'editor.float', text="File Browser", icon='FILE_FOLDER' + ).ui_type='FILES' - layout.operator("editor.float", text='Asset Manager', - icon='ASSET_MANAGER').ui_type='ASSETS' + layout.operator( + 'editor.float', text="Asset Manager", icon='ASSET_MANAGER' + ).ui_type='ASSETS' - layout.operator("editor.float", text='Sepreadsheet', - icon='SPREADSHEET').ui_type='SPREADSHEET' + layout.operator( + 'editor.float', text="Sepreadsheet", icon='SPREADSHEET' + ).ui_type='SPREADSHEET' - layout.operator("editor.float", text='Prefrences', - icon='PREFERENCES').ui_type='PREFERENCES' + layout.operator( + 'editor.float', text="Prefrences", icon='PREFERENCES' + ).ui_type='PREFERENCES' -def float_editor_menu(self, ctx): + +def float_editor_menu(self, _): layout = self.layout layout.menu('BSMAX_MT_new_editor') - -classes = ( +classes = { Editor_OT_Open_As_Float_Window, Editor_OT_Script_Listener_Open, BsMax_MT_New_Editor -) - +} def register_float_editor(): - for c in classes: - bpy.utils.register_class(c) - bpy.types.TOPBAR_MT_window.prepend(float_editor_menu) + for cls in classes: + register_class(cls) + bpy.types.TOPBAR_MT_window.prepend(float_editor_menu) def unregister_float_editor(): bpy.types.TOPBAR_MT_window.remove(float_editor_menu) - for c in classes: - bpy.utils.unregister_class(c) + + for cls in classes: + unregister_class(cls) diff --git a/tools/internal/view/gride.py b/tools/internal/view/gride.py index af8cdcd..517c611 100644 --- a/tools/internal/view/gride.py +++ b/tools/internal/view/gride.py @@ -19,14 +19,12 @@ from bsmax.gride import LocalGride - localGride = LocalGride() localGride.set(2, 20, None) localGride.cross_on = True localGride.genarate_gride_lines() - def blender_gride(ctx, state): overlay = ctx.space_data.overlay overlay.show_floor = state @@ -36,7 +34,6 @@ def blender_gride(ctx, state): overlay.show_axis_z = False - def max_gride(ctx, stata): global localGride if stata: @@ -45,7 +42,6 @@ def max_gride(ctx, stata): localGride.unregister() - def toggle_view_gride_mode(ctx): bg = ctx.space_data.overlay.show_floor mg = localGride.handler != None @@ -71,9 +67,8 @@ def toggle_view_gride_mode(ctx): blender_gride(ctx, not bg) - class View3D_OT_Show_Hide_Gride(bpy.types.Operator): - bl_idname = "view3d.show_hide_gride" + bl_idname = 'view3d.show_hide_gride' bl_label = "Show Hide Gride" @classmethod @@ -87,16 +82,13 @@ def execute(self, ctx): return{"FINISHED"} - def register_gride(): bpy.utils.register_class(View3D_OT_Show_Hide_Gride) - def unregister_gride(): bpy.utils.unregister_class(View3D_OT_Show_Hide_Gride) - -if __name__ == "__main__": +if __name__ == '__main__': register_gride() \ No newline at end of file diff --git a/tools/internal/view/viewport.py b/tools/internal/view/viewport.py index d828532..911f173 100644 --- a/tools/internal/view/viewport.py +++ b/tools/internal/view/viewport.py @@ -20,6 +20,7 @@ from bpy.types import Operator, Menu from bpy.props import EnumProperty, BoolProperty +from bpy.utils import register_class, unregister_class #TODO Open ViewLayer as float dialog for Set State in quad menu @@ -395,7 +396,7 @@ def random_object_color_menu(self, ctx): def register_viewport(): for cls in classes: - bpy.utils.register_class(cls) + register_class(cls) bpy.types.VIEW3D_MT_object_showhide.append(random_object_color_menu) @@ -404,7 +405,7 @@ def unregister_viewport(): bpy.types.VIEW3D_MT_object_showhide.remove(random_object_color_menu) for cls in classes: - bpy.utils.unregister_class(cls) + unregister_class(cls) if __name__ == '__main__': register_viewport() \ No newline at end of file diff --git a/tools/internal/view/viewportbg.py b/tools/internal/view/viewportbg.py index 5c37b32..231911b 100644 --- a/tools/internal/view/viewportbg.py +++ b/tools/internal/view/viewportbg.py @@ -21,13 +21,12 @@ # Edit by Nevil July 2019 - class View3D_OT_Background(Operator): - bl_idname = "view3d.background" + bl_idname = 'view3d.background' bl_label = "3D View Color" - bl_description = "Cycle 3D view background colors" + bl_description = "Cycle 3D view background Colors" - index: IntProperty(default = 1) + index: IntProperty(default = 1) # type: ignore def execute(self, ctx): grad = ctx.preferences.themes[0].view_3d.space.gradients @@ -38,16 +37,19 @@ def execute(self, ctx): grad.high_gradient[0] = 0.0 grad.high_gradient[1] = 0.0 grad.high_gradient[2] = 0.0 + elif self.index == 2: #7f7f7f - XSI grad.high_gradient[0] = 0.498 grad.high_gradient[1] = 0.498 grad.high_gradient[2] = 0.498 + elif self.index == 3: #a2a2a2 - maya light grad.high_gradient[0] = 0.635 grad.high_gradient[1] = 0.635 grad.high_gradient[2] = 0.635 + elif self.index == 4: #697b8f - maya gradient show_grad = True @@ -57,6 +59,7 @@ def execute(self, ctx): grad.high_gradient[0] = 0.412 grad.high_gradient[1] = 0.482 grad.high_gradient[2] = 0.561 + elif self.index == 5: #dark blue gradient show_grad = True @@ -66,6 +69,7 @@ def execute(self, ctx): grad.high_gradient[0] = 0.267 grad.high_gradient[1] = 0.302 grad.high_gradient[2] = 0.341 + else: #4b4b4b grad.high_gradient[0] = 0.294 @@ -83,7 +87,6 @@ def execute(self, ctx): return {'FINISHED'} - # selection menu class BMAX_PickViewportBackground_MT(Menu): bl_label = "Viewport Background" @@ -100,11 +103,9 @@ def draw(self, ctx): ui.operator(vbg, text="Grey Blue Gradient").index = 5 - def register_viewportbg(): bpy.utils.register_class(View3D_OT_Background) - def unregister_viewportbg(): bpy.utils.unregister_class(View3D_OT_Background) \ No newline at end of file diff --git a/tools/internal/vse/__init__.py b/tools/internal/vse/__init__.py index 6f15b67..d372c86 100644 --- a/tools/internal/vse/__init__.py +++ b/tools/internal/vse/__init__.py @@ -12,14 +12,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -from .videp_sequence_ediotor import register_video_sequence_ediotor, unregister_video_sequence_ediotor +from .videp_sequence_ediotor import ( + register_video_sequence_ediotor, unregister_video_sequence_ediotor +) def register_vse(): register_video_sequence_ediotor() - def unregister_vse(): unregister_video_sequence_ediotor() \ No newline at end of file diff --git a/tools/internal/vse/videp_sequence_ediotor.py b/tools/internal/vse/videp_sequence_ediotor.py index 5cac26d..8071732 100644 --- a/tools/internal/vse/videp_sequence_ediotor.py +++ b/tools/internal/vse/videp_sequence_ediotor.py @@ -12,11 +12,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/08/30 import bpy from bpy.types import Operator from bpy.props import EnumProperty, IntProperty +from bpy.utils import register_class, unregister_class #TODO show/hide isolate ... tools @@ -31,181 +33,174 @@ # print('Out of strip') - def get_selected_sequences(scene): """ check the sequences return true if detect first selected sequence """ - return [sequence for sequence in scene.sequence_editor.sequences if sequence.select] + return [ + sequence for sequence in scene.sequence_editor.sequences + if sequence.select + ] + + +def sort_by_channel(sequences, invert=False): + """ Get list of sequence sort by chanel index + * sequences: list of sequence + * invert: invert the direction of sorting to up to down + """ + # get chanel indexes in unique array + channels, sorted_sequences = [], [] + for sequence in sequences: + if sequence.channel not in channels: + channels.append(sequence.channel) + channels.sort() + + if invert: + channels.reverse() + + # collect sequences line by line in in ret array + for channel in channels: + for sequence in sequences: + if sequence.channel == channel: + sorted_sequences.append(sequence) + return sorted_sequences -class Sequencer_OT_Shift(Operator): - bl_idname = "sequencer.shift" - bl_label = "Shift Sequences" - bl_description = "" - bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} +def sort_by_start(sequences, invert=False): + """ Get list of sequence sort by start Frame + * sequences: list of sequence + * invert: invert the direction of sorting to end to start + """ + # collect start frames in unique array + frames, sorted_sequences = [], [] - direction: EnumProperty( - items=[ - ('UP', 'Up', 'Move selected Sequences to upper empty chanel'), - ('DOWN', 'Down', 'Move selected Sequences to lower empty chanel'), - ('LEFT', 'Left', 'Shift selected Sequences to one frame to Left'), - ('RIGHT', 'Right', 'Shift selected Sequences to one frame to Right') - ] - ) + for sequence in sequences: + start_frame = sequence.frame_start + sequence.frame_offset_start + if start_frame not in frames: + frames.append(start_frame) - step: IntProperty(name="Step Size", min= 1, max= 64, default=1) + frames.sort() + if invert: + frames.reverse() - def sort_by_channel(self, sequences, invert=False): - """ Get list of sequence sort by chanel index - * sequences: list of sequence - * invert: invert the direction of sorting to up to down - """ - # get chanel indexes in unique array - channels, sorted_sequences = [], [] + # collect sequences line by line in in ret array + for frame in frames: for sequence in sequences: - if sequence.channel not in channels: - channels.append(sequence.channel) - channels.sort() + start_frame = sequence.frame_start + sequence.frame_offset_start + if start_frame == frame: + sorted_sequences.append(sequence) - if invert: - channels.reverse() + return sorted_sequences - # collect sequences line by line in in ret array - for channel in channels: - for sequence in sequences: - if sequence.channel == channel: - sorted_sequences.append(sequence) - return sorted_sequences +def use_side_shift(sequence): + return sequence.type in { + 'SOUND', 'MOVIE', 'IMAGE', 'COLOR', + 'TEXT', 'ADJUSTMENT', 'SCENE' + } - def sort_by_start(self, sequences, invert=False): - """ Get list of sequence sort by start Frame - * sequences: list of sequence - * invert: invert the direction of sorting to end to start - """ - # collect start frames in unique array - frames, sorted_sequences = [], [] - for sequence in sequences: - start_frame = sequence.frame_start + sequence.frame_offset_start - if start_frame not in frames: - frames.append(start_frame) - - frames.sort() - if invert: - frames.reverse() +class Sequencer_OT_Shift(Operator): + bl_idname = 'sequencer.shift' + bl_label = "Shift Sequences" + bl_description = "Shift selected strips" + bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - # collect sequences line by line in in ret array - for frame in frames: - for sequence in sequences: - start_frame = sequence.frame_start + sequence.frame_offset_start - if start_frame == frame: - sorted_sequences.append(sequence) + direction: EnumProperty( + items=[ + ('UP', "Up", "Move selected Sequences to upper empty chanel"), + ('DOWN', "Down", "Move selected Sequences to lower empty chanel"), + ('LEFT', "Left", "Shift selected Sequences to one frame to Left"), + ('RIGHT', "Right", "Shift selected Sequences to one frame to Right") + ] + ) # type: ignore - return sorted_sequences + step: IntProperty( + name="Step Size", min= 1, max= 64, default=1 + ) # type: ignore - def draw(self, ctx): + def draw(self, _): self.layout.prop(self, 'direction') - def use_side_shift(self, sequence): - return sequence.type in {'SOUND', 'MOVIE', 'IMAGE', 'COLOR', - 'TEXT', 'ADJUSTMENT', 'SCENE'} - def execute(self, ctx): sequences = get_selected_sequences(ctx.scene) if self.direction == 'UP': - sequences = self.sort_by_channel(sequences, invert=True) + sequences = sort_by_channel(sequences, invert=True) + elif self.direction == 'DOWN': - sequences = self.sort_by_channel(sequences) + sequences = sort_by_channel(sequences) + elif self.direction == 'LEFT': - sequences = self.sort_by_start(sequences) + sequences = sort_by_start(sequences) + elif self.direction == 'RIGHT': - sequences = self.sort_by_start(sequences, invert=True) + sequences = sort_by_start(sequences, invert=True) """ Shift the sequence by given step """ for sequence in sequences: if self.direction == 'UP': sequence.channel += self.step + elif self.direction == 'DOWN': sequence.channel -= self.step - elif self.direction == 'LEFT' and self.use_side_shift(sequence): + + elif self.direction == 'LEFT' and use_side_shift(sequence): sequence.frame_start -= self.step - elif self.direction == 'RIGHT' and self.use_side_shift(sequence): - sequence.frame_start += self.step - return{"FINISHED"} + elif self.direction == 'RIGHT' and use_side_shift(sequence): + sequence.frame_start += self.step + return{'FINISHED'} class Sequencer_OT_Mute_Toggle(Operator): - bl_idname = "sequencer.mute_toggle" + bl_idname = 'sequencer.mute_toggle' bl_label = "Mute_toggle" - bl_description = "" + bl_description = "Mute Strips" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} def execute(self, ctx): sequences = get_selected_sequences(ctx.scene) active_strip = ctx.scene.sequence_editor.active_strip + if active_strip.select: state = not active_strip.mute for sequence in sequences: sequence.mute = state - return{"FINISHED"} + + return{'FINISHED'} class Sequencer_OT_Zoom_Extended(Operator): - bl_idname = "sequencer.zoom_extended" + bl_idname = 'sequencer.zoom_extended' bl_label = "Zoom Extended" bl_description = "Zoom Extended" bl_options = {'REGISTER', 'INTERNAL'} - def any_selected(self, scene): - """ check the sequences return true if detect first selected sequence """ - for sequence in scene.sequence_editor.sequences: - if sequence.select: - return True - return False - def execute(self, ctx): - if self.any_selected(ctx.scene): + if ctx.selected_sequences: bpy.ops.sequencer.view_selected() else: bpy.ops.sequencer.view_all() - return{"FINISHED"} - - -#TODO Select all, select before time select after time -# class Sequencer_OT_SelectRow(Operator): -# bl_idname = "sequencer.select_row" -# bl_label = "Select Row" -# bl_description = "Select Row" -# bl_options = {'REGISTER', 'INTERNAL'} - -# def execute(self, ctx): -# return{"FINISHED"} + return{'FINISHED'} - -classes = ( +classes = { Sequencer_OT_Shift, Sequencer_OT_Mute_Toggle, Sequencer_OT_Zoom_Extended -) - +} def register_video_sequence_ediotor(): - for c in classes: - bpy.utils.register_class(c) - + for cls in classes: + register_class(cls) def unregister_video_sequence_ediotor(): - for c in classes: - bpy.utils.unregister_class(c) - + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_video_sequence_ediotor() \ No newline at end of file diff --git a/tools/pipeline/__init__.py b/tools/pipeline/__init__.py index faa3fff..a8da2fb 100644 --- a/tools/pipeline/__init__.py +++ b/tools/pipeline/__init__.py @@ -25,15 +25,21 @@ register_make_ready_render_v3, unregister_make_ready_render_v3 ) +from .maya_abc_cleaner import ( + register_maya_abc_cleaner, unregister_maya_abc_cleaner +) + def register_pipeline(preferences): if preferences.nevil_stuff: # register_make_ready_render_v1() # register_make_ready_render_v2() register_make_ready_render_v3() + register_maya_abc_cleaner() def unregister_pipeline(): # unregister_make_ready_render_v1() # unregister_make_ready_render_v2() unregister_make_ready_render_v3() + unregister_maya_abc_cleaner() diff --git a/tools/pipeline/maya_abc_cleaner.py b/tools/pipeline/maya_abc_cleaner.py new file mode 100644 index 0000000..76fc194 --- /dev/null +++ b/tools/pipeline/maya_abc_cleaner.py @@ -0,0 +1,423 @@ +############################################################################ +# 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 +import os + +from bpy.types import Operator +from bpy.utils import register_class, unregister_class +from time import sleep + + +def delete_isolated_empties(): + object_count = len(bpy.data.objects) + + for obj in bpy.data.objects: + if obj.type != 'EMPTY': + continue + + if obj.children: + continue + + bpy.data.objects.remove(obj) + + if object_count == len(bpy.data.objects): + return + + delete_isolated_empties() + + +def delete_by_namekeys(keys): + for key in keys: + for obj in bpy.data.objects: + if key in obj.name: + bpy.data.objects.remove(obj) + delete_isolated_empties() + + +def delete_by_hierarchy(obj): + objs = obj.children_recursive + objs += [obj] + for obj in objs: + try: + bpy.data.objects.remove(obj) + except: + pass + + +def delete_layout(ctx): + objs = [] + camera = None + # collect all layout objects + for name in ['Char_Parent', 'Env_Parent']: + if name in bpy.data.objects: + parent = bpy.data.objects[name] + objs.append(parent) + objs += parent.children_recursive + + # detect camera of layout + for obj in objs: + if obj.type == 'CAMERA': + camera = obj + break + + # unlink camera from parents + if camera: + bpy.ops.object.select_all(action='DESELECT') + camera.select_set(True) + ctx.view_layer.objects.active = obj + bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM') + + # delete all layout objects + for name in ['Char_Parent', 'Env_Parent']: + if name in bpy.data.objects: + delete_by_hierarchy(bpy.data.objects[name]) + + return camera + + +def delete_free_cameras(): + bpy.ops.object.select_all(action='DESELECT') + + for cam in bpy.data.objects: + if cam.type != 'CAMERA': + continue + + if cam.parent: + continue + + bpy.data.objects.remove(cam) + + +def delete_curves(): + for obj in bpy.data.objects: + if obj.type == 'CURVES': + bpy.data.objects.remove(obj) + delete_isolated_empties() + + +def delete_non_geometry_branches(): + # delete empty objects witout parent and + # name dose not countain :Geometry + for obj in bpy.data.objects: + if obj.type != 'EMPTY': + continue + + if obj.parent: + continue + + if ':Geometry' in obj.name: + continue + + delete_by_hierarchy(obj) + + delete_isolated_empties() + + +def delete_mesh_geometry(): + # Delete empty objects with :MeshGeometry on name + for obj in bpy.data.objects: + if obj.type != 'EMPTY': + continue + + if ':MeshGeometry' in obj.name: + delete_by_hierarchy(obj) + + delete_isolated_empties() + + +def delete_special_sufixes(type, suffix): + for obj in bpy.data.objects: + if obj.type != type: + continue + + if obj.name.lower().endswith(suffix): + bpy.data.objects.remove(obj) + + delete_isolated_empties() + + +def delete_special_keys(type, suffix): + for obj in bpy.data.objects: + if obj.type != type: + continue + + if suffix in obj.name.lower(): + bpy.data.objects.remove(obj) + + delete_isolated_empties() + + +def force_get_colection(ctx, name): + collections = bpy.data.collections + if name in collections: + collection = collections[name] + return collection + + newCollection = collections.new(name) + ctx.scene.collection.children.link(newCollection) + return newCollection + + +def link_objs_to_collection(objs, collection): + for obj in objs: + for layer in obj.users_collection: + layer.objects.unlink(obj) + collection.objects.link(obj) + + +def put_everything_in_new_collection(ctx): + collection = force_get_colection(ctx, "ABC Meshes") + objs = bpy.data.objects + link_objs_to_collection(objs, collection) + + +# objects are in condition that not able to auto detect for now +# listed here and to delete by name +# TODO pout this in a text file in project refrence or repository folder +# and let code stay dynamic for difrent projects +def check_black_list(): + balack_list = { + "Ayse_Rig_Final:Ayse_Face_main1", + "Ayse_Rig_Final:Ayse_Face_main5", + "Ayse_Rig_Final:Ayse_Face_main7", + "Ayse_Rig_Final:Ayse_Face_main6", + "Cemil_Rig_Final:Cemil_Labtume_bl_shape_geo", + "Zahra_Rig_Final:Zahra_Face1" + } + for name in balack_list: + if name in bpy.data.objects: + obj = bpy.data.objects[name] + bpy.data.objects.remove(obj) + delete_isolated_empties() + + +# long delays couse the blender crash +# short sleep delays solve the issue for now +def clean_abc_scene(ctx): + delete_free_cameras() + delete_layout(ctx) + sleep(0.1) + + delete_curves() + sleep(0.1) + + keys = {':skinCage', ':Group'} + delete_by_namekeys(keys) + sleep(0.1) + + delete_non_geometry_branches() + sleep(0.1) + + delete_mesh_geometry() + sleep(0.1) + + + delete_special_sufixes('MESH', "_prx") + delete_special_sufixes('MESH', "_wrap") + delete_special_keys('MESH', "_prx") + delete_special_keys('MESH', "_proxy") + delete_special_keys('MESH', "base") + + check_black_list() + + put_everything_in_new_collection(ctx) + + +def get_character_parents(ctx): + main_parents = [] + for obj in bpy.data.objects: + if obj.parent: + continue + + if obj.type == 'EMPTY': + main_parents.append(obj) + return main_parents + + +def geuss_character_name(empty_name): + parts = empty_name.split(':') + if len(parts) == 1: + return empty_name + + + parts = parts[0].split('_') + if len(parts) == 1: + return parts[0] + + name = "" + keys = {'rig', 'final'} + for part in parts: + if part.lower() in keys: + break + + if name: + name += '_' + + name += part + + return name + + +def geuss_charater_part_name(name): + parts = name.split(':') + if len(parts) == 1: + return name + return parts[1] + + +def get_mesh_objects_from_branch(root): + mesh_objects = [] + objs = root.children_recursive + for obj in objs: + if obj.type == 'MESH': + mesh_objects.append(obj) + return mesh_objects + + +def get_safe_clone_node_groupe(): + if "Safe Clone" in bpy.data.node_groups: + return bpy.data.node_groups["Safe Clone"] + + new_node_group = bpy.data.node_groups.new("Safe Clone", 'GeometryNodeTree') + new_node_group.interface.new_socket( + name="Geo In", in_out ="INPUT", socket_type="NodeSocketGeometry" + ) + + new_node_group.interface.new_socket( + name="Obj In", in_out ="INPUT", socket_type="NodeSocketObject" + ) + + new_node_group.interface.new_socket( + name="Geo Out", in_out ="OUTPUT", socket_type="NodeSocketGeometry" + ) + + gn_input = new_node_group.nodes.new('NodeGroupInput') + gn_input.location = (-200, 0) + + gn_objinfo = new_node_group.nodes.new('GeometryNodeObjectInfo') + gn_objinfo.transform_space = 'RELATIVE' + + gn_output = new_node_group.nodes.new('NodeGroupOutput') + gn_output.location = (200, 0) + + new_node_group.links.new( + gn_input.outputs[1], gn_objinfo.inputs[0] + ) + + new_node_group.links.new( + gn_objinfo.outputs[4], gn_output.inputs[0] + ) + + return new_node_group + + +def create_safe_clones(ctx, mesh, collection): + name = geuss_charater_part_name(mesh.name) + mehs_date = bpy.data.meshes.new(name) + new_object = bpy.data.objects.new(name, mehs_date) + collection.objects.link(new_object) + modifier = new_object.modifiers.new(name="Safe Clone", type='NODES') + node_groupe = get_safe_clone_node_groupe() + modifier.node_group = node_groupe + modifier['Socket_1'] = mesh + + +def get_abc_file_name(suffix): + file_name = bpy.data.filepath + abc_folder = os.path.dirname(file_name) + os.sep + 'ABC' + preffix = os.path.basename(file_name).split('.')[0] + + if not os.path.isdir(abc_folder): + os.mkdir(abc_folder) + + return "//ABC" + os.sep + preffix + "_" + suffix + ".ABC" + + +def collection_export_setup(ctx, collection): + layer_collection = ctx.view_layer.layer_collection.children[collection.name] + ctx.view_layer.active_layer_collection = layer_collection + + if not 'Alembic' in collection.exporters: + bpy.ops.collection.exporter_add(name="IO_FH_alembic") + + export_data = { + 'filepath': get_abc_file_name(collection.name), + 'start': ctx.scene.frame_start, + 'end': ctx.scene.frame_end, + 'evaluation_mode': 0, + 'use_instancing': False, + 'uvs': False, + 'normals': False, + 'face_sets': False, + 'orcos': False, + 'packuv': False, + 'apply_subdiv': False, + 'export_custom_properties': False, + 'export_hair': False, + 'export_particles': False, + 'curves_as_mesh': False, + 'subdiv_schema': False, + 'vcolors': False, + 'triangulate': False + } + + export_properties = collection.exporters['Alembic'].export_properties + for key, value in export_data.items(): + export_properties[key] = value + + +def create_safe_clone_of_charcters(ctx): + chararacter_parents = get_character_parents(ctx) + + for character in chararacter_parents: + name = geuss_character_name(character.name) + collection = force_get_colection(ctx, name) + charater_meshs = get_mesh_objects_from_branch(character) + for mesh in charater_meshs: + create_safe_clones(ctx, mesh, collection) + collection_export_setup(ctx, collection) + + +class Nevil_OT_Maya_ABC_Cleaner(Operator): + bl_idname = 'nevil.maya_abc_cleaner' + bl_label = "Clear ABC from Maya v1 (Nevil)" + bl_description = "Clear and meake redy for export abc scene from maya" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, ctx): + clean_abc_scene(ctx) + create_safe_clone_of_charcters(ctx) + return{'FINISHED'} + + +classes = { + Nevil_OT_Maya_ABC_Cleaner +} + + +def register_maya_abc_cleaner(): + for cls in classes: + register_class(cls) + + +def unregister_maya_abc_cleaner(): + for cls in classes: + if cls.is_registered: + unregister_class(cls) + + +if __name__ == '__main__': + register_maya_abc_cleaner() \ No newline at end of file diff --git a/tools/special/max/modifier_panel.py b/tools/special/max/modifier_panel.py index d8f50e8..e943491 100644 --- a/tools/special/max/modifier_panel.py +++ b/tools/special/max/modifier_panel.py @@ -307,21 +307,24 @@ def clear(self): class OBJECT_UL_modifier_list(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - scene = data - ob = item + def draw_item(self, context, layout, data, item, icon, + active_data, active_propname): if self.layout_type in {'DEFAULT', 'COMPACT'}: - layout.prop(ob, "name", text="", emboss=False, icon_value=layout.icon(ob)) + layout.prop(item, "name", text="", emboss=False, + icon_value=layout.icon(item) + ) class Object_OT_Modifier_Create(Operator): bl_idname = 'object.modifier_create' - bl_label = 'Create Modifier' + bl_label = "Create Modifier" bl_property = 'search' - bl_description = '' + bl_description = "" - search: EnumProperty(name='Select Modifier', items=get_modifier_list) + search: EnumProperty( + name="Select Modifier", items=get_modifier_list + ) # type: ignore def execute(self, ctx): modifierList = get_modifier_list(None, ctx) @@ -408,7 +411,7 @@ class Object_OT_Active_Modifier(Operator): bl_label = 'Active Modifier' bl_options = {'REGISTER', 'INTERNAL'} - index: IntProperty() + index: IntProperty() # type: ignore def execute(self, ctx): if self.index < len(ctx.object.modifiers): @@ -432,7 +435,7 @@ class Modifier_OT_Add_Geodifier(Operator): bl_label = "ADD Geodifier" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - idname: StringProperty() + idname: StringProperty() # type: ignore def execute(self, ctx): return{"FINISHED"}