deep_parser.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import sys
  2. from pathlib import Path
  3. import ruamel.yaml
  4. # Add the utils directory to the Python path
  5. utils_path = Path(__file__).parent
  6. sys.path.append(str(utils_path))
  7. from yaml_utils import load_unity_yaml, convert_to_plain_python_types
  8. from file_utils import create_guid_to_path_map
  9. def parse_scene_or_prefab(file_path, guid_map=None, assets_dir=None):
  10. """
  11. Parses a Unity scene or prefab file and returns a flat list of
  12. GameObject dictionaries with embedded component data.
  13. """
  14. if guid_map is None or assets_dir is None:
  15. p = Path(file_path).resolve()
  16. while p.name != 'Assets' and p.parent != p:
  17. p = p.parent
  18. if p.name == 'Assets':
  19. assets_dir = str(p)
  20. else:
  21. assets_dir = str(Path(file_path).resolve().parent.parent)
  22. guid_map = create_guid_to_path_map(assets_dir)
  23. documents = load_unity_yaml(file_path)
  24. if not documents:
  25. return None
  26. print(" -> Converting YAML documents to Python objects...")
  27. raw_object_map = {int(doc.anchor.value): doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None}
  28. object_map = {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()}
  29. print(f" -> Found {len(object_map)} objects in the file.")
  30. flat_gameobject_list = []
  31. transform_to_gameobject = {}
  32. parent_to_children_transforms = {}
  33. # First pass: Populate relationship maps
  34. print(" -> Pass 1/2: Mapping object relationships...")
  35. for file_id, obj_data in object_map.items():
  36. if 'Transform' in obj_data:
  37. parent_id = obj_data['Transform'].get('m_Father', {}).get('fileID')
  38. if parent_id:
  39. if parent_id not in parent_to_children_transforms:
  40. parent_to_children_transforms[parent_id] = []
  41. parent_to_children_transforms[parent_id].append(file_id)
  42. for file_id, obj_data in object_map.items():
  43. if 'GameObject' in obj_data:
  44. for comp in obj_data['GameObject'].get('m_Component', []):
  45. comp_id = comp['component']['fileID']
  46. if comp_id in object_map and 'Transform' in object_map.get(comp_id, {}):
  47. transform_to_gameobject[comp_id] = file_id
  48. break
  49. # Second pass: Process each GameObject
  50. print(" -> Pass 2/2: Extracting GameObject data and components...")
  51. total_gos = sum(1 for obj in object_map.values() if 'GameObject' in obj)
  52. processed_gos = 0
  53. for file_id, obj_data in object_map.items():
  54. if 'GameObject' in obj_data:
  55. processed_gos += 1
  56. if processed_gos % 100 == 0:
  57. print(f" ...processed {processed_gos}/{total_gos} GameObjects...")
  58. go_info = obj_data['GameObject']
  59. source_obj_info = go_info.get('m_CorrespondingSourceObject')
  60. if source_obj_info and source_obj_info.get('guid'):
  61. guid = source_obj_info['guid']
  62. source_prefab_path = guid_map.get(guid)
  63. if source_prefab_path:
  64. modifications = go_info.get('m_Modification')
  65. flat_gameobject_list.append({
  66. 'fileID': file_id,
  67. 'm_Name': go_info.get('m_Name'),
  68. 'isPrefab': True,
  69. 'sourcePrefabGUID': guid,
  70. 'm_Modification': modifications
  71. })
  72. continue
  73. components_data = []
  74. for comp_ref in go_info.get('m_Component', []):
  75. comp_id = comp_ref['component']['fileID']
  76. if comp_id in object_map:
  77. component_data = object_map[comp_id]
  78. # Create a new structure to avoid modifying the original object_map
  79. restructured_component = {}
  80. for component_type, component_values in component_data.items():
  81. if isinstance(component_values, dict):
  82. # Copy the inner dict and inject the fileID
  83. new_values = component_values.copy()
  84. new_values['fileID'] = comp_id
  85. restructured_component[component_type] = new_values
  86. else:
  87. # Fallback for non-dict components, though unlikely
  88. restructured_component[component_type] = component_values
  89. components_data.append(restructured_component)
  90. children_ids = []
  91. my_transform_id = None
  92. for t_id, go_id in transform_to_gameobject.items():
  93. if go_id == file_id:
  94. my_transform_id = t_id
  95. break
  96. if my_transform_id in parent_to_children_transforms:
  97. child_transform_ids = parent_to_children_transforms[my_transform_id]
  98. for child_t_id in child_transform_ids:
  99. child_go_id = transform_to_gameobject.get(child_t_id)
  100. if child_go_id:
  101. children_ids.append(child_go_id)
  102. flat_gameobject_list.append({
  103. 'fileID': file_id,
  104. 'm_Name': go_info.get('m_Name'),
  105. 'm_IsActive': go_info.get('m_IsActive', 1),
  106. 'm_TagString': go_info.get('m_TagString'),
  107. 'm_Layer': go_info.get('m_Layer'),
  108. 'm_ComponentsData': components_data,
  109. 'm_Children': children_ids
  110. })
  111. return flat_gameobject_list