misc_containers.py 69 KB

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