misc_containers.py 67 KB

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