hierarchy_utils.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. from pathlib import Path
  2. class HierarchyParser:
  3. """
  4. Parses a Unity scene's object_map to identify parent-child relationships
  5. and determine the root objects in the hierarchy.
  6. """
  7. def __init__(self, object_map):
  8. self.object_map = object_map
  9. self.transform_to_gameobject = {}
  10. self.gameobject_to_transform = {}
  11. self.transform_children = {}
  12. self.prefab_instances = {}
  13. self.stripped_transforms = {}
  14. self.all_child_ids = set()
  15. self._build_maps()
  16. self._find_all_children()
  17. def _build_maps(self):
  18. """
  19. First pass over the object map to build essential lookups for transforms,
  20. GameObjects, and PrefabInstances.
  21. """
  22. for file_id, obj_data in self.object_map.items():
  23. if 'Transform' in obj_data:
  24. transform_info = obj_data['Transform']
  25. gameobject_id = transform_info.get('m_GameObject', {}).get('fileID')
  26. if gameobject_id:
  27. self.transform_to_gameobject[file_id] = gameobject_id
  28. self.gameobject_to_transform[gameobject_id] = file_id
  29. parent_id = transform_info.get('m_Father', {}).get('fileID')
  30. if parent_id and parent_id != 0:
  31. if parent_id not in self.transform_children:
  32. self.transform_children[parent_id] = []
  33. self.transform_children[parent_id].append(file_id)
  34. prefab_instance_id = transform_info.get('m_PrefabInstance', {}).get('fileID')
  35. if prefab_instance_id:
  36. self.stripped_transforms[file_id] = prefab_instance_id
  37. elif 'PrefabInstance' in obj_data:
  38. prefab_info = obj_data['PrefabInstance']
  39. modifications = prefab_info.get('m_Modification', {})
  40. parent_id = modifications.get('m_TransformParent', {}).get('fileID')
  41. if parent_id and parent_id != 0:
  42. self.prefab_instances[file_id] = {'parent': parent_id}
  43. def _find_all_children(self):
  44. """
  45. Iterates through the built maps to populate a set of all fileIDs that
  46. are children of another object.
  47. """
  48. # Children of regular GameObjects (via Transform)
  49. for parent_id, children in self.transform_children.items():
  50. for child_transform_id in children:
  51. child_go_id = self.transform_to_gameobject.get(child_transform_id)
  52. if child_go_id:
  53. self.all_child_ids.add(child_go_id)
  54. # Also add stripped transforms that point to prefabs
  55. elif child_transform_id in self.stripped_transforms:
  56. prefab_instance_id = self.stripped_transforms[child_transform_id]
  57. self.all_child_ids.add(prefab_instance_id)
  58. # Children that are PrefabInstances
  59. for prefab_id, prefab_data in self.prefab_instances.items():
  60. self.all_child_ids.add(prefab_id)
  61. def get_root_object_ids(self):
  62. """
  63. Identifies all root objects (GameObjects and PrefabInstances) and returns
  64. them as a sorted list of tuples containing (fileID, m_RootOrder).
  65. """
  66. root_objects = []
  67. # Find root GameObjects
  68. for go_id, transform_id in self.gameobject_to_transform.items():
  69. if go_id not in self.all_child_ids:
  70. transform_info = self.object_map.get(transform_id, {}).get('Transform', {})
  71. root_order = transform_info.get('m_RootOrder', 999999)
  72. root_objects.append((go_id, root_order))
  73. # Find root PrefabInstances
  74. for prefab_id, obj_data in self.object_map.items():
  75. if 'PrefabInstance' not in obj_data:
  76. continue
  77. if prefab_id not in self.all_child_ids:
  78. modifications = obj_data['PrefabInstance'].get('m_Modification', {})
  79. root_order = 999999
  80. for mod in modifications.get('m_Modifications', []):
  81. if mod.get('propertyPath') == 'm_RootOrder':
  82. root_order = mod.get('value', 999999)
  83. break
  84. root_objects.append((prefab_id, root_order))
  85. # Sort all root objects by their m_RootOrder
  86. root_objects.sort(key=lambda x: x[1])
  87. return root_objects