readtree.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. from .utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
  2. wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
  3. # what in the cuss is this horrible abomination??
  4. def class_for_mantis_prototype_node(prototype_node):
  5. """ This is a class which returns a class to instantiate for
  6. the given prototype node."""
  7. #from .node_container_classes import TellClasses
  8. from . import xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers
  9. classes = {}
  10. for module in [xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers]:
  11. for cls in module.TellClasses():
  12. classes[cls.__name__] = cls
  13. # I could probably do a string.replace() here
  14. # But I actually think this is a bad idea since I might not
  15. # want to use this name convention in the future
  16. # this is easy enough for now, may refactor.
  17. #
  18. # kek, turns out it was completely friggin' inconsistent already
  19. if prototype_node.bl_idname == 'xFormRootNode':
  20. return classes["xFormRoot"]
  21. elif prototype_node.bl_idname == 'xFormArmatureNode':
  22. return classes["xFormArmature"]
  23. elif prototype_node.bl_idname == 'xFormBoneNode':
  24. return classes["xFormBone"]
  25. elif prototype_node.bl_idname == 'xFormGeometryObject':
  26. return classes["xFormGeometryObject"]
  27. elif prototype_node.bl_idname == 'linkInherit':
  28. return classes["LinkInherit"]
  29. # BAD need to fix the above, bl_idname is not consistent
  30. # Copy's
  31. elif prototype_node.bl_idname == 'LinkCopyLocation':
  32. return classes["LinkCopyLocation"] # also bad
  33. elif prototype_node.bl_idname == 'LinkCopyRotation':
  34. return classes["LinkCopyRotation"]
  35. elif prototype_node.bl_idname == 'LinkCopyScale':
  36. return classes["LinkCopyScale"]
  37. elif prototype_node.bl_idname == 'LinkCopyTransforms':
  38. return classes["LinkCopyTransforms"]
  39. elif prototype_node.bl_idname == 'LinkTransformation':
  40. return classes["LinkTransformation"]
  41. # Limits
  42. elif prototype_node.bl_idname == 'LinkLimitLocation':
  43. return classes["LinkLimitLocation"]
  44. elif prototype_node.bl_idname == 'LinkLimitRotation':
  45. return classes["LinkLimitRotation"]
  46. elif prototype_node.bl_idname == 'LinkLimitScale':
  47. return classes["LinkLimitScale"]
  48. elif prototype_node.bl_idname == 'LinkLimitDistance':
  49. return classes["LinkLimitDistance"]
  50. # tracking
  51. elif prototype_node.bl_idname == 'LinkStretchTo':
  52. return classes["LinkStretchTo"]
  53. elif prototype_node.bl_idname == 'LinkDampedTrack':
  54. return classes["LinkDampedTrack"]
  55. elif prototype_node.bl_idname == 'LinkLockedTrack':
  56. return classes["LinkLockedTrack"]
  57. elif prototype_node.bl_idname == 'LinkTrackTo':
  58. return classes["LinkTrackTo"]
  59. # misc
  60. elif prototype_node.bl_idname == 'LinkInheritConstraint':
  61. return classes["LinkInheritConstraint"]
  62. # IK
  63. elif prototype_node.bl_idname == 'LinkInverseKinematics':
  64. return classes["LinkInverseKinematics"]
  65. elif prototype_node.bl_idname == 'LinkSplineIK':
  66. return classes["LinkSplineIK"]
  67. # utilities
  68. elif prototype_node.bl_idname == 'InputFloatNode':
  69. return classes["InputFloat"]
  70. elif prototype_node.bl_idname == 'InputVectorNode':
  71. return classes["InputVector"]
  72. elif prototype_node.bl_idname == 'InputBooleanNode':
  73. return classes["InputBoolean"]
  74. elif prototype_node.bl_idname == 'InputBooleanThreeTupleNode':
  75. return classes["InputBooleanThreeTuple"]
  76. elif prototype_node.bl_idname == 'InputRotationOrderNode':
  77. return classes["InputRotationOrder"]
  78. elif prototype_node.bl_idname == 'InputTransformSpaceNode':
  79. return classes["InputTransformSpace"]
  80. elif prototype_node.bl_idname == 'InputStringNode':
  81. return classes["InputString"]
  82. elif prototype_node.bl_idname == 'InputQuaternionNode':
  83. return classes["InputQuaternion"]
  84. elif prototype_node.bl_idname == 'InputQuaternionNodeAA':
  85. return classes["InputQuaternionAA"]
  86. elif prototype_node.bl_idname == 'InputMatrixNode':
  87. return classes["InputMatrix"]
  88. elif prototype_node.bl_idname == 'MetaRigMatrixNode':
  89. return classes["InputMatrix"]
  90. elif prototype_node.bl_idname == 'InputLayerMaskNode':
  91. return classes["InputLayerMask"]
  92. # geometry
  93. elif prototype_node.bl_idname == 'GeometryCirclePrimitive':
  94. return classes["CirclePrimitive"]
  95. # Deformers:
  96. elif prototype_node.bl_idname == 'DeformerArmature':
  97. return classes["DeformerArmature"]
  98. # every node before this point is not guarenteed to follow the pattern
  99. # but every node not checked above does follow the pattern.
  100. try:
  101. return classes[ prototype_node.bl_idname ]
  102. except KeyError:
  103. pass
  104. if prototype_node.bl_idname in [
  105. "NodeReroute",
  106. "NodeGroupInput",
  107. "NodeGroupOutput",
  108. "MantisNodeGroup",
  109. "NodeFrame",
  110. ]:
  111. return None
  112. prRed(prototype_node.bl_idname)
  113. raise RuntimeError("Failed to create node container for: %s" % prototype_node.bl_idname)
  114. return None
  115. # This is really, really stupid HACK
  116. def gen_nc_input_for_data(socket):
  117. # Class List #TODO deduplicate
  118. from . import xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers
  119. classes = {}
  120. for module in [xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers]:
  121. for cls in module.TellClasses():
  122. classes[cls.__name__] = cls
  123. #
  124. socket_class_map = {
  125. "MatrixSocket" : classes["InputMatrix"],
  126. "xFormSocket" : None,
  127. "xFormMultiSocket" : None,
  128. "RelationshipSocket" : classes["xFormRoot"], # world in
  129. "DeformerSocket" : classes["xFormRoot"], # world in
  130. "GeometrySocket" : classes["InputExistingGeometryData"],
  131. "EnableSocket" : classes["InputBoolean"],
  132. "HideSocket" : classes["InputBoolean"],
  133. #
  134. "DriverSocket" : None,
  135. "DriverVariableSocket" : None,
  136. "FCurveSocket" : None,
  137. "KeyframeSocket" : None,
  138. "LayerMaskInputSocket" : classes["InputLayerMask"],
  139. "LayerMaskSocket" : classes["InputLayerMask"],
  140. #
  141. "xFormParameterSocket" : None,
  142. "ParameterBoolSocket" : classes["InputBoolean"],
  143. "ParameterIntSocket" : classes["InputFloat"], #TODO: make an Int node for this
  144. "ParameterFloatSocket" : classes["InputFloat"],
  145. "ParameterVectorSocket" : classes["InputVector"],
  146. "ParameterStringSocket" : classes["InputString"],
  147. #
  148. "TransformSpaceSocket" : classes["InputTransformSpace"],
  149. "BooleanSocket" : classes["InputBoolean"],
  150. "BooleanThreeTupleSocket" : classes["InputBooleanThreeTuple"],
  151. "RotationOrderSocket" : classes["InputRotationOrder"],
  152. "QuaternionSocket" : classes["InputQuaternion"],
  153. "QuaternionSocketAA" : classes["InputQuaternionAA"],
  154. "IntSocket" : classes["InputFloat"],
  155. "StringSocket" : classes["InputString"],
  156. #
  157. "BoolUpdateParentNode" : classes["InputBoolean"],
  158. "IKChainLengthSocket" : classes["InputFloat"],
  159. "EnumInheritScale" : classes["InputString"],
  160. "EnumRotationMix" : classes["InputString"],
  161. "EnumRotationMixCopyTransforms" : classes["InputString"],
  162. "EnumMaintainVolumeStretchTo" : classes["InputString"],
  163. "EnumRotationStretchTo" : classes["InputString"],
  164. "EnumTrackAxis" : classes["InputString"],
  165. "EnumUpAxis" : classes["InputString"],
  166. "EnumLockAxis" : classes["InputString"],
  167. "EnumLimitMode" : classes["InputString"],
  168. "EnumYScaleMode" : classes["InputString"],
  169. "EnumXZScaleMode" : classes["InputString"],
  170. # Deformers
  171. "EnumSkinning" : classes["InputString"],
  172. #
  173. "FloatSocket" : classes["InputFloat"],
  174. "FloatFactorSocket" : classes["InputFloat"],
  175. "FloatPositiveSocket" : classes["InputFloat"],
  176. "FloatAngleSocket" : classes["InputFloat"],
  177. "VectorSocket" : classes["InputVector"],
  178. "VectorEulerSocket" : classes["InputVector"],
  179. "VectorTranslationSocket" : classes["InputVector"],
  180. "VectorScaleSocket" : classes["InputVector"],
  181. # Drivers
  182. "EnumDriverVariableType" : classes["InputString"],
  183. "EnumDriverVariableEvaluationSpace" : classes["InputString"],
  184. "EnumDriverRotationMode" : classes["InputString"],
  185. "EnumDriverType" : classes["InputString"],
  186. }
  187. return socket_class_map.get(socket.bl_idname, None)
  188. class DummyLink:
  189. #gonna use this for faking links to keep the interface consistent
  190. def __init__(self, from_socket, to_socket, nc_from=None, nc_to=None, original_from=None):
  191. self.from_socket = from_socket
  192. self.to_socket = to_socket
  193. self.nc_from = nc_from
  194. self.nc_to = nc_to
  195. if (original_from):
  196. self.original_from = original_from
  197. else:
  198. self.original_from = self.from_socket
  199. def __repr__(self):
  200. return(self.nc_from.__repr__()+":"+self.from_socket.name + " -> " + self.nc_to.__repr__()+":"+self.to_socket.name)
  201. # A fuction for getting to the end of a Reroute.
  202. def socket_seek(start_link, links):
  203. link = start_link
  204. while(link.from_socket):
  205. for newlink in links:
  206. if link.from_socket.node.inputs:
  207. if newlink.to_socket == link.from_socket.node.inputs[0]:
  208. link=newlink; break
  209. else:
  210. break
  211. return link.from_socket
  212. def clear_reroutes(links):
  213. kept_links, rerouted_starts = [], []
  214. rerouted = []
  215. all_links = links.copy()
  216. while(all_links):
  217. link = all_links.pop()
  218. to_cls = link.to_socket.node.bl_idname
  219. from_cls = link.from_socket.node.bl_idname
  220. reroute_classes = ["NodeReroute"]
  221. if (to_cls in reroute_classes and
  222. from_cls in reroute_classes):
  223. rerouted.append(link)
  224. elif (to_cls in reroute_classes and not
  225. from_cls in reroute_classes):
  226. rerouted.append(link)
  227. elif (from_cls in reroute_classes and not
  228. to_cls in reroute_classes):
  229. rerouted_starts.append(link)
  230. else:
  231. kept_links.append(link)
  232. for start in rerouted_starts:
  233. from_socket = socket_seek(start, rerouted)
  234. new_link = DummyLink(from_socket=from_socket, to_socket=start.to_socket, nc_from=None, nc_to=None)
  235. kept_links.append(new_link)
  236. return kept_links
  237. def reroute_common(nc, nc_to, all_nc):
  238. # we need to do this: go to the to-node
  239. # then reroute the link in the to_node all the way to the beginning
  240. # so that the number of links in "real" nodes is unchanged
  241. # then the links in the dummy nodes need to be deleted
  242. watch=False
  243. # if nc.signature[-1] == 'NodeGroupOutput': watch=True
  244. for inp_name, inp in nc.inputs.items():
  245. # assume each input socket only has one input for now
  246. if inp.is_connected:
  247. while (inp.links):
  248. in_link = inp.links.pop()
  249. from_nc = in_link.from_node
  250. from_socket = in_link.from_socket
  251. links = []
  252. from_links = from_nc.outputs[from_socket].links.copy()
  253. while(from_links):
  254. from_link = from_links.pop()
  255. if from_link == in_link:
  256. continue # DELETE the dummy node link
  257. links.append(from_link)
  258. from_nc.outputs[from_socket].links = links
  259. down = nc_to.outputs[inp_name]
  260. for downlink in down.links:
  261. downlink.from_node = from_nc
  262. downlink.from_socket = from_socket
  263. from_nc.outputs[from_socket].links.append(downlink)
  264. if hasattr(downlink.to_node, "reroute_links"):
  265. # Recurse!
  266. downlink.to_node.reroute_links(downlink.to_node, all_nc)
  267. def reroute_links_grp(nc, all_nc):
  268. nc_to = all_nc.get( ( *nc.signature, "NodeGroupInput") )
  269. reroute_common(nc, nc_to, all_nc)
  270. def reroute_links_grpout(nc, all_nc):
  271. nc_to = all_nc.get( ( *nc.signature[:-1],) )
  272. reroute_common(nc, nc_to, all_nc)
  273. def data_from_tree(base_tree, tree_path = [None], dummy_nodes = {}, all_nc = {}):
  274. from .node_container_common import NodeSocket
  275. from .internal_containers import DummyNode
  276. nc_dict = {}
  277. tree_path_names = [tree.name for tree in tree_path if hasattr(tree, "name")]
  278. all_child_ng = []
  279. tree = base_tree
  280. if tree_path[-1]:
  281. tree = tree_path[-1].node_tree
  282. # Start by looking through the nodes and making nc's where possible
  283. # store the groups, we'll process them soon.
  284. for np in tree.nodes:
  285. if (nc_cls := class_for_mantis_prototype_node(np)):
  286. nc = nc_cls( sig := (None, *tree_path_names, np.name) , base_tree)
  287. nc_dict[sig] = nc; all_nc[sig] = nc
  288. elif np.bl_idname in ["NodeGroupInput", "NodeGroupOutput"]: # make a Dummy Node
  289. # we only want ONE dummy in/out per tree_path, so use the bl_idname
  290. sig = (None, *tree_path_names, np.bl_idname)
  291. if nc_dict.get(sig):
  292. continue
  293. nc = DummyNode( signature=sig , base_tree=base_tree, prototype=np )
  294. nc_dict[sig] = nc; all_nc[sig] = nc#; dummy_nodes[sig] = nc
  295. # dummy_nodes[sig]=nc
  296. if np.bl_idname in ["NodeGroupOutput"]:
  297. nc.reroute_links = reroute_links_grpout
  298. dummy_nodes[sig]=nc
  299. elif np.bl_idname in ["MantisNodeGroup"]:
  300. # if we do this here, no duplicate links.
  301. nc = DummyNode( signature= (sig := (None, *tree_path_names, np.name) ), base_tree=base_tree, prototype=np )
  302. nc_dict[sig] = nc; all_nc[sig] = nc; dummy_nodes[sig] = nc
  303. nc.reroute_links = reroute_links_grp
  304. all_child_ng.append(np)
  305. # else:
  306. # prRed(np.bl_idname)
  307. # Then deal with the links in the current tree and the held_nodes.
  308. kept_links = clear_reroutes(list(tree.links))
  309. for link in kept_links:
  310. from_name = link.from_socket.node.name
  311. to_name = link.to_socket.node.name
  312. if link.from_socket.node.bl_idname in ["NodeGroupInput", "NodeGroupOutput"]:
  313. from_name = link.from_socket.node.bl_idname
  314. if link.to_socket.node.bl_idname in ["NodeGroupInput", "NodeGroupOutput"]:
  315. to_name = link.to_socket.node.bl_idname
  316. if link.to_socket.node.bl_idname in ["MantisNodeGroup"]:
  317. continue
  318. nc_from = nc_dict.get( tuple([None] + tree_path_names + [from_name]) )
  319. nc_to = nc_dict.get( tuple([None] + tree_path_names + [to_name]) )
  320. if (nc_from and nc_to):
  321. from_s, to_s = link.from_socket.name, link.to_socket.name
  322. if nc_to.node_type == "DUMMY": to_s = link.to_socket.identifier
  323. if nc_from.node_type == "DUMMY": from_s = link.from_socket.identifier
  324. connection = nc_from.outputs[from_s].connect(node=nc_to, socket=to_s)
  325. else:
  326. raise RuntimeError(wrapRed("Link not connected: %s -> %s in tree %s" % (from_name, to_name, tree_path_names[-1])))
  327. nc_from = None; nc_to = None #clear them, since we use the same variable names again
  328. # Now, descend into the Node Group
  329. for ng in all_child_ng:
  330. nc_to = nc_dict[(None, *tree_path_names, ng.name)]
  331. for inp in ng.inputs:
  332. # nc_to = nc_dict.get((None, *tree_path_names, ng.name))
  333. to_s = inp.identifier
  334. if not inp.is_linked:
  335. nc_cls = gen_nc_input_for_data(inp)
  336. print (inp)
  337. prRed(nc_cls)
  338. # at this point we also need to get the "Dummy Node" for
  339. # this node group.
  340. if (nc_cls):
  341. sig = ("MANTIS_AUTOGENERATED", *tree_path_names, ng.name, inp.name, inp.identifier)
  342. nc_from = nc_cls(sig, base_tree)
  343. # HACK HACK HACK
  344. nc_from.inputs = {}
  345. nc_from.outputs = {inp.name:NodeSocket(name = inp.name, node=nc_from)}
  346. from .node_container_common import get_socket_value
  347. nc_from.parameters = {inp.name:get_socket_value(inp)}
  348. # HACK HACK HACK
  349. nc_dict[sig] = nc_from; all_nc[sig] = nc_from
  350. from_s = inp.name
  351. else:
  352. prRed("No available auto-generated class for input %s:%s:%s" % (tree_path, ng.name, inp.name))
  353. else: # We need to handle the incoming connections
  354. for link in inp.links: #Usually there will only be 1
  355. from_socket = link.from_socket
  356. if (link.from_socket.node.bl_idname == "NodeReroute"):
  357. from_socket = socket_seek(link, list(tree.links))
  358. sig = tuple( [None] + tree_path_names +[from_socket.node.name])
  359. from_s = from_socket.name
  360. if (link.from_socket.node.bl_idname in ["NodeGroupInput"]):
  361. sig = tuple( [None] + tree_path_names +[from_socket.node.bl_idname])
  362. from_s = from_socket.identifier
  363. nc_from = nc_dict.get(sig)
  364. # this can be None. Why?
  365. prRed (sig)
  366. nc_from.outputs[from_s].connect(node=nc_to, socket=to_s)
  367. nc_from = None
  368. nc_to = None
  369. # Recurse!
  370. data_from_tree(base_tree, tree_path+[ng], dummy_nodes, all_nc)
  371. return dummy_nodes, all_nc
  372. def establish_node_connections(nc):
  373. # This is ugly bc it adds parameters to an object
  374. # but it's kinda necesary to do it after the fact; and it
  375. # wouldn't be ugly if I just initialized the parameter elsewhere
  376. connections, hierarchy_connections = [], []
  377. for socket in nc.outputs.values():
  378. for link in socket.links:
  379. connections.append(link.to_node)
  380. # this may catch custom properties... too bad.
  381. if link.from_socket in from_name_filter:
  382. continue
  383. if link.to_socket in to_name_filter:
  384. continue
  385. hierarchy_connections.append(link.to_node)
  386. nc.connected_to = connections
  387. nc.hierarchy_connections = hierarchy_connections
  388. if nc.node_type == 'DUMMY':
  389. nc.hierarchy_connections = []
  390. def insert_lazy_parents(nc):
  391. from .link_containers import LinkInherit
  392. from .node_container_common import NodeLink
  393. inherit_nc = None
  394. if nc.inputs["Relationship"].is_connected:
  395. link = nc.inputs["Relationship"].links[0]
  396. from_nc = link.from_node
  397. if from_nc.__class__.__name__ == "xFormRoot":
  398. return
  399. if from_nc.node_type in ["XFORM"] and link.from_socket in ["xForm Out"]:
  400. inherit_nc = LinkInherit(("MANTIS_AUTOGENERATED", *nc.signature[1:], "LAZY_INHERIT"), nc.base_tree)
  401. for from_link in from_nc.outputs["xForm Out"].links:
  402. if from_link.to_node == nc and from_link.to_socket == "Relationship":
  403. break # this is it
  404. from_link.to_node = inherit_nc; from_link.to_socket="Parent"
  405. links=[]
  406. while (nc.inputs["Relationship"].links):
  407. to_link = nc.inputs["Relationship"].links.pop()
  408. if to_link.from_node == from_nc and to_link.from_socket == "xForm Out":
  409. continue # don't keep this one
  410. links.append(to_link)
  411. nc.inputs["Relationship"].links=links
  412. link=NodeLink(from_node=inherit_nc, from_socket="Inheritance", to_node=nc, to_socket="Relationship")
  413. inherit_nc.inputs["Parent"].links.append(from_link)
  414. inherit_nc.parameters = {
  415. "Parent":None,
  416. "Inherit Rotation":True,
  417. "Inherit Scale":'FULL',
  418. "Connected":False,
  419. }
  420. # because the from node may have already been done.
  421. establish_node_connections(from_nc)
  422. establish_node_connections(inherit_nc)
  423. # and the inherit node never was
  424. return inherit_nc
  425. def parse_tree(base_tree, do_reroute=True):
  426. dummy_nodes, all_nc = data_from_tree(base_tree, tree_path = [None], dummy_nodes = {}, all_nc = {})
  427. if do_reroute:
  428. for sig, dummy in dummy_nodes.items():
  429. if (hasattr(dummy, "reroute_links")):
  430. dummy.reroute_links(dummy, all_nc)
  431. all_nc = list(all_nc.values()).copy()
  432. kept_nc = {}
  433. while (all_nc):
  434. nc = all_nc.pop()
  435. nc.fill_parameters()
  436. if (nc.node_type in ['XFORM']) and ("Relationship" in nc.inputs.keys()):
  437. new_nc = insert_lazy_parents(nc)
  438. if new_nc:
  439. kept_nc[new_nc.signature]=new_nc
  440. establish_node_connections(nc)
  441. if nc.connected_to == 0:
  442. continue
  443. if nc.node_type == 'DUMMY' and do_reroute:
  444. continue
  445. kept_nc[nc.signature]=nc
  446. # return {}
  447. return kept_nc
  448. from_name_filter = ["Driver", ]
  449. to_name_filter = [
  450. "Custom Object xForm Override",
  451. "Custom Object",
  452. "Deform Bones"
  453. ]
  454. def sort_tree_into_layers(nodes, context):
  455. from time import time
  456. from .node_container_common import (get_depth_lines,
  457. node_depth)
  458. # All this function needs to do is sort out the hierarchy and
  459. # get things working in order of their dependencies.
  460. roots, drivers = [], []
  461. start = time()
  462. for n in nodes.values():
  463. if n.node_type == 'DRIVER': drivers.append(n)
  464. # ugly but necesary to ensure that drivers are always connected.
  465. if not (hasattr(n, 'inputs')) or ( len(n.inputs) == 0):
  466. roots.append(n)
  467. elif (hasattr(n, 'inputs')):
  468. none_connected = True
  469. for inp in n.inputs.values():
  470. if inp.is_linked: none_connected = False
  471. if none_connected: roots.append(n)
  472. layers, nodes_heights = {}, {}
  473. #Possible improvement: unify roots if they represent the same data
  474. all_sorted_nodes = []
  475. for root in roots:
  476. nodes_heights[root.signature] = 0
  477. depth_lines = get_depth_lines(root)[0]
  478. for n in nodes.values():
  479. if n.signature not in (depth_lines.keys()):
  480. continue #belongs to a different root
  481. d = nodes_heights.get(n.signature, 0)
  482. if (new_d := node_depth(depth_lines[n.signature])) > d:
  483. d = new_d
  484. nodes_heights[n.signature] = d
  485. for k, v in nodes_heights.items():
  486. if (layer := layers.get(v, None)):
  487. layer.append(nodes[k]) # add it to the existing layer
  488. else: layers[v] = [nodes[k]] # or make a new layer with the node
  489. all_sorted_nodes.append(nodes[k]) # add it to the sorted list
  490. for n in nodes.values():
  491. if n not in all_sorted_nodes:
  492. for drv in drivers:
  493. if n in drv.connected_to:
  494. depth = nodes_heights[drv.signature] + 1
  495. nodes_heights[n.signature] = depth
  496. # don't try to push downstream deps up bc this
  497. # is a driver and it will be done in the
  498. # finalize pass anyway
  499. if (layer := layers.get(depth, None)):
  500. layer.append(n)
  501. else: layers[v] = [n]
  502. else:
  503. prRed(n)
  504. raise RuntimeError(wrapRed("Failed to depth-sort nodes (because of a driver-combine node?)"))
  505. #
  506. prGreen("Sorting depth for %d nodes finished in %s seconds" %
  507. (len(nodes), time() - start))
  508. keys = list(layers.keys())
  509. keys.sort()
  510. if (False): # True to print the layers
  511. for i in keys:
  512. # print_layer = [l_item for l_item in layers[i] if l_item.node_type in ["XFORM",]]# "LINK", "DRIVER"]]
  513. print_layer = [l_item for l_item in layers[i]]
  514. print(wrapGreen("%d: " % i), wrapWhite("%s" % print_layer))
  515. return layers
  516. def execute_tree(nodes, base_tree, context):
  517. import bpy
  518. from time import time
  519. from .node_container_common import GraphError
  520. start_time = time()
  521. original_active = context.view_layer.objects.active
  522. layers = sort_tree_into_layers(nodes, context)
  523. start_execution_time = time()
  524. # Execute the first pass (xForm, Utility) #
  525. for i in range(len(layers)):
  526. for node in layers[i]:
  527. if (node.node_type in ['XFORM', 'UTILITY']):
  528. try:
  529. node.bExecute(context)
  530. except Exception as e:
  531. prRed("Execution failed at %s" % node); raise e
  532. # Switch to Pose Mode #
  533. active = None
  534. switch_me = []
  535. for n in nodes.values():
  536. # if it is a armature, switch modes
  537. # total hack #kinda dumb
  538. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  539. try:
  540. ob = n.bGetObject()
  541. except KeyError: # for bones
  542. ob = None
  543. # TODO this will be a problem if and when I add mesh/curve stuff
  544. if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
  545. switch_me.append(ob)
  546. active = ob
  547. context.view_layer.objects.active = ob# need to have an active ob, not None, to switch modes.
  548. # we override selected_objects to prevent anyone else from mode-switching
  549. # TODO it's possible but unlikely that the user will try to run a
  550. # graph with no armature nodes in it.
  551. if (active):
  552. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  553. bpy.ops.object.mode_set(mode='POSE')
  554. # Execute second pass (Link, Driver) #
  555. for i in range(len(layers)):
  556. for n in layers[i]:
  557. # Now do the Link & Driver nodes during the second pass.
  558. if (n.node_type in ['LINK', 'DRIVER']):
  559. try:
  560. n.bExecute(context)
  561. except GraphError:
  562. pass
  563. except Exception as e:
  564. print (n); raise e
  565. # Finalize #
  566. for i in range(len(layers)):
  567. for node in layers[i]:
  568. if (hasattr(node, "bFinalize")):
  569. node.bFinalize(context)
  570. for n in nodes.values():
  571. # if it is a armature, switch modes
  572. # total hack #kinda dumb
  573. if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
  574. try:
  575. ob = n.bGetObject()
  576. except KeyError: # for bones
  577. ob = None
  578. # TODO this will be a problem if and when I add mesh/curve stuff
  579. if (hasattr(ob, 'mode') and ob.mode == 'POSE'):
  580. switch_me.append(ob)
  581. active = ob
  582. if (active):
  583. with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
  584. bpy.ops.object.mode_set(mode='OBJECT')
  585. prGreen("Executed Tree in %s seconds" % (time() - start_execution_time))
  586. prGreen("Finished executing tree in %f seconds" % (time() - start_time))
  587. if (original_active):
  588. context.view_layer.objects.active = original_active
  589. original_active.select_set(True)