|
|
@@ -106,7 +106,8 @@ def TellClasses():
|
|
|
return [ MantisExportNodeTreeSaveAs,
|
|
|
MantisExportNodeTreeSave,
|
|
|
MantisExportNodeTree,
|
|
|
- MantisImportNodeTree,
|
|
|
+ MantisImportNodeTree,
|
|
|
+ MantisImportNodeTreeNoMenu,
|
|
|
MantisReloadNodeTree]
|
|
|
|
|
|
# https://stackoverflow.com/questions/42033142/is-there-an-easy-way-to-check-if-an-object-is-json-serializable-in-python - thanks!
|
|
|
@@ -172,48 +173,78 @@ def fix_custom_parameter(n, property_definition, ):
|
|
|
# to see if a dependency is created by node connections.
|
|
|
# TODO it remains to be seen if that is even a desirable behaviour.
|
|
|
def scan_tree_for_objects(base_tree, current_tree):
|
|
|
- from bpy import data
|
|
|
+ # this should work
|
|
|
armatures, curves = set(), set()
|
|
|
- for node in current_tree.nodes:
|
|
|
- match node.bl_idname:
|
|
|
+ if current_tree == base_tree:
|
|
|
+ scan_tree_dependencies(base_tree, curves, armatures,)
|
|
|
+ return (curves, armatures )
|
|
|
+
|
|
|
+def scan_tree_dependencies(base_tree, curves:set, armatures:set, ):
|
|
|
+ from .utilities import check_and_add_root
|
|
|
+ from collections import deque
|
|
|
+ from bpy import context, data
|
|
|
+ xForm_pass = deque()
|
|
|
+ if base_tree.tree_valid:
|
|
|
+ nodes = base_tree.parsed_tree
|
|
|
+ else:
|
|
|
+ base_tree.update_tree(context=context)
|
|
|
+ nodes = base_tree
|
|
|
+ for nc in nodes.values():
|
|
|
+ nc.reset_execution()
|
|
|
+ check_and_add_root(nc, xForm_pass)
|
|
|
+ from .readtree import sort_execution
|
|
|
+ from .utilities import get_node_prototype
|
|
|
+ sorted_nodes, execution_failed = sort_execution(nodes, xForm_pass)
|
|
|
+ if execution_failed:
|
|
|
+ prRed("Error reading dependencies from tree, skipping")
|
|
|
+ return curves, armatures
|
|
|
+ for n in sorted_nodes:
|
|
|
+ if n.ui_signature is None:
|
|
|
+ continue # doesn't matter
|
|
|
+ ui_node = get_node_prototype(n.ui_signature, n.base_tree)
|
|
|
+ if not ui_node:
|
|
|
+ continue
|
|
|
+ # we need to see if it is receiving a Curve
|
|
|
+ # so check the ui_node if it is a socket that takes an object
|
|
|
+ for s in ui_node.inputs:
|
|
|
+ if s.bl_idname in 'EnumCurveSocket':
|
|
|
+ curve_name = n.evaluate_input(s.name)
|
|
|
+ prRed(curve_name)
|
|
|
+ if curve := data.objects.get(curve_name):
|
|
|
+ curves.add(curve)
|
|
|
+ else:
|
|
|
+ raise NotImplementedError(curve_name)
|
|
|
+ match ui_node.bl_idname:
|
|
|
case "UtilityMetaRig":
|
|
|
- if node.inputs[0].is_linked:
|
|
|
- continue
|
|
|
- if (armature := node.inputs[0].search_prop) is not None:
|
|
|
+ armature_name = n.evaluate_input("Meta-Armature")
|
|
|
+ prWhite(armature_name)
|
|
|
+ if armature := data.objects.get(armature_name):
|
|
|
armatures.add(armature)
|
|
|
case "InputExistingGeometryObjectNode":
|
|
|
- if node.inputs["Name"].is_linked:
|
|
|
- continue
|
|
|
- ob_name = node.inputs["Name"].default_value
|
|
|
- if ob := data.objects.get(ob_name):
|
|
|
- if ob.type == "ARMATURE":
|
|
|
- armatures.add(ob)
|
|
|
- elif ob.type == "CURVE":
|
|
|
- curves.add(ob)
|
|
|
- case "xFormArmatureNode":
|
|
|
- if node.inputs["Name"].is_linked:
|
|
|
- continue
|
|
|
- armature_name = node.inputs["Name"].default_value
|
|
|
- armature = data.objects.get(armature_name)
|
|
|
- if armature:
|
|
|
- armatures.add(armature)
|
|
|
+ object_name = n.evaluate_input("Name")
|
|
|
+ prWhite(object_name)
|
|
|
+ if object := data.objects.get(object_name):
|
|
|
+ if object.type == "ARMATURE":
|
|
|
+ armatures.add(object)
|
|
|
+ elif object.type == "CURVE":
|
|
|
+ curves.add(object)
|
|
|
+ # Usually we don't want an object that is generated by the tree
|
|
|
+ # case "xFormArmatureNode":
|
|
|
+ # armature_name = n.evaluate_input("Name")
|
|
|
+ # prWhite(armature_name)
|
|
|
+ # if armature := data.objects.get(armature_name):
|
|
|
+ # armatures.add(armature)
|
|
|
case "xFormGeometryObjectNode":
|
|
|
- if node.inputs["Name"].is_linked:
|
|
|
- continue
|
|
|
- ob_name = node.inputs["Name"].default_value
|
|
|
- if ob := data.objects.get(ob_name):
|
|
|
- if ob.type == "ARMATURE":
|
|
|
- armatures.add(ob)
|
|
|
- elif ob.type == "CURVE":
|
|
|
- curves.add(ob)
|
|
|
- for input in node.inputs:
|
|
|
- if input.bl_idname in ["EnumCurveSocket"]:
|
|
|
- if input.search_prop is not None:
|
|
|
- curves.add(input.search_prop)
|
|
|
- # NOW check the parsed_tree and see if it is possible to find any other
|
|
|
- # objects referred to/provided by the tree
|
|
|
+ object_name = n.evaluate_input("Name")
|
|
|
+ prWhite(object_name)
|
|
|
+ if object := data.objects.get(object_name):
|
|
|
+ if object.type == "ARMATURE":
|
|
|
+ armatures.add(object)
|
|
|
+ elif object.type == "CURVE":
|
|
|
+ curves.add(object)
|
|
|
return (curves, armatures )
|
|
|
|
|
|
+# TODO move these dataclasses into a new file
|
|
|
from dataclasses import dataclass, field, asdict
|
|
|
# some basic classes to define curve point types
|
|
|
@dataclass
|
|
|
@@ -1097,7 +1128,7 @@ def do_import(data, context, search_multi_files=False, filepath=''):
|
|
|
tree.do_live_update = True
|
|
|
|
|
|
|
|
|
-def export_multi_file(trees : list, filepath : str, base_name :str) -> None:
|
|
|
+def export_multi_file(trees : list, base_tree, filepath : str, base_name :str) -> None:
|
|
|
for t in trees:
|
|
|
# this should name them the name of the tree...
|
|
|
from bpy.path import native_pathsep, clean_name
|
|
|
@@ -1105,7 +1136,7 @@ def export_multi_file(trees : list, filepath : str, base_name :str) -> None:
|
|
|
from os import mkdir
|
|
|
native_filepath = native_pathsep(filepath)
|
|
|
directory = os_path.split(native_filepath)[0]
|
|
|
- export_data = export_to_json([t], os_path.join(directory,
|
|
|
+ export_data = export_to_json([t], base_tree, os_path.join(directory,
|
|
|
clean_name(t.name)+'.rig'))
|
|
|
write_json_data(export_data, os_path.join(directory,
|
|
|
clean_name(t.name)+'.rig'))
|
|
|
@@ -1158,7 +1189,7 @@ class MantisExportNodeTreeSaveAs(Operator, ExportHelper):
|
|
|
export_data = export_to_json(trees, base_tree, self.filepath)
|
|
|
write_json_data(export_data, self.filepath)
|
|
|
else:
|
|
|
- export_multi_file(trees, self.filepath, base_tree.name)
|
|
|
+ export_multi_file(trees, base_tree, self.filepath, base_tree.name)
|
|
|
base_tree.is_exporting = False
|
|
|
base_tree.prevent_next_exec = True
|
|
|
# set the properties on the base tree for re-exporting with alt-s
|
|
|
@@ -1186,10 +1217,10 @@ class MantisExportNodeTreeSave(Operator):
|
|
|
prGreen ("Node graph: \"%s\"" % (t.name))
|
|
|
base_tree.is_exporting = True
|
|
|
if base_tree.export_all_subtrees_together:
|
|
|
- export_data = export_to_json(trees, filepath)
|
|
|
+ export_data = export_to_json(trees, base_tree, filepath)
|
|
|
write_json_data(export_data, filepath)
|
|
|
else:
|
|
|
- export_multi_file(trees, filepath, base_tree.name)
|
|
|
+ export_multi_file(trees, base_tree, filepath, base_tree.name)
|
|
|
base_tree.is_exporting = False
|
|
|
base_tree.prevent_next_exec = True
|
|
|
return {'FINISHED'}
|
|
|
@@ -1244,6 +1275,17 @@ class MantisImportNodeTree(Operator, ImportHelper):
|
|
|
return do_import_from_file(self.filepath, context)
|
|
|
|
|
|
|
|
|
+
|
|
|
+class MantisImportNodeTreeNoMenu(Operator):
|
|
|
+ """Import a Mantis Node Tree."""
|
|
|
+ bl_idname = "mantis.import_tree_no_menu"
|
|
|
+ bl_label = "Import Mantis Tree (.rig)"
|
|
|
+
|
|
|
+ filepath : StringProperty()
|
|
|
+
|
|
|
+ def execute(self, context):
|
|
|
+ return do_import_from_file(self.filepath, context)
|
|
|
+
|
|
|
# this is useful:
|
|
|
# https://blender.stackexchange.com/questions/73286/how-to-call-a-confirmation-dialog-box
|
|
|
|