misc_containers.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439
  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. print (keys)
  541. self.parameters["fCurve"] = keys
  542. self.executed = True
  543. #TODO make the fCurve data a data class instead of a dict
  544. class UtilityDriver(MantisNode):
  545. '''A node representing an armature object'''
  546. def __init__(self, signature, base_tree):
  547. super().__init__(signature, base_tree)
  548. inputs = [
  549. "Driver Type" ,
  550. "Expression" ,
  551. "fCurve" ,
  552. ]
  553. outputs = [
  554. "Driver",
  555. ]
  556. from .drivers import MantisDriver
  557. additional_parameters = {
  558. "Driver":MantisDriver(),
  559. }
  560. self.inputs.init_sockets(inputs)
  561. self.outputs.init_sockets(outputs)
  562. self.init_parameters(additional_parameters=additional_parameters)
  563. self.node_type = "DRIVER" # MUST be run in Pose mode
  564. setup_custom_props(self)
  565. self.prepared = True
  566. def bExecute(self, bContext = None,):
  567. prepare_parameters(self)
  568. from .drivers import MantisDriver
  569. #prPurple("Executing Driver Node")
  570. my_vars = []
  571. keys = self.evaluate_input("fCurve")
  572. if keys is None or len(keys) <2:
  573. prWhite(f"INFO: no fCurve connected to {self}; using default fCurve.")
  574. from mathutils import Vector
  575. keys = [
  576. {"co":Vector( (0, 0,)), "type":"GENERATED", "interpolation":"LINEAR" },
  577. {"co":Vector( (1, 1,)), "type":"GENERATED", "interpolation":"LINEAR" },
  578. "CONSTANT",]
  579. for inp in list(self.inputs.keys() )[3:]:
  580. if (new_var := self.evaluate_input(inp)):
  581. new_var["name"] = inp
  582. my_vars.append(new_var)
  583. else:
  584. raise RuntimeError(f"Failed to initialize Driver variable for {self}")
  585. my_driver ={ "owner" : None,
  586. "prop" : None, # will be filled out in the node that uses the driver
  587. "expression" : self.evaluate_input("Expression"),
  588. "ind" : -1, # same here
  589. "type" : self.evaluate_input("Driver Type"),
  590. "vars" : my_vars,
  591. "keys" : keys[:-1],
  592. "extrapolation" : keys[-1] }
  593. my_driver = MantisDriver(my_driver)
  594. self.parameters["Driver"].update(my_driver)
  595. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  596. self.executed = True
  597. class UtilitySwitch(MantisNode):
  598. '''A node representing an armature object'''
  599. def __init__(self, signature, base_tree):
  600. super().__init__(signature, base_tree)
  601. inputs = {
  602. "Parameter" ,
  603. "Parameter Index" ,
  604. "Invert Switch" ,
  605. }
  606. outputs = [
  607. "Driver",
  608. ]
  609. from .drivers import MantisDriver
  610. additional_parameters = {
  611. "Driver":MantisDriver(),
  612. }
  613. self.inputs.init_sockets(inputs)
  614. self.outputs.init_sockets(outputs)
  615. self.init_parameters(additional_parameters=additional_parameters)
  616. self.node_type = "DRIVER" # MUST be run in Pose mode
  617. self.prepared = True
  618. def evaluate_input(self, input_name):
  619. if input_name == 'Parameter':
  620. if self.inputs['Parameter'].is_connected:
  621. trace = trace_single_line(self, input_name)
  622. return trace[1].name # the name of the socket
  623. return self.parameters["Parameter"]
  624. return super().evaluate_input(input_name)
  625. def GetxForm(self,):
  626. trace = trace_single_line(self, "Parameter" )
  627. for node in trace[0]:
  628. if (node.__class__ in [xFormArmature, xFormBone]):
  629. return node #this will fetch the first one, that's good!
  630. return None
  631. def bExecute(self, bContext = None,):
  632. #prepare_parameters(self)
  633. #prPurple ("Executing Switch Node")
  634. xForm = self.GetxForm()
  635. if xForm : xForm = xForm.bGetObject()
  636. if not xForm:
  637. raise RuntimeError("Could not evaluate xForm for %s" % self)
  638. from .drivers import MantisDriver
  639. my_driver ={ "owner" : None,
  640. "prop" : None, # will be filled out in the node that uses the driver
  641. "ind" : -1, # same here
  642. "type" : "SCRIPTED",
  643. "vars" : [ { "owner" : xForm,
  644. "prop" : self.evaluate_input("Parameter"),
  645. "name" : "a",
  646. "type" : "SINGLE_PROP", } ],
  647. "keys" : [ { "co":(0,0),
  648. "interpolation": "LINEAR",
  649. "type":"KEYFRAME",}, #display type
  650. { "co":(1,1),
  651. "interpolation": "LINEAR",
  652. "type":"KEYFRAME",},],
  653. "extrapolation": 'CONSTANT', }
  654. my_driver ["expression"] = "a"
  655. my_driver = MantisDriver(my_driver)
  656. # this makes it so I can check for type later!
  657. if self.evaluate_input("Invert Switch") == True:
  658. my_driver ["expression"] = "1 - a"
  659. # this way, regardless of what order things are handled, the
  660. # driver is sent to the next node.
  661. # In the case of some drivers, the parameter may be sent out
  662. # before it's filled in (because there is a circular dependency)
  663. # I want to support this behaviour because Blender supports it.
  664. # We do not make a copy. We update the driver, so that
  665. # the same instance is filled out.
  666. self.parameters["Driver"].update(my_driver)
  667. print("Initializing driver %s " % (wrapPurple(self.__repr__())) )
  668. self.executed = True
  669. class UtilityCombineThreeBool(MantisNode):
  670. '''A node for combining three booleans into a boolean three-tuple'''
  671. def __init__(self, signature, base_tree):
  672. super().__init__(signature, base_tree)
  673. inputs = [
  674. "X" ,
  675. "Y" ,
  676. "Z" ,
  677. ]
  678. outputs = [
  679. "Three-Bool",
  680. ]
  681. self.inputs.init_sockets(inputs)
  682. self.outputs.init_sockets(outputs)
  683. self.init_parameters()
  684. self.node_type = "UTILITY"
  685. def bPrepare(self, bContext = None,):
  686. self.parameters["Three-Bool"] = (
  687. self.evaluate_input("X"),
  688. self.evaluate_input("Y"),
  689. self.evaluate_input("Z"), )
  690. self.prepared = True
  691. self.executed = True
  692. # Note this is a copy of the above. This needs to be de-duplicated.
  693. class UtilityCombineVector(MantisNode):
  694. '''A node for combining three floats into a vector'''
  695. def __init__(self, signature, base_tree):
  696. super().__init__(signature, base_tree)
  697. super().__init__(signature, base_tree)
  698. inputs = [
  699. "X" ,
  700. "Y" ,
  701. "Z" ,
  702. ]
  703. outputs = [
  704. "Vector",
  705. ]
  706. self.inputs.init_sockets(inputs)
  707. self.outputs.init_sockets(outputs)
  708. self.init_parameters()
  709. self.node_type = "UTILITY"
  710. def bPrepare(self, bContext = None,):
  711. #prPurple("Executing CombineVector Node")
  712. prepare_parameters(self)
  713. self.parameters["Vector"] = (
  714. self.evaluate_input("X"),
  715. self.evaluate_input("Y"),
  716. self.evaluate_input("Z"), )
  717. self.prepared = True
  718. self.executed = True
  719. class UtilitySeparateVector(MantisNode):
  720. '''A node for separating a vector into three floats'''
  721. def __init__(self, signature, base_tree):
  722. super().__init__(signature, base_tree)
  723. inputs = [
  724. "Vector"
  725. ]
  726. outputs = [
  727. "X" ,
  728. "Y" ,
  729. "Z" ,
  730. ]
  731. self.inputs.init_sockets(inputs)
  732. self.outputs.init_sockets(outputs)
  733. self.init_parameters()
  734. self.node_type = "UTILITY"
  735. def bPrepare(self, bContext = None,):
  736. self.parameters["X"] = self.evaluate_input("Vector")[0]
  737. self.parameters["Y"] = self.evaluate_input("Vector")[1]
  738. self.parameters["Z"] = self.evaluate_input("Vector")[2]
  739. self.prepared = True
  740. self.executed = True
  741. class UtilityCatStrings(MantisNode):
  742. '''A node representing an armature object'''
  743. def __init__(self, signature, base_tree):
  744. super().__init__(signature, base_tree)
  745. inputs = [
  746. "String_1" ,
  747. "String_2" ,
  748. ]
  749. outputs = [
  750. "OutputString" ,
  751. ]
  752. self.inputs.init_sockets(inputs)
  753. self.outputs.init_sockets(outputs)
  754. self.init_parameters()
  755. self.node_type = "UTILITY"
  756. def bPrepare(self, bContext = None,):
  757. self.parameters["OutputString"] = self.evaluate_input("String_1")+self.evaluate_input("String_2")
  758. self.prepared = True
  759. self.executed = True
  760. class InputExistingGeometryObject(MantisNode):
  761. '''A node representing an existing object'''
  762. def __init__(self, signature, base_tree):
  763. super().__init__(signature, base_tree)
  764. inputs = [
  765. "Name" ,
  766. ]
  767. outputs = [
  768. "Object" ,
  769. ]
  770. self.inputs.init_sockets(inputs)
  771. self.outputs.init_sockets(outputs)
  772. self.init_parameters()
  773. self.node_type = "XFORM"
  774. def bPrepare(self, bContext=None):
  775. from bpy import data
  776. name = self.evaluate_input("Name")
  777. if name:
  778. self.bObject = data.objects.get( name )
  779. else:
  780. self.bObject = None
  781. if self is None and (name := self.evaluate_input("Name")):
  782. prRed(f"No object found with name {name} in {self}")
  783. self.prepared = True; self.executed = True
  784. def bGetObject(self, mode=''):
  785. return self.bObject
  786. class InputExistingGeometryData(MantisNode):
  787. '''A node representing existing object data'''
  788. def __init__(self, signature, base_tree):
  789. super().__init__(signature, base_tree)
  790. inputs = [
  791. "Name" ,
  792. ]
  793. outputs = [
  794. "Geometry" ,
  795. ]
  796. self.inputs.init_sockets(inputs)
  797. self.outputs.init_sockets(outputs)
  798. self.init_parameters()
  799. self.node_type = "UTILITY"
  800. self.prepared = True
  801. self.executed = True
  802. # the mode argument is only for interface consistency
  803. def bGetObject(self, mode=''):
  804. from bpy import data
  805. # first try Curve, then try Mesh
  806. bObject = data.curves.get(self.evaluate_input("Name"))
  807. if not bObject:
  808. bObject = data.meshes.get(self.evaluate_input("Name"))
  809. if bObject is None:
  810. raise RuntimeError(f"Could not find a mesh or curve datablock named \"{self.evaluate_input('Name')}\" for node {self}")
  811. return bObject
  812. class UtilityGeometryOfXForm(MantisNode):
  813. '''A node representing existing object data'''
  814. def __init__(self, signature, base_tree):
  815. super().__init__(signature, base_tree)
  816. inputs = [
  817. "xForm" ,
  818. ]
  819. outputs = [
  820. "Geometry" ,
  821. ]
  822. self.inputs.init_sockets(inputs)
  823. self.outputs.init_sockets(outputs)
  824. self.init_parameters()
  825. self.node_type = "UTILITY"
  826. self.prepared = True
  827. self.executed = True
  828. # mode for interface consistency
  829. def bGetObject(self, mode=''):
  830. if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
  831. prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
  832. return None
  833. xf = self.inputs["xForm"].links[0].from_node
  834. if xf.node_type == 'XFORM':
  835. xf_ob = xf.bGetObject()
  836. if xf_ob.type in ['MESH', 'CURVE']:
  837. return xf_ob.data
  838. prOrange(f"WARN: Cannot retrieve data from {self}, the connected xForm is not a mesh or curve.")
  839. return None
  840. class UtilityNameOfXForm(MantisNode):
  841. '''A node representing existing object data'''
  842. def __init__(self, signature, base_tree):
  843. super().__init__(signature, base_tree)
  844. inputs = [
  845. "xForm" ,
  846. ]
  847. outputs = [
  848. "Name" ,
  849. ]
  850. self.inputs.init_sockets(inputs)
  851. self.outputs.init_sockets(outputs)
  852. self.init_parameters()
  853. self.node_type = "UTILITY"
  854. # mode for interface consistency
  855. def bPrepare(self, bContext = None,):
  856. if not (self.inputs.get('xForm') and self.inputs['xForm'].links):
  857. prOrange(f"WARN: Cannot retrieve data from {self}, there is no xForm node connected.")
  858. return ''
  859. xf = self.inputs["xForm"].links[0].from_node
  860. self.parameters["Name"] = xf.evaluate_input('Name')
  861. self.prepared, self.executed = True, True
  862. class UtilityGetBoneLength(MantisNode):
  863. '''A node to get the length of a bone matrix'''
  864. def __init__(self, signature, base_tree):
  865. super().__init__(signature, base_tree)
  866. inputs = [
  867. "Bone Matrix" ,
  868. ]
  869. outputs = [
  870. "Bone Length" ,
  871. ]
  872. self.inputs.init_sockets(inputs)
  873. self.outputs.init_sockets(outputs)
  874. self.init_parameters()
  875. self.node_type = "UTILITY"
  876. def bPrepare(self, bContext = None,):
  877. if l := self.evaluate_input("Bone Matrix"):
  878. self.parameters["Bone Length"] = l[3][3]
  879. self.prepared = True
  880. self.executed = True
  881. class UtilityPointFromBoneMatrix(MantisNode):
  882. '''A node representing an armature object'''
  883. def __init__(self, signature, base_tree):
  884. super().__init__(signature, base_tree)
  885. inputs = [
  886. "Bone Matrix" ,
  887. "Head/Tail" ,
  888. ]
  889. outputs = [
  890. "Point" ,
  891. ]
  892. self.inputs.init_sockets(inputs)
  893. self.outputs.init_sockets(outputs)
  894. self.init_parameters()
  895. self.node_type = "UTILITY"
  896. # TODO: find out why this is sometimes not ready at bPrepare phase
  897. def bPrepare(self, bContext = None,):
  898. from mathutils import Vector
  899. matrix = self.evaluate_input("Bone Matrix")
  900. head, rotation, _scale = matrix.copy().decompose()
  901. tail = head.copy() + (rotation @ Vector((0,1,0)))*matrix[3][3]
  902. self.parameters["Point"] = head.lerp(tail, self.evaluate_input("Head/Tail"))
  903. self.prepared = True
  904. self.executed = True
  905. class UtilitySetBoneLength(MantisNode):
  906. '''Sets the length of a Bone's matrix'''
  907. def __init__(self, signature, base_tree):
  908. super().__init__(signature, base_tree)
  909. inputs = [
  910. "Bone Matrix" ,
  911. "Length" ,
  912. ]
  913. outputs = [
  914. "Bone Matrix" ,
  915. ]
  916. self.inputs.init_sockets(inputs)
  917. self.outputs.init_sockets(outputs)
  918. self.init_parameters()
  919. self.node_type = "UTILITY"
  920. def bPrepare(self, bContext = None,):
  921. from mathutils import Vector
  922. if matrix := self.evaluate_input("Bone Matrix"):
  923. matrix = matrix.copy()
  924. # print (self.inputs["Length"].links)
  925. matrix[3][3] = self.evaluate_input("Length")
  926. self.parameters["Length"] = self.evaluate_input("Length")
  927. self.parameters["Bone Matrix"] = matrix
  928. self.prepared = True
  929. self.executed = True
  930. class UtilityMatrixSetLocation(MantisNode):
  931. '''Sets the location of a matrix'''
  932. def __init__(self, signature, base_tree):
  933. super().__init__(signature, base_tree)
  934. inputs = [
  935. "Matrix" ,
  936. "Location" ,
  937. ]
  938. outputs = [
  939. "Matrix" ,
  940. ]
  941. self.inputs.init_sockets(inputs)
  942. self.outputs.init_sockets(outputs)
  943. self.init_parameters()
  944. self.node_type = "UTILITY"
  945. def bPrepare(self, bContext = None,):
  946. from mathutils import Vector
  947. if matrix := self.evaluate_input("Matrix"):
  948. matrix = matrix.copy()
  949. # print (self.inputs["Length"].links)
  950. loc = self.evaluate_input("Location")
  951. matrix[0][3] = loc[0]; matrix[1][3] = loc[1]; matrix[2][3] = loc[2]
  952. self.parameters["Matrix"] = matrix
  953. self.prepared = True
  954. self.executed = True
  955. class UtilityMatrixGetLocation(MantisNode):
  956. '''Gets the location of a matrix'''
  957. def __init__(self, signature, base_tree):
  958. super().__init__(signature, base_tree)
  959. inputs = [
  960. "Matrix" ,
  961. ]
  962. outputs = [
  963. "Location" ,
  964. ]
  965. self.inputs.init_sockets(inputs)
  966. self.outputs.init_sockets(outputs)
  967. self.init_parameters()
  968. self.node_type = "UTILITY"
  969. def bPrepare(self, bContext = None,):
  970. from mathutils import Vector
  971. if matrix := self.evaluate_input("Matrix"):
  972. self.parameters["Location"] = matrix.to_translation()
  973. self.prepared = True; self.executed = True
  974. class UtilityMatrixFromXForm(MantisNode):
  975. """Returns the matrix of the given xForm node."""
  976. def __init__(self, signature, base_tree):
  977. super().__init__(signature, base_tree)
  978. inputs = [
  979. "xForm" ,
  980. ]
  981. outputs = [
  982. "Matrix" ,
  983. ]
  984. self.node_type = "UTILITY"
  985. self.inputs.init_sockets(inputs)
  986. self.outputs.init_sockets(outputs)
  987. self.init_parameters()
  988. def GetxForm(self):
  989. trace = trace_single_line(self, "xForm")
  990. for node in trace[0]:
  991. if (node.node_type == 'XFORM'):
  992. return node
  993. raise GraphError("%s is not connected to an xForm" % self)
  994. def bPrepare(self, bContext = None,):
  995. from mathutils import Vector, Matrix
  996. self.parameters["Matrix"] = Matrix.Identity(4)
  997. if matrix := self.GetxForm().parameters.get("Matrix"):
  998. self.parameters["Matrix"] = matrix.copy()
  999. elif hasattr(self.GetxForm().bObject, "matrix"):
  1000. self.parameters["Matrix"] = self.GetxForm().bObject.matrix.copy()
  1001. elif hasattr(self.GetxForm().bObject, "matrix_world"):
  1002. self.parameters["Matrix"] = self.GetxForm().bObject.matrix_world.copy()
  1003. else:
  1004. prRed(f"Could not find matrix for {self} - check if the referenced object exists.")
  1005. self.prepared = True; self.executed = True
  1006. class UtilityAxesFromMatrix(MantisNode):
  1007. """Returns the axes of the given matrix."""
  1008. def __init__(self, signature, base_tree):
  1009. super().__init__(signature, base_tree)
  1010. inputs = [
  1011. "Matrix" ,
  1012. ]
  1013. outputs = [
  1014. "X Axis" ,
  1015. "Y Axis" ,
  1016. "Z Axis" ,
  1017. ]
  1018. self.inputs.init_sockets(inputs)
  1019. self.outputs.init_sockets(outputs)
  1020. self.init_parameters()
  1021. self.node_type = "UTILITY"
  1022. def bPrepare(self, bContext = None,):
  1023. from mathutils import Vector
  1024. if matrix := self.evaluate_input("Matrix"):
  1025. matrix= matrix.copy().to_3x3()
  1026. self.parameters['X Axis'] = matrix @ Vector((1,0,0))
  1027. self.parameters['Y Axis'] = matrix @ Vector((0,1,0))
  1028. self.parameters['Z Axis'] = matrix @ Vector((0,0,1))
  1029. self.prepared = True; self.executed = True
  1030. class UtilityBoneMatrixHeadTailFlip(MantisNode):
  1031. def __init__(self, signature, base_tree):
  1032. super().__init__(signature, base_tree)
  1033. inputs = [
  1034. "Bone Matrix" ,
  1035. ]
  1036. outputs = [
  1037. "Bone Matrix" ,
  1038. ]
  1039. self.inputs.init_sockets(inputs)
  1040. self.outputs.init_sockets(outputs)
  1041. self.init_parameters()
  1042. self.node_type = "UTILITY"
  1043. def bPrepare(self, bContext = None,):
  1044. from mathutils import Vector, Matrix, Quaternion
  1045. from bpy.types import Bone
  1046. if matrix := self.evaluate_input("Bone Matrix"):
  1047. axis, roll = Bone.AxisRollFromMatrix(matrix.to_3x3())
  1048. new_mat = Bone.MatrixFromAxisRoll(-1*axis, roll)
  1049. length = matrix[3][3]
  1050. new_mat.resize_4x4() # last column contains
  1051. new_mat[0][3] = matrix[0][3] + axis[0]*length # x location
  1052. new_mat[1][3] = matrix[1][3] + axis[1]*length # y location
  1053. new_mat[2][3] = matrix[2][3] + axis[2]*length # z location
  1054. new_mat[3][3] = length # length
  1055. self.parameters["Bone Matrix"] = new_mat
  1056. self.prepared, self.executed = True, True
  1057. class UtilityMatrixTransform(MantisNode):
  1058. def __init__(self, signature, base_tree):
  1059. super().__init__(signature, base_tree)
  1060. inputs = [
  1061. "Matrix 1" ,
  1062. "Matrix 2" ,
  1063. ]
  1064. outputs = [
  1065. "Out Matrix" ,
  1066. ]
  1067. self.inputs.init_sockets(inputs)
  1068. self.outputs.init_sockets(outputs)
  1069. self.init_parameters()
  1070. self.node_type = "UTILITY"
  1071. def bPrepare(self, bContext = None,):
  1072. from mathutils import Vector
  1073. mat1 = self.evaluate_input("Matrix 1"); mat2 = self.evaluate_input("Matrix 2")
  1074. if mat1 and mat2:
  1075. mat1copy = mat1.copy()
  1076. self.parameters["Out Matrix"] = mat2 @ mat1copy
  1077. self.parameters["Out Matrix"].translation = mat1copy.to_translation()+ mat2.to_translation()
  1078. else:
  1079. raise RuntimeError(wrapRed(f"Node {self} did not receive all matrix inputs... found input 1? {mat1 is not None}, 2? {mat2 is not None}"))
  1080. self.prepared = True
  1081. self.executed = True
  1082. class UtilityTransformationMatrix(MantisNode):
  1083. def __init__(self, signature, base_tree):
  1084. super().__init__(signature, base_tree)
  1085. inputs = [
  1086. "Operation" ,
  1087. "Vector" ,
  1088. "W" ,
  1089. ]
  1090. outputs = [
  1091. "Matrix" ,
  1092. ]
  1093. self.inputs.init_sockets(inputs)
  1094. self.outputs.init_sockets(outputs)
  1095. self.init_parameters()
  1096. self.node_type = "UTILITY"
  1097. def bPrepare(self, bContext = None,):
  1098. from mathutils import Matrix, Vector
  1099. if (operation := self.evaluate_input("Operation")) == 'ROTATE_AXIS_ANGLE':
  1100. # this can, will, and should fail if the axis is 0,0,0
  1101. self.parameters["Matrix"] = rotMat = Matrix.Rotation(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1102. elif (operation := self.evaluate_input("Operation")) == 'TRANSLATE':
  1103. m = Matrix.Identity(4)
  1104. if axis := self.evaluate_input("Vector"):
  1105. m[0][3]=axis[0];m[1][3]=axis[1];m[2][3]=axis[2]
  1106. self.parameters['Matrix'] = m
  1107. elif (operation := self.evaluate_input("Operation")) == 'SCALE':
  1108. self.parameters["Matrix"] = Matrix.Scale(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
  1109. else:
  1110. raise NotImplementedError(self.evaluate_input("Operation").__repr__()+ " Operation not yet implemented.")
  1111. self.prepared = True
  1112. self.executed = True
  1113. class UtilityIntToString(MantisNode):
  1114. def __init__(self, signature, base_tree):
  1115. super().__init__(signature, base_tree)
  1116. inputs = [
  1117. "Number" ,
  1118. "Zero Padding" ,
  1119. ]
  1120. outputs = [
  1121. "String" ,
  1122. ]
  1123. self.inputs.init_sockets(inputs)
  1124. self.outputs.init_sockets(outputs)
  1125. self.init_parameters()
  1126. self.node_type = "UTILITY"
  1127. def bPrepare(self, bContext = None,):
  1128. number = self.evaluate_input("Number")
  1129. zeroes = self.evaluate_input("Zero Padding")
  1130. # I'm casting to int because I want to support any number, even though the node asks for int.
  1131. self.parameters["String"] = str(int(number)).zfill(int(zeroes))
  1132. self.prepared = True
  1133. self.executed = True
  1134. class UtilityArrayGet(MantisNode):
  1135. def __init__(self, signature, base_tree):
  1136. super().__init__(signature, base_tree)
  1137. inputs = [
  1138. "Index" ,
  1139. "OoB Behaviour" ,
  1140. "Array" ,
  1141. ]
  1142. outputs = [
  1143. "Output" ,
  1144. ]
  1145. self.inputs.init_sockets(inputs)
  1146. self.outputs.init_sockets(outputs)
  1147. self.init_parameters()
  1148. self.node_type = "UTILITY"
  1149. def bPrepare(self, bContext = None,):
  1150. if self.prepared == False:
  1151. # sort the array entries
  1152. for inp in self.inputs.values():
  1153. inp.links.sort(key=lambda a : -a.multi_input_sort_id)
  1154. oob = self.evaluate_input("OoB Behaviour")
  1155. index = self.evaluate_input("Index")
  1156. from .utilities import cap, wrap
  1157. # we must assume that the array has sent the correct number of links
  1158. if oob == 'WRAP':
  1159. index = index % len(self.inputs['Array'].links)
  1160. if oob == 'HOLD':
  1161. index = cap(index, len(self.inputs['Array'].links)-1)
  1162. # relink the connections and then kill all the links to and from the array
  1163. from .utilities import init_connections, init_dependencies
  1164. l = self.inputs["Array"].links[index]
  1165. for link in self.outputs["Output"].links:
  1166. to_node = link.to_node
  1167. l.from_node.outputs[l.from_socket].connect(to_node, link.to_socket)
  1168. link.die()
  1169. init_dependencies(to_node)
  1170. from_node=l.from_node
  1171. for inp in self.inputs.values():
  1172. for l in inp.links:
  1173. l.die()
  1174. init_connections(from_node)
  1175. if self in from_node.hierarchy_connections:
  1176. raise RuntimeError()
  1177. # this is intentional because the Array Get is kind of a weird hybrid between a Utility and a Schema
  1178. # so it should be removed from the tree when it is done. it has already dealt with the actual links.
  1179. # however I think this is redundant. Check.
  1180. self.hierarchy_connections, self.connections = [], []
  1181. self.hierarchy_dependencies, self.dependencies = [], []
  1182. self.prepared = True
  1183. self.executed = True
  1184. class UtilitySetBoneMatrixTail(MantisNode):
  1185. def __init__(self, signature, base_tree):
  1186. super().__init__(signature, base_tree)
  1187. inputs = {
  1188. "Matrix" ,
  1189. "Tail Location" ,
  1190. }
  1191. outputs = [
  1192. "Result" ,
  1193. ]
  1194. self.inputs.init_sockets(inputs)
  1195. self.outputs.init_sockets(outputs)
  1196. self.init_parameters()
  1197. self.node_type = "UTILITY"
  1198. def bPrepare(self, bContext = None,):
  1199. from mathutils import Matrix
  1200. matrix = self.evaluate_input("Matrix")
  1201. if matrix is None: matrix = Matrix.Identity(4)
  1202. #just do this for now lol
  1203. self.parameters["Result"] = matrix_from_head_tail(matrix.translation, self.evaluate_input("Tail Location"))
  1204. class UtilityPrint(MantisNode):
  1205. def __init__(self, signature, base_tree):
  1206. super().__init__(signature, base_tree)
  1207. inputs = [
  1208. "Input" ,
  1209. ]
  1210. self.inputs.init_sockets(inputs)
  1211. self.outputs.init_sockets(outputs)
  1212. self.init_parameters()
  1213. self.node_type = "UTILITY"
  1214. def bPrepare(self, bContext = None,):
  1215. if my_input := self.evaluate_input("Input"):
  1216. print("Preparation phase: ", wrapWhite(self), wrapGreen(my_input))
  1217. # else:
  1218. # prRed("No input to print.")
  1219. self.prepared = True
  1220. def bExecute(self, bContext = None,):
  1221. if my_input := self.evaluate_input("Input"):
  1222. print("Execution phase: ", wrapWhite(self), wrapGreen(my_input))
  1223. # else:
  1224. # prRed("No input to print.")
  1225. self.executed = True
  1226. class UtilityCompare(MantisNode):
  1227. def __init__(self, signature, base_tree):
  1228. super().__init__(signature, base_tree)
  1229. inputs = [
  1230. "A" ,
  1231. "B" ,
  1232. ]
  1233. outputs = [
  1234. "Result" ,
  1235. ]
  1236. self.inputs.init_sockets(inputs)
  1237. self.outputs.init_sockets(outputs)
  1238. self.init_parameters()
  1239. self.node_type = "UTILITY"
  1240. def bPrepare(self, bContext = None,):
  1241. self.parameters["Result"] = self.evaluate_input("A") == self.evaluate_input("B")
  1242. self.prepared = True; self.executed = True
  1243. class UtilityChoose(MantisNode):
  1244. def __init__(self, signature, base_tree):
  1245. super().__init__(signature, base_tree)
  1246. inputs = [
  1247. "Condition" ,
  1248. "A" ,
  1249. "B" ,
  1250. ]
  1251. outputs = [
  1252. "Result" ,
  1253. ]
  1254. self.inputs.init_sockets(inputs)
  1255. self.outputs.init_sockets(outputs)
  1256. self.init_parameters()
  1257. self.node_type = "UTILITY"
  1258. def bPrepare(self, bContext = None,):
  1259. condition = self.evaluate_input("Condition")
  1260. if condition:
  1261. self.parameters["Result"] = self.evaluate_input("B")
  1262. else:
  1263. self.parameters["Result"] = self.evaluate_input("A")
  1264. self.prepared = True
  1265. self.executed = True