misc_containers.py 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865
  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 keyframe 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. print (keys)
  695. self.parameters["fCurve"] = keys
  696. self.prepared = True
  697. self.executed = True
  698. #TODO make the fCurve data a data class instead of a dict
  699. class UtilityDriver:
  700. '''A node representing an armature object'''
  701. def __init__(self, signature, base_tree):
  702. self.base_tree=base_tree
  703. self.executed = False
  704. self.signature = signature
  705. self.inputs = {
  706. "Driver Type" : NodeSocket(is_input = True, name = "Driver Type", node = self),
  707. "Expression" : NodeSocket(is_input = True, name = "Expression", node = self),
  708. "fCurve" : NodeSocket(is_input = True, name = "fCurve", node = self),
  709. }
  710. self.outputs = {
  711. "Driver" : NodeSocket(name = "Driver", node=self),
  712. }
  713. from .drivers import MantisDriver
  714. self.parameters = {
  715. "Driver Type":None,
  716. "Expression":None,
  717. "fCurve":None,
  718. "Driver":MantisDriver(),
  719. }
  720. self.node_type = "DRIVER" # MUST be run in Pose mode
  721. setup_custom_props(self)
  722. self.hierarchy_connections = []
  723. self.connections = []
  724. self.hierarchy_dependencies = []
  725. self.dependencies = []
  726. self.prepared = True
  727. self.executed = True
  728. def bExecute(self, bContext = None,):
  729. prepare_parameters(self)
  730. from .drivers import MantisDriver
  731. #prPurple("Executing Driver Node")
  732. my_vars = []
  733. keys = self.evaluate_input("fCurve")
  734. if len(keys) <2:
  735. prWhite(f"INFO: no fCurve connected to {self}; using default fCurve.")
  736. from mathutils import Vector
  737. keys = [
  738. {"co":Vector( (0, 0,)), "type":"GENERATED", "interpolation":"LINEAR" },
  739. {"co":Vector( (1, 1,)), "type":"GENERATED", "interpolation":"LINEAR" },
  740. "CONSTANT",]
  741. for inp in list(self.inputs.keys() )[3:]:
  742. if (new_var := self.evaluate_input(inp)):
  743. new_var["name"] = inp
  744. my_vars.append(new_var)
  745. else:
  746. raise RuntimeError(f"Failed to initialize Driver variable for {self}")
  747. my_driver ={ "owner" : None,
  748. "prop" : None, # will be filled out in the node that uses the driver
  749. "expression" : self.evaluate_input("Expression"),
  750. "ind" : -1, # same here
  751. "type" : self.evaluate_input("Driver Type"),
  752. "vars" : my_vars,
  753. "keys" : keys[:-1],
  754. "extrapolation" : keys[-1] }
  755. my_driver = MantisDriver(my_driver)
  756. self.parameters["Driver"].update(my_driver)
  757. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  758. self.executed = True
  759. class UtilitySwitch:
  760. '''A node representing an armature object'''
  761. def __init__(self, signature, base_tree):
  762. self.base_tree=base_tree
  763. self.executed = False
  764. self.signature = signature
  765. self.inputs = {
  766. # "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  767. "Parameter" : NodeSocket(is_input = True, name = "Parameter", node = self),
  768. "Parameter Index" : NodeSocket(is_input = True, name = "Parameter Index", node = self),
  769. "Invert Switch" : NodeSocket(is_input = True, name = "Invert Switch", node = self),
  770. }
  771. self.outputs = {
  772. "Driver" : NodeSocket(name = "Driver", node=self),
  773. }
  774. from .drivers import MantisDriver
  775. self.parameters = {
  776. # "xForm":None,
  777. "Parameter":None,
  778. "Parameter Index":None,
  779. "Invert Switch":None,
  780. "Driver":MantisDriver(), # empty for now
  781. }
  782. self.node_type = "DRIVER" # MUST be run in Pose mode
  783. self.hierarchy_connections = []
  784. self.connections = []
  785. self.hierarchy_dependencies = []
  786. self.dependencies = []
  787. self.prepared = True
  788. self.executed = False
  789. def evaluate_input(self, input_name):
  790. if input_name == 'Parameter':
  791. if self.inputs['Parameter'].is_connected:
  792. trace = trace_single_line(self, input_name)
  793. return trace[1].name # the name of the socket
  794. return self.parameters["Parameter"]
  795. return evaluate_input(self, input_name)
  796. def GetxForm(self,):
  797. trace = trace_single_line(self, "Parameter" )
  798. for node in trace[0]:
  799. if (node.__class__ in [xFormArmature, xFormBone]):
  800. return node #this will fetch the first one, that's good!
  801. return None
  802. def bExecute(self, bContext = None,):
  803. #prepare_parameters(self)
  804. #prPurple ("Executing Switch Node")
  805. xForm = self.GetxForm()
  806. if xForm : xForm = xForm.bGetObject()
  807. if not xForm:
  808. raise RuntimeError("Could not evaluate xForm for %s" % self)
  809. from .drivers import MantisDriver
  810. my_driver ={ "owner" : None,
  811. "prop" : None, # will be filled out in the node that uses the driver
  812. "ind" : -1, # same here
  813. "type" : "SCRIPTED",
  814. "vars" : [ { "owner" : xForm,
  815. "prop" : self.evaluate_input("Parameter"),
  816. "name" : "a",
  817. "type" : "SINGLE_PROP", } ],
  818. "keys" : [ { "co":(0,0),
  819. "interpolation": "LINEAR",
  820. "type":"KEYFRAME",}, #display type
  821. { "co":(1,1),
  822. "interpolation": "LINEAR",
  823. "type":"KEYFRAME",},],
  824. "extrapolation": 'CONSTANT', }
  825. my_driver ["expression"] = "a"
  826. my_driver = MantisDriver(my_driver)
  827. # this makes it so I can check for type later!
  828. if self.evaluate_input("Invert Switch") == True:
  829. my_driver ["expression"] = "1 - a"
  830. # this way, regardless of what order things are handled, the
  831. # driver is sent to the next node.
  832. # In the case of some drivers, the parameter may be sent out
  833. # before it's filled in (because there is a circular dependency)
  834. # I want to support this behaviour because Blender supports it,
  835. # but I also do not want to support it because it makes things
  836. # more complex and IMO it's bad practice.
  837. # We do not make a copy. We update the driver, so that
  838. # the same instance is filled out.
  839. self.parameters["Driver"].update(my_driver)
  840. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  841. self.executed = True
  842. class UtilityCombineThreeBool:
  843. '''A node for combining three booleans into a boolean three-tuple'''
  844. def __init__(self, signature, base_tree):
  845. self.base_tree=base_tree
  846. self.executed = False
  847. self.signature = signature
  848. self.inputs = {
  849. "X" : NodeSocket(is_input = True, name = "X", node = self),
  850. "Y" : NodeSocket(is_input = True, name = "Y", node = self),
  851. "Z" : NodeSocket(is_input = True, name = "Z", node = self),
  852. }
  853. self.outputs = {
  854. "Three-Bool" : NodeSocket(name = "Three-Bool", node=self),
  855. }
  856. self.parameters = {
  857. "X":None,
  858. "Y":None,
  859. "Z":None, }
  860. self.node_type = "UTILITY"
  861. self.hierarchy_connections = []
  862. self.connections = []
  863. self.hierarchy_dependencies = []
  864. self.dependencies = []
  865. self.prepared = False
  866. self.executed = False
  867. def bPrepare(self, bContext = None,):
  868. #prPurple("Executing CombineThreeBool Node")
  869. #prepare_parameters(self)
  870. self.parameters["Three-Bool"] = (
  871. self.evaluate_input("X"),
  872. self.evaluate_input("Y"),
  873. self.evaluate_input("Z"), )
  874. # DO:
  875. # figure out how to get the driver at execute-time
  876. # because Blender allows circular dependencies in drivers
  877. # (sort of), I need to adopt a more convoluted way of doing
  878. # things here or elsewhere.
  879. self.prepared = True
  880. self.executed = True
  881. # Note this is a copy of the above. This needs to be de-duplicated into
  882. # a simpler CombineVector node_container.
  883. # TODO
  884. class UtilityCombineVector:
  885. '''A node for combining three floats into a vector'''
  886. def __init__(self, signature, base_tree):
  887. self.base_tree=base_tree
  888. self.executed = False
  889. self.signature = signature
  890. self.inputs = {
  891. "X" : NodeSocket(is_input = True, name = "X", node = self),
  892. "Y" : NodeSocket(is_input = True, name = "Y", node = self),
  893. "Z" : NodeSocket(is_input = True, name = "Z", node = self),
  894. }
  895. self.outputs = {
  896. "Vector" : NodeSocket(name = "Vector", node=self),
  897. }
  898. self.parameters = {
  899. "X":None,
  900. "Y":None,
  901. "Z":None, }
  902. self.node_type = "UTILITY"
  903. self.hierarchy_connections = []
  904. self.connections = []
  905. self.hierarchy_dependencies = []
  906. self.dependencies = []
  907. self.prepared = False
  908. self.executed = False
  909. def bPrepare(self, bContext = None,):
  910. #prPurple("Executing CombineVector Node")
  911. prepare_parameters(self)
  912. self.parameters["Vector"] = (
  913. self.evaluate_input("X"),
  914. self.evaluate_input("Y"),
  915. self.evaluate_input("Z"), )
  916. self.prepared = True
  917. self.executed = True
  918. # TODO
  919. class UtilitySeparateVector:
  920. '''A node for separating a vector into three floats'''
  921. def __init__(self, signature, base_tree):
  922. self.base_tree=base_tree
  923. self.executed = False
  924. self.signature = signature
  925. self.inputs = {
  926. "Vector" : NodeSocket(is_input = True, name = "Vector", node=self),
  927. }
  928. self.outputs = {
  929. "X" : NodeSocket(name = "X", node = self),
  930. "Y" : NodeSocket(name = "Y", node = self),
  931. "Z" : NodeSocket(name = "Z", node = self),
  932. }
  933. self.parameters = {
  934. "X" : None,
  935. "Y" : None,
  936. "Z" : None, }
  937. self.node_type = "UTILITY"
  938. self.hierarchy_connections, self.connections = [], []
  939. self.hierarchy_dependencies, self.dependencies = [], []
  940. self.prepared, self.executed = False, False
  941. def bPrepare(self, bContext = None,):
  942. # prepare_parameters(self)
  943. self.parameters["X"] = self.evaluate_input("Vector")[0]
  944. self.parameters["Y"] = self.evaluate_input("Vector")[1]
  945. self.parameters["Z"] = self.evaluate_input("Vector")[2]
  946. self.prepared = True
  947. self.executed = True
  948. class UtilityCatStrings:
  949. '''A node representing an armature object'''
  950. def __init__(self, signature, base_tree):
  951. self.base_tree=base_tree
  952. self.executed = False
  953. self.signature = signature
  954. self.inputs = {
  955. "String_1" : NodeSocket(is_input = True, name = "String_1", node = self),
  956. "String_2" : NodeSocket(is_input = True, name = "String_2", node = self),
  957. }
  958. self.outputs = {
  959. "OutputString" : NodeSocket(name = "OutputString", node=self),
  960. }
  961. self.parameters = {
  962. "String_1":None,
  963. "String_2":None,
  964. }
  965. self.node_type = "UTILITY"
  966. self.hierarchy_connections = []
  967. self.connections = []
  968. self.hierarchy_dependencies = []
  969. self.dependencies = []
  970. self.prepared = False
  971. self.executed = False
  972. def bPrepare(self, bContext = None,):
  973. self.parameters["OutputString"] = self.evaluate_input("String_1")+self.evaluate_input("String_2")
  974. self.prepared = True
  975. self.executed = True
  976. class InputLayerMask:
  977. '''A node representing an armature 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. }
  984. self.outputs = {
  985. "Layer Mask" : NodeSocket(is_input = True, name = "Layer Mask", node = self),
  986. }
  987. self.parameters = {
  988. "Layer Mask":None,
  989. }
  990. self.node_type = "UTILITY"
  991. self.hierarchy_connections = []
  992. self.connections = []
  993. self.hierarchy_dependencies = []
  994. self.dependencies = []
  995. self.prepared = True
  996. self.executed = True
  997. class InputExistingGeometryObject:
  998. '''A node representing an existing object'''
  999. def __init__(self, signature, base_tree):
  1000. self.base_tree=base_tree
  1001. self.executed = False
  1002. self.signature = signature
  1003. self.inputs = {
  1004. "Name" : NodeSocket(is_input = True, name = "Name", node = self),
  1005. }
  1006. self.outputs = {
  1007. "Object" : NodeSocket(is_input = False, name = "Object", node=self),
  1008. }
  1009. self.parameters = {
  1010. "Name":None,
  1011. "Object":None,
  1012. }
  1013. self.node_type = "XFORM"
  1014. self.hierarchy_connections = []
  1015. self.connections = []
  1016. self.hierarchy_dependencies = []
  1017. self.dependencies = []
  1018. self.prepared = False
  1019. self.executed = False
  1020. def bPrepare(self, bContext=None):
  1021. from bpy import data
  1022. name = self.evaluate_input("Name")
  1023. if name:
  1024. self.bObject = data.objects.get( name )
  1025. else:
  1026. self.bObject = None
  1027. if self is None and (name := self.evaluate_input("Name")):
  1028. prRed(f"No object found with name {name} in {self}")
  1029. self.prepared = True; self.executed = True
  1030. def bGetObject(self, mode=''):
  1031. return self.bObject
  1032. class InputExistingGeometryData:
  1033. '''A node representing existing object data'''
  1034. def __init__(self, signature, base_tree):
  1035. self.base_tree=base_tree
  1036. self.executed = False
  1037. self.signature = signature
  1038. self.inputs = {
  1039. "Name" : NodeSocket(is_input = True, name = "Name", node = self),
  1040. }
  1041. self.outputs = {
  1042. "Geometry" : NodeSocket(is_input = False, name = "Geometry", node=self),
  1043. }
  1044. self.parameters = {
  1045. "Name":None,
  1046. "Geometry":None,
  1047. }
  1048. self.node_type = "UTILITY"
  1049. self.hierarchy_connections = []
  1050. self.connections = []
  1051. self.hierarchy_dependencies = []
  1052. self.dependencies = []
  1053. self.prepared = True
  1054. self.executed = True
  1055. # mode for interface consistency
  1056. def bGetObject(self, mode=''):
  1057. from bpy import data
  1058. # first try Curve, then try Mesh
  1059. bObject = data.curves.get(self.evaluate_input("Name"))
  1060. if not bObject:
  1061. bObject = data.meshes.get(self.evaluate_input("Name"))
  1062. if bObject is None:
  1063. raise RuntimeError(f"Could not find a mesh or curve datablock named \"{self.evaluate_input('Name')}\" for node {self}")
  1064. return bObject
  1065. class UtilityGeometryOfXForm:
  1066. '''A node representing existing object data'''
  1067. def __init__(self, signature, base_tree):
  1068. self.base_tree=base_tree
  1069. self.executed = False
  1070. self.signature = signature
  1071. self.inputs = {
  1072. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1073. }
  1074. self.outputs = {
  1075. "Geometry" : NodeSocket(is_input = False, name = "Geometry", node=self),
  1076. }
  1077. self.parameters = {
  1078. "xForm":None,
  1079. "Geometry":None,
  1080. }
  1081. self.node_type = "UTILITY"
  1082. self.hierarchy_connections, self.connections = [], []
  1083. self.hierarchy_dependencies,self.dependencies = [], []
  1084. self.prepared, self.executed = True, True
  1085. # mode for interface consistency
  1086. def bGetObject(self, mode=''):
  1087. if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
  1088. prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
  1089. return None
  1090. xf = self.inputs["xForm"].links[0].from_node
  1091. if xf.node_type == 'XFORM':
  1092. xf_ob = xf.bGetObject()
  1093. if xf_ob.type in ['MESH', 'CURVE']:
  1094. return xf_ob.data
  1095. prOrange(f"WARN: Cannot retrieve data from {self}, the connected xForm is not a mesh or curve.")
  1096. return None
  1097. class UtilityNameOfXForm:
  1098. '''A node representing existing object data'''
  1099. def __init__(self, signature, base_tree):
  1100. self.base_tree=base_tree
  1101. self.executed = False
  1102. self.signature = signature
  1103. self.inputs = {
  1104. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1105. }
  1106. self.outputs = {
  1107. "Name" : NodeSocket(is_input = False, name = "Name", node=self),
  1108. }
  1109. self.parameters = {
  1110. "xForm":None,
  1111. "Name":'',
  1112. }
  1113. self.node_type = "UTILITY"
  1114. self.hierarchy_connections, self.connections = [], []
  1115. self.hierarchy_dependencies,self.dependencies = [], []
  1116. self.prepared, self.executed = False, False
  1117. # mode for interface consistency
  1118. def bPrepare(self, bContext = None,):
  1119. if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
  1120. prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
  1121. return ''
  1122. xf = self.inputs["xForm"].links[0].from_node
  1123. self.parameters["Name"] = xf.evaluate_input('Name')
  1124. self.prepared, self.executed = True, True
  1125. class UtilityGetBoneLength:
  1126. '''A node to get the length of a bone matrix'''
  1127. def __init__(self, signature, base_tree):
  1128. self.base_tree=base_tree
  1129. self.executed = False
  1130. self.signature = signature
  1131. self.inputs = {
  1132. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1133. }
  1134. self.outputs = {
  1135. "Bone Length" : NodeSocket(name = "Bone Length", node = self),
  1136. }
  1137. self.parameters = {
  1138. "Bone Length":None,
  1139. }
  1140. self.node_type = "UTILITY"
  1141. self.hierarchy_connections = []
  1142. self.connections = []
  1143. self.hierarchy_dependencies = []
  1144. self.dependencies = []
  1145. self.prepared = False
  1146. self.executed = False
  1147. def bPrepare(self, bContext = None,):
  1148. # print (self.inputs['Bone Matrix'].links)
  1149. if l := self.evaluate_input("Bone Matrix"):
  1150. self.parameters["Bone Length"] = l[3][3]
  1151. self.prepared = True
  1152. self.executed = True
  1153. class UtilityPointFromBoneMatrix:
  1154. '''A node representing an armature object'''
  1155. def __init__(self, signature, base_tree):
  1156. self.base_tree=base_tree
  1157. self.executed = False
  1158. self.signature = signature
  1159. self.inputs = {
  1160. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1161. "Head/Tail" : NodeSocket(is_input = True, name = "Head/Tail", node = self),
  1162. }
  1163. self.outputs = {
  1164. "Point" : NodeSocket(name = "Point", node = self),
  1165. }
  1166. self.parameters = {
  1167. "Bone Matrix":None,
  1168. "Head/Tail":None,
  1169. "Point":None,
  1170. }
  1171. self.node_type = "UTILITY"
  1172. self.hierarchy_connections = []
  1173. self.connections = []
  1174. self.hierarchy_dependencies = []
  1175. self.dependencies = []
  1176. self.prepared = False
  1177. self.executed = False
  1178. # TODO: find out why this is sometimes not ready at bPrepare phase
  1179. def bPrepare(self, bContext = None,):
  1180. from mathutils import Vector
  1181. matrix = self.evaluate_input("Bone Matrix")
  1182. head, rotation, _scale = matrix.copy().decompose()
  1183. tail = head.copy() + (rotation @ Vector((0,1,0)))*matrix[3][3]
  1184. self.parameters["Point"] = head.lerp(tail, self.evaluate_input("Head/Tail"))
  1185. self.prepared = True
  1186. self.executed = True
  1187. class UtilitySetBoneLength:
  1188. '''Sets the length of a Bone's matrix'''
  1189. def __init__(self, signature, base_tree):
  1190. self.base_tree=base_tree
  1191. self.executed = False
  1192. self.signature = signature
  1193. self.inputs = {
  1194. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1195. "Length" : NodeSocket(is_input = True, name = "Length", node = self),
  1196. }
  1197. self.outputs = {
  1198. "Bone Matrix" : NodeSocket(name = "Bone Matrix", node = self),
  1199. }
  1200. self.parameters = {
  1201. "Bone Matrix":None,
  1202. "Length":None,
  1203. }
  1204. self.node_type = "UTILITY"
  1205. self.hierarchy_connections = []
  1206. self.connections = []
  1207. self.hierarchy_dependencies = []
  1208. self.dependencies = []
  1209. self.prepared = False
  1210. self.executed = False
  1211. def bPrepare(self, bContext = None,):
  1212. from mathutils import Vector
  1213. if matrix := self.evaluate_input("Bone Matrix"):
  1214. matrix = matrix.copy()
  1215. # print (self.inputs["Length"].links)
  1216. matrix[3][3] = self.evaluate_input("Length")
  1217. self.parameters["Length"] = self.evaluate_input("Length")
  1218. self.parameters["Bone Matrix"] = matrix
  1219. self.prepared = True
  1220. self.executed = True
  1221. class UtilityMatrixSetLocation:
  1222. '''Sets the location of a matrix'''
  1223. def __init__(self, signature, base_tree):
  1224. self.base_tree=base_tree
  1225. self.executed = False
  1226. self.signature = signature
  1227. self.inputs = {
  1228. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1229. "Location" : NodeSocket(is_input = True, name = "Location", node = self),
  1230. }
  1231. self.outputs = {
  1232. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1233. }
  1234. self.parameters = {
  1235. "Matrix" : None,
  1236. "Location" : None,
  1237. }
  1238. self.node_type = "UTILITY"
  1239. self.connections, self.hierarchy_connections = [], []
  1240. self.dependencies, self.hierarchy_dependencies = [], []
  1241. self.prepared, self.executed = False,False
  1242. def bPrepare(self, bContext = None,):
  1243. from mathutils import Vector
  1244. if matrix := self.evaluate_input("Matrix"):
  1245. matrix = matrix.copy()
  1246. # print (self.inputs["Length"].links)
  1247. loc = self.evaluate_input("Location")
  1248. matrix[0][3] = loc[0]; matrix[1][3] = loc[1]; matrix[2][3] = loc[2]
  1249. self.parameters["Matrix"] = matrix
  1250. self.prepared = True
  1251. self.executed = True
  1252. class UtilityMatrixGetLocation:
  1253. '''Gets the location of a matrix'''
  1254. def __init__(self, signature, base_tree):
  1255. self.base_tree=base_tree
  1256. self.signature = signature
  1257. self.inputs = {
  1258. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1259. }
  1260. self.outputs = {
  1261. "Location" : NodeSocket(name = "Location", node = self),
  1262. }
  1263. self.parameters = {
  1264. "Matrix" : None,
  1265. "Location" : None,
  1266. }
  1267. self.node_type = "UTILITY"
  1268. self.connections, self.hierarchy_connections = [], []
  1269. self.dependencies, self.hierarchy_dependencies = [], []
  1270. self.prepared, self.executed = False,False
  1271. def bPrepare(self, bContext = None,):
  1272. from mathutils import Vector
  1273. if matrix := self.evaluate_input("Matrix"):
  1274. self.parameters["Location"] = matrix.to_translation()
  1275. self.prepared = True; self.executed = True
  1276. class UtilityMatrixFromXForm:
  1277. """Returns the matrix of the given xForm node."""
  1278. def __init__(self, signature, base_tree):
  1279. self.base_tree=base_tree
  1280. self.signature = signature
  1281. self.inputs = {
  1282. "xForm" : NodeSocket(is_input = True, name = "xForm", node = self),
  1283. }
  1284. self.outputs = {
  1285. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1286. }
  1287. self.parameters = {
  1288. "Matrix" : None,
  1289. }
  1290. self.node_type = "UTILITY"
  1291. self.connections, self.hierarchy_connections = [], []
  1292. self.dependencies, self.hierarchy_dependencies = [], []
  1293. self.prepared, self.executed = False,False
  1294. def GetxForm(self):
  1295. trace = trace_single_line(self, "xForm")
  1296. for node in trace[0]:
  1297. if (node.node_type == 'XFORM'):
  1298. return node
  1299. raise GraphError("%s is not connected to an xForm" % self)
  1300. def bPrepare(self, bContext = None,):
  1301. from mathutils import Vector, Matrix
  1302. self.parameters["Matrix"] = Matrix.Identity(4)
  1303. if matrix := self.GetxForm().parameters.get("Matrix"):
  1304. self.parameters["Matrix"] = matrix.copy()
  1305. elif hasattr(self.GetxForm().bObject, "matrix"):
  1306. self.parameters["Matrix"] = self.GetxForm().bObject.matrix.copy()
  1307. elif hasattr(self.GetxForm().bObject, "matrix_world"):
  1308. self.parameters["Matrix"] = self.GetxForm().bObject.matrix_world.copy()
  1309. else:
  1310. prRed(f"Could not find matrix for {self} - check if the referenced object exists.")
  1311. self.prepared = True; self.executed = True
  1312. class UtilityAxesFromMatrix:
  1313. """Returns the axes of the given matrix."""
  1314. def __init__(self, signature, base_tree):
  1315. self.base_tree=base_tree
  1316. self.signature = signature
  1317. self.inputs = {
  1318. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1319. }
  1320. self.outputs = {
  1321. "X Axis" : NodeSocket(name = "X Axis", node = self),
  1322. "Y Axis" : NodeSocket(name = "Y Axis", node = self),
  1323. "Z Axis" : NodeSocket(name = "Z Axis", node = self),
  1324. }
  1325. self.parameters = {
  1326. "Matrix" : None,
  1327. "X Axis" : None,
  1328. "Y Axis" : None,
  1329. "Z Axis" : None,
  1330. }
  1331. self.node_type = "UTILITY"
  1332. self.connections, self.hierarchy_connections = [], []
  1333. self.dependencies, self.hierarchy_dependencies = [], []
  1334. self.prepared, self.executed = False,False
  1335. def bPrepare(self, bContext = None,):
  1336. from mathutils import Vector
  1337. if matrix := self.evaluate_input("Matrix"):
  1338. matrix= matrix.copy().to_3x3()
  1339. self.parameters['X Axis'] = matrix @ Vector((1,0,0))
  1340. self.parameters['Y Axis'] = matrix @ Vector((0,1,0))
  1341. self.parameters['Z Axis'] = matrix @ Vector((0,0,1))
  1342. self.prepared = True; self.executed = True
  1343. class UtilityBoneMatrixHeadTailFlip:
  1344. def __init__(self, signature, base_tree):
  1345. self.base_tree=base_tree
  1346. self.executed = False
  1347. self.signature = signature
  1348. self.inputs = {
  1349. "Bone Matrix" : NodeSocket(is_input = True, name = "Bone Matrix", node = self),
  1350. }
  1351. self.outputs = {
  1352. "Bone Matrix" : NodeSocket(name = "Bone Matrix", node = self),
  1353. }
  1354. self.parameters = {
  1355. "Bone Matrix":None,
  1356. }
  1357. self.node_type = "UTILITY"
  1358. self.hierarchy_connections = []
  1359. self.connections = []
  1360. self.hierarchy_dependencies = []
  1361. self.dependencies = []
  1362. self.prepared = False
  1363. self.executed = False
  1364. def bPrepare(self, bContext = None,):
  1365. from mathutils import Vector, Matrix, Quaternion
  1366. from bpy.types import Bone
  1367. if matrix := self.evaluate_input("Bone Matrix"):
  1368. axis, roll = Bone.AxisRollFromMatrix(matrix.to_3x3())
  1369. new_mat = Bone.MatrixFromAxisRoll(-1*axis, roll)
  1370. length = matrix[3][3]
  1371. new_mat.resize_4x4() # last column contains
  1372. new_mat[0][3] = matrix[0][3] + axis[0]*length # x location
  1373. new_mat[1][3] = matrix[1][3] + axis[1]*length # y location
  1374. new_mat[2][3] = matrix[2][3] + axis[2]*length # z location
  1375. new_mat[3][3] = length # length
  1376. self.parameters["Bone Matrix"] = new_mat
  1377. self.prepared, self.executed = True, True
  1378. class UtilityMatrixTransform:
  1379. def __init__(self, signature, base_tree):
  1380. self.base_tree=base_tree
  1381. self.executed = False
  1382. self.signature = signature
  1383. self.inputs = {
  1384. "Matrix 1" : NodeSocket(is_input = True, name = "Matrix 1", node = self),
  1385. "Matrix 2" : NodeSocket(is_input = True, name = "Matrix 2", node = self),
  1386. }
  1387. self.outputs = {
  1388. "Out Matrix" : NodeSocket(name = "Out Matrix", node = self),
  1389. }
  1390. self.parameters = {
  1391. "Matrix 1" : None,
  1392. "Matrix 2" : None,
  1393. "Out Matrix" : None,
  1394. }
  1395. self.node_type = "UTILITY"
  1396. self.hierarchy_connections = []
  1397. self.connections = []
  1398. self.hierarchy_dependencies = []
  1399. self.dependencies = []
  1400. self.prepared = False
  1401. self.executed = False
  1402. def bPrepare(self, bContext = None,):
  1403. from mathutils import Vector
  1404. mat1 = self.evaluate_input("Matrix 1"); mat2 = self.evaluate_input("Matrix 2")
  1405. if mat1 and mat2:
  1406. mat1copy = mat1.copy()
  1407. self.parameters["Out Matrix"] = mat2 @ mat1copy
  1408. self.parameters["Out Matrix"].translation = mat1copy.to_translation()+ mat2.to_translation()
  1409. else:
  1410. raise RuntimeError(wrapRed(f"Node {self} did not receive all matrix inputs... found input 1? {mat1 is not None}, 2? {mat2 is not None}"))
  1411. self.prepared = True
  1412. self.executed = True
  1413. class UtilityTransformationMatrix:
  1414. def __init__(self, signature, base_tree):
  1415. self.base_tree=base_tree
  1416. self.executed = False
  1417. self.signature = signature
  1418. self.inputs = {
  1419. "Operation" : NodeSocket(is_input = True, name = "Operation", node = self),
  1420. "Vector" : NodeSocket(is_input = True, name = "Vector", node = self),
  1421. "W" : NodeSocket(is_input = True, name = "W", node = self),
  1422. }
  1423. self.outputs = {
  1424. "Matrix" : NodeSocket(name = "Matrix", node = self),
  1425. }
  1426. self.parameters = {
  1427. "Operation" : None,
  1428. "Origin" : None,
  1429. "Vector" : None,
  1430. "W" : None,
  1431. "Matrix" : None,
  1432. }
  1433. self.node_type = "UTILITY"
  1434. self.hierarchy_connections = []
  1435. self.connections = []
  1436. self.hierarchy_dependencies = []
  1437. self.dependencies = []
  1438. self.prepared = False
  1439. self.executed = False
  1440. def bPrepare(self, bContext = None,):
  1441. from mathutils import Matrix, Vector
  1442. if (operation := self.evaluate_input("Operation")) == 'ROTATE_AXIS_ANGLE':
  1443. # this can, will, and should fail if the axis is 0,0,0
  1444. self.parameters["Matrix"] = rotMat = Matrix.Rotation(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1445. elif (operation := self.evaluate_input("Operation")) == 'TRANSLATE':
  1446. m = Matrix.Identity(4)
  1447. if axis := self.evaluate_input("Vector"):
  1448. m[0][3]=axis[0];m[1][3]=axis[1];m[2][3]=axis[2]
  1449. self.parameters['Matrix'] = m
  1450. elif (operation := self.evaluate_input("Operation")) == 'SCALE':
  1451. self.parameters["Matrix"] = Matrix.Scale(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1452. else:
  1453. raise NotImplementedError(self.evaluate_input("Operation").__repr__()+ " Operation not yet implemented.")
  1454. self.prepared = True
  1455. self.executed = True
  1456. class UtilityIntToString:
  1457. def __init__(self, signature, base_tree):
  1458. self.base_tree=base_tree
  1459. self.executed = False
  1460. self.signature = signature
  1461. self.inputs = {
  1462. "Number" : NodeSocket(is_input = True, name = "Number", node = self),
  1463. "Zero Padding" : NodeSocket(is_input = True, name = "Zero Padding", node = self),
  1464. }
  1465. self.outputs = {
  1466. "String" : NodeSocket(name = "String", node = self),
  1467. }
  1468. self.parameters = {
  1469. "Number" : None,
  1470. "Zero Padding" : None,
  1471. "String" : None,
  1472. }
  1473. self.node_type = "UTILITY"
  1474. self.hierarchy_connections = []
  1475. self.connections = []
  1476. self.hierarchy_dependencies = []
  1477. self.dependencies = []
  1478. self.prepared = False
  1479. self.executed = False
  1480. def bPrepare(self, bContext = None,):
  1481. number = self.evaluate_input("Number")
  1482. zeroes = self.evaluate_input("Zero Padding")
  1483. # I'm casting to int because I want to support any number, even though the node asks for int.
  1484. self.parameters["String"] = str(int(number)).zfill(int(zeroes))
  1485. self.prepared = True
  1486. self.executed = True
  1487. class UtilityArrayGet:
  1488. def __init__(self, signature, base_tree):
  1489. self.base_tree=base_tree
  1490. self.executed = False
  1491. self.signature = signature
  1492. self.inputs = {
  1493. "Index" : NodeSocket(is_input = True, name = "Index", node = self),
  1494. "OoB Behaviour" : NodeSocket(is_input = True, name = "OoB Behaviour", node = self),
  1495. "Array" : NodeSocket(is_input = True, name = "Array", node = self),
  1496. }
  1497. self.outputs = {
  1498. "Output" : NodeSocket(name = "Output", node = self),
  1499. }
  1500. self.parameters = {
  1501. "Index" : None,
  1502. "OoB Behaviour" : None,
  1503. "Array" : None,
  1504. "Output" : None,
  1505. }
  1506. self.node_type = "UTILITY"
  1507. self.hierarchy_connections = []
  1508. self.connections = []
  1509. self.hierarchy_dependencies = []
  1510. self.dependencies = []
  1511. self.prepared = False
  1512. self.executed = False
  1513. def bPrepare(self, bContext = None,):
  1514. if self.prepared == False:
  1515. # sort the array entries
  1516. for inp in self.inputs.values():
  1517. inp.links.sort(key=lambda a : -a.multi_input_sort_id)
  1518. oob = self.evaluate_input("OoB Behaviour")
  1519. index = self.evaluate_input("Index")
  1520. from .utilities import cap, wrap
  1521. # we must assume that the array has sent the correct number of links
  1522. if oob == 'WRAP':
  1523. index = index % len(self.inputs['Array'].links)
  1524. if oob == 'HOLD':
  1525. index = cap(index, len(self.inputs['Array'].links)-1)
  1526. # relink the connections and then kill all the links to and from the array
  1527. from .utilities import init_connections, init_dependencies
  1528. l = self.inputs["Array"].links[index]
  1529. for link in self.outputs["Output"].links:
  1530. to_node = link.to_node
  1531. l.from_node.outputs[l.from_socket].connect(to_node, link.to_socket)
  1532. link.die()
  1533. init_dependencies(to_node)
  1534. from_node=l.from_node
  1535. for inp in self.inputs.values():
  1536. for l in inp.links:
  1537. l.die()
  1538. init_connections(from_node)
  1539. if self in from_node.hierarchy_connections:
  1540. raise RuntimeError()
  1541. # this is intentional because the Array Get is kind of a weird hybrid between a Utility and a Schema
  1542. # so it should be removed from the tree when it is done. it has already dealt with the actual links.
  1543. # however I think this is redundant. Check.
  1544. self.hierarchy_connections, self.connections = [], []
  1545. self.hierarchy_dependencies, self.dependencies = [], []
  1546. self.prepared = True
  1547. self.executed = True
  1548. class UtilitySetBoneMatrixTail:
  1549. def __init__(self, signature, base_tree):
  1550. self.base_tree=base_tree
  1551. self.executed = False
  1552. self.signature = signature
  1553. self.inputs = {
  1554. "Matrix" : NodeSocket(is_input = True, name = "Matrix", node = self),
  1555. "Tail Location" : NodeSocket(is_input = True, name = "Tail Location", node = self),
  1556. }
  1557. self.outputs = {
  1558. "Result" : NodeSocket(name = "Result", node = self),
  1559. }
  1560. self.parameters = {
  1561. "Matrix" : None,
  1562. "Tail Location" : None,
  1563. "Result" : None,
  1564. }
  1565. self.node_type = "UTILITY"
  1566. self.hierarchy_connections, self.connections = [], []
  1567. self.hierarchy_dependencies, self.dependencies = [], []
  1568. self.prepared, self.executed = False, False
  1569. def bPrepare(self, bContext = None,):
  1570. from mathutils import Matrix
  1571. matrix = self.evaluate_input("Matrix")
  1572. if matrix is None: matrix = Matrix.Identity(4)
  1573. #just do this for now lol
  1574. self.parameters["Result"] = matrix_from_head_tail(matrix.translation, self.evaluate_input("Tail Location"))
  1575. class UtilityPrint:
  1576. def __init__(self, signature, base_tree):
  1577. self.base_tree=base_tree
  1578. self.executed = False
  1579. self.signature = signature
  1580. self.inputs = {
  1581. "Input" : NodeSocket(is_input = True, name = "Input", node = self),
  1582. }
  1583. self.outputs = {}
  1584. self.parameters = {
  1585. "Input" : None,
  1586. }
  1587. self.node_type = "UTILITY"
  1588. self.hierarchy_connections = []
  1589. self.connections = []
  1590. self.hierarchy_dependencies = []
  1591. self.dependencies = []
  1592. self.prepared = False
  1593. self.executed = False
  1594. def bPrepare(self, bContext = None,):
  1595. if my_input := self.evaluate_input("Input"):
  1596. print("Preparation phase: ", wrapWhite(self), wrapGreen(my_input))
  1597. # else:
  1598. # prRed("No input to print.")
  1599. self.prepared = True
  1600. def bExecute(self, bContext = None,):
  1601. if my_input := self.evaluate_input("Input"):
  1602. print("Execution phase: ", wrapWhite(self), wrapGreen(my_input))
  1603. # else:
  1604. # prRed("No input to print.")
  1605. self.executed = True
  1606. class UtilityCompare:
  1607. def __init__(self, signature, base_tree):
  1608. self.base_tree=base_tree
  1609. self.executed = False
  1610. self.signature = signature
  1611. self.inputs = {
  1612. "A" : NodeSocket(is_input = True, name = "A", node = self),
  1613. "B" : NodeSocket(is_input = True, name = "B", node = self),
  1614. }
  1615. self.outputs = {
  1616. "Result" : NodeSocket(name = "Result", node = self),
  1617. }
  1618. self.parameters = {
  1619. "A" : None,
  1620. "B" : None,
  1621. "Result" : None,
  1622. }
  1623. self.node_type = "UTILITY"
  1624. self.hierarchy_connections, self.connections = [], []
  1625. self.hierarchy_dependencies, self.dependencies = [], []
  1626. self.prepared, self.executed = False, False
  1627. def bPrepare(self, bContext = None,):
  1628. self.parameters["Result"] = self.evaluate_input("A") == self.evaluate_input("B")
  1629. self.prepared = True; self.executed = True
  1630. class UtilityChoose:
  1631. def __init__(self, signature, base_tree):
  1632. self.base_tree=base_tree
  1633. self.executed = False
  1634. self.signature = signature
  1635. self.inputs = {
  1636. "Condition" : NodeSocket(is_input = True, name = "Condition", node = self),
  1637. "A" : NodeSocket(is_input = True, name = "A", node = self),
  1638. "B" : NodeSocket(is_input = True, name = "B", node = self),
  1639. }
  1640. self.outputs = {
  1641. "Result" : NodeSocket(name = "Result", node = self),
  1642. }
  1643. self.parameters = {
  1644. "Condition" : None,
  1645. "A" : None,
  1646. "B" : None,
  1647. "Result" : None,
  1648. }
  1649. self.node_type = "UTILITY"
  1650. self.hierarchy_connections, self.connections = [], []
  1651. self.hierarchy_dependencies, self.dependencies = [], []
  1652. self.prepared, self.executed = False, False
  1653. def bPrepare(self, bContext = None,):
  1654. condition = self.evaluate_input("Condition")
  1655. if condition:
  1656. self.parameters["Result"] = self.evaluate_input("B")
  1657. else:
  1658. self.parameters["Result"] = self.evaluate_input("A")
  1659. self.prepared = True
  1660. self.executed = True
  1661. for c in TellClasses():
  1662. setup_container(c)