| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 | 
							- using System.Linq;
 
- using Newtonsoft.Json.Linq;
 
- using System.Collections.Generic;
 
- using AssetBank.Editor.SchemaConverter.Data.Input;
 
- using AssetBank.Editor.SchemaConverter.Data.Output;
 
- namespace AssetBank.Editor.SchemaConverter.Processors
 
- {
 
-     /// <summary>
 
-     /// Handles the logic for building the scene hierarchy from GameObject nodes.
 
-     /// </summary>
 
-     public class HierarchyProcessor
 
-     {
 
-         private readonly List<string> _warnings = new();
 
-         private IReadOnlyList<OriginalNode> _allNodes;
 
-         private Dictionary<string, HierarchicalNode> _unappendedPrefabs;
 
-         private HashSet<string> _visitedGameObjects;
 
-         private Dictionary<string, HierarchicalNode> _nodeMap;
 
-         private static readonly HashSet<string> s_RedundantTransformKeys = new()
 
-         {
 
-             "m_Father", "m_Children", "m_PrefabInstance", "m_CorrespondingSourceObject", "m_PrefabAsset"
 
-         };
 
-         public (List<HierarchicalNode> rootNodes, List<string> warnings, HashSet<string> consumedAnchorIds) Process(
 
-             IReadOnlyList<OriginalNode> allNodes,
 
-             IReadOnlyDictionary<string, OriginalNode> prefabInstances,
 
-             List<string> prefabGuids)
 
-         {
 
-             _allNodes = allNodes;
 
-             _visitedGameObjects = new HashSet<string>();
 
-             _nodeMap = new Dictionary<string, HierarchicalNode>();
 
-             
 
-             var consumedAnchorIds = new HashSet<string>();
 
-             var rootHierarchicalNodes = new List<HierarchicalNode>();
 
-             var allGameObjects = allNodes.Where(n => n.type_id == "1").ToList();
 
-             // 1. Identify all Transforms that are linked to a PrefabInstance and create a lookup for them.
 
-             var allTransforms = allNodes.Where(n => n.type_id == "4").ToList();
 
-             _unappendedPrefabs = new Dictionary<string, HierarchicalNode>();
 
-             foreach (var transformNode in allTransforms)
 
-             {
 
-                 var prefabInstanceId = transformNode.data?["Transform"]?["m_PrefabInstance"]?["fileID"]?.ToString();
 
-                 if (!string.IsNullOrEmpty(prefabInstanceId) && prefabInstances.TryGetValue(prefabInstanceId, out var prefabInstance))
 
-                 {
 
-                     var guid = prefabInstance.data?["PrefabInstance"]?["m_SourcePrefab"]?["guid"]?.ToString();
 
-                     var guidIndex = prefabGuids.IndexOf(guid);
 
-                     if (guidIndex != -1)
 
-                     {
 
-                         if (!_nodeMap.TryGetValue(transformNode.anchor_id, out var prefabNode))
 
-                         {
 
-                             prefabNode = new HierarchicalNode
 
-                             {
 
-                                 anchor_id = transformNode.anchor_id,
 
-                                 type_id = transformNode.type_id,
 
-                             };
 
-                             _nodeMap[transformNode.anchor_id] = prefabNode;
 
-                         }
 
-                         prefabNode.prefab_guid_index = (ulong)guidIndex;
 
-                         PopulateNodeFields(prefabNode, transformNode);
 
-                         _unappendedPrefabs[transformNode.anchor_id] = prefabNode;
 
-                         consumedAnchorIds.Add(transformNode.anchor_id);
 
-                     }
 
-                     else if (!string.IsNullOrEmpty(guid))
 
-                     {
 
-                         _warnings.Add($"Found prefab with GUID '{guid}' but it was not in the provided GUID list.");
 
-                     }
 
-                 }
 
-             }
 
-             // 2. Identify and create nodes for all explicit root GameObjects.
 
-             foreach (var goNode in allGameObjects)
 
-             {
 
-                 consumedAnchorIds.Add(goNode.anchor_id);
 
-                 var transformComponent = GetTransformComponent(goNode);
 
-                 var fatherId = GetFatherIdFromTransform(transformComponent);
 
-                 if (fatherId == "0")
 
-                 {
 
-                     if (!_nodeMap.TryGetValue(goNode.anchor_id, out var rootNode))
 
-                     {
 
-                         rootNode = new HierarchicalNode { anchor_id = goNode.anchor_id, type_id = goNode.type_id };
 
-                         PopulateNodeFields(rootNode, goNode);
 
-                         _nodeMap[goNode.anchor_id] = rootNode;
 
-                     }
 
-                     rootHierarchicalNodes.Add(rootNode);
 
-                     _visitedGameObjects.Add(rootNode.anchor_id);
 
-                 }
 
-             }
 
-             // 3. Recursively build the hierarchy for each root GameObject.
 
-             // Create a copy for iteration as the collection will be modified.
 
-             foreach (var rootNode in rootHierarchicalNodes.ToList())
 
-             {
 
-                 BuildHierarchyRecursive(rootNode.anchor_id, allGameObjects);
 
-             }
 
-             
 
-             // 4. Iteratively process all unvisited GameObjects.
 
-             bool madeChangesInPass;
 
-             do
 
-             {
 
-                 madeChangesInPass = false;
 
-                 var unvisitedGameObjects = allGameObjects.Where(go => !_visitedGameObjects.Contains(go.anchor_id)).ToList();
 
-                 foreach (var goNode in unvisitedGameObjects)
 
-                 {
 
-                     var transformComponent = GetTransformComponent(goNode);
 
-                     var fatherId = GetFatherIdFromTransform(transformComponent);
 
-                     var parentNode = FindParentNode(fatherId);
 
-                     if (parentNode != null)
 
-                     {
 
-                         if (!_nodeMap.TryGetValue(goNode.anchor_id, out var childNode))
 
-                         {
 
-                             childNode = new HierarchicalNode { anchor_id = goNode.anchor_id, type_id = goNode.type_id };
 
-                             PopulateNodeFields(childNode, goNode);
 
-                             _nodeMap[childNode.anchor_id] = childNode;
 
-                         }
 
-                         
 
-                         if (!parentNode.children.Contains(childNode))
 
-                         {
 
-                             parentNode.children.Add(childNode);
 
-                         }
 
-                         _visitedGameObjects.Add(childNode.anchor_id);
 
-                         
 
-                         BuildHierarchyRecursive(childNode.anchor_id, allGameObjects);
 
-                         madeChangesInPass = true;
 
-                     }
 
-                 }
 
-             } while (madeChangesInPass);
 
-             // 5. Any remaining unvisited nodes are true orphans; treat them as roots.
 
-             var finalUnvisited = allGameObjects.Where(go => !_visitedGameObjects.Contains(go.anchor_id)).ToList();
 
-             foreach (var goNode in finalUnvisited)
 
-             {
 
-                 if (!_nodeMap.TryGetValue(goNode.anchor_id, out var rootNode))
 
-                 {
 
-                     rootNode = new HierarchicalNode { anchor_id = goNode.anchor_id, type_id = goNode.type_id };
 
-                     PopulateNodeFields(rootNode, goNode);
 
-                     _nodeMap[rootNode.anchor_id] = rootNode;
 
-                 }
 
-                 
 
-                 rootNode.is_orphan = true;
 
-                 rootHierarchicalNodes.Add(rootNode);
 
-                 _visitedGameObjects.Add(rootNode.anchor_id);
 
-                 _warnings.Add($"GameObject '{goNode.anchor_id}' was treated as an orphan root.");
 
-                 
 
-                 BuildHierarchyRecursive(rootNode.anchor_id, allGameObjects);
 
-             }
 
-             
 
-             // 5b. Prune false roots: remove any nodes from the root list that are actually children of other nodes.
 
-             var allChildren = new HashSet<HierarchicalNode>();
 
-             var queue = new Queue<HierarchicalNode>(rootHierarchicalNodes);
 
-             var visitedForPruning = new HashSet<HierarchicalNode>();
 
-             while (queue.Count > 0)
 
-             {
 
-                 var node = queue.Dequeue();
 
-                 if (!visitedForPruning.Add(node)) continue;
 
-                 foreach (var child in node.children)
 
-                 {
 
-                     allChildren.Add(child);
 
-                     queue.Enqueue(child);
 
-                 }
 
-             }
 
-             rootHierarchicalNodes.RemoveAll(node => allChildren.Contains(node));
 
-             
 
-             // 6. Update nodes that are implicitly prefabs.
 
-             UpdateImplicitPrefabInstances(rootHierarchicalNodes, prefabInstances, prefabGuids);
 
-             
 
-             // 7. Final Cleanup Pass
 
-             CleanupNodes(rootHierarchicalNodes);
 
-             return (rootHierarchicalNodes, _warnings, consumedAnchorIds);
 
-         }
 
-         private void BuildHierarchyRecursive(string parentAnchorId, IReadOnlyList<OriginalNode> allGameObjects)
 
-         {
 
-             if (!_nodeMap.TryGetValue(parentAnchorId, out var parentNode)) return;
 
-             var parentOriginalNode = _allNodes.FirstOrDefault(n => n.anchor_id == parentAnchorId);
 
-             if (parentOriginalNode == null) return;
 
-             
 
-             var parentTransformToken = GetTransformComponent(parentOriginalNode);
 
-             if (parentOriginalNode.type_id == "4")
 
-             {
 
-                 parentTransformToken = parentOriginalNode.data;
 
-             }
 
-             var childrenIds = parentTransformToken?["Transform"]?["m_Children"]?
 
-                 .Select(c => c["fileID"]?.ToString())
 
-                 .Where(id => !string.IsNullOrEmpty(id))
 
-                 .ToList();
 
-             if (childrenIds == null) return;
 
-             foreach (var childId in childrenIds)
 
-             {
 
-                 var childOriginalNode = allGameObjects.FirstOrDefault(go => GetTransformId(go) == childId);
 
-                 if (childOriginalNode != null)
 
-                 {
 
-                     if (!_nodeMap.TryGetValue(childOriginalNode.anchor_id, out var childNode))
 
-                     {
 
-                         childNode = new HierarchicalNode { anchor_id = childOriginalNode.anchor_id, type_id = childOriginalNode.type_id };
 
-                         PopulateNodeFields(childNode, childOriginalNode);
 
-                         _nodeMap[childNode.anchor_id] = childNode;
 
-                     }
 
-                     
 
-                     if (!parentNode.children.Contains(childNode))
 
-                     {
 
-                         parentNode.children.Add(childNode);
 
-                     }
 
-                     
 
-                     if (_visitedGameObjects.Add(childNode.anchor_id))
 
-                     {
 
-                         BuildHierarchyRecursive(childNode.anchor_id, allGameObjects);
 
-                     }
 
-                 }
 
-                 else if (_unappendedPrefabs.TryGetValue(childId, out var prefabChildNode))
 
-                 {
 
-                     if (!parentNode.children.Contains(prefabChildNode))
 
-                     {
 
-                         parentNode.children.Add(prefabChildNode);
 
-                     }
 
-                     _unappendedPrefabs.Remove(childId);
 
-                     BuildHierarchyRecursive(prefabChildNode.anchor_id, allGameObjects);
 
-                 }
 
-             }
 
-         }
 
-         
 
-         private static void UpdateImplicitPrefabInstances(
 
-             IEnumerable<HierarchicalNode> nodes,
 
-             IReadOnlyDictionary<string, OriginalNode> prefabInstances,
 
-             List<string> prefabGuids)
 
-         {
 
-             if (nodes == null) return;
 
-             foreach (var node in nodes)
 
-             {
 
-                 // Only try to update the node if its prefab status is unknown.
 
-                 if (!node.prefab_guid_index.HasValue)
 
-                 {
 
-                     string prefabInstanceFileId = null;
 
-                 
 
-                     if (node.type_id == "1" && node.Fields.TryGetValue("m_Component", out var componentsToken) && componentsToken is JArray components)
 
-                     {
 
-                         foreach (var component in components)
 
-                         {
 
-                             var fileID = component?["Transform"]?["m_PrefabInstance"]?["fileID"]?.ToString();
 
-                             if (!string.IsNullOrEmpty(fileID) && fileID != "0")
 
-                             {
 
-                                 prefabInstanceFileId = fileID;
 
-                                 break;
 
-                             }
 
-                         }
 
-                     }
 
-                     if (prefabInstanceFileId != null)
 
-                     {
 
-                         if (prefabInstances.TryGetValue(prefabInstanceFileId, out var prefabInstanceNode))
 
-                         {
 
-                             var guid = prefabInstanceNode.data?["PrefabInstance"]?["m_SourcePrefab"]?["guid"]?.ToString();
 
-                             if (!string.IsNullOrEmpty(guid))
 
-                             {
 
-                                 var guidIndex = prefabGuids.IndexOf(guid);
 
-                                 if (guidIndex != -1)
 
-                                 {
 
-                                     node.prefab_guid_index = (ulong)guidIndex;
 
-                                 }
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-                 
 
-                 // ALWAYS recurse, regardless of the parent's status.
 
-                 if (node.children.Any())
 
-                 {
 
-                     UpdateImplicitPrefabInstances(node.children, prefabInstances, prefabGuids);
 
-                 }
 
-             }
 
-         }
 
-         private static void PopulateNodeFields(HierarchicalNode node, OriginalNode originalNode)
 
-         {
 
-             JObject dataBlock = null;
 
-             if (originalNode.type_id == "1")
 
-             {
 
-                 dataBlock = originalNode.data?["GameObject"] as JObject;
 
-             }
 
-             else if (originalNode.type_id == "4")
 
-             {
 
-                 dataBlock = originalNode.data;
 
-             }
 
-             
 
-             if (dataBlock == null) return;
 
-             
 
-             foreach (var property in dataBlock.Properties())
 
-             {
 
-                 node.Fields[property.Name] = property.Value;
 
-             }
 
-         }
 
-         private static void CleanupNodes(IEnumerable<HierarchicalNode> nodes)
 
-         {
 
-             foreach (var node in nodes)
 
-             {
 
-                 if (node.Fields.TryGetValue("Transform", out var transformToken) && transformToken is JObject transformObj)
 
-                 {
 
-                     foreach (var key in s_RedundantTransformKeys)
 
-                     {
 
-                         transformObj.Remove(key);
 
-                     }
 
-                 }
 
-                 if (node.Fields.TryGetValue("m_Component", out var componentsToken) && componentsToken is JArray components)
 
-                 {
 
-                     foreach (var component in components)
 
-                     {
 
-                         if (component["Transform"] is JObject cTransform)
 
-                         {
 
-                             foreach (var key in s_RedundantTransformKeys)
 
-                             {
 
-                                 cTransform.Remove(key);
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-                 if (node.children.Any())
 
-                 {
 
-                     CleanupNodes(node.children);
 
-                 }
 
-             }
 
-         }
 
-         private static JToken GetTransformComponent(OriginalNode goNode)
 
-         {
 
-             return goNode.data?["GameObject"]?["m_Component"]?.FirstOrDefault(c => c["Transform"] != null);
 
-         }
 
-         
 
-         private static string GetTransformId(OriginalNode goNode)
 
-         {
 
-             return GetTransformComponent(goNode)?["anchor_id"]?.ToString();
 
-         }
 
-         private static string GetFatherIdFromTransform(JToken transformComponent)
 
-         {
 
-             return transformComponent?["Transform"]?["m_Father"]?["fileID"]?.ToString() ?? "0";
 
-         }
 
-         private HierarchicalNode FindParentNode(string fatherTransformId)
 
-         {
 
-             foreach (var node in _nodeMap.Values)
 
-             {
 
-                 if (node.type_id == "1")
 
-                 {
 
-                     var originalNode = _allNodes.FirstOrDefault(n => n.anchor_id == node.anchor_id);
 
-                     if (originalNode == null) continue;
 
-                     
 
-                     var transformId = GetTransformId(originalNode);
 
-                     if (transformId == fatherTransformId)
 
-                     {
 
-                         return node;
 
-                     }
 
-                 }
 
-                 else if (node.type_id == "4")
 
-                 {
 
-                     if (node.anchor_id == fatherTransformId)
 
-                     {
 
-                         return node;
 
-                     }
 
-                 }
 
-             }
 
-             return null;
 
-         }
 
-     }
 
- }
 
 
  |