| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- from .utilities import (prRed, prGreen, prPurple, prWhite,
- prOrange,
- wrapRed, wrapGreen, wrapPurple, wrapWhite,
- wrapOrange,)
- from .base_definitions import GraphError, NodeSocket, MantisNode
- from collections.abc import Callable
- # BE VERY CAREFUL
- # the x_containers files import * from this file
- # so all the top-level imports are carried over
- #TODO: refactor the socket definitions so this becomes unnecessary.
- def get_socket_value(node_socket):
- value = None
- if hasattr(node_socket, "default_value"):
- value = node_socket.default_value
- if node_socket.bl_idname == 'MatrixSocket':
- value = node_socket.TellValue()
- return value
- # TODO: modify this to work with multi-input nodes
- def trace_single_line(node_container, input_name, link_index=0):
- # DO: refactor this for new link class
- """Traces a line to its input."""
- nodes = [node_container]
- # Trace a single line
- if (socket := node_container.inputs.get(input_name) ):
- while (socket.is_linked):
- link = socket.links[link_index]; link_index = 0
- if (socket := link.from_node.outputs.get(link.from_socket)):
- nodes.append(socket.node)
- if socket.can_traverse:
- socket = socket.traverse_target
- else: # this is an output.
- break
- else:
- break
- return nodes, socket
- # this is same as the other, just flip from/to and in/out
- def trace_single_line_up(node_container, output_name,):
- """I use this to get the xForm from a link node."""
- nodes = [node_container]
- if hasattr(node_container, "outputs"):
- # Trace a single line
- if (socket := node_container.outputs.get(output_name) ):
- while (socket.is_linked):
- # This is bad, but it's efficient for nodes that only expect
- # one path along the given line
- link = socket.links[0] # TODO: find out if this is wise.
- other = link.to_node.inputs.get(link.to_socket)
- if (other):
- socket = other
- if socket.can_traverse:
- socket = socket.traverse_target
- nodes.append(socket.node)
- else: # this is an input.
- nodes.append(socket.node)
- break
- else:
- break
- return nodes, socket
- def trace_line_up_branching(node : MantisNode, output_name : str,
- break_condition : Callable = lambda node : False):
- """ Returns all leaf nodes at the ends of branching lines from an output."""
- leaf_nodes = []
- if hasattr(node, "outputs"):
- # Trace a single line
- if (socket := node.outputs.get(output_name) ):
- check_sockets={socket}
- while (check_sockets):
- # This is bad, but it's efficient for nodes that only expect
- # one path along the given line
- socket = check_sockets.pop()
- for link in socket.links:
- other = link.to_node.inputs.get(link.to_socket)
- if (other):
- socket = other
- if break_condition(socket.node):
- leaf_nodes.append(socket.node)
- elif socket.is_input and socket.can_traverse:
- check_sockets.add(socket.traverse_target)
- else: # this is an input.
- leaf_nodes.append(socket.node)
- return leaf_nodes
- def setup_custom_props(nc):
- from .utilities import get_node_prototype
- if nc.signature[0] == 'SCHEMA_AUTOGENERATED':
- from .base_definitions import custom_props_types
- if nc.__class__.__name__ not in custom_props_types:
- # prRed(f"Reminder: figure out how to deal with custom property setting for Schema Node {nc}")
- raise RuntimeError(wrapRed(f"Custom Properties not set up for node {nc}"))
- return
- else:
- np = get_node_prototype(nc.signature, nc.base_tree)
- if np:
- setup_custom_props_from_np(nc, np)
- else:
- prRed("Failed to setup custom properties for: nc")
- def setup_custom_props_from_np(nc, np):
- for inp in np.inputs:
- if inp.identifier == "__extend__": continue
- if not (inp.name in nc.inputs.keys()) :
- socket = NodeSocket(is_input = True, name = inp.name, node = nc,)
- nc.inputs[inp.name] = socket
- nc.parameters[inp.name] = None
- for attr_name in ["min", "max", "soft_min", "soft_max", "description"]:
- try:
- setattr(socket, attr_name, getattr(inp, attr_name))
- except AttributeError:
- pass
- for out in np.outputs:
- if out.identifier == "__extend__": continue
- if not (out.name in nc.outputs.keys()) :
- nc.outputs[out.name] = NodeSocket(is_input = False, name = out.name, node = nc,)
-
- def prepare_parameters(nc):
- # some nodes add new parameters at runtime, e.g. Drivers
- # so we need to take that stuff from the node_containers that have
- # been executed prior to this node.
- for s_name, sock in nc.inputs.items():
- if not (sock.is_linked):
- continue
- if (sock.name in sock.links[0].from_node.parameters.keys()):
- nc.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]
- # should work, this is ugly.
- def check_for_driver(node_container, input_name, index = None):
- prop = node_container.evaluate_input(input_name)
- if (index is not None):
- prop = prop[index]
- return (prop.__class__.__name__ == 'MantisDriver')
- # TODO: this should handle sub-properties better
- def evaluate_sockets(nc, b_object, props_sockets,):
- # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work
- def safe_setattr(ob, att_name, val):
- if ob.__class__.__name__ in ["NodesModifier"]:
- ob[att_name]=val
- elif b_object.__class__.__name__ in ["Key"]:
- if not val: val=0
- ob.key_blocks[att_name].value=val
- elif "]." in att_name:
- # it is of the form prop[int].prop2
- prop=att_name.split('[')[0]
- prop1=att_name.split('.')[1]
- index = int(att_name.split('[')[1][0])
- setattr(getattr(b_object, prop)[index], prop1, val)
- else:
- try:
- setattr(ob, att_name, val)
- except Exception as e:
- prRed(ob, att_name, val); raise e
- for prop, (sock, default) in props_sockets.items():
- # annoyingly, sometimes the socket is an array
- index = None
- if isinstance(sock, tuple):
- index = sock[1]; sock = sock[0]
- if (check_for_driver(nc, sock, index)):
- sock = (sock, index)
- original_prop = prop
- # TODO: deduplicate this terrible hack
- if ("." in prop) and not b_object.__class__.__name__ in ["Key"]: # this is a property of a property...
- sub_props = [b_object]
- while ("." in prop):
- split_prop = prop.split(".")
- prop = split_prop[1]
- sub_prop = (split_prop[0])
- if ("[" in sub_prop):
- sub_prop, index = sub_prop.split("[")
- index = int(index[0])
- sub_props.append(getattr(sub_props[-1], sub_prop)[index])
- else:
- sub_props.append(getattr(sub_props[-1], sub_prop))
- safe_setattr(sub_props[-1], prop, default)
- # this is really stupid
- else:
- safe_setattr(b_object, prop, default)
- if nc.node_type in ['LINK',]:
- printname = wrapOrange(b_object.id_data.name)
- elif nc.node_type in ['XFORM',]:
- printname = wrapOrange(nc.bGetObject().name)
- else:
- printname = wrapOrange(nc)
- print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(nc.signature[-1]), printname))
- if b_object.__class__.__name__ in ["NodesModifier"]:
- nc.drivers[sock] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
- elif b_object.__class__.__name__ in ["Key"]:
- nc.drivers[sock] = "key_blocks[\""+original_prop+"\"].value"
- else:
- nc.drivers[sock] = original_prop
- else: # here we can do error checking for the socket if needed
- if (index is not None):
- safe_setattr(b_object, prop, nc.evaluate_input(sock)[index])
- else: # 'mute' is better than 'enabled'
- # UGLY HACK # because it is available in older
- if (prop == 'mute'): # Blenders.
- safe_setattr(b_object, prop, not bool(nc.evaluate_input(sock)))
- elif (prop == 'hide'): # this will not cast it for me, annoying.
- safe_setattr(b_object, prop, bool(nc.evaluate_input(sock)))
- else:
- try:
- # prRed(b_object.name, nc, prop, nc.evaluate_input(sock) )
- # print( nc.evaluate_input(sock))
- # value_eval = nc.evaluate_input(sock)
- # just wanna see if we are dealing with some collection
- # check hasattr in case it is one of those ["such-and-such"] props, and ignore those
- if hasattr(b_object, prop) and (not isinstance(getattr(b_object, prop), str)) and hasattr(getattr(b_object, prop), "__getitem__"):
- # prGreen("Doing the thing")
- for val_index, value in enumerate(nc.evaluate_input(sock)):
- # assume this will work, both because val should have the right number of elements, and because this should be the right data type.
- from .drivers import MantisDriver
- if isinstance(value, MantisDriver):
- getattr(b_object,prop)[val_index] = default[val_index]
- print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(nc.signature[-1]), nc))
- try:
- nc.drivers[sock].append((prop, val_index))
- except:
- nc.drivers[sock] = [(prop, val_index)]
- else:
- getattr(b_object,prop)[val_index] = value
- else:
- # prOrange("Skipping the Thing", getattr(b_object, prop))
- safe_setattr(b_object, prop, nc.evaluate_input(sock))
- except Exception as e:
- prRed(b_object, nc, prop, sock, nc.evaluate_input(sock))
- raise e
- def finish_driver(nc, b_object, driver_item, prop):
- # prWhite(nc, prop)
- index = driver_item[1]; driver_sock = driver_item[0]
- driver_trace = trace_single_line(nc, driver_sock)
- driver_provider, driver_socket = driver_trace[0][-1], driver_trace[1]
- if index is not None:
- driver = driver_provider.parameters[driver_socket.name][index].copy()
- # this is harmless and necessary for the weird ones where the property is a vector too
- driver["ind"] = index
- else:
- driver = driver_provider.parameters[driver_socket.name].copy()
- if driver:
- if ("." in prop) and nc.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
- sub_props = [b_object]
- while ("." in prop):
- split_prop = prop.split(".")
- prop = split_prop[1]
- sub_prop = (split_prop[0])
- if ("[" in sub_prop):
- sub_prop, index = sub_prop.split("[")
- index = int(index[0])
- sub_props.append(getattr(sub_props[-1], sub_prop)[index])
- else:
- sub_props.append(getattr(sub_props[-1], sub_prop))
- driver["owner"] = sub_props[-1]
- elif nc.node_type in ['XFORM',] and nc.__class__.__name__ in ['xFormBone']:
- # TODO: I really shouldn't have to hardcode this. Look into better solutions.
- if prop in ['hide', 'show_wire']: # we need to get the bone, not the pose bone.
- bone_col = nc.bGetParentArmature().data.bones
- else:
- bone_col = nc.bGetParentArmature().pose.bones
- driver["owner"] = bone_col[b_object] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
- # HACK having special cases here is indicitave of a deeper problem that should be refactored
- elif nc.__class__.__name__ in ['xFormCurvePin'] and \
- prop in ['offset_factor', 'forward_axis', 'up_axis']:
- driver["owner"] = b_object.constraints['Curve Pin']
- else:
- driver["owner"] = b_object
- driver["prop"] = prop
- return driver
- else:
- prOrange("Provider", driver_provider)
- prGreen("socket", driver_socket)
- print (index)
- prPurple(driver_provider.parameters[driver_socket.name])
- prRed("Failed to create driver for %s" % prop)
- return None
- def finish_drivers(nc):
- drivers = []
- if not hasattr(nc, "drivers"):
- return # HACK
- for driver_item, prop in nc.drivers.items():
- b_objects = [nc.bObject]
- if nc.node_type == 'LINK':
- b_objects = nc.bObject # this is already a list
- for b_object in b_objects:
- if isinstance(prop, list):
- for sub_item in prop:
- drivers.append(finish_driver(nc, b_object, (driver_item, sub_item[1]), sub_item[0]))
- else:
- drivers.append(finish_driver(nc, b_object, (driver_item, sub_item[1]), sub_item[0]))
- else:
- drivers.append(finish_driver(nc, b_object, driver_item, prop))
- from .drivers import CreateDrivers
- CreateDrivers(drivers)
|