misc_containers.py 64 KB

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