misc_containers.py 65 KB

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