node_common.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. from .utilities import (prRed, prGreen, prPurple, prWhite,
  2. prOrange,
  3. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  4. wrapOrange,)
  5. from ..base_definitions import GraphError, NodeSocket, MantisNode
  6. from collections.abc import Callable
  7. # BE VERY CAREFUL
  8. # the x_containers files import * from this file
  9. # so all the top-level imports are carried over
  10. from bpy.app import version as bpy_version
  11. # Utility to print links nicely for end user.
  12. def print_link_list(links):
  13. ret_str = ""
  14. for link in links:
  15. ret_str+= link.__repr__() + '\n'
  16. return ret_str
  17. # I've moved this to a separate function because the trace functions
  18. # are kind of hard to understand and the cycle check breaks the flow
  19. # of the code.
  20. def detect_cycle_in_trace(link, link_circle):
  21. if link.is_hierarchy:
  22. if link in link_circle:
  23. raise GraphError( "There is a cycle in the following links:\n" +
  24. print_link_list(link_circle))
  25. link_circle.append(link)
  26. # NOTE this is a dupe of trace_single_line, and it must be used
  27. # at any time before the tree is parsed (to check for cycles)
  28. # I don't want or need to pay the performance cost to check this everywhere
  29. def trace_single_line_safe(mantis_node, input_name, link_index=0):
  30. # DO: refactor this for new link class
  31. """Traces a line to its input."""
  32. nodes = [mantis_node]
  33. # a test for cyclic connections
  34. link_circle = []
  35. # Trace a single line
  36. if (socket := mantis_node.inputs.get(input_name) ):
  37. while (socket.is_linked):
  38. link = socket.links[link_index]; link_index = 0
  39. # should throw an error if it is in the set
  40. if detect_cycle_in_trace(link, link_circle):
  41. return
  42. if (socket := link.from_node.outputs.get(link.from_socket)):
  43. nodes.append(socket.node)
  44. if socket.can_traverse:
  45. socket = socket.traverse_target
  46. else: # this is an output.
  47. break
  48. else:
  49. break
  50. return nodes, socket
  51. def trace_single_line(mantis_node, input_name, link_index=0):
  52. # DO: refactor this for new link class
  53. """Traces a line to its input."""
  54. nodes = [mantis_node]
  55. # a test for cyclic connections
  56. if (socket := mantis_node.inputs.get(input_name) ):
  57. while (socket.is_linked):
  58. link = socket.links[link_index]; link_index = 0
  59. if (socket := link.from_node.outputs.get(link.from_socket)):
  60. nodes.append(socket.node)
  61. if socket.can_traverse:
  62. socket = socket.traverse_target
  63. else: # this is an output.
  64. break
  65. else:
  66. break
  67. return nodes, socket
  68. def trace_single_line_up(mantis_node, output_name,):
  69. """I use this to get the xForm from a link node."""
  70. nodes = [mantis_node]
  71. if hasattr(mantis_node, "outputs"):
  72. # Trace a single line
  73. if (socket := mantis_node.outputs.get(output_name) ):
  74. while (socket.is_linked):
  75. # This is bad, but it's efficient for nodes that only expect
  76. # one path along the given line
  77. link = socket.links[0] # TODO: find out if this is wise.
  78. other = link.to_node.inputs.get(link.to_socket)
  79. if (other):
  80. socket = other
  81. if socket.can_traverse:
  82. socket = socket.traverse_target
  83. nodes.append(socket.node)
  84. else: # this is an input.
  85. nodes.append(socket.node)
  86. break
  87. else:
  88. break
  89. return nodes, socket
  90. def trace_line_up_branching(mantis_node : MantisNode, output_name : str,
  91. break_condition : Callable = lambda mantis_node : False):
  92. """ Returns all leaf nodes at the ends of branching lines from an output."""
  93. leaf_nodes = []
  94. if hasattr(mantis_node, "outputs"):
  95. # Trace a single line
  96. if (socket := mantis_node.outputs.get(output_name) ):
  97. check_sockets={socket}
  98. while (check_sockets):
  99. # This is bad, but it's efficient for nodes that only expect
  100. # one path along the given line
  101. socket = check_sockets.pop()
  102. for link in socket.links:
  103. other = link.to_node.inputs.get(link.to_socket)
  104. if (other):
  105. socket = other
  106. if break_condition(socket.node):
  107. leaf_nodes.append(socket.node)
  108. elif socket.is_input and socket.can_traverse:
  109. check_sockets.add(socket.traverse_target)
  110. else: # this is an input.
  111. leaf_nodes.append(socket.node)
  112. return leaf_nodes
  113. # For this, we basically just evaluate the driver once and use the result
  114. def find_driven_default_value(mantis_node, socket_name):
  115. from ..misc_nodes import UtilityDriver, UtilitySwitch
  116. from .utilities import get_ui_node
  117. xf_inputs_values_var_names = []
  118. node_line, _last_socket = trace_single_line(mantis_node, socket_name)
  119. driver_node = None
  120. driver_type = 'SUM'
  121. keys = [ (0, 0), (1, 1) ]
  122. for driver_node in node_line:
  123. if driver_node.node_type == 'DRIVER': break
  124. else: # it's not being driven
  125. return mantis_node.evaluate_input(socket_name)
  126. if isinstance(driver_node, UtilityDriver):
  127. driver_type = driver_node.evaluate_input("Driver Type")
  128. # then get the variables
  129. for i, input in enumerate(driver_node.inputs):
  130. # unicode for 'a'
  131. var_name = chr(i - 2 + 96)
  132. if i == 0 : continue # the Driver Type doesn't interest us
  133. elif i ==1 : continue # for now. we do need the fCurve
  134. else:
  135. if not input.is_linked: continue # nothing to do here
  136. var_node = input.links[0].from_node
  137. # NOTE: this var_node will not be prepared on time unless we sort the graph by depth
  138. # and it doesn't seem to be possible to do that without breaking drivers... so
  139. # HACK it's safe to do this, but VERY ugly and bad.
  140. if not var_node.prepared:
  141. var_node.bPrepare()
  142. # end HACK
  143. var_data = var_node.parameters["Driver Variable"]
  144. var_type = var_data['type']
  145. var_channel = var_data['channel']
  146. prop_name = None; value = None
  147. xf1 = var_node.GetxForm(index=1)
  148. match var_type:
  149. case "SINGLE_PROP":
  150. prop_name = var_node.evaluate_input("Property")
  151. # we'll get the property value in the next step
  152. case "TRANSFORMS":
  153. matrix1 = xf1.evaluate_input['Matrix']
  154. # Mantis ensures that these are identity at creation time
  155. if "LOC" in var_channel or "ROT" in var_channel:
  156. value = 0
  157. else: # Scale, defaults to 1
  158. value = 1
  159. case "ROTATION_DIFF":
  160. xf2 = var_node.GetxForm(index=2)
  161. matrix1 = xf1.evaluate_input['Matrix']
  162. matrix2 = xf2.evaluate_input['Matrix']
  163. value = matrix1.to_quaternion().rotation_difference(matrix2.to_quaternion()).angle
  164. case "LOC_DIFF":
  165. xf2 = var_node.GetxForm(index=2)
  166. matrix1 = xf1.evaluate_input['Matrix']
  167. matrix2 = xf2.evaluate_input['Matrix']
  168. value = (matrix1.to_translation() - matrix2.to_translation()).length
  169. case "CONTEXT_PROP":
  170. raise NotImplementedError("Mantis Does not yet support Context Property")
  171. pass
  172. xf_inputs_values_var_names.append((xf1, prop_name, value, var_name))
  173. # Now we'll get the keyframe points and sort them, too!
  174. if driver_node.inputs['fCurve'].is_linked:
  175. fCurve_node = driver_node.inputs['fCurve'].links[0].from_node
  176. extrapolation_mode = 'CONSTANT'
  177. keys = []
  178. if fCurve_node:
  179. extrapolation_mode = fCurve_node.evaluate_input("Extrapolation Mode")
  180. for l in fCurve_node.inputs['Keyframes'].links:
  181. key_node = l.from_node
  182. if key_node:
  183. keys.append( (key_node.evaluate_input("Frame"), key_node.evaluate_input("Value")) )
  184. keys = sorted( keys, key = lambda a : a[0]) # sort by the frame
  185. elif isinstance(driver_node, UtilitySwitch):
  186. # Reimplementing the GetxForm from the switch here
  187. # because it is faster to inline this and store the trace result
  188. trace = trace_single_line(driver_node, "Parameter" )
  189. for node in trace[0]:
  190. if node.node_type == 'XFORM':
  191. xf = node # reuse trace HERE
  192. prop_name = trace[1].name # saves us from tracing again.
  193. xf_inputs_values_var_names.append(( xf, prop_name, None, "a")); break
  194. else:
  195. raise RuntimeError(f"ERROR: Cannot Set the default of the driven value {socket_name} \n"
  196. f"in node {mantis_node}")
  197. # We're gonna assume the xForm is sorting these right
  198. # but we'll check the name if it is cheap
  199. default_values = {}
  200. for xf, prop_name, value, var_name in xf_inputs_values_var_names:
  201. i=0
  202. for output in xf.outputs:
  203. if 'xForm Out' == output.name: continue
  204. if output.name == prop_name: break
  205. i+=1
  206. node_line, _last_socket = trace_single_line(xf, "Custom Properties", i)
  207. custom_prop_node = node_line[-1]
  208. if value:
  209. default_values[var_name] = value; continue
  210. if prop_name:
  211. if custom_prop_node.evaluate_input("Name") == prop_name:
  212. custom_prop_type = custom_prop_node.evaluate_input("Type")
  213. default_prop_name = ""
  214. match custom_prop_type:
  215. case "BOOL":
  216. default_prop_name = 'Default (Bool)'
  217. case "FLOAT":
  218. default_prop_name = 'Default (Float)'
  219. case "INT":
  220. default_prop_name = 'Default (Int)'
  221. case _:
  222. raise RuntimeError(f"ERROR: Cannot Set the default of the driven value {socket_name} \n"
  223. f"in node {mantis_node}"
  224. f"Reason: Custom Property {prop_name} is not a valid type." )
  225. default_values[var_name] = float(custom_prop_node.evaluate_input(default_prop_name))
  226. else:
  227. raise RuntimeError(f"ERROR: failed to get proeprty {prop_name} for node {xf}, \n"
  228. f"cannot Set the default of the driven value {socket_name} \n"
  229. f"in node {mantis_node}"
  230. "Reason: Cannot Find Custom Property Node")
  231. frame = 0.0
  232. if default_values:
  233. match driver_type:
  234. case "AVERAGE":
  235. frame = sum(default_values.values())/len(default_values)
  236. case "SUM":
  237. frame = sum(default_values.values())
  238. case "MIN":
  239. frame = min(default_values.values())
  240. case "MAX":
  241. frame = max(default_values.values())
  242. case "SCRIPTED":
  243. raise NotImplementedError (f"ERROR: Cannot Set the default of the driven value {socket_name} \n"
  244. f"in node {mantis_node} \n"
  245. "Reason: Driver Expressions are not yet implemented.")
  246. # Now we apply the fCurve
  247. # For now, We'll assume LINEAR keys TODO: come back to this during Pose update!
  248. if len(keys) >= 2:
  249. previous_frame = None
  250. next_frame = None
  251. previous_value = None
  252. next_value = None
  253. for key in keys:
  254. if key[0] <= frame: previous_frame, previous_value = key
  255. if key[0] >= frame: next_frame, next_value = key
  256. if previous_frame is not None and next_frame is not None: break
  257. else: # if our frame is out-of-bounds we need a slope to extrapolate on.
  258. if previous_frame and not next_frame:
  259. next_frame, next_value = previous_frame, previous_value
  260. if extrapolation_mode == 'LINEAR':
  261. previous_frame, previous_value = keys[-2] # we need the second last key to extrapolate
  262. elif next_frame and not previous_frame:
  263. previous_frame, previous_value = next_frame, next_value
  264. if extrapolation_mode == 'LINEAR':
  265. next_frame, next_value = keys[1] # we need the second key to extrapolate
  266. else:
  267. raise RuntimeError (f"ERROR: Cannot Set the default of the driven value {socket_name} \n"
  268. f"in node {mantis_node} \n"
  269. "Reason: Cannot place value on driver fCurve.")
  270. if previous_frame == next_frame: # the frame is exactly on a key
  271. return previous_value
  272. # this is just a simple lerp because we are assuming the keys are linear for now
  273. result = previous_value + ((frame - previous_frame) / (next_frame - previous_frame)) * (next_value - previous_value)
  274. elif len(keys) ==1:
  275. result = key[1] # flat curve, take the value of the only key
  276. else:
  277. raise RuntimeError (f"ERROR: Cannot Set the default of the driven value {socket_name} \n"
  278. f"in node {mantis_node} \n"
  279. "Reason: Invalid fCurve has 0 keyframes")
  280. return result
  281. # this function exists to walk back to the first ancestor node of XFORM or LINK without
  282. # breaking the traverse behaviour which I still need for various reasons.
  283. def get_parent_xForm_info(mantis_node, socket_name = 'Relationship'):
  284. from ..mantis_dataclasses import xForm_info
  285. # we re-implement the trace logic here for performance.
  286. # because this kind of connection has traversal (for Links to find their children), we must
  287. # simply seek back to the first available xForm or Link and get its xForm info.\
  288. parent_node = None
  289. if (socket := mantis_node.inputs.get(socket_name) ):
  290. while (socket.is_linked):
  291. link = socket.links[0]
  292. if (socket := link.from_node.outputs.get(link.from_socket)):
  293. if socket.node.node_type in ['LINK', 'XFORM']:
  294. parent_node = socket.node; break
  295. if socket.can_traverse:
  296. socket = socket.traverse_target
  297. else: # this is an output.
  298. break
  299. else:
  300. break
  301. parent_xForm_info = None
  302. if parent_node:
  303. if parent_node.node_type == 'XFORM':
  304. parent_xForm_info = parent_node.parameters['xForm Out']
  305. else:
  306. if 'Output Relationship' in parent_node.parameters.keys():
  307. parent_xForm_info = parent_node.parameters['Output Relationship']
  308. else: # TODO refactor Inherit Node to have the same output as the other link nodes.
  309. parent_xForm_info = parent_node.parameters['Inheritance']
  310. if parent_xForm_info is None: parent_xForm_info = xForm_info() # default to empty
  311. return parent_xForm_info
  312. def setup_custom_property_inputs_outputs(mantis_node):
  313. from .utilities import get_ui_node
  314. if mantis_node.signature[0] == 'SCHEMA_AUTOGENERATED':
  315. from ..base_definitions import custom_props_types
  316. if mantis_node.__class__.__name__ not in custom_props_types:
  317. raise RuntimeError(wrapRed(f"Custom Properties not allowed for class {mantis_node.__class__.__name__}"))
  318. return
  319. else:
  320. ui_node = get_ui_node(mantis_node.ui_signature, mantis_node.base_tree)
  321. if ui_node:
  322. setup_custom_props_from_np(mantis_node, ui_node)
  323. else:
  324. prRed(f"Failed to setup custom properties for: {mantis_node.ui_signature}")
  325. def setup_custom_props_from_np(mantis_node, ui_node):
  326. for inp in ui_node.inputs:
  327. if inp.identifier == "__extend__": continue
  328. if not (inp.name in mantis_node.inputs.keys()) :
  329. socket = NodeSocket(is_input = True, name = inp.name, node = mantis_node,)
  330. mantis_node.inputs[inp.name] = socket
  331. mantis_node.parameters[inp.name] = None
  332. for attr_name in ["min", "max", "soft_min", "soft_max", "description"]:
  333. try:
  334. setattr(socket, attr_name, getattr(inp, attr_name))
  335. except AttributeError:
  336. pass
  337. for out in ui_node.outputs:
  338. if out.identifier == "__extend__": continue
  339. if not (out.name in mantis_node.outputs.keys()) :
  340. mantis_node.outputs[out.name] = NodeSocket(is_input = False, name = out.name, node = mantis_node,)
  341. def prepare_parameters(mantis_node):
  342. # some nodes add new parameters at runtime, e.g. Drivers
  343. # so we need to take that stuff from the mantis_nodes that have
  344. # been executed prior to this node.
  345. for s_name, sock in mantis_node.inputs.items():
  346. if not (sock.is_linked):
  347. continue
  348. if (sock.name in sock.links[0].from_node.parameters.keys()):
  349. mantis_node.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]
  350. # should work, this is ugly.
  351. def check_for_driver(mantis_node, input_name, value_index = None, array_index = 0, combo_property=False):
  352. prop = mantis_node.evaluate_input(input_name, array_index)
  353. if (value_index is not None):
  354. try:
  355. prop = prop[value_index]
  356. except KeyError: # We'll try to use the driver for all three channels.
  357. pass
  358. # TODO an Index or Key error here should have a reasonable error message
  359. return (prop.__class__.__name__ == 'MantisDriver')
  360. def print_driver_item(mantis_node, b_object, original_prop):
  361. if mantis_node.node_type in ['LINK',]:
  362. printname = wrapOrange(b_object.id_data.name)
  363. elif mantis_node.node_type in ['XFORM',]:
  364. printname = wrapOrange(mantis_node.bGetObject().name)
  365. else:
  366. printname = wrapOrange(mantis_node)
  367. print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(mantis_node.signature[-1]), printname))
  368. # HACK This is the most hideous voodoo black magic fucntion in the entire program
  369. # but everything depends on it and it works
  370. def evaluate_sockets(mantis_node, b_object, props_sockets,):
  371. # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work
  372. def safe_setattr(ob, att_name, val):
  373. if ob.__class__.__name__ in ["NodesModifier"]:
  374. ob[att_name]=val
  375. elif b_object.__class__.__name__ in ["Key"]:
  376. if not val: val=0
  377. ob.key_blocks[att_name].value=val
  378. elif "]." in att_name:
  379. # it is of the form prop[int].prop2
  380. prop=att_name.split('[')[0]
  381. prop1=att_name.split('.')[1]
  382. get_index = int(att_name.split('[')[1][0])
  383. setattr(getattr(b_object, prop)[get_index], prop1, val)
  384. else:
  385. try:
  386. setattr(ob, att_name, val)
  387. except Exception as e:
  388. print ("ob, att_name, val")
  389. prRed(ob, att_name, val)
  390. raise e
  391. for prop, filler_item in props_sockets.items():
  392. if isinstance(filler_item, tuple):
  393. raise NotImplementedError(mantis_node)
  394. sock = filler_item.socket_name
  395. default = filler_item.default_value
  396. value_index = filler_item.value_index
  397. array_index = filler_item.array_index
  398. combo_property = filler_item.combo_property
  399. driver_key = (sock, value_index, array_index)
  400. if (check_for_driver(mantis_node, sock, value_index, array_index, combo_property)):
  401. original_prop = prop
  402. # TODO: deduplicate this terrible hack
  403. if ("." in prop) and not b_object.__class__.__name__ in ["Key"]: # this is a property of a property...
  404. sub_props = [b_object]
  405. while ("." in prop):
  406. split_prop = prop.split(".")
  407. prop = split_prop[1]
  408. sub_prop = (split_prop[0])
  409. if ("[" in sub_prop):
  410. sub_prop, sub_index = sub_prop.split("[")
  411. sub_prop_index = int(sub_index[0])
  412. sub_props.append(getattr(sub_props[-1], sub_prop)[sub_prop_index])
  413. else:
  414. sub_props.append(getattr(sub_props[-1], sub_prop))
  415. safe_setattr(sub_props[-1], prop, default)
  416. # this is really stupid
  417. else:
  418. safe_setattr(b_object, prop, default)
  419. print_driver_item( mantis_node, b_object, original_prop)
  420. if b_object.__class__.__name__ in ["NodesModifier"]:
  421. mantis_node.drivers[driver_key] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
  422. elif b_object.__class__.__name__ in ["Key"]:
  423. mantis_node.drivers[driver_key] = "key_blocks[\""+original_prop+"\"].value"
  424. else:
  425. mantis_node.drivers[driver_key] = original_prop
  426. else: # here we can do error checking for the socket if needed
  427. if (value_index is not None):
  428. safe_setattr(b_object, prop, mantis_node.evaluate_input(sock)[value_index])
  429. else: # 'mute' is better than 'enabled'
  430. # UGLY HACK # because it is available in older
  431. if (prop == 'mute'): # Blenders.
  432. safe_setattr(b_object, prop, not bool(mantis_node.evaluate_input(sock)))
  433. elif (prop == 'hide'): # this will not cast it for me, annoying.
  434. safe_setattr(b_object, prop, bool(mantis_node.evaluate_input(sock)))
  435. else:
  436. try:
  437. # just wanna see if we are dealing with some collection
  438. # check hasattr in case it is one of those ["such-and-such"] props, and ignore those
  439. if hasattr(b_object, prop) and (not isinstance(getattr(b_object, prop), str)) and hasattr(getattr(b_object, prop), "__getitem__"):
  440. # prGreen("Doing the thing")
  441. for val_index, value in enumerate(mantis_node.evaluate_input(sock)):
  442. # assume this will work, both because val should have the right number of elements, and because this should be the right data type.
  443. from .drivers import MantisDriver
  444. if isinstance(value, MantisDriver):
  445. getattr(b_object,prop)[val_index] = default[val_index]
  446. print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(mantis_node.signature[-1]), mantis_node))
  447. try:
  448. mantis_node.drivers[sock].append((prop, val_index))
  449. except:
  450. mantis_node.drivers[sock] = [(prop, val_index)]
  451. else:
  452. getattr(b_object,prop)[val_index] = value
  453. else:
  454. safe_setattr(b_object, prop, mantis_node.evaluate_input(sock))
  455. except Exception as e:
  456. prRed(b_object, mantis_node, prop, sock, mantis_node.evaluate_input(sock))
  457. raise e
  458. def finish_driver(mantis_node, b_object, driver_item, prop):
  459. try:
  460. driver_sock = driver_item[0]; index = driver_item[1]; array_index =driver_item[2]
  461. except Exception as e:
  462. print (wrapGreen(mantis_node), wrapOrange(b_object), wrapRed(driver_item), wrapWhite(prop),)
  463. raise e
  464. driver_trace = trace_single_line(mantis_node, driver_sock, array_index)
  465. driver_provider, driver_socket = driver_trace[0][-1], driver_trace[1]
  466. combo_property = False
  467. # try to detect combo properties
  468. if mantis_node.socket_templates:
  469. for st in mantis_node.socket_templates:
  470. if st.name == driver_sock:
  471. if isinstance(st.blender_property, tuple):
  472. combo_property = True
  473. break
  474. if index is not None:
  475. try:
  476. driver = driver_provider.parameters[driver_socket.name][index].copy()
  477. except KeyError as e: # try single driver, it should still work fine.
  478. if combo_property:
  479. driver = driver_provider.parameters[driver_socket.name].copy()
  480. else:
  481. raise e
  482. # this is harmless and necessary for the weird ones where the property is a vector too
  483. if not combo_property:
  484. driver["ind"] = index
  485. else:
  486. driver = driver_provider.parameters[driver_socket.name].copy()
  487. if driver:
  488. socket_template = None
  489. if mantis_node.socket_templates:
  490. for socket_template in mantis_node.socket_templates:
  491. if socket_template.name == driver_sock: break
  492. if socket_template:
  493. if socket_template.is_animatable == False:
  494. raise RuntimeError(f"Driver is not allowed for {driver_sock} in node {mantis_node}")
  495. if ("." in prop) and mantis_node.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
  496. sub_props = [b_object]
  497. while ("." in prop):
  498. split_prop = prop.split(".")
  499. prop = split_prop[1]
  500. sub_prop = (split_prop[0])
  501. if ("[" in sub_prop):
  502. sub_prop, index = sub_prop.split("[")
  503. index = int(index[0])
  504. sub_props.append(getattr(sub_props[-1], sub_prop)[index])
  505. else:
  506. sub_props.append(getattr(sub_props[-1], sub_prop))
  507. driver["owner"] = sub_props[-1]
  508. elif mantis_node.node_type in ['XFORM',] and mantis_node.__class__.__name__ in ['xFormBone']:
  509. # TODO: I really shouldn't have to hardcode this. Look into better solutions.
  510. # NOTE: VERSIONING in Blender 5.0 we have to use pose bone for this, too.
  511. # default to pose bone, it's usually what we want
  512. bone_col = mantis_node.bGetParentArmature().pose.bones
  513. from ..xForm_socket_templates import attributes_shared_by_bone_and_pose_bone
  514. if socket_template.prop_owner_group == 'bone':
  515. bone_col = mantis_node.bGetParentArmature().data.bones
  516. # this is a little bit clumsy. TODO clean up here
  517. if prop in attributes_shared_by_bone_and_pose_bone:
  518. # we want to use the pose bone for this...only if it has a driver
  519. bone_col = mantis_node.bGetParentArmature().pose.bones
  520. driver["owner"] = bone_col[b_object] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
  521. # HACK having special cases here is indicitave of a deeper problem that should be refactored
  522. elif mantis_node.__class__.__name__ in ['xFormCurvePin'] and \
  523. prop in ['offset_factor', 'forward_axis', 'up_axis']:
  524. driver["owner"] = b_object.constraints['Curve Pin']
  525. else:
  526. driver["owner"] = b_object
  527. driver["prop"] = prop
  528. return driver
  529. else:
  530. prOrange("Provider", driver_provider)
  531. prGreen("socket", driver_socket)
  532. print (index)
  533. prPurple(driver_provider.parameters[driver_socket.name])
  534. prRed("Failed to create driver for %s" % prop)
  535. return None
  536. def finish_drivers(mantis_node):
  537. drivers = []
  538. if not hasattr(mantis_node, "drivers"):
  539. return # HACK
  540. for driver_item, prop in mantis_node.drivers.items():
  541. b_objects = [mantis_node.bObject]
  542. if mantis_node.node_type == 'LINK':
  543. b_objects = mantis_node.bObject # this is already a list
  544. for b_object in b_objects:
  545. # Mutating the driver_item here is the WORST THING I HAVE EVER SEEN
  546. # HACK UGLY BUG TERRIBLE BAD CODE
  547. if isinstance(prop, list):
  548. for sub_item in prop:
  549. drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1], 0), sub_item[0]))
  550. else:
  551. drivers.append(finish_driver(mantis_node, b_object, (driver_item, sub_item[1], 0), sub_item[0]))
  552. else:
  553. drivers.append(finish_driver(mantis_node, b_object, driver_item, prop))
  554. from .drivers import CreateDrivers
  555. CreateDrivers(drivers)