소스 검색

Sujith :) ->
1. Implemented mid level processing

Sujith:) 13 시간 전
부모
커밋
fbefeedc6d

+ 427 - 0
Assets/LLM/source/SceneParsing.md

@@ -0,0 +1,427 @@
+
+Use case 1:
+Link between gameobjects in scene
+
+--- !u!1 &330585543
+GameObject:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+serializedVersion: 6
+m_Component:
+- component: {fileID: 330585546} -> Usually the first one is transform
+- component: {fileID: 330585545} -> Any component that is not transform
+- component: {fileID: 330585544} -> Any component that is not transform
+- component: {fileID: 330585547} -> Any component that is not transform
+m_Layer: 0
+m_Name: Main Camera
+m_TagString: MainCamera
+m_Icon: {fileID: 0}
+m_NavMeshLayer: 0
+m_StaticEditorFlags: 0
+m_IsActive: 1
+
+Transform data:
+
+--- !u!4 &330585546
+Transform:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+m_GameObject: {fileID: 330585543}
+m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+m_LocalPosition: {x: 0, y: 1, z: -10}
+m_LocalScale: {x: 1, y: 1, z: 1}
+m_ConstrainProportionsScale: 0
+m_Children:
+- {fileID: 1429936258} -> This refers to the first child's transform
+m_Father: {fileID: 0}
+m_RootOrder: 0
+m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+
+--- !u!4 &1429936258
+Transform:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+m_GameObject: {fileID: 1429936256} -> This refers to the transform's gameobject data reference
+m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+m_LocalPosition: {x: 0.2898221, y: -23.73114, z: -48.733814}
+m_LocalScale: {x: 1, y: 1, z: 1}
+m_ConstrainProportionsScale: 0
+m_Children: []
+m_Father: {fileID: 330585546}
+m_RootOrder: 0
+m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+
+--- !u!1 &1429936256
+GameObject:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+serializedVersion: 6
+m_Component:
+- component: {fileID: 1429936258} 
+- component: {fileID: 1429936257}
+- component: {fileID: 1429936259}
+m_Layer: 0
+m_Name: MEEE
+m_TagString: Untagged
+m_Icon: {fileID: 0}
+m_NavMeshLayer: 0
+m_StaticEditorFlags: 0
+m_IsActive: 1
+
+Now, we have a link between the gameobject and the transform and their children. So for game object the children link are found using the transform data.
+So the way to traverse is: GameObject -> Transform -> Children -> <For Each Child> -> Transform -> GameObject, now we append the gameobject1 child info with the gameobject2. This is the way to traverse the hierarchy to find the parent-child relationship between scene objects.
+
+
+Use case 2:
+
+Prefab in root, with no children & no overrides:
+
+--- !u!1001 &1482527665
+PrefabInstance:
+m_ObjectHideFlags: 0
+serializedVersion: 2
+m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    m_RemovedComponents: []
+m_SourcePrefab: {fileID: 100100000, guid: 61c90d4029cba460eaac555f5cfa2f97, type: 3}
+
+
+There are no additional artifacts that unity generates, so in this use case, since the 'm_TransformParent' is 0, the prefab is in the root of the scene, with no custom scene objects as children + no overrides.
+
+
+Use case 3:
+
+Prefab in root with overrides:
+
+--- !u!1001 &1482527665
+PrefabInstance:
+m_ObjectHideFlags: 0
+serializedVersion: 2
+m_Modification:
+m_SourcePrefab: {fileID: 100100000, guid: 61c90d4029cba460eaac555f5cfa2f97, type: 3}
+--- !u!1 &1482527666 stripped
+GameObject:
+m_CorrespondingSourceObject: {fileID: 5118133154516674719, guid: 61c90d4029cba460eaac555f5cfa2f97,
+type: 3}
+m_PrefabInstance: {fileID: 1482527665}
+m_PrefabAsset: {fileID: 0}
+--- !u!65 &1482527667
+BoxCollider:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+m_GameObject: {fileID: 1482527666}
+m_Material: {fileID: 0}
+m_IsTrigger: 0
+m_Enabled: 1
+serializedVersion: 2
+m_Size: {x: 1, y: 1, z: 1}
+m_Center: {x: 0, y: 0, z: 0}
+
+Here in this use case, prefab has a override of a box collider, in that case unity would generate a ghost gameobject with prefab instance reference, where the box collider would have reference of gameobject.
+The way to note this is to identify if the gameobject data is stripped, then ideally just assume that the gameobject is a ghost created for prefab. It wouldn't have components list of identify the components that are attached on this prefab.
+So, we iterate through components which have m_GameObject and identify gameobject, and then implicitly see if the gameobject is stripped, then we identify its prefab instance.
+
+Use case 4:
+
+Prefab with a scene object as child:
+
+--- !u!1001 &1482527665
+PrefabInstance:
+m_ObjectHideFlags: 0
+serializedVersion: 2
+m_Modification:
+m_TransformParent: {fileID: 0}
+m_RemovedComponents: []
+m_SourcePrefab: {fileID: 100100000, guid: 61c90d4029cba460eaac555f5cfa2f97, type: 3}
+--- !u!4 &1482527666 stripped
+Transform:
+m_CorrespondingSourceObject: {fileID: 7908724477659422290, guid: 61c90d4029cba460eaac555f5cfa2f97,
+type: 3}
+m_PrefabInstance: {fileID: 1482527665}
+m_PrefabAsset: {fileID: 0}
+--- !u!1 &309364077
+GameObject:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+serializedVersion: 6
+m_Component:
+- component: {fileID: 309364078}
+- component: {fileID: 309364081}
+- component: {fileID: 309364080}
+- component: {fileID: 309364079}
+  m_Layer: 0
+  m_Name: Cube
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &309364078
+Transform:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+m_GameObject: {fileID: 309364077}
+m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+m_LocalPosition: {x: 0, y: 0, z: 0}
+m_LocalScale: {x: 1, y: 1, z: 1}
+m_ConstrainProportionsScale: 1
+m_Children: []
+m_Father: {fileID: 1482527666}
+m_RootOrder: 0
+m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+
+Here, the prefab has a child which is a scene object called cube. Here note, a ghost transform is generated against the prefab. The way the link works is the Cube (GameObject) -> Cube (Transform) ->  Check m_Father property -> Find related transform -> See if the transform property is stripped -> If stripped, then it is a ghost.
+Since we identified it is a ghost, we find the transform's 'm_PrefabInstance' and identify what is the prefab instance that this transform is related to. Now due to this, we can identify that this is a prefab with a scene object (Cube) as child.
+
+Use case 5:
+
+Prefab with a prefab as child:
+
+--- !u!1001 &1482527665
+PrefabInstance:
+m_ObjectHideFlags: 0
+serializedVersion: 2
+m_Modification:
+m_TransformParent: {fileID: 0}
+m_RemovedComponents: []
+m_SourcePrefab: {fileID: 100100000, guid: 61c90d4029cba460eaac555f5cfa2f97, type: 3}
+--- !u!4 &1482527666 stripped
+Transform:
+m_CorrespondingSourceObject: {fileID: 7908724477659422290, guid: 61c90d4029cba460eaac555f5cfa2f97, type: 3}
+m_PrefabInstance: {fileID: 1482527665}
+m_PrefabAsset: {fileID: 0}
+--- !u!1001 &617805067
+PrefabInstance:
+m_ObjectHideFlags: 0
+serializedVersion: 2
+m_Modification:
+m_TransformParent: {fileID: 1482527666}
+m_RemovedComponents: []
+m_SourcePrefab: {fileID: 100100000, guid: 7cf72a57738ed4c1e993c0388fa836d4, type: 3}
+
+Here one prefab instance has reference of another prefab instance. Here, the way we identify the relationship is by finding the 'm_TransformParent' property. If it is not 0, then it means the prefab has a parent.
+If we check the parent, which is linked to a stripped transform, we know that the transform is related to a prefab. We can then find the prefab instance. This is the way to traverse the prefab hierarchy.
+
+
+Use case 6:
+
+Prefab with scene object as parent:
+
+--- !u!1001 &1482527665
+PrefabInstance:
+m_ObjectHideFlags: 0
+serializedVersion: 2
+m_Modification:
+m_TransformParent: {fileID: 491276561}
+m_Modifications:
+m_RemovedComponents: []
+m_SourcePrefab: {fileID: 100100000, guid: 61c90d4029cba460eaac555f5cfa2f97, type: 3}
+--- !u!4 &1482527666 stripped
+Transform:
+m_CorrespondingSourceObject: {fileID: 7908724477659422290, guid: 61c90d4029cba460eaac555f5cfa2f97,
+type: 3}
+m_PrefabInstance: {fileID: 1482527665}
+m_PrefabAsset: {fileID: 0}
+--- !u!1 &491276560
+GameObject:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+serializedVersion: 6
+m_Component:
+- component: {fileID: 491276561}
+  m_Layer: 0
+  m_Name: Sala
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+  --- !u!4 &491276561
+  Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 491276560}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children:
+- {fileID: 1482527666}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+
+Here while parsing through gameobject, if we identify, the trasform has a child, and that child's transform is stripped, that means, the transform is related to a prefab. We can then find the prefab instance. This is the way to traverse the prefab hierarchy. 
+
+
+Use case 7:
+
+Prefab with the scene object as child for one of the prefab's children:
+
+--- !u!1 &5604345
+GameObject:
+m_ObjectHideFlags: 0
+m_CorrespondingSourceObject: {fileID: 0}
+m_PrefabInstance: {fileID: 0}
+m_PrefabAsset: {fileID: 0}
+serializedVersion: 6
+m_Component:
+- component: {fileID: 5604346}
+  m_Layer: 0
+  m_Name: PEACE
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+  --- !u!4 &5604346
+  Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5604345}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 920412709}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  --- !u!1001 &920412706
+  PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+  m_TransformParent: {fileID: 0}
+  m_Modifications:
+  m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 874028b39d9764ee997aa13686619919, type: 3}
+  --- !u!4 &920412709 stripped
+  Transform:
+  m_CorrespondingSourceObject: {fileID: 8145956705230478064, guid: 874028b39d9764ee997aa13686619919,
+  type: 3}
+  m_PrefabInstance: {fileID: 920412706}
+  m_PrefabAsset: {fileID: 0}
+
+To identify the parent child relationship, where the father of a scene object is prefab and that object can be 'n'th child of the prefab, we use the following steps:
+
+Step 1: Find "PEACE" and its Parent in SampleScene.unity
+
+First, we locate the "PEACE" GameObject in the scene file to find its direct parent.
+
+1. We search for m_Name: PEACE. This leads us to the GameObject with ID &5604345.
+2. This GameObject has one component, a Transform with fileID: 5604346.
+3. We examine the Transform component with ID &5604346:
+
+--- !u!4 &5604346
+ Transform:
+   # ...
+   m_GameObject: {fileID: 5604345}
+   m_Father: {fileID: 920412709}
+   m_RootOrder: 0
+     - Parent ID: The m_Father is 920412709.
+     - Sibling Index: The m_RootOrder is 0.
+
+This gives us the last number in our sequence: (?-?-0).
+
+Step 2: Identify the Parent (920412709) in SampleScene.unity
+
+Now, we look for the object with ID 920412709 in the scene file.
+
+--- !u!4 &920412709 stripped
+Transform:
+m_CorrespondingSourceObject: {fileID: 8145956705230478064, guid: 874028b39d9764ee997aa13686619919, type: 3}
+m_PrefabInstance: {fileID: 920412706}
+m_PrefabAsset: {fileID: 0}
+
+- The stripped keyword tells us this Transform is part of a prefab instance. Its properties are not defined here.
+- To find its real properties (like its parent and name), we must look inside the source prefab.
+- The m_CorrespondingSourceObject gives us the fileID of the original Transform inside the prefab file: 8145956705230478064.
+
+Step 3: Find the Corresponding Transform in Parent.prefab
+
+We now switch to the Parent.prefab file and search for the Transform with ID &8145956705230478064.
+
+1. Locating &8145956705230478064:
+
+--- !u!4 &8145956705230478064
+Transform:
+  # ...
+  m_GameObject: {fileID: 1790231484505338027}
+  m_Father: {fileID: 1455291841571425918}
+  m_RootOrder: 0
+
+2. We can now identify this object and its parent:
+  - Its m_GameObject is 1790231484505338027. Looking up this GameObject reveals its name: m_Name: SpawnPoint. So, 920412709 in the scene is the "SpawnPoint" Transform.
+  - Parent ID: The m_Father is 1455291841571425918.
+  - Sibling Index: The m_RootOrder is 0.
+
+This gives us the second number in our sequence: (?-0-0).
+
+Step 4: Find the Next Parent (1455291841571425918) in Parent.prefab
+
+We continue up the hierarchy within the prefab file, looking for the Transform with ID &1455291841571425918.
+
+1. Locating &1455291841571425918:
+
+--- !u!4 &1455291841571425918
+Transform:
+  # ...
+  m_GameObject: {fileID: 8435291834557686729}
+  m_Father: {fileID: 6729460392160249717}
+  m_RootOrder: 0
+
+2. We identify this object and its parent:
+  - Its m_GameObject is 8435291834557686729. Looking up this GameObject reveals its name: m_Name: Launcher.
+  - Parent ID: The m_Father is 6729460392160249717.
+  - Sibling Index: The m_RootOrder is 0.
+
+This gives us the first number in our sequence: (0-0-0).
+
+Final Verification: The Prefab Root
+
+To be certain, we can check the final parent, Transform &6729460392160249717.
+
+--- !u!4 &6729460392160249717
+Transform:
+# ...
+m_GameObject: {fileID: 5669290172027642543}
+m_Father: {fileID: 0}
+m_RootOrder: 0
+
+Its m_Father is {fileID: 0}, which signifies that it is the root Transform of the prefab, as it has no parent. Its GameObject (5669290172027642543) is named "Parent".
+
+By parsing the files and following the m_Father and m_RootOrder references, we have programmatically confirmed that the deep sibling index of the "PEACE" GameObject is indeed (0-0-0).
+
+
+Conclusion:
+
+So essentially, it is beneficial to resolve the parent-child relationship between normal game objects in the scene using m_Parent logic.
+Whenever, we deal with a prefab instance, we create a relationship of child-parent between the prefab instance and it's found normal scene object parent. The transforms can be stripped out of the prefab instances in the final output, since the relationship is already resolved.
+In instances where a scene object has a prefab as a parent, we add the deep sibling index json property, which exactly tells where does the scene object exist in the prefab hierarchy within scene.
+So irrespective of which sibling index's child the scene object is, we add it always to the root of the prefab data object and add children with the deep sibling index data.
+Finally, one more pass of run through in the left over data, to see if any gameobject is missed out, then they are marked as orphans and exported.
+Now, since this is done, we append the components name to the component section of every gameobject that has been identified and created a virtual tree out of.
+Ultimately, we parse through our collated data, and find the m_RootObjects in the root objects found and sort the objects in root based on them.
+Note: When extracting information from prefab's modification, check if it has value of 'm_IsActive' in 'm_Modifications' inside m_Modification object of PrefabInstance, we need only this value and 'm_RemovedComponents' property to be exported from the prefab instance.

+ 3 - 0
Assets/LLM/source/SceneParsing.md.meta

@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 20db35d0017e4224914d3cec1b9ee7db
+timeCreated: 1754370002

+ 8 - 0
Assets/LLM/source/__pycache__.meta

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

BIN
Assets/LLM/source/__pycache__/scene_processor.cpython-313.pyc


+ 7 - 0
Assets/LLM/source/__pycache__/scene_processor.cpython-313.pyc.meta

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

+ 55 - 38
Assets/LLM/source/extract_mid_level.py

@@ -2,20 +2,20 @@ import argparse
 import sys
 import json
 import shutil
+import subprocess
 from pathlib import Path
 
 # Add the utils directory to the Python path
 utils_path = Path(__file__).parent / 'utils'
 sys.path.append(str(utils_path))
 
-from file_utils import replicate_directory_structure, find_files_by_extension
-from virtual_tree_builder import build_virtual_tree
+from file_utils import replicate_directory_structure, find_files_by_extension, create_guid_to_path_map
 
 def copy_scripts(assets_dir, output_assets_dir):
     """
     Copies all C# scripts (.cs) to the target directory.
     """
-    print("\n--- Starting Task 3: Script Handling ---")
+    print("\n--- Starting Script Handling ---")
     cs_files = find_files_by_extension(str(assets_dir), '.cs')
     print(f"Found {len(cs_files)} C# script files to copy.")
 
@@ -24,7 +24,6 @@ def copy_scripts(assets_dir, output_assets_dir):
         relative_path = script_path.relative_to(assets_dir)
         destination_path = output_assets_dir / relative_path
         
-        # Ensure the destination directory exists
         destination_path.parent.mkdir(parents=True, exist_ok=True)
         
         try:
@@ -37,6 +36,7 @@ def copy_scripts(assets_dir, output_assets_dir):
 def main():
     """
     Main function to run the mid-level data extraction process.
+    This script orchestrates the parsing of scene and prefab files.
     """
     parser = argparse.ArgumentParser(
         description="Generates a virtual representation of the project's structure."
@@ -62,7 +62,7 @@ def main():
         print(f"Error: Input path '{input_dir}' is not a valid directory.", file=sys.stderr)
         sys.exit(1)
 
-    # --- Task 1: Create output folder and replicate structure ---
+    # --- Setup Output Directories ---
     mid_level_output_dir = output_dir / "MidLevel"
     output_assets_dir = mid_level_output_dir / "Assets"
     try:
@@ -73,52 +73,69 @@ def main():
         sys.exit(1)
 
     assets_dir = input_dir / "Assets"
-    if assets_dir.is_dir():
-        print(f"\n--- Replicating 'Assets' directory structure ---")
-        replicate_directory_structure(str(assets_dir), str(output_assets_dir))
-        print("Directory structure replication complete.")
-    else:
-        print(f"Warning: 'Assets' directory not found in '{input_dir}'. Skipping structure replication.", file=sys.stderr)
-        return # Exit if there's no Assets directory
-
-    # --- Task 3: Script Handling ---
+    if not assets_dir.is_dir():
+        print(f"Warning: 'Assets' directory not found in '{input_dir}'. Skipping all processing.", file=sys.stderr)
+        return
+
+    # --- Task 1: Replicate 'Assets' directory structure ---
+    print(f"\n--- Replicating 'Assets' directory structure ---")
+    replicate_directory_structure(str(assets_dir), str(output_assets_dir))
+    print("Directory structure replication complete.")
+
+    # --- Task 2: Copy C# Scripts ---
     copy_scripts(assets_dir, output_assets_dir)
 
-    # --- Task 4: Orchestration ---
-    print("\n--- Starting Task 4: Orchestration ---")
+    # --- Task 3: Generate GUID Map ---
+    print("\n--- Generating GUID Map ---")
+    guid_map = create_guid_to_path_map(str(input_dir))
+    guid_map_path = mid_level_output_dir / "guid_map.json"
+    try:
+        with open(guid_map_path, 'w', encoding='utf-8') as f:
+            json.dump(guid_map, f)
+        print(f"Successfully created GUID map: {guid_map_path}")
+    except IOError as e:
+        print(f"Error writing GUID map: {e}", file=sys.stderr)
+        sys.exit(1)
+
+    # --- Task 4: Orchestrate Scene and Prefab Parsing ---
+    print("\n--- Starting Scene/Prefab Parsing Orchestration ---")
     
-    # Find all scene and prefab files
     scene_files = find_files_by_extension(str(assets_dir), '.unity')
     prefab_files = find_files_by_extension(str(assets_dir), '.prefab')
     files_to_process = scene_files + prefab_files
     
     print(f"Found {len(files_to_process)} scene/prefab files to process.")
 
+    script_path = Path(__file__).parent / "scene_processor.py"
+
     for file_path_str in files_to_process:
         file_path = Path(file_path_str)
-        print(f"\nProcessing: {file_path.name}")
         
-        # Generate the virtual tree
-        virtual_tree = build_virtual_tree(str(file_path))
+        relative_path = file_path.relative_to(assets_dir)
+        output_path = output_assets_dir / relative_path
+        output_path = output_path.with_suffix('.json')
         
-        if virtual_tree:
-            # Construct the output path
-            relative_path = file_path.relative_to(assets_dir)
-            output_path = output_assets_dir / relative_path
-            output_path = output_path.with_suffix('.json')
-            
-            # Create parent directories if they don't exist
-            output_path.parent.mkdir(parents=True, exist_ok=True)
-            
-            # Save the JSON output
-            try:
-                with open(output_path, 'w', encoding='utf-8') as f:
-                    json.dump(virtual_tree, f, separators=(',', ':'))
-                print(f"Successfully created: {output_path}")
-            except IOError as e:
-                print(f"Error writing to {output_path}: {e}", file=sys.stderr)
-        else:
-            print(f"Skipped {file_path.name} as virtual tree generation failed.")
+        command = [
+            sys.executable,
+            str(script_path),
+            "--input",
+            str(file_path),
+            "--guid-map",
+            str(guid_map_path),
+            "--output",
+            str(output_path)
+        ]
+        
+        try:
+            print(f"\n--- Calling processor for: {file_path.name} ---")
+            subprocess.run(command, check=True, text=True)
+        except subprocess.CalledProcessError as e:
+            print(f"Error processing {file_path.name}. Subprocess failed with exit code {e.returncode}", file=sys.stderr)
+            print(f"Stderr: {e.stderr}", file=sys.stderr)
+            print(f"Stdout: {e.stdout}", file=sys.stderr)
+        except FileNotFoundError:
+            print(f"Error: Could not find the 'scene_processor.py' script at '{script_path}'", file=sys.stderr)
+            sys.exit(1)
 
     print("\nMid-level extraction complete.")
 

+ 603 - 0
Assets/LLM/source/scene_processor.py

@@ -0,0 +1,603 @@
+import argparse
+import sys
+import json
+from pathlib import Path
+
+# Add the utils directory to the Python path
+utils_path = Path(__file__).parent / 'utils'
+sys.path.append(str(utils_path))
+
+from yaml_utils import load_unity_yaml, convert_to_plain_python_types
+
+class UnitySceneProcessor:
+    def __init__(self, guid_map):
+        self.guid_map = guid_map
+        self.object_map = {}
+        self.nodes = {}
+        self.prefab_nodes = {}
+        self.transform_to_gameobject = {}
+        self.gameobject_to_transform = {}
+        self.transform_children = {}
+        self.stripped_gameobjects = {}
+        self.processed_relationships = set()
+
+    def load_prefab_data(self, prefab_guid):
+        """Load and parse prefab data from GUID"""
+        if not prefab_guid or prefab_guid not in self.guid_map:
+            return {}
+        
+        prefab_path = self.guid_map[prefab_guid]
+        try:
+            documents = load_unity_yaml(prefab_path)
+            if not documents:
+                return {}
+            
+            raw_object_map = {int(doc.anchor.value): doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None}
+            return {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()}
+        except Exception:
+            return {}
+
+    def calculate_deep_sibling_index(self, scene_transform_id, prefab_guid):
+        """Calculate deep sibling index for scene objects that are children of prefab objects"""
+        scene_transform = self.object_map.get(scene_transform_id, {}).get('Transform', {})
+        scene_root_order = scene_transform.get('m_RootOrder', 0)
+        
+        # Get parent transform (should be stripped)
+        parent_transform_id = scene_transform.get('m_Father', {}).get('fileID')
+        if not parent_transform_id:
+            return str(scene_root_order)
+        
+        parent_transform = self.object_map.get(parent_transform_id, {}).get('Transform', {})
+        corresponding_source = parent_transform.get('m_CorrespondingSourceObject', {})
+        prefab_transform_id = corresponding_source.get('fileID')
+        
+        if not prefab_transform_id:
+            return str(scene_root_order)
+        
+        # Load prefab data and traverse hierarchy
+        prefab_data = self.load_prefab_data(prefab_guid)
+        if not prefab_data:
+            return str(scene_root_order)
+        
+        # Build sibling index by traversing up the prefab hierarchy
+        sibling_indices = []
+        current_transform_id = prefab_transform_id
+        
+        while current_transform_id and current_transform_id in prefab_data:
+            transform_data = prefab_data.get(current_transform_id, {}).get('Transform', {})
+            if not transform_data:
+                break
+            
+            root_order = transform_data.get('m_RootOrder', 0)
+            sibling_indices.insert(0, str(root_order))
+            
+            # Move to parent
+            parent_id = transform_data.get('m_Father', {}).get('fileID')
+            if not parent_id or parent_id == 0:
+                break
+            current_transform_id = parent_id
+        
+        # Add the scene object's own root order at the end
+        sibling_indices.append(str(scene_root_order))
+        return '-'.join(sibling_indices)
+
+    def process_first_pass(self):
+        """First pass: Build relationship maps and create basic nodes"""
+        stripped_transforms = {}
+        stripped_gameobjects = {}
+        prefab_instances = {}
+        
+        for file_id, obj_data in self.object_map.items():
+            if 'GameObject' in obj_data:
+                go_info = obj_data['GameObject']
+                
+                # Always create a node for a GameObject.
+                # If it's part of a prefab, we'll link it later.
+                self.nodes[file_id] = {
+                    'fileID': str(file_id),
+                    'm_Name': go_info.get('m_Name', 'Unknown'),
+                    'm_IsActive': go_info.get('m_IsActive', 1),
+                    'm_TagString': go_info.get('m_TagString', 'Untagged'),
+                    'm_Layer': go_info.get('m_Layer', 0),
+                    'components': [],
+                    'children': []
+                }
+                
+                # If it's a stripped GameObject, track it for component linking
+                is_stripped = any('stripped' in str(key) for key in obj_data.keys() if hasattr(key, '__str__'))
+                if is_stripped:
+                    prefab_instance_id = go_info.get('m_PrefabInstance', {}).get('fileID')
+                    if prefab_instance_id:
+                        stripped_gameobjects[file_id] = {
+                            'prefab_instance_id': prefab_instance_id,
+                            'm_CorrespondingSourceObject': go_info.get('m_CorrespondingSourceObject', {})
+                        }
+                
+                # If it's the root GameObject of a prefab instance, add a link for merging
+                prefab_instance_id = go_info.get('m_PrefabInstance', {}).get('fileID')
+                if prefab_instance_id and not is_stripped:
+                    self.nodes[file_id]['prefab_instance_id'] = prefab_instance_id
+                
+            elif 'PrefabInstance' in obj_data:
+                prefab_info = obj_data['PrefabInstance']
+                source_prefab = prefab_info.get('m_SourcePrefab', {})
+                modifications = prefab_info.get('m_Modification', {})
+                
+                # Extract m_IsActive, m_Name, and m_RootOrder from modifications
+                m_is_active = 1  # default
+                m_name = None
+                m_root_order = 999999 # default
+                for mod in modifications.get('m_Modifications', []):
+                    if mod.get('propertyPath') == 'm_IsActive':
+                        m_is_active = mod.get('value', 1)
+                    elif mod.get('propertyPath') == 'm_Name':
+                        m_name = mod.get('value')
+                    elif mod.get('propertyPath') == 'm_RootOrder':
+                        m_root_order = mod.get('value', 999999)
+
+                prefab_instances[file_id] = {
+                    'source_prefab_guid': source_prefab.get('guid'),
+                    'm_TransformParent': modifications.get('m_TransformParent', {}).get('fileID'),
+                    'm_IsActive': m_is_active,
+                    'm_Name': m_name,
+                    'm_RootOrder': m_root_order
+                }
+                
+            elif 'Transform' in obj_data:
+                transform_info = obj_data['Transform']
+                
+                # Check if this is a stripped transform (has m_PrefabInstance)
+                prefab_instance_id = transform_info.get('m_PrefabInstance', {}).get('fileID')
+                if prefab_instance_id:
+                    stripped_transforms[file_id] = {
+                        'prefab_instance_id': prefab_instance_id,
+                        'm_CorrespondingSourceObject': transform_info.get('m_CorrespondingSourceObject', {})
+                    }
+                
+                # Build transform mappings
+                gameobject_id = transform_info.get('m_GameObject', {}).get('fileID')
+                if gameobject_id:
+                    self.transform_to_gameobject[file_id] = gameobject_id
+                    self.gameobject_to_transform[gameobject_id] = file_id
+                
+                # Build parent-child relationships
+                parent_id = transform_info.get('m_Father', {}).get('fileID')
+                if parent_id and parent_id != 0:
+                    if parent_id not in self.transform_children:
+                        self.transform_children[parent_id] = []
+                    self.transform_children[parent_id].append(file_id)
+        
+        # Create prefab nodes
+        for prefab_id, prefab_info in prefab_instances.items():
+            self.prefab_nodes[prefab_id] = {
+                'fileID': str(prefab_id),
+                'source_prefab_guid': prefab_info['source_prefab_guid'],
+                'm_IsActive': prefab_info['m_IsActive'],
+                'm_RootOrder': prefab_info.get('m_RootOrder', 999999),
+                'components': [],  # Will be populated with override components
+                'children': []
+            }
+            if prefab_info.get('m_Name'):
+                self.prefab_nodes[prefab_id]['m_Name'] = prefab_info['m_Name']
+        
+        # Store for second pass
+        self.stripped_transforms = stripped_transforms
+        self.stripped_gameobjects = stripped_gameobjects
+        self.prefab_instances = prefab_instances
+
+    def process_second_pass(self):
+        """Second pass: Build hierarchy relationships"""
+        
+        # 1. Handle standard GameObject parent-child relationships
+        for go_id, node in self.nodes.items():
+            transform_id = self.gameobject_to_transform.get(go_id)
+            if not transform_id:
+                continue
+            
+            # Get child transforms
+            child_transform_ids = self.transform_children.get(transform_id, [])
+            for child_transform_id in child_transform_ids:
+                
+                # Check if child transform is stripped (part of prefab)
+                if child_transform_id in self.stripped_transforms:
+                    stripped_info = self.stripped_transforms[child_transform_id]
+                    prefab_instance_id = stripped_info['prefab_instance_id']
+                    
+                    if prefab_instance_id in self.prefab_nodes:
+                        relationship_key = f"{go_id}->{prefab_instance_id}"
+                        if relationship_key not in self.processed_relationships:
+                            node['children'].append(self.prefab_nodes[prefab_instance_id])
+                            self.processed_relationships.add(relationship_key)
+                else:
+                    # Regular GameObject child
+                    child_go_id = self.transform_to_gameobject.get(child_transform_id)
+                    if child_go_id and child_go_id in self.nodes:
+                        relationship_key = f"{go_id}->{child_go_id}"
+                        if relationship_key not in self.processed_relationships:
+                            node['children'].append(self.nodes[child_go_id])
+                            self.processed_relationships.add(relationship_key)
+        
+        # 2. Handle prefab-to-parent relationships
+        for prefab_id, prefab_info in self.prefab_instances.items():
+            parent_transform_id = prefab_info['m_TransformParent']
+            if not parent_transform_id or parent_transform_id == 0:
+                continue  # Root prefab, will be handled in get_root_objects
+            
+            # Find parent GameObject or parent prefab
+            parent_go_id = self.transform_to_gameobject.get(parent_transform_id)
+            if parent_go_id and parent_go_id in self.nodes:
+                # Prefab child of GameObject
+                relationship_key = f"{parent_go_id}->{prefab_id}"
+                if relationship_key not in self.processed_relationships:
+                    self.nodes[parent_go_id]['children'].append(self.prefab_nodes[prefab_id])
+                    self.processed_relationships.add(relationship_key)
+            elif parent_transform_id in self.stripped_transforms:
+                # Prefab child of another prefab
+                stripped_info = self.stripped_transforms[parent_transform_id]
+                parent_prefab_id = stripped_info['prefab_instance_id']
+                
+                if parent_prefab_id in self.prefab_nodes:
+                    relationship_key = f"{parent_prefab_id}->{prefab_id}"
+                    if relationship_key not in self.processed_relationships:
+                        self.prefab_nodes[parent_prefab_id]['children'].append(self.prefab_nodes[prefab_id])
+                        self.processed_relationships.add(relationship_key)
+        
+        # 3. Handle scene objects as children of prefabs (with deep sibling index)
+        for go_id, node in self.nodes.items():
+            transform_id = self.gameobject_to_transform.get(go_id)
+            if not transform_id:
+                continue
+            
+            # Find parent transform
+            parent_transform_id = None
+            for parent_id, children in self.transform_children.items():
+                if transform_id in children:
+                    parent_transform_id = parent_id
+                    break
+            
+            if parent_transform_id and parent_transform_id in self.stripped_transforms:
+                stripped_info = self.stripped_transforms[parent_transform_id]
+                prefab_instance_id = stripped_info['prefab_instance_id']
+                
+                if prefab_instance_id in self.prefab_nodes:
+                    # Calculate deep sibling index
+                    prefab_guid = self.prefab_instances[prefab_instance_id]['source_prefab_guid']
+                    deep_sibling_index = self.calculate_deep_sibling_index(transform_id, prefab_guid)
+                    if deep_sibling_index:
+                        node['deep_sibling_index'] = deep_sibling_index
+                    
+                    # Add as child of prefab
+                    relationship_key = f"{prefab_instance_id}->{go_id}"
+                    if relationship_key not in self.processed_relationships:
+                        self.prefab_nodes[prefab_instance_id]['children'].append(node)
+                        self.processed_relationships.add(relationship_key)
+
+    def process_third_pass(self):
+        """Third pass: Extract components for both GameObjects and PrefabInstances"""
+        for file_id, obj_data in self.object_map.items():
+            # Skip GameObjects, Transforms, and PrefabInstances
+            if any(key in obj_data for key in ['GameObject', 'Transform', 'PrefabInstance']):
+                continue
+            
+            # Find the GameObject this component belongs to
+            gameobject_id = None
+            for key, data in obj_data.items():
+                if isinstance(data, dict) and 'm_GameObject' in data:
+                    gameobject_id = data['m_GameObject'].get('fileID')
+                    break
+            
+            if not gameobject_id:
+                continue
+            
+            component_name = next((key for key in obj_data if key not in ['m_ObjectHideFlags']), 'Unknown')
+            
+            # Handle MonoBehaviour with script GUID
+            if component_name == 'MonoBehaviour':
+                script_info = obj_data['MonoBehaviour'].get('m_Script', {})
+                script_guid = script_info.get('guid')
+                if script_guid:
+                    component_entry = script_guid
+                else:
+                    component_entry = "MonoBehaviour"
+            else:
+                component_entry = component_name
+            
+            # Check if this component belongs to a regular GameObject
+            if gameobject_id in self.nodes:
+                if component_entry not in self.nodes[gameobject_id]['components']:
+                    self.nodes[gameobject_id]['components'].append(component_entry)
+            
+            # Check if this component belongs to a stripped GameObject (prefab override)
+            elif gameobject_id in self.stripped_gameobjects:
+                stripped_info = self.stripped_gameobjects[gameobject_id]
+                prefab_instance_id = stripped_info['prefab_instance_id']
+                
+                if prefab_instance_id in self.prefab_nodes:
+                    if component_entry not in self.prefab_nodes[prefab_instance_id]['components']:
+                        self.prefab_nodes[prefab_instance_id]['components'].append(component_entry)
+            
+            # If neither, this component belongs to an unknown GameObject (potential orphan)
+            else:
+                print(f"Warning: Component {component_name} (ID: {file_id}) references unknown GameObject {gameobject_id}")
+
+    def verification_pass(self):
+        """
+        Verifies and fixes parent-child relationships that might have been missed
+        by using a direct child->parent lookup. This acts as a patch for the
+        less reliable reverse-lookup used in process_second_pass.
+        """
+        for go_id, node in list(self.nodes.items()):
+            transform_id = self.gameobject_to_transform.get(go_id)
+            if not transform_id:
+                continue
+
+            transform_info = self.object_map.get(transform_id, {}).get('Transform', {})
+            parent_transform_id = transform_info.get('m_Father', {}).get('fileID')
+
+            if not parent_transform_id or parent_transform_id == 0:
+                continue  # This is a root object, no parent to verify.
+
+            # Case 1: Parent is a PrefabInstance (via a stripped transform)
+            if parent_transform_id in self.stripped_transforms:
+                stripped_info = self.stripped_transforms[parent_transform_id]
+                parent_prefab_id = stripped_info['prefab_instance_id']
+                if parent_prefab_id in self.prefab_nodes:
+                    relationship_key = f"{parent_prefab_id}->{go_id}"
+                    if relationship_key not in self.processed_relationships:
+                        # Add deep sibling index if it's a child of a prefab
+                        prefab_guid = self.prefab_instances[parent_prefab_id]['source_prefab_guid']
+                        deep_sibling_index = self.calculate_deep_sibling_index(transform_id, prefab_guid)
+                        if deep_sibling_index:
+                            node['deep_sibling_index'] = deep_sibling_index
+                        
+                        self.prefab_nodes[parent_prefab_id]['children'].append(node)
+                        self.processed_relationships.add(relationship_key)
+
+            # Case 2: Parent is a regular GameObject
+            else:
+                parent_go_id = self.transform_to_gameobject.get(parent_transform_id)
+                if parent_go_id and parent_go_id in self.nodes:
+                    relationship_key = f"{parent_go_id}->{go_id}"
+                    if relationship_key not in self.processed_relationships:
+                        self.nodes[parent_go_id]['children'].append(node)
+                        self.processed_relationships.add(relationship_key)
+
+    def merge_prefab_data_pass(self):
+        """Fourth pass: Merge GameObject data into their corresponding PrefabInstance nodes"""
+        nodes_to_delete = []
+        for go_id, go_node in self.nodes.items():
+            prefab_instance_id = go_node.get('prefab_instance_id')
+            if not prefab_instance_id:
+                continue
+
+            if prefab_instance_id in self.prefab_nodes:
+                # Merge the GameObject data into the PrefabInstance node
+                # The prefab node's existing data takes precedence
+                prefab_node = self.prefab_nodes[prefab_instance_id]
+                
+                # Create a copy of the gameobject node to avoid modifying during iteration
+                go_node_copy = go_node.copy()
+                
+                # Remove keys that should not be merged or are already handled
+                del go_node_copy['fileID']
+                del go_node_copy['prefab_instance_id']
+                
+                # Update prefab_node with go_node_copy data
+                # This will overwrite keys in prefab_node with values from go_node_copy
+                # if they exist in both. We want the opposite.
+                
+                # Let's do it manually to ensure prefab data is kept
+                for key, value in go_node_copy.items():
+                    if key not in prefab_node:
+                        prefab_node[key] = value
+                
+                # Specifically handle children and components to merge them
+                prefab_node['children'].extend(go_node_copy.get('children', []))
+                
+                # Merge components, avoiding duplicates
+                existing_components = set(str(c) for c in prefab_node.get('components', []))
+                for comp in go_node_copy.get('components', []):
+                    if str(comp) not in existing_components:
+                        prefab_node['components'].append(comp)
+                
+                # Rename 'components' to 'addedComponents' for clarity
+                if 'components' in prefab_node:
+                    prefab_node['addedComponents'] = prefab_node.pop('components')
+
+                # Mark the original GameObject node for deletion
+                nodes_to_delete.append(go_id)
+
+        # Remove the now-redundant GameObject nodes
+        for go_id in nodes_to_delete:
+            del self.nodes[go_id]
+
+    def get_root_objects(self):
+        """Get all root-level objects, sorted by m_RootOrder, and handle orphans"""
+        root_objects = []
+        all_children_ids = set()
+        
+        # Collect all child IDs to identify orphans
+        for go_id, node in self.nodes.items():
+            self._collect_child_ids(node, all_children_ids)
+        for prefab_id, prefab_node in self.prefab_nodes.items():
+            self._collect_child_ids(prefab_node, all_children_ids)
+        
+        # Find GameObjects that have no parent and collect their m_RootOrder
+        gameobject_roots = []
+        for go_id, node in self.nodes.items():
+            transform_id = self.gameobject_to_transform.get(go_id)
+            if not transform_id:
+                # No transform - likely orphan
+                if go_id not in all_children_ids:
+                    node['is_orphan'] = True
+                    gameobject_roots.append((node, 999999))  # Orphans go to end
+                continue
+            
+            # Check if this transform has a parent
+            has_parent = False
+            for parent_id, children in self.transform_children.items():
+                if transform_id in children:
+                    has_parent = True
+                    break
+            
+            # Also check if it's a child of any prefab
+            is_prefab_child = False
+            for parent_transform_id in self.stripped_transforms:
+                if transform_id in self.transform_children.get(parent_transform_id, []):
+                    is_prefab_child = True
+                    break
+            
+            if not has_parent and not is_prefab_child:
+                if go_id not in all_children_ids:
+                    # Get m_RootOrder from transform
+                    root_order = self._get_root_order(transform_id)
+                    gameobject_roots.append((node, root_order))
+                else:
+                    # This is an orphan that was somehow referenced but not properly parented
+                    node['is_orphan'] = True
+                    gameobject_roots.append((node, 999999))  # Orphans go to end
+        
+        # Find root prefab instances and collect their m_RootOrder
+        prefab_roots = []
+        for prefab_id, prefab_info in self.prefab_instances.items():
+            parent_transform_id = prefab_info['m_TransformParent']
+            if not parent_transform_id or parent_transform_id == 0:
+                if prefab_id in self.prefab_nodes:
+                    if prefab_id not in all_children_ids:
+                        # Get m_RootOrder directly from the prefab node
+                        root_order = self.prefab_nodes[prefab_id].get('m_RootOrder', 999999)
+                        prefab_roots.append((self.prefab_nodes[prefab_id], root_order))
+                    else:
+                        # Orphan prefab
+                        self.prefab_nodes[prefab_id]['is_orphan'] = True
+                        prefab_roots.append((self.prefab_nodes[prefab_id], 999999))  # Orphans go to end
+        
+        # Check for completely disconnected GameObjects (orphans)
+        for go_id, node in self.nodes.items():
+            if go_id not in all_children_ids and not any(obj[0].get('fileID') == str(go_id) for obj in gameobject_roots):
+                node['is_orphan'] = True
+                gameobject_roots.append((node, 999999))  # Orphans go to end
+        
+        # Check for completely disconnected PrefabInstances (orphans)
+        for prefab_id, prefab_node in self.prefab_nodes.items():
+            if prefab_id not in all_children_ids and not any(obj[0].get('fileID') == str(prefab_id) for obj in prefab_roots):
+                prefab_node['is_orphan'] = True
+                prefab_roots.append((prefab_node, 999999))  # Orphans go to end
+        
+        # Combine and sort all root objects by m_RootOrder
+        all_roots = gameobject_roots + prefab_roots
+        all_roots.sort(key=lambda x: x[1])  # Sort by m_RootOrder (second element of tuple)
+        
+        return [obj[0] for obj in all_roots]  # Return only the objects, not the tuples
+
+    def _get_root_order(self, transform_id):
+        """Get m_RootOrder from a transform"""
+        if transform_id not in self.object_map:
+            return 999999  # Default for missing transforms
+        
+        transform_data = self.object_map[transform_id].get('Transform', {})
+        return transform_data.get('m_RootOrder', 999999)
+
+    def _collect_child_ids(self, node, child_ids_set):
+        """Recursively collect all child IDs from a node tree"""
+        for child in node.get('children', []):
+            child_id = child.get('fileID')
+            if child_id:
+                # Convert to int for comparison with our keys
+                try:
+                    child_ids_set.add(int(child_id))
+                except ValueError:
+                    pass
+            self._collect_child_ids(child, child_ids_set)
+
+    def cleanup_pass(self, nodes):
+        """
+        Recursively cleans up temporary or internal properties from the final node structure.
+        """
+        # List of properties to remove from the final output
+        cleanup_keys = ['m_RootOrder']
+
+        for node in nodes:
+            for key in cleanup_keys:
+                if key in node:
+                    del node[key]
+            
+            if 'children' in node and node['children']:
+                self.cleanup_pass(node['children'])
+
+    def process_file(self, file_path):
+        """Main processing method"""
+        # Load and parse the file
+        documents = load_unity_yaml(file_path)
+        if not documents:
+            return []
+
+        # Build object map
+        raw_object_map = {int(doc.anchor.value): doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None}
+        self.object_map = {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()}
+
+        # Process in passes
+        self.process_first_pass()
+        self.process_second_pass()
+        self.verification_pass()
+        self.process_third_pass()
+        self.merge_prefab_data_pass()
+
+        # Get the final, sorted root objects
+        root_objects = self.get_root_objects()
+        
+        # Run the final cleanup pass
+        self.cleanup_pass(root_objects)
+
+        return root_objects
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Process Unity scene/prefab files into JSON representation")
+    parser.add_argument("--input", required=True, help="Path to input .unity or .prefab file")
+    parser.add_argument("--guid-map", required=True, help="Path to guid_map.json file")
+    parser.add_argument("--output", required=True, help="Path to output .json file")
+    
+    args = parser.parse_args()
+    
+    input_path = Path(args.input)
+    guid_map_path = Path(args.guid_map)
+    output_path = Path(args.output)
+    
+    if not input_path.exists():
+        print(f"Error: Input file {input_path} does not exist", file=sys.stderr)
+        sys.exit(1)
+    
+    if not guid_map_path.exists():
+        print(f"Error: GUID map file {guid_map_path} does not exist", file=sys.stderr)
+        sys.exit(1)
+    
+    # Load GUID map
+    try:
+        with open(guid_map_path, 'r', encoding='utf-8') as f:
+            guid_map = json.load(f)
+    except Exception as e:
+        print(f"Error loading GUID map: {e}", file=sys.stderr)
+        sys.exit(1)
+    
+    # Process the file
+    processor = UnitySceneProcessor(guid_map)
+    try:
+        result = processor.process_file(input_path)
+        
+        # Ensure output directory exists
+        output_path.parent.mkdir(parents=True, exist_ok=True)
+        
+        # Write output
+        with open(output_path, 'w', encoding='utf-8') as f:
+            json.dump(result, f, indent=2, ensure_ascii=False)
+        
+        print(f"Successfully processed {input_path.name} -> {output_path}")
+        
+    except Exception as e:
+        print(f"Error processing file {input_path}: {e}", file=sys.stderr)
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

+ 7 - 0
Assets/LLM/source/scene_processor.py.meta

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

BIN
Assets/LLM/source/utils/__pycache__/virtual_tree_builder.cpython-313.pyc


BIN
Assets/LLM/source/utils/__pycache__/yaml_utils.cpython-313.pyc


+ 46 - 48
Assets/LLM/source/utils/virtual_tree_builder.py

@@ -1,6 +1,4 @@
 import sys
-import json
-import io
 from pathlib import Path
 import ruamel.yaml
 
@@ -10,69 +8,69 @@ sys.path.append(str(utils_path))
 
 from yaml_utils import load_unity_yaml, convert_to_plain_python_types
 
-def build_virtual_tree(file_path):
+def build_virtual_tree(file_path, object_map, guid_map):
     """
-    Parses a Unity scene or prefab file and builds a nested dictionary 
-    representing the GameObject hierarchy.
+    Parses a Unity scene/prefab's data to build a nested dictionary 
+    representing the GameObject hierarchy with resolved component names and correct children.
     """
-    documents = load_unity_yaml(file_path)
-    if not documents:
-        return None
-
-    # First, build the map from the raw documents to preserve anchors
-    raw_object_map = {doc.anchor.value: doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None}
-
-    # Now, convert the mapped objects to plain Python types
-    object_map = {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()}
-
     nodes = {}
     transform_to_gameobject = {}
+    child_to_parent_transform = {}
 
-    # First pass: create nodes for all GameObjects and map transforms
+    # --- Pass 1: Create all nodes and map all relationships ---
     for file_id, obj_data in object_map.items():
         if 'GameObject' in obj_data:
             go_info = obj_data['GameObject']
+            
+            resolved_components = []
+            for comp_ref in go_info.get('m_Component', []):
+                comp_id = str(comp_ref['component']['fileID'])
+                if comp_id not in object_map:
+                    continue
+
+                comp_data = object_map[comp_id]
+                if 'MonoBehaviour' in comp_data:
+                    script_guid = comp_data['MonoBehaviour'].get('m_Script', {}).get('guid')
+                    if script_guid:
+                        resolved_components.append({"script": {"guid": script_guid}})
+                else:
+                    comp_name = next((key for key in comp_data if key != 'm_ObjectHideFlags'), None)
+                    if comp_name:
+                        resolved_components.append(comp_name)
+
             nodes[file_id] = {
                 'fileID': file_id,
                 'm_Name': go_info.get('m_Name'),
                 'm_IsActive': go_info.get('m_IsActive', 1),
                 'm_TagString': go_info.get('m_TagString'),
                 'm_Layer': go_info.get('m_Layer'),
-                'm_Components': [comp['component']['fileID'] for comp in go_info.get('m_Component', []) if comp and 'component' in comp],
+                'm_Components': resolved_components,
                 'm_Children': []
             }
-            # Find the transform component to map it back to the GameObject
-            for comp in go_info.get('m_Component', []):
-                if comp and 'component' in comp:
-                    comp_id = comp['component']['fileID']
-                    if comp_id in object_map and 'Transform' in object_map.get(comp_id, {}):
-                        transform_to_gameobject[comp_id] = file_id
-                        break
-
-    # Second pass: build the hierarchy
-    root_nodes = list(nodes.values())
-    for go_id, node in nodes.items():
-        # Find the transform for the current GameObject
-        transform_id = None
-        for comp_id in node['m_Components']:
-            if comp_id in transform_to_gameobject:
-                transform_id = comp_id
-                break
         
-        if transform_id:
-            transform_data = object_map.get(transform_id, {}).get('Transform', {})
-            if transform_data:
-                parent_transform_id = transform_data.get('m_Father', {}).get('fileID')
+        elif 'Transform' in obj_data:
+            # Map the parent-child relationship between transforms
+            father_id = str(obj_data['Transform'].get('m_Father', {}).get('fileID', '0'))
+            if father_id != '0':
+                child_to_parent_transform[file_id] = father_id
+            
+            # Map the transform back to its owning GameObject
+            gameobject_id = str(obj_data['Transform'].get('m_GameObject', {}).get('fileID', '0'))
+            if gameobject_id != '0':
+                transform_to_gameobject[file_id] = gameobject_id
 
-                if parent_transform_id and parent_transform_id != 0:
-                    parent_go_id = transform_to_gameobject.get(parent_transform_id)
-                    if parent_go_id and parent_go_id in nodes:
-                        # Check to prevent adding a node to its own children list
-                        if nodes[parent_go_id] is not node:
-                            nodes[parent_go_id]['m_Children'].append(node)
-                            if node in root_nodes:
-                                root_nodes.remove(node)
+    # --- Pass 2: Build the hierarchy ---
+    root_nodes = []
+    for transform_id, gameobject_id in transform_to_gameobject.items():
+        if gameobject_id in nodes:
+            parent_transform_id = child_to_parent_transform.get(transform_id)
+            if parent_transform_id and parent_transform_id in transform_to_gameobject:
+                parent_gameobject_id = transform_to_gameobject[parent_transform_id]
+                if parent_gameobject_id in nodes:
+                    # This is a child, append it to its parent
+                    nodes[parent_gameobject_id]['m_Children'].append(nodes[gameobject_id])
+            else:
+                # This is a root GameObject
+                root_nodes.append(nodes[gameobject_id])
 
     return root_nodes
-
-

+ 4 - 1
Assets/LLM/source/utils/yaml_utils.py

@@ -41,12 +41,15 @@ def load_unity_yaml(file_path):
         # Use regex to fix the Unity-specific shorthand tags.
         # This looks for "--- !u!### &..." and replaces it with a valid tag format.
         processed_content = re.sub(r'--- !u!(\d+) &', r'--- !<tag:unity3d.com,2011:\1> &', yaml_content)
+        # Remove non-standard keywords like 'stripped' from document headers
+        processed_content = re.sub(r'(&\d+)\s+stripped', r'\1', processed_content)
         
         # Unity files start with a %YAML directive, which ruamel.yaml can handle.
         # The documents are separated by '---'.
         yaml = ruamel.yaml.YAML(typ='rt') # Use round-trip mode to handle custom tags
         # allow_duplicate_keys=True is important for some Unity files
-        yaml.allow_duplicate_keys = True 
+        yaml.allow_duplicate_keys = True
+        yaml.strict = False
         
         documents = list(yaml.load_all(processed_content))
         return documents

+ 655 - 144
Assets/Scenes/SampleScene.unity

@@ -122,6 +122,169 @@ NavMeshSettings:
     debug:
       m_Flags: 0
   m_NavMeshData: {fileID: 0}
+--- !u!1 &37563816
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 37563817}
+  m_Layer: 0
+  m_Name: CheChaw
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &37563817
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 37563816}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 1480218983}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &123470204
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 2339992666513029069, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_Name
+      value: Test2
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4063256588221426634, guid: 04894a299445a4ae0b9cd1817d4b28c5,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 04894a299445a4ae0b9cd1817d4b28c5, type: 3}
+--- !u!1 &147236320
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 147236321}
+  m_Layer: 0
+  m_Name: Test6
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &147236321
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 147236320}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children:
+  - {fileID: 2028088465571112568}
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &242539199
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 242539200}
+  m_Layer: 0
+  m_Name: Child1
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &242539200
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 242539199}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 7155510671133795233}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!1 &330585543
 GameObject:
   m_ObjectHideFlags: 0
@@ -135,7 +298,7 @@ GameObject:
   - component: {fileID: 330585544}
   - component: {fileID: 330585547}
   m_Layer: 0
-  m_Name: Main Camera
+  m_Name: Test1
   m_TagString: MainCamera
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
@@ -159,7 +322,7 @@ Camera:
   m_Enabled: 1
   serializedVersion: 2
   m_ClearFlags: 1
-  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 1}
   m_projectionMatrixMode: 1
   m_GateFitMode: 2
   m_FOVAxisMode: 0
@@ -203,7 +366,8 @@ Transform:
   m_LocalPosition: {x: 0, y: 1, z: -10}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
-  m_Children: []
+  m_Children:
+  - {fileID: 1429936258}
   m_Father: {fileID: 0}
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -240,122 +404,112 @@ MonoBehaviour:
   m_RequiresDepthTexture: 0
   m_RequiresColorTexture: 0
   m_Version: 2
---- !u!1 &410087039
-GameObject:
+--- !u!1001 &597996061
+PrefabInstance:
   m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  serializedVersion: 6
-  m_Component:
-  - component: {fileID: 410087041}
-  - component: {fileID: 410087040}
-  - component: {fileID: 410087042}
-  m_Layer: 0
-  m_Name: Directional Light
-  m_TagString: Untagged
-  m_Icon: {fileID: 0}
-  m_NavMeshLayer: 0
-  m_StaticEditorFlags: 0
-  m_IsActive: 1
---- !u!108 &410087040
-Light:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 410087039}
-  m_Enabled: 1
-  serializedVersion: 10
-  m_Type: 1
-  m_Shape: 0
-  m_Color: {r: 1, g: 1, b: 1, a: 1}
-  m_Intensity: 2
-  m_Range: 10
-  m_SpotAngle: 30
-  m_InnerSpotAngle: 21.80208
-  m_CookieSize: 10
-  m_Shadows:
-    m_Type: 2
-    m_Resolution: -1
-    m_CustomResolution: -1
-    m_Strength: 1
-    m_Bias: 0.05
-    m_NormalBias: 0.4
-    m_NearPlane: 0.2
-    m_CullingMatrixOverride:
-      e00: 1
-      e01: 0
-      e02: 0
-      e03: 0
-      e10: 0
-      e11: 1
-      e12: 0
-      e13: 0
-      e20: 0
-      e21: 0
-      e22: 1
-      e23: 0
-      e30: 0
-      e31: 0
-      e32: 0
-      e33: 1
-    m_UseCullingMatrixOverride: 0
-  m_Cookie: {fileID: 0}
-  m_DrawHalo: 0
-  m_Flare: {fileID: 0}
-  m_RenderMode: 0
-  m_CullingMask:
-    serializedVersion: 2
-    m_Bits: 4294967295
-  m_RenderingLayerMask: 1
-  m_Lightmapping: 4
-  m_LightShadowCasterMode: 0
-  m_AreaSize: {x: 1, y: 1}
-  m_BounceIntensity: 1
-  m_ColorTemperature: 5000
-  m_UseColorTemperature: 1
-  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
-  m_UseBoundingSphereOverride: 0
-  m_UseViewFrustumForShadowCasterCull: 1
-  m_ShadowRadius: 0
-  m_ShadowAngle: 0
---- !u!4 &410087041
-Transform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 2.3
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 23
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 74932978109944710, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4806392749618940852, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+        type: 3}
+      propertyPath: m_Name
+      value: Test3
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 7cf72a57738ed4c1e993c0388fa836d4, type: 3}
+--- !u!1 &597996062 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 4806392749618940852, guid: 7cf72a57738ed4c1e993c0388fa836d4,
+    type: 3}
+  m_PrefabInstance: {fileID: 597996061}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 410087039}
-  m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
-  m_LocalPosition: {x: 0, y: 3, z: 0}
-  m_LocalScale: {x: 1, y: 1, z: 1}
-  m_ConstrainProportionsScale: 0
-  m_Children: []
-  m_Father: {fileID: 0}
-  m_RootOrder: 1
-  m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
---- !u!114 &410087042
-MonoBehaviour:
+--- !u!146 &597996063
+WheelCollider:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 410087039}
+  m_GameObject: {fileID: 597996062}
+  m_Center: {x: 0, y: 0, z: 0}
+  m_Radius: 0.5
+  m_SuspensionSpring:
+    spring: 35000
+    damper: 4500
+    targetPosition: 0.5
+  m_SuspensionDistance: 0.3
+  m_ForceAppPointDistance: 0
+  m_Mass: 20
+  m_WheelDampingRate: 0.25
+  m_ForwardFriction:
+    m_ExtremumSlip: 0.4
+    m_ExtremumValue: 1
+    m_AsymptoteSlip: 0.8
+    m_AsymptoteValue: 0.5
+    m_Stiffness: 1
+  m_SidewaysFriction:
+    m_ExtremumSlip: 0.2
+    m_ExtremumValue: 1
+    m_AsymptoteSlip: 0.5
+    m_AsymptoteValue: 0.75
+    m_Stiffness: 1
   m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_Version: 1
-  m_UsePipelineSettings: 1
-  m_AdditionalLightsShadowResolutionTier: 2
-  m_LightLayerMask: 1
-  m_CustomShadowLayers: 0
-  m_ShadowLayerMask: 1
-  m_LightCookieSize: {x: 1, y: 1}
-  m_LightCookieOffset: {x: 0, y: 0}
---- !u!1 &832575517
+--- !u!1 &1325802091
 GameObject:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -363,46 +517,28 @@ GameObject:
   m_PrefabAsset: {fileID: 0}
   serializedVersion: 6
   m_Component:
-  - component: {fileID: 832575519}
-  - component: {fileID: 832575518}
+  - component: {fileID: 1325802092}
   m_Layer: 0
-  m_Name: Global Volume
+  m_Name: CheChaw 2
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
   m_IsActive: 1
---- !u!114 &832575518
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 832575517}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  m_IsGlobal: 1
-  priority: 0
-  blendDistance: 0
-  weight: 1
-  sharedProfile: {fileID: 11400000, guid: a6560a915ef98420e9faacc1c7438823, type: 2}
---- !u!4 &832575519
+--- !u!4 &1325802092
 Transform:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 832575517}
-  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
-  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_GameObject: {fileID: 1325802091}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 1.05}
   m_LocalScale: {x: 1, y: 1, z: 1}
-  m_ConstrainProportionsScale: 0
+  m_ConstrainProportionsScale: 1
   m_Children: []
-  m_Father: {fileID: 0}
-  m_RootOrder: 2
+  m_Father: {fileID: 1480218984}
+  m_RootOrder: 1
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!1 &1429936256
 GameObject:
@@ -416,7 +552,7 @@ GameObject:
   - component: {fileID: 1429936257}
   - component: {fileID: 1429936259}
   m_Layer: 0
-  m_Name: GameObject
+  m_Name: MEEE
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
@@ -445,13 +581,13 @@ Transform:
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 1429936256}
-  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
-  m_LocalPosition: {x: 0.2898221, y: -22.73114, z: -58.733814}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0.2898221, y: -23.73114, z: -48.733814}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
   m_Children: []
-  m_Father: {fileID: 0}
-  m_RootOrder: 3
+  m_Father: {fileID: 330585546}
+  m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!114 &1429936259
 MonoBehaviour:
@@ -470,3 +606,378 @@ MonoBehaviour:
   launchForce: 20
   upwardForce: 5
   fireRate: 1
+--- !u!1001 &1480218982
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5669290172027642543, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_Name
+      value: Test7
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 6
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.8715
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 6729460392160249717, guid: 874028b39d9764ee997aa13686619919,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 874028b39d9764ee997aa13686619919, type: 3}
+--- !u!4 &1480218983 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 8145956705230478064, guid: 874028b39d9764ee997aa13686619919,
+    type: 3}
+  m_PrefabInstance: {fileID: 1480218982}
+  m_PrefabAsset: {fileID: 0}
+--- !u!4 &1480218984 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1455291841571425918, guid: 874028b39d9764ee997aa13686619919,
+    type: 3}
+  m_PrefabInstance: {fileID: 1480218982}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &2028088465571112567
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 147236321}
+    m_Modifications:
+    - target: {fileID: 5868054921787411589, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_Name
+      value: Quad
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 955ecfc0b23dd4f719e3ed37fa14344b, type: 3}
+--- !u!4 &2028088465571112568 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 8867314908339017022, guid: 955ecfc0b23dd4f719e3ed37fa14344b,
+    type: 3}
+  m_PrefabInstance: {fileID: 2028088465571112567}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &7019270161952568811
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 9094295339241098772}
+    m_Modifications:
+    - target: {fileID: 26865921374172392, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_Name
+      value: Plane
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3314701741176706183, guid: 456d0f5fa284e434fb15e8cdbbd163cf,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 456d0f5fa284e434fb15e8cdbbd163cf, type: 3}
+--- !u!1001 &7155510671133795232
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 1607234435344914535, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_Name
+      value: Test4
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 16c359d6244cd477d8961adff1de9b74, type: 3}
+--- !u!4 &7155510671133795233 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 7591028888340819383, guid: 16c359d6244cd477d8961adff1de9b74,
+    type: 3}
+  m_PrefabInstance: {fileID: 7155510671133795232}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &9094295339241098771
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4088192060758156186, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_Name
+      value: Test5
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 70759af8221444904a5bf911363200ef, type: 3}
+--- !u!4 &9094295339241098772 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 8523160453602241341, guid: 70759af8221444904a5bf911363200ef,
+    type: 3}
+  m_PrefabInstance: {fileID: 9094295339241098771}
+  m_PrefabAsset: {fileID: 0}