misc_containers.py 69 KB

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