deformer_nodes_ui.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. mantis_node_class_name=bl_idname
  31. socket_templates = DeformArmatureSockets
  32. def display_update(self, parsed_tree, context):
  33. self.inputs["Copy Skin Weights From"].hide = True
  34. node_tree = context.space_data.path[0].node_tree
  35. mantis_node = parsed_tree.get(get_signature_from_edited_tree(self, context))
  36. if mantis_node:
  37. self.inputs["Copy Skin Weights From"].hide = not (mantis_node.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")
  38. class DeformerHook(Node, DeformerNode):
  39. '''A node representing a Hook Deformer'''
  40. bl_idname = 'DeformerHook'
  41. bl_label = "Hook Deform"
  42. bl_icon = 'HOOK'
  43. mantis_node_class_name=bl_idname
  44. socket_templates = HookSockets
  45. def display_update(self, parsed_tree, context):
  46. curve_sockets = [
  47. self.inputs["Affect Curve Radius"],
  48. self.inputs['Auto-Bezier'],
  49. self.inputs['Spline Index'],
  50. ]
  51. is_curve_hook=True
  52. if self.outputs["Deformer"].is_linked:
  53. from bpy.types import Object
  54. if (mantis_node := parsed_tree.get(get_signature_from_edited_tree(self, context))):
  55. if (xforms := mantis_node.GetxForm()):
  56. for xF_node in xforms:
  57. if (ob := xF_node.bObject) and isinstance (xF_node, Object):
  58. if ob.type != 'CURVE': is_curve_hook=False
  59. for socket in curve_sockets:
  60. socket.hide=not is_curve_hook
  61. from .utilities import get_socket_maps, relink_socket_map
  62. # TODO this should probably not be in this file but intstead in Utilities or something
  63. # NOTE this is a re-implementation that I had to do here.
  64. def simple_do_relink(node, map, in_out='INPUT'):
  65. from bpy.types import NodeSocket
  66. for key, val in map.items():
  67. s = node.inputs.get(key) if in_out == "INPUT" else node.outputs.get(key)
  68. if s is None:
  69. if in_out == "INPUT":
  70. if node.num_targets > 0:
  71. s = node.inputs["Target."+str(node.num_targets-1).zfill(3)]
  72. else:
  73. continue
  74. if isinstance(val, list):
  75. for sub_val in val:
  76. if isinstance(sub_val, NodeSocket):
  77. if in_out =='INPUT':
  78. node.id_data.links.new(input=sub_val, output=s)
  79. else:
  80. node.id_data.links.new(input=s, output=sub_val)
  81. else:
  82. try:
  83. s.default_value = val
  84. 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
  85. pass
  86. # Dynamic
  87. # - each Morph Target gets a MT input
  88. # - each Morph Target gets an influence input
  89. class DeformerMorphTargetDeform(Node, DeformerNode):
  90. '''A node representing a Morph Target Deformer'''
  91. bl_idname = 'DeformerMorphTargetDeform'
  92. bl_label = "Morph Deform"
  93. bl_icon = 'MOD_ARMATURE'
  94. num_targets : bpy.props.IntProperty(default = 0)
  95. mantis_node_class_name=bl_idname
  96. socket_templates = MorphTargetDeformSockets
  97. def init(self, context):
  98. live_updates = self.id_data.do_live_update
  99. self.id_data.do_live_update = False
  100. self.init_sockets(MorphTargetDeformSockets)
  101. self.update_morph_deformer()
  102. self.id_data.do_live_update = live_updates
  103. def update_morph_deformer(self, force=False):
  104. self.initialized = False
  105. # use_offset = self.inputs["Use Offset"].default_value
  106. socket_maps = get_socket_maps(self)
  107. if socket_maps is None:
  108. return
  109. input_map = socket_maps[0]
  110. # checc to see if targets have been removed... then modify the input map if necessary
  111. targets_deleted = 0 # this should usually be either 0 or 1
  112. for i in range(self.num_targets):
  113. name = "Target."+str(i).zfill(3); inf_name = "Value."+str(i).zfill(3)
  114. if self.inputs[name].is_linked == False:
  115. del input_map[name]; del input_map[inf_name]
  116. targets_deleted+=1
  117. elif targets_deleted: # move it back
  118. new_name = "Target."+str(i-targets_deleted).zfill(3); new_inf_name = "Value."+str(i-targets_deleted).zfill(3)
  119. input_map[new_name] = input_map[name]; input_map[new_inf_name] = input_map[inf_name]
  120. del input_map[name]; del input_map[inf_name]
  121. self.num_targets-=targets_deleted
  122. if self.inputs[-1].is_linked and self.inputs[-1].bl_idname == 'WildcardSocket':
  123. self.num_targets+=1
  124. self.inputs.clear()
  125. self.inputs.new('DeformerSocket', 'Deformer', )
  126. self.inputs.new('BooleanSocket', 'Use Shape Key', )
  127. self.inputs.new('BooleanSocket', 'Use Offset', )
  128. for i in range(self.num_targets):
  129. self.inputs.new("MorphTargetSocket", "Target."+str(i).zfill(3))
  130. self.inputs.new("FloatSocket", "Value."+str(i).zfill(3))
  131. simple_do_relink(self, input_map, in_out='INPUT')
  132. if self.inputs[-1].bl_idname not in ["WildcardSocket"]:
  133. self.inputs.new('WildcardSocket', '', identifier='__extend__')
  134. self.initialized = True
  135. def update(self):
  136. if self.id_data.is_exporting:
  137. return # so that we don't update it while saving/loading the tree
  138. self.update_morph_deformer(force=False)
  139. def display_update(self, parsed_tree, context):
  140. if self.inputs["Deformer"].is_linked:
  141. if self.inputs["Deformer"].links[0].from_node.bl_idname not in [self.bl_idname, "NodeGroupInput"]:
  142. self.inputs["Use Shape Key"].default_value = False
  143. self.inputs["Use Shape Key"].hide = True
  144. elif self.inputs["Deformer"].links[0].from_node.inputs["Use Shape Key"].default_value == False:
  145. self.inputs["Use Shape Key"].default_value = False
  146. self.inputs["Use Shape Key"].hide = True
  147. if self.inputs["Use Offset"] == False:
  148. self.inputs["Use Shape Key"].hide = True
  149. self.inputs["Use Shape Key"].default_value = False
  150. # NOTE: in the past I considered jamming this into the above node
  151. # as a dynamic node thing. Worth considering later, but it's fine.
  152. class DeformerMorphTarget(Node, DeformerNode):
  153. '''A node representing a single Morph Target'''
  154. bl_idname = 'DeformerMorphTarget'
  155. bl_label = "Morph Target"
  156. bl_icon = 'SHAPEKEY_DATA'
  157. num_targets : bpy.props.IntProperty(default = 0)
  158. mantis_node_class_name=bl_idname
  159. socket_templates = MorphTargetSockets
  160. class DeformerSurfaceDeform(Node, DeformerNode):
  161. '''A node representing a Surface Deform modifier'''
  162. bl_idname = 'DeformerSurfaceDeform'
  163. bl_label = "Surface Deform"
  164. bl_icon = 'MOD_SOFT'
  165. num_targets : bpy.props.IntProperty(default = 0)
  166. mantis_node_class_name=bl_idname
  167. socket_templates = SurfaceDeformSockets
  168. class DeformerMeshDeform(Node, DeformerNode):
  169. '''A node representing a Mesh Deform modifier'''
  170. bl_idname = 'DeformerMeshDeform'
  171. bl_label = "Mesh Deform"
  172. bl_icon = 'MOD_SOFT'
  173. num_targets : bpy.props.IntProperty(default = 0)
  174. mantis_node_class_name=bl_idname
  175. socket_templates = MeshDeformSockets
  176. class DeformerLatticeDeform(Node, DeformerNode):
  177. '''A node representing a Lattice Deformer'''
  178. bl_idname = 'DeformerLatticeDeform'
  179. bl_label = "Lattice Deform"
  180. bl_icon = 'MOD_LATTICE'
  181. num_targets : bpy.props.IntProperty(default = 0)
  182. mantis_node_class_name=bl_idname
  183. socket_templates = LatticeDeformSockets
  184. class DeformerSmoothCorrectiveDeform(Node, DeformerNode):
  185. '''A node representing a Corrective Smooth Deformer'''
  186. bl_idname = 'DeformerSmoothCorrectiveDeform'
  187. bl_label = "Smooth Deform"
  188. bl_icon = 'MOD_SMOOTH'
  189. num_targets : bpy.props.IntProperty(default = 0)
  190. mantis_node_class_name=bl_idname
  191. socket_templates = SmoothDeformSockets
  192. def display_update(self, parsed_tree, context):
  193. use_corrective_smooth = self.inputs["Use Corrective Smooth"]
  194. if ( use_corrective_smooth.is_linked or
  195. use_corrective_smooth.default_value == False): # this is inverted
  196. self.inputs['Corrective Smooth Scale'].hide = False
  197. else:
  198. self.inputs['Corrective Smooth Scale'].hide = True