deformer_nodes_ui.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import bpy
  2. from bpy.types import NodeTree, Node, NodeSocket
  3. from .base_definitions import MantisUINode, DeformerNode, get_signature_from_edited_tree
  4. from.deformer_socket_templates import *
  5. from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
  6. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  7. wrapOrange,)
  8. def TellClasses():
  9. return [
  10. DeformerArmatureNode,
  11. DeformerHook, #
  12. DeformerMorphTargetDeform,
  13. DeformerMorphTarget,
  14. DeformerSurfaceDeform, #
  15. DeformerMeshDeform, #
  16. DeformerLatticeDeform, #
  17. DeformerSmoothCorrectiveDeform, #
  18. ]
  19. def default_traverse(self, socket):
  20. if (socket == self.outputs["Deformer"]):
  21. return self.inputs["Deformer"]
  22. if (socket == self.inputs["Deformer"]):
  23. return self.outputs["Deformer"]
  24. return None
  25. class DeformerArmatureNode(Node, DeformerNode):
  26. '''A node representing an Armature Deformer'''
  27. bl_idname = 'DeformerArmature'
  28. bl_label = "Armature Deform"
  29. bl_icon = 'MOD_ARMATURE'
  30. initialized : bpy.props.BoolProperty(default = False)
  31. mantis_node_class_name=bl_idname
  32. def init(self, context):
  33. self.init_sockets(DeformArmatureSockets)
  34. self.initialized = True
  35. def display_update(self, parsed_tree, context):
  36. self.inputs["Copy Skin Weights From"].hide = True
  37. node_tree = context.space_data.path[0].node_tree
  38. mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
  39. if mantis_node:
  40. self.inputs["Copy Skin Weights From"].hide = not (mantis_node.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")
  41. class DeformerHook(Node, DeformerNode):
  42. '''A node representing a Hook Deformer'''
  43. bl_idname = 'DeformerHook'
  44. bl_label = "Hook Deform"
  45. bl_icon = 'HOOK'
  46. initialized : bpy.props.BoolProperty(default = False)
  47. mantis_node_class_name=bl_idname
  48. def init(self, context):
  49. self.init_sockets(HookSockets)
  50. self.initialized = True
  51. def display_update(self, parsed_tree, context):
  52. curve_sockets = [
  53. self.inputs["Affect Curve Radius"],
  54. self.inputs['Auto-Bezier'],
  55. self.inputs['Spline Index'],
  56. ]
  57. is_curve_hook=True
  58. if self.outputs["Deformer"].is_linked:
  59. from bpy.types import Object
  60. if (mantis_node := parsed_tree.get(get_signature_from_edited_tree(self, context))):
  61. if (xforms := mantis_node.GetxForm()):
  62. for xF_node in xforms:
  63. if (ob := xF_node.bObject) and isinstance (xF_node, Object):
  64. if ob.type != 'CURVE': is_curve_hook=False
  65. for socket in curve_sockets:
  66. socket.hide=not is_curve_hook
  67. from .utilities import get_socket_maps, relink_socket_map
  68. # TODO this should probably not be in this file but intstead in Utilities or something
  69. def simple_do_relink(node, map, in_out='INPUT'):
  70. from bpy.types import NodeSocket
  71. for key, val in map.items():
  72. s = node.inputs.get(key) if in_out == "INPUT" else node.outputs.get(key)
  73. if s is None:
  74. if in_out == "INPUT":
  75. if node.num_targets > 0:
  76. s = node.inputs["Target."+str(node.num_targets-1).zfill(3)]
  77. else:
  78. continue
  79. if isinstance(val, list):
  80. for sub_val in val:
  81. if isinstance(sub_val, NodeSocket):
  82. if in_out =='INPUT':
  83. node.id_data.links.new(input=sub_val, output=s)
  84. else:
  85. node.id_data.links.new(input=s, output=sub_val)
  86. else:
  87. try:
  88. s.default_value = val
  89. except (AttributeError, ValueError, TypeError): # must be readonly or maybe it doesn't have a d.v.. TypeError if the d.v. is None at this point
  90. pass
  91. # Dynamic
  92. # - each Morph Target gets a MT input
  93. # - each Morph Target gets an influence input
  94. class DeformerMorphTargetDeform(Node, DeformerNode):
  95. '''A node representing a Morph Target Deformer'''
  96. bl_idname = 'DeformerMorphTargetDeform'
  97. bl_label = "Morph Deform"
  98. bl_icon = 'MOD_ARMATURE'
  99. initialized : bpy.props.BoolProperty(default = False)
  100. num_targets : bpy.props.IntProperty(default = 0)
  101. mantis_node_class_name=bl_idname
  102. def init(self, context):
  103. self.id_data.do_live_update = False
  104. self.init_sockets(MorphTargetDeformSockets)
  105. self.update_morph_deformer()
  106. self.id_data.do_live_update = True
  107. def update_morph_deformer(self, force=False):
  108. self.initialized = False
  109. # use_offset = self.inputs["Use Offset"].default_value
  110. socket_maps = get_socket_maps(self)
  111. if socket_maps is None:
  112. return
  113. input_map = socket_maps[0]
  114. # checc to see if targets have been removed... then modify the input map if necessary
  115. targets_deleted = 0 # this should usually be either 0 or 1
  116. for i in range(self.num_targets):
  117. name = "Target."+str(i).zfill(3); inf_name = "Value."+str(i).zfill(3)
  118. if self.inputs[name].is_linked == False:
  119. del input_map[name]; del input_map[inf_name]
  120. targets_deleted+=1
  121. elif targets_deleted: # move it back
  122. new_name = "Target."+str(i-targets_deleted).zfill(3); new_inf_name = "Value."+str(i-targets_deleted).zfill(3)
  123. input_map[new_name] = input_map[name]; input_map[new_inf_name] = input_map[inf_name]
  124. del input_map[name]; del input_map[inf_name]
  125. self.num_targets-=targets_deleted
  126. if self.inputs[-1].is_linked and self.inputs[-1].bl_idname == 'WildcardSocket':
  127. self.num_targets+=1
  128. self.inputs.clear()
  129. self.inputs.new('DeformerSocket', 'Deformer', )
  130. self.inputs.new('BooleanSocket', 'Use Shape Key', )
  131. self.inputs.new('BooleanSocket', 'Use Offset', )
  132. for i in range(self.num_targets):
  133. self.inputs.new("MorphTargetSocket", "Target."+str(i).zfill(3))
  134. self.inputs.new("FloatSocket", "Value."+str(i).zfill(3))
  135. simple_do_relink(self, input_map, in_out='INPUT')
  136. if self.inputs[-1].bl_idname not in ["WildcardSocket"]:
  137. self.inputs.new('WildcardSocket', '', identifier='__extend__')
  138. self.initialized = True
  139. def update(self):
  140. if self.id_data.is_exporting:
  141. return # so that we don't update it while saving/loading the tree
  142. self.update_morph_deformer(force=False)
  143. def display_update(self, parsed_tree, context):
  144. if self.inputs["Deformer"].is_linked:
  145. if self.inputs["Deformer"].links[0].from_node.bl_idname not in [self.bl_idname, "NodeGroupInput"]:
  146. self.inputs["Use Shape Key"].default_value = False
  147. self.inputs["Use Shape Key"].hide = True
  148. elif self.inputs["Deformer"].links[0].from_node.inputs["Use Shape Key"].default_value == False:
  149. self.inputs["Use Shape Key"].default_value = False
  150. self.inputs["Use Shape Key"].hide = True
  151. if self.inputs["Use Offset"] == False:
  152. self.inputs["Use Shape Key"].hide = True
  153. self.inputs["Use Shape Key"].default_value = False
  154. # NOTE: in the past I considered jamming this into the above node
  155. # as a dynamic node thing. Worth considering later, but it's fine.
  156. class DeformerMorphTarget(Node, DeformerNode):
  157. '''A node representing a single Morph Target'''
  158. bl_idname = 'DeformerMorphTarget'
  159. bl_label = "Morph Target"
  160. bl_icon = 'SHAPEKEY_DATA'
  161. initialized : bpy.props.BoolProperty(default = False)
  162. num_targets : bpy.props.IntProperty(default = 0)
  163. mantis_node_class_name=bl_idname
  164. def init(self, context):
  165. self.init_sockets(MorphTargetSockets)
  166. self.initialized = True
  167. class DeformerSurfaceDeform(Node, DeformerNode):
  168. '''A node representing a Surface Deform modifier'''
  169. bl_idname = 'DeformerSurfaceDeform'
  170. bl_label = "Surface Deform"
  171. bl_icon = 'MOD_SOFT'
  172. initialized : bpy.props.BoolProperty(default = False)
  173. num_targets : bpy.props.IntProperty(default = 0)
  174. mantis_node_class_name=bl_idname
  175. def init(self, context):
  176. self.init_sockets(SurfaceDeformSockets)
  177. self.initialized = True
  178. class DeformerMeshDeform(Node, DeformerNode):
  179. '''A node representing a Mesh Deform modifier'''
  180. bl_idname = 'DeformerMeshDeform'
  181. bl_label = "Mesh Deform"
  182. bl_icon = 'MOD_SOFT'
  183. initialized : bpy.props.BoolProperty(default = False)
  184. num_targets : bpy.props.IntProperty(default = 0)
  185. mantis_node_class_name=bl_idname
  186. def init(self, context):
  187. self.init_sockets(MeshDeformSockets)
  188. self.initialized = True
  189. class DeformerLatticeDeform(Node, DeformerNode):
  190. '''A node representing a Lattice Deformer'''
  191. bl_idname = 'DeformerLatticeDeform'
  192. bl_label = "Lattice Deform"
  193. bl_icon = 'MOD_LATTICE'
  194. initialized : bpy.props.BoolProperty(default = False)
  195. num_targets : bpy.props.IntProperty(default = 0)
  196. mantis_node_class_name=bl_idname
  197. def init(self, context):
  198. self.init_sockets(LatticeDeformSockets)
  199. self.initialized = True
  200. class DeformerSmoothCorrectiveDeform(Node, DeformerNode):
  201. '''A node representing a Corrective Smooth Deformer'''
  202. bl_idname = 'DeformerSmoothCorrectiveDeform'
  203. bl_label = "Smooth Deform"
  204. bl_icon = 'MOD_SMOOTH'
  205. initialized : bpy.props.BoolProperty(default = False)
  206. num_targets : bpy.props.IntProperty(default = 0)
  207. mantis_node_class_name=bl_idname
  208. def init(self, context):
  209. self.init_sockets(SmoothDeformSockets)
  210. self.initialized = True
  211. def display_update(self, parsed_tree, context):
  212. use_corrective_smooth = self.inputs["Use Corrective Smooth"]
  213. if ( use_corrective_smooth.is_linked or
  214. use_corrective_smooth.default_value == False): # this is inverted
  215. self.inputs['Corrective Smooth Scale'].hide = False
  216. else:
  217. self.inputs['Corrective Smooth Scale'].hide = True
  218. # Set up the class property that ties the UI classes to the Mantis classes.
  219. for cls in TellClasses():
  220. cls.set_mantis_class()