Joseph Brandenburg před 2 měsíci
rodič
revize
e3e187c494
1 změnil soubory, kde provedl 223 přidání a 195 odebrání
  1. 223 195
      i_o.py

+ 223 - 195
i_o.py

@@ -15,6 +15,53 @@ SOCKETS_REMOVED=[("UtilityDriverVariable", "Transform Channel"),
                  ("LinkDrivenParameter", "Enable")]
                   # Node Class           #Prior bl_idname  # prior name # new bl_idname #       new name,          # Multi
 
+
+
+
+# ignore these because they are either unrelated python stuff or useless or borked
+prop_ignore = [ "__dict__", "__doc__", "__module__", "__weakref__",# "name",
+                "bl_height_default", "bl_height_max", "bl_height_min",
+                "bl_icon", "bl_rna", "bl_static_type", "bl_description",
+                "bl_width_default", "bl_width_max", "bl_width_min",
+                "__annotations__", "original", "rna_type", "view_center",
+                "links", "nodes", "internal_links", "inputs", "outputs",
+                "__slots__", "dimensions", "type", "interface",
+                "library_weak_reference", "parsed_tree", "node_tree_updater",
+                "asset_data", "preview",  # blender asset stuff
+                "object_reference", # this one is here to hold on to widgets when appending
+                "color_tag" , # added in blender 4.4, not used by Mantis, readonly.
+                # more blender properties...
+                "bl_use_group_interface", "default_group_node_width", "id_type",
+                # blender runtime stuff
+                "animation_data", "description", "grease_pencil", "is_editable",
+                "is_embedded_data", "is_evaluated", "is_library_indirect", "is_missing",
+                "is_runtime_data", "library", "name_full", "override_library",
+                "session_uid", "tag", "use_extra_user", "use_fake_user", "users",
+                # some Mantis stuff I don't need to save
+                "do_live_update", "is_executing", "is_exporting", "hash", "filepath",
+                "prevent_next_exec", "execution_id", "num_links", "tree_valid",
+                "interface_helper",
+                # node stuff
+                "mantis_node_class_name", "color", "height", "initialized", "select",
+                "show_options", "show_preview", "show_texture", "use_custom_color",
+                "warning_propagation",
+                # these are in Bone
+                "socket_count", "display_bb_settings", "display_def_settings",
+                "display_ik_settings", "display_vp_settings",
+                ] 
+# don't ignore: "bl_idname", "bl_label",
+# ignore the name, it's the dict - key for the node props
+    # no that's stupid don't ignore the name good grief
+
+# I am doing this because these are interactions with other addons that cause problems and probably don't exist for any given user
+prop_ignore.extend(['keymesh'])
+
+# trees
+prop_ignore_tree = prop_ignore.copy()
+prop_ignore_tree.extend(["bl_label", "name"])
+
+
+
 from bpy.app import version
 
 if version >= (4,5,0):
@@ -74,18 +121,15 @@ def fix_custom_parameter(n, property_definition, ):
         return input
 
     return None
-    
-    
 
-def get_socket_data(socket):
+def get_socket_data(socket, ignore_if_default=False):
+    # TODO: don't get stuff in the socket templates
+    # PROBLEM: I don't have easy access to this from the ui class (or mantis class)
     socket_data = {}
     socket_data["name"] = socket.name
     socket_data["bl_idname"] = socket.bl_idname
     socket_data["is_output"] = socket.is_output
     socket_data["is_multi_input"] = socket.is_multi_input
-
-    # if socket.bl_idname == 'TransformSpaceSocket':
-    #     prGreen(socket.default_value)
     
     # here is where we'll handle a socket_data'socket special data
     if socket.bl_idname == "EnumMetaBoneSocket":
@@ -94,216 +138,193 @@ def get_socket_data(socket):
         if sp := socket.get("search_prop"): # may be None
             socket_data["search_prop"] = sp.name # this is an object.
     #
-
-    # v = socket.get("default_value") # this doesn't seem to work, see below
     if hasattr(socket, "default_value"):
-        v = socket.default_value
+        value = socket.default_value
     else:
-        v = None
-    v_type = type(v)
-    if v is None:
-        return socket_data # we don't need to store this.
-    if not is_jsonable(v):
-        v = tuple(v)
-    if not is_jsonable(v):
-        raise RuntimeError(f"Error serializing data in {socket.node.name}::{socket.name} for value of type {v_type}")
-    socket_data["default_value"] = v
+        value = None
+        return socket_data # we don't need to store any more.
+    if not is_jsonable(value): # FIRST try and make a tuple out of it because JSON doesn't like mutables
+        value = tuple(value)
+    if not is_jsonable(value): # now see if it worked and crash out if it didn't
+        raise RuntimeError(f"Error serializing data in {socket.node.name}::{socket.name} for value of type {type(value)}")
+    socket_data["default_value"] = value
+    # TODO TODO implement "ignore if default" feature here
     # at this point we can get the custom parameter ui hints if we want
     if not socket.is_output:
         # try and get this data
-        if v := getattr(socket,'min', None):
-            socket_data["min"] = v
-        if v := getattr(socket,'max', None):
-            socket_data["max"] = v
-        if v := getattr(socket,'soft_min', None):
-            socket_data["soft_min"] = v
-        if v := getattr(socket,'soft_max', None):
-            socket_data["soft_max"] = v
-        if v := getattr(socket,'description', None):
-            socket_data["description"] = v
+        if value := getattr(socket,'min', None):
+            socket_data["min"] = value
+        if value := getattr(socket,'max', None):
+            socket_data["max"] = value
+        if value := getattr(socket,'soft_min', None):
+            socket_data["soft_min"] = value
+        if value := getattr(socket,'soft_max', None):
+            socket_data["soft_max"] = value
+        if value := getattr(socket,'description', None):
+            socket_data["description"] = value
     return socket_data
     #
+def get_node_data(ui_node):
+    # if this is a node-group, force it to update its interface, because it may be messed up.
+    # can remove this HACK when I have stronger guarentees about node-group's keeping the interface
+    from .base_definitions import node_group_update
+    if hasattr(ui_node, "node_tree"):
+        ui_node.is_updating = True
+        try: # HERE BE DANGER
+            node_group_update(ui_node, force=True)
+        finally: # ensure this line is run even if there is an error
+            ui_node.is_updating = False
+    node_props, sockets = {}, {}
+    for propname  in dir(ui_node):
+        value = getattr(ui_node, propname)
+        if propname in ['fake_fcurve_ob']:
+            value=value.name
+        if (propname in prop_ignore) or ( callable(value) ):
+            continue
+        if value.__class__.__name__ in ["Vector", "Color"]:
+            value = tuple(value)
+        if isinstance(value, bpy.types.NodeTree):
+            value = value.name
+        if isinstance(value, bpy.types.bpy_prop_array):
+            value = tuple(value)
+        if propname == "parent" and value:
+            value = value.name
+        if not is_jsonable(value):
+            raise RuntimeError(f"Could not export...  {ui_node.name}, {propname}, {type(value)}")
+        if value is None:
+            continue
+        node_props[propname] = value
+        # so we have to accumulate the parent location because the location is not absolute
+        if propname == "location" and ui_node.parent is not None:
+            location_acc = Vector((0,0))
+            parent = ui_node.parent
+            while (parent):
+                location_acc += parent.location
+                parent = parent.parent
+            location_acc += getattr(ui_node, propname)
+            node_props[propname] = tuple(location_acc)
+            # this works!
+    for i, ui_socket in enumerate(ui_node.inputs):
+        if ui_socket.is_linked: continue # not necessary to save it since it doesn't affect the tree
+        socket = get_socket_data(ui_socket)
+        socket["index"]=i
+        sockets[ui_socket.identifier] = socket
+    for i, ui_socket in enumerate(ui_node.outputs):
+        if ui_socket.is_linked: continue # see above
+        socket = get_socket_data(ui_socket)
+        socket["index"]=i
+        sockets[ui_socket.identifier] = socket
+    node_props["sockets"] = sockets
+    return node_props
+
+def get_tree_data(tree):
+    tree_info = {}
+    for propname  in dir(tree):
+        # if getattr(tree, propname):
+        #     pass
+        if (propname in prop_ignore_tree) or ( callable(getattr(tree, propname)) ):
+            continue
+        v = getattr(tree, propname)
+        if isinstance(getattr(tree, propname), bpy.types.bpy_prop_array):
+            v = tuple(getattr(tree, propname))
+        if not is_jsonable( v  ):
+            raise RuntimeError(f"Not JSON-able: {propname}, type: {type(v)}")
+        tree_info[propname] = v
+    tree_info["name"]=tree.name
+    return tree_info
+
+def get_interface_data(tree, tree_in_out):
+    for sock in tree.interface.items_tree:
+        sock_data={}
+
+        if sock.item_type == 'PANEL':
+            sock_data["name"] = sock.name
+            sock_data["item_type"] = sock.item_type
+            sock_data["description"] = sock.description
+            sock_data["default_closed"] = sock.default_closed
+            tree_in_out[sock.name] = sock_data
+
+        # if it is a socket....
+        else:
+            sock_parent = None
+            if sock.parent:
+                sock_parent = sock.parent.name
+            for propname  in dir(sock):
+                v = getattr(sock, propname)
+                if (propname in prop_ignore) or ( callable(v) ):
+                    continue
+                if (propname == "parent"):
+                    sock_data[propname] = sock_parent
+                    continue
+                if isinstance(getattr(sock, propname), bpy.types.bpy_prop_array):
+                    v = tuple(getattr(sock, propname))
+                if not is_jsonable( v ):
+                    raise RuntimeError(f"{propname}, {type(v)}")
+                sock_data[propname] = v
+            tree_in_out[sock.identifier] = sock_data
+                
 
 def export_to_json(trees, path="", write_file=True, only_selected=False):
-    # ignore these because they are either unrelated python stuff or useless or borked
-    prop_ignore = [ "__dict__", "__doc__", "__module__", "__weakref__",# "name",
-                    "bl_height_default", "bl_height_max", "bl_height_min",
-                    "bl_icon", "bl_rna", "bl_static_type", "bl_description",
-                    "bl_width_default", "bl_width_max", "bl_width_min",
-                    "__annotations__", "original", "rna_type", "view_center",
-                    "links", "nodes", "internal_links", "inputs", "outputs",
-                    "__slots__", "dimensions", "type", "interface",
-                    "library_weak_reference", "parsed_tree", "node_tree_updater",
-                    "asset_data", "preview",  # blender asset stuff
-                    "object_reference", # this one is here to hold on to widgets when appending
-                    "color_tag" , # added in blender 4.4, not used by Mantis, readonly.
-                    ] 
-    # don't ignore: "bl_idname", "bl_label",
-    # ignore the name, it's the dict - key for the node props
-     # no that's stupid don't ignore the name good grief
-
-    # I am doing this because these are interactions with other addons that cause problems and probably don't exist for any given user
-    prop_ignore.extend(['keymesh'])
-
     export_data = {}
     for tree in trees:
-        base_tree = False
+        current_tree_is_base_tree = False
         if tree is trees[-1]:
-            base_tree = True
-
+            current_tree_is_base_tree = True
+        
         tree_info, tree_in_out = {}, {}
-        for propname  in dir(tree):
-            # if getattr(tree, propname):
-            #     pass
-            if (propname in prop_ignore) or ( callable(getattr(tree, propname)) ):
-                continue
-            v = getattr(tree, propname)
-            if isinstance(getattr(tree, propname), bpy.types.bpy_prop_array):
-                v = tuple(getattr(tree, propname))
-            if not is_jsonable( v  ):
-                raise RuntimeError(f"Not JSON-able: {propname}, type: {type(v)}")
-            tree_info[propname] = v
-        tree_info["name"] = tree.name
+        tree_info = get_tree_data(tree)
 
         # if only_selected:
         #     # all in/out links, relative to the selection, should be marked and used to initialize tree properties
-        #     pass
-            
-        
+
         if not only_selected: # we'll handle this later with the links
             for sock in tree.interface.items_tree:
-                sock_data={}
-
-                if sock.item_type == 'PANEL':
-                    sock_data["name"] = sock.name
-                    sock_data["item_type"] = sock.item_type
-                    sock_data["description"] = sock.description
-                    sock_data["default_closed"] = sock.default_closed
-                    tree_in_out[sock.name] = sock_data
-
-                # if it is a socket....
-                else:
-                    sock_parent = None
-                    if sock.parent:
-                        sock_parent = sock.parent.name
-                    for propname  in dir(sock):
-                        if (propname in prop_ignore) or ( callable(v) ):
-                            continue
-                        if (propname == "parent"):
-                            sock_data[propname] = sock_parent
-                            continue
-                        v = getattr(sock, propname)
-                        if isinstance(getattr(sock, propname), bpy.types.bpy_prop_array):
-                            v = tuple(getattr(sock, propname))
-                        if not is_jsonable( v ):
-                            raise RuntimeError(f"{propname}, {type(v)}")
-                        sock_data[propname] = v
-                
-                    tree_in_out[sock.identifier] = sock_data
+                get_interface_data(tree, tree_in_out) # it concerns me that this one modifies
+                #  the collection instead of getting the data and returning it. TODO refactor this
 
         nodes = {}
-        for n in tree.nodes:
-            # if this is a node-group, force it to update its interface, because it may be messed up.
-            # can remove this HACK when I have stronger guarentees about node-group's keeping the interface
-            from .base_definitions import node_group_update
-            if hasattr(n, "node_tree"):
-                n.is_updating = True
-                try: # HERE BE DANGER
-                    node_group_update(n, force=True)
-                finally: # ensure this line is run even if there is an error
-                    n.is_updating = False
-            if only_selected and n.select == False:
+        for node in tree.nodes:
+            if only_selected and node.select == False:
                 continue
-            node_props, sockets = {}, {}
-            for propname  in dir(n):
-                v = getattr(n, propname)
-                if propname in ['fake_fcurve_ob']:
-                    v=v.name
-                if (propname in prop_ignore) or ( callable(v) ):
-                    continue
-                if v.__class__.__name__ in ["Vector", "Color"]:
-                    v = tuple(v)
-                if isinstance(v, bpy.types.NodeTree):
-                    v = v.name
-                if isinstance(v, bpy.types.bpy_prop_array):
-                    v = tuple(v)
-                if propname == "parent" and v:
-                    v = v.name
-                if not is_jsonable(v):
-                    raise RuntimeError(f"Could not export...  {n.name}, {propname}, {type(v)}")
-                if v is None:
-                    continue
-
-                node_props[propname] = v
-
-                # so we have to accumulate the parent location because the location is not absolute
-                if propname == "location" and n.parent is not None:
-                    location_acc = Vector((0,0))
-                    parent = n.parent
-                    while (parent):
-                        location_acc += parent.location
-                        parent = parent.parent
-                    location_acc += getattr(n, propname)
-                    node_props[propname] = tuple(location_acc)
-                    # this works!
-
-            for i, s in enumerate(n.inputs):
-                socket = get_socket_data(s)
-                socket["index"]=i
-                sockets[s.identifier] = socket
-            for i, s in enumerate(n.outputs):
-                socket = get_socket_data(s)
-                socket["index"]=i
-                sockets[s.identifier] = socket
+            nodes[node.name] = get_node_data(node)
             
-            node_props["sockets"] = sockets
-            nodes[n.name] = node_props
-            
-        
         links = []
-
-        in_sockets = {}
-        out_sockets = {}
+        in_sockets, out_sockets = {}, {}
+        unique_sockets_from, unique_sockets_to = {}, {}
 
         in_node = {"name":"MANTIS_AUTOGEN_GROUP_INPUT", "bl_idname":"NodeGroupInput", "sockets":in_sockets}
         out_node = {"name":"MANTIS_AUTOGEN_GROUP_OUTPUT", "bl_idname":"NodeGroupOutput", "sockets":out_sockets}
-
-
         add_input_node, add_output_node = False, False
 
-        unique_sockets_from={}
-        unique_sockets_to={}
-
-
-        for l in tree.links:
-            a, b = l.from_node.name, l.from_socket.identifier
-            c, d = l.to_node.name, l.to_socket.identifier
+        for link in tree.links:
+            from_node_name, from_socket_id = link.from_node.name, link.from_socket.identifier
+            to_node_name, to_socket_id = link.to_node.name, link.to_socket.identifier
+            from_socket_name, to_socket_name = link.from_socket.name, link.to_socket.name
 
             # get the indices of the sockets to be absolutely sure
-            for e, outp in enumerate(l.from_node.outputs):
+            for from_outoput_index, outp in enumerate(link.from_node.outputs):
                 # for some reason, 'is' does not return True no matter what...
                 # so we are gonn compare the memory address directly, this is stupid
-                if (outp.as_pointer() == l.from_socket.as_pointer()): break
+                if (outp.as_pointer() == link.from_socket.as_pointer()): break
             else:
-                problem=l.from_node.name + "::" + l.from_socket.name
+                problem=link.from_node.name + "::" + link.from_socket.name
                 raise RuntimeError(wrapRed(f"Error saving index of socket: {problem}"))
-            for f, inp in enumerate(l.to_node.inputs):
-                if (inp.as_pointer() == l.to_socket.as_pointer()): break
+            for to_input_index, inp in enumerate(link.to_node.inputs):
+                if (inp.as_pointer() == link.to_socket.as_pointer()): break
             else:
-                problem = l.to_node.name + "::" + l.to_socket.name
+                problem = link.to_node.name + "::" + link.to_socket.name
                 raise RuntimeError(wrapRed(f"Error saving index of socket: {problem}"))
-            g, h = l.from_socket.name, l.to_socket.name
 
-            if base_tree:
-                if (only_selected and l.from_node.select) and (not l.to_node.select):
+            if current_tree_is_base_tree:
+                if (only_selected and link.from_node.select) and (not link.to_node.select):
                     # handle an output in the tree
                     add_output_node=True
-                    if not (sock_name := unique_sockets_to.get(l.from_socket.node.name+l.from_socket.identifier)):
-                        sock_name = l.to_socket.name; name_stub = sock_name
+                    if not (sock_name := unique_sockets_to.get(link.from_socket.node.name+link.from_socket.identifier)):
+                        sock_name = link.to_socket.name; name_stub = sock_name
                         used_names = list(tree_in_out.keys()); i=0
                         while sock_name in used_names:
                             sock_name=name_stub+'.'+str(i).zfill(3); i+=1
-                        unique_sockets_to[l.from_socket.node.name+l.from_socket.identifier]=sock_name
+                        unique_sockets_to[link.from_socket.node.name+link.from_socket.identifier]=sock_name
 
                     out_sock = out_sockets.get(sock_name)
                     if not out_sock:
@@ -311,35 +332,35 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                         out_sock["index"]=len(out_sockets) # zero indexed, so zero length makes zero the first index and so on, this works
                     out_sock["name"] = sock_name
                     out_sock["identifier"] = sock_name
-                    out_sock["bl_idname"] = l.to_socket.bl_idname
+                    out_sock["bl_idname"] = link.to_socket.bl_idname
                     out_sock["is_output"] = False
-                    out_sock["source"]=[l.to_socket.node.name,l.to_socket.identifier]
+                    out_sock["source"]=[link.to_socket.node.name,link.to_socket.identifier]
                     out_sock["is_multi_input"] = False # this is not something I can even set on tree interface items, and this code is not intended for making Schema
                     sock_data={}
                     sock_data["name"] = sock_name
                     sock_data["item_type"] = "SOCKET"
                     sock_data["default_closed"] = False
-                    sock_data["socket_type"] = l.from_socket.bl_idname
+                    sock_data["socket_type"] = link.from_socket.bl_idname
                     sock_data["identifier"] = sock_name
                     sock_data["in_out"]="OUTPUT"
                     sock_data["index"]=out_sock["index"]
                     tree_in_out[sock_name] = sock_data
 
-                    c=out_node["name"]
-                    d=out_sock["identifier"]
-                    f=out_sock["index"]
-                    h=out_sock["name"]
+                    to_node_name=out_node["name"]
+                    to_socket_id=out_sock["identifier"]
+                    to_input_index=out_sock["index"]
+                    to_socket_name=out_sock["name"]
 
-                elif (only_selected and (not l.from_node.select)) and l.to_node.select:
+                elif (only_selected and (not link.from_node.select)) and link.to_node.select:
                     add_input_node=True
                     # we need to get a unique name for this
                     # use the Tree IN/Out because we are dealing with Group in/out
-                    if not (sock_name := unique_sockets_from.get(l.from_socket.node.name+l.from_socket.identifier)):
-                        sock_name = l.from_socket.name; name_stub = sock_name
+                    if not (sock_name := unique_sockets_from.get(link.from_socket.node.name+link.from_socket.identifier)):
+                        sock_name = link.from_socket.name; name_stub = sock_name
                         used_names = list(tree_in_out.keys()); i=0
                         while sock_name in used_names:
                             sock_name=name_stub+'.'+str(i).zfill(3); i+=1
-                        unique_sockets_from[l.from_socket.node.name+l.from_socket.identifier]=sock_name
+                        unique_sockets_from[link.from_socket.node.name+link.from_socket.identifier]=sock_name
 
                     in_sock = in_sockets.get(sock_name)
                     if not in_sock:
@@ -348,15 +369,15 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                         #
                         in_sock["name"] = sock_name
                         in_sock["identifier"] = sock_name
-                        in_sock["bl_idname"] = l.from_socket.bl_idname
+                        in_sock["bl_idname"] = link.from_socket.bl_idname
                         in_sock["is_output"] = True
                         in_sock["is_multi_input"] = False # this is not something I can even set on tree interface items, and this code is not intended for making Schema
-                        in_sock["source"] = [l.from_socket.node.name,l.from_socket.identifier]
+                        in_sock["source"] = [link.from_socket.node.name,link.from_socket.identifier]
                         sock_data={}
                         sock_data["name"] = sock_name
                         sock_data["item_type"] = "SOCKET"
                         sock_data["default_closed"] = False
-                        sock_data["socket_type"] = l.from_socket.bl_idname
+                        sock_data["socket_type"] = link.from_socket.bl_idname
                         sock_data["identifier"] = sock_name
                         sock_data["in_out"]="INPUT"
                         sock_data["index"]=in_sock["index"]
@@ -364,16 +385,23 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                         
                         tree_in_out[sock_name] = sock_data
 
-                    a=in_node.get("name")
-                    b=in_sock["identifier"]
-                    e=in_sock["index"]
-                    g=in_node.get("name")
+                    from_node_name=in_node.get("name")
+                    from_socket_id=in_sock["identifier"]
+                    from_outoput_index=in_sock["index"]
+                    from_socket_name=in_node.get("name")
                 # parentheses matter here...
-                elif (only_selected and not (l.from_node.select and l.to_node.select)):
+                elif (only_selected and not (link.from_node.select and link.to_node.select)):
                     continue
-            elif only_selected and not (l.from_node.select and l.to_node.select):
+            elif only_selected and not (link.from_node.select and link.to_node.select):
                 continue # pass if both links are not selected
-            links.append( (a,b,c,d,e,f,g,h) ) # it's a tuple
+            links.append( (from_node_name,
+                           from_socket_id,
+                           to_node_name,
+                           to_socket_id,
+                           from_outoput_index,
+                           to_input_index,
+                           from_socket_name,
+                           to_socket_name) ) # it's a tuple
         
         
         if add_input_node or add_output_node: