diff --git a/CHANGELOG.md b/CHANGELOG.md index dcadf7d..dc776e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0, 1, 2, 20240603 +* Join plus issue with None mesh object fixed. +* Mouse Weel up/down in edit mode select more and less (3DsMax & Blender adapted). +* Bone Hierarchy selection tool error on None Armatour selected in pose mode fixed. +* Link/Append/Import added to Add menu for speed up work flow (Shift+A). +* Cylinder pirimitive issue with bevel modifier when slice is on fixed. +* Code cleaning and some other minor bug fixed. + # 0, 1, 2, 20240527 * Backburner submiter has some updates * 1. custom Repository directory option. diff --git a/README.md b/README.md index 3839acb..9c613d5 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,10 @@ If you found this product useful and want to support this project you can Donate * [Download Older Version (2.80-2.92)(2.93-3.2)](https://github.com/NevilArt/BsMax_2_80) ## Recent Updates and Changes -* Join plus issue with None mesh object fixed. -* Mouse Weel up/down in edit mode select more and less (3DsMax & Blender adapted). -* Bone Hierarchy selection tool error on None Armatour selected in pose mode fixed. -* Link/Append/Import added to Add menu for speed up work flow (Shift+A). -* Cylinder pirimitive issue with bevel modifier when slice is on fixed. -* Code cleaning and some other minor bug fixed. +* Kemap issues from last update fixed. +* GraphEditor select chanels auto hide unselected chanels (3DsMax Mode). +* Copy Camera animation to other softwares (Alpha version yet). +* Export Camera via Alembic do not transfer FOV animation. this tool helps to port fov and other animations to 3DsMax scene (Maya option is on the way too). * [Change log ...](https://github.com/NevilArt/BsMax/blob/master/CHANGELOG.md) ## Special Thanks diff --git a/__init__.py b/__init__.py index ffcffd8..c2f2fe3 100644 --- a/__init__.py +++ b/__init__.py @@ -21,7 +21,7 @@ 'name': "BsMax", 'description': "BsMax UI simulations and Tool pack (Blender 3.3LTS ~ 4.1)", 'author': "Naser Merati (Nevil)", - 'version': (0, 1, 2, 20240603), + 'version': (0, 1, 2, 20240605), 'blender': (3, 3, 0), 'location': "Almost Everywhere in Blender", 'wiki_url': 'https://github.com/NevilArt/BsMax/wiki', diff --git a/bsmax/prerequisite.py b/bsmax/prerequisite.py index 13f551e..675c51e 100644 --- a/bsmax/prerequisite.py +++ b/bsmax/prerequisite.py @@ -19,23 +19,31 @@ class BsMax_MT_View3D_tools(Menu): - bl_idname = 'BSMAX_MT_view3dtools' - bl_label = 'Tools' + bl_idname = 'BSMAX_MT_view3d_tools' + bl_label = "Tools" + + # @classmethod + # def poll(self, ctx): + # return ctx.mode == 'OBJECT' - def draw(self, ctx): + def draw(self, _): pass class BsMax_MT_Compositor_tools(Menu): bl_idname = 'BSMAX_MT_compositor_tools' - bl_label = 'Tools' + bl_label = "Tools" + + # @classmethod + # def poll(self, ctx): + # return ctx.mode == 'OBJECT' - def draw(self, ctx): + def draw(self, _): pass class BsMax_MT_View3D_Create(Menu): - bl_idname = "BSMAX_MT_create_menu" + bl_idname = 'BSMAX_MT_create_menu' bl_label = "Create" bl_context = "objectmode" @@ -43,22 +51,53 @@ class BsMax_MT_View3D_Create(Menu): def poll(self, ctx): return ctx.mode == 'OBJECT' - def draw(self, ctx): + def draw(self, _): pass +class BsMax_MT_View3D_Copy(Menu): + bl_idname = 'BSMAX_MT_view3d_copy' + bl_label = "Copy" + bl_description = "Copy" + + # @classmethod + # def poll(self, ctx): + # return ctx.mode == 'OBJECT' + + def draw(self, _): + self.layout.operator( + 'view3d.copybuffer', text="Object", icon='OBJECT_DATA' + ) + + +class BsMax_MT_View3D_Paste(Menu): + bl_idname = 'BSMAX_MT_view3d_paste' + bl_label = "Paste" + bl_description = "Paste" + + # @classmethod + # def poll(self, ctx): + # return ctx.mode == 'OBJECT' + + def draw(self, _): + self.layout.operator( + 'view3d.pastebuffer', text="Object", icon='OBJECT_DATA' + ) + -classes = ( +classes = { BsMax_MT_View3D_tools, BsMax_MT_Compositor_tools, - BsMax_MT_View3D_Create -) + BsMax_MT_View3D_Create, + BsMax_MT_View3D_Copy, + BsMax_MT_View3D_Paste +} def register_prerequisite(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) def unregister_prerequisite(): - for c in classes: - 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/keymaps/blender.py b/keymaps/blender.py index 731b541..b3108c9 100644 --- a/keymaps/blender.py +++ b/keymaps/blender.py @@ -25,7 +25,7 @@ def __init__(self): def get_select_mouse(self): keyconfig = bpy.context.window_manager.keyconfigs.active - return getattr(keyconfig.preferences, "select_mouse", "LEFT") + return getattr(keyconfig.preferences, 'select_mouse', 'LEFT') blender_state = Blender_State() @@ -36,11 +36,15 @@ def window(km): def screen(km): space = km.space('Screen', 'EMPTY', 'WINDOW') - km.new(space, 'screen.marker_jump', 'RIGHT_ARROW', 'PRESS', - [("next", True)], ctrl=True) + km.new( + space, 'screen.marker_jump', 'RIGHT_ARROW', 'PRESS', + [('next', True)], ctrl=True + ) - km.new(space, 'screen.marker_jump', 'LEFT_ARROW', 'PRESS', - [("next", False)], ctrl=True) + km.new( + space, 'screen.marker_jump', 'LEFT_ARROW', 'PRESS', + [('next', False)], ctrl=True + ) def view2d(km): @@ -53,10 +57,10 @@ def view2d_navigation(km, preferences): def view3d(km): space = km.space('3D View', 'VIEW_3D', 'WINDOW') - km.new(space, "wm.multi_item_rename", "F2", "PRESS", []) + km.new(space, 'wm.multi_item_rename', 'F2', 'PRESS', []) km.new( - space, "wm.call_menu", "A", "PRESS", - [('name', "BsMax_MT_Create")], ctrl=True, shift=True + space, 'wm.call_menu', 'A', 'PRESS', + [('name', 'BsMax_MT_Create')], ctrl=True, shift=True ) @@ -64,21 +68,27 @@ def view3d_navigation(km,preferences): space = km.space('3D View', 'VIEW_3D', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space,"view3d.drop_tool", "RIGHTMOUSE", "PRESS",[]) + km.new(space,'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS',[]) if preferences.view_undo: - km.new(space, "view3d.movecover", "MIDDLEMOUSE", "PRESS", - [], shift=True) + km.new( + space, 'view3d.movecover', 'MIDDLEMOUSE', 'PRESS', + [], shift=True + ) - km.new(space, "view3d.rotatecover", "MIDDLEMOUSE", "PRESS", []) - km.new(space, "view3d.zoomcover", "MIDDLEMOUSE", "PRESS", - [], ctrl=True) + km.new(space, 'view3d.rotatecover', 'MIDDLEMOUSE', 'PRESS', []) + km.new( + space, 'view3d.zoomcover', 'MIDDLEMOUSE', 'PRESS', + [], ctrl=True + ) - km.new(space, "view3d.dollycover", "MIDDLEMOUSE", "PRESS", - [], ctrl=True, shift=True) + km.new( + space, 'view3d.dollycover', 'MIDDLEMOUSE', 'PRESS', + [], ctrl=True, shift=True + ) - km.new(space, "view3d.zoomincover", "WHEELINMOUSE", "PRESS", []) - km.new(space, "view3d.zoomoutcover", "WHEELOUTMOUSE", "PRESS", []) + km.new(space, 'view3d.zoomincover', 'WHEELINMOUSE', 'PRESS', []) + km.new(space, 'view3d.zoomoutcover', 'WHEELOUTMOUSE', 'PRESS', []) def view3d_generic(km): @@ -125,19 +135,25 @@ def object_mode(km): space = km.space('Object Non-modal', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space,"view3d.drop_tool", "RIGHTMOUSE", "PRESS", []) + km.new(space,'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS', []) - km.new(space, "bsmax.mode_set", 'F9', "PRESS", []) - km.new(space, 'wm.call_menu', 'A', 'PRESS', - [('name', 'BSMAX_MT_create_menu')], ctrl=True, shift=True) + km.new(space, 'bsmax.mode_set', 'F9', 'PRESS', []) + km.new( + space, 'wm.call_menu', 'A', 'PRESS', + [('name', 'BSMAX_MT_create_menu')], ctrl=True, shift=True + ) km.new(space, 'object.join_plus', 'J', 'PRESS', [], ctrl=True) - km.new(space, 'wm.call_menu', 'C', 'PRESS', - [('name', 'OBJECT_MT_object_copy')], ctrl=True) + km.new( + space, 'wm.call_menu', 'C', 'PRESS', + [('name', 'BSMAX_MT_view3d_copy')], ctrl=True + ) - km.new(space, 'wm.call_menu', 'V', 'PRESS', - [('name', 'OBJECT_MT_object_paste')], ctrl=True) + km.new( + space, 'wm.call_menu', 'V', 'PRESS', + [('name', 'BSMAX_MT_view3d_paste')], ctrl=True + ) km.new(space, 'object.link_to', 'P', 'PRESS', [], shift=True, ctrl=True) @@ -148,13 +164,14 @@ def mesh(km): space = km.space('Mesh', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space, "view3d.drop_tool", "RIGHTMOUSE", "PRESS", []) + km.new(space, 'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS', []) + km.new(space, 'mesh.copy', 'C', 'PRESS', [], ctrl=True) km.new(space, 'mesh.paste', 'V', 'PRESS', [], ctrl=True) km.new( space, 'mesh.select_more', 'WHEELUPMOUSE', 'PRESS', - [('mode', 'SET')], ctr=True + [('mode', 'SET')], ctrl=True ) km.new( @@ -167,29 +184,29 @@ def curve(km): space = km.space('Curve', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space, "view3d.drop_tool", "RIGHTMOUSE", "PRESS", []) + km.new(space, 'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS', []) def armature(km): space = km.space('Armature', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space, "view3d.drop_tool", "RIGHTMOUSE", "PRESS", []) + km.new(space, 'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS', []) - km.new(space, "wm.multi_item_rename", "F2", "PRESS", []) + km.new(space, 'wm.multi_item_rename', 'F2', 'PRESS', []) def metaball(km): space = km.space('Metaball', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space, "view3d.drop_tool", "RIGHTMOUSE", "PRESS", []) + km.new(space, 'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS', []) def lattice(km): space = km.space('Lattice', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space, "view3d.drop_tool", "RIGHTMOUSE", "PRESS", []) + km.new(space, 'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS', []) def grease_pencil(km): @@ -200,8 +217,7 @@ def pos(km): space = km.space('Pose', 'EMPTY', 'WINDOW') if blender_state.select_mouse == 'LEFT': - km.new(space, "view3d.drop_tool", "RIGHTMOUSE", "PRESS", - []) + km.new(space, 'view3d.drop_tool', 'RIGHTMOUSE', 'PRESS',[]) km.new( space, 'pose.select_hierarchy_plus', 'LEFT_BRACKET', 'PRESS', @@ -247,8 +263,8 @@ def sculpt(km): def node_editor(km): - space = km.space("Node Editor", "NODE_EDITOR", 'WINDOW') - km.new(space,"wm.multi_item_rename", "F2", "PRESS", []) + space = km.space('Node Editor', 'NODE_EDITOR', 'WINDOW') + km.new(space,'wm.multi_item_rename', 'F2', 'PRESS', []) def graph_editor(km): @@ -269,7 +285,7 @@ def uv_editor(km): def sequence_editor(km): space = km.space('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW') - km.new(space, "wm.multi_item_rename", "F2", "PRESS", []) + km.new(space, 'wm.multi_item_rename', 'F2', 'PRESS', []) def text(km): @@ -309,20 +325,20 @@ def file_browser(km): def register_blender(preferences): ctx = bpy.context if ctx.window_manager.keyconfigs.addon: - if preferences.navigation_3d == 'Blender': + if preferences.navigation_3d == 'BLENDER': view3d_navigation(km_navigation_3d,preferences) km_navigation_3d.register() ctx.preferences.inputs.view_zoom_axis = 'VERTICAL' else: km_navigation_3d.unregister() - if preferences.navigation_2d == 'Blender': + if preferences.navigation_2d == 'BLENDER': view2d_navigation(km_navigation_2d,preferences) km_navigation_2d.register() else: km_navigation_2d.unregister() - if preferences.viowport == 'Blender': + if preferences.viowport == 'BLENDER': window(km_viowport) screen(km_viowport) view3d(km_viowport) @@ -349,7 +365,7 @@ def register_blender(preferences): else: km_viowport.unregister() - if preferences.sculpt == "Blender": + if preferences.sculpt == 'BLENDER': vertex_paint(km_sculpt) weight_paint(km_sculpt) image_paint(km_sculpt) @@ -358,19 +374,19 @@ def register_blender(preferences): else: km_sculpt.unregister() - if preferences.uv_editor == "Blender": + if preferences.uv_editor == 'BLENDER': uv_editor(km_uv_editor) km_uv_editor.register() else: km_uv_editor.unregister() - if preferences.node_editor == "Blender": + if preferences.node_editor == 'BLENDER': node_editor(km_node_editor) km_node_editor.register() else: km_node_editor.unregister() - if preferences.graph_editor == "Blender": + if preferences.graph_editor == 'BLENDER': graph_editor(km_graph_editor) dopesheet_editor(km_graph_editor) nla_editor(km_graph_editor) @@ -378,25 +394,25 @@ def register_blender(preferences): else: km_graph_editor.unregister() - if preferences.clip_editor == "Blender": + if preferences.clip_editor == 'BLENDER': km_clip_editor.register() else: km_clip_editor.unregister() - if preferences.video_sequencer == "Blender": + if preferences.video_sequencer == 'BLENDER': sequence_editor(km_video_sequencer) km_video_sequencer.register() else: km_video_sequencer.unregister() - if preferences.text_editor == "Blender": + if preferences.text_editor == 'BLENDER': console(km_text_editor) text(km_text_editor) km_text_editor.register() else: km_text_editor.unregister() - if preferences.file_browser == "Blender": + if preferences.file_browser == 'BLENDER': file_browser(km_file_browser) km_file_browser.register() else: diff --git a/keymaps/cinema4d.py b/keymaps/cinema4d.py index 8333dbf..55ce808 100644 --- a/keymaps/cinema4d.py +++ b/keymaps/cinema4d.py @@ -146,19 +146,19 @@ def file_browser(km): def register_cinema4d(preferences): if bpy.context.window_manager.keyconfigs.addon: - if preferences.navigation_3d == "Cinema4D": + if preferences.navigation_3d == 'CINEEMA4D': view3d_navigation(km_navigation_3d,preferences) km_navigation_3d.register() else: km_navigation_3d.unregister() - if preferences.navigation_2d == "Cinema4D": + if preferences.navigation_2d == 'CINEEMA4D': view2d_navigation(km_navigation_2d,preferences) km_navigation_2d.register() else: km_navigation_2d.unregister() - if preferences.viowport == "Cinema4D": + if preferences.viowport == 'CINEEMA4D': window(km_viowport) screen(km_viowport) view3d(km_viowport,preferences) @@ -185,7 +185,7 @@ def register_cinema4d(preferences): else: km_viowport.unregister() - if preferences.sculpt == "Cinema4D": + if preferences.sculpt == 'CINEEMA4D': vertex_paint(km_sculpt) weight_paint(km_sculpt) image_paint(km_sculpt) @@ -194,19 +194,19 @@ def register_cinema4d(preferences): else: km_sculpt.unregister() - if preferences.uv_editor == "Cinema4D": + if preferences.uv_editor == 'CINEEMA4D': uv_editor(km_uv_editor) km_uv_editor.register() else: km_uv_editor.unregister() - if preferences.node_editor == "Cinema4D": + if preferences.node_editor == 'CINEEMA4D': node_editor(km_node_editor) km_node_editor.register() else: km_node_editor.unregister() - if preferences.graph_editor == "Cinema4D": + if preferences.graph_editor == 'CINEEMA4D': graph_editor(km_graph_editor) dopesheet_editor(km_graph_editor) nla_editor(km_graph_editor) @@ -214,25 +214,25 @@ def register_cinema4d(preferences): else: km_graph_editor.unregister() - if preferences.clip_editor == "Cinema4D": + if preferences.clip_editor == 'CINEEMA4D': km_clip_editor.register() else: km_clip_editor.unregister() - if preferences.video_sequencer == "Cinema4D": + if preferences.video_sequencer == 'CINEEMA4D': sequence_editor(km_video_sequencer) km_video_sequencer.register() else: km_video_sequencer.unregister() - if preferences.text_editor == "Cinema4D": + if preferences.text_editor == 'CINEEMA4D': # console(km_text_editor) text(km_text_editor) km_text_editor.register() else: km_text_editor.unregister() - if preferences.file_browser == "Cinema4D": + if preferences.file_browser == 'CINEEMA4D': file_browser(km_file_browser) km_file_browser.register() else: diff --git a/keymaps/float_menu.py b/keymaps/float_menu.py index 5f6eb57..8d4cbe6 100644 --- a/keymaps/float_menu.py +++ b/keymaps/float_menu.py @@ -64,7 +64,7 @@ def add_3dsmax_quad_menu(km, space, preferences): ) """ Ignore Alt + RMB in Maya navigation enabled """ - if preferences.navigation_3d != 'Maya': + if preferences.navigation_3d != 'MAYA': km.new( space, 'bsmax.view3dquadmenue', 'RIGHTMOUSE', 'PRESS', [('menu', 'coordinate')], alt=True diff --git a/keymaps/max.py b/keymaps/max.py index 0e56e14..ee7234d 100644 --- a/keymaps/max.py +++ b/keymaps/max.py @@ -717,12 +717,12 @@ def object_mode(km, preferences): km.new( space, 'wm.call_menu', 'C', 'PRESS', - [('name', 'OBJECT_MT_object_copy')], ctrl=True + [('name', 'BSMAX_MT_view3d_copy')], ctrl=True ) km.new( space, 'wm.call_menu', 'V', 'PRESS', - [('name', 'OBJECT_MT_object_paste')], ctrl=True + [('name', 'BSMAX_MT_view3d_paste')], ctrl=True ) km.new( @@ -1631,6 +1631,42 @@ def info(km): # space = km.space('Frames', 'EMPTY', 'WINDOW') +def animation_chanel(km): + # km.mute( + # 'Node Tool: Select Box (fallback)', + # 'node.select_box', 'LEFTMOUSE', 'CLICK_DRAG', ctrl=True + # ) + space = km.space('Animation Channels', 'EMPTY', 'WINDOW') + + km.new(space, 'anim.channels_click_plus', 'LEFTMOUSE', 'PRESS', + [('extend', False), ('extend_range', False)] + ) + + km.new( + space, 'anim.channels_click_plus', 'LEFTMOUSE', 'PRESS', + [('extend', True), ('extend_range', False)], + ctrl=True + ) + + km.new( + space, 'anim.channels_click_plus', 'LEFTMOUSE', 'PRESS', + [('extend', False), ('extend_range', True)], + shift=True + ) + + km.new( + space, 'anim.channels_click_plus', 'LEFTMOUSE', 'PRESS', + [('extend', True), ('extend_range', True)], + ctrl=True, shift=True + ) + + # km.new( + # space, 'anim.channels_click_plus', 'LEFTMOUSE', 'PRESS', + # [('extend', True), ('extend_range', True)], + # alt=True + # ) + + def graph_editor(km): space = km.space('Graph Editor', 'GRAPH_EDITOR', 'WINDOW') @@ -2218,10 +2254,11 @@ def register_max(preferences): graph_editor(km_graph_editor) dopesheet_editor(km_graph_editor) nla_editor(km_graph_editor) + animation_chanel(km_graph_editor) km_graph_editor.register() else: km_graph_editor.unregister() - + if preferences.clip_editor == '3DSMAX': clip_editor(km_clip_editor) mask_editor(km_clip_editor) diff --git a/maxscript/abc_from_blender.ms b/maxscript/abc_from_blender.ms new file mode 100644 index 0000000..8b635ca --- /dev/null +++ b/maxscript/abc_from_blender.ms @@ -0,0 +1,298 @@ +/*########################################################################## +# 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/06/03 + +struct SceneObjects +( + animated = #(), + static = #(), + diformed = #(), + cameras = #() +) + + +function transformToKey objs startFrame endFrame = +( + /* bake object transform by key or parent or driver to key + args: + objs: array of max objects + startFrame: Integer + endFrame: Integer + return: + None + */ + struct transKey (transform, frame, fov, nearclip, farclip, targetDistance) + struct transformPack (owner, transKeys=#()) + local transformPackes = #() + + /* Store Transform per mesh */ + for obj in Objs do ( + newTransformPack = transformPack() + newTransformPack.owner = obj + for frame = startFrame to endFrame do ( + newTranskey = transKey() + newTranskey.transform = at time frame obj.transform + newTranskey.frame = frame + -- Store camera info + if superClassOf obj == camera do ( + newTranskey.fov = at time frame obj.fov + newTranskey.nearclip = at time frame obj.nearclip + newTranskey.farclip = at time frame obj.farclip + newTranskey.targetDistance = at time frame obj.targetDistance + ) + append newTransformPack.transKeys newTranskey + ) + append transformPackes newTransformPack + ) + + /* Clear Obj controller layers */ + for obj in Objs do ( + try( + obj.Transform.controller = prs() + obj.pos.controller = Position_XYZ() + + for i = 1 to 3 do + obj.pos.controller[i].controller = bezier_float() + obj.rotation.controller = Euler_XYZ() + + for i = 1 to 3 do + obj.rotation.controller[i].controller = bezier_float() + + obj.scale.controller = bezier_scale() + ) + catch() + obj.parent = undefined + ) + + /* Clear animation */ + clearSelection() + select Objs + maxOps.deleteSelectedAnimation() + + /* Restore transforms */ + for tfp in transformPackes do ( + on animate on ( + for tk in tfp.transKeys do ( + at time tk.frame ( + tfp.owner.transform = tk.transform + if superClassOf tfp.owner == camera do( + tfp.owner.fov = tk.fov + tfp.owner.nearclip = tk.nearclip + tfp.owner.farclip = tk.farclip + tfp.owner.targetDistance = tk.targetDistance + ) + ) + ) + ) + ) +) + + +function delete_all_helpers = +( + for obj in objects do ( + if superclassof obj == helper do ( + if obj.name != "Env_Parent" or obj.name != "Char_Parent" do ( + delete obj + ) + ) + ) +) + + +function create_parent scene_objects = +( + char_parent = $Char_Parent + env_parent = $Env_Parent + + if char_parent == undefined do ( + char_parent = point pos:[0, 0, 0] size:100 cross:on box:off + char_parent.name = "Char_Parent" + ) + + if env_parent == undefined do ( + env_parent = point pos:[0, 0, 0] size:100 cross:off box:on + env_parent.name = "Env_Parent" + ) + + for obj in scene_objects.animated do ( + obj.parent = char_parent + ) + + for obj in scene_objects.static do ( + obj.parent = env_parent + ) +) + + +function convert_camera = +( + abcCams = for cam in cameras where classof cam == AlembicCamera collect cam + newCams = #() + for cam in abcCams do ( + newCam = Freecamera() + newCam.parent = cam + newCam.transform = cam.transform + append newCams newCam + ) + transformToKey newCams animationRange.start animationRange.end + delete abcCams + return newCams +) + + +function does_deform obj = +( + -- there is no sure method yet + return False +) + + +function collect_moving_and_static_objects = +( + scene_objects = SceneObjects() + -- movingObjects = #() + -- staticObjects = #() + -- diformObjects = #() + + for obj in objects do ( + if ClassOf obj.transform.controller == prs then ( + -- append staticObjects obj + append scene_objects.static obj + ) + + else if ClassOf obj.transform.controller == AlembicXform do ( + if does_deform obj then ( + -- append diformObjects obj + append scene_objects.diformed obj + ) + + else ( + -- append movingObjects obj + append scene_objects.animated obj + ) + ) + ) + -- return #(movingObjects, staticObjects, diformObjects) + return scene_objects +) + + +function convert_to_mesh objs = +( + convertToMesh objs + for obj in objs do ( + obj.renderbylayer = true + ) +) + + +fn put_objs_in_layer layerName objArray = +( + layer = LayerManager.getLayerFromName layerName + if layer == undefined do ( + layer = LayerManager.newLayer() + layer.setName layerName + ) + + for obj in objArray do ( + layer.addnode obj + ) +) + + +-- function layer_arangment movingObjects staticObjects diformObjects = +function layer_arangment scene_objects = +( + -- put_objs_in_layer "EnvProxy" staticObjects + -- put_objs_in_layer "Charlayout" movingObjects + -- put_objs_in_layer "PropLayout" diformObjects + -- put_objs_in_layer "Camera" cameras + + put_objs_in_layer "EnvProxy" scene_objects.static + put_objs_in_layer "Charlayout" scene_objects.animated + put_objs_in_layer "PropLayout" scene_objects.diformed + put_objs_in_layer "Camera" scene_objects.cameras +) + + +function scene_setting = +( + units.SystemType = #Centimeters + units.displaytype = #metric + units.MetricType = #Centimeters +) + + +function set_render_size = +( + renderWidth = 1920 + rendImageAspectRatio = 1.77778 + rendLockImageAspectRatio = true + renderSceneDialog.update() +) + + +function set_frame_rate = +( + frameRate = 25 +) + + +function abc_from_blender = +( + delete_all_helpers() + cameras = convert_camera() + + -- objGroups = collect_moving_and_static_objects() + scene_objects = collect_moving_and_static_objects() + scene_objects.cameras = cameras + -- movingObjects = objGroups[1] + -- staticObjects = objGroups[2] + -- diformObjects = objGroups[3] + + --transformToKey movingObjects animationRange.start animationRange.end + ----transformToKey scene_objects.animated animationRange.start animationRange.end + --convert_to_mesh movingObjects + --convert_to_mesh staticObjects + ----convert_to_mesh scene_objects.animated + ----convert_to_mesh scene_objects.static + + -- layer_arangment movingObjects staticObjects diformObjects + ----layer_arangment scene_objects + ----create_parent scene_objects +) + + +function make_scene_ready_to_work = +( + scene_setting() + set_render_size() + set_frame_rate() +) + + +function execute_script = +( + if objects.count == 0 then ( + make_scene_ready_to_work() + ) + else ( + abc_from_blender() + ) +) + + +execute_script() \ No newline at end of file diff --git a/tools/internal/__init__.py b/tools/internal/__init__.py index bfc3b91..8de0b60 100644 --- a/tools/internal/__init__.py +++ b/tools/internal/__init__.py @@ -20,7 +20,7 @@ from .clip_editor import register_clip_editor, unregister_clip_editor from .curve import register_curve, unregister_curve from .file import register_file, unregister_file -# from .graph_editor import register_graph_editor, unregister_graph_editor +from .graph_editor import register_graph_editor, unregister_graph_editor from .ligth import register_light, unregister_light from .material import register_material, unregister_material from .nodes import register_nodes, unregister_nodes @@ -47,7 +47,7 @@ def register_internal(preferences): register_clip_editor() register_curve() register_file(preferences) - # register_graph_editor() + register_graph_editor() register_light() register_material(preferences) register_mesh() @@ -73,7 +73,7 @@ def unregister_internal(): unregister_clip_editor() unregister_curve() unregister_file() - # unregister_graph_editor() + unregister_graph_editor() unregister_light() unregister_material() unregister_mesh() diff --git a/tools/internal/animation/menu.py b/tools/internal/animation/menu.py index e23a787..96d29e1 100644 --- a/tools/internal/animation/menu.py +++ b/tools/internal/animation/menu.py @@ -18,7 +18,7 @@ class BsMax_MT_Animation_Tools(bpy.types.Menu): - bl_idname = 'BSMAX_MT_animationtools' + bl_idname = 'BSMAX_MT_animation_tools' bl_label = 'Animation' # bl_context = 'objectmode' @@ -71,7 +71,7 @@ def draw(self, ctx): def animation_menu(self, ctx): - self.layout.menu('BSMAX_MT_animationtools') + self.layout.menu('BSMAX_MT_animation_tools') def key_menu(self, ctx): self.layout.prop( diff --git a/tools/internal/camera/__init__.py b/tools/internal/camera/__init__.py index 0dfc275..fa87cb2 100644 --- a/tools/internal/camera/__init__.py +++ b/tools/internal/camera/__init__.py @@ -12,17 +12,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ + from .cameras import register_cameras, unregister_cameras +from .copy_paste import register_copy_past, unregister_copy_past from .target_camera import register_terget_camera, unregister_terget_camera - def register_camera(): register_cameras() + register_copy_past() register_terget_camera() - def unregister_camera(): unregister_cameras() + unregister_copy_past() unregister_terget_camera() \ No newline at end of file diff --git a/tools/internal/camera/copy_paste.py b/tools/internal/camera/copy_paste.py index ee3ecda..ec5ab06 100644 --- a/tools/internal/camera/copy_paste.py +++ b/tools/internal/camera/copy_paste.py @@ -12,128 +12,150 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/06/04 import bpy +import math + from bpy.types import Operator +from bpy.props import EnumProperty, BoolProperty +from bpy.utils import register_class, unregister_class + + +def copy_to_clipboard(ctx, text): + ctx.window_manager.clipboard = text + + +def keys_to_array_string(keys, mode, decimal=0): + if mode == '3DSMAX': + script = "#(" + for index, key in enumerate(keys): + script += "#(" + script += str(int(key[0])) + script += "," + script += str(key[1]) if decimal == 0 else str(round(key[1], decimal)) + script += ")" + script += "," if index < len(keys)-1 else "" + script += ")" + return script + + if mode == "PYTHON": + script = "[]" + return script + if mode == "MEL": + script = "{}" + return script + return "" -class FloatKey: - def __init__(self): - self.frame = 0 - self.value = 0 - self.type = None - - def from_string(self, string, pick): - field = string.split(',') - self.frame = int(field[0]) - self.value = float(field[pick]) - def to_string(self): - return "" - +def for_loop_script_for_chanel(chanel, mode): + if mode == '3DSMAX': + script = "for key in keys do at time key[1] " + script += "cam." + chanel +" = key[2]" + return script + return "" -class TransformKey: - def __init__(self): - self.frame = 0 +def chanel_to_maxscript_field(chanel, target, decimal=0): + script = " keys = " + script += keys_to_array_string(chanel, '3DSMAX', decimal) + "\n" + script += " " + for_loop_script_for_chanel(target, '3DSMAX') + "\n" + script += "\n" + return script - self.px = 0 - self.py = 0 - self.pz = 0 - self.rx = 0 - self.ry = 0 - self.rz = 0 +def data_to_maxscript(cls, data): + """ cls : Camera_OT_Copy \n + animation_data : CameraAnimationData + """ + script = "-- Camera paste Script Start from here --\n" + if cls.create: + script += "cam = Freecamera isSelected:on\n" + script += "cam.name = \"" + data.owner.name + "\"\n" + else: + script += "cam = $\n" - # scale has no use for camera - self.sx = 1 - self.sy = 1 - self.sz = 1 - - def from_string(self, string): - field = string.split(',') + script += "on animate on (\n" - self.frame = int(field[0]) + if cls.transform: + script += "" - self.px = float(field[1]) - self.py = float(field[2]) - self.pz = float(field[3]) + if cls.fov: + script += chanel_to_maxscript_field(data.lens_keys, "fov", 3) - self.rx = float(field[4]) - self.ry = float(field[5]) - self.rz = float(field[6]) + if cls.clip: + script += " $.clipManually = True \n" + script += chanel_to_maxscript_field(data.clip_start_keys, "nearclip") + script += chanel_to_maxscript_field(data.clip_end_keys, "farclip") - def to_string(self): - return "" + script += ")\n" #end of animate on + script += "-- Camera paste Script end here --" + return script +#TODO check is camera selected +def data_to_script(cls, data, target): + script = "" + if target == '3DSMAX': + return data_to_maxscript(cls, data) -""" -# Field format -# line0 = identefyKey -# line1 = camera name -# line2 = scene name -# 0 1 2 -# line3 = startFrame, endFrame, frameRate -# line4+ data fields -# 0 1 2 3 4 5 6 7 8 -# frame, px, py, pz, rx, ry, rz, fov, lens -""" -class ClipboardCameraAnimation: - def __init__(self): - self.identefyKey = "3DSMAXTOCLIPBOARNEVILDCAMERACOPYPASTDATAVERSION02" - self.cameraName = "Camera" - self.sequenceName = "Sequence" - self.startFrame = 0 - self.endFrame = 250 - self.frameRate = 25 - self.fov = [] - self.lens = [] - self.transform = [] - - def read_clipboard(self): - cb = bpy.context.window_manager.clipboard - text = cb.get_clipboard_text() - lines = text.splitlines() - - # ignore if data not detected - if not lines: - return False - - # ignore if ID key not detcted - if lines[0] != self.identefyKey: - return False - - # read camera and sequence name - self.cameraName = lines[1] - self.sequenceName = lines[2] - - # read sequence settings - field = lines[3].split(',') - self.startFrame = int(field[0]) - self.endFrame = int(field[1]) - self.frameRate = int(field[2]) - - # read keyframes - for line in lines[4:]: - newLens = FloatKey() - newLens.from_string(line, 7) - self.lens.append(newLens) - - newFov = FloatKey() - newFov.from_string(line, 8) - self.fov.append(newFov) - - newTransformKey = TransformKey() - newTransformKey.from_string(line) - self.transform.append(newTransformKey) - - # return True if camera succesfully created - return True + return script +def get_datapath(action, chanel): + for fcurve in action.fcurves: + if fcurve.data_path == chanel: + return fcurve + return None + + +def get_sensor_width_value(camera, fcurve, frame): + if fcurve: + return fcurve.evaluate(frame) + return camera.data.sensor_width + + +def get_fcurve_keys(fcurve): + keys = [] + if fcurve: + for keyframe in fcurve.keyframe_points: + frame, value = keyframe.co + keys.append((frame, value)) + return keys + + +def get_transform_animation(cls): + action = cls.owner.animation_data.action + + +def get_fov_animation(cls): + camera_data = bpy.data.cameras[cls.data.name] + action = camera_data.animation_data.action + lens_fcurve = get_datapath(action, 'lens') + sensor_width_fcurve = get_datapath(action, 'sensor_width') + + if lens_fcurve: + for keyframe in lens_fcurve.keyframe_points: + frame, lens = keyframe.co + sensor_width = get_sensor_width_value( + cls.owner, sensor_width_fcurve, frame + ) + + fov = math.degrees(2 * math.atan(sensor_width / (2 * lens))) + cls.lens_keys.append((frame, fov)) + + +def get_clip_animation(cls): + camera_data = bpy.data.cameras[cls.data.name] + action = camera_data.animation_data.action + start_clip_fcurve = get_datapath(action, 'clip_start') + end_clip_fcurve = get_datapath(action, 'clip_end') + cls.clip_start_keys = get_fcurve_keys(start_clip_fcurve) + cls.clip_end_keys = get_fcurve_keys(end_clip_fcurve) + def create_camera(cameraData): camera_data = bpy.data.cameras.new(name=cameraData.cameraName) @@ -146,30 +168,159 @@ def create_camera(cameraData): return camera_object -def paste_camera_from_clipboard(): - cameraData = ClipboardCameraAnimation() - cameraData.read_clipboard() - create_camera(cameraData) +class Key: + def __init__(self): + self.frame = 0 + self.value = 0 + self.type = None + + +class CameraAnimationData: + def __init__(self, camera): + self.owner = camera + self.data = camera.data + + self.transform_keys = [] + + self.type_keys = [] + self.lens_keys = []#done + self.lens_unit_keys = [] + self.shift_x_keys = [] + self.shift_y_keys = [] + self.clip_start_keys = []#done + self.clip_end_keys = []#done + self.clip_end_keys = [] + self.dof_focus_distance_keys = [] + self.dof_aperture_fstop_keys = [] + self.dof_aperture_blades_keys = [] + self.dof_aperture_rotation_keys = [] + self.dof_aperture_ratio_keys = [] + self.sensor_fit_keys = [] + self.sensor_width_keys = [] + self.show_safe_areas_keys = [] + self.show_safe_center_keys = [] + self.display_size_keys = [] + self.show_limits_keys = [] + self.show_mist_keys = [] + self.show_sensor_keys = [] + self.show_name_keys = [] + self.passepartout_alpha_keys = [] + self.show_sensor_keys = [] + self.show_composition_thirds_keys = [] + self.show_composition_center_keys = [] + self.show_composition_center_diagonal_keys = [] + self.show_composition_golden_keys = [] + self.show_composition_golden_tria_a_keys = [] + self.show_composition_golden_tria_b_keys = [] + self.show_composition_harmony_tri_a_keys = [] + self.show_composition_harmony_tri_b_keys = [] + + +#TODO check does camera has action or not +def get_camera_animation_data(cls, camera_object): + if camera_object.type != 'CAMERA': + return [] + + animation_data = CameraAnimationData(camera_object) + + if cls.transform: + get_transform_animation(animation_data) + + if cls.fov: + get_fov_animation(animation_data) + + if cls.clip: + get_clip_animation(animation_data) + + return animation_data + + +def copy_camera_draw(cls): + layout = cls.layout + box = layout.box() + box.prop(cls, 'create', text="Create New Camera") + + if cls.has_transform_animation: + box.prop(cls, 'transform', text="Transform (Not Ready Yet)") + else: + box.label(text="There is no transform animation.") + + if cls.has_data_animation: + box.prop(cls, 'fov', text="F.O.V.") + box.prop(cls, 'clip', text="Near/Far Cliping") + else: + box.label(text="There is no attribute animation.") + + box = layout.box() + box.prop(cls, 'target', text="Target App") class Camera_OT_Copy(Operator): - bl_idname = "camera.copy" - bl_label = "Copy Camera" - bl_options = {'REGISTER'} + bl_idname = 'camera.copy_animation' + bl_label = "Copy Camera Animation (Alpha Version)" + bl_description = "Copy Camera Animation as executable Script for external softwares" + bl_options = {'REGISTER', 'UNDO'} + + target: EnumProperty( + name="Targte", + items=[ + ('3DSMAX', "Autodesk 3DsMax", "") + ], + default='3DSMAX' + ) # type: ignore + + create: BoolProperty( + name="Create New Camera", default=False, + description="Create a new camera in target software" + ) # type: ignore + + transform: BoolProperty(name="Transform", default=False, + description="Copy Camera Transform or Animation" + ) # type: ignore + + fov: BoolProperty(name="FOV", default=True, + description="Copy Camrea FOV value or Animation" + ) # type: ignore + + clip: BoolProperty(name="Clip", default=True, + description="Copy Camrea Cliping value or Animation" + ) # type: ignore + + has_transform_animation = False + has_data_animation = False @classmethod def poll(self, ctx): - return ctx.mode == "OBJECT" + if ctx.object: + return ctx.object.type == 'CAMERA' + return False + + def draw(self, _): + copy_camera_draw(self) def execute(self, ctx): + animation_data = get_camera_animation_data(self, ctx.object) + script = data_to_script(self, animation_data, self.target) + copy_to_clipboard(ctx, script) + return{'FINISHED'} + + def invoke(self, ctx, event): + self.has_transform_animation = hasattr( + ctx.object.animation_data.action, 'fcurves' + ) - return{"FINISHED"} + self.has_data_animation = hasattr( + ctx.object.data.animation_data, 'action' + ) + ctx.window_manager.invoke_props_dialog(self) + return {'RUNNING_MODAL'} class Camera_OT_Paste(Operator): - bl_idname = "camera.paste" + bl_idname = 'camera.paste' bl_label = "Paste Camera" + bl_description = "under development for now do nothing" bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -177,23 +328,39 @@ def poll(self, ctx): return ctx.mode == "OBJECT" def execute(self, ctx): - - return{"FINISHED"} + return{'FINISHED'} + +def camera_copy_manu(self, ctx): + layout = self.layout + if ctx.object: + if ctx.object.type == 'CAMERA': + layout.operator_context = 'INVOKE_DEFAULT' + layout.operator( + 'camera.copy_animation', text="Camera Animation", + icon='CAMERA_DATA' + ) -classes = ( +classes = { Camera_OT_Copy, Camera_OT_Paste -) +} + def register_copy_past(): - for c in classes: - bpy.utils.register_class(c) + for cls in classes: + register_class(cls) + + bpy.types.BSMAX_MT_view3d_copy.append(camera_copy_manu) + def unregister_copy_past(): - for c in classes: - bpy.utils.unregister_class(c) + bpy.types.BSMAX_MT_view3d_copy.remove(camera_copy_manu) + + for cls in classes: + unregister_class(cls) + -if __name__ == "__main__": +if __name__ == '__main__': register_copy_past() \ No newline at end of file diff --git a/tools/internal/graph_editor/__init__.py b/tools/internal/graph_editor/__init__.py index 1907f50..994b4b6 100644 --- a/tools/internal/graph_editor/__init__.py +++ b/tools/internal/graph_editor/__init__.py @@ -13,12 +13,15 @@ # along with this program. If not,see . ############################################################################ -from tool_bar import register_tool_bar, unregister_tool_bar +from .selection import register_selection, unregister_selection +# from .tool_bar import register_tool_bar, unregister_tool_bar def register_graph_editor(): - register_tool_bar() + register_selection() + # register_tool_bar() def unregister_graph_editor(): - unregister_tool_bar() \ No newline at end of file + unregister_selection() + # unregister_tool_bar() \ No newline at end of file diff --git a/tools/internal/graph_editor/selection.py b/tools/internal/graph_editor/selection.py new file mode 100644 index 0000000..169c316 --- /dev/null +++ b/tools/internal/graph_editor/selection.py @@ -0,0 +1,65 @@ + +############################################################################ +# 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 +from bpy.props import BoolProperty +from bpy.utils import register_class, unregister_class + + +class ANIM_OT_Channels_Click_Plus(Operator): + bl_idname = "anim.channels_click_plus" + bl_label = "Mouse Click on Channel (Auto)" + bl_description = "" + bl_options = {'REGISTER', 'UNDO'} + + extend: BoolProperty(default=False) # type: ignore + extend_range: BoolProperty(default=False) # type: ignore + children_only: BoolProperty(default=False) # type: ignore + + @classmethod + def poll(self, ctx): + return True + + def execute(self, ctx): + bpy.ops.anim.channels_click( + 'INVOKE_DEFAULT', + extend=self.extend, + extend_range=self.extend_range, + children_only=self.children_only + ) + if bpy.ops.graph.hide.poll(): + bpy.ops.graph.hide(unselected=True) + return{'FINISHED'} + + +classes = { + ANIM_OT_Channels_Click_Plus +} + + +def register_selection(): + for cls in classes: + register_class(cls) + + +def unregister_selection(): + for cls in classes: + unregister_class(cls) + + +if __name__ == '__main__': + register_selection() \ No newline at end of file diff --git a/tools/internal/menu.py b/tools/internal/menu.py index 9573038..8752f94 100644 --- a/tools/internal/menu.py +++ b/tools/internal/menu.py @@ -16,25 +16,25 @@ import bpy -def bsmax_tool_menu(self, ctx): +def bsmax_tool_menu(self, _): layout=self.layout - layout.menu('BSMAX_MT_animationtools', icon='ARMATURE_DATA') - layout.menu('BSMAX_MT_riggtools', icon='TOOL_SETTINGS') - layout.menu('BSMAX_MT_particletools', icon='MOD_PARTICLES') + layout.menu('BSMAX_MT_animation_tools', icon='ARMATURE_DATA') + layout.menu('BSMAX_MT_rigg_tools', icon='TOOL_SETTINGS') + layout.menu('BSMAX_MT_particle_tools', icon='MOD_PARTICLES') -def tools_menu(self, ctx): - self.layout.menu('BSMAX_MT_view3dtools') +def tools_menu(self, _): + self.layout.menu('BSMAX_MT_view3d_tools') def register_menu(): - bpy.types.BSMAX_MT_view3dtools.append(bsmax_tool_menu) + bpy.types.BSMAX_MT_view3d_tools.append(bsmax_tool_menu) bpy.types.VIEW3D_MT_editor_menus.append(tools_menu) def unregister_menu(): bpy.types.VIEW3D_MT_editor_menus.remove(tools_menu) - bpy.types.BSMAX_MT_view3dtools.remove(bsmax_tool_menu) + bpy.types.BSMAX_MT_view3d_tools.remove(bsmax_tool_menu) if __name__ == '__main__': diff --git a/tools/internal/particle/hair_guide.py b/tools/internal/particle/hair_guide.py index 1d1887b..1c7a4d7 100644 --- a/tools/internal/particle/hair_guide.py +++ b/tools/internal/particle/hair_guide.py @@ -231,7 +231,7 @@ def execute(self, ctx): class BsMax_MT_particle_tools(Menu): - bl_idname = "BSMAX_MT_particletools" + bl_idname = "BSMAX_MT_particle_tools" bl_label = "Particle" bl_context = "objectmode" @@ -247,13 +247,12 @@ def draw(self, ctx): -classes = ( +classes = { Particle_OT_Hair_Guides_From_Curve, Particle_OT_Hair_Guides_To_Curve, Particle_OT_Hair_Grap_Style, BsMax_MT_particle_tools -) - +} def register_hair_guide(): @@ -261,12 +260,10 @@ def register_hair_guide(): bpy.utils.register_class(c) - def unregister_hair_guide(): for c in classes: bpy.utils.unregister_class(c) - if __name__ == "__main__": register_hair_guide() \ No newline at end of file diff --git a/tools/internal/rigg/menu.py b/tools/internal/rigg/menu.py index c3facd2..7ff107a 100644 --- a/tools/internal/rigg/menu.py +++ b/tools/internal/rigg/menu.py @@ -18,7 +18,7 @@ class BsMax_MT_rigg_tools(bpy.types.Menu): bl_label = "Rigg" - bl_idname = 'BSMAX_MT_riggtools' + bl_idname = 'BSMAX_MT_rigg_tools' def draw(self, ctx): layout=self.layout @@ -37,7 +37,7 @@ def draw(self, ctx): def rigg_menu(self, ctx): - self.layout.menu('BSMAX_MT_riggtools') + self.layout.menu('BSMAX_MT_rigg_tools') def register_menu(): diff --git a/tools/internal/scene/crowds.py b/tools/internal/scene/crowds.py index 17635fd..22f2534 100644 --- a/tools/internal/scene/crowds.py +++ b/tools/internal/scene/crowds.py @@ -12,7 +12,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ -# 2024/02/13 +# 2024/06/04 import bpy @@ -338,59 +338,58 @@ def stuff_variation(self, ctx): class Crowds_TO_Clone_Refrenses(Operator): bl_idname = 'crowds.clone_refrenses' - bl_label = 'Clone Refrences (Crowds)' + bl_label = "Clone Refrences (Crowds)" bl_options = {'REGISTER', 'UNDO'} target: EnumProperty( items=[ ( 'COLLECTION', - 'Collection', - 'Clone active collection if contain object with Alembic' + "Collection", + "Clone active collection if contain object with Alembic" ), ( 'OBJECT', - 'Object', - 'Clone active object if contain Alembic data' + "Object", + "Clone active object if contain Alembic data" ) ], default = 'COLLECTION' - ) + ) # type: ignore method: EnumProperty( items=[ ( 'ABC', - 'Alembic', - 'Use Alembic clone and loop method' + "Alembic", + "Use Alembic clone and loop method" ) ], default='ABC' - ) + ) # type: ignore count: IntProperty( default=3, min=0, description="Number if clones want to create" - ) + ) # type: ignore length: IntProperty( default=100, min=1, description="Length of Alembic cache file" - ) + ) # type: ignore speedVariation: FloatProperty( default=0.1, min=0, description="Variation of Alembic play faster 0 => 1x speed" - ) + ) # type: ignore - # @classmethod # def poll(self, ctx): # if ctx.area.type == 'VIEW_3D': # return ctx.active_object # return False - def draw(self, ctx): + def draw(self, _): layout = self.layout layout.prop(self, 'target') layout.prop(self, 'method') @@ -400,54 +399,52 @@ def draw(self, ctx): def execute(self, ctx): clone_refrences(self, ctx) - return{"FINISHED"} + return{'FINISHED'} - def invoke(self, ctx, event): + def invoke(self, ctx, _): return ctx.window_manager.invoke_props_dialog(self) class Crowds_TO_Loop_Refrenses(Operator): bl_idname = 'crowds.loop_refrenses' - bl_label = 'Loop Refrences (Crowds)' + bl_label = "Loop Refrences (Crowds)" bl_options = {'REGISTER', 'UNDO'} target: EnumProperty( items=[ ( 'COLLECTION', - 'Collection', - 'Loop and random time offset grouped collection children' + "Collection", + "Loop and random time offset grouped collection children" ), ( 'OBJECT', - 'Object', - 'Loop and random time offset selected objects sepratly' + "Object", + "Loop and random time offset selected objects sepratly" ) ], default = 'COLLECTION' - ) + ) # type: ignore method: EnumProperty( - items=[ - ('ABC', 'Alembic', '') - ], + items=[('ABC', "Alembic", "Alembic")], default='ABC' - ) + ) # type: ignore length: IntProperty( default=100, min=1, description="" - ) + ) # type: ignore startVariation: FloatProperty( default=1, min=0, description="" - ) + ) # type: ignore speedVariation: FloatProperty( default=0.1, min=0, description="" - ) + ) # type: ignore # @classmethod # def poll(self, ctx): @@ -473,29 +470,29 @@ def invoke(self, ctx, event): class Crowds_TO_Stuff_Variation(Operator): bl_idname = 'crowds.stuff_variation' - bl_label = 'Stuff Variation (Crowds)' + bl_label = "Stuff Variation (Crowds)" bl_options = {'REGISTER', 'UNDO'} method: EnumProperty( items=[ ( 'HIDE', - 'Hide', - 'Randomly hide unhide parts of sub collection' + "Hide", + "Randomly hide unhide parts of sub collection" ), ( 'UNHIDE', - 'Unhide', - 'Unhide all objects under active colection' + "Unhide", + "Unhide all objects under active colection" ), ( 'DELETE', - 'Delete', - 'Delete all hiden object under active colection children' + "Delete", + "Delete all hiden object under active colection children" ) ], default='HIDE' - ) + ) # type: ignore # @classmethod # def poll(self, ctx): @@ -545,31 +542,31 @@ def draw(self, ctx): ) -def crowds_menu(self, ctx): +def crowds_menu(self, _): self.layout.menu('BSMAX_MT_crowdstools', icon='COMMUNITY') -classes = ( +classes = { Crowds_TO_Clone_Refrenses, Crowds_TO_Loop_Refrenses, Crowds_TO_Stuff_Variation, BsMax_MT_Crowds_Tools -) +} def register_crowds(): - for c in classes: - register_class(c) + for cls in classes: + register_class(cls) - bpy.types.BSMAX_MT_view3dtools.append(crowds_menu) + bpy.types.BSMAX_MT_view3d_tools.append(crowds_menu) def unregister_crowds(): - bpy.types.BSMAX_MT_view3dtools.remove(crowds_menu) + bpy.types.BSMAX_MT_view3d_tools.remove(crowds_menu) - for c in classes: - unregister_class(c) + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_crowds() \ No newline at end of file diff --git a/tools/internal/transform/transform_control.py b/tools/internal/transform/transform_control.py index b55821b..d5cd599 100644 --- a/tools/internal/transform/transform_control.py +++ b/tools/internal/transform/transform_control.py @@ -12,83 +12,81 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/06/04 import bpy from mathutils import Vector -from bpy.types import Operator, Menu +from bpy.types import Operator from bpy.props import BoolProperty +from bpy.utils import register_class, unregister_class from bsmax.bsmatrix import ( - matrix_from_elements, - matrix_to_array, - array_to_matrix - ) -from bsmax.actions import ( - freeze_transform, - copy_array_to_clipboard, - paste_array_from_clipboard - ) + matrix_from_elements, matrix_to_array, array_to_matrix +) +from bsmax.actions import ( + freeze_transform, copy_array_to_clipboard, paste_array_from_clipboard +) class Object_OT_Freeze_Transform(Operator): - """ Copy selected objects transform to Delta transform and - reset transform values - """ - bl_idname = "object.freeze_transform" + bl_idname = 'object.freeze_transform' bl_label = "Freeze Transform" + bl_description = "Copy selected objects transform to Delta transform and reset transform values" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - location: BoolProperty(name="Location", default=True) - rotation: BoolProperty(name="Rotation", default=True) - scale: BoolProperty(name="Scale", default=True) + location: BoolProperty(name="Location", default=True) # type: ignore + rotation: BoolProperty(name="Rotation", default=True) # type: ignore + scale: BoolProperty(name="Scale", default=True) # type: ignore def execute(self, ctx): freeze_transform(ctx.selected_objects, location=self.location, rotation=self.rotation, scale=self.scale) - return{"FINISHED"} - + return{'FINISHED'} class Object_OT_Transform_To_Zero(Operator): - """ Clare transform """ - bl_idname = "object.transform_to_zero" + bl_idname = 'object.transform_to_zero' bl_label = "Transform To Zero" + bl_description = "Clare transform" bl_options = {'REGISTER', 'UNDO', 'INTERNAL'} - location: BoolProperty(name="Location", default=True) - rotation: BoolProperty(name="Rotation", default=True) - scale: BoolProperty(name="Scale", default=True) + location: BoolProperty(name="Location", default=True) # type: ignore + rotation: BoolProperty(name="Rotation", default=True) # type: ignore + scale: BoolProperty(name="Scale", default=True) # type: ignore def execute(self, ctx): if ctx.mode == 'OBJECT': if self.location: bpy.ops.object.location_clear(clear_delta=False) + if self.rotation: bpy.ops.object.rotation_clear(clear_delta=False) + if self.scale: bpy.ops.object.scale_clear(clear_delta=False) elif ctx.mode == 'POSE': if self.location: bpy.ops.pose.loc_clear() + if self.rotation: bpy.ops.pose.rot_clear() + if self.scale: bpy.ops.pose.scale_clear() - return{"FINISHED"} - + return{'FINISHED'} class Object_OT_Transform_Copy(Operator): - """ Copy Transform to buffer """ - bl_idname = "object.transform_copy" + bl_idname = 'object.transform_copy' bl_label = "Copy Transform" + bl_description = "Copy Transform to buffer" bl_options = {'REGISTER'} @classmethod @@ -96,10 +94,11 @@ def poll(self, ctx): return ctx.mode == 'OBJECT' and ctx.active_object def execute(self, ctx): - copy_array_to_clipboard('BSMAXTRANSFORMCLIPBOARD', - matrix_to_array(ctx.object.matrix_world)) - return{"FINISHED"} - + copy_array_to_clipboard( + 'BSMAXTRANSFORMCLIPBOARD', + matrix_to_array(ctx.object.matrix_world) + ) + return{'FINISHED'} def get_clipboard_key(): @@ -110,11 +109,10 @@ def get_clipboard_key(): return None - class Object_OT_Transform_Paste(Operator): - """ Paste transform from buffer """ - bl_idname = "object.transform_paste" + bl_idname = 'object.transform_paste' bl_label = "Paste Transform" + bl_description = "Paste transform from buffer." bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -134,70 +132,58 @@ def execute(self, ctx): ctx.object.matrix_world = matrix_world elif key == "BSMAXTRANSFORMCLIPBOARDV2": - lines = (bpy.context.window_manager.clipboard).splitlines() - cbData = [float(f) for f in lines[1].split(",")] + lines = (ctx.window_manager.clipboard).splitlines() + cb_data = [float(f) for f in lines[1].split(',')] - location = Vector((cbData[0], cbData[1], cbData[2])) - rotation = Vector((cbData[3], cbData[4], cbData[5])) - scale = Vector((cbData[6], cbData[7], cbData[8])) + location = Vector((cb_data[0], cb_data[1], cb_data[2])) + rotation = Vector((cb_data[3], cb_data[4], cb_data[5])) + scale = Vector((cb_data[6], cb_data[7], cb_data[8])) matrix = matrix_from_elements( - location=location, - euler_rotation=rotation, - scale=scale + location=location, euler_rotation=rotation, scale=scale ) - ctx.object.matrix_world = matrix - # print(matrix) - - return{"FINISHED"} - - -#TODO for now is ok but need to move to better place if add more items -class Object_MT_Object_Copy(Menu): - bl_idname = "OBJECT_MT_object_copy" - bl_label = "Copy" - - def draw(self, ctx): - layout=self.layout - layout.operator("view3d.copybuffer", text="Object") - layout.operator("object.transform_copy", text="Transform") + ctx.object.matrix_world = matrix + + return{'FINISHED'} -class Object_MT_Object_Paste(Menu): - bl_idname = "OBJECT_MT_object_paste" - bl_label = "Paste" +def transform_copy_menu(self, _): + self.layout.operator( + 'object.transform_copy', text="Transform", icon='OBJECT_ORIGIN' + ) - def draw(self, ctx): - layout=self.layout - layout.operator("view3d.pastebuffer", text="Object") - layout.operator("object.transform_paste", text="Transform") +def transform_paste_menu(self, _): + self.layout.operator( + "object.transform_paste", text="Transform", icon='OBJECT_ORIGIN' + ) -classes = ( +classes = { Object_OT_Freeze_Transform, Object_OT_Transform_To_Zero, Object_OT_Transform_Copy, - Object_OT_Transform_Paste, - Object_MT_Object_Copy, - Object_MT_Object_Paste -) - + Object_OT_Transform_Paste +} def register_transform_control(): - for c in classes: - bpy.utils.register_class(c) - + for cls in classes: + register_class(cls) + + bpy.types.BSMAX_MT_view3d_copy.append(transform_copy_menu) + bpy.types.BSMAX_MT_view3d_paste.append(transform_paste_menu) def unregister_transform_control(): - for c in classes: - bpy.utils.unregister_class(c) + bpy.types.BSMAX_MT_view3d_copy.remove(transform_copy_menu) + bpy.types.BSMAX_MT_view3d_paste.remove(transform_paste_menu) + for cls in classes: + unregister_class(cls) -if __name__ == "__main__": +if __name__ == '__main__': register_transform_control() \ No newline at end of file diff --git a/tools/internal/transform/zoom_extended.py b/tools/internal/transform/zoom_extended.py index ca6bd79..d7ef3f3 100644 --- a/tools/internal/transform/zoom_extended.py +++ b/tools/internal/transform/zoom_extended.py @@ -12,62 +12,73 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . ############################################################################ +# 2024/06/04 import bpy - from mathutils import Matrix - from bpy.types import Operator +from bpy.utils import register_class, unregister_class class View3d_OT_HomeView(Operator): bl_idname = 'view3d.homeview' - bl_label = 'Home View' + bl_label = "Home View" + bl_description = "Home View" + bl_options = {'REGISTER', 'INTERNAL'} @classmethod def poll(self, ctx): return ctx.area.type == 'VIEW_3D' def execute(self, ctx): - homeview = (( 0.4100,0.9120,-0.0133,0),(-0.4017,0.1936,0.8950,-1.9045), - ( 0.8188,-0.3617,0.4458,-17.9866),( 0,0,0,1)) + homeview = ( + (0.4100, 0.9120, -0.0133, 0), + (-0.4017, 0.1936, 0.8950, -1.9045), + (0.8188, -0.3617, 0.4458, -17.9866), + (0, 0, 0, 1) + ) ctx.area.spaces.active.region_3d.view_matrix = Matrix(homeview) - # self.report({'OPERATOR'},'bpy.ops.view3d.homeview()') return{'FINISHED'} - class View3d_OT_Zoom_Extended(Operator): bl_idname = 'view3d.zoom_extended' - bl_label = 'Zoom Extended' + bl_label = "Zoom Extended" + bl_description = "Zoom Extended" + bl_options = {'REGISTER', 'INTERNAL'} @classmethod def poll(self, ctx): return ctx.area.type == 'VIEW_3D' def execute(self, ctx): + view3d = bpy.ops.view3d + ops_obj = bpy.ops.object if ctx.mode == 'OBJECT': - if len(ctx.scene.objects) == 0: - bpy.ops.view3d.homeview('INVOKE_DEFAULT') - elif len(ctx.selected_objects) == 0: - bpy.ops.view3d.view_all(use_all_regions=False,center=False) + if not ctx.scene.objects: + view3d.homeview('INVOKE_DEFAULT') + + elif not ctx.selected_objects: + view3d.view_all(use_all_regions=False,center=False) + else: - bpy.ops.view3d.view_selected(use_all_regions=False) + view3d.view_selected(use_all_regions=False) + elif ctx.mode == 'EDIT_ARMATURE': - if len(ctx.selected_bones) == 0: - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.view3d.view_selected(use_all_regions=False) - bpy.ops.object.mode_set(mode='EDIT') + if not ctx.selected_bones: + ops_obj.mode_set(mode='OBJECT') + view3d.view_selected(use_all_regions=False) + ops_obj.mode_set(mode='EDIT') + else: - bpy.ops.view3d.view_selected(use_all_regions=False) + view3d.view_selected(use_all_regions=False) + else: - bpy.ops.view3d.view_selected(use_all_regions=False) + view3d.view_selected(use_all_regions=False) - # self.report({'OPERATOR'},'bpy.ops.view3d.zoom_extended()') return{'FINISHED'} - class Node_OT_Zoom_Extended(Operator): bl_idname = 'node.zoom_extended' bl_label = 'Zoom Extended' @@ -77,35 +88,33 @@ def poll(self, ctx): return ctx.area.type == 'NODE_EDITOR' def execute(self, ctx): - if len(ctx.selected_nodes) > 0: - bpy.ops.node.view_selected('INVOKE_DEFAULT') + node = bpy.ops.node + if ctx.selected_nodes: + node.view_selected('INVOKE_DEFAULT') else: try: - bpy.ops.node.view_all('INVOKE_DEFAULT') + node.view_all('INVOKE_DEFAULT') except: pass - return{'FINISHED'} + return{'FINISHED'} -classes = ( +classes = { View3d_OT_HomeView, View3d_OT_Zoom_Extended, Node_OT_Zoom_Extended -) - +} def register_zoom_extended(): - for c in classes: - bpy.utils.register_class(c) - + for cls in classes: + register_class(cls) def unregister_zoom_extended(): - for c in classes: - bpy.utils.unregister_class(c) - + for cls in classes: + unregister_class(cls) if __name__ == '__main__':