| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 | import bpyfrom bpy.types import NodeTree, Node, NodeSocketfrom .base_definitions import MantisNode, DeformerNode, get_signature_from_edited_treefrom .utilities import (prRed, prGreen, prPurple, prWhite, prOrange,                        wrapRed, wrapGreen, wrapPurple, wrapWhite,                        wrapOrange,)def TellClasses():    return [             DeformerArmatureNode,             DeformerHook,             DeformerMorphTargetDeform,             DeformerMorphTarget,           ]def default_traverse(self, socket):        if (socket == self.outputs["Deformer"]):            return self.inputs["Deformer"]        if (socket == self.inputs["Deformer"]):            return self.outputs["Deformer"]        return Noneclass DeformerArmatureNode(Node, DeformerNode):    '''A node representing an Armature Deformer'''    bl_idname = 'DeformerArmature'    bl_label = "Armature Deform"    bl_icon = 'MOD_ARMATURE'    initialized : bpy.props.BoolProperty(default = False)    def init(self, context):        # self.inputs.new ("RelationshipSocket", "Input Relationship")        self.inputs.new('xFormSocket', "Armature Object")        self.inputs.new('StringSocket', "Blend Vertex Group")        # self.inputs.new('StringSocket', "Preserve Volume Vertex Group") #TODO figure out the right UX for automatic dual-quat blending        self.inputs.new('BooleanSocket', "Invert Vertex Group")                self.inputs.new('BooleanSocket', "Preserve Volume")        # TODO: make the above controlled by a vertex group instead.        self.inputs.new('BooleanSocket', "Use Multi Modifier")# might just set this auto        self.inputs.new('BooleanSocket', "Use Envelopes")        self.inputs.new('BooleanSocket', "Use Vertex Groups")                self.inputs.new("DeformerSocket", "Deformer")        s = self.inputs.new("xFormSocket", "Copy Skin Weights From")        s.hide = True        self.inputs.new("EnumSkinning", "Skinning Method")                self.outputs.new('DeformerSocket', "Deformer")        self.initialized = True        def display_update(self, parsed_tree, context):        self.inputs["Copy Skin Weights From"].hide = True        node_tree = context.space_data.path[0].node_tree        nc = parsed_tree.get(get_signature_from_edited_tree(self, context))        if nc:            self.inputs["Copy Skin Weights From"].hide = not (nc.evaluate_input("Skinning Method") == "COPY_FROM_OBJECT")                class DeformerHook(Node, DeformerNode):    '''A node representing a Hook Deformer'''    bl_idname = 'DeformerHook'    bl_label = "Hook Deform"    bl_icon = 'HOOK'    initialized : bpy.props.BoolProperty(default = False)    def init(self, context):        self.inputs.new("DeformerSocket", "Deformer")        self.inputs.new('xFormSocket', "Hook Target")        self.inputs.new('IntSocket', "Index")        # self.inputs.new('StringSocket', "Blend Vertex Group")        # self.inputs.new('BooleanSocket', "Invert Vertex Group")        self.outputs.new('DeformerSocket', "Deformer")        self.initialized = True        # def display_update(self, parsed_tree, context):from .utilities import get_socket_maps, relink_socket_map# TODO this should probably not be in this file but intstead in Utilities or somethingdef simple_do_relink(node, map, in_out='INPUT'):    from bpy.types import NodeSocket    for key, val in map.items():        s = node.inputs.get(key) if in_out == "INPUT" else node.outputs.get(key)        if s is None:            if in_out == "INPUT":                if node.num_targets > 0:                    s = node.inputs["Target."+str(node.num_targets-1).zfill(3)]                else:                    continue        if isinstance(val, list):            for sub_val in val:                if isinstance(sub_val, NodeSocket):                    if in_out =='INPUT':                        node.id_data.links.new(input=sub_val, output=s)                    else:                        node.id_data.links.new(input=s, output=sub_val)        else:            try:                s.default_value = val            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                pass# Dynamic#   - each Morph Target gets a MT input#   - each Morph Target gets an influence inputclass DeformerMorphTargetDeform(Node, DeformerNode):    '''A node representing a Morph Target Deformer'''    bl_idname = 'DeformerMorphTargetDeform'    bl_label = "Morph Deform"    bl_icon = 'MOD_ARMATURE'    initialized : bpy.props.BoolProperty(default = False)    num_targets : bpy.props.IntProperty(default = 0)    def init(self, context):        self.id_data.do_live_update = False        self.inputs.new('DeformerSocket', 'Deformer', )        self.inputs.new('BooleanSocket', 'Use Shape Key', )        s = self.inputs.new('BooleanSocket', 'Use Offset', )        s.default_value = True        self.inputs.new('WildcardSocket', '', identifier='__extend__')        self.outputs.new('DeformerSocket', "Deformer")        self.update_morph_deformer()    def update_morph_deformer(self, force=False):        self.initialized = False        # use_offset = self.inputs["Use Offset"].default_value        input_map = get_socket_maps(self)[0]        # checc to see if targets have been removed... then modify the input map if necessary        targets_deleted = 0 # this should usually be either 0 or 1        for i in range(self.num_targets):            name = "Target."+str(i).zfill(3); inf_name = "Value."+str(i).zfill(3)            if self.inputs[name].is_linked == False:                del input_map[name]; del input_map[inf_name]                targets_deleted+=1            elif targets_deleted: # move it back                new_name = "Target."+str(i-targets_deleted).zfill(3); new_inf_name = "Value."+str(i-targets_deleted).zfill(3)                input_map[new_name] = input_map[name]; input_map[new_inf_name] = input_map[inf_name]                del input_map[name]; del input_map[inf_name]        self.num_targets-=targets_deleted        if self.inputs[-1].is_linked and self.inputs[-1].bl_idname == 'WildcardSocket':            self.num_targets+=1        self.inputs.clear()        self.inputs.new('DeformerSocket', 'Deformer', )        self.inputs.new('BooleanSocket', 'Use Shape Key', )        self.inputs.new('BooleanSocket', 'Use Offset', )        for i in range(self.num_targets):            self.inputs.new("MorphTargetSocket", "Target."+str(i).zfill(3))            self.inputs.new("FloatSocket", "Value."+str(i).zfill(3))        simple_do_relink(self, input_map, in_out='INPUT')        if self.inputs[-1].bl_idname not in ["WildcardSocket"]:            self.inputs.new('WildcardSocket', '', identifier='__extend__')        self.initialized = True    def update(self):        if self.id_data.is_executing:            return # so that we don't update it while saving/loading the tree        def display_update(self, parsed_tree, context):        if self.inputs["Deformer"].is_linked:            if self.inputs["Deformer"].links[0].from_node.bl_idname not in [self.bl_idname, "NodeGroupInput"]:                self.inputs["Use Shape Key"].default_value = False                self.inputs["Use Shape Key"].hide = True            elif self.inputs["Deformer"].links[0].from_node.inputs["Use Shape Key"].default_value == False:                self.inputs["Use Shape Key"].default_value = False                self.inputs["Use Shape Key"].hide = True        if self.inputs["Use Offset"] == False:                self.inputs["Use Shape Key"].hide = True                self.inputs["Use Shape Key"].default_value = False# TODO: there is no reason for this to be a separate node!class DeformerMorphTarget(Node, DeformerNode):    '''A node representing a single Morph Target'''    bl_idname = 'DeformerMorphTarget'    bl_label = "Morph Target"    bl_icon = 'SHAPEKEY_DATA'    initialized : bpy.props.BoolProperty(default = False)    num_targets : bpy.props.IntProperty(default = 0)    def init(self, context):        self.inputs.new('xFormSocket', "Relative to")        self.inputs.new('xFormSocket', "Object")        self.inputs.new('StringSocket', "Vertex Group")        self.outputs.new('MorphTargetSocket', "Morph Target")        self.initialized = True    
 |