deformer_definitions.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import bpy
  2. from bpy.types import NodeTree, Node, NodeSocket
  3. from .base_definitions import MantisNode, DeformerNode, get_signature_from_edited_tree
  4. from .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,
  5. wrapRed, wrapGreen, wrapPurple, wrapWhite,
  6. wrapOrange,)
  7. def TellClasses():
  8. return [
  9. DeformerArmatureNode,
  10. DeformerHook,
  11. DeformerMorphTargetDeform,
  12. DeformerMorphTarget,
  13. ]
  14. def default_traverse(self, socket):
  15. if (socket == self.outputs["Deformer"]):
  16. return self.inputs["Deformer"]
  17. if (socket == self.inputs["Deformer"]):
  18. return self.outputs["Deformer"]
  19. return None
  20. class DeformerArmatureNode(Node, DeformerNode):
  21. '''A node representing an Armature Deformer'''
  22. bl_idname = 'DeformerArmature'
  23. bl_label = "Armature Deform"
  24. bl_icon = 'MOD_ARMATURE'
  25. initialized : bpy.props.BoolProperty(default = False)
  26. def init(self, context):
  27. # self.inputs.new ("RelationshipSocket", "Input Relationship")
  28. self.inputs.new('xFormSocket', "Armature Object")
  29. self.inputs.new('StringSocket', "Blend Vertex Group")
  30. # self.inputs.new('StringSocket', "Preserve Volume Vertex Group") #TODO figure out the right UX for automatic dual-quat blending
  31. self.inputs.new('BooleanSocket', "Invert Vertex Group")
  32. self.inputs.new('BooleanSocket', "Preserve Volume")
  33. # TODO: make the above controlled by a vertex group instead.
  34. self.inputs.new('BooleanSocket', "Use Multi Modifier")# might just set this auto
  35. self.inputs.new('BooleanSocket', "Use Envelopes")
  36. self.inputs.new('BooleanSocket', "Use Vertex Groups")
  37. self.inputs.new("DeformerSocket", "Deformer")
  38. s = self.inputs.new("xFormSocket", "Copy Skin Weights From")
  39. s.hide = True
  40. self.inputs.new("EnumSkinning", "Skinning Method")
  41. self.outputs.new('DeformerSocket', "Deformer")
  42. self.initialized = True
  43. def display_update(self, parsed_tree, context):
  44. self.inputs["Copy Skin Weights From"].hide = True
  45. node_tree = context.space_data.path[0].node_tree
  46. nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
  47. if nc:
  48. self.inputs["Copy Skin Weights From"].hide = not (nc.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")
  49. class DeformerHook(Node, DeformerNode):
  50. '''A node representing a Hook Deformer'''
  51. bl_idname = 'DeformerHook'
  52. bl_label = "Hook Deform"
  53. bl_icon = 'HOOK'
  54. initialized : bpy.props.BoolProperty(default = False)
  55. def init(self, context):
  56. self.inputs.new("DeformerSocket", "Deformer")
  57. self.inputs.new('xFormSocket', "Hook Target")
  58. self.inputs.new('IntSocket', "Index")
  59. # self.inputs.new('StringSocket', "Blend Vertex Group")
  60. # self.inputs.new('BooleanSocket', "Invert Vertex Group")
  61. self.outputs.new('DeformerSocket', "Deformer")
  62. self.initialized = True
  63. # def display_update(self, parsed_tree, context):
  64. from .utilities import get_socket_maps, relink_socket_map
  65. # TODO this should probably not be in this file but intstead in Utilities or something
  66. def simple_do_relink(node, map, in_out='INPUT'):
  67. from bpy.types import NodeSocket
  68. for key, val in map.items():
  69. s = node.inputs.get(key) if in_out == "INPUT" else node.outputs.get(key)
  70. if s is None:
  71. if in_out == "INPUT":
  72. if node.num_targets > 0:
  73. s = node.inputs["Target."+str(node.num_targets-1).zfill(3)]
  74. else:
  75. continue
  76. if isinstance(val, list):
  77. for sub_val in val:
  78. if isinstance(sub_val, NodeSocket):
  79. if in_out =='INPUT':
  80. node.id_data.links.new(input=sub_val, output=s)
  81. else:
  82. node.id_data.links.new(input=s, output=sub_val)
  83. else:
  84. try:
  85. s.default_value = val
  86. 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
  87. pass
  88. # Dynamic
  89. # - each Morph Target gets a MT input
  90. # - each Morph Target gets an influence input
  91. # this node creates a GN deformer that ADDS the position deltas (from the base position)
  92. # Value has to scale the delta
  93. class DeformerMorphTargetDeform(Node, DeformerNode):
  94. '''A node representing a Morph Target Deformer'''
  95. bl_idname = 'DeformerMorphTargetDeform'
  96. bl_label = "Morph Deform"
  97. bl_icon = 'MOD_ARMATURE'
  98. initialized : bpy.props.BoolProperty(default = False)
  99. num_targets : bpy.props.IntProperty(default = 0)
  100. def init(self, context):
  101. self.id_data.do_live_update = False
  102. self.inputs.new('DeformerSocket', 'Deformer', )
  103. self.inputs.new('BooleanSocket', 'Use Shape Key', )
  104. self.inputs.new('WildcardSocket', '', identifier='__extend__')
  105. self.outputs.new('DeformerSocket', "Deformer")
  106. self.update()
  107. def update(self):
  108. if self.id_data.is_executing:
  109. return # so that we don't update it while saving/loading the tree
  110. self.initialized = False
  111. input_map = get_socket_maps(self)[0]
  112. # checc to see if targets have been removed... then modify the input map if necessary
  113. targets_deleted = 0 # this should usually be either 0 or 1
  114. for i in range(self.num_targets):
  115. name = "Target."+str(i).zfill(3); inf_name = "Value."+str(i).zfill(3)
  116. if self.inputs[name].is_linked == False:
  117. del input_map[name]; del input_map[inf_name]
  118. targets_deleted+=1
  119. elif targets_deleted: # move it back
  120. new_name = "Target."+str(i-targets_deleted).zfill(3); new_inf_name = "Value."+str(i-targets_deleted).zfill(3)
  121. input_map[new_name] = input_map[name]; input_map[new_inf_name] = input_map[inf_name]
  122. del input_map[name]; del input_map[inf_name]
  123. self.num_targets-=targets_deleted
  124. if self.inputs[-1].is_linked and self.inputs[-1].bl_idname == 'WildcardSocket':
  125. self.num_targets+=1
  126. self.inputs.clear()
  127. self.inputs.new('DeformerSocket', 'Deformer', )
  128. self.inputs.new('BooleanSocket', 'Use Shape Key', )
  129. # have to do this manually to avoid making things harder elsewhere
  130. # input_map
  131. for i in range(self.num_targets):
  132. self.inputs.new("MorphTargetSocket", "Target."+str(i).zfill(3))
  133. self.inputs.new("FloatSocket", "Value."+str(i).zfill(3))
  134. # if self.num_targets > 0:
  135. simple_do_relink(self, input_map, in_out='INPUT')
  136. if len(self.inputs)<2 or self.inputs[-1].bl_idname not in ["WildcardSocket"]:
  137. self.inputs.new('WildcardSocket', '', identifier='__extend__')
  138. self.initialized = True
  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 != self.bl_idname:
  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. # TODO: there is no reason for this to be a separate node!
  148. class DeformerMorphTarget(Node, DeformerNode):
  149. '''A node representing a single Morph Target'''
  150. bl_idname = 'DeformerMorphTarget'
  151. bl_label = "Morph Target"
  152. bl_icon = 'SHAPEKEY_DATA'
  153. initialized : bpy.props.BoolProperty(default = False)
  154. num_targets : bpy.props.IntProperty(default = 0)
  155. def init(self, context):
  156. self.inputs.new('xFormSocket', "Relative to")
  157. self.inputs.new('xFormSocket', "Object")
  158. self.inputs.new('StringSocket', "Vertex Group")
  159. self.outputs.new('MorphTargetSocket', "Morph Target")
  160. self.initialized = True