misc_containers.py 62 KB

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