misc_containers.py 51 KB

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