deformer_definitions.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. ]
  18. def default_traverse(self, socket):
  19. if (socket == self.outputs["Deformer"]):
  20. return self.inputs["Deformer"]
  21. if (socket == self.inputs["Deformer"]):
  22. return self.outputs["Deformer"]
  23. return None
  24. class DeformerArmatureNode(Node, DeformerNode):
  25. '''A node representing an Armature Deformer'''
  26. bl_idname = 'DeformerArmature'
  27. bl_label = "Armature Deform"
  28. bl_icon = 'MOD_ARMATURE'
  29. initialized : bpy.props.BoolProperty(default = False)
  30. mantis_node_class_name=bl_idname
  31. def init(self, context):
  32. # self.inputs.new ("RelationshipSocket", "Input Relationship")
  33. self.inputs.new('xFormSocket', "Armature Object")
  34. self.inputs.new('StringSocket', "Blend Vertex Group")
  35. # self.inputs.new('StringSocket', "Preserve Volume Vertex Group") #TODO figure out the right UX for automatic dual-quat blending
  36. self.inputs.new('BooleanSocket', "Invert Vertex Group")
  37. self.inputs.new('BooleanSocket', "Preserve Volume")
  38. # TODO: make the above controlled by a vertex group instead.
  39. self.inputs.new('BooleanSocket', "Use Multi Modifier")# might just set this auto
  40. self.inputs.new('BooleanSocket', "Use Envelopes")
  41. self.inputs.new('BooleanSocket', "Use Vertex Groups")
  42. self.inputs.new("DeformerSocket", "Deformer")
  43. s = self.inputs.new("xFormSocket", "Copy Skin Weights From")
  44. s.hide = True
  45. self.inputs.new("EnumSkinning", "Skinning Method")
  46. self.outputs.new('DeformerSocket', "Deformer")
  47. self.initialized = True
  48. def display_update(self, parsed_tree, context):
  49. self.inputs["Copy Skin Weights From"].hide = True
  50. node_tree = context.space_data.path[0].node_tree
  51. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  52. if nc:
  53. self.inputs["Copy Skin Weights From"].hide = not (nc.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")
  54. class DeformerHook(Node, DeformerNode):
  55. '''A node representing a Hook Deformer'''
  56. bl_idname = 'DeformerHook'
  57. bl_label = "Hook Deform"
  58. bl_icon = 'HOOK'
  59. initialized : bpy.props.BoolProperty(default = False)
  60. mantis_node_class_name=bl_idname
  61. def init(self, context):
  62. self.init_sockets(HookSockets)
  63. self.initialized = True
  64. def display_update(self, parsed_tree, context):
  65. curve_sockets = [
  66. self.inputs["Affect Curve Radius"],
  67. self.inputs['Auto-Bezier'],
  68. self.inputs['Spline Index'],
  69. ]
  70. is_curve_hook=True
  71. if self.outputs["Deformer"].is_linked:
  72. from bpy.types import Object
  73. if (mantis_node := parsed_tree.get(get_signature_from_edited_tree(self, context))):
  74. if (xforms := mantis_node.GetxForm()):
  75. for xF_node in xforms:
  76. if (ob := xF_node.bObject) and isinstance (xF_node, Object):
  77. if ob.type != 'CURVE': is_curve_hook=False
  78. for socket in curve_sockets:
  79. socket.hide=not is_curve_hook
  80. from .utilities import get_socket_maps, relink_socket_map
  81. # TODO this should probably not be in this file but intstead in Utilities or something
  82. def simple_do_relink(node, map, in_out='INPUT'):
  83. from bpy.types import NodeSocket
  84. for key, val in map.items():
  85. s = node.inputs.get(key) if in_out == "INPUT" else node.outputs.get(key)
  86. if s is None:
  87. if in_out == "INPUT":
  88. if node.num_targets > 0:
  89. s = node.inputs["Target."+str(node.num_targets-1).zfill(3)]
  90. else:
  91. continue
  92. if isinstance(val, list):
  93. for sub_val in val:
  94. if isinstance(sub_val, NodeSocket):
  95. if in_out =='INPUT':
  96. node.id_data.links.new(input=sub_val, output=s)
  97. else:
  98. node.id_data.links.new(input=s, output=sub_val)
  99. else:
  100. try:
  101. s.default_value = val
  102. 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
  103. pass
  104. # Dynamic
  105. # - each Morph Target gets a MT input
  106. # - each Morph Target gets an influence input
  107. class DeformerMorphTargetDeform(Node, DeformerNode):
  108. '''A node representing a Morph Target Deformer'''
  109. bl_idname = 'DeformerMorphTargetDeform'
  110. bl_label = "Morph Deform"
  111. bl_icon = 'MOD_ARMATURE'
  112. initialized : bpy.props.BoolProperty(default = False)
  113. num_targets : bpy.props.IntProperty(default = 0)
  114. mantis_node_class_name=bl_idname
  115. def init(self, context):
  116. self.id_data.do_live_update = False
  117. self.inputs.new('DeformerSocket', 'Deformer', )
  118. self.inputs.new('BooleanSocket', 'Use Shape Key', )
  119. s = self.inputs.new('BooleanSocket', 'Use Offset', )
  120. s.default_value = True
  121. self.inputs.new('WildcardSocket', '', identifier='__extend__')
  122. self.outputs.new('DeformerSocket', "Deformer")
  123. self.update_morph_deformer()
  124. def update_morph_deformer(self, force=False):
  125. self.initialized = False
  126. # use_offset = self.inputs["Use Offset"].default_value
  127. socket_maps = get_socket_maps(self)
  128. if socket_maps is None:
  129. return
  130. input_map = socket_maps[0]
  131. # checc to see if targets have been removed... then modify the input map if necessary
  132. targets_deleted = 0 # this should usually be either 0 or 1
  133. for i in range(self.num_targets):
  134. name = "Target."+str(i).zfill(3); inf_name = "Value."+str(i).zfill(3)
  135. if self.inputs[name].is_linked == False:
  136. del input_map[name]; del input_map[inf_name]
  137. targets_deleted+=1
  138. elif targets_deleted: # move it back
  139. new_name = "Target."+str(i-targets_deleted).zfill(3); new_inf_name = "Value."+str(i-targets_deleted).zfill(3)
  140. input_map[new_name] = input_map[name]; input_map[new_inf_name] = input_map[inf_name]
  141. del input_map[name]; del input_map[inf_name]
  142. self.num_targets-=targets_deleted
  143. if self.inputs[-1].is_linked and self.inputs[-1].bl_idname == 'WildcardSocket':
  144. self.num_targets+=1
  145. self.inputs.clear()
  146. self.inputs.new('DeformerSocket', 'Deformer', )
  147. self.inputs.new('BooleanSocket', 'Use Shape Key', )
  148. self.inputs.new('BooleanSocket', 'Use Offset', )
  149. for i in range(self.num_targets):
  150. self.inputs.new("MorphTargetSocket", "Target."+str(i).zfill(3))
  151. self.inputs.new("FloatSocket", "Value."+str(i).zfill(3))
  152. simple_do_relink(self, input_map, in_out='INPUT')
  153. if self.inputs[-1].bl_idname not in ["WildcardSocket"]:
  154. self.inputs.new('WildcardSocket', '', identifier='__extend__')
  155. self.initialized = True
  156. def update(self):
  157. if self.id_data.is_exporting:
  158. return # so that we don't update it while saving/loading the tree
  159. self.update_morph_deformer(force=False)
  160. def display_update(self, parsed_tree, context):
  161. if self.inputs["Deformer"].is_linked:
  162. if self.inputs["Deformer"].links[0].from_node.bl_idname not in [self.bl_idname, "NodeGroupInput"]:
  163. self.inputs["Use Shape Key"].default_value = False
  164. self.inputs["Use Shape Key"].hide = True
  165. elif self.inputs["Deformer"].links[0].from_node.inputs["Use Shape Key"].default_value == False:
  166. self.inputs["Use Shape Key"].default_value = False
  167. self.inputs["Use Shape Key"].hide = True
  168. if self.inputs["Use Offset"] == False:
  169. self.inputs["Use Shape Key"].hide = True
  170. self.inputs["Use Shape Key"].default_value = False
  171. # TODO: there is no reason for this to be a separate node!
  172. class DeformerMorphTarget(Node, DeformerNode):
  173. '''A node representing a single Morph Target'''
  174. bl_idname = 'DeformerMorphTarget'
  175. bl_label = "Morph Target"
  176. bl_icon = 'SHAPEKEY_DATA'
  177. initialized : bpy.props.BoolProperty(default = False)
  178. num_targets : bpy.props.IntProperty(default = 0)
  179. mantis_node_class_name=bl_idname
  180. def init(self, context):
  181. self.inputs.new('xFormSocket', "Relative to")
  182. self.inputs.new('xFormSocket', "Object")
  183. self.inputs.new('StringSocket', "Vertex Group")
  184. self.outputs.new('MorphTargetSocket', "Morph Target")
  185. self.initialized = True
  186. class DeformerSurfaceDeform(Node, DeformerNode):
  187. '''A node representing a Surface Deform modifier'''
  188. bl_idname = 'DeformerSurfaceDeform'
  189. bl_label = "Surface Deform"
  190. bl_icon = 'MOD_SOFT'
  191. initialized : bpy.props.BoolProperty(default = False)
  192. num_targets : bpy.props.IntProperty(default = 0)
  193. mantis_node_class_name=bl_idname
  194. def init(self, context):
  195. self.init_sockets(SurfaceDeformSockets)
  196. self.initialized = True
  197. class DeformerMeshDeform(Node, DeformerNode):
  198. '''A node representing a Mesh Deform modifier'''
  199. bl_idname = 'DeformerMeshDeform'
  200. bl_label = "Mesh Deform"
  201. bl_icon = 'MOD_SOFT'
  202. initialized : bpy.props.BoolProperty(default = False)
  203. num_targets : bpy.props.IntProperty(default = 0)
  204. mantis_node_class_name=bl_idname
  205. def init(self, context):
  206. self.init_sockets(MeshDeformSockets)
  207. self.initialized = True
  208. class DeformerLatticeDeform(Node, DeformerNode):
  209. '''A node representing a Lattice Deformer'''
  210. bl_idname = 'DeformerLatticeDeform'
  211. bl_label = "Lattice Deform"
  212. bl_icon = 'MOD_LATTICE'
  213. initialized : bpy.props.BoolProperty(default = False)
  214. num_targets : bpy.props.IntProperty(default = 0)
  215. mantis_node_class_name=bl_idname
  216. def init(self, context):
  217. self.init_sockets(LatticeDeformSockets)
  218. self.initialized = True
  219. # Set up the class property that ties the UI classes to the Mantis classes.
  220. for cls in TellClasses():
  221. cls.set_mantis_class()