virtual_tree_builder.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import sys
  2. import json
  3. import io
  4. from pathlib import Path
  5. import ruamel.yaml
  6. # Add the utils directory to the Python path
  7. utils_path = Path(__file__).parent
  8. sys.path.append(str(utils_path))
  9. from yaml_utils import load_unity_yaml, convert_to_plain_python_types
  10. def build_virtual_tree(file_path):
  11. """
  12. Parses a Unity scene or prefab file and builds a nested dictionary
  13. representing the GameObject hierarchy.
  14. """
  15. documents = load_unity_yaml(file_path)
  16. if not documents:
  17. return None
  18. # First, build the map from the raw documents to preserve anchors
  19. raw_object_map = {doc.anchor.value: doc for doc in documents if hasattr(doc, 'anchor') and doc.anchor is not None}
  20. # Now, convert the mapped objects to plain Python types
  21. object_map = {file_id: convert_to_plain_python_types(obj) for file_id, obj in raw_object_map.items()}
  22. nodes = {}
  23. transform_to_gameobject = {}
  24. # First pass: create nodes for all GameObjects and map transforms
  25. for file_id, obj_data in object_map.items():
  26. if 'GameObject' in obj_data:
  27. go_info = obj_data['GameObject']
  28. nodes[file_id] = {
  29. 'fileID': file_id,
  30. 'm_Name': go_info.get('m_Name'),
  31. 'm_IsActive': go_info.get('m_IsActive', 1),
  32. 'm_TagString': go_info.get('m_TagString'),
  33. 'm_Layer': go_info.get('m_Layer'),
  34. 'm_Components': [comp['component']['fileID'] for comp in go_info.get('m_Component', []) if comp and 'component' in comp],
  35. 'm_Children': []
  36. }
  37. # Find the transform component to map it back to the GameObject
  38. for comp in go_info.get('m_Component', []):
  39. if comp and 'component' in comp:
  40. comp_id = comp['component']['fileID']
  41. if comp_id in object_map and 'Transform' in object_map.get(comp_id, {}):
  42. transform_to_gameobject[comp_id] = file_id
  43. break
  44. # Second pass: build the hierarchy
  45. root_nodes = list(nodes.values())
  46. for go_id, node in nodes.items():
  47. # Find the transform for the current GameObject
  48. transform_id = None
  49. for comp_id in node['m_Components']:
  50. if comp_id in transform_to_gameobject:
  51. transform_id = comp_id
  52. break
  53. if transform_id:
  54. transform_data = object_map.get(transform_id, {}).get('Transform', {})
  55. if transform_data:
  56. parent_transform_id = transform_data.get('m_Father', {}).get('fileID')
  57. if parent_transform_id and parent_transform_id != 0:
  58. parent_go_id = transform_to_gameobject.get(parent_transform_id)
  59. if parent_go_id and parent_go_id in nodes:
  60. # Check to prevent adding a node to its own children list
  61. if nodes[parent_go_id] is not node:
  62. nodes[parent_go_id]['m_Children'].append(node)
  63. if node in root_nodes:
  64. root_nodes.remove(node)
  65. return root_nodes