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__':