|
@@ -19,7 +19,7 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
|
|
|
private static readonly HashSet<string> s_RedundantTransformKeys = new()
|
|
|
{
|
|
|
- "m_Father", "m_Children", "m_PrefabInstance"
|
|
|
+ "m_Father", "m_Children", "m_PrefabInstance", "m_CorrespondingSourceObject", "m_PrefabAsset"
|
|
|
};
|
|
|
|
|
|
public (List<HierarchicalNode> rootNodes, List<string> warnings, HashSet<string> consumedAnchorIds) Process(
|
|
@@ -48,15 +48,18 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
|
|
|
if (guidIndex != -1)
|
|
|
{
|
|
|
- var prefabNode = new HierarchicalNode
|
|
|
+ if (!_nodeMap.TryGetValue(transformNode.anchor_id, out var prefabNode))
|
|
|
{
|
|
|
- anchor_id = transformNode.anchor_id,
|
|
|
- type_id = transformNode.type_id,
|
|
|
- prefab_guid_index = (ulong)guidIndex
|
|
|
- };
|
|
|
+ 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;
|
|
|
- _nodeMap[transformNode.anchor_id] = prefabNode;
|
|
|
consumedAnchorIds.Add(transformNode.anchor_id);
|
|
|
}
|
|
|
else if (!string.IsNullOrEmpty(guid))
|
|
@@ -75,16 +78,20 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
|
|
|
if (fatherId == "0")
|
|
|
{
|
|
|
- var rootNode = new HierarchicalNode { anchor_id = goNode.anchor_id, type_id = goNode.type_id };
|
|
|
- PopulateNodeFields(rootNode, goNode);
|
|
|
+ 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);
|
|
|
- _nodeMap[rootNode.anchor_id] = rootNode;
|
|
|
_visitedGameObjects.Add(rootNode.anchor_id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 3. Recursively build the hierarchy for each root GameObject.
|
|
|
- foreach (var rootNode in rootHierarchicalNodes)
|
|
|
+ // Create a copy for iteration as the collection will be modified.
|
|
|
+ foreach (var rootNode in rootHierarchicalNodes.ToList())
|
|
|
{
|
|
|
BuildHierarchyRecursive(rootNode.anchor_id, allGameObjects);
|
|
|
}
|
|
@@ -104,10 +111,17 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
|
|
|
if (parentNode != null)
|
|
|
{
|
|
|
- var childNode = new HierarchicalNode { anchor_id = goNode.anchor_id, type_id = goNode.type_id };
|
|
|
- PopulateNodeFields(childNode, goNode);
|
|
|
- _nodeMap[childNode.anchor_id] = childNode;
|
|
|
- parentNode.children.Add(childNode);
|
|
|
+ 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);
|
|
@@ -120,18 +134,43 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
var finalUnvisited = allGameObjects.Where(go => !_visitedGameObjects.Contains(go.anchor_id)).ToList();
|
|
|
foreach (var goNode in finalUnvisited)
|
|
|
{
|
|
|
- var rootNode = new HierarchicalNode { anchor_id = goNode.anchor_id, type_id = goNode.type_id };
|
|
|
- PopulateNodeFields(rootNode, goNode);
|
|
|
+ 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);
|
|
|
- _nodeMap[rootNode.anchor_id] = rootNode;
|
|
|
_visitedGameObjects.Add(rootNode.anchor_id);
|
|
|
_warnings.Add($"GameObject '{goNode.anchor_id}' was treated as an orphan root.");
|
|
|
|
|
|
BuildHierarchyRecursive(rootNode.anchor_id, allGameObjects);
|
|
|
}
|
|
|
|
|
|
- // 6. Final Cleanup Pass
|
|
|
+ // 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);
|
|
@@ -144,7 +183,7 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
var parentOriginalNode = _allNodes.FirstOrDefault(n => n.anchor_id == parentAnchorId);
|
|
|
if (parentOriginalNode == null) return;
|
|
|
|
|
|
- JToken parentTransformToken = GetTransformComponent(parentOriginalNode);
|
|
|
+ var parentTransformToken = GetTransformComponent(parentOriginalNode);
|
|
|
if (parentOriginalNode.type_id == "4")
|
|
|
{
|
|
|
parentTransformToken = parentOriginalNode.data;
|
|
@@ -163,23 +202,89 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
|
|
|
if (childOriginalNode != null)
|
|
|
{
|
|
|
- var childNode = new HierarchicalNode { anchor_id = childOriginalNode.anchor_id, type_id = childOriginalNode.type_id };
|
|
|
- PopulateNodeFields(childNode, childOriginalNode);
|
|
|
- _nodeMap[childNode.anchor_id] = childNode;
|
|
|
- parentNode.children.Add(childNode);
|
|
|
- _visitedGameObjects.Add(childNode.anchor_id);
|
|
|
- BuildHierarchyRecursive(childNode.anchor_id, allGameObjects);
|
|
|
+ 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))
|
|
|
{
|
|
|
- parentNode.children.Add(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 void PopulateNodeFields(HierarchicalNode node, OriginalNode originalNode)
|
|
|
+ private static void PopulateNodeFields(HierarchicalNode node, OriginalNode originalNode)
|
|
|
{
|
|
|
JObject dataBlock = null;
|
|
|
if (originalNode.type_id == "1")
|
|
@@ -199,7 +304,7 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void CleanupNodes(IEnumerable<HierarchicalNode> nodes)
|
|
|
+ private static void CleanupNodes(IEnumerable<HierarchicalNode> nodes)
|
|
|
{
|
|
|
foreach (var node in nodes)
|
|
|
{
|
|
@@ -232,17 +337,17 @@ namespace AssetBank.Editor.SchemaConverter.Processors
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private JToken GetTransformComponent(OriginalNode goNode)
|
|
|
+ private static JToken GetTransformComponent(OriginalNode goNode)
|
|
|
{
|
|
|
return goNode.data?["GameObject"]?["m_Component"]?.FirstOrDefault(c => c["Transform"] != null);
|
|
|
}
|
|
|
|
|
|
- private string GetTransformId(OriginalNode goNode)
|
|
|
+ private static string GetTransformId(OriginalNode goNode)
|
|
|
{
|
|
|
return GetTransformComponent(goNode)?["anchor_id"]?.ToString();
|
|
|
}
|
|
|
|
|
|
- private string GetFatherIdFromTransform(JToken transformComponent)
|
|
|
+ private static string GetFatherIdFromTransform(JToken transformComponent)
|
|
|
{
|
|
|
return transformComponent?["Transform"]?["m_Father"]?["fileID"]?.ToString() ?? "0";
|
|
|
}
|