123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- from pathlib import Path
- class HierarchyParser:
- """
- Parses a Unity scene's object_map to identify parent-child relationships
- and determine the root objects in the hierarchy.
- """
- def __init__(self, object_map):
- self.object_map = object_map
- self.transform_to_gameobject = {}
- self.gameobject_to_transform = {}
- self.transform_children = {}
- self.prefab_instances = {}
- self.stripped_transforms = {}
- self.all_child_ids = set()
- self._build_maps()
- self._find_all_children()
- def _build_maps(self):
- """
- First pass over the object map to build essential lookups for transforms,
- GameObjects, and PrefabInstances.
- """
- for file_id, obj_data in self.object_map.items():
- if 'Transform' in obj_data:
- transform_info = obj_data['Transform']
- 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
-
- 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)
- prefab_instance_id = transform_info.get('m_PrefabInstance', {}).get('fileID')
- if prefab_instance_id:
- self.stripped_transforms[file_id] = prefab_instance_id
- elif 'PrefabInstance' in obj_data:
- prefab_info = obj_data['PrefabInstance']
- modifications = prefab_info.get('m_Modification', {})
- parent_id = modifications.get('m_TransformParent', {}).get('fileID')
- if parent_id and parent_id != 0:
- self.prefab_instances[file_id] = {'parent': parent_id}
- def _find_all_children(self):
- """
- Iterates through the built maps to populate a set of all fileIDs that
- are children of another object.
- """
- # Children of regular GameObjects (via Transform)
- for parent_id, children in self.transform_children.items():
- for child_transform_id in children:
- child_go_id = self.transform_to_gameobject.get(child_transform_id)
- if child_go_id:
- self.all_child_ids.add(child_go_id)
- # Also add stripped transforms that point to prefabs
- elif child_transform_id in self.stripped_transforms:
- prefab_instance_id = self.stripped_transforms[child_transform_id]
- self.all_child_ids.add(prefab_instance_id)
- # Children that are PrefabInstances
- for prefab_id, prefab_data in self.prefab_instances.items():
- self.all_child_ids.add(prefab_id)
- def get_root_object_ids(self):
- """
- Identifies all root objects (GameObjects and PrefabInstances) and returns
- them as a sorted list of tuples containing (fileID, m_RootOrder).
- """
- root_objects = []
- # Find root GameObjects
- for go_id, transform_id in self.gameobject_to_transform.items():
- if go_id not in self.all_child_ids:
- transform_info = self.object_map.get(transform_id, {}).get('Transform', {})
- root_order = transform_info.get('m_RootOrder', 999999)
- root_objects.append((go_id, root_order))
- # Find root PrefabInstances
- for prefab_id, obj_data in self.object_map.items():
- if 'PrefabInstance' not in obj_data:
- continue
-
- if prefab_id not in self.all_child_ids:
- modifications = obj_data['PrefabInstance'].get('m_Modification', {})
- root_order = 999999
- for mod in modifications.get('m_Modifications', []):
- if mod.get('propertyPath') == 'm_RootOrder':
- root_order = mod.get('value', 999999)
- break
- root_objects.append((prefab_id, root_order))
-
- # Sort all root objects by their m_RootOrder
- root_objects.sort(key=lambda x: x[1])
-
- return root_objects
|