import sys from pathlib import Path import ruamel.yaml # Add the utils directory to the Python path utils_path = Path(__file__).parent sys.path.append(str(utils_path)) from yaml_utils import load_unity_yaml, convert_to_plain_python_types from file_utils import create_guid_to_path_map def parse_scene_or_prefab(file_path, guid_map=None, assets_dir=None): """ Parses a Unity scene or prefab file and returns a flat list of GameObject dictionaries with embedded component data. """ if guid_map is None or assets_dir is None: p = Path(file_path).resolve() while p.name != 'Assets' and p.parent != p: p = p.parent if p.name == 'Assets': assets_dir = str(p) else: assets_dir = str(Path(file_path).resolve().parent.parent) guid_map = create_guid_to_path_map(assets_dir) documents = load_unity_yaml(file_path) if not documents: return None raw_object_map = {int(doc.anchor.value): doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None} object_map = {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()} flat_gameobject_list = [] transform_to_gameobject = {} parent_to_children_transforms = {} # First pass: Populate relationship maps for file_id, obj_data in object_map.items(): if 'Transform' in obj_data: parent_id = obj_data['Transform'].get('m_Father', {}).get('fileID') if parent_id: if parent_id not in parent_to_children_transforms: parent_to_children_transforms[parent_id] = [] parent_to_children_transforms[parent_id].append(file_id) for file_id, obj_data in object_map.items(): if 'GameObject' in obj_data: for comp in obj_data['GameObject'].get('m_Component', []): 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: Process each GameObject for file_id, obj_data in object_map.items(): if 'GameObject' in obj_data: go_info = obj_data['GameObject'] source_obj_info = go_info.get('m_CorrespondingSourceObject') if source_obj_info and source_obj_info.get('guid'): guid = source_obj_info['guid'] source_prefab_path = guid_map.get(guid) if source_prefab_path: modifications = go_info.get('m_Modification') flat_gameobject_list.append({ 'fileID': file_id, 'm_Name': go_info.get('m_Name'), 'isPrefab': True, 'sourcePrefabGUID': guid, 'm_Modification': modifications }) continue components_data = [] for comp_ref in go_info.get('m_Component', []): comp_id = comp_ref['component']['fileID'] if comp_id in object_map: components_data.append(object_map[comp_id]) children_ids = [] my_transform_id = None for t_id, go_id in transform_to_gameobject.items(): if go_id == file_id: my_transform_id = t_id break if my_transform_id in parent_to_children_transforms: child_transform_ids = parent_to_children_transforms[my_transform_id] for child_t_id in child_transform_ids: child_go_id = transform_to_gameobject.get(child_t_id) if child_go_id: children_ids.append(child_go_id) flat_gameobject_list.append({ '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_ComponentsData': components_data, 'm_Children': children_ids }) return flat_gameobject_list