__init__.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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. schema_nodes_ui,
  12. menu_classes,
  13. )
  14. from .ops_generate_tree import GenerateMantisTree
  15. from .utilities import i_o
  16. from .utilities.utilities import prRed
  17. from .base_definitions import (MANTIS_VERSION_MAJOR,
  18. MANTIS_VERSION_MINOR,
  19. MANTIS_VERSION_SUB)
  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 .dev_helpers.visualize import MantisVisualizeNode, MantisVisualizeOutput, MantisVisualizeTree
  46. classes.extend([MantisVisualizeTree, MantisVisualizeNode, MantisVisualizeOutput, ])
  47. DOERROR = False
  48. if environ.get("DOERROR"):
  49. DOERROR = True
  50. DOVERSIONING = True
  51. if environ.get("DOVERSIONING") in ["0", "False", "false", "off"]:
  52. DOVERSIONING = False
  53. import nodeitems_utils
  54. from nodeitems_utils import NodeCategory, NodeItem
  55. class MantisNodeCategory(NodeCategory):
  56. @classmethod
  57. def poll(cls, context):
  58. return (context.space_data.tree_type in ['MantisTree', 'SchemaTree'])
  59. class SchemaNodeCategory(NodeCategory):
  60. @classmethod
  61. def poll(cls, context):
  62. return (context.space_data.path[len(context.space_data.path)-1].node_tree.bl_idname == 'SchemaTree')
  63. class MantisGroupCategory(NodeCategory):
  64. @classmethod
  65. def poll(cls, context):
  66. return (context.space_data.path[len(context.space_data.path)-1].node_tree.bl_idname in ['MantisTree'] and len(context.space_data.path)>1)
  67. input_category=[
  68. NodeItem("InputFloatNode"),
  69. NodeItem("InputVectorNode"),
  70. NodeItem("InputBooleanNode"),
  71. NodeItem("InputBooleanThreeTupleNode"),
  72. NodeItem("InputStringNode"),
  73. NodeItem("InputIntNode"),
  74. NodeItem("InputMatrixNode"),
  75. NodeItem("InputWidget"),
  76. NodeItem("InputExistingGeometryObject"),
  77. NodeItem("xFormGetBone"),
  78. NodeItem("InputExistingGeometryData"),
  79. NodeItem("UtilityDeclareCollections"),
  80. NodeItem("InputThemeBoneColorSets"),
  81. NodeItem("InputColorSetPallete"),
  82. ]
  83. link_transform_category = [
  84. NodeItem("LinkCopyLocation"),
  85. NodeItem("LinkCopyRotation"),
  86. NodeItem("LinkCopyScale"),
  87. NodeItem("LinkCopyTransforms"),
  88. NodeItem("LinkLimitLocation"),
  89. NodeItem("LinkLimitRotation"),
  90. NodeItem("LinkLimitScale"),
  91. NodeItem("LinkLimitDistance"),
  92. NodeItem("LinkTransformation"),
  93. NodeItem("LinkGeometryAttribute"),
  94. NodeItem("LinkFloor"),
  95. ]
  96. link_tracking_category = [
  97. NodeItem("LinkInverseKinematics"),
  98. NodeItem("LinkSplineIK"),
  99. NodeItem("LinkStretchTo"),
  100. NodeItem("LinkDampedTrack"),
  101. NodeItem("LinkLockedTrack"),
  102. NodeItem("LinkTrackTo"),
  103. NodeItem("LinkShrinkWrap"),
  104. ]
  105. link_relationship_category = [
  106. NodeItem("linkInherit"),
  107. NodeItem("LinkInheritConstraint"),
  108. NodeItem("LinkArmature"),
  109. ]
  110. deformer_category=[NodeItem(cls.bl_idname) for cls in deformer_nodes_ui.TellClasses()]
  111. xForm_category = [
  112. NodeItem("xFormGeometryObject"),
  113. NodeItem("xFormBoneNode"),
  114. NodeItem("xFormArmatureNode"),
  115. NodeItem("xFormObjectInstance"),
  116. NodeItem("xFormCurvePin"),
  117. ]
  118. driver_category = [
  119. NodeItem("UtilityCustomProperty"),
  120. NodeItem("LinkDrivenParameter"),
  121. NodeItem("UtilityFCurve"),
  122. NodeItem("UtilityBoneProperties"),
  123. NodeItem("UtilityDriverVariable"),
  124. NodeItem("UtilitySwitch"),
  125. NodeItem("UtilityDriver"),
  126. NodeItem("UtilityKeyframe"),
  127. ]
  128. geometry_category = [
  129. NodeItem("GeometryCirclePrimitive"),
  130. NodeItem("GeometryLattice"),
  131. ]
  132. utility_category = [
  133. NodeItem("MathStaticInt"),
  134. NodeItem("MathStaticFloat"),
  135. NodeItem("MathStaticVector"),
  136. NodeItem("UtilityCatStrings"),
  137. NodeItem("UtilityCollectionJoin"),
  138. NodeItem("UtilityCollectionHierarchy"),
  139. NodeItem("UtilityGeometryOfXForm"),
  140. NodeItem("UtilityNameOfXForm"),
  141. NodeItem("UtilityCombineThreeBool"),
  142. NodeItem("UtilityCombineVector"),
  143. NodeItem("UtilityIntToString"),
  144. NodeItem("UtilityArrayGet"),
  145. NodeItem("UtilityArrayLength"),
  146. NodeItem("UtilityChoose"),
  147. NodeItem("UtilityCompare"),
  148. NodeItem("UtilityPrint"),
  149. NodeItem("UtilitySeparateVector"),
  150. NodeItem("UtilityGetNearestFactorOnCurve"),
  151. NodeItem("UtilityKDChoosePoint"),
  152. NodeItem("UtilityKDChooseXForm"),
  153. ]
  154. matrix_category = [
  155. NodeItem("UtilityMetaRig"),
  156. NodeItem("UtilityMatrixFromCurve"),
  157. NodeItem("UtilityMatricesFromCurve"),
  158. NodeItem("UtilityNumberOfCurveSegments"),
  159. NodeItem("UtilityMatrixFromCurveSegment"),
  160. NodeItem("UtilityPointFromCurve"),
  161. NodeItem("UtilityGetCurvePoint"),
  162. NodeItem("UtilityNumberOfSplines"),
  163. NodeItem("UtilityPointFromBoneMatrix"),
  164. NodeItem("UtilitySetBoneLength"),
  165. NodeItem("UtilityGetBoneLength"),
  166. NodeItem("UtilityBoneMatrixHeadTailFlip"),
  167. NodeItem("UtilityMatrixSetLocation"),
  168. NodeItem("UtilityMatrixFromXForm"),
  169. NodeItem("UtilityAxesFromMatrix"),
  170. NodeItem("UtilityMatrixTransform"),
  171. NodeItem("UtilityMatrixInvert"),
  172. NodeItem("UtilityMatrixCompose"),
  173. NodeItem("UtilityMatrixAlignRoll"),
  174. NodeItem("UtilityTransformationMatrix"),
  175. NodeItem("UtilitySetBoneMatrixTail"),
  176. ]
  177. groups_category = [
  178. NodeItem("MantisNodeGroup"),
  179. NodeItem("MantisSchemaGroup"),
  180. ]
  181. group_interface_category = [
  182. NodeItem("NodeGroupInput"),
  183. NodeItem("NodeGroupOutput"),
  184. ]
  185. node_categories = [
  186. # identifier, label, items list
  187. MantisNodeCategory('INPUT', "Input", items=input_category),
  188. MantisNodeCategory('LINK_TRANSFORM', "Link (Transform)", items=link_transform_category),
  189. MantisNodeCategory('LINK_TRACKING', "Link (Tracking)", items=link_tracking_category),
  190. MantisNodeCategory('LINK_RELATIONSHIP', "Link (Inheritance)", items=link_relationship_category),
  191. MantisNodeCategory('DEFORMER', "Deformer", items=deformer_category),
  192. MantisNodeCategory('XFORM', "Transform", items=xForm_category),
  193. MantisNodeCategory('DRIVER', "Driver", items=driver_category),
  194. MantisNodeCategory('GEOMETRY', "Geometry", items =geometry_category),
  195. MantisNodeCategory('UTILITIES', "Utility", items=utility_category),
  196. MantisNodeCategory('MATRIX', "Matrix", items=matrix_category),
  197. MantisNodeCategory('GROUPS', "Groups", items=groups_category),
  198. MantisGroupCategory('GROUP_INTERFACE', "Group In/Out", items=group_interface_category),
  199. ]
  200. schema_category=[NodeItem(cls.bl_idname) for cls in schema_nodes_ui.TellClasses()]
  201. schema_categories = [
  202. SchemaNodeCategory('SCHEMA_SCHEMA', "Schema", items=schema_category),
  203. ]
  204. import bpy
  205. def init_keymaps():
  206. kc = bpy.context.window_manager.keyconfigs.addon
  207. km = kc.keymaps.new(name="Node Generic", space_type='NODE_EDITOR')
  208. kmi = [
  209. # Normal operation
  210. km.keymap_items.new("mantis.group_nodes", 'G', 'PRESS', ctrl=True),
  211. km.keymap_items.new("mantis.edit_group", 'TAB', 'PRESS'),
  212. km.keymap_items.new("mantis.execute_node_tree", 'E', 'PRESS'),
  213. km.keymap_items.new("mantis.mute_node", 'M', 'PRESS'),
  214. km.keymap_items.new("mantis.nodes_cleanup", "C", 'PRESS', shift=True,),
  215. # Testing
  216. km.keymap_items.new("mantis.query_sockets", 'Q', 'PRESS'),
  217. km.keymap_items.new("mantis.test_operator", 'T', 'PRESS'),
  218. km.keymap_items.new("mantis.visualize_output", 'V', 'PRESS'),
  219. # Saving, Loading, Reloading, etc.
  220. km.keymap_items.new("mantis.export_save_choose", "S", 'PRESS', alt=True,),
  221. km.keymap_items.new("mantis.export_save_as", "S", 'PRESS', alt=True, shift=True),
  222. km.keymap_items.new("mantis.reload_tree", "R", 'PRESS', alt=True,),
  223. km.keymap_items.new("mantis.import_tree", "O", 'PRESS', ctrl=True,),
  224. ]
  225. return km, kmi
  226. addon_keymaps = []
  227. # handlers! these have to be persistent
  228. from bpy.app.handlers import persistent
  229. from .base_definitions import hash_tree
  230. @persistent
  231. def update_handler(scene):
  232. # return
  233. context=bpy.context
  234. if context.space_data:
  235. if not hasattr(context.space_data, "path"):
  236. return
  237. trees = [p.node_tree for p in context.space_data.path]
  238. if not trees: return
  239. if (node_tree := trees[0]).bl_idname in ['MantisTree']:
  240. if node_tree.is_exporting:
  241. return
  242. if node_tree.prevent_next_exec : pass
  243. elif node_tree.do_live_update and not (node_tree.is_executing):
  244. node_tree.update_tree(context)
  245. @persistent
  246. def execute_handler(scene):
  247. context = bpy.context
  248. if context.space_data:
  249. if not hasattr(context.space_data, "path"):
  250. return
  251. trees = [p.node_tree for p in context.space_data.path]
  252. if not trees: return
  253. if (node_tree := trees[0]).bl_idname in ['MantisTree']:
  254. # check here instead of in execute_tree because these values can be
  255. # modified at weird times and checking from the handler is more consistent
  256. if ( node_tree.tree_valid) and ( node_tree.do_live_update ):
  257. node_tree.execute_tree(context)
  258. node_tree.tree_valid=False
  259. from .versioning import versioning_tasks
  260. def node_version_update(node, do_once):
  261. from .xForm_nodes_ui import xFormNode
  262. for bl_idname, task, required_kwargs in versioning_tasks:
  263. arg_map = {}
  264. if 'node' in required_kwargs:
  265. arg_map['node']=node
  266. if 'tree' in required_kwargs or 'node_tree' in required_kwargs:
  267. arg_map['tree']=node.id_data; arg_map['node_tree']=node.id_data
  268. # we'll match all, or categories, or specific bl_idname as needed.
  269. if ('ALL' in bl_idname) or \
  270. ('XFORM' in bl_idname) and isinstance(node, xFormNode) or\
  271. node.bl_idname in bl_idname:
  272. if do_once:
  273. print (f"Updating tree {node.id_data.name} to "
  274. f"{MANTIS_VERSION_MAJOR}.{MANTIS_VERSION_MINOR}.{MANTIS_VERSION_SUB}")
  275. task(**arg_map)
  276. def do_version_update(node_tree):
  277. # set updating status for dynamic nodes to prevent bugs in socket remapping
  278. do_once = True
  279. for node in node_tree.nodes:
  280. if hasattr(node, 'is_updating'):
  281. node.is_updating = True
  282. # start by doing tree versioning tasks
  283. for affected_bl_idnames, task, required_kwargs in versioning_tasks:
  284. if node_tree.bl_idname not in affected_bl_idnames: continue # this is a node task.
  285. arguments = {}
  286. if 'tree' in required_kwargs:
  287. arguments['tree']=node_tree
  288. task(**arguments)
  289. # run the updates that have no prerequisites
  290. for node in node_tree.nodes:
  291. node_version_update(node, do_once)
  292. do_once = False
  293. # NOTE: if future versoning tasks have prerequisites, resolve them here and update again
  294. # reset the updating status for dynamic nodes
  295. for node in node_tree.nodes:
  296. if hasattr(node, 'is_updating'):
  297. node.is_updating = False
  298. # increment the version at the end
  299. node_tree.mantis_version[0] = MANTIS_VERSION_MAJOR
  300. node_tree.mantis_version[1] = MANTIS_VERSION_MINOR
  301. node_tree.mantis_version[2] = MANTIS_VERSION_SUB
  302. @persistent
  303. def version_update_handler(filename):
  304. for node_tree in bpy.data.node_groups: # ensure it can update again after file load.
  305. if node_tree.bl_idname in ["MantisTree", "SchemaTree"]:
  306. node_tree.is_exporting=False; node_tree.is_executing=False
  307. for node_tree in bpy.data.node_groups:
  308. if node_tree.bl_idname in ["MantisTree", "SchemaTree"]:
  309. if DOVERSIONING == False:
  310. version = node_tree.mantis_version
  311. print (f"{node_tree.name}: v{version[0], version[1], version[2]}")
  312. elif (node_tree.mantis_version[0] < MANTIS_VERSION_MAJOR) or \
  313. (node_tree.mantis_version[1] < MANTIS_VERSION_MINOR) or \
  314. (node_tree.mantis_version[2] < MANTIS_VERSION_SUB):
  315. do_version_update(node_tree)
  316. @persistent
  317. def autoload_components(filename):
  318. # this should not be blocking or slow!
  319. print("Auto-loading components")
  320. from os import path as os_path
  321. from .utilities.utilities import get_component_library_items, get_default_collection
  322. from .utilities.i_o import do_import
  323. from .preferences import get_bl_addon_object
  324. import json
  325. from bpy import context, data
  326. bl_addon_object = get_bl_addon_object()
  327. unlink_curves = True
  328. unlink_armatures = True
  329. if data.collections.get(bl_addon_object.preferences.CurveDefaultCollection):
  330. unlink_curves=False
  331. if data.collections.get(bl_addon_object.preferences.MetaArmatureDefaultCollection):
  332. unlink_armatures=False
  333. base_path = bl_addon_object.preferences.ComponentsAutoLoadFolder
  334. components = get_component_library_items(path='AUTOLOAD')
  335. for autoload_component in components:
  336. path = os_path.join(base_path, autoload_component[0])
  337. with open(path, 'r', encoding='utf-8') as f:
  338. json_data = json.load(f)
  339. do_import(json_data, context,
  340. search_multi_files=True, filepath=path,
  341. skip_existing=True)
  342. # let's get the node trees and assign a fake user
  343. for tree_name in json_data.keys():
  344. tree = data.node_groups.get(tree_name)
  345. tree.use_fake_user = True
  346. # now we need to unlink the collections, and add fake users to them
  347. curves_collection = get_default_collection(collection_type="CURVE")
  348. armature_collection = get_default_collection(collection_type="ARMATURE")
  349. if unlink_curves and (curves_collection := data.collections.get(
  350. bl_addon_object.preferences.CurveDefaultCollection)):
  351. context.scene.collection.children.unlink(curves_collection)
  352. if unlink_armatures and (armature_collection := data.collections.get(
  353. bl_addon_object.preferences.MetaArmatureDefaultCollection)):
  354. context.scene.collection.children.unlink(armature_collection)
  355. # This has to be done after file load because the file load clears the timers
  356. @persistent
  357. def begin_selection_sync_timer(filename):
  358. # And here I will register any timers used by Mantis.
  359. from .timers import timer_update_selection_sync, ENABLED, SELECTION_POLL_RATE
  360. from .preferences import get_bl_addon_object
  361. bl_mantis_addon = get_bl_addon_object()
  362. if bl_mantis_addon and bl_mantis_addon.preferences:
  363. ENABLED = bl_mantis_addon.preferences.EnableSelectionSync
  364. SELECTION_POLL_RATE = bl_mantis_addon.preferences.SelectionSyncPollRate
  365. bpy.app.timers.register(timer_update_selection_sync)
  366. # Build the reroute cache on file load
  367. @persistent
  368. def on_load_build_reroute_cache(filename):
  369. for tree in bpy.data.node_groups:
  370. if tree.bl_idname in ['MantisTree', 'SchemaTree']:
  371. # and I have to do this somewhere, this is the best place...
  372. tree.is_quitting = False # reset the quitting flag
  373. from .base_definitions import hash_tree, build_reroute_cache
  374. tree.hash = str(hash_tree(tree, recurse=False))
  375. build_reroute_cache(tree, tree.links)
  376. # I'll need to do some fiddling here when it comes time to try
  377. # and make rig definitions animatable.
  378. @persistent
  379. def on_animation_playback_pre_handler(scene,depsgraph):
  380. for t in bpy.data.node_groups:
  381. if t.bl_idname in ['MantisTree', 'SchemaTree']:
  382. t.is_executing = True
  383. @persistent
  384. def on_animation_playback_post_handler(scene,depsgraph):
  385. for t in bpy.data.node_groups:
  386. if t.bl_idname in ['MantisTree', 'SchemaTree']:
  387. t.is_executing = False
  388. @persistent
  389. def on_undo_post_handler(scene): # the undo will trigger a depsgraph update
  390. for t in bpy.data.node_groups: # so we enable prevent_next_exec.
  391. if t.bl_idname in ['MantisTree', 'SchemaTree']:
  392. t.prevent_next_exec = True
  393. t.hash_recursive=""
  394. # set the tree to invalid to trigger a tree update
  395. # since the context data is wiped by an undo.
  396. @persistent
  397. def on_quit_handler(is_ui):
  398. # we'll let the node trees know they're quitting
  399. # this prevents them from spamming updates on file close.
  400. for tree in bpy.data.node_groups:
  401. if tree.bl_idname in ['MantisTree', 'SchemaTree']:
  402. tree.is_quitting=True
  403. from .menu_classes import (node_context_menu_draw, node_add_menu_draw,
  404. armature_add_menu_draw, import_menu_draw)
  405. from .socket_definitions import generate_custom_interface_types
  406. generated_classes = generate_custom_interface_types()
  407. classes.extend(generated_classes)
  408. def register():
  409. from bpy.utils import register_class
  410. for cls in classes:
  411. try:
  412. register_class(cls)
  413. except RuntimeError as e:
  414. prRed(f"Registration error for class: {cls.__name__}")
  415. raise e
  416. nodeitems_utils.register_node_categories('MantisNodeCategories', node_categories)
  417. nodeitems_utils.register_node_categories('SchemaNodeCategories', schema_categories)
  418. bpy.types.NODE_MT_context_menu.append(node_context_menu_draw)
  419. bpy.types.NODE_MT_add.append(node_add_menu_draw)
  420. bpy.types.VIEW3D_MT_armature_add.append(armature_add_menu_draw)
  421. bpy.types.TOPBAR_MT_file_import.append(import_menu_draw)
  422. km, kmi = init_keymaps()
  423. for k in kmi:
  424. k.active = True
  425. addon_keymaps.append((km, k))
  426. # add the handlers
  427. bpy.app.handlers.depsgraph_update_pre.insert(0, update_handler)
  428. bpy.app.handlers.depsgraph_update_post.insert(0, execute_handler)
  429. bpy.app.handlers.load_post.insert(0, begin_selection_sync_timer)
  430. bpy.app.handlers.load_post.insert(0, autoload_components)
  431. bpy.app.handlers.load_post.insert(0, version_update_handler)
  432. bpy.app.handlers.load_post.insert(0, on_load_build_reroute_cache)
  433. bpy.app.handlers.animation_playback_pre.insert(0, on_animation_playback_pre_handler)
  434. bpy.app.handlers.animation_playback_post.insert(0, on_animation_playback_post_handler)
  435. bpy.app.handlers.undo_post.insert(0, on_undo_post_handler)
  436. bpy.app.handlers.exit_pre.insert(0, on_quit_handler)
  437. # I'm adding mine in first to ensure other addons don't mess up mine
  438. # but I am a good citizen! so my addon won't mess up yours! probably...
  439. # Now it is time to pre-cache stuff for Mantis
  440. from .base_definitions import MantisNode
  441. mantis_classes = [cls for cls in classes if isinstance(cls, MantisNode)]
  442. for cls in mantis_classes:
  443. # Set up the property socket map cache
  444. cls.gen_property_socket_map()
  445. def unregister():
  446. bpy.types.NODE_MT_context_menu.remove(node_context_menu_draw)
  447. bpy.types.NODE_MT_add.remove(node_add_menu_draw)
  448. bpy.types.VIEW3D_MT_armature_add.remove(armature_add_menu_draw)
  449. bpy.types.TOPBAR_MT_file_import.remove(import_menu_draw)
  450. for tree in bpy.data.node_groups: # ensure it doesn't try to update while quitting.
  451. if tree.bl_idname in ['MantisTree, SchemaTree']:
  452. tree.is_exporting=True; tree.is_executing=True
  453. nodeitems_utils.unregister_node_categories('MantisNodeCategories')
  454. nodeitems_utils.unregister_node_categories('SchemaNodeCategories')
  455. from bpy.utils import unregister_class
  456. for cls in reversed(classes):
  457. unregister_class(cls)
  458. for km, kmi in addon_keymaps:
  459. km.keymap_items.remove(kmi)
  460. addon_keymaps.clear()