xForm_definitions.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. def TellClasses():
  10. return [
  11. # xFormNullNode,
  12. xFormBoneNode,
  13. xFormArmatureNode,
  14. xFormGeometryObjectNode,
  15. ]
  16. def default_traverse(self, socket):
  17. if (socket == self.outputs["xForm Out"]):
  18. return self.inputs["Relationship"]
  19. if (socket == self.inputs["Relationship"]):
  20. return self.outputs["xForm Out"]
  21. return None
  22. # Representing an Empty or non-armature-Object
  23. # class xFormNullNode(Node, xFormNode):
  24. # '''A node representing a Null node'''
  25. # bl_idname = 'xFormNullNode'
  26. # bl_label = "Null"
  27. # bl_icon = 'EMPTY_AXIS'
  28. # # === Optional Functions ===
  29. # def init(self, context):
  30. # self.inputs.new('StringSocket', "Name")
  31. # self.inputs.new('RelationshipSocket', "Relationship")
  32. # self.inputs.new('RotationOrderSocket', "Rotation Order")
  33. # self.inputs.new('MatrixSocket', "Matrix")
  34. # self.outputs.new('xFormSocket', "xForm Out")
  35. def check_if_connected(start, end, line):
  36. started=False
  37. for path_nc in line:
  38. prWhite(" ", path_nc.signature)
  39. if path_nc.signature == start.signature:
  40. started = True
  41. elif path_nc.signature == end.signature:
  42. break
  43. if started:
  44. if path_nc.inputs.get("Connected"):
  45. if path_nc.evaluate_input("Connected") == False:
  46. return False
  47. else:
  48. return False
  49. return True
  50. # I had chat gpt flip these so they may be a little innacurate
  51. # always visible
  52. main_names = {
  53. "Name":'StringSocket',
  54. "Rotation Order":'RotationOrderSocket',
  55. "Relationship":'RelationshipSocket',
  56. "Matrix":'MatrixSocket',}
  57. # IK SETTINGS
  58. ik_names = {
  59. "IK Stretch":'FloatFactorSocket',
  60. "Lock IK":'BooleanThreeTupleSocket',
  61. "IK Stiffness":'NodeSocketVector',
  62. "Limit IK":'BooleanThreeTupleSocket',
  63. "X Min":'NodeSocketFloatAngle',
  64. "X Max":'NodeSocketFloatAngle',
  65. "Y Min":'NodeSocketFloatAngle',
  66. "Y Max":'NodeSocketFloatAngle',
  67. "Z Min":'NodeSocketFloatAngle',
  68. "Z Max":'NodeSocketFloatAngle',
  69. }
  70. #display settings
  71. display_names = {
  72. "Bone Collection":'BoneCollectionSocket',
  73. "Custom Object":'xFormSocket',
  74. "Custom Object xForm Override":'xFormSocket',
  75. "Custom Object Scale to Bone Length":'BooleanSocket',
  76. "Custom Object Wireframe":'BooleanSocket',
  77. "Custom Object Scale":'VectorScaleSocket',
  78. "Custom Object Translation":'VectorSocket',
  79. "Custom Object Rotation":'VectorEulerSocket',
  80. }
  81. # deform_names
  82. deform_names = {
  83. "Deform":'BooleanSocket',
  84. "Envelope Distance":'FloatPositiveSocket',
  85. "Envelope Weight":'FloatFactorSocket',
  86. "Envelope Multiply":'BooleanSocket',
  87. "Envelope Head Radius":'FloatPositiveSocket',
  88. "Envelope Tail Radius":'FloatPositiveSocket',
  89. }
  90. bbone_names = {
  91. "BBone Segments":"IntSocket", # BONE
  92. "BBone X Size":"FloatSocket", # BONE
  93. "BBone Z Size":"FloatSocket", # BONE
  94. # "bbone_mapping_mode":"StringSocket", <== BONE
  95. "BBone HQ Deformation":"BooleanSocket", # BONE bbone_mapping_mode
  96. "BBone X Curve-In":"FloatSocket", # BONE AND POSE
  97. "BBone Z Curve-In":"FloatSocket", # BONE AND POSE
  98. "BBone X Curve-Out":"FloatSocket", # BONE AND POSE
  99. "BBone Z Curve-Out":"FloatSocket", # BONE AND POSE
  100. "BBone Roll-In":"FloatSocket", # BONE AND POSE
  101. "BBone Roll-Out":"FloatSocket", # BONE AND POSE
  102. "BBone Inherit End Roll":"BooleanSocket", # BONE
  103. "BBone Scale-In":"VectorSocket", # BONE AND POSE
  104. "BBone Scale-Out":"VectorSocket", # BONE AND POSE
  105. "BBone Ease-In":"FloatSocket", # BONE AND POSE
  106. "BBone Ease-Out":"FloatSocket", # BONE AND POSE
  107. "BBone Easing":"BooleanSocket", # BONE
  108. "BBone Start Handle Type":"EnumBBoneHandleType", # BONE
  109. "BBone Custom Start Handle":"StringSocket", # BONE
  110. "BBone Start Handle Scale":"BooleanThreeTupleSocket", # BONE
  111. "BBone Start Handle Ease":"BooleanSocket", # BONE
  112. "BBone End Handle Type":"EnumBBoneHandleType", # BONE
  113. "BBone Custom End Handle":"StringSocket", # BONE
  114. "BBone End Handle Scale":"BooleanThreeTupleSocket", # BONE
  115. "BBone End Handle Ease":"BooleanSocket", # BONE
  116. }
  117. other_names = {
  118. "Lock Location":'BooleanThreeTupleSocket',
  119. "Lock Rotation":'BooleanThreeTupleSocket',
  120. "Lock Scale":'BooleanThreeTupleSocket',
  121. "Hide":'HideSocket',
  122. }
  123. from mathutils import Color
  124. xFormColor = Color((0.093172, 0.047735, 0.028036)).from_scene_linear_to_srgb()
  125. class xFormBoneNode(Node, xFormNode):
  126. '''A node representing a Bone'''
  127. bl_idname = 'xFormBoneNode'
  128. bl_label = "Bone"
  129. bl_icon = 'BONE_DATA'
  130. display_ik_settings : bpy.props.BoolProperty(default=False)
  131. display_vp_settings : bpy.props.BoolProperty(default=False)
  132. display_def_settings : bpy.props.BoolProperty(default=False)
  133. display_bb_settings : bpy.props.BoolProperty(default=False)
  134. socket_count : bpy.props.IntProperty()
  135. initialized : bpy.props.BoolProperty(default = False)
  136. def init(self, context):
  137. for name, sock_type in main_names.items():
  138. self.inputs.new(sock_type, name)
  139. for name, sock_type in ik_names.items():
  140. s = self.inputs.new(sock_type, name)
  141. s.hide = True
  142. for name, sock_type in display_names.items():
  143. s = self.inputs.new(sock_type, name)
  144. if s.name in ['Custom Object', 'Bone Collection']:
  145. continue
  146. s.hide = True
  147. for name, sock_type in deform_names.items():
  148. s = self.inputs.new(sock_type, name)
  149. if s.name == 'Deform':
  150. continue
  151. s.hide = True
  152. for name, sock_type in bbone_names.items():
  153. s = self.inputs.new(sock_type, name)
  154. if s.name == "BBone Segments":
  155. continue
  156. s.hide = True
  157. for name, sock_type in other_names.items():
  158. self.inputs.new(sock_type, name)
  159. # could probably simplify this further with iter_tools.chain() but meh
  160. self.socket_count = len(self.inputs)
  161. #
  162. self.outputs.new('xFormSocket', "xForm Out")
  163. # set up some defaults...
  164. self.inputs['Rotation Order'].default_value = "XYZ"
  165. self.inputs['Lock Location'].default_value[0] = True
  166. self.inputs['Lock Location'].default_value[1] = True
  167. self.inputs['Lock Location'].default_value[2] = True
  168. self.inputs['Lock Rotation'].default_value[0] = True
  169. self.inputs['Lock Rotation'].default_value[1] = True
  170. self.inputs['Lock Rotation'].default_value[2] = True
  171. self.inputs['Lock Scale'].default_value[0] = True
  172. self.inputs['Lock Scale'].default_value[1] = True
  173. self.inputs['Lock Scale'].default_value[2] = True
  174. # color
  175. self.use_custom_color = True
  176. self.color = xFormColor
  177. #
  178. self.initialized=True
  179. def draw_buttons(self, context, layout):
  180. # return
  181. layout.operator("mantis.add_custom_property", text='+Add Custom Parameter')
  182. # layout.label(text="Edit Parameter ... not implemented")
  183. if (len(self.inputs) > self.socket_count):
  184. layout.operator("mantis.edit_custom_property", text=' Edit Custom Parameter')
  185. layout.operator("mantis.remove_custom_property", text='-Remove Custom Parameter')
  186. else:
  187. layout.label(text="")
  188. def display_update(self, parsed_tree, context):
  189. if context.space_data:
  190. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  191. other_nc = None
  192. if len(self.inputs.get("Relationship").links)>0:
  193. prev_node = self.inputs.get("Relationship").links[0].from_node
  194. if prev_node:
  195. other_nc = parsed_tree.get(get_signature_from_edited_tree(prev_node, context))
  196. if nc and other_nc:
  197. self.display_vp_settings = nc.inputs["Custom Object"].is_connected
  198. self.display_def_settings = nc.evaluate_input("Deform")
  199. self.display_bb_settings = nc.evaluate_input("BBone Segments") > 1
  200. self.display_ik_settings = False
  201. #
  202. from .node_container_common import ( trace_all_lines_up,
  203. trace_single_line)
  204. #TODO find a much faster way to do this
  205. if False and self.id_data.do_live_update: #TODO this is extremely freaking slow
  206. trace = trace_all_lines_up(nc, "xForm Out")
  207. for key in trace.keys():
  208. if (ik_nc:= parsed_tree.get(key)):
  209. if ik_nc.__class__.__name__ in ["LinkInverseKinematics"]:
  210. # if the tree is invalid? This shouldn't be necessary.
  211. if ik_nc.inputs["Input Relationship"].is_connected:
  212. chain_count = ik_nc.evaluate_input("Chain Length")
  213. # if chain_count == 0: # this is wrong
  214. # self.display_ik_settings = True
  215. # else:
  216. if ik_nc.evaluate_input("Use Tail") == False:
  217. chain_count+=1
  218. for line in trace[key]:
  219. # preprocess it to get rid of non-xForms:
  220. xForm_line=[]
  221. for path_nc in line:
  222. if path_nc == ik_nc:
  223. if ik_nc.inputs["Input Relationship"].links[0].from_node != prev_path_nc:
  224. break # not a constraint connection
  225. if path_nc.node_type == 'XFORM':
  226. xForm_line.append(path_nc)
  227. prev_path_nc = path_nc
  228. else: # if it can finish cleaning the line of non-xForms and still has a connection
  229. if (len(xForm_line) < chain_count) or (chain_count == 0):
  230. self.display_ik_settings = True
  231. inp = nc.inputs["Relationship"]
  232. link = None
  233. if inp.is_connected:
  234. link = inp.links[0]
  235. while(link):
  236. if link.from_node.__class__.__name__ in ["LinkInverseKinematics"]:
  237. self.display_ik_settings = link.from_node.evaluate_input("Use Tail")
  238. break
  239. inp = link.from_node.outputs[link.from_socket]
  240. inp = inp.traverse_target
  241. if not inp:
  242. break
  243. if inp.links:
  244. link = inp.links[0]
  245. else:
  246. link = None
  247. for name in ik_names.keys():
  248. self.inputs[name].hide = not self.display_ik_settings
  249. for name in display_names.keys():
  250. if name in ['Custom Object', 'Bone Collection']: continue
  251. self.inputs[name].hide = not self.display_vp_settings
  252. for name in deform_names.keys():
  253. if name in ['Deform']: continue
  254. self.inputs[name].hide = not self.display_def_settings
  255. for name in bbone_names.keys():
  256. if name in ['BBone Segments']: continue
  257. self.inputs[name].hide = not self.display_bb_settings
  258. class xFormArmatureNode(Node, xFormNode):
  259. '''A node representing an Armature object node'''
  260. bl_idname = 'xFormArmatureNode'
  261. bl_label = "Armature"
  262. bl_icon = 'OUTLINER_OB_ARMATURE'
  263. initialized : bpy.props.BoolProperty(default = False)
  264. def init(self, context):
  265. self.inputs.new('StringSocket', "Name")
  266. self.inputs.new('RelationshipSocket', "Relationship")
  267. self.inputs.new('RotationOrderSocket', "Rotation Order")
  268. self.inputs.new('MatrixSocket', "Matrix")
  269. self.outputs.new('xFormSocket', "xForm Out")
  270. # color
  271. self.use_custom_color = True
  272. self.color = xFormColor
  273. self.initialized=True
  274. class xFormGeometryObjectNode(Node, xFormNode):
  275. """Represents a curve or mesh object."""
  276. bl_idname = "xFormGeometryObject"
  277. bl_label = "Geometry Object"
  278. bl_icon = "EMPTY_AXIS"
  279. initialized : bpy.props.BoolProperty(default = False)
  280. def init(self, context):
  281. self.inputs.new('StringSocket', "Name")
  282. self.inputs.new('GeometrySocket', "Geometry")
  283. self.inputs.new('MatrixSocket', "Matrix")
  284. self.inputs.new('RelationshipSocket', "Relationship")
  285. self.inputs.new('DeformerSocket', "Deformer")
  286. self.inputs.new ("HideSocket", "Hide in Viewport")
  287. self.inputs.new ("BooleanSocket", "Hide in Render")
  288. self.outputs.new('xFormSocket', "xForm Out")
  289. # color
  290. self.use_custom_color = True
  291. self.color = xFormColor
  292. self.initialized=True