misc_containers.py 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846
  1. from .node_container_common import *
  2. # The fact that I need this means that some of these classes should
  3. # probably be moved to link_containers.py
  4. from .xForm_containers import xFormArmature, xFormBone
  5. from math import pi, tau
  6. def TellClasses():
  7. return [
  8. # utility
  9. InputFloat,
  10. InputIntNode,
  11. InputVector,
  12. InputBoolean,
  13. InputBooleanThreeTuple,
  14. InputRotationOrder,
  15. InputTransformSpace,
  16. InputString,
  17. InputMatrix,
  18. InputExistingGeometryObject,
  19. InputExistingGeometryData,
  20. UtilityGeometryOfXForm,
  21. UtilityNameOfXForm,
  22. UtilityPointFromCurve,
  23. UtilityMatrixFromCurve,
  24. UtilityMatricesFromCurve,
  25. UtilityMetaRig,
  26. UtilityBoneProperties,
  27. UtilityDriverVariable,
  28. UtilityDriver,
  29. UtilityFCurve,
  30. UtilityKeyframe,
  31. UtilitySwitch,
  32. UtilityCombineThreeBool,
  33. UtilityCombineVector,
  34. UtilityCatStrings,
  35. UtilityGetBoneLength,
  36. UtilityPointFromBoneMatrix,
  37. UtilitySetBoneLength,
  38. UtilityMatrixSetLocation,
  39. UtilityMatrixGetLocation,
  40. UtilityMatrixFromXForm,
  41. UtilityAxesFromMatrix,
  42. UtilityBoneMatrixHeadTailFlip,
  43. UtilityMatrixTransform,
  44. UtilityTransformationMatrix,
  45. UtilityIntToString,
  46. UtilityArrayGet,
  47. UtilitySetBoneMatrixTail,
  48. # Control flow switches
  49. UtilityCompare,
  50. UtilityChoose,
  51. # useful NoOp:
  52. UtilityPrint,
  53. ]
  54. def matrix_from_head_tail(head, tail):
  55. from mathutils import Vector, Quaternion, Matrix
  56. rotation = Vector((0,1,0)).rotation_difference((tail-head).normalized()).to_matrix()
  57. m= Matrix.LocRotScale(head, rotation, None)
  58. m[3][3] = (tail-head).length
  59. return m
  60. #*#-------------------------------#++#-------------------------------#*#
  61. # U T I L I T Y N O D E S
  62. #*#-------------------------------#++#-------------------------------#*#
  63. class InputFloat:
  64. '''A node representing float input'''
  65. def __init__(self, signature, base_tree):
  66. self.base_tree=base_tree
  67. self.signature = signature
  68. self.inputs = {}
  69. self.outputs = {"Float Input" : NodeSocket(name = "Float Input", node=self) }
  70. self.parameters = {"Float Input":None, "Mute":None}
  71. self.node_type = 'UTILITY'
  72. self.hierarchy_connections = []
  73. self.connections = []
  74. self.hierarchy_dependencies = []
  75. self.dependencies = []
  76. self.prepared = True
  77. self.executed = True
  78. def evaluate_input(self, input_name):
  79. return self.parameters["Float Input"]
  80. class InputIntNode:
  81. '''A node representing integer input'''
  82. def __init__(self, signature, base_tree):
  83. self.base_tree=base_tree
  84. self.signature = signature
  85. self.inputs = {}
  86. self.outputs = {"Integer" : NodeSocket(name = "Integer", node=self) }
  87. self.parameters = {"Integer":None, "Mute":None}
  88. self.node_type = 'UTILITY'
  89. self.hierarchy_connections = []
  90. self.connections = []
  91. self.hierarchy_dependencies = []
  92. self.dependencies = []
  93. self.prepared = True
  94. self.executed = True
  95. def evaluate_input(self, input_name):
  96. return self.parameters["Integer"]
  97. class InputVector:
  98. '''A node representing vector input'''
  99. def __init__(self, signature, base_tree):
  100. self.base_tree=base_tree
  101. self.signature = signature
  102. self.inputs = {}
  103. self.outputs = {"" : NodeSocket(name = '', node=self) }
  104. self.parameters = {'':None, "Mute":None}
  105. self.node_type = 'UTILITY'
  106. self.hierarchy_connections = []
  107. self.connections = []
  108. self.hierarchy_dependencies = []
  109. self.dependencies = []
  110. self.prepared = True
  111. self.executed = True
  112. def evaluate_input(self, input_name):
  113. return self.parameters[""]
  114. class InputBoolean:
  115. '''A node representing boolean input'''
  116. def __init__(self, signature, base_tree):
  117. self.base_tree=base_tree
  118. self.signature = signature
  119. self.inputs = {}
  120. self.outputs = {"" : NodeSocket(name = '', node=self) }
  121. self.parameters = {'':None, "Mute":None}
  122. self.node_type = 'UTILITY'
  123. self.hierarchy_connections = []
  124. self.connections = []
  125. self.hierarchy_dependencies = []
  126. self.dependencies = []
  127. self.prepared = True
  128. self.executed = True
  129. def evaluate_input(self, input_name):
  130. return self.parameters[""]
  131. class InputBooleanThreeTuple:
  132. '''A node representing a tuple of three booleans'''
  133. def __init__(self, signature, base_tree):
  134. self.base_tree=base_tree
  135. self.signature = signature
  136. self.inputs = {}
  137. self.outputs = {"" : NodeSocket(name = '', node=self) }
  138. self.parameters = {'':None, "Mute":None}
  139. self.node_type = 'UTILITY'
  140. self.hierarchy_connections = []
  141. self.connections = []
  142. self.hierarchy_dependencies = []
  143. self.dependencies = []
  144. self.prepared = True
  145. self.executed = True
  146. def evaluate_input(self, input_name):
  147. return self.parameters[""]
  148. class InputRotationOrder:
  149. '''A node representing string input for rotation order'''
  150. def __init__(self, signature, base_tree):
  151. self.base_tree=base_tree
  152. self.signature = signature
  153. self.inputs = {}
  154. self.outputs = {"" : NodeSocket(name = '', node=self) }
  155. self.parameters = {'':None, "Mute":None}
  156. self.node_type = 'UTILITY'
  157. self.hierarchy_connections = []
  158. self.connections = []
  159. self.hierarchy_dependencies = []
  160. self.dependencies = []
  161. self.prepared = True
  162. self.executed = True
  163. def evaluate_input(self, input_name):
  164. return self.parameters[""]
  165. class InputTransformSpace:
  166. '''A node representing string input for transform space'''
  167. def __init__(self, signature, base_tree):
  168. self.base_tree=base_tree
  169. self.signature = signature
  170. self.inputs = {}
  171. self.outputs = {"" : NodeSocket(name = '', node=self) }
  172. self.parameters = {'':None, "Mute":None}
  173. self.node_type = 'UTILITY'
  174. self.hierarchy_connections = []
  175. self.connections = []
  176. self.hierarchy_dependencies = []
  177. self.dependencies = []
  178. self.prepared = True
  179. self.executed = True
  180. def evaluate_input(self, input_name):
  181. return self.parameters[""]
  182. class InputString:
  183. '''A node representing string input'''
  184. def __init__(self, signature, base_tree):
  185. self.base_tree=base_tree
  186. self.signature = signature
  187. self.inputs = {}
  188. self.outputs = {"" : NodeSocket(name = '', node=self) }
  189. self.parameters = {'':None, "Mute":None}
  190. self.node_type = 'UTILITY'
  191. self.hierarchy_connections = []
  192. self.connections = []
  193. self.hierarchy_dependencies = []
  194. self.dependencies = []
  195. self.prepared = True
  196. self.executed = True
  197. def evaluate_input(self, input_name):
  198. return self.parameters[""]
  199. class InputMatrix:
  200. '''A node representing axis-angle quaternion input'''
  201. def __init__(self, signature, base_tree):
  202. self.base_tree=base_tree
  203. self.signature = signature
  204. self.inputs = {}
  205. self.outputs = {"Matrix" : NodeSocket(name = 'Matrix', node=self) }
  206. self.parameters = {'Matrix':None, "Mute":None}
  207. self.node_type = 'UTILITY'
  208. self.hierarchy_connections = []
  209. self.connections = []
  210. self.hierarchy_dependencies = []
  211. self.dependencies = []
  212. self.prepared = True
  213. self.executed = True
  214. #
  215. from mathutils import Matrix
  216. from .utilities import get_node_prototype
  217. node_prototype = get_node_prototype(self.signature, self.base_tree)
  218. matrix = ( node_prototype.first_row[ 0], node_prototype.first_row[ 1], node_prototype.first_row[ 2], node_prototype.first_row[ 3],
  219. node_prototype.second_row[0], node_prototype.second_row[1], node_prototype.second_row[2], node_prototype.second_row[3],
  220. node_prototype.third_row[ 0], node_prototype.third_row[ 1], node_prototype.third_row[ 2], node_prototype.third_row[ 3],
  221. node_prototype.fourth_row[0], node_prototype.fourth_row[1], node_prototype.fourth_row[2], node_prototype.fourth_row[3], )
  222. self.parameters["Matrix"] = Matrix([matrix[0:4], matrix[4:8], matrix[8:12], matrix[12:16]])
  223. def evaluate_input(self, input_name):
  224. return self.parameters["Matrix"]
  225. def fill_parameters(self):
  226. return
  227. class UtilityMatrixFromCurve:
  228. '''Get a matrix from a curve'''
  229. def __init__(self, signature, base_tree):
  230. self.base_tree=base_tree
  231. self.executed = False
  232. self.signature = signature
  233. self.inputs = {
  234. "Curve" : NodeSocket(is_input = True, name = "Curve", node=self),
  235. "Total Divisions" : NodeSocket(is_input = True, name = "Total Divisions", node=self),
  236. "Matrix Index" : NodeSocket(is_input = True, name = "Matrix Index", node=self),
  237. }
  238. self.outputs = {
  239. "Matrix" : NodeSocket(name = "Matrix", node=self),
  240. }
  241. self.parameters = {
  242. "Curve" : None,
  243. "Total Divisions" : None,
  244. "Matrix Index" : None,
  245. "Matrix" : None,
  246. }
  247. self.node_type = "UTILITY"
  248. self.hierarchy_connections, self.connections = [], []
  249. self.hierarchy_dependencies, self.dependencies = [], []
  250. self.prepared, self.executed = False, False
  251. def bPrepare(self, bContext = None,):
  252. from mathutils import Matrix
  253. import bpy
  254. m = Matrix.Identity(4)
  255. curve = bpy.data.objects.get(self.evaluate_input("Curve"))
  256. if not curve:
  257. prRed(f"No curve found for {self}. Using an Identity matrix instead.")
  258. m[3][3] = 1.0
  259. else:
  260. from .utilities import mesh_from_curve, data_from_ribbon_mesh
  261. if not bContext:
  262. # TODO find out if this is bad or a HACK or if it is OK
  263. bContext = bpy.context
  264. # IMPORTANT TODO: I need to be able to reuse this m
  265. # First, try to get the one we made before
  266. m_name = curve.name+'.'+self.base_tree.execution_id
  267. if not (m := bpy.data.meshes.get(m_name)):
  268. m = mesh_from_curve(curve, bContext)
  269. m.name = m_name
  270. #
  271. num_divisions = self.evaluate_input("Total Divisions")
  272. m_index = self.evaluate_input("Matrix Index")
  273. factors = [1/num_divisions*m_index, 1/num_divisions*(m_index+1)]
  274. data = data_from_ribbon_mesh(m, [factors], curve.matrix_world)
  275. # print(data)
  276. # this is in world space... let's just convert it back
  277. m = matrix_from_head_tail(data[0][0][0], data[0][0][1])
  278. m.translation -= curve.location
  279. # TODO HACK TODO
  280. # all the nodes should work in world-space, and it should be the responsibility
  281. # of the xForm node to convert!
  282. self.parameters["Matrix"] = m
  283. self.prepared = True
  284. self.executed = True
  285. def bFinalize(self, bContext=None):
  286. import bpy
  287. curve_name = self.evaluate_input("Curve")
  288. curve = bpy.data.objects.get(curve_name)
  289. m_name = curve.name+'.'+self.base_tree.execution_id
  290. if (mesh := bpy.data.meshes.get(m_name)):
  291. bpy.data.meshes.remove(mesh)
  292. def fill_parameters(self):
  293. fill_parameters(self)
  294. class UtilityPointFromCurve:
  295. '''Get a point from a curve'''
  296. def __init__(self, signature, base_tree):
  297. self.base_tree=base_tree
  298. self.executed = False
  299. self.signature = signature
  300. self.inputs = {
  301. "Curve" : NodeSocket(is_input = True, name = "Curve", node=self),
  302. "Factor" : NodeSocket(is_input = True, name = "Factor", node=self),
  303. }
  304. self.outputs = {
  305. "Point" : NodeSocket(name = "Point", node=self),
  306. }
  307. self.parameters = {
  308. "Curve" : None,
  309. "Factor" : None,
  310. "Point" : None,
  311. }
  312. self.node_type = "UTILITY"
  313. self.hierarchy_connections, self.connections = [], []
  314. self.hierarchy_dependencies, self.dependencies = [], []
  315. self.prepared, self.executed = False, False
  316. def bPrepare(self, bContext = None,):
  317. from mathutils import Matrix
  318. import bpy
  319. curve = bpy.data.objects.get(self.evaluate_input("Curve"))
  320. if not curve:
  321. raise RuntimeError(f"No curve found for {self}.")
  322. else:
  323. from .utilities import mesh_from_curve, data_from_ribbon_mesh
  324. if not bContext:
  325. # TODO find out if this is bad or a HACK or if it is OK
  326. bContext = bpy.context
  327. # IMPORTANT TODO: I need to be able to reuse this m
  328. # First, try to get the one we made before
  329. m_name = curve.name+'.'+self.base_tree.execution_id
  330. if not (m := bpy.data.meshes.get(m_name)):
  331. m = mesh_from_curve(curve, bContext)
  332. m.name = m_name
  333. #
  334. num_divisions = 1
  335. factors = [self.evaluate_input("Factor")]
  336. data = data_from_ribbon_mesh(m, [factors], curve.matrix_world)
  337. p = data[0][0][0] - curve.location
  338. self.parameters["Point"] = p
  339. self.prepared, self.executed = True, True
  340. def bFinalize(self, bContext=None):
  341. import bpy
  342. curve_name = self.evaluate_input("Curve")
  343. curve = bpy.data.objects.get(curve_name)
  344. m_name = curve.name+'.'+self.base_tree.execution_id
  345. if (mesh := bpy.data.meshes.get(m_name)):
  346. bpy.data.meshes.remove(mesh)
  347. def fill_parameters(self):
  348. fill_parameters(self)
  349. class UtilityMatricesFromCurve:
  350. '''Get matrices from a curve'''
  351. def __init__(self, signature, base_tree):
  352. self.base_tree=base_tree
  353. self.executed = False
  354. self.signature = signature
  355. self.inputs = {
  356. "Curve" : NodeSocket(is_input = True, name = "Curve", node=self),
  357. "Total Divisions" : NodeSocket(is_input = True, name = "Total Divisions", node=self),
  358. # "Matrix Index" : NodeSocket(is_input = True, name = "Matrix Index", node=self),
  359. }
  360. self.outputs = {
  361. "Matrices" : NodeSocket(name = "Matrices", node=self),
  362. }
  363. self.parameters = {
  364. "Curve" : None,
  365. "Total Divisions" : None,
  366. # "Matrix Index" : None,
  367. "Matrices" : None,
  368. }
  369. self.node_type = "UTILITY"
  370. self.hierarchy_connections = []
  371. self.connections = []
  372. self.hierarchy_dependencies = []
  373. self.dependencies = []
  374. self.prepared = False
  375. self.executed = False
  376. def bPrepare(self, bContext = None,):
  377. import time
  378. # start_time = time.time()
  379. #
  380. from mathutils import Matrix
  381. import bpy
  382. m = Matrix.Identity(4)
  383. curve_name = self.evaluate_input("Curve")
  384. curve = bpy.data.objects.get(curve_name)
  385. if not curve:
  386. prRed(f"No curve found for {self}. Using an Identity matrix instead.")
  387. m[3][3] = 1.0
  388. else:
  389. from .utilities import mesh_from_curve, data_from_ribbon_mesh
  390. if not bContext:
  391. bContext = bpy.context
  392. m_name = curve.name+'.'+self.base_tree.execution_id
  393. if not (mesh := bpy.data.meshes.get(m_name)):
  394. mesh = mesh_from_curve(curve, bContext)
  395. mesh.name = m_name
  396. num_divisions = self.evaluate_input("Total Divisions")
  397. factors = [0.0] + [(1/num_divisions*(i+1)) for i in range(num_divisions)]
  398. data = data_from_ribbon_mesh(mesh, [factors], curve.matrix_world)
  399. # 0 is the spline index. 0 selects points as opposed to normals or whatever.
  400. matrices = [matrix_from_head_tail(data[0][0][i], data[0][0][i+1]) for i in range(num_divisions)]
  401. for link in self.outputs["Matrices"].links:
  402. for i, m in enumerate(matrices):
  403. name = "Matrix"+str(i).zfill(4)
  404. if not (out := self.outputs.get(name)): # reuse them if there are multiple links.
  405. out = self.outputs[name] = NodeSocket(name = name, node=self)
  406. c = out.connect(link.to_node, link.to_socket)
  407. # prOrange(c)
  408. self.parameters[name] = m
  409. # print (mesh)
  410. link.die()
  411. self.prepared = True
  412. self.executed = True
  413. # prGreen(f"Matrices from curves took {time.time() - start_time} seconds.")
  414. def bFinalize(self, bContext=None):
  415. import bpy
  416. curve_name = self.evaluate_input("Curve")
  417. curve = bpy.data.objects.get(curve_name)
  418. m_name = curve.name+'.'+self.base_tree.execution_id
  419. if (mesh := bpy.data.meshes.get(m_name)):
  420. prGreen(f"Freeing mesh data {m_name}...")
  421. bpy.data.meshes.remove(mesh)
  422. def fill_parameters(self):
  423. fill_parameters(self)
  424. class UtilityMetaRig:
  425. '''A node representing an armature object'''
  426. def __init__(self, signature, base_tree):
  427. self.base_tree=base_tree
  428. self.executed = False
  429. self.signature = signature
  430. self.inputs = {
  431. "Meta-Armature" : NodeSocket(is_input = True, name = "Meta-Armature", node=self),
  432. "Meta-Bone" : NodeSocket(is_input = True, name = "Meta-Bone", node=self),
  433. }
  434. self.outputs = {
  435. "Matrix" : NodeSocket(name = "Matrix", node=self),
  436. }
  437. self.parameters = {
  438. "Meta-Armature" : None,
  439. "Meta-Bone" : None,
  440. }
  441. self.node_type = "UTILITY"
  442. self.hierarchy_connections = []
  443. self.connections = []
  444. self.hierarchy_dependencies = []
  445. self.dependencies = []
  446. self.prepared = False
  447. self.executed = False
  448. def bPrepare(self, bContext = None,):
  449. #kinda clumsy, whatever
  450. import bpy
  451. from mathutils import Matrix
  452. m = Matrix.Identity(4)
  453. meta_rig = self.evaluate_input("Meta-Armature")
  454. meta_bone = self.evaluate_input("Meta-Bone")
  455. if meta_rig:
  456. if ( armOb := bpy.data.objects.get(meta_rig) ):
  457. m = armOb.matrix_world
  458. if ( b := armOb.data.bones.get(meta_bone)):
  459. # calculate the correct object-space matrix
  460. m = Matrix.Identity(3)
  461. bones = [] # from the last ancestor, mult the matrices until we get to b
  462. while (b): bones.append(b); b = b.parent
  463. while (bones): b = bones.pop(); m = m @ b.matrix
  464. m = Matrix.Translation(b.head_local) @ m.to_4x4()
  465. #
  466. m[3][3] = b.length # this is where I arbitrarily decided to store length
  467. # else:
  468. # prRed("no bone for MetaRig node ", self)
  469. else:
  470. raise RuntimeError(wrapRed(f"No meta-rig input for MetaRig node {self}"))
  471. self.parameters["Matrix"] = m
  472. self.prepared = True
  473. self.executed = True
  474. def fill_parameters(self):
  475. fill_parameters(self)
  476. # self.parameters["Matrix"] = None # why in the
  477. class UtilityBoneProperties:
  478. '''A node representing an armature object'''
  479. def __init__(self, signature, base_tree):
  480. self.base_tree=base_tree
  481. self.executed = False
  482. self.signature = signature
  483. self.inputs = {}
  484. self.outputs = {
  485. "matrix" : NodeSocket(name = "matrix", node=self),
  486. "matrix_local" : NodeSocket(name = "matrix_local", node=self),
  487. "matrix_basis" : NodeSocket(name = "matrix_basis", node=self),
  488. "head" : NodeSocket(name = "head", node=self),
  489. "tail" : NodeSocket(name = "tail", node=self),
  490. "length" : NodeSocket(name = "length", node=self),
  491. "rotation" : NodeSocket(name = "rotation", node=self),
  492. "location" : NodeSocket(name = "location", node=self),
  493. "scale" : NodeSocket(name = "scale", node=self),
  494. }
  495. self.parameters = {
  496. "matrix":None,
  497. "matrix_local":None,
  498. "matrix_basis":None,
  499. "head":None,
  500. "tail":None,
  501. "length":None,
  502. "rotation":None,
  503. "location":None,
  504. "scale":None,
  505. }
  506. self.node_type = "UTILITY"
  507. #
  508. self.hierarchy_connections = []
  509. self.connections = []
  510. self.hierarchy_dependencies = []
  511. self.dependencies = []
  512. self.prepared = True
  513. self.executed = True
  514. def fill_parameters(self):
  515. pass#fill_parameters(self)
  516. # TODO this should probably be moved to Links
  517. class UtilityDriverVariable:
  518. '''A node representing an armature object'''
  519. def __init__(self, signature, base_tree):
  520. self.base_tree=base_tree
  521. self.executed = False
  522. self.signature = signature
  523. self.inputs = {
  524. "Variable Type" : NodeSocket(is_input = True, name = "Variable Type", node = self),
  525. "Property" : NodeSocket(is_input = True, name = "Property", node = self),
  526. "Property Index" : NodeSocket(is_input = True, name = "Property Index", node = self),
  527. "Evaluation Space" : NodeSocket(is_input = True, name = "Evaluation Space", node = self),
  528. "Rotation Mode" : NodeSocket(is_input = True, name = "Rotation Mode", node = self),
  529. "xForm 1" : NodeSocket(is_input = True, name = "xForm 1", node = self),
  530. "xForm 2" : NodeSocket(is_input = True, name = "xForm 2", node = self),
  531. }
  532. self.outputs = {
  533. "Driver Variable" : NodeSocket(name = "Driver Variable", node=self),
  534. }
  535. self.parameters = {
  536. "Variable Type":None,
  537. "Property":None,
  538. "Property Index":None,
  539. "Evaluation Space":None,
  540. "Rotation Mode":None,
  541. "xForm 1":None,
  542. "xForm 2":None,
  543. }
  544. self.node_type = "DRIVER" # MUST be run in Pose mode
  545. #
  546. self.hierarchy_connections = []
  547. self.connections = []
  548. self.hierarchy_dependencies = []
  549. self.dependencies = []
  550. self.prepared = True
  551. self.executed = False
  552. def evaluate_input(self, input_name):
  553. if input_name == 'Property':
  554. if self.inputs.get('Property'):
  555. if self.inputs['Property'].is_linked:
  556. # get the name instead...
  557. trace = trace_single_line(self, input_name)
  558. return trace[1].name # the name of the socket
  559. return self.parameters["Property"]
  560. return evaluate_input(self, input_name)
  561. def GetxForm(self, index=1):
  562. trace = trace_single_line(self, "xForm 1" if index == 1 else "xForm 2")
  563. for node in trace[0]:
  564. if (node.__class__ in [xFormArmature, xFormBone]):
  565. return node #this will fetch the first one, that's good!
  566. return None
  567. def bExecute(self, bContext = None,):
  568. prepare_parameters(self)
  569. #prPurple ("Executing Driver Variable Node")
  570. xForm1 = self.GetxForm()
  571. xForm2 = self.GetxForm(index=2)
  572. # kinda clumsy
  573. if xForm1 : xForm1 = xForm1.bGetObject()
  574. if xForm2 : xForm2 = xForm2.bGetObject()
  575. v_type = self.evaluate_input("Variable Type")
  576. i = self.evaluate_input("Property Index"); dVarChannel = ""
  577. if (i >= 0): #negative values will use the vector property.
  578. if self.evaluate_input("Property") == 'location':
  579. if i == 0: dVarChannel = "LOC_X"
  580. elif i == 1: dVarChannel = "LOC_Y"
  581. elif i == 2: dVarChannel = "LOC_Z"
  582. else: raise RuntimeError("Invalid property index for %s" % self)
  583. if self.evaluate_input("Property") == 'rotation':
  584. if i == 0: dVarChannel = "ROT_X"
  585. elif i == 1: dVarChannel = "ROT_Y"
  586. elif i == 2: dVarChannel = "ROT_Z"
  587. elif i == 3: dVarChannel = "ROT_W"
  588. else: raise RuntimeError("Invalid property index for %s" % self)
  589. if self.evaluate_input("Property") == 'scale':
  590. if i == 0: dVarChannel = "SCALE_X"
  591. elif i == 1: dVarChannel = "SCALE_Y"
  592. elif i == 2: dVarChannel = "SCALE_Z"
  593. elif i == 3: dVarChannel = "SCALE_AVG"
  594. else: raise RuntimeError("Invalid property index for %s" % self)
  595. if self.evaluate_input("Property") == 'scale_average':
  596. dVarChannel = "SCALE_AVG"
  597. if dVarChannel: v_type = "TRANSFORMS"
  598. my_var = {
  599. "owner" : xForm1, # will be filled in by Driver
  600. "prop" : self.evaluate_input("Property"), # will be filled in by Driver
  601. "type" : v_type,
  602. "space" : self.evaluate_input("Evaluation Space"),
  603. "rotation_mode" : self.evaluate_input("Rotation Mode"),
  604. "xForm 1" : xForm1,#self.GetxForm(index = 1),
  605. "xForm 2" : xForm2,#self.GetxForm(index = 2),
  606. "channel" : dVarChannel,}
  607. # Push parameter to downstream connected node.connected:
  608. if (out := self.outputs["Driver Variable"]).is_linked:
  609. self.parameters[out.name] = my_var
  610. for link in out.links:
  611. link.to_node.parameters[link.to_socket] = my_var
  612. self.executed = True
  613. class UtilityKeyframe:
  614. '''A node representing a keyframe for a F-Curve'''
  615. def __init__(self, signature, base_tree):
  616. self.base_tree=base_tree
  617. self.executed = False
  618. self.signature = signature
  619. self.inputs = {
  620. "Frame" : NodeSocket(is_input = True, name = "Frame", node = self),
  621. "Value" : NodeSocket(is_input = True, name = "Value", node = self),
  622. }
  623. self.outputs = {
  624. "Keyframe" : NodeSocket(name = "Keyframe", node=self),
  625. }
  626. self.parameters = {
  627. "Frame":None,
  628. "Value":None,
  629. "Keyframe":{}, # for some reason I have to initialize this and then use the dict... why? TODO find out
  630. }
  631. self.node_type = "DRIVER" # MUST be run in Pose mode
  632. setup_custom_props(self)
  633. self.hierarchy_connections = []
  634. self.connections = []
  635. self.hierarchy_dependencies = []
  636. self.dependencies = []
  637. self.prepared = False
  638. self.executed = False
  639. def bPrepare(self, bContext = None,):
  640. key = self.parameters["Keyframe"]
  641. from mathutils import Vector
  642. key["co"]= Vector( (self.evaluate_input("Frame"), self.evaluate_input("Value"),))
  643. key["type"]="GENERATED"
  644. key["interpolation"] = "LINEAR"
  645. # eventually this will have the right data, TODO
  646. # self.parameters["Keyframe"] = key
  647. self.prepared = True
  648. self.executed = True
  649. class UtilityFCurve:
  650. '''A node representing an armature object'''
  651. def __init__(self, signature, base_tree):
  652. self.base_tree=base_tree
  653. self.executed = False
  654. self.signature = signature
  655. self.inputs = {}
  656. self.outputs = {
  657. "fCurve" : NodeSocket(name = "fCurve", node=self),
  658. }
  659. self.parameters = {
  660. "Use_KeyFrame_Nodes": True, # HACK
  661. "fCurve":None,
  662. }
  663. self.node_type = "UTILITY"
  664. setup_custom_props(self)
  665. self.hierarchy_connections = []
  666. self.connections = []
  667. self.hierarchy_dependencies = []
  668. self.dependencies = []
  669. self.prepared = False
  670. self.executed = False
  671. def evaluate_input(self, input_name):
  672. return evaluate_input(self, input_name)
  673. def bExecute(self, bContext = None,):
  674. prepare_parameters(self)
  675. from .utilities import get_node_prototype
  676. np = get_node_prototype(self.signature, self.base_tree)
  677. keys = []
  678. #['amplitude', 'back', 'bl_rna', 'co', 'co_ui', 'easing', 'handle_left', 'handle_left_type', 'handle_right', 'handle_right_type',
  679. # 'interpolation', 'period', 'rna_type', 'select_control_point', 'select_left_handle', 'select_right_handle', 'type']
  680. if True: #np.use_kf_nodes:
  681. for k in self.inputs.keys():
  682. # print (self.inputs[k])
  683. if (key := self.evaluate_input(k)) is None:
  684. prOrange(f"WARN: No enough keyframes connected to {self}:{k}. Skipping Link.")
  685. else:
  686. keys.append(key)
  687. else:
  688. raise NotImplementedError("Getting the keys from an fCurve isn't really working anymore lol need to fix")
  689. fc_ob = np.fake_fcurve_ob
  690. fc = fc_ob.animation_data.action.fcurves[0]
  691. for k in fc.keyframe_points:
  692. key = {}
  693. for prop in dir(k):
  694. if ("__" in prop) or ("bl_" in prop): continue
  695. #it's __name__ or bl_rna or something
  696. key[prop] = getattr(k, prop)
  697. keys.append(key)
  698. # Push parameter to downstream connected node.connected:
  699. # TODO: find out if this is necesary, even
  700. if (out := self.outputs["fCurve"]).is_linked:
  701. self.parameters[out.name] = keys
  702. for link in out.links:
  703. link.to_node.parameters[link.to_socket] = keys
  704. # the above was a HACK. I don't want nodes modiying their descenedents.
  705. # If the above was necessary, I want to get an error from it so I can fix it in the descendent's class
  706. if len(keys) <1:
  707. prOrange(f"WARN: no keys in fCurve {self}.")
  708. self.prepared = True
  709. self.executed = True
  710. class UtilityDriver:
  711. '''A node representing an armature object'''
  712. def __init__(self, signature, base_tree):
  713. self.base_tree=base_tree
  714. self.executed = False
  715. self.signature = signature
  716. self.inputs = {
  717. "Driver Type" : NodeSocket(is_input = True, name = "Driver Type", node = self),
  718. "Expression" : NodeSocket(is_input = True, name = "Expression", node = self),
  719. "fCurve" : NodeSocket(is_input = True, name = "fCurve", node = self),
  720. }
  721. self.outputs = {
  722. "Driver" : NodeSocket(name = "Driver", node=self),
  723. }
  724. from .drivers import MantisDriver
  725. self.parameters = {
  726. "Driver Type":None,
  727. "Expression":None,
  728. "fCurve":None,
  729. "Driver":MantisDriver(),
  730. }
  731. self.node_type = "DRIVER" # MUST be run in Pose mode
  732. setup_custom_props(self)
  733. self.hierarchy_connections = []
  734. self.connections = []
  735. self.hierarchy_dependencies = []
  736. self.dependencies = []
  737. self.prepared = True
  738. self.executed = True
  739. def bExecute(self, bContext = None,):
  740. prepare_parameters(self)
  741. from .drivers import MantisDriver
  742. #prPurple("Executing Driver Node")
  743. my_vars = []
  744. if (keys := self.evaluate_input("fCurve")) is None:
  745. keys={}
  746. prWhite(f"INFO: no fCurve connected to {self}; using default fCurve.")
  747. from mathutils import Vector
  748. keys = [
  749. {"co":Vector( (0, 0,)), "type":"GENERATED", "interpolation":"LINEAR" },
  750. {"co":Vector( (1, 1,)), "type":"GENERATED", "interpolation":"LINEAR" },
  751. ]
  752. for inp in list(self.inputs.keys() )[3:]:
  753. if (new_var := self.evaluate_input(inp)):
  754. new_var["name"] = inp
  755. my_vars.append(new_var)
  756. else:
  757. raise RuntimeError(f"Failed to initialize Driver variable for {self}")
  758. my_driver ={ "owner" : None,
  759. "prop" : None, # will be filled out in the node that uses the driver
  760. "expression" : self.evaluate_input("Expression"),
  761. "ind" : -1, # same here
  762. "type" : self.evaluate_input("Driver Type"),
  763. "vars" : my_vars,
  764. "keys" : keys, }
  765. my_driver = MantisDriver(my_driver)
  766. self.parameters["Driver"].update(my_driver)
  767. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  768. self.executed = True
  769. class UtilitySwitch:
  770. '''A node representing an armature object'''
  771. def __init__(self, signature, base_tree):
  772. self.base_tree=base_tree
  773. self.executed = False
  774. self.signature = signature
  775. self.inputs = {
  776. # "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  777. "Parameter" : NodeSocket(is_input = True, name = "Parameter", node = self),
  778. "Parameter Index" : NodeSocket(is_input = True, name = "Parameter Index", node = self),
  779. "Invert Switch" : NodeSocket(is_input = True, name = "Invert Switch", node = self),
  780. }
  781. self.outputs = {
  782. "Driver" : NodeSocket(name = "Driver", node=self),
  783. }
  784. from .drivers import MantisDriver
  785. self.parameters = {
  786. # "xForm":None,
  787. "Parameter":None,
  788. "Parameter Index":None,
  789. "Invert Switch":None,
  790. "Driver":MantisDriver(), # empty for now
  791. }
  792. self.node_type = "DRIVER" # MUST be run in Pose mode
  793. self.hierarchy_connections = []
  794. self.connections = []
  795. self.hierarchy_dependencies = []
  796. self.dependencies = []
  797. self.prepared = True
  798. self.executed = False
  799. def evaluate_input(self, input_name):
  800. if input_name == 'Parameter':
  801. if self.inputs['Parameter'].is_connected:
  802. trace = trace_single_line(self, input_name)
  803. return trace[1].name # the name of the socket
  804. return self.parameters["Parameter"]
  805. return evaluate_input(self, input_name)
  806. def GetxForm(self,):
  807. trace = trace_single_line(self, "Parameter" )
  808. for node in trace[0]:
  809. if (node.__class__ in [xFormArmature, xFormBone]):
  810. return node #this will fetch the first one, that's good!
  811. return None
  812. def bExecute(self, bContext = None,):
  813. #prepare_parameters(self)
  814. #prPurple ("Executing Switch Node")
  815. xForm = self.GetxForm()
  816. if xForm : xForm = xForm.bGetObject()
  817. if not xForm:
  818. raise RuntimeError("Could not evaluate xForm for %s" % self)
  819. from .drivers import MantisDriver
  820. my_driver ={ "owner" : None,
  821. "prop" : None, # will be filled out in the node that uses the driver
  822. "ind" : -1, # same here
  823. "type" : "SCRIPTED",
  824. "vars" : [ { "owner" : xForm,
  825. "prop" : self.evaluate_input("Parameter"),
  826. "name" : "a",
  827. "type" : "SINGLE_PROP", } ],
  828. "keys" : [ { "co":(0,0),
  829. "interpolation": "LINEAR",
  830. "type":"KEYFRAME",}, #display type
  831. { "co":(1,1),
  832. "interpolation": "LINEAR",
  833. "type":"KEYFRAME",},], }
  834. my_driver ["expression"] = "a"
  835. my_driver = MantisDriver(my_driver)
  836. # this makes it so I can check for type later!
  837. if self.evaluate_input("Invert Switch") == True:
  838. my_driver ["expression"] = "1 - a"
  839. # this way, regardless of what order things are handled, the
  840. # driver is sent to the next node.
  841. # In the case of some drivers, the parameter may be sent out
  842. # before it's filled in (because there is a circular dependency)
  843. # I want to support this behaviour because Blender supports it,
  844. # but I also do not want to support it because it makes things
  845. # more complex and IMO it's bad practice.
  846. # We do not make a copy. We update the driver, so that
  847. # the same instance is filled out.
  848. self.parameters["Driver"].update(my_driver)
  849. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  850. self.executed = True
  851. class UtilityCombineThreeBool:
  852. '''A node for combining three booleans into a boolean three-tuple'''
  853. def __init__(self, signature, base_tree):
  854. self.base_tree=base_tree
  855. self.executed = False
  856. self.signature = signature
  857. self.inputs = {
  858. "X" : NodeSocket(is_input = True, name = "X", node = self),
  859. "Y" : NodeSocket(is_input = True, name = "Y", node = self),
  860. "Z" : NodeSocket(is_input = True, name = "Z", node = self),
  861. }
  862. self.outputs = {
  863. "Three-Bool" : NodeSocket(name = "Three-Bool", node=self),
  864. }
  865. self.parameters = {
  866. "X":None,
  867. "Y":None,
  868. "Z":None, }
  869. self.node_type = "UTILITY"
  870. self.hierarchy_connections = []
  871. self.connections = []
  872. self.hierarchy_dependencies = []
  873. self.dependencies = []
  874. self.prepared = False
  875. self.executed = False
  876. def bPrepare(self, bContext = None,):
  877. #prPurple("Executing CombineThreeBool Node")
  878. #prepare_parameters(self)
  879. self.parameters["Three-Bool"] = (
  880. self.evaluate_input("X"),
  881. self.evaluate_input("Y"),
  882. self.evaluate_input("Z"), )
  883. # DO:
  884. # figure out how to get the driver at execute-time
  885. # because Blender allows circular dependencies in drivers
  886. # (sort of), I need to adopt a more convoluted way of doing
  887. # things here or elsewhere.
  888. self.prepared = True
  889. self.executed = True
  890. # Note this is a copy of the above. This needs to be de-duplicated into
  891. # a simpler CombineVector node_container.
  892. # TODO
  893. class UtilityCombineVector:
  894. '''A node for combining three floats into a vector'''
  895. def __init__(self, signature, base_tree):
  896. self.base_tree=base_tree
  897. self.executed = False
  898. self.signature = signature
  899. self.inputs = {
  900. "X" : NodeSocket(is_input = True, name = "X", node = self),
  901. "Y" : NodeSocket(is_input = True, name = "Y", node = self),
  902. "Z" : NodeSocket(is_input = True, name = "Z", node = self),
  903. }
  904. self.outputs = {
  905. "Vector" : NodeSocket(name = "Vector", node=self),
  906. }
  907. self.parameters = {
  908. "X":None,
  909. "Y":None,
  910. "Z":None, }
  911. self.node_type = "UTILITY"
  912. self.hierarchy_connections = []
  913. self.connections = []
  914. self.hierarchy_dependencies = []
  915. self.dependencies = []
  916. self.prepared = False
  917. self.executed = False
  918. def bPrepare(self, bContext = None,):
  919. #prPurple("Executing CombineVector Node")
  920. prepare_parameters(self)
  921. self.parameters["Vector"] = (
  922. self.evaluate_input("X"),
  923. self.evaluate_input("Y"),
  924. self.evaluate_input("Z"), )
  925. self.prepared = True
  926. self.executed = True
  927. class UtilityCatStrings:
  928. '''A node representing an armature object'''
  929. def __init__(self, signature, base_tree):
  930. self.base_tree=base_tree
  931. self.executed = False
  932. self.signature = signature
  933. self.inputs = {
  934. "String_1" : NodeSocket(is_input = True, name = "String_1", node = self),
  935. "String_2" : NodeSocket(is_input = True, name = "String_2", node = self),
  936. }
  937. self.outputs = {
  938. "OutputString" : NodeSocket(name = "OutputString", node=self),
  939. }
  940. self.parameters = {
  941. "String_1":None,
  942. "String_2":None,
  943. }
  944. self.node_type = "UTILITY"
  945. self.hierarchy_connections = []
  946. self.connections = []
  947. self.hierarchy_dependencies = []
  948. self.dependencies = []
  949. self.prepared = False
  950. self.executed = False
  951. def bPrepare(self, bContext = None,):
  952. self.parameters["OutputString"] = self.evaluate_input("String_1")+self.evaluate_input("String_2")
  953. self.prepared = True
  954. self.executed = True
  955. class InputLayerMask:
  956. '''A node representing an armature object'''
  957. def __init__(self, signature, base_tree):
  958. self.base_tree=base_tree
  959. self.executed = False
  960. self.signature = signature
  961. self.inputs = {
  962. }
  963. self.outputs = {
  964. "Layer Mask" : NodeSocket(is_input = True, name = "Layer Mask", node = self),
  965. }
  966. self.parameters = {
  967. "Layer Mask":None,
  968. }
  969. self.node_type = "UTILITY"
  970. self.hierarchy_connections = []
  971. self.connections = []
  972. self.hierarchy_dependencies = []
  973. self.dependencies = []
  974. self.prepared = True
  975. self.executed = True
  976. class InputExistingGeometryObject:
  977. '''A node representing an existing object'''
  978. def __init__(self, signature, base_tree):
  979. self.base_tree=base_tree
  980. self.executed = False
  981. self.signature = signature
  982. self.inputs = {
  983. "Name" : NodeSocket(is_input = True, name = "Name", node = self),
  984. }
  985. self.outputs = {
  986. "Object" : NodeSocket(is_input = False, name = "Object", node=self),
  987. }
  988. self.parameters = {
  989. "Name":None,
  990. "Object":None,
  991. }
  992. self.node_type = "XFORM"
  993. self.hierarchy_connections = []
  994. self.connections = []
  995. self.hierarchy_dependencies = []
  996. self.dependencies = []
  997. self.prepared = False
  998. self.executed = False
  999. def bPrepare(self, bContext=None):
  1000. from bpy import data
  1001. name = self.evaluate_input("Name")
  1002. if name:
  1003. self.bObject = data.objects.get( name )
  1004. else:
  1005. self.bObject = None
  1006. if self is None and (name := self.evaluate_input("Name")):
  1007. prRed(f"No object found with name {name} in {self}")
  1008. self.prepared = True; self.executed = True
  1009. def bGetObject(self, mode=''):
  1010. return self.bObject
  1011. class InputExistingGeometryData:
  1012. '''A node representing existing object data'''
  1013. def __init__(self, signature, base_tree):
  1014. self.base_tree=base_tree
  1015. self.executed = False
  1016. self.signature = signature
  1017. self.inputs = {
  1018. "Name" : NodeSocket(is_input = True, name = "Name", node = self),
  1019. }
  1020. self.outputs = {
  1021. "Geometry" : NodeSocket(is_input = False, name = "Geometry", node=self),
  1022. }
  1023. self.parameters = {
  1024. "Name":None,
  1025. "Geometry":None,
  1026. }
  1027. self.node_type = "UTILITY"
  1028. self.hierarchy_connections = []
  1029. self.connections = []
  1030. self.hierarchy_dependencies = []
  1031. self.dependencies = []
  1032. self.prepared = True
  1033. self.executed = True
  1034. # mode for interface consistency
  1035. def bGetObject(self, mode=''):
  1036. from bpy import data
  1037. # first try Curve, then try Mesh
  1038. bObject = data.curves.get(self.evaluate_input("Name"))
  1039. if not bObject:
  1040. bObject = data.meshes.get(self.evaluate_input("Name"))
  1041. if bObject is None:
  1042. raise RuntimeError(f"Could not find a mesh or curve datablock named \"{self.evaluate_input('Name')}\" for node {self}")
  1043. return bObject
  1044. class UtilityGeometryOfXForm:
  1045. '''A node representing existing object data'''
  1046. def __init__(self, signature, base_tree):
  1047. self.base_tree=base_tree
  1048. self.executed = False
  1049. self.signature = signature
  1050. self.inputs = {
  1051. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1052. }
  1053. self.outputs = {
  1054. "Geometry" : NodeSocket(is_input = False, name = "Geometry", node=self),
  1055. }
  1056. self.parameters = {
  1057. "xForm":None,
  1058. "Geometry":None,
  1059. }
  1060. self.node_type = "UTILITY"
  1061. self.hierarchy_connections, self.connections = [], []
  1062. self.hierarchy_dependencies,self.dependencies = [], []
  1063. self.prepared, self.executed = True, True
  1064. # mode for interface consistency
  1065. def bGetObject(self, mode=''):
  1066. if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
  1067. prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
  1068. return None
  1069. xf = self.inputs["xForm"].links[0].from_node
  1070. if xf.node_type == 'XFORM':
  1071. xf_ob = xf.bGetObject()
  1072. if xf_ob.type in ['MESH', 'CURVE']:
  1073. return xf_ob.data
  1074. prOrange(f"WARN: Cannot retrieve data from {self}, the connected xForm is not a mesh or curve.")
  1075. return None
  1076. class UtilityNameOfXForm:
  1077. '''A node representing existing object data'''
  1078. def __init__(self, signature, base_tree):
  1079. self.base_tree=base_tree
  1080. self.executed = False
  1081. self.signature = signature
  1082. self.inputs = {
  1083. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1084. }
  1085. self.outputs = {
  1086. "Name" : NodeSocket(is_input = False, name = "Name", node=self),
  1087. }
  1088. self.parameters = {
  1089. "xForm":None,
  1090. "Name":'',
  1091. }
  1092. self.node_type = "UTILITY"
  1093. self.hierarchy_connections, self.connections = [], []
  1094. self.hierarchy_dependencies,self.dependencies = [], []
  1095. self.prepared, self.executed = False, False
  1096. # mode for interface consistency
  1097. def bPrepare(self, bContext = None,):
  1098. if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
  1099. prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
  1100. return ''
  1101. xf = self.inputs["xForm"].links[0].from_node
  1102. self.parameters["Name"] = xf.evaluate_input('Name')
  1103. self.prepared, self.executed = True, True
  1104. class UtilityGetBoneLength:
  1105. '''A node to get the length of a bone matrix'''
  1106. def __init__(self, signature, base_tree):
  1107. self.base_tree=base_tree
  1108. self.executed = False
  1109. self.signature = signature
  1110. self.inputs = {
  1111. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1112. }
  1113. self.outputs = {
  1114. "Bone Length" : NodeSocket(name = "Bone Length", node = self),
  1115. }
  1116. self.parameters = {
  1117. "Bone Length":None,
  1118. }
  1119. self.node_type = "UTILITY"
  1120. self.hierarchy_connections = []
  1121. self.connections = []
  1122. self.hierarchy_dependencies = []
  1123. self.dependencies = []
  1124. self.prepared = False
  1125. self.executed = False
  1126. def bPrepare(self, bContext = None,):
  1127. # print (self.inputs['Bone Matrix'].links)
  1128. if l := self.evaluate_input("Bone Matrix"):
  1129. self.parameters["Bone Length"] = l[3][3]
  1130. self.prepared = True
  1131. self.executed = True
  1132. class UtilityPointFromBoneMatrix:
  1133. '''A node representing an armature object'''
  1134. def __init__(self, signature, base_tree):
  1135. self.base_tree=base_tree
  1136. self.executed = False
  1137. self.signature = signature
  1138. self.inputs = {
  1139. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1140. "Head/Tail" : NodeSocket(is_input = True, name = "Head/Tail", node = self),
  1141. }
  1142. self.outputs = {
  1143. "Point" : NodeSocket(name = "Point", node = self),
  1144. }
  1145. self.parameters = {
  1146. "Bone Matrix":None,
  1147. "Head/Tail":None,
  1148. "Point":None,
  1149. }
  1150. self.node_type = "UTILITY"
  1151. self.hierarchy_connections = []
  1152. self.connections = []
  1153. self.hierarchy_dependencies = []
  1154. self.dependencies = []
  1155. self.prepared = False
  1156. self.executed = False
  1157. # TODO: find out why this is sometimes not ready at bPrepare phase
  1158. def bPrepare(self, bContext = None,):
  1159. from mathutils import Vector
  1160. matrix = self.evaluate_input("Bone Matrix")
  1161. head, rotation, _scale = matrix.copy().decompose()
  1162. tail = head.copy() + (rotation @ Vector((0,1,0)))*matrix[3][3]
  1163. self.parameters["Point"] = head.lerp(tail, self.evaluate_input("Head/Tail"))
  1164. self.prepared = True
  1165. self.executed = True
  1166. class UtilitySetBoneLength:
  1167. '''Sets the length of a Bone's matrix'''
  1168. def __init__(self, signature, base_tree):
  1169. self.base_tree=base_tree
  1170. self.executed = False
  1171. self.signature = signature
  1172. self.inputs = {
  1173. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1174. "Length" : NodeSocket(is_input = True, name = "Length", node = self),
  1175. }
  1176. self.outputs = {
  1177. "Bone Matrix" : NodeSocket(name = "Bone Matrix", node = self),
  1178. }
  1179. self.parameters = {
  1180. "Bone Matrix":None,
  1181. "Length":None,
  1182. }
  1183. self.node_type = "UTILITY"
  1184. self.hierarchy_connections = []
  1185. self.connections = []
  1186. self.hierarchy_dependencies = []
  1187. self.dependencies = []
  1188. self.prepared = False
  1189. self.executed = False
  1190. def bPrepare(self, bContext = None,):
  1191. from mathutils import Vector
  1192. if matrix := self.evaluate_input("Bone Matrix"):
  1193. matrix = matrix.copy()
  1194. # print (self.inputs["Length"].links)
  1195. matrix[3][3] = self.evaluate_input("Length")
  1196. self.parameters["Length"] = self.evaluate_input("Length")
  1197. self.parameters["Bone Matrix"] = matrix
  1198. self.prepared = True
  1199. self.executed = True
  1200. class UtilityMatrixSetLocation:
  1201. '''Sets the location of a matrix'''
  1202. def __init__(self, signature, base_tree):
  1203. self.base_tree=base_tree
  1204. self.executed = False
  1205. self.signature = signature
  1206. self.inputs = {
  1207. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1208. "Location" : NodeSocket(is_input = True, name = "Location", node = self),
  1209. }
  1210. self.outputs = {
  1211. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1212. }
  1213. self.parameters = {
  1214. "Matrix" : None,
  1215. "Location" : None,
  1216. }
  1217. self.node_type = "UTILITY"
  1218. self.connections, self.hierarchy_connections = [], []
  1219. self.dependencies, self.hierarchy_dependencies = [], []
  1220. self.prepared, self.executed = False,False
  1221. def bPrepare(self, bContext = None,):
  1222. from mathutils import Vector
  1223. if matrix := self.evaluate_input("Matrix"):
  1224. matrix = matrix.copy()
  1225. # print (self.inputs["Length"].links)
  1226. loc = self.evaluate_input("Location")
  1227. matrix[0][3] = loc[0]; matrix[1][3] = loc[1]; matrix[2][3] = loc[2]
  1228. self.parameters["Matrix"] = matrix
  1229. self.prepared = True
  1230. self.executed = True
  1231. class UtilityMatrixGetLocation:
  1232. '''Gets the location of a matrix'''
  1233. def __init__(self, signature, base_tree):
  1234. self.base_tree=base_tree
  1235. self.signature = signature
  1236. self.inputs = {
  1237. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1238. }
  1239. self.outputs = {
  1240. "Location" : NodeSocket(name = "Location", node = self),
  1241. }
  1242. self.parameters = {
  1243. "Matrix" : None,
  1244. "Location" : None,
  1245. }
  1246. self.node_type = "UTILITY"
  1247. self.connections, self.hierarchy_connections = [], []
  1248. self.dependencies, self.hierarchy_dependencies = [], []
  1249. self.prepared, self.executed = False,False
  1250. def bPrepare(self, bContext = None,):
  1251. from mathutils import Vector
  1252. if matrix := self.evaluate_input("Matrix"):
  1253. self.parameters["Location"] = matrix.to_translation()
  1254. self.prepared = True; self.executed = True
  1255. class UtilityMatrixFromXForm:
  1256. """Returns the matrix of the given xForm node."""
  1257. def __init__(self, signature, base_tree):
  1258. self.base_tree=base_tree
  1259. self.signature = signature
  1260. self.inputs = {
  1261. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1262. }
  1263. self.outputs = {
  1264. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1265. }
  1266. self.parameters = {
  1267. "Matrix" : None,
  1268. }
  1269. self.node_type = "UTILITY"
  1270. self.connections, self.hierarchy_connections = [], []
  1271. self.dependencies, self.hierarchy_dependencies = [], []
  1272. self.prepared, self.executed = False,False
  1273. def GetxForm(self):
  1274. trace = trace_single_line(self, "xForm")
  1275. for node in trace[0]:
  1276. if (node.node_type == 'XFORM'):
  1277. return node
  1278. raise GraphError("%s is not connected to an xForm" % self)
  1279. def bPrepare(self, bContext = None,):
  1280. from mathutils import Vector, Matrix
  1281. self.parameters["Matrix"] = Matrix.Identity(4)
  1282. if matrix := self.GetxForm().parameters.get("Matrix"):
  1283. self.parameters["Matrix"] = matrix.copy()
  1284. elif hasattr(self.GetxForm().bObject, "matrix"):
  1285. self.parameters["Matrix"] = self.GetxForm().bObject.matrix.copy()
  1286. elif hasattr(self.GetxForm().bObject, "matrix_world"):
  1287. self.parameters["Matrix"] = self.GetxForm().bObject.matrix_world.copy()
  1288. else:
  1289. prRed(f"Could not find matrix for {self} - check if the referenced object exists.")
  1290. self.prepared = True; self.executed = True
  1291. class UtilityAxesFromMatrix:
  1292. """Returns the axes of the given matrix."""
  1293. def __init__(self, signature, base_tree):
  1294. self.base_tree=base_tree
  1295. self.signature = signature
  1296. self.inputs = {
  1297. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1298. }
  1299. self.outputs = {
  1300. "X Axis" : NodeSocket(name = "X Axis", node = self),
  1301. "Y Axis" : NodeSocket(name = "Y Axis", node = self),
  1302. "Z Axis" : NodeSocket(name = "Z Axis", node = self),
  1303. }
  1304. self.parameters = {
  1305. "Matrix" : None,
  1306. "X Axis" : None,
  1307. "Y Axis" : None,
  1308. "Z Axis" : None,
  1309. }
  1310. self.node_type = "UTILITY"
  1311. self.connections, self.hierarchy_connections = [], []
  1312. self.dependencies, self.hierarchy_dependencies = [], []
  1313. self.prepared, self.executed = False,False
  1314. def bPrepare(self, bContext = None,):
  1315. from mathutils import Vector
  1316. if matrix := self.evaluate_input("Matrix"):
  1317. matrix= matrix.copy().to_3x3()
  1318. self.parameters['X Axis'] = matrix @ Vector((1,0,0))
  1319. self.parameters['Y Axis'] = matrix @ Vector((0,1,0))
  1320. self.parameters['Z Axis'] = matrix @ Vector((0,0,1))
  1321. self.prepared = True; self.executed = True
  1322. class UtilityBoneMatrixHeadTailFlip:
  1323. def __init__(self, signature, base_tree):
  1324. self.base_tree=base_tree
  1325. self.executed = False
  1326. self.signature = signature
  1327. self.inputs = {
  1328. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1329. }
  1330. self.outputs = {
  1331. "Bone Matrix" : NodeSocket(name = "Bone Matrix", node = self),
  1332. }
  1333. self.parameters = {
  1334. "Bone Matrix":None,
  1335. }
  1336. self.node_type = "UTILITY"
  1337. self.hierarchy_connections = []
  1338. self.connections = []
  1339. self.hierarchy_dependencies = []
  1340. self.dependencies = []
  1341. self.prepared = False
  1342. self.executed = False
  1343. def bPrepare(self, bContext = None,):
  1344. from mathutils import Vector, Matrix, Quaternion
  1345. from bpy.types import Bone
  1346. if matrix := self.evaluate_input("Bone Matrix"):
  1347. axis, roll = Bone.AxisRollFromMatrix(matrix.to_3x3())
  1348. new_mat = Bone.MatrixFromAxisRoll(-1*axis, roll)
  1349. length = matrix[3][3]
  1350. new_mat.resize_4x4() # last column contains
  1351. new_mat[0][3] = matrix[0][3] + axis[0]*length # x location
  1352. new_mat[1][3] = matrix[1][3] + axis[1]*length # y location
  1353. new_mat[2][3] = matrix[2][3] + axis[2]*length # z location
  1354. new_mat[3][3] = length # length
  1355. self.parameters["Bone Matrix"] = new_mat
  1356. self.prepared, self.executed = True, True
  1357. class UtilityMatrixTransform:
  1358. def __init__(self, signature, base_tree):
  1359. self.base_tree=base_tree
  1360. self.executed = False
  1361. self.signature = signature
  1362. self.inputs = {
  1363. "Matrix 1" : NodeSocket(is_input = True, name = "Matrix 1", node = self),
  1364. "Matrix 2" : NodeSocket(is_input = True, name = "Matrix 2", node = self),
  1365. }
  1366. self.outputs = {
  1367. "Out Matrix" : NodeSocket(name = "Out Matrix", node = self),
  1368. }
  1369. self.parameters = {
  1370. "Matrix 1" : None,
  1371. "Matrix 2" : None,
  1372. "Out Matrix" : None,
  1373. }
  1374. self.node_type = "UTILITY"
  1375. self.hierarchy_connections = []
  1376. self.connections = []
  1377. self.hierarchy_dependencies = []
  1378. self.dependencies = []
  1379. self.prepared = False
  1380. self.executed = False
  1381. def bPrepare(self, bContext = None,):
  1382. from mathutils import Vector
  1383. mat1 = self.evaluate_input("Matrix 1"); mat2 = self.evaluate_input("Matrix 2")
  1384. if mat1 and mat2:
  1385. mat1copy = mat1.copy()
  1386. self.parameters["Out Matrix"] = mat2 @ mat1copy
  1387. self.parameters["Out Matrix"].translation = mat1copy.to_translation()+ mat2.to_translation()
  1388. else:
  1389. raise RuntimeError(wrapRed(f"Node {self} did not receive all matrix inputs... found input 1? {mat1 is not None}, 2? {mat2 is not None}"))
  1390. self.prepared = True
  1391. self.executed = True
  1392. class UtilityTransformationMatrix:
  1393. def __init__(self, signature, base_tree):
  1394. self.base_tree=base_tree
  1395. self.executed = False
  1396. self.signature = signature
  1397. self.inputs = {
  1398. "Operation" : NodeSocket(is_input = True, name = "Operation", node = self),
  1399. "Vector" : NodeSocket(is_input = True, name = "Vector", node = self),
  1400. "W" : NodeSocket(is_input = True, name = "W", node = self),
  1401. }
  1402. self.outputs = {
  1403. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1404. }
  1405. self.parameters = {
  1406. "Operation" : None,
  1407. "Origin" : None,
  1408. "Vector" : None,
  1409. "W" : None,
  1410. "Matrix" : None,
  1411. }
  1412. self.node_type = "UTILITY"
  1413. self.hierarchy_connections = []
  1414. self.connections = []
  1415. self.hierarchy_dependencies = []
  1416. self.dependencies = []
  1417. self.prepared = False
  1418. self.executed = False
  1419. def bPrepare(self, bContext = None,):
  1420. from mathutils import Matrix, Vector
  1421. if (operation := self.evaluate_input("Operation")) == 'ROTATE_AXIS_ANGLE':
  1422. # this can, will, and should fail if the axis is 0,0,0
  1423. self.parameters["Matrix"] = rotMat = Matrix.Rotation(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1424. elif (operation := self.evaluate_input("Operation")) == 'TRANSLATE':
  1425. m = Matrix.Identity(4)
  1426. if axis := self.evaluate_input("Vector"):
  1427. m[0][3]=axis[0];m[1][3]=axis[1];m[2][3]=axis[2]
  1428. self.parameters['Matrix'] = m
  1429. elif (operation := self.evaluate_input("Operation")) == 'SCALE':
  1430. self.parameters["Matrix"] = Matrix.Scale(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1431. else:
  1432. raise NotImplementedError(self.evaluate_input("Operation").__repr__()+ " Operation not yet implemented.")
  1433. self.prepared = True
  1434. self.executed = True
  1435. class UtilityIntToString:
  1436. def __init__(self, signature, base_tree):
  1437. self.base_tree=base_tree
  1438. self.executed = False
  1439. self.signature = signature
  1440. self.inputs = {
  1441. "Number" : NodeSocket(is_input = True, name = "Number", node = self),
  1442. "Zero Padding" : NodeSocket(is_input = True, name = "Zero Padding", node = self),
  1443. }
  1444. self.outputs = {
  1445. "String" : NodeSocket(name = "String", node = self),
  1446. }
  1447. self.parameters = {
  1448. "Number" : None,
  1449. "Zero Padding" : None,
  1450. "String" : None,
  1451. }
  1452. self.node_type = "UTILITY"
  1453. self.hierarchy_connections = []
  1454. self.connections = []
  1455. self.hierarchy_dependencies = []
  1456. self.dependencies = []
  1457. self.prepared = False
  1458. self.executed = False
  1459. def bPrepare(self, bContext = None,):
  1460. number = self.evaluate_input("Number")
  1461. zeroes = self.evaluate_input("Zero Padding")
  1462. # I'm casting to int because I want to support any number, even though the node asks for int.
  1463. self.parameters["String"] = str(int(number)).zfill(int(zeroes))
  1464. self.prepared = True
  1465. self.executed = True
  1466. class UtilityArrayGet:
  1467. def __init__(self, signature, base_tree):
  1468. self.base_tree=base_tree
  1469. self.executed = False
  1470. self.signature = signature
  1471. self.inputs = {
  1472. "Index" : NodeSocket(is_input = True, name = "Index", node = self),
  1473. "OoB Behaviour" : NodeSocket(is_input = True, name = "OoB Behaviour", node = self),
  1474. "Array" : NodeSocket(is_input = True, name = "Array", node = self),
  1475. }
  1476. self.outputs = {
  1477. "Output" : NodeSocket(name = "Output", node = self),
  1478. }
  1479. self.parameters = {
  1480. "Index" : None,
  1481. "OoB Behaviour" : None,
  1482. "Array" : None,
  1483. "Output" : None,
  1484. }
  1485. self.node_type = "UTILITY"
  1486. self.hierarchy_connections = []
  1487. self.connections = []
  1488. self.hierarchy_dependencies = []
  1489. self.dependencies = []
  1490. self.prepared = False
  1491. self.executed = False
  1492. def bPrepare(self, bContext = None,):
  1493. if self.prepared == False:
  1494. # sort the array entries
  1495. for inp in self.inputs.values():
  1496. inp.links.sort(key=lambda a : -a.multi_input_sort_id)
  1497. oob = self.evaluate_input("OoB Behaviour")
  1498. index = self.evaluate_input("Index")
  1499. from .utilities import cap, wrap
  1500. # we must assume that the array has sent the correct number of links
  1501. if oob == 'WRAP':
  1502. index = index % len(self.inputs['Array'].links)
  1503. if oob == 'HOLD':
  1504. index = cap(index, len(self.inputs['Array'].links)-1)
  1505. # relink the connections and then kill all the links to and from the array
  1506. from .utilities import init_connections, init_dependencies
  1507. l = self.inputs["Array"].links[index]
  1508. for link in self.outputs["Output"].links:
  1509. to_node = link.to_node
  1510. l.from_node.outputs[l.from_socket].connect(to_node, link.to_socket)
  1511. link.die()
  1512. init_dependencies(to_node)
  1513. from_node=l.from_node
  1514. for inp in self.inputs.values():
  1515. for l in inp.links:
  1516. l.die()
  1517. init_connections(from_node)
  1518. if self in from_node.hierarchy_connections:
  1519. raise RuntimeError()
  1520. # this is intentional because the Array Get is kind of a weird hybrid between a Utility and a Schema
  1521. # so it should be removed from the tree when it is done. it has already dealt with the actual links.
  1522. # however I think this is redundant. Check.
  1523. self.hierarchy_connections, self.connections = [], []
  1524. self.hierarchy_dependencies, self.dependencies = [], []
  1525. self.prepared = True
  1526. self.executed = True
  1527. class UtilitySetBoneMatrixTail:
  1528. def __init__(self, signature, base_tree):
  1529. self.base_tree=base_tree
  1530. self.executed = False
  1531. self.signature = signature
  1532. self.inputs = {
  1533. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1534. "Tail Location" : NodeSocket(is_input = True, name = "Tail Location", node = self),
  1535. }
  1536. self.outputs = {
  1537. "Result" : NodeSocket(name = "Result", node = self),
  1538. }
  1539. self.parameters = {
  1540. "Matrix" : None,
  1541. "Tail Location" : None,
  1542. "Result" : None,
  1543. }
  1544. self.node_type = "UTILITY"
  1545. self.hierarchy_connections, self.connections = [], []
  1546. self.hierarchy_dependencies, self.dependencies = [], []
  1547. self.prepared, self.executed = False, False
  1548. def bPrepare(self, bContext = None,):
  1549. from mathutils import Matrix
  1550. matrix = self.evaluate_input("Matrix")
  1551. if matrix is None: matrix = Matrix.Identity(4)
  1552. #just do this for now lol
  1553. self.parameters["Result"] = matrix_from_head_tail(matrix.translation, self.evaluate_input("Tail Location"))
  1554. class UtilityPrint:
  1555. def __init__(self, signature, base_tree):
  1556. self.base_tree=base_tree
  1557. self.executed = False
  1558. self.signature = signature
  1559. self.inputs = {
  1560. "Input" : NodeSocket(is_input = True, name = "Input", node = self),
  1561. }
  1562. self.outputs = {}
  1563. self.parameters = {
  1564. "Input" : None,
  1565. }
  1566. self.node_type = "UTILITY"
  1567. self.hierarchy_connections = []
  1568. self.connections = []
  1569. self.hierarchy_dependencies = []
  1570. self.dependencies = []
  1571. self.prepared = False
  1572. self.executed = False
  1573. def bPrepare(self, bContext = None,):
  1574. if my_input := self.evaluate_input("Input"):
  1575. print("Preparation phase: ", wrapWhite(self), wrapGreen(my_input))
  1576. # else:
  1577. # prRed("No input to print.")
  1578. self.prepared = True
  1579. def bExecute(self, bContext = None,):
  1580. if my_input := self.evaluate_input("Input"):
  1581. print("Execution phase: ", wrapWhite(self), wrapGreen(my_input))
  1582. # else:
  1583. # prRed("No input to print.")
  1584. self.executed = True
  1585. class UtilityCompare:
  1586. def __init__(self, signature, base_tree):
  1587. self.base_tree=base_tree
  1588. self.executed = False
  1589. self.signature = signature
  1590. self.inputs = {
  1591. "A" : NodeSocket(is_input = True, name = "A", node = self),
  1592. "B" : NodeSocket(is_input = True, name = "B", node = self),
  1593. }
  1594. self.outputs = {
  1595. "Result" : NodeSocket(name = "Result", node = self),
  1596. }
  1597. self.parameters = {
  1598. "A" : None,
  1599. "B" : None,
  1600. "Result" : None,
  1601. }
  1602. self.node_type = "UTILITY"
  1603. self.hierarchy_connections, self.connections = [], []
  1604. self.hierarchy_dependencies, self.dependencies = [], []
  1605. self.prepared, self.executed = False, False
  1606. def bPrepare(self, bContext = None,):
  1607. self.parameters["Result"] = self.evaluate_input("A") == self.evaluate_input("B")
  1608. self.prepared = True; self.executed = True
  1609. class UtilityChoose:
  1610. def __init__(self, signature, base_tree):
  1611. self.base_tree=base_tree
  1612. self.executed = False
  1613. self.signature = signature
  1614. self.inputs = {
  1615. "Condition" : NodeSocket(is_input = True, name = "Condition", node = self),
  1616. "A" : NodeSocket(is_input = True, name = "A", node = self),
  1617. "B" : NodeSocket(is_input = True, name = "B", node = self),
  1618. }
  1619. self.outputs = {
  1620. "Result" : NodeSocket(name = "Result", node = self),
  1621. }
  1622. self.parameters = {
  1623. "Condition" : None,
  1624. "A" : None,
  1625. "B" : None,
  1626. "Result" : None,
  1627. }
  1628. self.node_type = "UTILITY"
  1629. self.hierarchy_connections, self.connections = [], []
  1630. self.hierarchy_dependencies, self.dependencies = [], []
  1631. self.prepared, self.executed = False, False
  1632. def bPrepare(self, bContext = None,):
  1633. condition = self.evaluate_input("Condition")
  1634. if condition:
  1635. self.parameters["Result"] = self.evaluate_input("B")
  1636. else:
  1637. self.parameters["Result"] = self.evaluate_input("A")
  1638. self.prepared = True
  1639. self.executed = True
  1640. for c in TellClasses():
  1641. setup_container(c)