__init__.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. from . import ( ops_nodegroup,
  2. ops_ui,
  3. base_definitions,
  4. socket_definitions,
  5. link_nodes_ui,
  6. xForm_nodes_ui,
  7. misc_nodes_ui,
  8. primitives_nodes_ui,
  9. deformer_nodes_ui,
  10. math_nodes_ui,
  11. i_o,
  12. schema_nodes_ui,
  13. menu_classes,
  14. )
  15. from .ops_generate_tree import GenerateMantisTree
  16. from .utilities import prRed
  17. MANTIS_VERSION_MAJOR=0
  18. MANTIS_VERSION_MINOR=12
  19. MANTIS_VERSION_SUB=9
  20. classLists = [module.TellClasses() for module in [
  21. link_nodes_ui,
  22. xForm_nodes_ui,
  23. misc_nodes_ui,
  24. socket_definitions,
  25. ops_nodegroup,
  26. ops_ui,
  27. primitives_nodes_ui,
  28. deformer_nodes_ui,
  29. math_nodes_ui,
  30. i_o,
  31. schema_nodes_ui,
  32. base_definitions,
  33. menu_classes,
  34. ]]
  35. classLists.append( [GenerateMantisTree] )
  36. #
  37. classes = []
  38. while (classLists):
  39. classes.extend(classLists.pop())
  40. interface_classes = []
  41. from .preferences import MantisPreferences
  42. classes.append(MantisPreferences)
  43. from os import environ
  44. if environ.get("ENABLEVIS"):
  45. from .visualize import MantisVisualizeNode, MantisVisualizeOutput, MantisVisualizeTree
  46. classes.extend([MantisVisualizeTree, MantisVisualizeNode, MantisVisualizeOutput, ])
  47. import nodeitems_utils
  48. from nodeitems_utils import NodeCategory, NodeItem
  49. class MantisNodeCategory(NodeCategory):
  50. @classmethod
  51. def poll(cls, context):
  52. return (context.space_data.tree_type in ['MantisTree', 'SchemaTree'])
  53. class SchemaNodeCategory(NodeCategory):
  54. @classmethod
  55. def poll(cls, context):
  56. return (context.space_data.path[len(context.space_data.path)-1].node_tree.bl_idname == 'SchemaTree')
  57. class MantisGroupCategory(NodeCategory):
  58. @classmethod
  59. def poll(cls, context):
  60. return (context.space_data.path[len(context.space_data.path)-1].node_tree.bl_idname in ['MantisTree'] and len(context.space_data.path)>1)
  61. input_category=[
  62. NodeItem("InputFloatNode"),
  63. NodeItem("InputVectorNode"),
  64. NodeItem("InputBooleanNode"),
  65. NodeItem("InputStringNode"),
  66. NodeItem("InputIntNode"),
  67. NodeItem("InputMatrixNode"),
  68. NodeItem("InputWidget"),
  69. NodeItem("InputExistingGeometryObject"),
  70. NodeItem("InputExistingGeometryData"),
  71. NodeItem("UtilityDeclareCollections"),
  72. NodeItem("InputThemeBoneColorSets"),
  73. NodeItem("InputColorSetPallete"),
  74. ]
  75. link_transform_category = [
  76. NodeItem("LinkCopyLocation"),
  77. NodeItem("LinkCopyRotation"),
  78. NodeItem("LinkCopyScale"),
  79. NodeItem("LinkCopyTransforms"),
  80. NodeItem("LinkLimitLocation"),
  81. NodeItem("LinkLimitScale"),
  82. NodeItem("LinkLimitRotation"),
  83. NodeItem("LinkLimitDistance"),
  84. NodeItem("LinkTransformation"),
  85. NodeItem("LinkFloor"),
  86. ]
  87. link_tracking_category = [
  88. NodeItem("LinkInverseKinematics"),
  89. NodeItem("LinkSplineIK"),
  90. NodeItem("LinkStretchTo"),
  91. NodeItem("LinkDampedTrack"),
  92. NodeItem("LinkLockedTrack"),
  93. NodeItem("LinkTrackTo"),
  94. ]
  95. link_relationship_category = [
  96. NodeItem("linkInherit"),
  97. NodeItem("LinkInheritConstraint"),
  98. NodeItem("LinkArmature"),
  99. ]
  100. deformer_category=[NodeItem(cls.bl_idname) for cls in deformer_nodes_ui.TellClasses()]
  101. xForm_category = [
  102. NodeItem("xFormGeometryObject"),
  103. NodeItem("xFormBoneNode"),
  104. NodeItem("xFormArmatureNode"),
  105. NodeItem("xFormObjectInstance"),
  106. NodeItem("xFormCurvePin"),
  107. ]
  108. driver_category = [
  109. NodeItem("LinkDrivenParameter"),
  110. NodeItem("UtilityFCurve"),
  111. NodeItem("UtilityBoneProperties"),
  112. NodeItem("UtilityDriverVariable"),
  113. NodeItem("UtilitySwitch"),
  114. NodeItem("UtilityDriver"),
  115. NodeItem("UtilityKeyframe"),
  116. ]
  117. geometry_category = [
  118. NodeItem("GeometryCirclePrimitive"),
  119. NodeItem("GeometryLattice"),
  120. ]
  121. utility_category = [
  122. NodeItem("MathStaticInt"),
  123. NodeItem("MathStaticFloat"),
  124. NodeItem("MathStaticVector"),
  125. NodeItem("UtilityCatStrings"),
  126. NodeItem("UtilityCollectionJoin"),
  127. NodeItem("UtilityCollectionHierarchy"),
  128. NodeItem("UtilityGeometryOfXForm"),
  129. NodeItem("UtilityNameOfXForm"),
  130. NodeItem("UtilityCombineThreeBool"),
  131. NodeItem("UtilityCombineVector"),
  132. NodeItem("UtilityIntToString"),
  133. NodeItem("UtilityArrayGet"),
  134. NodeItem("UtilityArrayLength"),
  135. NodeItem("UtilityChoose"),
  136. NodeItem("UtilityCompare"),
  137. NodeItem("UtilityPrint"),
  138. NodeItem("UtilitySeparateVector"),
  139. NodeItem("UtilityGetNearestFactorOnCurve"),
  140. NodeItem("UtilityKDChoosePoint"),
  141. NodeItem("UtilityKDChooseXForm"),
  142. ]
  143. matrix_category = [
  144. NodeItem("UtilityMetaRig"),
  145. NodeItem("UtilityMatrixFromCurve"),
  146. NodeItem("UtilityMatricesFromCurve"),
  147. NodeItem("UtilityNumberOfCurveSegments"),
  148. NodeItem("UtilityMatrixFromCurveSegment"),
  149. NodeItem("UtilityPointFromCurve"),
  150. NodeItem("UtilityGetCurvePoint"),
  151. NodeItem("UtilityNumberOfSplines"),
  152. NodeItem("UtilityPointFromBoneMatrix"),
  153. NodeItem("UtilitySetBoneLength"),
  154. NodeItem("UtilityGetBoneLength"),
  155. NodeItem("UtilityBoneMatrixHeadTailFlip"),
  156. NodeItem("UtilityMatrixSetLocation"),
  157. NodeItem("UtilityMatrixFromXForm"),
  158. NodeItem("UtilityAxesFromMatrix"),
  159. NodeItem("UtilityMatrixTransform"),
  160. NodeItem("UtilityMatrixInvert"),
  161. NodeItem("UtilityMatrixCompose"),
  162. NodeItem("UtilityMatrixAlignRoll"),
  163. NodeItem("UtilityTransformationMatrix"),
  164. NodeItem("UtilitySetBoneMatrixTail"),
  165. ]
  166. groups_category = [
  167. NodeItem("MantisNodeGroup"),
  168. NodeItem("MantisSchemaGroup"),
  169. ]
  170. group_interface_category = [
  171. NodeItem("NodeGroupInput"),
  172. NodeItem("NodeGroupOutput"),
  173. ]
  174. node_categories = [
  175. # identifier, label, items list
  176. MantisNodeCategory('INPUT', "Input", items=input_category),
  177. MantisNodeCategory('LINK_TRANSFORM', "Link (Transform)", items=link_transform_category),
  178. MantisNodeCategory('LINK_TRACKING', "Link (Tracking)", items=link_tracking_category),
  179. MantisNodeCategory('LINK_RELATIONSHIP', "Link (Inheritance)", items=link_relationship_category),
  180. MantisNodeCategory('DEFORMER', "Deformer", items=deformer_category),
  181. MantisNodeCategory('XFORM', "Transform", items=xForm_category),
  182. MantisNodeCategory('DRIVER', "Driver", items=driver_category),
  183. MantisNodeCategory('GEOMETRY', "Geometry", items =geometry_category),
  184. MantisNodeCategory('UTILITIES', "Utility", items=utility_category),
  185. MantisNodeCategory('MATRIX', "Matrix", items=matrix_category),
  186. MantisNodeCategory('GROUPS', "Groups", items=groups_category),
  187. MantisGroupCategory('GROUP_INTERFACE', "Group In/Out", items=group_interface_category),
  188. ]
  189. schema_category=[NodeItem(cls.bl_idname) for cls in schema_nodes_ui.TellClasses()]
  190. schema_categories = [
  191. SchemaNodeCategory('SCHEMA_SCHEMA', "Schema", items=schema_category),
  192. ]
  193. import bpy
  194. def init_keymaps():
  195. kc = bpy.context.window_manager.keyconfigs.addon
  196. km = kc.keymaps.new(name="Node Generic", space_type='NODE_EDITOR')
  197. kmi = [
  198. # Normal operation
  199. km.keymap_items.new("mantis.group_nodes", 'G', 'PRESS', ctrl=True),
  200. km.keymap_items.new("mantis.edit_group", 'TAB', 'PRESS'),
  201. km.keymap_items.new("mantis.execute_node_tree", 'E', 'PRESS'),
  202. km.keymap_items.new("mantis.mute_node", 'M', 'PRESS'),
  203. km.keymap_items.new("mantis.nodes_cleanup", "C", 'PRESS', shift=True,),
  204. # Testing
  205. km.keymap_items.new("mantis.query_sockets", 'Q', 'PRESS'),
  206. km.keymap_items.new("mantis.test_operator", 'T', 'PRESS'),
  207. km.keymap_items.new("mantis.visualize_output", 'V', 'PRESS'),
  208. # Saving, Loading, Reloading, etc.
  209. km.keymap_items.new("mantis.export_save_choose", "S", 'PRESS', alt=True,),
  210. km.keymap_items.new("mantis.export_save_as", "S", 'PRESS', alt=True, shift=True),
  211. km.keymap_items.new("mantis.reload_tree", "R", 'PRESS', alt=True,),
  212. km.keymap_items.new("mantis.import_tree", "O", 'PRESS', ctrl=True,),
  213. ]
  214. return km, kmi
  215. addon_keymaps = []
  216. # handlers! these have to be persistent
  217. from bpy.app.handlers import persistent
  218. from .base_definitions import hash_tree
  219. @persistent
  220. def update_handler(scene):
  221. # return
  222. context=bpy.context
  223. if context.space_data:
  224. if not hasattr(context.space_data, "path"):
  225. return
  226. trees = [p.node_tree for p in context.space_data.path]
  227. if not trees: return
  228. if (node_tree := trees[0]).bl_idname in ['MantisTree']:
  229. if node_tree.is_exporting:
  230. return
  231. if node_tree.prevent_next_exec : pass
  232. elif node_tree.do_live_update and not (node_tree.is_executing):
  233. node_tree.update_tree(context)
  234. @persistent
  235. def execute_handler(scene):
  236. context = bpy.context
  237. if context.space_data:
  238. if not hasattr(context.space_data, "path"):
  239. return
  240. trees = [p.node_tree for p in context.space_data.path]
  241. if not trees: return
  242. if (node_tree := trees[0]).bl_idname in ['MantisTree']:
  243. # check here instead of in execute_tree because these values can be
  244. # modified at weird times and checking from the handler is more consistent
  245. if ( node_tree.tree_valid) and ( node_tree.do_live_update ):
  246. node_tree.execute_tree(context)
  247. node_tree.tree_valid=False
  248. from .versioning import versioning_tasks
  249. def node_version_update(node):
  250. do_once = True
  251. for bl_idname, task, required_kwargs in versioning_tasks:
  252. arg_map = {}
  253. if 'node' in required_kwargs:
  254. arg_map['node']=node
  255. if 'node_tree' in required_kwargs:
  256. arg_map['node_tree']=node.id_data
  257. if bl_idname == 'ALL' or node.bl_idname in bl_idname:
  258. if do_once:
  259. print (f"Updating tree {node.id_data.name} to "
  260. f"{MANTIS_VERSION_MAJOR}.{MANTIS_VERSION_MINOR}.{MANTIS_VERSION_SUB}")
  261. do_once=False
  262. task(**arg_map)
  263. def do_version_update(node_tree):
  264. # set updating status for dynamic nodes to prevent bugs in socket remapping
  265. for node in node_tree.nodes:
  266. if hasattr(node, 'is_updating'):
  267. node.is_updating = True
  268. # run the updates that have no prerequisites
  269. for node in node_tree.nodes:
  270. node_version_update(node)
  271. # NOTE: if future versoning tasks have prerequisites, resolve them here and update again
  272. # reset the updating status for dynamic nodes
  273. for node in node_tree.nodes:
  274. if hasattr(node, 'is_updating'):
  275. node.is_updating = False
  276. # increment the version at the end
  277. node_tree.mantis_version[0] = MANTIS_VERSION_MAJOR
  278. node_tree.mantis_version[1] = MANTIS_VERSION_MINOR
  279. node_tree.mantis_version[2] = MANTIS_VERSION_SUB
  280. @persistent
  281. def version_update_handler(filename):
  282. for node_tree in bpy.data.node_groups: # ensure it can update again after file load.
  283. if node_tree.bl_idname in ["MantisTree", "SchemaTree"]:
  284. node_tree.is_exporting=False; node_tree.is_executing=False
  285. for node_tree in bpy.data.node_groups:
  286. if node_tree.bl_idname in ["MantisTree", "SchemaTree"]:
  287. if (node_tree.mantis_version[0] < MANTIS_VERSION_MAJOR) or \
  288. (node_tree.mantis_version[1] < MANTIS_VERSION_MINOR) or \
  289. (node_tree.mantis_version[2] < MANTIS_VERSION_SUB):
  290. do_version_update(node_tree)
  291. # I'll need to do some fiddling here when it comes time to try
  292. # and make rig definitions animatable.
  293. @persistent
  294. def on_animation_playback_pre_handler(scene,depsgraph):
  295. for t in bpy.data.node_groups:
  296. if t.bl_idname in ['MantisTree', 'SchemaTree']:
  297. t.is_executing = True
  298. @persistent
  299. def on_animation_playback_post_handler(scene,depsgraph):
  300. for t in bpy.data.node_groups:
  301. if t.bl_idname in ['MantisTree', 'SchemaTree']:
  302. t.is_executing = False
  303. @persistent
  304. def on_undo_post_handler(scene): # the undo will trigger a depsgraph update
  305. for t in bpy.data.node_groups: # so we enable prevent_next_exec.
  306. if t.bl_idname in ['MantisTree', 'SchemaTree']:
  307. t.prevent_next_exec = True
  308. t.hash=""
  309. # set the tree to invalid to trigger a tree update
  310. # since the context data is wiped by an undo.
  311. # Function to append submenu to context menu
  312. def node_context_menu_draw(self, context):
  313. layout = self.layout
  314. layout.separator() # Optional: Adds a separator before your submenu
  315. layout.operator_context = "INVOKE_DEFAULT"
  316. layout.operator("mantis.nodes_cleanup", text='Sort Selected Nodes')
  317. layout.operator("mantis.connect_nodes_to_input")
  318. layout.operator("mantis.select_nodes_of_type")
  319. # layout.menu('NODE_MT_context_menu_mantis')
  320. def register():
  321. from bpy.utils import register_class
  322. for cls in classes:
  323. try:
  324. register_class(cls)
  325. except RuntimeError as e:
  326. prRed(f"Registration error for class: {cls.__name__}")
  327. raise e
  328. nodeitems_utils.register_node_categories('MantisNodeCategories', node_categories)
  329. nodeitems_utils.register_node_categories('SchemaNodeCategories', schema_categories)
  330. bpy.types.NODE_MT_context_menu.append(node_context_menu_draw)
  331. km, kmi = init_keymaps()
  332. for k in kmi:
  333. k.active = True
  334. addon_keymaps.append((km, k))
  335. # add the handlers
  336. bpy.app.handlers.depsgraph_update_pre.insert(0, update_handler)
  337. bpy.app.handlers.depsgraph_update_post.insert(0, execute_handler)
  338. bpy.app.handlers.load_post.insert(0, version_update_handler)
  339. bpy.app.handlers.animation_playback_pre.insert(0, on_animation_playback_pre_handler)
  340. bpy.app.handlers.animation_playback_post.insert(0, on_animation_playback_post_handler)
  341. bpy.app.handlers.undo_post.insert(0, on_undo_post_handler)
  342. # I'm adding mine in first to ensure other addons don't mess up mine
  343. # but I am a good citizen! so my addon won't mess up yours! probably...
  344. def unregister():
  345. bpy.types.NODE_MT_context_menu.remove(node_context_menu_draw)
  346. for tree in bpy.data.node_groups: # ensure it doesn't try to update while quitting.
  347. if tree.bl_idname in ['MantisTree, SchemaTree']:
  348. tree.is_exporting=True; tree.is_executing=True
  349. nodeitems_utils.unregister_node_categories('MantisNodeCategories')
  350. nodeitems_utils.unregister_node_categories('SchemaNodeCategories')
  351. from bpy.utils import unregister_class
  352. for cls in reversed(classes):
  353. unregister_class(cls)
  354. for km, kmi in addon_keymaps:
  355. km.keymap_items.remove(kmi)
  356. addon_keymaps.clear()