|
|
@@ -1,641 +0,0 @@
|
|
|
-from .utilities import prRed, prGreen, prPurple, prWhite, prOrange, \
|
|
|
- wrapRed, wrapGreen, wrapPurple, wrapWhite, wrapOrange
|
|
|
-
|
|
|
-# what in the cuss is this horrible abomination??
|
|
|
-def class_for_mantis_prototype_node(prototype_node):
|
|
|
- """ This is a class which returns a class to instantiate for
|
|
|
- the given prototype node."""
|
|
|
- #from .node_container_classes import TellClasses
|
|
|
- from mantis import xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers
|
|
|
- classes = {}
|
|
|
- for module in [xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers]:
|
|
|
- for cls in module.TellClasses():
|
|
|
- classes[cls.__name__] = cls
|
|
|
- # I could probably do a string.replace() here
|
|
|
- # But I actually think this is a bad idea since I might not
|
|
|
- # want to use this name convention in the future
|
|
|
- # this is easy enough for now, may refactor.
|
|
|
- #
|
|
|
- # kek, turns out it was completely friggin' inconsistent already
|
|
|
- if prototype_node.bl_idname == 'xFormRootNode':
|
|
|
- return classes["xFormRoot"]
|
|
|
- elif prototype_node.bl_idname == 'xFormArmatureNode':
|
|
|
- return classes["xFormArmature"]
|
|
|
- elif prototype_node.bl_idname == 'xFormBoneNode':
|
|
|
- return classes["xFormBone"]
|
|
|
- elif prototype_node.bl_idname == 'xFormGeometryObject':
|
|
|
- return classes["xFormGeometryObject"]
|
|
|
- elif prototype_node.bl_idname == 'linkInherit':
|
|
|
- return classes["LinkInherit"]
|
|
|
- # BAD need to fix the above, bl_idname is not consistent
|
|
|
- # Copy's
|
|
|
- elif prototype_node.bl_idname == 'LinkCopyLocation':
|
|
|
- return classes["LinkCopyLocation"] # also bad
|
|
|
- elif prototype_node.bl_idname == 'LinkCopyRotation':
|
|
|
- return classes["LinkCopyRotation"]
|
|
|
- elif prototype_node.bl_idname == 'LinkCopyScale':
|
|
|
- return classes["LinkCopyScale"]
|
|
|
- elif prototype_node.bl_idname == 'LinkCopyTransforms':
|
|
|
- return classes["LinkCopyTransforms"]
|
|
|
- # Limits
|
|
|
- elif prototype_node.bl_idname == 'LinkLimitLocation':
|
|
|
- return classes["LinkLimitLocation"]
|
|
|
- elif prototype_node.bl_idname == 'LinkLimitRotation':
|
|
|
- return classes["LinkLimitRotation"]
|
|
|
- elif prototype_node.bl_idname == 'LinkLimitScale':
|
|
|
- return classes["LinkLimitScale"]
|
|
|
- elif prototype_node.bl_idname == 'LinkLimitDistance':
|
|
|
- return classes["LinkLimitDistance"]
|
|
|
- # tracking
|
|
|
- elif prototype_node.bl_idname == 'LinkStretchTo':
|
|
|
- return classes["LinkStretchTo"]
|
|
|
- elif prototype_node.bl_idname == 'LinkDampedTrack':
|
|
|
- return classes["LinkDampedTrack"]
|
|
|
- elif prototype_node.bl_idname == 'LinkLockedTrack':
|
|
|
- return classes["LinkLockedTrack"]
|
|
|
- elif prototype_node.bl_idname == 'LinkTrackTo':
|
|
|
- return classes["LinkTrackTo"]
|
|
|
- # misc
|
|
|
- elif prototype_node.bl_idname == 'LinkInheritConstraint':
|
|
|
- return classes["LinkInheritConstraint"]
|
|
|
- # IK
|
|
|
- elif prototype_node.bl_idname == 'LinkInverseKinematics':
|
|
|
- return classes["LinkInverseKinematics"]
|
|
|
- elif prototype_node.bl_idname == 'LinkSplineIK':
|
|
|
- return classes["LinkSplineIK"]
|
|
|
- # utilities
|
|
|
- elif prototype_node.bl_idname == 'InputFloatNode':
|
|
|
- return classes["InputFloat"]
|
|
|
- elif prototype_node.bl_idname == 'InputVectorNode':
|
|
|
- return classes["InputVector"]
|
|
|
- elif prototype_node.bl_idname == 'InputBooleanNode':
|
|
|
- return classes["InputBoolean"]
|
|
|
- elif prototype_node.bl_idname == 'InputBooleanThreeTupleNode':
|
|
|
- return classes["InputBooleanThreeTuple"]
|
|
|
- elif prototype_node.bl_idname == 'InputRotationOrderNode':
|
|
|
- return classes["InputRotationOrder"]
|
|
|
- elif prototype_node.bl_idname == 'InputTransformSpaceNode':
|
|
|
- return classes["InputTransformSpace"]
|
|
|
- elif prototype_node.bl_idname == 'InputStringNode':
|
|
|
- return classes["InputString"]
|
|
|
- elif prototype_node.bl_idname == 'InputQuaternionNode':
|
|
|
- return classes["InputQuaternion"]
|
|
|
- elif prototype_node.bl_idname == 'InputQuaternionNodeAA':
|
|
|
- return classes["InputQuaternionAA"]
|
|
|
- elif prototype_node.bl_idname == 'InputMatrixNode':
|
|
|
- return classes["InputMatrix"]
|
|
|
- elif prototype_node.bl_idname == 'MetaRigMatrixNode':
|
|
|
- return classes["InputMatrix"]
|
|
|
- elif prototype_node.bl_idname == 'InputLayerMaskNode':
|
|
|
- return classes["InputLayerMask"]
|
|
|
- # geometry
|
|
|
- elif prototype_node.bl_idname == 'GeometryCirclePrimitive':
|
|
|
- return classes["CirclePrimitive"]
|
|
|
- # Deformers:
|
|
|
- elif prototype_node.bl_idname == 'DeformerArmature':
|
|
|
- return classes["DeformerArmature"]
|
|
|
-
|
|
|
- # every node before this point is not guarenteed to follow the pattern
|
|
|
- # but every node not checked above does follow the pattern.
|
|
|
-
|
|
|
- try:
|
|
|
- return classes[ prototype_node.bl_idname ]
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
-
|
|
|
- if prototype_node.bl_idname in [
|
|
|
- "NodeReroute",
|
|
|
- "NodeGroupInput",
|
|
|
- "NodeGroupOutput",
|
|
|
- "MantisNodeGroup",
|
|
|
- "NodeFrame",
|
|
|
- ]:
|
|
|
- return None
|
|
|
-
|
|
|
- prRed(prototype_node.bl_idname)
|
|
|
- raise RuntimeError("Failed to create node container for: %s" % prototype_node.bl_idname)
|
|
|
- return None
|
|
|
-
|
|
|
-# This is really, really stupid HACK
|
|
|
-def gen_nc_input_for_data(socket):
|
|
|
- # Class List #TODO deduplicate
|
|
|
- from mantis import xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers
|
|
|
- classes = {}
|
|
|
- for module in [xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers]:
|
|
|
- for cls in module.TellClasses():
|
|
|
- classes[cls.__name__] = cls
|
|
|
- #
|
|
|
- socket_class_map = {
|
|
|
- "MatrixSocket" : classes["InputMatrix"],
|
|
|
- "xFormSocket" : None,
|
|
|
- "xFormMultiSocket" : None,
|
|
|
- "RelationshipSocket" : classes["xFormRoot"], # world in
|
|
|
- "DeformerSocket" : classes["xFormRoot"], # world in
|
|
|
- "GeometrySocket" : classes["InputExistingGeometryData"],
|
|
|
- "EnableSocket" : classes["InputBoolean"],
|
|
|
- "HideSocket" : classes["InputBoolean"],
|
|
|
- #
|
|
|
- "DriverSocket" : None,
|
|
|
- "DriverVariableSocket" : None,
|
|
|
- "FCurveSocket" : None,
|
|
|
- "KeyframeSocket" : None,
|
|
|
- "LayerMaskInputSocket" : classes["InputLayerMask"],
|
|
|
- "LayerMaskSocket" : classes["InputLayerMask"],
|
|
|
- #
|
|
|
- "xFormParameterSocket" : None,
|
|
|
- "ParameterBoolSocket" : classes["InputBoolean"],
|
|
|
- "ParameterIntSocket" : classes["InputFloat"], #TODO: make an Int node for this
|
|
|
- "ParameterFloatSocket" : classes["InputFloat"],
|
|
|
- "ParameterVectorSocket" : classes["InputVector"],
|
|
|
- "ParameterStringSocket" : classes["InputString"],
|
|
|
- #
|
|
|
- "TransformSpaceSocket" : classes["InputTransformSpace"],
|
|
|
- "BooleanSocket" : classes["InputBoolean"],
|
|
|
- "BooleanThreeTupleSocket" : classes["InputBooleanThreeTuple"],
|
|
|
- "RotationOrderSocket" : classes["InputRotationOrder"],
|
|
|
- "QuaternionSocket" : classes["InputQuaternion"],
|
|
|
- "QuaternionSocketAA" : classes["InputQuaternionAA"],
|
|
|
- "IntSocket" : classes["InputFloat"],
|
|
|
- "StringSocket" : classes["InputString"],
|
|
|
- #
|
|
|
- "BoolUpdateParentNode" : classes["InputBoolean"],
|
|
|
- "IKChainLengthSocket" : classes["InputFloat"],
|
|
|
- "EnumInheritScale" : classes["InputString"],
|
|
|
- "EnumRotationMix" : classes["InputString"],
|
|
|
- "EnumRotationMixCopyTransforms" : classes["InputString"],
|
|
|
- "EnumMaintainVolumeStretchTo" : classes["InputString"],
|
|
|
- "EnumRotationStretchTo" : classes["InputString"],
|
|
|
- "EnumTrackAxis" : classes["InputString"],
|
|
|
- "EnumUpAxis" : classes["InputString"],
|
|
|
- "EnumLockAxis" : classes["InputString"],
|
|
|
- "EnumLimitMode" : classes["InputString"],
|
|
|
- "EnumYScaleMode" : classes["InputString"],
|
|
|
- "EnumXZScaleMode" : classes["InputString"],
|
|
|
- # Deformers
|
|
|
- "EnumSkinning" : classes["InputString"],
|
|
|
- #
|
|
|
- "FloatSocket" : classes["InputFloat"],
|
|
|
- "FloatFactorSocket" : classes["InputFloat"],
|
|
|
- "FloatPositiveSocket" : classes["InputFloat"],
|
|
|
- "FloatAngleSocket" : classes["InputFloat"],
|
|
|
- "VectorSocket" : classes["InputVector"],
|
|
|
- "VectorEulerSocket" : classes["InputVector"],
|
|
|
- "VectorTranslationSocket" : classes["InputVector"],
|
|
|
- "VectorScaleSocket" : classes["InputVector"],
|
|
|
- # Drivers
|
|
|
- "EnumDriverVariableType" : classes["InputString"],
|
|
|
- "EnumDriverVariableEvaluationSpace" : classes["InputString"],
|
|
|
- "EnumDriverRotationMode" : classes["InputString"],
|
|
|
- "EnumDriverType" : classes["InputString"],
|
|
|
- }
|
|
|
- return socket_class_map.get(socket.bl_idname, None)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-class DummyLink:
|
|
|
- #gonna use this for faking links to keep the interface consistent
|
|
|
- def __init__(self, from_socket, to_socket, nc_from=None, nc_to=None, original_from=None):
|
|
|
- self.from_socket = from_socket
|
|
|
- self.to_socket = to_socket
|
|
|
- self.nc_from = nc_from
|
|
|
- self.nc_to = nc_to
|
|
|
- if (original_from):
|
|
|
- self.original_from = original_from
|
|
|
- else:
|
|
|
- self.original_from = self.from_socket
|
|
|
- def __repr__(self):
|
|
|
- return(self.nc_from.__repr__()+":"+self.from_socket.name + " -> " + self.nc_to.__repr__()+":"+self.to_socket.name)
|
|
|
-
|
|
|
-# We'll treat this as a "dangling" link if either socket is unset...
|
|
|
-
|
|
|
-
|
|
|
-# # May or may not use this for my Dummy Links to help me connect things
|
|
|
-# # I think I can avoid it tho
|
|
|
-# class DummyNode:
|
|
|
- # def __init__(self, signature, base_tree, prototype):
|
|
|
- # self.signature = signature
|
|
|
- # self.base_tree = base_tree
|
|
|
- # self.prototype = prototype
|
|
|
-
|
|
|
-# This really might be useful in sorting the tree, too.
|
|
|
-
|
|
|
-
|
|
|
-def socket_seek(start_link, links):
|
|
|
- link = start_link
|
|
|
- while(link.from_socket):
|
|
|
- for newlink in links:
|
|
|
- if link.from_socket.node.inputs:
|
|
|
- if newlink.to_socket == link.from_socket.node.inputs[0]:
|
|
|
- link=newlink; break
|
|
|
- else:
|
|
|
- break
|
|
|
- return link.from_socket
|
|
|
-
|
|
|
-def clear_reroutes(links):
|
|
|
- kept_links, rerouted_starts = [], []
|
|
|
- rerouted = []
|
|
|
- all_links = links.copy()
|
|
|
- while(all_links):
|
|
|
- link = all_links.pop()
|
|
|
- to_cls = link.to_socket.node.bl_idname
|
|
|
- from_cls = link.from_socket.node.bl_idname
|
|
|
- reroute_classes = ["NodeReroute"]
|
|
|
- if (to_cls in reroute_classes and
|
|
|
- from_cls in reroute_classes):
|
|
|
- rerouted.append(link)
|
|
|
- elif (to_cls in reroute_classes and not
|
|
|
- from_cls in reroute_classes):
|
|
|
- rerouted.append(link)
|
|
|
- elif (from_cls in reroute_classes and not
|
|
|
- to_cls in reroute_classes):
|
|
|
- rerouted_starts.append(link)
|
|
|
- else:
|
|
|
- kept_links.append(link)
|
|
|
- for start in rerouted_starts:
|
|
|
- from_socket = socket_seek(start, rerouted)
|
|
|
- new_link = DummyLink(from_socket=from_socket, to_socket=start.to_socket, nc_from=None, nc_to=None)
|
|
|
- kept_links.append(new_link)
|
|
|
- return kept_links
|
|
|
-
|
|
|
-def data_from_tree(base_tree, tree_path = [None], held_links = {}, all_nc = {}):
|
|
|
- # prGreen("Starting! Base Tree: %s, held nodes: %d, held links: %d, nc's: %d" % (base_tree.name, len(held_nodes), len(held_links), len(all_nc)))
|
|
|
- # prPurple(tree_path)
|
|
|
- nc_dict = {}
|
|
|
- tree_path_names = [tree.name for tree in tree_path if hasattr(tree, "name")]
|
|
|
- all_child_ng = []
|
|
|
- tree = base_tree
|
|
|
- if tree_path[-1]:
|
|
|
- tree = tree_path[-1].node_tree
|
|
|
-
|
|
|
- # Start by looking through the nodes and making nc's where possible
|
|
|
- # store the groups, we'll pricess them soon.
|
|
|
- for np in tree.nodes:
|
|
|
- if (nc_cls := class_for_mantis_prototype_node(np)):
|
|
|
- nc = nc_cls( sig := tuple([None] + tree_path_names + [np.name]) , base_tree)
|
|
|
- nc_dict[sig] = nc; all_nc[sig] = nc
|
|
|
- if hasattr(np, "node_tree"):
|
|
|
- all_child_ng.append(np)
|
|
|
-
|
|
|
- # Then deal with the links in the current tree and the held_links.
|
|
|
- kept_links, incoming, outgoing = [], [], []
|
|
|
- all_links = clear_reroutes(list(tree.links))
|
|
|
- while(all_links):
|
|
|
- link = all_links.pop()
|
|
|
- to_cls = link.to_socket.node.bl_idname
|
|
|
- from_cls = link.from_socket.node.bl_idname
|
|
|
- if (from_cls in ["NodeGroupInput"]):
|
|
|
- incoming.append(link)
|
|
|
- elif (to_cls in ["NodeGroupOutput"]):
|
|
|
- nc_from = nc_dict.get( tuple([None]+tree_path_names+[link.from_socket.node.name]) )
|
|
|
- to_s = link.to_socket.identifier
|
|
|
- # Let's try and connect it now; go UP:
|
|
|
- nc_to, new_link = None, None
|
|
|
-
|
|
|
- if len(tree_path)==1:
|
|
|
- prRed("Warning: There is a GroupOutput node in the Base Tree.")
|
|
|
- kept_links.append(link)
|
|
|
- continue
|
|
|
- elif tree_path[-2] is None:
|
|
|
- up_tree=base_tree
|
|
|
- else:
|
|
|
- up_tree=tree_path[-2].node_tree
|
|
|
-
|
|
|
- for up_node in up_tree.nodes:
|
|
|
- for out in up_node.outputs:
|
|
|
- if not hasattr(up_node, "node_tree"):
|
|
|
- continue
|
|
|
- if not out.is_linked:
|
|
|
- continue
|
|
|
- for up_link in out.links:
|
|
|
- if up_link.from_socket.identifier == to_s:
|
|
|
- new_link = DummyLink(from_socket=up_link.from_socket, to_socket=up_link.to_socket, nc_from=nc_from, nc_to=None)
|
|
|
- link_sig = tuple([None]+tree_path_names[:-1]+[up_link.to_socket.node.name, link.from_socket.name])
|
|
|
- held_links[link_sig] = new_link
|
|
|
- nc_from, nc_to = None, None # clear them
|
|
|
- elif (from_cls in ["MantisNodeGroup"]):
|
|
|
- outgoing.append(link)
|
|
|
- else:
|
|
|
- kept_links.append(link)
|
|
|
- # Make the connections:
|
|
|
- for link in kept_links:
|
|
|
- nc_from = nc_dict.get( tuple([None] + tree_path_names + [link.from_socket.node.name]) )
|
|
|
- nc_to = nc_dict.get( tuple([None] + tree_path_names + [link.to_socket.node.name]) )
|
|
|
- if (nc_from and nc_to):
|
|
|
- from_s, to_s = link.from_socket.name, link.to_socket.name
|
|
|
- connection = nc_from.outputs[from_s].connect(node=nc_to, socket=to_s)
|
|
|
- nc_from = None; nc_to = None #clear them, since we use the same variable names again
|
|
|
-
|
|
|
- # At this point we're also gonna deal with held links and held nodes
|
|
|
- hold_further = {}
|
|
|
- del_me = set() # I donno why but there can be dupes.
|
|
|
- for link_sig, held in held_links.items():
|
|
|
- found, connected = False, False
|
|
|
- nc_from, from_s = held.nc_from, held.original_from.name
|
|
|
- watch = (nc_from.signature[-1] == 'Parent') and (tree_path_names[-1] in ["right", "left"])
|
|
|
- for link in incoming:
|
|
|
- if watch:
|
|
|
- prWhite(link_sig)
|
|
|
- if ((link.from_socket.identifier != link_sig[-1]) or
|
|
|
- (link.from_socket.name != link_sig[-2])):
|
|
|
- continue # This ain't it
|
|
|
- if (link.from_socket.identifier == link_sig[-1]):
|
|
|
- if (link.to_socket.node.bl_idname in [ "MantisNodeGroup" ]):
|
|
|
- del_me.add(link_sig)
|
|
|
- link_sig = tuple(list(link_sig[:-2]) + [link.to_socket.name, link.to_socket.identifier])
|
|
|
- hold_further[link_sig] = DummyLink(from_socket = held.from_socket, to_socket = link.to_socket, nc_from=nc_from, nc_to = None, original_from=held.original_from)
|
|
|
- prGreen("Holding further %s" % held.original_from.name)
|
|
|
- continue # just continue to hold it
|
|
|
- found = True
|
|
|
- # TO-Node:
|
|
|
- to_s = link.to_socket.name
|
|
|
- sig_to = tuple([None] + tree_path_names + [link.to_socket.node.name])
|
|
|
- nc_to = nc_dict.get( sig_to )
|
|
|
- if (nc_to and nc_from):
|
|
|
- if watch:
|
|
|
- prGreen("Connecting: %s%s -> %s%s" % (nc_from, from_s, nc_to, to_s) )
|
|
|
- connection = nc_from.outputs[from_s].connect(node=nc_to, socket=to_s)
|
|
|
- connected = True
|
|
|
- elif watch:
|
|
|
- prRed("Not Connecting: %s%s -> %s%s" % (nc_from, from_s, nc_to, to_s) )
|
|
|
- if (connected) != found:
|
|
|
- print(wrapRed("Not Connected: ") ,link_sig, held)
|
|
|
-
|
|
|
- # it's fairly annoying that I can't do this while I go.
|
|
|
- # for k in del_me:
|
|
|
- # del held_links[k]
|
|
|
- for k,v in hold_further.items():
|
|
|
- held_links[k] = v
|
|
|
-
|
|
|
-
|
|
|
- for ng in all_child_ng:
|
|
|
- for inp in ng.inputs:
|
|
|
- if not inp.is_linked:
|
|
|
- nc_cls = gen_nc_input_for_data(inp)
|
|
|
- if (nc_cls):
|
|
|
- sig = ("MANTIS_AUTOGENERATED", *tree_path_names, inp.node.name, inp.identifier)
|
|
|
- nc = nc_cls(sig, tree)
|
|
|
- # HACK HACK HACK
|
|
|
- for k, v in nc.outputs.items():
|
|
|
- v.name = inp.name; break
|
|
|
- from mantis.node_container_common import NodeSocket
|
|
|
- nc.outputs[inp.name] = NodeSocket(name = inp.name, node=nc)
|
|
|
- # del nc.outputs[k]; del nc.parameters[k]
|
|
|
- nc.parameters[inp.name]=inp.default_value
|
|
|
- # HACK HACK HACK
|
|
|
-
|
|
|
- nc_dict[sig] = nc; all_nc[sig] = nc
|
|
|
-
|
|
|
- dummy = DummyLink(from_socket = inp, to_socket = inp, nc_from=nc, nc_to=None)
|
|
|
- link_sig = tuple([None] + tree_path_names +[ng.name, inp.name, inp.identifier])
|
|
|
- held_links[link_sig]=dummy
|
|
|
- else: # We need to hold the incoming connections
|
|
|
- for link in inp.links: #Usually there will only be 1
|
|
|
- from_socket = link.from_socket
|
|
|
- if (link.from_socket.node.bl_idname == "NodeGroupInput"):
|
|
|
- # shouldn't there be a held link for this?
|
|
|
- continue
|
|
|
-
|
|
|
- if (link.from_socket.node.bl_idname == "NodeReroute"):
|
|
|
- from_socket = socket_seek(link, list(tree.links))
|
|
|
- sig = tuple( [None] + tree_path_names +[from_socket.node.name])
|
|
|
- # print(sig)
|
|
|
- nc_from = nc_dict.get(sig)
|
|
|
- # This is kind of stupid
|
|
|
- if from_socket.node.bl_idname in "NodeGroupInput":
|
|
|
- nc_from = held_links.get( tuple([None] + tree_path_names + [link.from_socket.name, inp.identifier]) )
|
|
|
- if not (nc_from):
|
|
|
- prRed( [None] + tree_path_names + [link.from_socket.name, inp.identifier])
|
|
|
- for signature, link in held_links.items():
|
|
|
- print ( wrapGreen(signature), wrapWhite(link))
|
|
|
- nc_from = nc_from.nc_from
|
|
|
-
|
|
|
- if (nc_from):
|
|
|
- dummy = DummyLink(from_socket = from_socket, to_socket = inp, nc_from=nc_from, nc_to=None, original_from=from_socket )
|
|
|
- # The link sig should take us back to the group node.
|
|
|
- link_sig = tuple( [None] + tree_path_names + [ng.name, inp.name, inp.identifier])
|
|
|
- held_links[link_sig]=dummy
|
|
|
- prGreen("Adding %s" % from_socket)
|
|
|
- else:
|
|
|
- prRed("no nc?")
|
|
|
- prOrange(sig)
|
|
|
- # Recurse!
|
|
|
- # data_from_tree(base_tree, tree_path+[ng], grps, solved_trees, solved_tree_links, held_nodes, held_links, all_nc)
|
|
|
-
|
|
|
-
|
|
|
- data_from_tree(base_tree, tree_path+[ng], held_links, all_nc)
|
|
|
-
|
|
|
- for link_sig, held in held_links.items():
|
|
|
- from_cls = held.from_socket.node.bl_idname
|
|
|
- to_cls = held.to_socket.node.bl_idname
|
|
|
- if (from_cls in ["MantisNodeGroup"] and not
|
|
|
- to_cls in ["MantisNodeGroup"]):
|
|
|
- nc_from = held.nc_from
|
|
|
- for link in outgoing:
|
|
|
- if link.from_socket.node.name == nc_from.signature[-2]:
|
|
|
- to_sig = tuple([None] + tree_path_names + [held.to_socket.node.name])
|
|
|
- nc_to = nc_dict.get( to_sig )
|
|
|
- if (nc_from and nc_to):
|
|
|
- from_s, to_s = link_sig[-1], held.to_socket.name
|
|
|
- connection = nc_from.outputs[from_s].connect(node=nc_to, socket=to_s)
|
|
|
-
|
|
|
- held_nodes = {}; held_links = {} # NO IDEA why I have to do this
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- # return None, grps, all_links, solved_trees, solved_tree_links, all_nc
|
|
|
- return all_nc
|
|
|
-
|
|
|
-
|
|
|
-from itertools import chain
|
|
|
-
|
|
|
-def parse_tree(base_tree):
|
|
|
- all_nc = data_from_tree(base_tree, tree_path = [None], held_links = {}, all_nc = {})
|
|
|
- all_nc = list(all_nc.values()).copy()
|
|
|
- kept_nc = {}
|
|
|
- while (all_nc):
|
|
|
- nc = all_nc.pop()
|
|
|
- # total_links=0
|
|
|
- # for sock in chain( nc.inputs.values(), nc.outputs.values()):
|
|
|
- # total_links+=len(sock.links)
|
|
|
- # if total_links > 0:
|
|
|
- nc.fill_parameters()
|
|
|
- # ugly, but it solves the problem easily:
|
|
|
- establish_node_connections(nc)
|
|
|
- kept_nc[nc.signature]=nc
|
|
|
- return kept_nc
|
|
|
-
|
|
|
-
|
|
|
-from_name_filter = ["Driver", ]
|
|
|
-
|
|
|
-to_name_filter = [
|
|
|
- "Custom Object xForm Override",
|
|
|
- "Custom Object",
|
|
|
- "Deform Bones"
|
|
|
- ]
|
|
|
-
|
|
|
-def establish_node_connections(nc):
|
|
|
- # This is ugly bc it adds parameters to an object
|
|
|
- # but it's kinda necesary to do it after the fact; and it
|
|
|
- # wouldn't be ugly if I just initialized the parameter elsewhere
|
|
|
- connections, hierarchy_connections = [], []
|
|
|
- for socket in nc.outputs.values():
|
|
|
- for link in socket.links:
|
|
|
- connections.append(link.to_node)
|
|
|
- # this may catch custom properties... too bad.
|
|
|
- if link.from_socket in from_name_filter:
|
|
|
- continue
|
|
|
- if link.to_socket in to_name_filter:
|
|
|
- continue
|
|
|
- hierarchy_connections.append(link.to_node)
|
|
|
- nc.connected_to = connections
|
|
|
- nc.hierarchy_connections = hierarchy_connections
|
|
|
-
|
|
|
-
|
|
|
-def sort_tree_into_layers(nodes, context):
|
|
|
- from time import time
|
|
|
- from mantis.node_container_common import (get_depth_lines,
|
|
|
- node_depth)
|
|
|
- from mantis.utilities import prGreen, prOrange, prRed, prPurple
|
|
|
- # All this function needs to do is sort out the hierarchy and
|
|
|
- # get things working in order of their dependencies.
|
|
|
-
|
|
|
- prPurple ("Number of nodes: ", len(nodes))
|
|
|
- roots, drivers = [], []
|
|
|
- start = time()
|
|
|
-
|
|
|
- for n in nodes.values():
|
|
|
- if n.node_type == 'DRIVER': drivers.append(n)
|
|
|
- # ugly but necesary to ensure that drivers are always connected.
|
|
|
- if not (hasattr(n, 'inputs')) or ( len(n.inputs) == 0):
|
|
|
- roots.append(n)
|
|
|
- elif (hasattr(n, 'inputs')):
|
|
|
- none_connected = True
|
|
|
- for inp in n.inputs.values():
|
|
|
- if inp.is_linked: none_connected = False
|
|
|
- if none_connected: roots.append(n)
|
|
|
-
|
|
|
- layers, nodes_heights = {}, {}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- for root in roots:
|
|
|
- nodes_heights[root.signature] = 0
|
|
|
-
|
|
|
- #Possible improvement: unify roots if they represent the same data
|
|
|
- all_sorted_nodes = []
|
|
|
- for root in roots:
|
|
|
-
|
|
|
- # if len(root.hierarchy_connections) == 0:
|
|
|
- # if (len(root.connected_to) == 0):
|
|
|
- # prRed("No connections: ", root)
|
|
|
- # continue
|
|
|
-
|
|
|
- depth_lines = get_depth_lines(root)[0]
|
|
|
-
|
|
|
- for n in nodes.values():
|
|
|
- if n.signature not in (depth_lines.keys()):
|
|
|
- continue #belongs to a different root
|
|
|
- d = nodes_heights.get(n.signature, 0)
|
|
|
- if (new_d := node_depth(depth_lines[n.signature])) > d:
|
|
|
- d = new_d
|
|
|
- nodes_heights[n.signature] = d
|
|
|
-
|
|
|
- for k, v in nodes_heights.items():
|
|
|
- if (layer := layers.get(v, None)):
|
|
|
- layer.append(nodes[k]) # add it to the existing layer
|
|
|
- else: layers[v] = [nodes[k]] # or make a new layer with the node
|
|
|
- all_sorted_nodes.append(nodes[k]) # add it to the sorted list
|
|
|
-
|
|
|
- for n in nodes.values():
|
|
|
- if n not in all_sorted_nodes:
|
|
|
- for drv in drivers:
|
|
|
- if n in drv.connected_to:
|
|
|
- depth = nodes_heights[drv.signature] + 1
|
|
|
- nodes_heights[n.signature] = depth
|
|
|
- # don't try to push downstream deps up bc this
|
|
|
- # is a driver and it will be done in the
|
|
|
- # finalize pass anyway
|
|
|
- if (layer := layers.get(depth, None)):
|
|
|
- layer.append(n)
|
|
|
- else: layers[v] = [n]
|
|
|
- else:
|
|
|
- prRed(n)
|
|
|
- for inp in n.inputs.values():
|
|
|
- print (len(inp.links))
|
|
|
- raise RuntimeError(wrapRed("Failed to depth-sort nodes (because of a driver-combine node?)"))
|
|
|
- #
|
|
|
- prGreen("Sorting depth for %d nodes finished in %s seconds" %
|
|
|
- (len(nodes), time() - start))
|
|
|
-
|
|
|
- if (False): # True to print the layers
|
|
|
- for i in range(len(layers)):
|
|
|
- try:
|
|
|
- print(i, layers[i])
|
|
|
- except KeyError: # empty layer?
|
|
|
- print (i)
|
|
|
- return layers
|
|
|
-
|
|
|
-
|
|
|
-def execute_tree(nodes, base_tree, context):
|
|
|
- import bpy
|
|
|
- from time import time
|
|
|
- from mantis.node_container_common import GraphError
|
|
|
- start_time = time()
|
|
|
-
|
|
|
- # input_from_grp_nodes(parsed_tree, base_tree, nodes)
|
|
|
- # bpy.ops.wm.quit_blender()
|
|
|
-
|
|
|
- layers = sort_tree_into_layers(nodes, context)
|
|
|
- start_execution_time = time()
|
|
|
-
|
|
|
- # Execute the first pass (xForm, Utility) #
|
|
|
- for i in range(len(layers)):
|
|
|
- for node in layers[i]:
|
|
|
- if (node.node_type in ['XFORM', 'UTILITY']):
|
|
|
- try:
|
|
|
- node.bExecute(context)
|
|
|
- except Exception as e:
|
|
|
- prRed("Execution failed at %s" % node); raise e
|
|
|
- # Switch to Pose Mode #
|
|
|
- active = None
|
|
|
- switch_me = []
|
|
|
- for n in nodes.values():
|
|
|
- # if it is a armature, switch modes
|
|
|
- # total hack #kinda dumb
|
|
|
- if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
|
|
|
- try:
|
|
|
- ob = n.bGetObject()
|
|
|
- except KeyError: # for bones
|
|
|
- ob = None
|
|
|
- # TODO this will be a problem if and when I add mesh/curve stuff
|
|
|
- if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
|
|
|
- switch_me.append(ob)
|
|
|
- active = ob # need to have an active ob, not None, to switch modes.
|
|
|
- # we override selected_objects to prevent anyone else from mode-switching
|
|
|
- # TODO it's possible but unlikely that the user will try to run a
|
|
|
- # graph with no armature nodes in it.
|
|
|
- if (active):
|
|
|
- bpy.ops.object.mode_set({'active_object':active, 'selected_objects':switch_me}, mode='POSE')
|
|
|
-
|
|
|
- # Execute second pass (Link, Driver) #
|
|
|
- for i in range(len(layers)):
|
|
|
- for n in layers[i]:
|
|
|
- # Now do the Link & Driver nodes during the second pass.
|
|
|
- if (n.node_type in ['LINK', 'DRIVER']):
|
|
|
- try:
|
|
|
- n.bExecute(context)
|
|
|
- except GraphError:
|
|
|
- pass
|
|
|
- except Exception as e:
|
|
|
- print (n); raise e
|
|
|
-
|
|
|
- # Finalize #
|
|
|
- for i in range(len(layers)):
|
|
|
- for node in layers[i]:
|
|
|
- if (hasattr(node, "bFinalize")):
|
|
|
- node.bFinalize(context)
|
|
|
-
|
|
|
- prGreen("Executed Tree in %s seconds" % (time() - start_execution_time))
|
|
|
- prGreen("Finished executing tree in %f seconds" % (time() - start_time))
|
|
|
-
|