nodes_generic.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. import bpy
  2. from bpy.types import Node
  3. from .base_definitions import MantisNode
  4. from .utilities import (prRed, prGreen, prPurple, prWhite,
  5. prOrange,
  6. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  7. wrapOrange,)
  8. def TellClasses():
  9. return [ InputFloatNode,
  10. InputIntNode,
  11. InputVectorNode,
  12. InputBooleanNode,
  13. InputBooleanThreeTupleNode,
  14. InputRotationOrderNode,
  15. InputTransformSpaceNode,
  16. InputStringNode,
  17. InputQuaternionNode,
  18. InputQuaternionNodeAA,
  19. InputMatrixNode,
  20. InputLayerMaskNode,
  21. # InputGeometryNode,
  22. InputExistingGeometryObjectNode,
  23. InputExistingGeometryDataNode,
  24. # ComposeMatrixNode,
  25. MetaRigMatrixNode,
  26. UtilityMatrixFromCurve,
  27. UtilityMatricesFromCurve,
  28. # ScaleBoneLengthNode,
  29. UtilityMetaRigNode,
  30. UtilityBonePropertiesNode,
  31. UtilityDriverVariableNode,
  32. UtilityFCurveNode,
  33. UtilityDriverNode,
  34. UtilitySwitchNode,
  35. UtilityKeyframe,
  36. UtilityCombineThreeBoolNode,
  37. UtilityCombineVectorNode,
  38. UtilityCatStringsNode,
  39. UtilityGetBoneLength,
  40. UtilityPointFromBoneMatrix,
  41. UtilitySetBoneLength,
  42. UtilityMatrixSetLocation,
  43. UtilityMatrixGetLocation,
  44. UtilityMatrixFromXForm,
  45. UtilityAxesFromMatrix,
  46. UtilityBoneMatrixHeadTailFlip,
  47. UtilityMatrixTransform,
  48. UtilityTransformationMatrix,
  49. UtilitySetBoneMatrixTail,
  50. UtilityIntToString,
  51. UtilityArrayGet,
  52. #
  53. UtilityCompare,
  54. UtilityChoose,
  55. # for testing
  56. UtilityPrint,
  57. ]
  58. def default_traverse(self,socket):
  59. return None
  60. class InputFloatNode(Node, MantisNode):
  61. '''A node representing inheritance'''
  62. bl_idname = 'InputFloatNode'
  63. bl_label = "Float"
  64. bl_icon = 'NODE'
  65. initialized : bpy.props.BoolProperty(default = False)
  66. def init(self, context):
  67. self.outputs.new('FloatSocket', "Float Input").input = True
  68. self.initialized = True
  69. class InputIntNode(Node, MantisNode):
  70. '''A node representing inheritance'''
  71. bl_idname = 'InputIntNode'
  72. bl_label = "Integer"
  73. bl_icon = 'NODE'
  74. initialized : bpy.props.BoolProperty(default = False)
  75. def init(self, context):
  76. self.outputs.new('IntSocket', "Integer").input = True
  77. self.initialized = True
  78. class InputVectorNode(Node, MantisNode):
  79. '''A node representing inheritance'''
  80. bl_idname = 'InputVectorNode'
  81. bl_label = "Vector"
  82. bl_icon = 'NODE'
  83. initialized : bpy.props.BoolProperty(default = False)
  84. def init(self, context):
  85. self.outputs.new('VectorSocket', "").input = True
  86. self.initialized = True
  87. class InputBooleanNode(Node, MantisNode):
  88. '''A node representing inheritance'''
  89. bl_idname = 'InputBooleanNode'
  90. bl_label = "Boolean"
  91. bl_icon = 'NODE'
  92. initialized : bpy.props.BoolProperty(default = False)
  93. def init(self, context):
  94. self.outputs.new('BooleanSocket', "").input = True
  95. self.initialized = True
  96. class InputBooleanThreeTupleNode(Node, MantisNode):
  97. '''A node representing inheritance'''
  98. bl_idname = 'InputBooleanThreeTupleNode'
  99. bl_label = "Boolean Vector"
  100. bl_icon = 'NODE'
  101. initialized : bpy.props.BoolProperty(default = False)
  102. def init(self, context):
  103. self.outputs.new('BooleanThreeTupleSocket', "")
  104. self.initialized = True
  105. class InputRotationOrderNode(Node, MantisNode):
  106. '''A node representing inheritance'''
  107. bl_idname = 'InputRotationOrderNode'
  108. bl_label = "Rotation Order"
  109. bl_icon = 'NODE'
  110. initialized : bpy.props.BoolProperty(default = False)
  111. def init(self, context):
  112. self.outputs.new('RotationOrderSocket', "").input = True
  113. self.initialized = True
  114. class InputTransformSpaceNode(Node, MantisNode):
  115. '''A node representing inheritance'''
  116. bl_idname = 'InputTransformSpaceNode'
  117. bl_label = "Transform Space"
  118. bl_icon = 'NODE'
  119. initialized : bpy.props.BoolProperty(default = False)
  120. def init(self, context):
  121. self.outputs.new('TransformSpaceSocket', "").input = True
  122. self.initialized = True
  123. class InputStringNode(Node, MantisNode):
  124. '''A node representing inheritance'''
  125. bl_idname = 'InputStringNode'
  126. bl_label = "String"
  127. bl_icon = 'NODE'
  128. initialized : bpy.props.BoolProperty(default = False)
  129. def init(self, context):
  130. self.outputs.new('StringSocket', "").input = True
  131. self.initialized = True
  132. class InputQuaternionNode(Node, MantisNode):
  133. '''A node representing inheritance'''
  134. bl_idname = 'InputQuaternionNode'
  135. bl_label = "Quaternion"
  136. bl_icon = 'NODE'
  137. initialized : bpy.props.BoolProperty(default = False)
  138. def init(self, context):
  139. self.outputs.new('QuaternionSocket', "").input = True
  140. self.initialized = True
  141. class InputQuaternionNodeAA(Node, MantisNode):
  142. '''A node representing inheritance'''
  143. bl_idname = 'InputQuaternionNodeAA'
  144. bl_label = "Axis Angle"
  145. bl_icon = 'NODE'
  146. initialized : bpy.props.BoolProperty(default = False)
  147. def init(self, context):
  148. self.outputs.new('QuaternionSocketAA', "").input = True
  149. self.initialized = True
  150. class InputMatrixNode(Node, MantisNode):
  151. '''A node representing inheritance'''
  152. bl_idname = 'InputMatrixNode'
  153. bl_label = "Matrix"
  154. bl_icon = 'NODE'
  155. first_row : bpy.props.FloatVectorProperty(name="", size=4, default = (1.0, 0.0, 0.0, 0.0,))
  156. second_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 1.0, 0.0, 0.0,))
  157. third_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 1.0, 0.0,))
  158. fourth_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 0.0, 1.0,))
  159. initialized : bpy.props.BoolProperty(default = False)
  160. def set_matrix(self):
  161. return (self.first_row[ 0], self.first_row[ 1], self.first_row[ 2], self.first_row[ 3],
  162. self.second_row[0], self.second_row[1], self.second_row[2], self.second_row[3],
  163. self.third_row[ 0], self.third_row[ 1], self.third_row[ 2], self.third_row[ 3],
  164. self.fourth_row[0], self.fourth_row[1], self.fourth_row[2], self.fourth_row[3],)
  165. def init(self, context):
  166. self.outputs.new('MatrixSocket', "Matrix")
  167. self.initialized = True
  168. def update_node(self, context):
  169. self.outputs["Matrix"].default_value = self.set_matrix()
  170. def draw_buttons(self, context, layout):
  171. # return
  172. layout.prop(self, "first_row")
  173. layout.prop(self, "second_row")
  174. layout.prop(self, "third_row")
  175. layout.prop(self, "fourth_row")
  176. def update(self):
  177. mat_sock = self.outputs[0]
  178. mat_sock.default_value = self.set_matrix()
  179. # TODO: reimplement the nodes beneath here
  180. # from .utilities import QuerySocket, to_mathutils_value
  181. # class ComposeMatrixNode(Node, MantisNode):
  182. # '''A utility node for composing a matrix'''
  183. # bl_idname = 'ComposeMatrixNode'
  184. # bl_label = "Compose Matrix"
  185. # bl_icon = 'NODE'
  186. # def init(self, context):
  187. # self.inputs.new('VectorTranslationSocket', "Translation")
  188. # self.inputs.new('GenericRotationSocket', "Rotation")
  189. # self.inputs.new('VectorScaleSocket', "Scale")
  190. # self.outputs.new('MatrixSocket', "Matrix")
  191. # def update_node(self, context = None):
  192. # from mathutils import Matrix, Euler, Quaternion, Vector
  193. # mat_sock = self.outputs[0]
  194. # rotation = Matrix.Identity(4)
  195. # scale = Matrix.Identity(4)
  196. # translation = Matrix.Identity(4)
  197. # sock = QuerySocket(self.inputs["Rotation"])[0]
  198. # val = to_mathutils_value(sock)
  199. # if (val):
  200. # if (isinstance(val, Vector)):
  201. # val = Euler((val[0], val[1], val[2]), 'XYZ')
  202. # rotation = val.to_matrix().to_4x4()
  203. # sock = QuerySocket(self.inputs["Scale"])[0]
  204. # val = to_mathutils_value(sock)
  205. # if (val):
  206. # if (isinstance(val, Vector)):
  207. # scale = Matrix.Scale(val[0],4,(1.0,0.0,0.0)) @ Matrix.Scale(val[1],4,(0.0,1.0,0.0)) @ Matrix.Scale(val[2],4,(0.0,0.0,1.0))
  208. # sock = QuerySocket(self.inputs["Translation"])[0]
  209. # val = to_mathutils_value(sock)
  210. # if (val):
  211. # if (isinstance(val, Vector)):
  212. # translation = Matrix.Translation((val))
  213. # mat = translation @ rotation @ scale
  214. # mat_sock.default_value = ( mat[0][0], mat[0][1], mat[0][2], mat[0][3],
  215. # mat[1][0], mat[1][1], mat[1][2], mat[1][3],
  216. # mat[2][0], mat[2][1], mat[2][2], mat[2][3],
  217. # mat[3][0], mat[3][1], mat[3][2], mat[3][3], )
  218. class ScaleBoneLengthNode(Node, MantisNode):
  219. '''Scale Bone Length'''
  220. bl_idname = 'ScaleBoneLength'
  221. bl_label = "Scale Bone Length"
  222. bl_icon = 'NODE'
  223. initialized : bpy.props.BoolProperty(default = False)
  224. # === Optional Functions ===
  225. def init(self, context):
  226. self.inputs.new('MatrixSocket', "In Matrix")
  227. self.inputs.new('FloatSocket', "Factor")
  228. self.outputs.new('MatrixSocket', "Out Matrix")
  229. self.initialized = True
  230. class MetaRigMatrixNode(Node, MantisNode):
  231. # Identical to the above, except
  232. '''A node representing a bone's matrix'''
  233. bl_idname = 'MetaRigMatrixNode'
  234. bl_label = "Matrix"
  235. bl_icon = 'NODE'
  236. first_row : bpy.props.FloatVectorProperty(name="", size=4, default = (1.0, 0.0, 0.0, 0.0,))
  237. second_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 1.0, 0.0, 0.0,))
  238. third_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 1.0, 0.0,))
  239. fourth_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 0.0, 1.0,))
  240. initialized : bpy.props.BoolProperty(default = False)
  241. def set_matrix(self):
  242. return (self.first_row[ 0], self.first_row[ 1], self.first_row[ 2], self.first_row[ 3],
  243. self.second_row[0], self.second_row[1], self.second_row[2], self.second_row[3],
  244. self.third_row[ 0], self.third_row[ 1], self.third_row[ 2], self.third_row[ 3],
  245. self.fourth_row[0], self.fourth_row[1], self.fourth_row[2], self.fourth_row[3],)
  246. def init(self, context):
  247. self.outputs.new('MatrixSocket', "Matrix")
  248. self.initialized = True
  249. def traverse(self, context):
  250. from mathutils import Matrix
  251. v = self.outputs[0].default_value
  252. # print( Matrix( ( ( v[ 0], v[ 1], v[ 2], v[ 3],),
  253. # ( v[ 4], v[ 5], v[ 6], v[ 7],),
  254. # ( v[ 8], v[ 9], v[10], v[11],),
  255. # ( v[12], v[13], v[14], v[15],), ) ) )
  256. return None
  257. def update_node(self, context):
  258. self.outputs["Matrix"].default_value = self.set_matrix()
  259. def update(self):
  260. mat_sock = self.outputs[0]
  261. mat_sock.default_value = self.set_matrix()
  262. class UtilityMatrixFromCurve(Node, MantisNode):
  263. """Gets a matrix from a curve."""
  264. bl_idname = "UtilityMatrixFromCurve"
  265. bl_label = "Matrix from Curve"
  266. bl_icon = "NODE"
  267. initialized : bpy.props.BoolProperty(default = False)
  268. def init(self, context):
  269. curv = self.inputs.new("EnumCurveSocket", "Curve")
  270. curv.icon = "OUTLINER_OB_CURVE"
  271. self.inputs.new('IntSocket', 'Total Divisions')
  272. self.inputs.new('IntSocket', 'Matrix Index')
  273. self.outputs.new("MatrixSocket", "Matrix")
  274. self.initialized = True
  275. class UtilityMatricesFromCurve(Node, MantisNode):
  276. """Gets a matrix from a curve."""
  277. bl_idname = "UtilityMatricesFromCurve"
  278. bl_label = "Matrices from Curve"
  279. bl_icon = "NODE"
  280. initialized : bpy.props.BoolProperty(default = False)
  281. def init(self, context):
  282. curv = self.inputs.new("EnumCurveSocket", "Curve")
  283. curv.icon = "OUTLINER_OB_CURVE"
  284. self.inputs.new('IntSocket', 'Total Divisions')
  285. o = self.outputs.new("MatrixSocket", "Matrices")
  286. o.display_shape = 'SQUARE_DOT'
  287. self.initialized = True
  288. class UtilityMetaRigNode(Node, MantisNode):
  289. """Gets a matrix from a meta-rig bone."""
  290. bl_idname = "UtilityMetaRig"
  291. bl_label = "Meta-Rig"
  292. bl_icon = "NODE"
  293. armature:bpy.props.StringProperty()
  294. pose_bone:bpy.props.StringProperty()
  295. initialized : bpy.props.BoolProperty(default = False)
  296. def init(self, context):
  297. armt = self.inputs.new("EnumMetaRigSocket", "Meta-Armature")
  298. bone = self.inputs.new("EnumMetaBoneSocket", "Meta-Bone")
  299. armt.icon = "OUTLINER_OB_ARMATURE"
  300. bone.icon = "BONE_DATA"
  301. bone.hide=True
  302. self.outputs.new("MatrixSocket", "Matrix")
  303. self.initialized = True
  304. def display_update(self, parsed_tree, context):
  305. from .base_definitions import get_signature_from_edited_tree
  306. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  307. if nc:
  308. self.armature= nc.evaluate_input("Meta-Armature")
  309. self.pose_bone= nc.evaluate_input("Meta-Bone")
  310. if not self.armature:
  311. self.inputs["Meta-Bone"].hide=True
  312. else:
  313. self.inputs["Meta-Bone"].hide=False
  314. if self.inputs["Meta-Armature"].is_connected:
  315. self.inputs["Meta-Armature"].search_prop = None
  316. if self.inputs["Meta-Bone"].is_connected:
  317. self.inputs["Meta-Bone"].search_prop = None
  318. class UtilityBonePropertiesNode(Node, MantisNode):
  319. """Provides as sockets strings identifying bone transform properties."""
  320. bl_idname = "UtilityBoneProperties"
  321. bl_label = "Bone Properties"
  322. bl_icon = "NODE"
  323. #bl_width_default = 250
  324. initialized : bpy.props.BoolProperty(default = False)
  325. def init(self, context):
  326. self.outputs.new("ParameterStringSocket", "matrix")
  327. self.outputs.new("ParameterStringSocket", "matrix_local")
  328. self.outputs.new("ParameterStringSocket", "matrix_basis")
  329. self.outputs.new("ParameterStringSocket", "head")
  330. self.outputs.new("ParameterStringSocket", "tail")
  331. self.outputs.new("ParameterStringSocket", "length")
  332. self.outputs.new("ParameterStringSocket", "rotation")
  333. self.outputs.new("ParameterStringSocket", "location")
  334. self.outputs.new("ParameterStringSocket", "scale")
  335. self.initialized = True
  336. for o in self.outputs:
  337. o.text_only = True
  338. class UtilityDriverVariableNode(Node, MantisNode):
  339. """Creates a variable for use in a driver."""
  340. bl_idname = "UtilityDriverVariable"
  341. bl_label = "Driver Variable"
  342. bl_icon = "NODE"
  343. initialized : bpy.props.BoolProperty(default = False)
  344. def init(self, context):
  345. self.inputs.new("EnumDriverVariableType", "Variable Type") # 0
  346. self.inputs.new("ParameterStringSocket", "Property") # 1
  347. self.inputs.new("IntSocket", "Property Index") # 2
  348. self.inputs.new("EnumDriverVariableTransformChannel", "Transform Channel") # 3
  349. self.inputs.new("EnumDriverVariableEvaluationSpace", "Evaluation Space") # 4
  350. self.inputs.new("EnumDriverRotationMode", "Rotation Mode") # 5
  351. self.inputs.new("xFormSocket", "xForm 1") # 6
  352. self.inputs.new("xFormSocket", "xForm 2") # 7
  353. self.outputs.new("DriverVariableSocket", "Driver Variable")
  354. self.initialized = True
  355. # def update_on_socket_change(self, context):
  356. # self.update()
  357. def display_update(self, parsed_tree, context):
  358. from .base_definitions import get_signature_from_edited_tree
  359. if self.inputs["Variable Type"].is_linked:
  360. if context.space_data:
  361. node_tree = context.space_data.path[0].node_tree
  362. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  363. if nc:
  364. driver_type = nc.evaluate_input("Variable Type")
  365. else:
  366. driver_type = self.inputs[0].default_value
  367. if driver_type == 'SINGLE_PROP':
  368. self.inputs[1].hide = False
  369. self.inputs[2].hide = False
  370. self.inputs[3].hide = True
  371. self.inputs[4].hide = False
  372. self.inputs[5].hide = False
  373. self.inputs[6].hide = False
  374. self.inputs[7].hide = True
  375. elif driver_type == 'LOC_DIFF':
  376. self.inputs[1].hide = True
  377. self.inputs[2].hide = True
  378. self.inputs[3].hide = True
  379. self.inputs[4].hide = True
  380. self.inputs[5].hide = True
  381. self.inputs[6].hide = False
  382. self.inputs[7].hide = False
  383. elif driver_type == 'ROTATION_DIFF':
  384. self.inputs[1].hide = True
  385. self.inputs[2].hide = True
  386. self.inputs[3].hide = True
  387. self.inputs[4].hide = True
  388. self.inputs[5].hide = False
  389. self.inputs[6].hide = False
  390. self.inputs[7].hide = False
  391. elif driver_type == 'TRANSFORMS':
  392. self.inputs[1].hide = True
  393. self.inputs[2].hide = True
  394. self.inputs[3].hide = False
  395. self.inputs[4].hide = False
  396. self.inputs[5].hide = False
  397. self.inputs[6].hide = False
  398. self.inputs[7].hide = True
  399. class UtilityFCurveNode(Node, MantisNode):
  400. """Creates an fCurve for use with a driver."""
  401. bl_idname = "UtilityFCurve"
  402. bl_label = "fCurve"
  403. bl_icon = "NODE"
  404. use_kf_nodes : bpy.props.BoolProperty(default=True)
  405. # fake_fcurve_ob : bpy.props.PointerProperty(type=bpy.types.Object)
  406. initialized : bpy.props.BoolProperty(default = False)
  407. def init(self, context):
  408. self.outputs.new("FCurveSocket", "fCurve")
  409. # if not self.fake_fcurve_ob:
  410. # ob = bpy.data.objects.new("fake_ob_"+self.name, None)
  411. # self.fake_fcurve_ob = ob
  412. # ob.animation_data_create()
  413. # ob.animation_data.action = bpy.data.actions.new('fake_action_'+self.name)
  414. # fc = ob.animation_data.action.fcurves.new('location', index=0, action_group='location')
  415. # fc.keyframe_points.add(2)
  416. # kf0 = fc.keyframe_points[0]; kf0.co_ui = (0, 0)
  417. # kf1 = fc.keyframe_points[1]; kf1.co_ui = (1, 1)
  418. # #
  419. # kf0.interpolation = 'BEZIER'
  420. # kf0.handle_left_type = 'AUTO_CLAMPED'
  421. # kf0.handle_right_type = 'AUTO_CLAMPED'
  422. # kf1.interpolation = 'BEZIER'
  423. # kf1.handle_left_type = 'AUTO_CLAMPED'
  424. # kf1.handle_right_type = 'AUTO_CLAMPED'
  425. #
  426. self.initialized = True
  427. def draw_buttons(self, context, layout):
  428. # return
  429. # if self.use_kf_nodes:
  430. # layout.prop(self, "use_kf_nodes", text="[ Use fCurve data ]", toggle=True, invert_checkbox=True)
  431. layout.operator( 'mantis.fcurve_node_add_kf' )
  432. if (len(self.inputs) > 0):
  433. layout.operator( 'mantis.fcurve_node_remove_kf' )
  434. # else:
  435. # layout.prop(self, "use_kf_nodes", text="[ Use Keyframe Nodes ]", toggle=True)
  436. # layout.operator('mantis.edit_fcurve_node')
  437. # THE DIFFICULT part is getting it to show up in the graph editor
  438. # TRY:
  439. # a modal operator that opens the Graph Editor
  440. # and then finishes when it is closed
  441. # it would reveal the object holding the fCurve before
  442. # showing the Graph Editor
  443. # And hide it after closing it.
  444. #
  445. class UtilityDriverNode(Node, MantisNode):
  446. """Represents a Driver relationship"""
  447. bl_idname = "UtilityDriver"
  448. bl_label = "Driver"
  449. bl_icon = "NODE"
  450. initialized : bpy.props.BoolProperty(default = False)
  451. def init(self, context):
  452. self.inputs.new("EnumDriverType", "Driver Type")
  453. self.inputs.new("FCurveSocket", "fCurve")
  454. self.inputs.new("StringSocket", "Expression")
  455. self.outputs.new("DriverSocket", "Driver")
  456. self.initialized = True
  457. def update(self):
  458. return
  459. context = bpy.context
  460. try:
  461. tree = context.space_data.path[0].node_tree
  462. proceed = True
  463. except AttributeError:
  464. proceed = False
  465. if proceed:
  466. from .f_nodegraph import (GetDownstreamXFormNodes, get_node_container)
  467. if (node_container := get_node_container(self, context)[0]):
  468. dType = node_container.evaluate_input("Driver Type")
  469. else:
  470. dType = self.inputs[0].default_value
  471. if dType == 'SCRIPTED':
  472. self.inputs["Expression"].hide = False
  473. else:
  474. self.inputs["Expression"].hide = True
  475. def draw_buttons(self, context, layout):
  476. # return
  477. layout.operator( 'mantis.driver_node_add_variable' )
  478. if (len(self.inputs) > 3):
  479. layout.operator( 'mantis.driver_node_remove_variable' )
  480. class UtilitySwitchNode(Node, MantisNode):
  481. """Represents a switch relationship between one driver property and one or more driven properties."""
  482. bl_idname = "UtilitySwitch"
  483. bl_label = "Switch"
  484. bl_icon = "NODE"
  485. initialized : bpy.props.BoolProperty(default = False)
  486. def init(self, context):
  487. # self.inputs.new("xFormSocket", "xForm")
  488. self.inputs.new("ParameterStringSocket", "Parameter")
  489. self.inputs.new("IntSocket", "Parameter Index")
  490. self.inputs.new("BooleanSocket", "Invert Switch")
  491. self.outputs.new("DriverSocket", "Driver")
  492. self.initialized = True
  493. class UtilityCombineThreeBoolNode(Node, MantisNode):
  494. """Combines three booleans into a three-bool."""
  495. bl_idname = "UtilityCombineThreeBool"
  496. bl_label = "CombineThreeBool"
  497. bl_icon = "NODE"
  498. initialized : bpy.props.BoolProperty(default = False)
  499. def init(self, context):
  500. self.inputs.new("BooleanSocket", "X")
  501. self.inputs.new("BooleanSocket", "Y")
  502. self.inputs.new("BooleanSocket", "Z")
  503. self.outputs.new("BooleanThreeTupleSocket", "Three-Bool")
  504. # this node should eventually just be a Combine Boolean Three-Tuple node
  505. # and the "Driver" output will need to be figured out some other way
  506. self.initialized = True
  507. class UtilityCombineVectorNode(Node, MantisNode):
  508. """Combines three floats into a vector."""
  509. bl_idname = "UtilityCombineVector"
  510. bl_label = "CombineVector"
  511. bl_icon = "NODE"
  512. initialized : bpy.props.BoolProperty(default = False)
  513. def init(self, context):
  514. self.inputs.new("FloatSocket", "X")
  515. self.inputs.new("FloatSocket", "Y")
  516. self.inputs.new("FloatSocket", "Z")
  517. self.outputs.new("VectorSocket", "Vector")
  518. # this node should eventually just be a Combine Boolean Three-Tuple node
  519. # and the "Driver" output will need to be figured out some other way
  520. self.initialized = True
  521. class UtilityCatStringsNode(Node, MantisNode):
  522. """Adds a suffix to a string"""
  523. bl_idname = "UtilityCatStrings"
  524. bl_label = "Concatenate Strings"
  525. bl_icon = "NODE"
  526. initialized : bpy.props.BoolProperty(default = False)
  527. def init(self, context):
  528. self.inputs.new("StringSocket", "String_1")
  529. self.inputs.new("StringSocket", "String_2")
  530. self.outputs.new("StringSocket", "OutputString")
  531. self.initialized = True
  532. class InputLayerMaskNode(Node, MantisNode):
  533. """Represents a layer mask for a bone."""
  534. bl_idname = "InputLayerMaskNode"
  535. bl_label = "Layer Mask"
  536. bl_icon = "NODE"
  537. initialized : bpy.props.BoolProperty(default = False)
  538. def init(self, context):
  539. self.outputs.new("LayerMaskInputSocket", "Layer Mask")
  540. self.initialized = True
  541. class InputExistingGeometryObjectNode(Node, MantisNode):
  542. """Represents an existing geometry object from within the scene."""
  543. bl_idname = "InputExistingGeometryObject"
  544. bl_label = "Existing Object"
  545. bl_icon = "NODE"
  546. initialized : bpy.props.BoolProperty(default = False)
  547. # We want Mantis to import widgets and stuff, so we hold a reference to the object
  548. object_reference : bpy.props.PointerProperty(type=bpy.types.Object,)
  549. def init(self, context):
  550. self.inputs.new("StringSocket", "Name")
  551. self.outputs.new("xFormSocket", "Object")
  552. self.initialized = True
  553. def display_update(self, parsed_tree, context):
  554. from .base_definitions import get_signature_from_edited_tree
  555. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  556. if nc: # this is done here so I don't have to define yet another custom socket.
  557. self.object_reference = bpy.data.objects.get(nc.evaluate_input("Name"))
  558. # TODO: maybe I should hold a data reference here, too.
  559. # but it is complicated by the fact that Mantis does not distinguish b/tw geo types
  560. class InputExistingGeometryDataNode(Node, MantisNode):
  561. """Represents a mesh or curve datablock from the scene."""
  562. bl_idname = "InputExistingGeometryData"
  563. bl_label = "Existing Geometry"
  564. bl_icon = "NODE"
  565. initialized : bpy.props.BoolProperty(default = False)
  566. def init(self, context):
  567. self.inputs.new("StringSocket", "Name")
  568. self.outputs.new("GeometrySocket", "Geometry")
  569. self.initialized = True
  570. class UtilityGetBoneLength(Node, MantisNode):
  571. """Returns the length of the bone from its matrix."""
  572. bl_idname = "UtilityGetBoneLength"
  573. bl_label = "Get Bone Length"
  574. bl_icon = "NODE"
  575. initialized : bpy.props.BoolProperty(default = False)
  576. def init(self, context):
  577. self.inputs.new("MatrixSocket", "Bone Matrix")
  578. self.outputs.new("FloatSocket", "Bone Length")
  579. self.initialized = True
  580. # TODO: make it work with BBones!
  581. class UtilityPointFromBoneMatrix(Node, MantisNode):
  582. """Returns a point representing the location along a bone, given a matrix representing that bone's shape."""
  583. bl_idname = "UtilityPointFromBoneMatrix"
  584. bl_label = "Point from Bone Matrix"
  585. bl_icon = "NODE"
  586. initialized : bpy.props.BoolProperty(default = False)
  587. def init(self, context):
  588. self.inputs.new("MatrixSocket", "Bone Matrix")
  589. self.inputs.new("FloatFactorSocket", "Head/Tail")
  590. self.outputs.new("VectorSocket", "Point")
  591. self.initialized = True
  592. class UtilitySetBoneLength(Node, MantisNode):
  593. """Sets the length of a bone matrix."""
  594. bl_idname = "UtilitySetBoneLength"
  595. bl_label = "Set Bone Matrix Length"
  596. bl_icon = "NODE"
  597. initialized : bpy.props.BoolProperty(default = False)
  598. def init(self, context):
  599. self.inputs.new("MatrixSocket", "Bone Matrix")
  600. self.inputs.new("FloatSocket", "Length")
  601. self.outputs.new("MatrixSocket", "Bone Matrix")
  602. self.initialized = True
  603. class UtilityKeyframe(Node, MantisNode):
  604. """A keyframe for a FCurve"""
  605. bl_idname = "UtilityKeyframe"
  606. bl_label = "KeyFrame"
  607. bl_icon = "NODE"
  608. initialized : bpy.props.BoolProperty(default = False)
  609. def init(self, context):
  610. # x and y
  611. # output is keyframe
  612. # self.inputs.new("EnumKeyframeInterpolationTypeSocket", "Interpolation")
  613. # self.inputs.new("EnumKeyframeBezierHandleType", "Left Handle Type")
  614. # self.inputs.new("EnumKeyframeBezierHandleType", "Right Handle Type")
  615. # self.inputs.new("FloatSocket", "Left Handle Distance")
  616. # self.inputs.new("FloatSocket", "Left Handle Value")
  617. # self.inputs.new("FloatSocket", "Right Handle Frame")
  618. # self.inputs.new("FloatSocket", "Right Handle Value")
  619. self.inputs.new("FloatSocket", "Frame")
  620. self.inputs.new("FloatSocket", "Value")
  621. self.outputs.new("KeyframeSocket", "Keyframe")
  622. # there will eventually be inputs for e.g. key type, key handles, etc.
  623. # right now I am gonna hardcode LINEAR keyframes so I don't have to deal with anything else
  624. # TODO TODO TODO
  625. # def display_update(self, parsed_tree, context):
  626. # if context.space_data:
  627. # nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  628. # if nc.evaluate_input("Interpolation") in ["CONSTANT", "LINEAR"]:
  629. # for inp in self.inputs[1:6]:
  630. # inp.hide = True
  631. # else:
  632. # if nc.evaluate_input("Left Handle Type") in ["FREE", "ALIGNED"]:
  633. # for inp in self.inputs[1:6]:
  634. # inp.hide = False
  635. self.initialized = True
  636. class UtilityBoneMatrixHeadTailFlip(Node, MantisNode):
  637. """Flips a bone matrix so that the head is where the tail was and visa versa."""
  638. bl_idname = "UtilityBoneMatrixHeadTailFlip"
  639. bl_label = "Flip Head/Tail"
  640. bl_icon = "NODE"
  641. initialized : bpy.props.BoolProperty(default = False)
  642. def init(self, context):
  643. self.inputs.new("MatrixSocket", "Bone Matrix")
  644. self.outputs.new("MatrixSocket", "Bone Matrix")
  645. self.initialized = True
  646. class UtilityMatrixTransform(Node, MantisNode):
  647. """Transforms a matrix by another."""
  648. bl_idname = "UtilityMatrixTransform"
  649. bl_label = "Matrix Transform"
  650. bl_icon = "NODE"
  651. initialized : bpy.props.BoolProperty(default = False)
  652. def init(self, context):
  653. self.inputs.new("MatrixSocket", "Matrix 1")
  654. self.inputs.new("MatrixSocket", "Matrix 2")
  655. self.outputs.new("MatrixSocket", "Out Matrix")
  656. self.initialized = True
  657. class UtilityMatrixSetLocation(Node, MantisNode):
  658. """Sets a matrix's location."""
  659. bl_idname = "UtilityMatrixSetLocation"
  660. bl_label = "Set Matrix Location"
  661. bl_icon = "NODE"
  662. initialized : bpy.props.BoolProperty(default = False)
  663. def init(self, context):
  664. self.inputs.new("MatrixSocket", "Matrix")
  665. self.inputs.new("VectorSocket", "Location")
  666. self.outputs.new("MatrixSocket", "Matrix")
  667. self.initialized = True
  668. class UtilityMatrixGetLocation(Node, MantisNode):
  669. """Gets a matrix's location."""
  670. bl_idname = "UtilityMatrixGetLocation"
  671. bl_label = "Get Matrix Location"
  672. bl_icon = "NODE"
  673. initialized : bpy.props.BoolProperty(default = False)
  674. def init(self, context):
  675. self.inputs.new("MatrixSocket", "Matrix")
  676. self.outputs.new("VectorSocket", "Location")
  677. self.initialized = True
  678. class UtilityTransformationMatrix(Node, MantisNode):
  679. """Constructs a matrix representing a transformation"""
  680. bl_idname = "UtilityTransformationMatrix"
  681. bl_label = "Transformation Matrix"
  682. bl_icon = "NODE"
  683. initialized : bpy.props.BoolProperty(default = False)
  684. def init(self, context):
  685. # first input is a transformation type - translation, rotation, or scale
  686. # rotation is an especially annoying feature because it can take multiple types
  687. # so Euler, axis/angle, quaternion, matrix...
  688. # for now I am only going to implement axis-angle
  689. # it should get an axis and a magnitude
  690. # self.inputs.new("MatrixSocket", "Bone Matrix")
  691. self.inputs.new("MatrixTransformOperation", "Operation")
  692. self.inputs.new("VectorSocket", "Vector")
  693. self.inputs.new("FloatSocket", "W")
  694. self.outputs.new("MatrixSocket", "Matrix")
  695. self.initialized = True
  696. # Blender calculates bone roll this way...
  697. # https://projects.blender.org/blender/blender/src/commit/dd209221675ac7b62ce47b7ea42f15cbe34a6035/source/blender/editors/armature/armature_edit.cc#L281
  698. # 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
  699. # in the edit bone. Thus, this node currently does nothing and the xForm node has to handle it by reading back through the tree....
  700. # this will lead to bugs.
  701. # So instead, we need to avoid calculating the roll for now.
  702. # but I want to make that its own node and add roll-recalc to this node, too.
  703. class UtilitySetBoneMatrixTail(Node, MantisNode):
  704. """Constructs a matrix representing a transformation"""
  705. bl_idname = "UtilitySetBoneMatrixTail"
  706. bl_label = "Set Bone Matrix Tail"
  707. bl_icon = "NODE"
  708. initialized : bpy.props.BoolProperty(default = False)
  709. def init(self, context):
  710. self.inputs.new("MatrixSocket", "Matrix")
  711. self.inputs.new("VectorSocket", "Tail Location")
  712. self.outputs.new("MatrixSocket", "Result")
  713. self.initialized = True
  714. class UtilityMatrixFromXForm(Node, MantisNode):
  715. """Returns the matrix of the given xForm node."""
  716. bl_idname = "UtilityMatrixFromXForm"
  717. bl_label = "Matrix of xForm"
  718. bl_icon = "NODE"
  719. initialized : bpy.props.BoolProperty(default = False)
  720. def init(self, context):
  721. self.inputs.new("xFormSocket", "xForm")
  722. self.outputs.new("MatrixSocket", "Matrix")
  723. self.initialized = True
  724. class UtilityAxesFromMatrix(Node, MantisNode):
  725. """Returns the axes of the matrix."""
  726. bl_idname = "UtilityAxesFromMatrix"
  727. bl_label = "Axes of Matrix"
  728. bl_icon = "NODE"
  729. initialized : bpy.props.BoolProperty(default = False)
  730. def init(self, context):
  731. self.inputs.new("MatrixSocket", "Matrix")
  732. self.outputs.new("VectorSocket", "X Axis")
  733. self.outputs.new("VectorSocket", "Y Axis")
  734. self.outputs.new("VectorSocket", "Z Axis")
  735. self.initialized = True
  736. class UtilityIntToString(Node, MantisNode):
  737. """Converts a number to a string"""
  738. bl_idname = "UtilityIntToString"
  739. bl_label = "Number String"
  740. bl_icon = "NODE"
  741. initialized : bpy.props.BoolProperty(default = False)
  742. def init(self, context):
  743. self.inputs.new("IntSocket", "Number")
  744. self.inputs.new("IntSocket", "Zero Padding")
  745. self.outputs.new("StringSocket", "String")
  746. self.initialized = True
  747. class UtilityArrayGet(Node, MantisNode):
  748. """Gets a value from an array at a specified index."""
  749. bl_idname = "UtilityArrayGet"
  750. bl_label = "Array Get"
  751. bl_icon = "NODE"
  752. initialized : bpy.props.BoolProperty(default = False)
  753. def init(self, context):
  754. self.inputs.new('EnumArrayGetOptions', 'OoB Behaviour')
  755. self.inputs.new("IntSocket", "Index")
  756. s = self.inputs.new("WildcardSocket", "Array", use_multi_input=True)
  757. s.display_shape = 'SQUARE_DOT'
  758. self.outputs.new("WildcardSocket", "Output")
  759. self.initialized = True
  760. def update(self):
  761. wildcard_color = (0.0,0.0,0.0,0.0)
  762. if self.inputs['Array'].is_linked == False:
  763. self.inputs['Array'].color = wildcard_color
  764. self.outputs['Output'].color = wildcard_color
  765. def insert_link(self, link):
  766. prGreen(link.from_node.name, link.from_socket.identifier, link.to_node.name, link.to_socket.identifier)
  767. if link.to_socket.identifier == self.inputs['Array'].identifier:
  768. from_socket = link.from_socket
  769. print (from_socket.color)
  770. if hasattr(from_socket, "color"):
  771. self.inputs['Array'].color = from_socket.color
  772. self.outputs['Output'].color = from_socket.color
  773. class UtilityCompare(Node, MantisNode):
  774. """Compares two inputs and produces a boolean output"""
  775. bl_idname = "UtilityCompare"
  776. bl_label = "Compare"
  777. bl_icon = "NODE"
  778. initialized : bpy.props.BoolProperty(default = False)
  779. def init(self, context):
  780. self.inputs.new("WildcardSocket", "A")
  781. self.inputs.new("WildcardSocket", "B")
  782. self.outputs.new("BooleanSocket", "Result")
  783. self.initialized = True
  784. def update(self):
  785. wildcard_color = (0.0,0.0,0.0,0.0)
  786. if self.inputs['A'].is_linked == False:
  787. self.inputs['A'].color = wildcard_color
  788. if self.inputs['B'].is_linked == False:
  789. self.inputs['B'].color = wildcard_color
  790. def insert_link(self, link):
  791. if link.to_socket.identifier == self.inputs['A'].identifier:
  792. self.inputs['A'].color = from_socket.color_simple
  793. if hasattr(from_socket, "color"):
  794. self.inputs['A'].color = from_socket.color
  795. if link.to_socket.identifier == self.inputs['B'].identifier:
  796. self.inputs['B'].color = from_socket.color_simple
  797. if hasattr(from_socket, "color"):
  798. self.inputs['B'].color = from_socket.color
  799. class UtilityChoose(Node, MantisNode):
  800. """Chooses an output"""
  801. bl_idname = "UtilityChoose"
  802. bl_label = "Choose"
  803. bl_icon = "NODE"
  804. initialized : bpy.props.BoolProperty(default = False)
  805. def init(self, context):
  806. self.inputs.new("BooleanSocket", "Condition")
  807. self.inputs.new("WildcardSocket", "A")
  808. self.inputs.new("WildcardSocket", "B")
  809. self.outputs.new("WildcardSocket", "Result")
  810. self.initialized = True
  811. def update(self):
  812. wildcard_color = (0.0,0.0,0.0,0.0)
  813. if self.inputs['A'].is_linked == False:
  814. self.inputs['A'].color = wildcard_color
  815. self.outputs['Result'].color = (1.0,0.0,0.0,0.0) # red for Error
  816. if self.inputs['B'].is_linked == False:
  817. self.inputs['B'].color = wildcard_color
  818. self.outputs['Result'].color = (1.0,0.0,0.0,0.0)
  819. # if both inputs are the same color, then use that color for the result
  820. if self.inputs['A'].is_linked and self.inputs['A'].color == self.inputs['B'].color:
  821. self.outputs['Result'].color = self.inputs['A'].color
  822. #
  823. if ((self.inputs['A'].is_linked and self.inputs['B'].is_linked) and
  824. (self.inputs['A'].links[0].from_socket.bl_idname != self.inputs['B'].links[0].from_socket.bl_idname)):
  825. self.inputs['A'].color = (1.0,0.0,0.0,0.0)
  826. self.inputs['B'].color = (1.0,0.0,0.0,0.0)
  827. self.outputs['Result'].color = (1.0,0.0,0.0,0.0)
  828. def insert_link(self, link):
  829. if link.to_socket.identifier == self.inputs['A'].identifier:
  830. self.inputs['A'].color = from_socket.color_simple
  831. if hasattr(from_socket, "color"):
  832. self.inputs['A'].color = from_socket.color
  833. if link.to_socket.identifier == self.inputs['B'].identifier:
  834. self.inputs['B'].color = from_socket.color_simple
  835. if hasattr(from_socket, "color"):
  836. self.inputs['B'].color = from_socket.color
  837. class UtilityPrint(Node, MantisNode):
  838. """A utility used to print arbitrary values."""
  839. bl_idname = "UtilityPrint"
  840. bl_label = "Print"
  841. bl_icon = "NODE"
  842. initialized : bpy.props.BoolProperty(default = False)
  843. def init(self, context):
  844. self.inputs.new("WildcardSocket", "Input")
  845. self.initialized = True