nodes_generic.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. import bpy
  2. from bpy.types import Node
  3. from .base_definitions import MantisNode
  4. def TellClasses():
  5. return [ InputFloatNode,
  6. InputVectorNode,
  7. InputBooleanNode,
  8. InputBooleanThreeTupleNode,
  9. InputRotationOrderNode,
  10. InputTransformSpaceNode,
  11. InputStringNode,
  12. InputQuaternionNode,
  13. InputQuaternionNodeAA,
  14. InputMatrixNode,
  15. InputLayerMaskNode,
  16. # InputGeometryNode,
  17. InputExistingGeometryObjectNode,
  18. InputExistingGeometryDataNode,
  19. # ComposeMatrixNode,
  20. MetaRigMatrixNode,
  21. # ScaleBoneLengthNode,
  22. UtilityMetaRigNode,
  23. UtilityBonePropertiesNode,
  24. UtilityDriverVariableNode,
  25. UtilityFCurveNode,
  26. UtilityDriverNode,
  27. UtilitySwitchNode,
  28. UtilityCombineThreeBoolNode,
  29. UtilityCombineVectorNode,
  30. UtilityCatStringsNode,
  31. ]
  32. def default_traverse(self,socket):
  33. return None
  34. class InputFloatNode(Node, MantisNode):
  35. '''A node representing inheritance'''
  36. bl_idname = 'InputFloatNode'
  37. bl_label = "Float"
  38. bl_icon = 'NODE'
  39. def init(self, context):
  40. self.outputs.new('FloatSocket', "Float Input").input = True
  41. class InputVectorNode(Node, MantisNode):
  42. '''A node representing inheritance'''
  43. bl_idname = 'InputVectorNode'
  44. bl_label = "Vector"
  45. bl_icon = 'NODE'
  46. def init(self, context):
  47. self.outputs.new('VectorSocket', "").input = True
  48. class InputBooleanNode(Node, MantisNode):
  49. '''A node representing inheritance'''
  50. bl_idname = 'InputBooleanNode'
  51. bl_label = "Boolean"
  52. bl_icon = 'NODE'
  53. def init(self, context):
  54. self.outputs.new('BooleanSocket', "").input = True
  55. class InputBooleanThreeTupleNode(Node, MantisNode):
  56. '''A node representing inheritance'''
  57. bl_idname = 'InputBooleanThreeTupleNode'
  58. bl_label = "Boolean Vector"
  59. bl_icon = 'NODE'
  60. def init(self, context):
  61. self.outputs.new('BooleanThreeTupleSocket', "")
  62. class InputRotationOrderNode(Node, MantisNode):
  63. '''A node representing inheritance'''
  64. bl_idname = 'InputRotationOrderNode'
  65. bl_label = "Rotation Order"
  66. bl_icon = 'NODE'
  67. def init(self, context):
  68. self.outputs.new('RotationOrderSocket', "").input = True
  69. class InputTransformSpaceNode(Node, MantisNode):
  70. '''A node representing inheritance'''
  71. bl_idname = 'InputTransformSpaceNode'
  72. bl_label = "Transform Space"
  73. bl_icon = 'NODE'
  74. def init(self, context):
  75. self.outputs.new('TransformSpaceSocket', "").input = True
  76. class InputStringNode(Node, MantisNode):
  77. '''A node representing inheritance'''
  78. bl_idname = 'InputStringNode'
  79. bl_label = "String"
  80. bl_icon = 'NODE'
  81. def init(self, context):
  82. self.outputs.new('StringSocket', "").input = True
  83. class InputQuaternionNode(Node, MantisNode):
  84. '''A node representing inheritance'''
  85. bl_idname = 'InputQuaternionNode'
  86. bl_label = "Quaternion"
  87. bl_icon = 'NODE'
  88. def init(self, context):
  89. self.outputs.new('QuaternionSocket', "").input = True
  90. class InputQuaternionNodeAA(Node, MantisNode):
  91. '''A node representing inheritance'''
  92. bl_idname = 'InputQuaternionNodeAA'
  93. bl_label = "Axis Angle"
  94. bl_icon = 'NODE'
  95. def init(self, context):
  96. self.outputs.new('QuaternionSocketAA', "").input = True
  97. class InputMatrixNode(Node, MantisNode):
  98. '''A node representing inheritance'''
  99. bl_idname = 'InputMatrixNode'
  100. bl_label = "Matrix"
  101. bl_icon = 'NODE'
  102. first_row : bpy.props.FloatVectorProperty(name="", size=4, default = (1.0, 0.0, 0.0, 0.0,))
  103. second_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 1.0, 0.0, 0.0,))
  104. third_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 1.0, 0.0,))
  105. fourth_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 0.0, 1.0,))
  106. def set_matrix(self):
  107. return (self.first_row[ 0], self.first_row[ 1], self.first_row[ 2], self.first_row[ 3],
  108. self.second_row[0], self.second_row[1], self.second_row[2], self.second_row[3],
  109. self.third_row[ 0], self.third_row[ 1], self.third_row[ 2], self.third_row[ 3],
  110. self.fourth_row[0], self.fourth_row[1], self.fourth_row[2], self.fourth_row[3],)
  111. def init(self, context):
  112. self.outputs.new('MatrixSocket', "Matrix")
  113. def update_node(self, context):
  114. self.outputs["Matrix"].default_value = self.set_matrix()
  115. def draw_buttons(self, context, layout):
  116. layout.prop(self, "first_row")
  117. layout.prop(self, "second_row")
  118. layout.prop(self, "third_row")
  119. layout.prop(self, "fourth_row")
  120. def update(self):
  121. mat_sock = self.outputs[0]
  122. mat_sock.default_value = self.set_matrix()
  123. # TODO: reimplement the nodes beneath here
  124. # from .utilities import QuerySocket, to_mathutils_value
  125. # class ComposeMatrixNode(Node, MantisNode):
  126. # '''A utility node for composing a matrix'''
  127. # bl_idname = 'ComposeMatrixNode'
  128. # bl_label = "Compose Matrix"
  129. # bl_icon = 'NODE'
  130. # def init(self, context):
  131. # self.inputs.new('VectorTranslationSocket', "Translation")
  132. # self.inputs.new('GenericRotationSocket', "Rotation")
  133. # self.inputs.new('VectorScaleSocket', "Scale")
  134. # self.outputs.new('MatrixSocket', "Matrix")
  135. # def update_node(self, context = None):
  136. # from mathutils import Matrix, Euler, Quaternion, Vector
  137. # mat_sock = self.outputs[0]
  138. # rotation = Matrix.Identity(4)
  139. # scale = Matrix.Identity(4)
  140. # translation = Matrix.Identity(4)
  141. # sock = QuerySocket(self.inputs["Rotation"])[0]
  142. # val = to_mathutils_value(sock)
  143. # if (val):
  144. # if (isinstance(val, Vector)):
  145. # val = Euler((val[0], val[1], val[2]), 'XYZ')
  146. # rotation = val.to_matrix().to_4x4()
  147. # sock = QuerySocket(self.inputs["Scale"])[0]
  148. # val = to_mathutils_value(sock)
  149. # if (val):
  150. # if (isinstance(val, Vector)):
  151. # 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))
  152. # sock = QuerySocket(self.inputs["Translation"])[0]
  153. # val = to_mathutils_value(sock)
  154. # if (val):
  155. # if (isinstance(val, Vector)):
  156. # translation = Matrix.Translation((val))
  157. # mat = translation @ rotation @ scale
  158. # mat_sock.default_value = ( mat[0][0], mat[0][1], mat[0][2], mat[0][3],
  159. # mat[1][0], mat[1][1], mat[1][2], mat[1][3],
  160. # mat[2][0], mat[2][1], mat[2][2], mat[2][3],
  161. # mat[3][0], mat[3][1], mat[3][2], mat[3][3], )
  162. class ScaleBoneLengthNode(Node, MantisNode):
  163. '''Scale Bone Length'''
  164. bl_idname = 'ScaleBoneLength'
  165. bl_label = "Scale Bone Length"
  166. bl_icon = 'NODE'
  167. # === Optional Functions ===
  168. def init(self, context):
  169. self.inputs.new('MatrixSocket', "In Matrix")
  170. self.inputs.new('FloatSocket', "Factor")
  171. self.outputs.new('MatrixSocket', "Out Matrix")
  172. class MetaRigMatrixNode(Node, MantisNode):
  173. # Identical to the above, except
  174. '''A node representing a bone's matrix'''
  175. bl_idname = 'MetaRigMatrixNode'
  176. bl_label = "Matrix"
  177. bl_icon = 'NODE'
  178. first_row : bpy.props.FloatVectorProperty(name="", size=4, default = (1.0, 0.0, 0.0, 0.0,))
  179. second_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 1.0, 0.0, 0.0,))
  180. third_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 1.0, 0.0,))
  181. fourth_row : bpy.props.FloatVectorProperty(name="", size=4, default = (0.0, 0.0, 0.0, 1.0,))
  182. def set_matrix(self):
  183. return (self.first_row[ 0], self.first_row[ 1], self.first_row[ 2], self.first_row[ 3],
  184. self.second_row[0], self.second_row[1], self.second_row[2], self.second_row[3],
  185. self.third_row[ 0], self.third_row[ 1], self.third_row[ 2], self.third_row[ 3],
  186. self.fourth_row[0], self.fourth_row[1], self.fourth_row[2], self.fourth_row[3],)
  187. def init(self, context):
  188. self.outputs.new('MatrixSocket', "Matrix")
  189. def traverse(self, context):
  190. from mathutils import Matrix
  191. v = self.outputs[0].default_value
  192. # print( Matrix( ( ( v[ 0], v[ 1], v[ 2], v[ 3],),
  193. # ( v[ 4], v[ 5], v[ 6], v[ 7],),
  194. # ( v[ 8], v[ 9], v[10], v[11],),
  195. # ( v[12], v[13], v[14], v[15],), ) ) )
  196. return None
  197. def update_node(self, context):
  198. self.outputs["Matrix"].default_value = self.set_matrix()
  199. def update(self):
  200. mat_sock = self.outputs[0]
  201. mat_sock.default_value = self.set_matrix()
  202. class UtilityMetaRigNode(Node, MantisNode):
  203. """Gets a matrix from a meta-rig bone."""
  204. bl_idname = "UtilityMetaRig"
  205. bl_label = "Meta-Rig"
  206. bl_icon = "NODE"
  207. armature:bpy.props.StringProperty()
  208. pose_bone:bpy.props.StringProperty()
  209. def init(self, context):
  210. armt = self.inputs.new("EnumMetaRigSocket", "Meta-Armature")
  211. bone = self.inputs.new("EnumMetaBoneSocket", "Meta-Bone")
  212. armt.icon = "OUTLINER_OB_ARMATURE"
  213. bone.icon = "BONE_DATA"
  214. bone.hide=True
  215. self.outputs.new("MatrixSocket", "Matrix")
  216. def display_update(self, parsed_tree, context):
  217. from .base_definitions import get_signature_from_edited_tree
  218. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  219. if nc:
  220. self.armature= nc.evaluate_input("Meta-Armature")
  221. self.pose_bone= nc.evaluate_input("Meta-Bone")
  222. if not self.armature:
  223. self.inputs["Meta-Bone"].hide=True
  224. else:
  225. self.inputs["Meta-Bone"].hide=False
  226. class UtilityBonePropertiesNode(Node, MantisNode):
  227. """Provides as sockets strings identifying bone transform properties."""
  228. bl_idname = "UtilityBoneProperties"
  229. bl_label = "Bone Properties"
  230. bl_icon = "NODE"
  231. #bl_width_default = 250
  232. def init(self, context):
  233. self.outputs.new("ParameterStringSocket", "matrix")
  234. self.outputs.new("ParameterStringSocket", "matrix_local")
  235. self.outputs.new("ParameterStringSocket", "matrix_basis")
  236. self.outputs.new("ParameterStringSocket", "head")
  237. self.outputs.new("ParameterStringSocket", "tail")
  238. self.outputs.new("ParameterStringSocket", "length")
  239. self.outputs.new("ParameterStringSocket", "rotation")
  240. self.outputs.new("ParameterStringSocket", "location")
  241. self.outputs.new("ParameterStringSocket", "scale")
  242. for o in self.outputs:
  243. o.text_only = True
  244. class UtilityDriverVariableNode(Node, MantisNode):
  245. """Creates a variable for use in a driver."""
  246. bl_idname = "UtilityDriverVariable"
  247. bl_label = "Driver Variable"
  248. bl_icon = "NODE"
  249. def init(self, context):
  250. self.inputs.new("EnumDriverVariableType", "Variable Type")
  251. self.inputs.new("ParameterStringSocket", "Property")
  252. self.inputs.new("IntSocket", "Property Index")
  253. self.inputs.new("EnumDriverVariableEvaluationSpace", "Evaluation Space")
  254. self.inputs.new("EnumDriverRotationMode", "Rotation Mode")
  255. self.inputs.new("xFormSocket", "xForm 1")
  256. self.inputs.new("xFormSocket", "xForm 2")
  257. self.outputs.new("DriverVariableSocket", "Driver Variable")
  258. def update_on_socket_change(self, context):
  259. self.update()
  260. def display_update(self, parsed_tree, context):
  261. from .base_definitions import get_signature_from_edited_tree
  262. if context.space_data:
  263. node_tree = context.space_data.path[0].node_tree
  264. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  265. if nc:
  266. driver_type = nc.evaluate_input("Variable Type")
  267. if driver_type == 'SINGLE_PROP':
  268. self.inputs[0].hide = False
  269. self.inputs[1].hide = False
  270. self.inputs[2].hide = False
  271. self.inputs[3].hide = False
  272. self.inputs[4].hide = False
  273. self.inputs[5].hide = False
  274. self.inputs[6].hide = True
  275. elif driver_type == 'LOC_DIFF':
  276. self.inputs[0].hide = False
  277. self.inputs[1].hide = True
  278. self.inputs[2].hide = True
  279. self.inputs[3].hide = True
  280. self.inputs[4].hide = True
  281. self.inputs[5].hide = False
  282. self.inputs[6].hide = False
  283. elif driver_type == 'ROTATION_DIFF':
  284. self.inputs[0].hide = False
  285. self.inputs[1].hide = True
  286. self.inputs[2].hide = True
  287. self.inputs[3].hide = True
  288. self.inputs[4].hide = False
  289. self.inputs[5].hide = False
  290. self.inputs[6].hide = False
  291. class UtilityFCurveNode(Node, MantisNode):
  292. """Creates an fCurve for use with a driver."""
  293. bl_idname = "UtilityFCurve"
  294. bl_label = "fCurve"
  295. bl_icon = "NODE"
  296. use_kf_nodes : bpy.props.BoolProperty(default=False)
  297. fake_fcurve_ob : bpy.props.PointerProperty(type=bpy.types.Object)
  298. def init(self, context):
  299. self.outputs.new("FCurveSocket", "fCurve")
  300. if not self.fake_fcurve_ob:
  301. ob = bpy.data.objects.new("fake_ob_"+self.name, None)
  302. self.fake_fcurve_ob = ob
  303. ob.animation_data_create()
  304. ob.animation_data.action = bpy.data.actions.new('fake_action_'+self.name)
  305. fc = ob.animation_data.action.fcurves.new('location', index=0, action_group='location')
  306. fc.keyframe_points.add(2)
  307. kf0 = fc.keyframe_points[0]; kf0.co_ui = (0, 0)
  308. kf1 = fc.keyframe_points[1]; kf1.co_ui = (1, 1)
  309. #
  310. kf0.interpolation = 'BEZIER'
  311. kf0.handle_left_type = 'AUTO_CLAMPED'
  312. kf0.handle_right_type = 'AUTO_CLAMPED'
  313. kf1.interpolation = 'BEZIER'
  314. kf1.handle_left_type = 'AUTO_CLAMPED'
  315. kf1.handle_right_type = 'AUTO_CLAMPED'
  316. #
  317. def draw_buttons(self, context, layout):
  318. if self.use_kf_nodes:
  319. layout.prop(self, "use_kf_nodes", text="[ Use fCurve data ]", toggle=True, invert_checkbox=True)
  320. layout.operator( 'mantis.fcurve_node_add_kf' )
  321. if (len(self.inputs) > 0):
  322. layout.operator( 'mantis.fcurve_node_remove_kf' )
  323. else:
  324. layout.prop(self, "use_kf_nodes", text="[ Use Keyframe Nodes ]", toggle=True)
  325. layout.operator('mantis.edit_fcurve_node')
  326. # THE DIFFICULT part is getting it to show up in the graph editor
  327. # TRY:
  328. # a modal operator that opens the Graph Editor
  329. # and then finishes when it is closed
  330. # it would reveal the object holding the fCurve before
  331. # showing the Graph Editor
  332. # And hide it after closing it.
  333. #
  334. class UtilityDriverNode(Node, MantisNode):
  335. """Represents a Driver relationship"""
  336. bl_idname = "UtilityDriver"
  337. bl_label = "Driver"
  338. bl_icon = "NODE"
  339. def init(self, context):
  340. self.inputs.new("EnumDriverType", "Driver Type")
  341. self.inputs.new("FCurveSocket", "fCurve")
  342. self.inputs.new("StringSocket", "Expression")
  343. self.outputs.new("DriverSocket", "Driver")
  344. def update(self):
  345. return
  346. context = bpy.context
  347. try:
  348. tree = context.space_data.path[0].node_tree
  349. proceed = True
  350. except AttributeError:
  351. proceed = False
  352. if proceed:
  353. from .f_nodegraph import (GetDownstreamXFormNodes, get_node_container)
  354. if (node_container := get_node_container(self, context)[0]):
  355. dType = node_container.evaluate_input("Driver Type")
  356. else:
  357. dType = self.inputs[0].default_value
  358. if dType == 'SCRIPTED':
  359. self.inputs["Expression"].hide = False
  360. else:
  361. self.inputs["Expression"].hide = True
  362. def draw_buttons(self, context, layout):
  363. layout.operator( 'mantis.driver_node_add_variable' )
  364. if (len(self.inputs) > 2):
  365. layout.operator( 'mantis.driver_node_remove_variable' )
  366. class UtilitySwitchNode(Node, MantisNode):
  367. """Represents a switch relationship between one driver property and one or more driven properties."""
  368. bl_idname = "UtilitySwitch"
  369. bl_label = "Switch"
  370. bl_icon = "NODE"
  371. def init(self, context):
  372. self.inputs.new("xFormSocket", "xForm")
  373. self.inputs.new("ParameterStringSocket", "Parameter")
  374. self.inputs.new("IntSocket", "Parameter Index")
  375. self.inputs.new("BooleanSocket", "Invert Switch")
  376. self.outputs.new("DriverSocket", "Driver")
  377. class UtilityCombineThreeBoolNode(Node, MantisNode):
  378. """Combines three booleans into a three-bool."""
  379. bl_idname = "UtilityCombineThreeBool"
  380. bl_label = "CombineThreeBool"
  381. bl_icon = "NODE"
  382. def init(self, context):
  383. self.inputs.new("BooleanSocket", "X")
  384. self.inputs.new("BooleanSocket", "Y")
  385. self.inputs.new("BooleanSocket", "Z")
  386. self.outputs.new("BooleanThreeTupleSocket", "Three-Bool")
  387. # this node should eventually just be a Combine Boolean Three-Tuple node
  388. # and the "Driver" output will need to be figured out some other way
  389. class UtilityCombineVectorNode(Node, MantisNode):
  390. """Combines three floats into a vector."""
  391. bl_idname = "UtilityCombineVector"
  392. bl_label = "CombineVector"
  393. bl_icon = "NODE"
  394. def init(self, context):
  395. self.inputs.new("FloatSocket", "X")
  396. self.inputs.new("FloatSocket", "Y")
  397. self.inputs.new("FloatSocket", "Z")
  398. self.outputs.new("VectorSocket", "Vector")
  399. # this node should eventually just be a Combine Boolean Three-Tuple node
  400. # and the "Driver" output will need to be figured out some other way
  401. class UtilityCatStringsNode(Node, MantisNode):
  402. """Adds a suffix to a string"""
  403. bl_idname = "UtilityCatStrings"
  404. bl_label = "Concatenate Strings"
  405. bl_icon = "NODE"
  406. def init(self, context):
  407. self.inputs.new("StringSocket", "String_1")
  408. self.inputs.new("StringSocket", "String_2")
  409. self.outputs.new("StringSocket", "OutputString")
  410. class InputLayerMaskNode(Node, MantisNode):
  411. """Represents a layer mask for a bone."""
  412. bl_idname = "InputLayerMaskNode"
  413. bl_label = "Layer Mask"
  414. bl_icon = "NODE"
  415. def init(self, context):
  416. self.outputs.new("LayerMaskInputSocket", "Layer Mask")
  417. class InputExistingGeometryObjectNode(Node, MantisNode):
  418. """Represents an existing geometry object from within the scene."""
  419. bl_idname = "InputExistingGeometryObject"
  420. bl_label = "Existing Object"
  421. bl_icon = "NODE"
  422. def init(self, context):
  423. self.inputs.new("StringSocket", "Name")
  424. self.outputs.new("xFormSocket", "Object")
  425. class InputExistingGeometryDataNode(Node, MantisNode):
  426. """Represents a mesh or curve datablock from the scene."""
  427. bl_idname = "InputExistingGeometryData"
  428. bl_label = "Existing Geometry"
  429. bl_icon = "NODE"
  430. def init(self, context):
  431. self.inputs.new("StringSocket", "Name")
  432. self.outputs.new("GeometrySocket", "Geometry")