xForm_nodes_ui.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import bpy
  2. from .base_definitions import xFormNode
  3. from bpy.types import Node
  4. from .utilities import (prRed, prGreen, prPurple, prWhite,
  5. prOrange,
  6. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  7. wrapOrange,)
  8. from .base_definitions import get_signature_from_edited_tree
  9. from .xForm_socket_templates import *
  10. def TellClasses():
  11. return [
  12. # xFormNullNode,
  13. xFormBoneNode,
  14. xFormArmatureNode,
  15. xFormGeometryObjectNode,
  16. xFormObjectInstance,
  17. xFormCurvePin,
  18. ]
  19. def default_traverse(self, socket):
  20. if (socket == self.outputs["xForm Out"]):
  21. return self.inputs["Relationship"]
  22. if (socket == self.inputs["Relationship"]):
  23. return self.outputs["xForm Out"]
  24. return None
  25. # Representing an Empty or non-armature-Object
  26. # class xFormNullNode(Node, xFormNode):
  27. # '''A node representing a Null node'''
  28. # bl_idname = 'xFormNullNode'
  29. # bl_label = "Null"
  30. # bl_icon = 'EMPTY_AXIS'
  31. # # === Optional Functions ===
  32. # def init(self, context):
  33. # self.inputs.new('StringSocket', "Name")
  34. # self.inputs.new('RelationshipSocket', "Relationship")
  35. # self.inputs.new('RotationOrderSocket', "Rotation Order")
  36. # self.inputs.new('MatrixSocket', "Matrix")
  37. # self.outputs.new('xFormSocket', "xForm Out")
  38. def check_if_connected(start, end, line):
  39. started=False
  40. for path_mantis_node in line:
  41. prWhite(" ", path_mantis_node.signature)
  42. if path_mantis_node.signature == start.signature:
  43. started = True
  44. elif path_mantis_node.signature == end.signature:
  45. break
  46. if started:
  47. if path_mantis_node.inputs.get("Connected"):
  48. if path_mantis_node.evaluate_input("Connected") == False:
  49. return False
  50. else:
  51. return False
  52. return True
  53. def main_draw_label(self): # this will prefer a user-set label, or return the evaluated name
  54. if self.label:
  55. return self.label
  56. if self.inputs['Name'].display_text:
  57. return self.inputs['Name'].display_text
  58. return self.name
  59. def custom_prop_display_update(self, mantis_node = None):
  60. custom_props = {}
  61. type_map = {
  62. 'BOOL' : 'ParameterBoolSocket',
  63. 'INT' : 'ParameterIntSocket',
  64. 'FLOAT' : 'ParameterFloatSocket',
  65. 'VECTOR' : 'ParameterVectorSocket',
  66. 'STRING' : 'ParameterStringSocket',
  67. }
  68. cant_read=0
  69. if not mantis_node:
  70. links = [link for link in self.inputs['Custom Properties'].links]
  71. links.sort(key=lambda a : -a.multi_input_sort_id)
  72. for link in links:
  73. if link.from_node.bl_idname == 'UtilityCustomProperty':
  74. if (link.from_node.inputs['Name'].is_linked or
  75. link.from_node.inputs['Type'].is_linked):
  76. cant_read+=1 # we can't eval this without the Mantis data
  77. continue # but we need to count it
  78. else:
  79. custom_props[link.from_node.inputs['Name'].default_value] = \
  80. type_map[link.from_node.inputs['Type'].default_value]
  81. else:
  82. cant_read+=1
  83. for name, type in custom_props.items():
  84. if self.outputs.get(name): continue # already there
  85. else:
  86. self.outputs.new(type, name)
  87. else:
  88. from .misc_nodes import UtilityCustomProperty
  89. from .mantis_dataclasses import custom_prop_template
  90. mantis_node.inputs['Custom Properties'].flush_links() # clean/sort the links
  91. if len(mantis_node.inputs['Custom Properties'].links) != len(self.inputs['Custom Properties'].links):
  92. return # there is a problem somewhere
  93. for i, link in enumerate(mantis_node.inputs['Custom Properties'].links):
  94. from .node_common import trace_single_line
  95. node_line, _last_socket = trace_single_line(mantis_node, 'Custom Properties', i)
  96. if not node_line[-1].prepared:
  97. return
  98. custom_prop_data = mantis_node.evaluate_input('Custom Property', i)
  99. if isinstance(node_line[-1], UtilityCustomProperty) and \
  100. not isinstance(custom_prop_data, custom_prop_template):
  101. return
  102. elif not isinstance(custom_prop_data, custom_prop_template):
  103. continue # wrong connection
  104. name = custom_prop_data.name
  105. type = custom_prop_data.prop_type
  106. custom_props[name] = type_map[type]
  107. from .utilities import get_socket_maps, do_relink
  108. _socket_map_in, socket_map_out = get_socket_maps(self)
  109. for name, type in custom_props.items():
  110. if socket_map_out.get(name) is None:
  111. socket_map_out[name] = None # initialize the new ones
  112. # remove and replace sockets
  113. if cant_read == 0:
  114. remove_me = self.outputs[1:]
  115. while remove_me:
  116. remove_socket = remove_me.pop()
  117. self.outputs.remove(remove_socket)
  118. for name, type in custom_props.items():
  119. socket = self.outputs.new(type, name)
  120. do_relink(self, socket, socket_map_out, in_out='OUTPUT')
  121. # I had chat gpt flip these so they may be a little innacurate
  122. # always visible
  123. main_names = {
  124. "Name":'StringSocket',
  125. "Rotation Order":'RotationOrderSocket',
  126. "Relationship":'RelationshipSocket',
  127. "Matrix":'MatrixSocket',}
  128. # IK SETTINGS
  129. ik_names = {
  130. "IK Stretch":'FloatFactorSocket',
  131. "Lock IK":'BooleanThreeTupleSocket',
  132. "IK Stiffness":'NodeSocketVector',
  133. "Limit IK":'BooleanThreeTupleSocket',
  134. "X Min":'NodeSocketFloatAngle',
  135. "X Max":'NodeSocketFloatAngle',
  136. "Y Min":'NodeSocketFloatAngle',
  137. "Y Max":'NodeSocketFloatAngle',
  138. "Z Min":'NodeSocketFloatAngle',
  139. "Z Max":'NodeSocketFloatAngle',
  140. }
  141. #display settings
  142. display_names = {
  143. "Bone Collection":'BoneCollectionSocket',
  144. "Custom Object":'xFormSocket',
  145. "Custom Object xForm Override":'xFormSocket',
  146. "Custom Object Scale to Bone Length":'BooleanSocket',
  147. "Custom Object Wireframe":'BooleanSocket',
  148. "Custom Object Scale":'VectorScaleSocket',
  149. "Custom Object Translation":'VectorSocket',
  150. "Custom Object Rotation":'VectorEulerSocket',
  151. "Color":'ColorSetSocket',
  152. "Inherit Color":'BooleanSocket',
  153. }
  154. # deform_names
  155. deform_names = {
  156. "Deform":'BooleanSocket',
  157. "Envelope Distance":'FloatPositiveSocket',
  158. "Envelope Weight":'FloatFactorSocket',
  159. "Envelope Multiply":'BooleanSocket',
  160. "Envelope Head Radius":'FloatPositiveSocket',
  161. "Envelope Tail Radius":'FloatPositiveSocket',
  162. }
  163. bbone_names = {
  164. "BBone Segments":"IntSocket", # BONE
  165. "BBone X Size":"FloatSocket", # BONE
  166. "BBone Z Size":"FloatSocket", # BONE
  167. # "bbone_mapping_mode":"StringSocket", <== BONE
  168. "BBone HQ Deformation":"BooleanSocket", # BONE bbone_mapping_mode
  169. "BBone X Curve-In":"FloatSocket", # BONE AND POSE
  170. "BBone Z Curve-In":"FloatSocket", # BONE AND POSE
  171. "BBone X Curve-Out":"FloatSocket", # BONE AND POSE
  172. "BBone Z Curve-Out":"FloatSocket", # BONE AND POSE
  173. "BBone Roll-In":"FloatSocket", # BONE AND POSE
  174. "BBone Roll-Out":"FloatSocket", # BONE AND POSE
  175. "BBone Inherit End Roll":"BooleanSocket", # BONE
  176. "BBone Scale-In":"VectorSocket", # BONE AND POSE
  177. "BBone Scale-Out":"VectorSocket", # BONE AND POSE
  178. "BBone Ease-In":"FloatSocket", # BONE AND POSE
  179. "BBone Ease-Out":"FloatSocket", # BONE AND POSE
  180. "BBone Easing":"BooleanSocket", # BONE
  181. "BBone Start Handle Type":"EnumBBoneHandleType", # BONE
  182. "BBone Custom Start Handle":"StringSocket", # BONE
  183. "BBone Start Handle Scale":"BooleanThreeTupleSocket", # BONE
  184. "BBone Start Handle Ease":"BooleanSocket", # BONE
  185. "BBone End Handle Type":"EnumBBoneHandleType", # BONE
  186. "BBone Custom End Handle":"StringSocket", # BONE
  187. "BBone End Handle Scale":"BooleanThreeTupleSocket", # BONE
  188. "BBone End Handle Ease":"BooleanSocket", # BONE
  189. }
  190. other_names = {
  191. "Lock Location":'BooleanThreeTupleSocket',
  192. "Lock Rotation":'BooleanThreeTupleSocket',
  193. "Lock Scale":'BooleanThreeTupleSocket',
  194. "Hide":'HideSocket',
  195. }
  196. from mathutils import Color
  197. xFormColor = Color((0.093172, 0.047735, 0.028036)).from_scene_linear_to_srgb()
  198. class xFormBoneNode(Node, xFormNode):
  199. '''A node representing a Bone'''
  200. bl_idname = 'xFormBoneNode'
  201. bl_label = "Bone"
  202. bl_icon = 'BONE_DATA'
  203. display_ik_settings : bpy.props.BoolProperty(default=False)
  204. display_vp_settings : bpy.props.BoolProperty(default=False)
  205. display_def_settings : bpy.props.BoolProperty(default=False)
  206. display_bb_settings : bpy.props.BoolProperty(default=False)
  207. socket_count : bpy.props.IntProperty()
  208. initialized : bpy.props.BoolProperty(default = False)
  209. mantis_node_class_name=bl_idname[:-4]
  210. def init(self, context):
  211. for name, sock_type in main_names.items():
  212. self.inputs.new(sock_type, name)
  213. for name, sock_type in ik_names.items():
  214. s = self.inputs.new(sock_type, name)
  215. s.hide = True
  216. for name, sock_type in display_names.items():
  217. if name == 'Bone Collection': # HACK because I am not using Socket Templates yet
  218. s = self.inputs.new(sock_type, name, use_multi_input=True)
  219. else:
  220. s = self.inputs.new(sock_type, name)
  221. if s.name in ['Custom Object', 'Bone Collection']:
  222. continue
  223. s.hide = True
  224. for name, sock_type in deform_names.items():
  225. s = self.inputs.new(sock_type, name)
  226. if s.name == 'Deform':
  227. continue
  228. s.hide = True
  229. for name, sock_type in bbone_names.items():
  230. s = self.inputs.new(sock_type, name)
  231. if s.name == "BBone Segments":
  232. continue
  233. s.hide = True
  234. for name, sock_type in other_names.items():
  235. self.inputs.new(sock_type, name)
  236. # could probably simplify this further with iter_tools.chain() but meh
  237. self.inputs.new("CustomPropSocket", "Custom Properties", use_multi_input=True)
  238. self.socket_count = len(self.inputs)
  239. #
  240. self.outputs.new('xFormSocket', "xForm Out")
  241. # set up some defaults...
  242. self.inputs['Rotation Order'].default_value = "XYZ"
  243. self.inputs['Lock Location'].default_value[0] = True
  244. self.inputs['Lock Location'].default_value[1] = True
  245. self.inputs['Lock Location'].default_value[2] = True
  246. self.inputs['Lock Rotation'].default_value[0] = True
  247. self.inputs['Lock Rotation'].default_value[1] = True
  248. self.inputs['Lock Rotation'].default_value[2] = True
  249. self.inputs['Lock Scale'].default_value[0] = True
  250. self.inputs['Lock Scale'].default_value[1] = True
  251. self.inputs['Lock Scale'].default_value[2] = True
  252. self.inputs['Inherit Color'].default_value = True
  253. # color
  254. self.use_custom_color = True
  255. self.color = xFormColor
  256. #
  257. self.initialized=True
  258. def draw_label(self): # this will prefer a user-set label, or return the evaluated name
  259. return main_draw_label(self)
  260. def display_update(self, parsed_tree, context):
  261. # let's setup the outputs for the custom properties:
  262. # we'll start by trying to do it without the special mantis data
  263. if self.id_data.mantis_version[1] < 13: return #or the old custom properties will get wiped.
  264. custom_prop_display_update(self)
  265. if context.space_data:
  266. mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
  267. if mantis_node and mantis_node.prepared:
  268. custom_prop_display_update(self, mantis_node)
  269. self.display_ik_settings = False
  270. if mantis_node and (pb := mantis_node.bGetObject(mode='POSE')):
  271. self.display_ik_settings = pb.is_in_ik_chain
  272. self.inputs['Name'].display_text = ""
  273. if mantis_node and mantis_node.prepared:
  274. try:
  275. self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
  276. self.display_vp_settings = mantis_node.inputs["Custom Object"].is_connected
  277. self.display_def_settings = mantis_node.evaluate_input("Deform")
  278. self.display_bb_settings = mantis_node.evaluate_input("BBone Segments") > 1
  279. except KeyError:
  280. return # the tree isn't ready yet.
  281. for name in ik_names.keys():
  282. self.inputs[name].hide = not self.display_ik_settings
  283. for name in display_names.keys():
  284. if name in ['Custom Object', 'Bone Collection']: continue
  285. self.inputs[name].hide = not self.display_vp_settings
  286. for name in deform_names.keys():
  287. if name in ['Deform']: continue
  288. self.inputs[name].hide = not self.display_def_settings
  289. for name in bbone_names.keys():
  290. if name in ['BBone Segments']: continue
  291. self.inputs[name].hide = not self.display_bb_settings
  292. def object_displaty_update(self, parsed_tree, context):
  293. custom_prop_display_update(self)
  294. if context.space_data:
  295. mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
  296. self.inputs['Name'].display_text = ""
  297. if mantis_node and mantis_node.prepared:
  298. self.inputs['Name'].display_text = mantis_node.evaluate_input("Name")
  299. custom_prop_display_update(self, mantis_node)
  300. class xFormArmatureNode(Node, xFormNode):
  301. '''A node representing an Armature object node'''
  302. bl_idname = 'xFormArmatureNode'
  303. bl_label = "Armature"
  304. bl_icon = 'OUTLINER_OB_ARMATURE'
  305. initialized : bpy.props.BoolProperty(default = False)
  306. mantis_node_class_name=bl_idname[:-4]
  307. def init(self, context):
  308. self.init_sockets(xFormArmatureSockets)
  309. self.use_custom_color = True
  310. self.color = xFormColor
  311. self.initialized=True
  312. def draw_label(self): # this will prefer a user-set label, or return the evaluated name
  313. return main_draw_label(self)
  314. def display_update(self, parsed_tree, context):
  315. object_displaty_update(self, parsed_tree, context)
  316. class xFormGeometryObjectNode(Node, xFormNode):
  317. """Represents a curve or mesh object."""
  318. bl_idname = "xFormGeometryObject"
  319. bl_label = "Geometry Object"
  320. bl_icon = "EMPTY_AXIS"
  321. initialized : bpy.props.BoolProperty(default = False)
  322. mantis_node_class_name=bl_idname
  323. def init(self, context):
  324. self.init_sockets(xFormGeometryObjectSockets)
  325. self.use_custom_color = True
  326. self.color = xFormColor
  327. self.initialized=True
  328. def draw_label(self): # this will prefer a user-set label, or return the evaluated name
  329. return main_draw_label(self)
  330. def display_update(self, parsed_tree, context):
  331. object_displaty_update(self, parsed_tree, context)
  332. class xFormObjectInstance(Node, xFormNode):
  333. """Represents an instance of an existing geometry object."""
  334. bl_idname = "xFormObjectInstance"
  335. bl_label = "Object Instance"
  336. bl_icon = "EMPTY_AXIS"
  337. initialized : bpy.props.BoolProperty(default = False)
  338. mantis_node_class_name=bl_idname
  339. def init(self, context):
  340. self.init_sockets(xFormGeometryObjectInstanceSockets)
  341. self.use_custom_color = True
  342. self.color = xFormColor
  343. self.initialized=True
  344. def draw_label(self): # this will prefer a user-set label, or return the evaluated name
  345. return main_draw_label(self)
  346. def display_update(self, parsed_tree, context):
  347. object_displaty_update(self, parsed_tree, context)
  348. from .xForm_nodes import xFormCurvePinSockets
  349. class xFormCurvePin(Node, xFormNode):
  350. """"A node representing a curve pin"""
  351. bl_idname = "xFormCurvePin"
  352. bl_label = "Curve Pin"
  353. bl_icon = "FORCE_CURVE"
  354. initialized : bpy.props.BoolProperty(default = False)
  355. mantis_node_class_name=bl_idname
  356. def init(self, context):
  357. self.init_sockets(xFormCurvePinSockets)
  358. self.use_custom_color = True
  359. self.color = xFormColor
  360. self.initialized = True
  361. for cls in TellClasses():
  362. cls.set_mantis_class()