misc_nodes_ui.py 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. import bpy
  2. from bpy.types import Node
  3. from .base_definitions import MantisUINode, get_signature_from_edited_tree
  4. from .misc_nodes_socket_templates import *
  5. from .utilities import (prRed, prGreen, prPurple, prWhite,
  6. prOrange,
  7. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  8. wrapOrange,)
  9. def TellClasses():
  10. return [ InputFloatNode,
  11. InputIntNode,
  12. InputVectorNode,
  13. InputBooleanNode,
  14. InputBooleanThreeTupleNode,
  15. InputRotationOrderNode,
  16. InputTransformSpaceNode,
  17. InputStringNode,
  18. InputMatrixNode,
  19. # InputGeometryNode,
  20. InputExistingGeometryObjectNode,
  21. InputExistingGeometryDataNode,
  22. UtilityDeclareCollections,
  23. UtilityCollectionJoin,
  24. UtilityCollectionHierarchy,
  25. UtilityGeometryOfXForm,
  26. UtilityNameOfXForm,
  27. # ComposeMatrixNode,
  28. MetaRigMatrixNode,
  29. UtilityPointFromCurve,
  30. UtilityMatrixFromCurve,
  31. UtilityMatricesFromCurve,
  32. UtilityNumberOfCurveSegments,
  33. UtilityNumberOfSplines,
  34. UtilityMatrixFromCurveSegment,
  35. UtilityGetCurvePoint,
  36. UtilityGetNearestFactorOnCurve,
  37. UtilityKDChoosePoint,
  38. UtilityKDChooseXForm,
  39. # ScaleBoneLengthNode,
  40. UtilityMetaRigNode,
  41. UtilityBonePropertiesNode,
  42. UtilityDriverVariableNode,
  43. UtilityFCurveNode,
  44. UtilityDriverNode,
  45. UtilitySwitchNode,
  46. UtilityKeyframe,
  47. UtilityCombineThreeBoolNode,
  48. UtilityCombineVectorNode,
  49. UtilitySeparateVector,
  50. UtilityCatStringsNode,
  51. UtilityGetBoneLength,
  52. UtilityPointFromBoneMatrix,
  53. UtilitySetBoneLength,
  54. UtilityMatrixSetLocation,
  55. UtilityMatrixGetLocation,
  56. UtilityMatrixFromXForm,
  57. UtilityAxesFromMatrix,
  58. UtilityBoneMatrixHeadTailFlip,
  59. UtilityMatrixTransform,
  60. UtilityMatrixInvert,
  61. UtilityMatrixCompose,
  62. UtilityMatrixAlignRoll,
  63. UtilityTransformationMatrix,
  64. UtilitySetBoneMatrixTail,
  65. UtilityIntToString,
  66. UtilityArrayGet,
  67. UtilityArrayLength,
  68. #
  69. UtilityCompare,
  70. UtilityChoose,
  71. # for testing
  72. UtilityPrint,
  73. ]
  74. def default_traverse(self,socket):
  75. return None
  76. class InputFloatNode(Node, MantisUINode):
  77. '''A node representing inheritance'''
  78. bl_idname = 'InputFloatNode'
  79. bl_label = "Float"
  80. bl_icon = 'NODE'
  81. initialized : bpy.props.BoolProperty(default = False)
  82. mantis_node_class_name=bl_idname[:-4]
  83. def init(self, context):
  84. self.outputs.new('FloatSocket', "Float Input").input = True
  85. self.initialized = True
  86. class InputIntNode(Node, MantisUINode):
  87. '''A node representing inheritance'''
  88. bl_idname = 'InputIntNode'
  89. bl_label = "Integer"
  90. bl_icon = 'NODE'
  91. initialized : bpy.props.BoolProperty(default = False)
  92. mantis_node_class_name=bl_idname
  93. def init(self, context):
  94. self.outputs.new('IntSocket', "Integer").input = True
  95. self.initialized = True
  96. class InputVectorNode(Node, MantisUINode):
  97. '''A node representing inheritance'''
  98. bl_idname = 'InputVectorNode'
  99. bl_label = "Vector"
  100. bl_icon = 'NODE'
  101. initialized : bpy.props.BoolProperty(default = False)
  102. mantis_node_class_name=bl_idname[:-4]
  103. def init(self, context):
  104. self.outputs.new('VectorSocket', "").input = True
  105. self.initialized = True
  106. class InputBooleanNode(Node, MantisUINode):
  107. '''A node representing inheritance'''
  108. bl_idname = 'InputBooleanNode'
  109. bl_label = "Boolean"
  110. bl_icon = 'NODE'
  111. initialized : bpy.props.BoolProperty(default = False)
  112. mantis_node_class_name=bl_idname[:-4]
  113. def init(self, context):
  114. self.outputs.new('BooleanSocket', "").input = True
  115. self.initialized = True
  116. class InputBooleanThreeTupleNode(Node, MantisUINode):
  117. '''A node representing inheritance'''
  118. bl_idname = 'InputBooleanThreeTupleNode'
  119. bl_label = "Boolean Vector"
  120. bl_icon = 'NODE'
  121. initialized : bpy.props.BoolProperty(default = False)
  122. mantis_node_class_name=bl_idname[:-4]
  123. def init(self, context):
  124. self.outputs.new('BooleanThreeTupleSocket', "")
  125. self.initialized = True
  126. class InputRotationOrderNode(Node, MantisUINode):
  127. '''A node representing inheritance'''
  128. bl_idname = 'InputRotationOrderNode'
  129. bl_label = "Rotation Order"
  130. bl_icon = 'NODE'
  131. initialized : bpy.props.BoolProperty(default = False)
  132. mantis_node_class_name=bl_idname[:-4]
  133. def init(self, context):
  134. self.outputs.new('RotationOrderSocket', "").input = True
  135. self.initialized = True
  136. class InputTransformSpaceNode(Node, MantisUINode):
  137. '''A node representing inheritance'''
  138. bl_idname = 'InputTransformSpaceNode'
  139. bl_label = "Transform Space"
  140. bl_icon = 'NODE'
  141. initialized : bpy.props.BoolProperty(default = False)
  142. mantis_node_class_name=bl_idname[:-4]
  143. def init(self, context):
  144. self.outputs.new('TransformSpaceSocket', "").input = True
  145. self.initialized = True
  146. class InputStringNode(Node, MantisUINode):
  147. '''A node representing inheritance'''
  148. bl_idname = 'InputStringNode'
  149. bl_label = "String"
  150. bl_icon = 'NODE'
  151. initialized : bpy.props.BoolProperty(default = False)
  152. mantis_node_class_name=bl_idname[:-4]
  153. def init(self, context):
  154. self.outputs.new('StringSocket', "").input = True
  155. self.initialized = True
  156. class InputMatrixNode(Node, MantisUINode):
  157. '''A node representing inheritance'''
  158. bl_idname = 'InputMatrixNode'
  159. bl_label = "Matrix"
  160. bl_icon = 'NODE'
  161. first_row : bpy.props.FloatVectorProperty(name="", size=4, default = (1.0, 0.0, 0.0, 0.0,))
  162. second_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 1.0, 0.0, 0.0,))
  163. third_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 1.0, 0.0,))
  164. fourth_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 0.0, 1.0,))
  165. initialized : bpy.props.BoolProperty(default = False)
  166. mantis_node_class_name=bl_idname[:-4]
  167. def set_matrix(self):
  168. return (self.first_row[ 0], self.first_row[ 1], self.first_row[ 2], self.first_row[ 3],
  169. self.second_row[0], self.second_row[1], self.second_row[2], self.second_row[3],
  170. self.third_row[ 0], self.third_row[ 1], self.third_row[ 2], self.third_row[ 3],
  171. self.fourth_row[0], self.fourth_row[1], self.fourth_row[2], self.fourth_row[3],)
  172. def init(self, context):
  173. self.outputs.new('MatrixSocket', "Matrix")
  174. self.initialized = True
  175. def draw_buttons(self, context, layout):
  176. # return
  177. layout.prop(self, "first_row")
  178. layout.prop(self, "second_row")
  179. layout.prop(self, "third_row")
  180. layout.prop(self, "fourth_row")
  181. def update(self):
  182. mat_sock = self.outputs[0]
  183. mat_sock.default_value = self.set_matrix()
  184. class ScaleBoneLengthNode(Node, MantisUINode):
  185. '''Scale Bone Length'''
  186. bl_idname = 'ScaleBoneLength'
  187. bl_label = "Scale Bone Length"
  188. bl_icon = 'NODE'
  189. initialized : bpy.props.BoolProperty(default = False)
  190. # === Optional Functions ===
  191. def init(self, context):
  192. self.inputs.new('MatrixSocket', "In Matrix")
  193. self.inputs.new('FloatSocket', "Factor")
  194. self.outputs.new('MatrixSocket', "Out Matrix")
  195. self.initialized = True
  196. class MetaRigMatrixNode(Node, MantisUINode):
  197. # Identical to the above, except
  198. '''A node representing a bone's matrix'''
  199. bl_idname = 'MetaRigMatrixNode'
  200. bl_label = "Matrix"
  201. bl_icon = 'NODE'
  202. first_row : bpy.props.FloatVectorProperty(name="", size=4, default = (1.0, 0.0, 0.0, 0.0,))
  203. second_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 1.0, 0.0, 0.0,))
  204. third_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 1.0, 0.0,))
  205. fourth_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 0.0, 1.0,))
  206. initialized : bpy.props.BoolProperty(default = False)
  207. mantis_node_class_name="UtilityMetaRig"
  208. def set_matrix(self):
  209. return (self.first_row[ 0], self.first_row[ 1], self.first_row[ 2], self.first_row[ 3],
  210. self.second_row[0], self.second_row[1], self.second_row[2], self.second_row[3],
  211. self.third_row[ 0], self.third_row[ 1], self.third_row[ 2], self.third_row[ 3],
  212. self.fourth_row[0], self.fourth_row[1], self.fourth_row[2], self.fourth_row[3],)
  213. def init(self, context):
  214. self.outputs.new('MatrixSocket', "Matrix")
  215. self.initialized = True
  216. def update(self):
  217. mat_sock = self.outputs[0]
  218. mat_sock.default_value = self.set_matrix()
  219. class UtilityMatrixFromCurve(Node, MantisUINode):
  220. """Gets a matrix from a curve."""
  221. bl_idname = "UtilityMatrixFromCurve"
  222. bl_label = "Matrix from Curve"
  223. bl_icon = "NODE"
  224. initialized : bpy.props.BoolProperty(default = False)
  225. mantis_node_class_name=bl_idname
  226. def init(self, context):
  227. self.init_sockets(MatrixFromCurveSockets)
  228. self.initialized = True
  229. class UtilityPointFromCurve(Node, MantisUINode):
  230. """Gets a point from a curve."""
  231. bl_idname = "UtilityPointFromCurve"
  232. bl_label = "Point from Curve"
  233. bl_icon = "NODE"
  234. initialized : bpy.props.BoolProperty(default = False)
  235. mantis_node_class_name=bl_idname
  236. def init(self, context):
  237. self.init_sockets(PointFromCurveSockets)
  238. self.initialized = True
  239. class UtilityNumberOfCurveSegments(Node, MantisUINode):
  240. """Tells the number of segments in a curve."""
  241. bl_idname = "UtilityNumberOfCurveSegments"
  242. bl_label = "Number of Curve Segments"
  243. bl_icon = "NODE"
  244. initialized : bpy.props.BoolProperty(default = False)
  245. mantis_node_class_name=bl_idname
  246. def init(self, context):
  247. self.inputs.new("EnumCurveSocket", "Curve")
  248. self.inputs.new('UnsignedIntSocket', 'Spline Index')
  249. self.outputs.new("UnsignedIntSocket", "Number of Segments")
  250. self.initialized = True
  251. class UtilityNumberOfSplines(Node, MantisUINode):
  252. """Tells the number of splines in a curve."""
  253. bl_idname = "UtilityNumberOfSplines"
  254. bl_label = "Number of Splines"
  255. bl_icon = "NODE"
  256. initialized : bpy.props.BoolProperty(default = False)
  257. mantis_node_class_name=bl_idname
  258. def init(self, context):
  259. self.init_sockets(NumberOfSplinesSockets)
  260. self.initialized = True
  261. class UtilityMatrixFromCurveSegment(Node, MantisUINode):
  262. """Gets a matrix from a curve segment."""
  263. bl_idname = "UtilityMatrixFromCurveSegment"
  264. bl_label = "Matrix from Curve Segment"
  265. bl_icon = "NODE"
  266. initialized : bpy.props.BoolProperty(default = False)
  267. mantis_node_class_name=bl_idname
  268. def init(self, context):
  269. self.init_sockets(MatrixFromCurveSegmentSockets)
  270. self.initialized = True
  271. class UtilityGetCurvePoint(Node, MantisUINode):
  272. bl_idname = 'UtilityGetCurvePoint'
  273. bl_label = "Control Point from Curve"
  274. bl_icon = 'NODE'
  275. initialized : bpy.props.BoolProperty(default = False)
  276. mantis_node_class_name=bl_idname
  277. def init(self, context):
  278. self.init_sockets(GetCurvePointSockets)
  279. self.initialized = True
  280. def display_update(self, parsed_tree, context):
  281. self.outputs["Point"].hide=False
  282. self.outputs["Left Handle"].hide=True
  283. self.outputs["Right Handle"].hide=True
  284. spline_index = self.inputs['Spline Index'].default_value
  285. index = self.inputs['Index'].default_value
  286. curve = self.inputs['Curve'].default_value
  287. if self.inputs['Spline Index'].is_linked or self.inputs['Index'].is_linked \
  288. or self.inputs['Curve'].is_linked:
  289. mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
  290. spline_index = mantis_node.evaluate_input("Spline Index")
  291. index = mantis_node.evaluate_input("Index")
  292. curve = mantis_node.evaluate_input("Curve")
  293. if curve := bpy.data.objects.get(curve):
  294. if curve.type != "CURVE":
  295. self.outputs["Point"].hide=True
  296. spline = curve.data.splines[spline_index]
  297. if spline.type == 'BEZIER':
  298. self.outputs["Left Handle"].hide=False
  299. self.outputs["Right Handle"].hide=False
  300. class UtilityMatricesFromCurve(Node, MantisUINode):
  301. """Gets a matrix from a curve."""
  302. bl_idname = "UtilityMatricesFromCurve"
  303. bl_label = "Matrices from Curve"
  304. bl_icon = "NODE"
  305. initialized : bpy.props.BoolProperty(default = False)
  306. mantis_node_class_name=bl_idname
  307. def init(self, context):
  308. self.init_sockets(MatricesFromCurveSockets)
  309. self.initialized = True
  310. def display_update_choose_nearest(self, parsed_tree, context):
  311. number_of_points = self.inputs['Number to Find'].default_value
  312. if self.inputs["Number to Find"].is_linked:
  313. mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
  314. number_of_points = mantis_node.evaluate_input("Number to Find")
  315. elif number_of_points == 0:
  316. self.inputs['Number to Find'].default_value=1
  317. if number_of_points > 1:
  318. # then we need to make it an array out
  319. self.outputs.display_shape = 'SQUARE_DOT'
  320. else:
  321. self.outputs.display_shape = 'CIRCLE'
  322. class UtilityGetNearestFactorOnCurve(Node, MantisUINode):
  323. bl_idname = 'UtilityGetNearestFactorOnCurve'
  324. bl_label = "Get Factor on Curve at Point"
  325. bl_icon = 'NODE'
  326. initialized : bpy.props.BoolProperty(default = False)
  327. mantis_node_class_name=bl_idname
  328. def init(self, context):
  329. self.init_sockets(GetNearestFactorOnCurveSockets)
  330. self.initialized = True
  331. class UtilityKDChoosePoint(Node, MantisUINode):
  332. """Chooses the nearest point with a KD Tree."""
  333. bl_idname = "UtilityKDChoosePoint"
  334. bl_label = "Choose Nearest Point"
  335. bl_icon = "NODE"
  336. initialized : bpy.props.BoolProperty(default = False)
  337. mantis_node_class_name=bl_idname
  338. def init(self, context):
  339. self.inputs.new("VectorSocket", "Reference Point")
  340. a = self.inputs.new('VectorSocket', 'Points', use_multi_input=True)
  341. a.display_shape='SQUARE_DOT'
  342. s = self.inputs.new("UnsignedIntSocket", "Number to Find")
  343. s.default_value=1
  344. self.outputs.new("VectorSocket", "Result Point")
  345. self.outputs.new("UnsignedIntSocket", "Result Index")
  346. self.outputs.new("FloatSocket", "Result Distance")
  347. self.initialized = True
  348. def display_update(self, parsed_tree, context):
  349. display_update_choose_nearest(self, parsed_tree, context)
  350. class UtilityKDChooseXForm(Node, MantisUINode):
  351. """Chooses the nearest xForm with a KD Tree."""
  352. bl_idname = "UtilityKDChooseXForm"
  353. bl_label = "Choose Nearest xForm"
  354. bl_icon = "NODE"
  355. initialized : bpy.props.BoolProperty(default = False)
  356. mantis_node_class_name=bl_idname
  357. def init(self, context):
  358. self.inputs.new("VectorSocket", "Reference Point")
  359. self.inputs.new('xFormSocket', 'xForm Nodes', use_multi_input=True)
  360. self.inputs.new("FloatFactorSocket", "Get Point Head/Tail")
  361. s = self.inputs.new("UnsignedIntSocket", "Number to Find")
  362. s.default_value=1
  363. self.outputs.new("xFormSocket", "Result xForm")
  364. self.outputs.new("UnsignedIntSocket", "Result Index")
  365. self.outputs.new("FloatSocket", "Result Distance")
  366. self.initialized = True
  367. def display_update(self, parsed_tree, context):
  368. display_update_choose_nearest(self, parsed_tree, context)
  369. class UtilityMetaRigNode(Node, MantisUINode):
  370. """Gets a matrix from a meta-rig bone."""
  371. bl_idname = "UtilityMetaRig"
  372. bl_label = "Meta-Rig"
  373. bl_icon = "NODE"
  374. armature:bpy.props.StringProperty()
  375. pose_bone:bpy.props.StringProperty()
  376. initialized : bpy.props.BoolProperty(default = False)
  377. mantis_node_class_name=bl_idname
  378. def init(self, context):
  379. armt = self.inputs.new("EnumMetaRigSocket", "Meta-Armature")
  380. bone = self.inputs.new("EnumMetaBoneSocket", "Meta-Bone")
  381. bone.hide=True
  382. self.outputs.new("MatrixSocket", "Matrix")
  383. self.initialized = True
  384. def display_update(self, parsed_tree, context):
  385. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  386. if nc:
  387. self.armature= nc.evaluate_input("Meta-Armature")
  388. self.pose_bone= nc.evaluate_input("Meta-Bone")
  389. if not self.armature:
  390. self.inputs["Meta-Bone"].hide=True
  391. else:
  392. self.inputs["Meta-Bone"].hide=False
  393. if self.inputs["Meta-Armature"].is_linked:
  394. self.inputs["Meta-Armature"].search_prop = None
  395. if self.inputs["Meta-Bone"].is_linked:
  396. self.inputs["Meta-Bone"].search_prop = None
  397. class UtilityBonePropertiesNode(Node, MantisUINode):
  398. """Provides as sockets strings identifying bone transform properties."""
  399. bl_idname = "UtilityBoneProperties"
  400. bl_label = "Bone Properties"
  401. bl_icon = "NODE"
  402. #bl_width_default = 250
  403. initialized : bpy.props.BoolProperty(default = False)
  404. mantis_node_class_name=bl_idname
  405. def init(self, context):
  406. self.outputs.new("StringSocket", "matrix")
  407. self.outputs.new("StringSocket", "matrix_local")
  408. self.outputs.new("StringSocket", "matrix_basis")
  409. self.outputs.new("StringSocket", "head")
  410. self.outputs.new("StringSocket", "tail")
  411. self.outputs.new("StringSocket", "length")
  412. self.outputs.new("StringSocket", "rotation")
  413. self.outputs.new("StringSocket", "location")
  414. self.outputs.new("StringSocket", "scale")
  415. self.initialized = True
  416. class UtilityDriverVariableNode(Node, MantisUINode):
  417. """Creates a variable for use in a driver."""
  418. bl_idname = "UtilityDriverVariable"
  419. bl_label = "Driver Variable"
  420. bl_icon = "NODE"
  421. initialized : bpy.props.BoolProperty(default = False)
  422. mantis_node_class_name=bl_idname
  423. def init(self, context):
  424. self.inputs.new("EnumDriverVariableType", "Variable Type") # 0
  425. self.inputs.new("ParameterStringSocket", "Property") # 1
  426. self.inputs.new("IntSocket", "Property Index") # 2
  427. self.inputs.new("EnumDriverVariableEvaluationSpace", "Evaluation Space") # 3
  428. self.inputs.new("EnumDriverRotationMode", "Rotation Mode") # 4
  429. self.inputs.new("xFormSocket", "xForm 1") # 5
  430. self.inputs.new("xFormSocket", "xForm 2") # 6
  431. self.outputs.new("DriverVariableSocket", "Driver Variable")
  432. self.inputs[3].hide = True
  433. self.initialized = True
  434. def display_update(self, parsed_tree, context):
  435. if self.inputs["Variable Type"].is_linked:
  436. if context.space_data:
  437. node_tree = context.space_data.path[0].node_tree
  438. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  439. if nc:
  440. driver_type = nc.evaluate_input("Variable Type")
  441. else:
  442. driver_type = self.inputs[0].default_value
  443. if driver_type == 'SINGLE_PROP':
  444. self.inputs[1].hide = False
  445. self.inputs[2].hide = False
  446. self.inputs[3].hide = False
  447. self.inputs[4].hide = False
  448. self.inputs[5].hide = False
  449. self.inputs[6].hide = True
  450. elif driver_type == 'LOC_DIFF':
  451. self.inputs[1].hide = True
  452. self.inputs[2].hide = True
  453. self.inputs[3].hide = True
  454. self.inputs[4].hide = True
  455. self.inputs[5].hide = False
  456. self.inputs[6].hide = False
  457. elif driver_type == 'ROTATION_DIFF':
  458. self.inputs[1].hide = True
  459. self.inputs[2].hide = True
  460. self.inputs[3].hide = True
  461. self.inputs[4].hide = False
  462. self.inputs[5].hide = False
  463. self.inputs[6].hide = False
  464. elif driver_type == 'TRANSFORMS':
  465. self.inputs[1].hide = True
  466. self.inputs[2].hide = True
  467. self.inputs[3].hide = False
  468. self.inputs[4].hide = False
  469. self.inputs[5].hide = False
  470. self.inputs[6].hide = True
  471. # TODO: make a way to edit the fCurve directly.
  472. # I had a working version of this in the past, but it required doing sinful things like
  473. # keeping track of the RAM address of the window.
  474. class UtilityFCurveNode(Node, MantisUINode):
  475. """Creates an fCurve for use with a driver."""
  476. bl_idname = "UtilityFCurve"
  477. bl_label = "fCurve"
  478. bl_icon = "NODE"
  479. use_kf_nodes : bpy.props.BoolProperty(default=True)
  480. initialized : bpy.props.BoolProperty(default = False)
  481. mantis_node_class_name=bl_idname
  482. def init(self, context):
  483. self.inputs.new("eFCrvExtrapolationMode", "Extrapolation Mode")
  484. self.outputs.new("FCurveSocket", "fCurve")
  485. self.initialized = True
  486. def draw_buttons(self, context, layout):
  487. layout.operator( 'mantis.fcurve_node_add_kf' )
  488. if (len(self.inputs) > 1):
  489. layout.operator( 'mantis.fcurve_node_remove_kf' )
  490. class UtilityDriverNode(Node, MantisUINode):
  491. """Represents a Driver relationship"""
  492. bl_idname = "UtilityDriver"
  493. bl_label = "Driver"
  494. bl_icon = "NODE"
  495. initialized : bpy.props.BoolProperty(default = False)
  496. mantis_node_class_name=bl_idname
  497. def init(self, context):
  498. self.inputs.new("EnumDriverType", "Driver Type")
  499. self.inputs.new("FCurveSocket", "fCurve")
  500. self.inputs.new("StringSocket", "Expression")
  501. self.outputs.new("DriverSocket", "Driver")
  502. self.initialized = True
  503. def display_update(self, parsed_tree, context):
  504. if not self.inputs["Driver Type"].is_linked:
  505. dType = self.inputs["Driver Type"].default_value
  506. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  507. if nc:
  508. dType = nc.evaluate_input("Driver Type")
  509. if dType == 'SCRIPTED':
  510. self.inputs["Expression"].hide = False
  511. else:
  512. self.inputs["Expression"].hide = True
  513. def draw_buttons(self, context, layout):
  514. # return
  515. layout.operator( 'mantis.driver_node_add_variable' )
  516. if (len(self.inputs) > 3):
  517. layout.operator( 'mantis.driver_node_remove_variable' )
  518. class UtilitySwitchNode(Node, MantisUINode):
  519. """Represents a switch relationship between one driver property and one or more driven properties."""
  520. bl_idname = "UtilitySwitch"
  521. bl_label = "Switch"
  522. bl_icon = "NODE"
  523. initialized : bpy.props.BoolProperty(default = False)
  524. mantis_node_class_name=bl_idname
  525. def init(self, context):
  526. # self.inputs.new("xFormSocket", "xForm")
  527. self.inputs.new("ParameterStringSocket", "Parameter")
  528. self.inputs.new("IntSocket", "Parameter Index")
  529. self.inputs.new("BooleanSocket", "Invert Switch")
  530. self.outputs.new("DriverSocket", "Driver")
  531. self.initialized = True
  532. class UtilityCombineThreeBoolNode(Node, MantisUINode):
  533. """Combines three booleans into a three-bool."""
  534. bl_idname = "UtilityCombineThreeBool"
  535. bl_label = "CombineThreeBool"
  536. bl_icon = "NODE"
  537. initialized : bpy.props.BoolProperty(default = False)
  538. mantis_node_class_name=bl_idname
  539. def init(self, context):
  540. self.inputs.new("BooleanSocket", "X")
  541. self.inputs.new("BooleanSocket", "Y")
  542. self.inputs.new("BooleanSocket", "Z")
  543. self.outputs.new("BooleanThreeTupleSocket", "Three-Bool")
  544. self.initialized = True
  545. class UtilityCombineVectorNode(Node, MantisUINode):
  546. """Combines three floats into a vector."""
  547. bl_idname = "UtilityCombineVector"
  548. bl_label = "CombineVector"
  549. bl_icon = "NODE"
  550. initialized : bpy.props.BoolProperty(default = False)
  551. mantis_node_class_name=bl_idname
  552. def init(self, context):
  553. self.inputs.new("FloatSocket", "X")
  554. self.inputs.new("FloatSocket", "Y")
  555. self.inputs.new("FloatSocket", "Z")
  556. self.outputs.new("VectorSocket", "Vector")
  557. self.initialized = True
  558. class UtilitySeparateVector(Node, MantisUINode):
  559. """Separates a vector into three floats"""
  560. bl_idname = "UtilitySeparateVector"
  561. bl_label = "Separate Vector"
  562. bl_icon = "NODE"
  563. initialized : bpy.props.BoolProperty(default = False)
  564. mantis_node_class_name=bl_idname
  565. def init(self, context):
  566. self.inputs.new("VectorSocket", "Vector")
  567. self.outputs.new("FloatSocket", "X")
  568. self.outputs.new("FloatSocket", "Y")
  569. self.outputs.new("FloatSocket", "Z")
  570. self.initialized = True
  571. class UtilityCatStringsNode(Node, MantisUINode):
  572. """Adds a suffix to a string"""
  573. bl_idname = "UtilityCatStrings"
  574. bl_label = "Concatenate Strings"
  575. bl_icon = "NODE"
  576. initialized : bpy.props.BoolProperty(default = False)
  577. mantis_node_class_name=bl_idname
  578. def init(self, context):
  579. self.inputs.new("StringSocket", "String_1")
  580. self.inputs.new("StringSocket", "String_2")
  581. self.outputs.new("StringSocket", "OutputString")
  582. self.initialized = True
  583. def draw_label(self): # this will prefer a user-set label, or return the evaluated name
  584. if self.label:
  585. return self.label
  586. if self.outputs['OutputString'].display_text:
  587. return self.outputs['OutputString'].display_text
  588. return self.name
  589. def display_update(self, parsed_tree, context):
  590. if context.space_data:
  591. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  592. self.inputs['String_1'].display_text = ""
  593. self.inputs['String_2'].display_text = ""
  594. self.outputs['OutputString'].display_text = ""
  595. if nc:
  596. try:
  597. self.inputs['String_1'].display_text = a = nc.evaluate_input("String_1")
  598. self.inputs['String_2'].display_text = b = nc.evaluate_input("String_2")
  599. # cat the strings here, since the node may not have run yet.
  600. self.outputs['OutputString'].display_text = a+b
  601. except KeyError:
  602. return # the tree isn't ready yet.
  603. class InputLayerMaskNode(Node, MantisUINode):
  604. """Represents a layer mask for a bone."""
  605. bl_idname = "InputLayerMaskNode"
  606. bl_label = "Layer Mask"
  607. bl_icon = "NODE"
  608. initialized : bpy.props.BoolProperty(default = False)
  609. mantis_node_class_name=bl_idname
  610. def init(self, context):
  611. self.outputs.new("LayerMaskInputSocket", "Layer Mask")
  612. self.initialized = True
  613. class InputExistingGeometryObjectNode(Node, MantisUINode):
  614. """Represents an existing geometry object from within the scene."""
  615. bl_idname = "InputExistingGeometryObject"
  616. bl_label = "Existing Object"
  617. bl_icon = "NODE"
  618. initialized : bpy.props.BoolProperty(default = False)
  619. mantis_node_class_name=bl_idname
  620. # We want Mantis to import widgets and stuff, so we hold a reference to the object
  621. object_reference : bpy.props.PointerProperty(type=bpy.types.Object,)
  622. def init(self, context):
  623. self.inputs.new("StringSocket", "Name")
  624. self.outputs.new("xFormSocket", "Object")
  625. self.initialized = True
  626. def display_update(self, parsed_tree, context):
  627. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  628. if nc: # this is done here so I don't have to define yet another custom socket.
  629. self.object_reference = bpy.data.objects.get(nc.evaluate_input("Name"))
  630. # TODO: maybe I should hold a data reference here, too.
  631. # but it is complicated by the fact that Mantis does not distinguish b/tw geo types
  632. class InputExistingGeometryDataNode(Node, MantisUINode):
  633. """Represents a mesh or curve datablock from the scene."""
  634. bl_idname = "InputExistingGeometryData"
  635. bl_label = "Existing Geometry"
  636. bl_icon = "NODE"
  637. initialized : bpy.props.BoolProperty(default = False)
  638. mantis_node_class_name=bl_idname
  639. def init(self, context):
  640. self.inputs.new("StringSocket", "Name")
  641. self.outputs.new("GeometrySocket", "Geometry")
  642. self.initialized = True
  643. def socket_data_from_collection_paths(root_data, root_name, path, socket_data):
  644. # so we need to 'push' the socket names and their paths in order
  645. # socket_data is a list of tuples of ( name, path, )
  646. for key, value in root_data.items():
  647. path.append(key)
  648. socket_data.append( (key, path))
  649. if hasattr(value , 'items'):
  650. socket_data = socket_data_from_collection_paths(value, key, path.copy(), socket_data)
  651. path.pop()
  652. return socket_data
  653. class UtilityDeclareCollections(Node, MantisUINode):
  654. """A utility used to declare bone collections."""
  655. bl_idname = "UtilityDeclareCollections"
  656. bl_label = "Collections"
  657. bl_icon = "NODE"
  658. bl_width_min = 320
  659. initialized : bpy.props.BoolProperty(default = False)
  660. mantis_node_class_name=bl_idname
  661. # Here is the layout of the data:
  662. # nested dicts of key:dict ( key = name, dict = children)
  663. # the 'leaf nodes' are empty dicts
  664. # we'll store it as a JSON string in order to make it a bpy.props
  665. # and still have the ability to use it as a dict and save it
  666. # TODO: check and see if these strings have a character limit
  667. collection_declarations : bpy.props.StringProperty(default="")
  668. def update_interface(self):
  669. # we need to do dynamic stuff here like with interfaces
  670. self.outputs.clear()
  671. current_data = self.read_declarations_from_json()
  672. socket_data = socket_data_from_collection_paths(current_data, self.name, [], [])
  673. for item in socket_data:
  674. full_path_name = '>'.join(item[1]+[item[0]])
  675. s = self.outputs.new('CollectionDeclarationSocket', name=item[0],identifier=full_path_name )
  676. s.collection_path = full_path_name
  677. def init(self, context):
  678. self.initialized = True
  679. if self.collection_declarations == "":
  680. self.push_declarations_to_json({})
  681. def push_declarations_to_json(self, dict):
  682. import json
  683. j_str = json.dumps(dict)
  684. self.collection_declarations = j_str
  685. def read_declarations_from_json(self):
  686. import json
  687. j_data = json.loads(self.collection_declarations)
  688. return j_data
  689. def draw_buttons(self, context, layout):
  690. op_props = layout.operator('mantis.collection_add_new')
  691. op_props.socket_invoked = '' # this isn't reset between invocations
  692. # so we have to make sure to unset it when running it from the node
  693. class UtilityCollectionJoin(Node, MantisUINode):
  694. """A utility used to join bone collection declarations."""
  695. bl_idname = "UtilityCollectionJoin"
  696. bl_label = "Join Collection Declarations"
  697. bl_icon = "NODE"
  698. initialized : bpy.props.BoolProperty(default = False)
  699. mantis_node_class_name=bl_idname
  700. def init(self, context):
  701. self.init_sockets(CollectionJoinSockets)
  702. self.initialized = True
  703. class UtilityCollectionHierarchy(Node, MantisUINode):
  704. """A utility used to declare bone collection hierarchies."""
  705. bl_idname = "UtilityCollectionHierarchy"
  706. bl_label = "Collection Child Declaration"
  707. bl_icon = "NODE"
  708. initialized : bpy.props.BoolProperty(default = False)
  709. mantis_node_class_name=bl_idname
  710. def init(self, context):
  711. self.init_sockets(CollectionHierarchySockets)
  712. self.initialized = True
  713. class UtilityGeometryOfXForm(Node, MantisUINode):
  714. """Retrieves a mesh or curve datablock from an xForm."""
  715. bl_idname = "UtilityGeometryOfXForm"
  716. bl_label = "Geometry of xForm"
  717. bl_icon = "NODE"
  718. initialized : bpy.props.BoolProperty(default = False)
  719. mantis_node_class_name=bl_idname
  720. def init(self, context):
  721. self.inputs.new("xFormSocket", "xForm")
  722. self.outputs.new("GeometrySocket", "Geometry")
  723. self.initialized = True
  724. class UtilityNameOfXForm(Node, MantisUINode):
  725. """Retrieves the name of a xForm."""
  726. bl_idname = "UtilityNameOfXForm"
  727. bl_label = "Name of xForm"
  728. bl_icon = "NODE"
  729. initialized : bpy.props.BoolProperty(default = False)
  730. mantis_node_class_name=bl_idname
  731. def init(self, context):
  732. self.inputs.new("xFormSocket", "xForm")
  733. self.outputs.new("StringSocket", "Name")
  734. self.initialized = True
  735. class UtilityGetBoneLength(Node, MantisUINode):
  736. """Returns the length of the bone from its matrix."""
  737. bl_idname = "UtilityGetBoneLength"
  738. bl_label = "Get Bone Length"
  739. bl_icon = "NODE"
  740. initialized : bpy.props.BoolProperty(default = False)
  741. mantis_node_class_name=bl_idname
  742. def init(self, context):
  743. self.inputs.new("MatrixSocket", "Bone Matrix")
  744. self.outputs.new("FloatSocket", "Bone Length")
  745. self.initialized = True
  746. # TODO: make it work with BBones!
  747. class UtilityPointFromBoneMatrix(Node, MantisUINode):
  748. """Returns a point representing the location along a bone, given a matrix representing that bone's shape."""
  749. bl_idname = "UtilityPointFromBoneMatrix"
  750. bl_label = "Point from Bone Matrix"
  751. bl_icon = "NODE"
  752. initialized : bpy.props.BoolProperty(default = False)
  753. mantis_node_class_name=bl_idname
  754. def init(self, context):
  755. self.inputs.new("MatrixSocket", "Bone Matrix")
  756. self.inputs.new("FloatFactorSocket", "Head/Tail")
  757. self.outputs.new("VectorSocket", "Point")
  758. self.initialized = True
  759. class UtilitySetBoneLength(Node, MantisUINode):
  760. """Sets the length of a bone matrix."""
  761. bl_idname = "UtilitySetBoneLength"
  762. bl_label = "Set Bone Matrix Length"
  763. bl_icon = "NODE"
  764. initialized : bpy.props.BoolProperty(default = False)
  765. mantis_node_class_name=bl_idname
  766. def init(self, context):
  767. self.inputs.new("MatrixSocket", "Bone Matrix")
  768. self.inputs.new("FloatSocket", "Length")
  769. self.outputs.new("MatrixSocket", "Bone Matrix")
  770. self.initialized = True
  771. # TODO: more keyframe types should be supported in the future.
  772. # Some of the code that can do this is commented out here until I can implement it properly.
  773. class UtilityKeyframe(Node, MantisUINode):
  774. """A keyframe for a FCurve"""
  775. bl_idname = "UtilityKeyframe"
  776. bl_label = "KeyFrame"
  777. bl_icon = "NODE"
  778. initialized : bpy.props.BoolProperty(default = False)
  779. mantis_node_class_name=bl_idname
  780. def init(self, context):
  781. # x and y
  782. # output is keyframe
  783. # self.inputs.new("EnumKeyframeInterpolationTypeSocket", "Interpolation")
  784. # self.inputs.new("EnumKeyframeBezierHandleType", "Left Handle Type")
  785. # self.inputs.new("EnumKeyframeBezierHandleType", "Right Handle Type")
  786. # self.inputs.new("FloatSocket", "Left Handle Distance")
  787. # self.inputs.new("FloatSocket", "Left Handle Value")
  788. # self.inputs.new("FloatSocket", "Right Handle Frame")
  789. # self.inputs.new("FloatSocket", "Right Handle Value")
  790. self.inputs.new("FloatSocket", "Frame")
  791. self.inputs.new("FloatSocket", "Value")
  792. self.outputs.new("KeyframeSocket", "Keyframe")
  793. # there will eventually be inputs for e.g. key type, key handles, etc.
  794. # right now I am gonna hardcode LINEAR keyframes so I don't have to deal with anything else
  795. # TODO TODO TODO
  796. # def display_update(self, parsed_tree, context):
  797. # if context.space_data:
  798. # nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  799. # if nc.evaluate_input("Interpolation") in ["CONSTANT", "LINEAR"]:
  800. # for inp in self.inputs[1:6]:
  801. # inp.hide = True
  802. # else:
  803. # if nc.evaluate_input("Left Handle Type") in ["FREE", "ALIGNED"]:
  804. # for inp in self.inputs[1:6]:
  805. # inp.hide = False
  806. self.initialized = True
  807. class UtilityBoneMatrixHeadTailFlip(Node, MantisUINode):
  808. """Flips a bone matrix so that the head is where the tail was and visa versa."""
  809. bl_idname = "UtilityBoneMatrixHeadTailFlip"
  810. bl_label = "Flip Head/Tail"
  811. bl_icon = "NODE"
  812. initialized : bpy.props.BoolProperty(default = False)
  813. mantis_node_class_name=bl_idname
  814. def init(self, context):
  815. self.inputs.new("MatrixSocket", "Bone Matrix")
  816. self.outputs.new("MatrixSocket", "Bone Matrix")
  817. self.initialized = True
  818. class UtilityMatrixTransform(Node, MantisUINode):
  819. """Transforms a matrix by another."""
  820. bl_idname = "UtilityMatrixTransform"
  821. bl_label = "Multiply Matrices"
  822. bl_icon = "NODE"
  823. initialized : bpy.props.BoolProperty(default = False)
  824. mantis_node_class_name=bl_idname
  825. def init(self, context):
  826. self.inputs.new("MatrixSocket", "Matrix 1")
  827. self.inputs.new("MatrixSocket", "Matrix 2")
  828. self.outputs.new("MatrixSocket", "Out Matrix")
  829. self.initialized = True
  830. class UtilityMatrixInvert(Node, MantisUINode):
  831. """Inverts an invertable matrix, otherwise throws an error."""
  832. bl_idname = "UtilityMatrixInvert"
  833. bl_label = "Invert Matrix"
  834. bl_icon = "NODE"
  835. initialized : bpy.props.BoolProperty(default = False)
  836. mantis_node_class_name=bl_idname
  837. def init(self, context):
  838. self.init_sockets(MatrixInvertSockets)
  839. self.initialized = True
  840. class UtilityMatrixCompose(Node, MantisUINode):
  841. """Inverts an invertable matrix, otherwise throws an error."""
  842. bl_idname = "UtilityMatrixCompose"
  843. bl_label = "Compose Matrix"
  844. bl_icon = "NODE"
  845. initialized : bpy.props.BoolProperty(default = False)
  846. mantis_node_class_name=bl_idname
  847. def init(self, context):
  848. self.init_sockets(MatrixComposeSockets)
  849. self.initialized = True
  850. class UtilityMatrixAlignRoll(Node, MantisUINode):
  851. """Aligns the Z axis of a matrix to point in a direction."""
  852. bl_idname = "UtilityMatrixAlignRoll"
  853. bl_label = "Align Matrix Roll"
  854. bl_icon = "NODE"
  855. initialized : bpy.props.BoolProperty(default = False)
  856. mantis_node_class_name=bl_idname
  857. def init(self, context):
  858. self.init_sockets(MatrixAlignRollSockets)
  859. self.initialized = True
  860. class UtilityMatrixSetLocation(Node, MantisUINode):
  861. """Sets a matrix's location."""
  862. bl_idname = "UtilityMatrixSetLocation"
  863. bl_label = "Set Matrix Location"
  864. bl_icon = "NODE"
  865. initialized : bpy.props.BoolProperty(default = False)
  866. mantis_node_class_name=bl_idname
  867. def init(self, context):
  868. self.inputs.new("MatrixSocket", "Matrix")
  869. self.inputs.new("VectorSocket", "Location")
  870. self.outputs.new("MatrixSocket", "Matrix")
  871. self.initialized = True
  872. class UtilityMatrixGetLocation(Node, MantisUINode):
  873. """Gets a matrix's location."""
  874. bl_idname = "UtilityMatrixGetLocation"
  875. bl_label = "Get Matrix Location"
  876. bl_icon = "NODE"
  877. initialized : bpy.props.BoolProperty(default = False)
  878. mantis_node_class_name=bl_idname
  879. def init(self, context):
  880. self.inputs.new("MatrixSocket", "Matrix")
  881. self.outputs.new("VectorSocket", "Location")
  882. self.initialized = True
  883. class UtilityTransformationMatrix(Node, MantisUINode):
  884. """Constructs a matrix representing a transformation"""
  885. bl_idname = "UtilityTransformationMatrix"
  886. bl_label = "Transformation Matrix"
  887. bl_icon = "NODE"
  888. initialized : bpy.props.BoolProperty(default = False)
  889. mantis_node_class_name=bl_idname
  890. def init(self, context):
  891. # first input is a transformation type - translation, rotation, or scale
  892. # rotation is an especially annoying feature because it can take multiple types
  893. # so Euler, axis/angle, quaternion, matrix...
  894. # for now I am only going to implement axis-angle
  895. # it should get an axis and a magnitude
  896. # self.inputs.new("MatrixSocket", "Bone Matrix")
  897. self.inputs.new("MatrixTransformOperation", "Operation")
  898. self.inputs.new("VectorSocket", "Vector")
  899. self.inputs.new("FloatSocket", "W")
  900. self.outputs.new("MatrixSocket", "Matrix")
  901. self.initialized = True
  902. def display_update(self, parsed_tree, context):
  903. operation = self.inputs['Operation'].default_value
  904. if self.inputs['Operation'].is_linked:
  905. if context.space_data:
  906. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  907. operation = nc.evaluate_input("Operation")
  908. if operation in ["ROTATE_AXIS_ANGLE", "SCALE"]:
  909. self.inputs["Vector"].hide = False
  910. self.inputs["W"].hide = False
  911. if operation in ["TRANSLATE"]:
  912. self.inputs["Vector"].hide = False
  913. self.inputs["W"].hide = True
  914. # Blender calculates bone roll this way...
  915. # https://projects.blender.org/blender/blender/src/commit/dd209221675ac7b62ce47b7ea42f15cbe34a6035/source/blender/editors/armature/armature_edit.cc#L281
  916. # but this looks like it will be harder to re-implement than to re-use. Unfortunately, it doesn't apply directly to a matrix so I have to call a method
  917. # in the edit bone.
  918. # So instead, we need to avoid calculating the roll for now.
  919. # but I want to make that its own node and add roll-recalc to this node, too.
  920. class UtilitySetBoneMatrixTail(Node, MantisUINode):
  921. """Constructs a matrix representing a transformation"""
  922. bl_idname = "UtilitySetBoneMatrixTail"
  923. bl_label = "Set Bone Matrix Tail"
  924. bl_icon = "NODE"
  925. initialized : bpy.props.BoolProperty(default = False)
  926. mantis_node_class_name=bl_idname
  927. def init(self, context):
  928. self.inputs.new("MatrixSocket", "Matrix")
  929. self.inputs.new("VectorSocket", "Tail Location")
  930. self.outputs.new("MatrixSocket", "Result")
  931. self.initialized = True
  932. class UtilityMatrixFromXForm(Node, MantisUINode):
  933. """Returns the matrix of the given xForm node."""
  934. bl_idname = "UtilityMatrixFromXForm"
  935. bl_label = "Matrix of xForm"
  936. bl_icon = "NODE"
  937. initialized : bpy.props.BoolProperty(default = False)
  938. mantis_node_class_name=bl_idname
  939. def init(self, context):
  940. self.inputs.new("xFormSocket", "xForm")
  941. self.outputs.new("MatrixSocket", "Matrix")
  942. self.initialized = True
  943. class UtilityAxesFromMatrix(Node, MantisUINode):
  944. """Returns the axes of the matrix."""
  945. bl_idname = "UtilityAxesFromMatrix"
  946. bl_label = "Axes of Matrix"
  947. bl_icon = "NODE"
  948. initialized : bpy.props.BoolProperty(default = False)
  949. mantis_node_class_name=bl_idname
  950. def init(self, context):
  951. self.inputs.new("MatrixSocket", "Matrix")
  952. self.outputs.new("VectorSocket", "X Axis")
  953. self.outputs.new("VectorSocket", "Y Axis")
  954. self.outputs.new("VectorSocket", "Z Axis")
  955. self.initialized = True
  956. class UtilityIntToString(Node, MantisUINode):
  957. """Converts a number to a string"""
  958. bl_idname = "UtilityIntToString"
  959. bl_label = "Number String"
  960. bl_icon = "NODE"
  961. initialized : bpy.props.BoolProperty(default = False)
  962. mantis_node_class_name=bl_idname
  963. def init(self, context):
  964. self.inputs.new("IntSocket", "Number")
  965. self.inputs.new("IntSocket", "Zero Padding")
  966. self.outputs.new("StringSocket", "String")
  967. self.initialized = True
  968. class UtilityArrayGet(Node, MantisUINode):
  969. """Gets a value from an array at a specified index."""
  970. bl_idname = "UtilityArrayGet"
  971. bl_label = "Array Get"
  972. bl_icon = "NODE"
  973. initialized : bpy.props.BoolProperty(default = False)
  974. mantis_node_class_name=bl_idname
  975. def init(self, context):
  976. self.inputs.new('EnumArrayGetOptions', 'OoB Behaviour')
  977. self.inputs.new("IntSocket", "Index")
  978. s = self.inputs.new("WildcardSocket", "Array", use_multi_input=True)
  979. s.display_shape = 'SQUARE_DOT'
  980. self.outputs.new("WildcardSocket", "Output")
  981. self.initialized = True
  982. def update(self):
  983. wildcard_color = (0.0,0.0,0.0,0.0)
  984. if self.inputs['Array'].is_linked == False:
  985. self.inputs['Array'].color = wildcard_color
  986. self.outputs['Output'].color = wildcard_color
  987. def insert_link(self, link):
  988. super().insert_link(link)
  989. if link.to_socket.identifier == self.inputs['Array'].identifier:
  990. from_socket = link.from_socket
  991. if hasattr(from_socket, "color"):
  992. self.inputs['Array'].color = from_socket.color
  993. self.outputs['Output'].color = from_socket.color
  994. class UtilityArrayLength(Node, MantisUINode):
  995. """Gets The length of an array."""
  996. bl_idname = "UtilityArrayLength"
  997. bl_label = "Array Length"
  998. bl_icon = "NODE"
  999. initialized : bpy.props.BoolProperty(default = False)
  1000. mantis_node_class_name=bl_idname
  1001. def init(self, context):
  1002. s = self.inputs.new("WildcardSocket", "Array", use_multi_input=True)
  1003. s.display_shape = 'SQUARE_DOT'
  1004. self.outputs.new("UnsignedIntSocket", "Length")
  1005. self.initialized = True
  1006. def update(self):
  1007. wildcard_color = (0.0,0.0,0.0,0.0)
  1008. if self.inputs['Array'].is_linked == False:
  1009. self.inputs['Array'].color = wildcard_color
  1010. def insert_link(self, link):
  1011. super().insert_link(link)
  1012. if link.to_socket.identifier == self.inputs['Array'].identifier:
  1013. from_socket = link.from_socket
  1014. if hasattr(from_socket, "color"):
  1015. self.inputs['Array'].color = from_socket.color
  1016. class UtilityCompare(Node, MantisUINode):
  1017. """Compares two inputs and produces a boolean output"""
  1018. bl_idname = "UtilityCompare"
  1019. bl_label = "Compare"
  1020. bl_icon = "NODE"
  1021. initialized : bpy.props.BoolProperty(default = False)
  1022. mantis_node_class_name=bl_idname
  1023. def init(self, context):
  1024. self.init_sockets(CompareSockets)
  1025. self.initialized = True
  1026. def update(self):
  1027. wildcard_color = (0.0,0.0,0.0,0.0)
  1028. if self.inputs['A'].is_linked == False:
  1029. self.inputs['A'].color = wildcard_color
  1030. if self.inputs['B'].is_linked == False:
  1031. self.inputs['B'].color = wildcard_color
  1032. def insert_link(self, link):
  1033. super().insert_link(link)
  1034. if link.to_socket.identifier == self.inputs['A'].identifier:
  1035. self.inputs['A'].color = link.from_socket.color_simple
  1036. if hasattr(link.from_socket, "color"):
  1037. self.inputs['A'].color = link.from_socket.color
  1038. if link.to_socket.identifier == self.inputs['B'].identifier:
  1039. self.inputs['B'].color = link.from_socket.color_simple
  1040. if hasattr(link.from_socket, "color"):
  1041. self.inputs['B'].color = link.from_socket.color
  1042. class UtilityChoose(Node, MantisUINode):
  1043. """Chooses an output"""
  1044. bl_idname = "UtilityChoose"
  1045. bl_label = "Choose"
  1046. bl_icon = "NODE"
  1047. initialized : bpy.props.BoolProperty(default = False)
  1048. mantis_node_class_name=bl_idname
  1049. def init(self, context):
  1050. self.inputs.new("BooleanSocket", "Condition")
  1051. self.inputs.new("WildcardSocket", "A")
  1052. self.inputs.new("WildcardSocket", "B")
  1053. self.outputs.new("WildcardSocket", "Result")
  1054. self.initialized = True
  1055. def display_update(self, parsed_tree, context):
  1056. wildcard_color = (0.0,0.0,0.0,0.0)
  1057. if not self.inputs['A'].is_linked:
  1058. self.inputs['A'].color = wildcard_color
  1059. if not self.inputs['B'].is_linked:
  1060. self.inputs['B'].color = wildcard_color
  1061. self.outputs['Result'].color = wildcard_color
  1062. # if both inputs are the same color, then use that color for the result
  1063. if self.inputs['Condition'].is_linked:
  1064. from .base_definitions import get_signature_from_edited_tree
  1065. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  1066. if nc:
  1067. condition = nc.evaluate_input('Condition')
  1068. else:
  1069. condition = self.inputs['Condition'].default_value
  1070. if condition == True:
  1071. self.outputs['Result'].color = self.inputs['B'].color
  1072. else:
  1073. self.outputs['Result'].color = self.inputs['A'].color
  1074. def insert_link(self, link):
  1075. super().insert_link(link)
  1076. if link.to_socket.identifier == self.inputs['A'].identifier:
  1077. self.inputs['A'].color = link.from_socket.color_simple
  1078. if hasattr(link.from_socket, "color"):
  1079. self.inputs['A'].color = link.from_socket.color
  1080. if link.to_socket.identifier == self.inputs['B'].identifier:
  1081. self.inputs['B'].color = link.from_socket.color_simple
  1082. if hasattr(link.from_socket, "color"):
  1083. self.inputs['B'].color = link.from_socket.color
  1084. class UtilityPrint(Node, MantisUINode):
  1085. """A utility used to print arbitrary values."""
  1086. bl_idname = "UtilityPrint"
  1087. bl_label = "Print"
  1088. bl_icon = "NODE"
  1089. initialized : bpy.props.BoolProperty(default = False)
  1090. mantis_node_class_name=bl_idname
  1091. def init(self, context):
  1092. self.inputs.new("WildcardSocket", "Input")
  1093. self.initialized = True
  1094. # Set up the class property that ties the UI classes to the Mantis classes.
  1095. for cls in TellClasses():
  1096. cls.mantis_node_library='.misc_nodes'
  1097. cls.set_mantis_class()