Pārlūkot izejas kodu

Sujith :) ->
1. Project data exporter implemented.

Sujith:) 2 nedēļas atpakaļ
vecāks
revīzija
f8f3571006

+ 8 - 0
Assets/ProjectExporter.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 13cfee785129048a5994ea463d486074
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/ProjectExporter/Editor.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f5c0d15f810a1434bbd7d4fe96e53f55
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 171 - 0
Assets/ProjectExporter/Editor/ProjectExporterController.cs

@@ -0,0 +1,171 @@
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace ProjectExporter
+{
+    public class ProjectExporterController
+    {
+        private const string PythonExecutable = "/Users/sujithpudussery/Documents/UnityProjects/terra-llm/venv/bin/python3";
+        private const string ConversionScript = "/Users/sujithpudussery/Documents/UnityProjects/terra-llm/Assets/ProjectExporter/source/convert_scene.py";
+        private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath);
+        private static readonly string OutputDirectory = Path.Combine(ProjectRoot, "Library", "ExportedJSON");
+
+        // Finds assets based on a search filter (e.g., "t:Scene") and builds a hierarchical model.
+        public AssetModel GetAssets(string filter)
+        {
+            var root = new AssetModel("Assets", "Assets");
+            var assetGuids = AssetDatabase.FindAssets(filter, new[] { "Assets" });
+
+            foreach (var guid in assetGuids)
+            {
+                var path = AssetDatabase.GUIDToAssetPath(guid);
+
+                if (filter.Contains("t:ScriptableObject") && !path.EndsWith(".asset"))
+                {
+                    continue;
+                }
+
+                var parts = path.Split('/');
+                var currentNode = root;
+
+                for (int i = 1; i < parts.Length; i++) // Start at 1 to skip "Assets"
+                {
+                    var part = parts[i];
+                    var child = currentNode.Children.FirstOrDefault(c => c.Name == part);
+
+                    if (child == null)
+                    {
+                        var currentPath = string.Join("/", parts.Take(i + 1));
+                        child = new AssetModel(currentPath, part);
+                        currentNode.Children.Add(child);
+                    }
+                    currentNode = child;
+                }
+            }
+            return root;
+        }
+
+        // Lists all files in the ProjectSettings directory.
+        public AssetModel GetProjectSettingsFiles()
+        {
+            var settingsRootPath = Path.Combine(ProjectRoot, "ProjectSettings");
+            var root = new AssetModel(settingsRootPath, "ProjectSettings");
+
+            if (Directory.Exists(settingsRootPath))
+            {
+                // Filter to only include *.asset files.
+                var files = Directory.GetFiles(settingsRootPath, "*.asset", SearchOption.AllDirectories);
+                foreach (var file in files)
+                {
+                    var relativePath = Path.GetRelativePath(ProjectRoot, file);
+                    root.Children.Add(new AssetModel(relativePath, Path.GetFileName(file)));
+                }
+            }
+            return root;
+        }
+
+        // Scans the entire Assets folder for .meta files and builds a hierarchical model.
+        public AssetModel GetAllMetaFiles()
+        {
+            var assetsRootPath = Application.dataPath; // This is the "Assets" folder
+            var root = new AssetModel("Assets", "Assets");
+
+            var files = Directory.GetFiles(assetsRootPath, "*.meta", SearchOption.AllDirectories);
+            
+            foreach (var file in files)
+            {
+                var relativePath = "Assets/" + Path.GetRelativePath(assetsRootPath, file).Replace('\\', '/');
+                var parts = relativePath.Split('/');
+                var currentNode = root;
+
+                for (int i = 1; i < parts.Length; i++) // Start at 1 to skip "Assets"
+                {
+                    var part = parts[i];
+                    var child = currentNode.Children.FirstOrDefault(c => c.Name == part);
+
+                    if (child == null)
+                    {
+                        var currentPath = string.Join("/", parts.Take(i + 1));
+                        child = new AssetModel(currentPath, part);
+                        currentNode.Children.Add(child);
+                    }
+                    currentNode = child;
+                }
+            }
+            return root;
+        }
+
+        // Executes the Python conversion script for the selected assets.
+        public void ExportAssets(AssetModel rootNode)
+        {
+            var assetsToExport = new List<string>();
+            CollectSelectedAssets(rootNode, assetsToExport);
+
+            EditorUtility.DisplayProgressBar("Exporting...", "Starting export process...", 0f);
+
+            try
+            {
+                for (int i = 0; i < assetsToExport.Count; i++)
+                {
+                    var relativeAssetPath = assetsToExport[i];
+                    var progress = (float)i / assetsToExport.Count;
+                    EditorUtility.DisplayProgressBar("Exporting...", $"Processing {Path.GetFileName(relativeAssetPath)}", progress);
+
+                    var absoluteAssetPath = Path.Combine(ProjectRoot, relativeAssetPath);
+                    var outputJsonPath = Path.Combine(OutputDirectory, relativeAssetPath + ".json");
+
+                    var process = new Process
+                    {
+                        StartInfo = new ProcessStartInfo
+                        {
+                            FileName = PythonExecutable,
+                            Arguments = $"\"{ConversionScript}\" \"{absoluteAssetPath}\" \"{outputJsonPath}\"",
+                            RedirectStandardOutput = true,
+                            RedirectStandardError = true,
+                            UseShellExecute = false,
+                            CreateNoWindow = true
+                        }
+                    };
+
+                    process.Start();
+                    string output = process.StandardOutput.ReadToEnd();
+                    string error = process.StandardError.ReadToEnd();
+                    process.WaitForExit();
+
+                    if (process.ExitCode != 0)
+                    {
+                        UnityEngine.Debug.LogError($"Error exporting {relativeAssetPath}: {error}");
+                    }
+                    else
+                    {
+                        UnityEngine.Debug.Log($"Successfully exported {relativeAssetPath}: {output}");
+                    }
+                }
+            }
+            finally
+            {
+                EditorUtility.ClearProgressBar();
+            }
+        }
+
+        // Recursively collects the paths of all selected assets.
+        private void CollectSelectedAssets(AssetModel node, List<string> selectedPaths)
+        {
+            // If the node itself is a file and is selected, add it.
+            if (node.Children.Count == 0 && node.IsSelected)
+            {
+                selectedPaths.Add(node.Path);
+            }
+
+            // Recurse into children
+            foreach (var child in node.Children)
+            {
+                CollectSelectedAssets(child, selectedPaths);
+            }
+        }
+    }
+}

+ 11 - 0
Assets/ProjectExporter/Editor/ProjectExporterController.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 242e5465b41c843b283fb7525b897276
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 22 - 0
Assets/ProjectExporter/Editor/ProjectExporterModel.cs

@@ -0,0 +1,22 @@
+
+using System.Collections.Generic;
+
+namespace ProjectExporter
+{
+    // Represents a single asset (file or folder) that can be exported.
+    public class AssetModel
+    {
+        public string Path { get; }
+        public string Name { get; }
+        public bool IsSelected { get; set; }
+        public List<AssetModel> Children { get; }
+
+        public AssetModel(string path, string name)
+        {
+            Path = path;
+            Name = name;
+            IsSelected = true; // Default to selected
+            Children = new List<AssetModel>();
+        }
+    }
+}

+ 11 - 0
Assets/ProjectExporter/Editor/ProjectExporterModel.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 333668f74450746df8c41b797e590854
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 89 - 0
Assets/ProjectExporter/Editor/ProjectExporterUI.cs

@@ -0,0 +1,89 @@
+
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace ProjectExporter
+{
+    // This class provides static methods to create styled visual elements for the editor window.
+    public static class ProjectExporterUI
+    {
+        // Creates a main container for a tab's content.
+        public static VisualElement CreateMainContainer()
+        {
+            return new VisualElement
+            {
+                style =
+                {
+                    paddingLeft = 10,
+                    paddingRight = 10,
+                    paddingTop = 10,
+                    paddingBottom = 10
+                }
+            };
+        }
+
+        // Creates a tab button.
+        public static Button CreateTab(string label)
+        {
+            var button = new Button
+            {
+                text = label,
+                style =
+                {
+                    flexGrow = 1,
+                    borderBottomWidth = 1,
+                    borderBottomColor = Color.gray,
+                    marginLeft = 0,
+                    marginRight = 0
+                }
+            };
+            return button;
+        }
+
+        // Creates a Foldout element for hierarchical display.
+        public static Foldout CreateFoldout(string text)
+        {
+            var foldout = new Foldout
+            {
+                text = text,
+                value = false, // Start collapsed
+                style =
+                {
+                    marginTop = 5
+                }
+            };
+            return foldout;
+        }
+
+        // Creates a Toggle for selecting an asset.
+        public static Toggle CreateToggle(string label)
+        {
+            var toggle = new Toggle
+            {
+                text = label,
+                style =
+                {
+                    marginTop = 2
+                }
+            };
+            return toggle;
+        }
+
+        // Creates the "Generate" button.
+        public static Button CreateGenerateButton()
+        {
+            var button = new Button
+            {
+                text = "Generate",
+                style =
+                {
+                    height = 30,
+                    marginTop = 20,
+                    backgroundColor = new StyleColor(new Color(0.2f, 0.6f, 0.2f))
+                }
+            };
+            return button;
+        }
+    }
+}

+ 11 - 0
Assets/ProjectExporter/Editor/ProjectExporterUI.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d88a1bacc005c4719a62f28062838177
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 166 - 0
Assets/ProjectExporter/Editor/ProjectExporterWindow.cs

@@ -0,0 +1,166 @@
+
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UIElements;
+using System.Collections.Generic;
+
+namespace ProjectExporter
+{
+    public class ProjectExporterWindow : EditorWindow
+    {
+        private ProjectExporterController _controller;
+        private VisualElement _rootElement;
+        private VisualElement _contentContainer;
+        private Dictionary<string, AssetModel> _assetTrees;
+        private string _currentTab = "Scenes";
+
+        [MenuItem("Tools/Project Exporter")]
+        public static void ShowWindow()
+        {
+            ProjectExporterWindow wnd = GetWindow<ProjectExporterWindow>(true, "Project Exporter");
+            wnd.minSize = new Vector2(400, 300);
+        }
+
+        private void OnEnable()
+        {
+            _controller = new ProjectExporterController();
+            _assetTrees = new Dictionary<string, AssetModel>();
+            _rootElement = rootVisualElement;
+
+            // Build the UI frame without loading asset data initially
+            BuildUI();
+            
+            // Trigger the first content refresh
+            RefreshContent(_currentTab);
+        }
+
+        private void BuildUI()
+        {
+            _rootElement.Clear();
+
+            var tabContainer = new VisualElement { style = { flexDirection = FlexDirection.Row } };
+            tabContainer.Add(ProjectExporterUI.CreateTab("Scenes"));
+            tabContainer.Add(ProjectExporterUI.CreateTab("Prefabs"));
+            tabContainer.Add(ProjectExporterUI.CreateTab("Scriptable Objects"));
+            tabContainer.Add(ProjectExporterUI.CreateTab("Meta Files"));
+            tabContainer.Add(ProjectExporterUI.CreateTab("Settings"));
+            _rootElement.Add(tabContainer);
+
+            // Create a scrollable container for the main content
+            var scrollView = new ScrollView(ScrollViewMode.Vertical);
+            _rootElement.Add(scrollView);
+
+            _contentContainer = ProjectExporterUI.CreateMainContainer();
+            scrollView.Add(_contentContainer);
+
+            var generateButton = ProjectExporterUI.CreateGenerateButton();
+            _rootElement.Add(generateButton);
+            generateButton.clicked += () => _controller.ExportAssets(_assetTrees[_currentTab]);
+
+            var tabs = tabContainer.Query<Button>().ToList();
+            tabs[0].clicked += () => RefreshContent("Scenes");
+            tabs[1].clicked += () => RefreshContent("Prefabs");
+            tabs[2].clicked += () => RefreshContent("Scriptable Objects");
+            tabs[3].clicked += () => RefreshContent("Meta Files");
+            tabs[4].clicked += () => RefreshContent("Settings");
+        }
+
+        private void RefreshContent(string tabName)
+        {
+            _currentTab = tabName;
+            _contentContainer.Clear();
+
+            // Load data on demand if it hasn't been loaded yet
+            if (!_assetTrees.ContainsKey(tabName))
+            {
+                EditorUtility.DisplayProgressBar("Loading...", $"Fetching {tabName}...", 0.5f);
+                try
+                {
+                    switch (tabName)
+                    {
+                        case "Scenes":
+                            _assetTrees[tabName] = _controller.GetAssets("t:Scene");
+                            break;
+                        case "Prefabs":
+                            _assetTrees[tabName] = _controller.GetAssets("t:Prefab");
+                            break;
+                        case "Scriptable Objects":
+                            _assetTrees[tabName] = _controller.GetAssets("t:ScriptableObject");
+                            break;
+                        case "Meta Files":
+                            _assetTrees[tabName] = _controller.GetAllMetaFiles();
+                            break;
+                        case "Settings":
+                            _assetTrees[tabName] = _controller.GetProjectSettingsFiles();
+                            break;
+                    }
+                }
+                finally
+                {
+                    EditorUtility.ClearProgressBar();
+                }
+            }
+
+            var rootModel = _assetTrees[tabName];
+            if (rootModel != null)
+            {
+                foreach (var child in rootModel.Children)
+                {
+                    _contentContainer.Add(CreateAssetView(child, 0));
+                }
+            }
+        }
+
+        private VisualElement CreateAssetView(AssetModel model, int indentLevel)
+        {
+            const int indentWidth = 20;
+
+            if (model.Children.Count > 0)
+            {
+                var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginLeft = indentLevel * indentWidth } };
+                var foldout = ProjectExporterUI.CreateFoldout(model.Name);
+                foldout.style.flexGrow = 1;
+                var toggle = new Toggle { value = model.IsSelected, text = "" };
+                
+                toggle.RegisterValueChangedCallback(evt => {
+                    model.IsSelected = evt.newValue;
+                    SetChildrenSelection(model, evt.newValue);
+                    UpdateChildTogglesUI(foldout, evt.newValue);
+                });
+
+                row.Add(toggle);
+                row.Add(foldout);
+
+                foreach (var child in model.Children)
+                {
+                    foldout.Add(CreateAssetView(child, indentLevel + 1));
+                }
+                return row;
+            }
+            else
+            {
+                var toggle = ProjectExporterUI.CreateToggle(model.Name);
+                toggle.style.marginLeft = (indentLevel + 1) * indentWidth;
+                toggle.value = model.IsSelected;
+                toggle.RegisterValueChangedCallback(evt => model.IsSelected = evt.newValue);
+                return toggle;
+            }
+        }
+        
+        private void UpdateChildTogglesUI(VisualElement container, bool isSelected)
+        {
+            container.Query<Toggle>().ForEach(childToggle => {
+                childToggle.SetValueWithoutNotify(isSelected);
+            });
+        }
+
+        private void SetChildrenSelection(AssetModel parent, bool isSelected)
+        {
+            parent.IsSelected = isSelected;
+            foreach (var child in parent.Children)
+            {
+                SetChildrenSelection(child, isSelected);
+            }
+        }
+    }
+}

+ 11 - 0
Assets/ProjectExporter/Editor/ProjectExporterWindow.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e14552f385e914c11968dc4e50902ce0
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/ProjectExporter/source.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d2fe851f0810b4c2dbec202d566b059d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 82 - 0
Assets/ProjectExporter/source/convert_scene.py

@@ -0,0 +1,82 @@
+import yaml
+import json
+import re
+import sys
+import argparse
+import os
+
+# Regex to capture the type ID and anchor ID from the document separator
+header_pattern = re.compile(r"--- !u!(\d+) &(\S+)")
+# Regex to find and remove the tags for the parser
+tag_remover_pattern = re.compile(r"!u!\d+\s")
+
+def convert_unity_yaml_to_json(yaml_content):
+    """
+    Parses a Unity YAML file string, preserving fileID references, and returns a JSON string.
+    """
+    json_data = []
+    
+    # First, find all the original headers
+    headers = header_pattern.findall(yaml_content)
+    
+    # Next, remove the problematic tags from the content so the parser doesn't fail
+    sanitized_content = tag_remover_pattern.sub("", yaml_content)
+    
+    # Use the standard SafeLoader, as the tags are now gone
+    documents = list(yaml.safe_load_all(sanitized_content))
+
+    # The first document is the file info, which we can often skip if it's empty
+    if documents and isinstance(documents[0], str) and 'YAML' in documents[0]:
+        documents.pop(0)
+
+    if len(headers) != len(documents):
+        print(f"Warning: Mismatch between headers found ({len(headers)}) and documents parsed ({len(documents)}).", file=sys.stderr)
+
+    for i, doc in enumerate(documents):
+        if i < len(headers):
+            type_id, anchor_id = headers[i]
+            
+            structured_doc = {
+                'type_id': type_id,
+                'anchor_id': anchor_id,
+                'data': doc
+            }
+            json_data.append(structured_doc)
+        else:
+            # Append any extra docs without headers (should be rare in Unity files)
+            json_data.append({'data': doc})
+
+    # Use compact encoding for the final JSON
+    return json.dumps(json_data)
+
+def main():
+    parser = argparse.ArgumentParser(description='Convert Unity YAML assets to JSON.')
+    parser.add_argument('input_path', type=str, help='Absolute path to the input Unity asset file.')
+    parser.add_argument('output_path', type=str, help='Absolute path for the output JSON file.')
+    args = parser.parse_args()
+
+    input_path = args.input_path
+    output_path = args.output_path
+
+    try:
+        # Ensure the output directory exists
+        output_dir = os.path.dirname(output_path)
+        if not os.path.exists(output_dir):
+            os.makedirs(output_dir)
+
+        with open(input_path, 'r') as f:
+            content = f.read()
+
+        json_output = convert_unity_yaml_to_json(content)
+
+        with open(output_path, 'w') as f:
+            f.write(json_output)
+
+        print(f"Successfully converted '{input_path}' to '{output_path}'")
+
+    except Exception as e:
+        print(f"An error occurred: {e}", file=sys.stderr)
+        sys.exit(1)
+
+if __name__ == "__main__":
+    main()

+ 7 - 0
Assets/ProjectExporter/source/convert_scene.py.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d4d1da03c3911461da28dd05e42dba99
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: