Remove legacy prefab outlining

This commit is contained in:
ds5678 2025-02-26 14:56:10 -08:00
parent 2fe9ac59ec
commit de5ce5b031
4 changed files with 3 additions and 377 deletions

View File

@ -12,11 +12,10 @@ using AssetRipper.Processing.AnimatorControllers;
using AssetRipper.Processing.Assemblies; using AssetRipper.Processing.Assemblies;
using AssetRipper.Processing.AudioMixers; using AssetRipper.Processing.AudioMixers;
using AssetRipper.Processing.Editor; using AssetRipper.Processing.Editor;
using AssetRipper.Processing.ScriptableObject;
using AssetRipper.Processing.PrefabOutlining;
using AssetRipper.Processing.Scenes;
using AssetRipper.Processing.Textures;
using AssetRipper.Processing.Prefabs; using AssetRipper.Processing.Prefabs;
using AssetRipper.Processing.Scenes;
using AssetRipper.Processing.ScriptableObject;
using AssetRipper.Processing.Textures;
namespace AssetRipper.Export.UnityProjects; namespace AssetRipper.Export.UnityProjects;
@ -68,10 +67,6 @@ public class ExportHandler
yield return new AudioMixerProcessor(); yield return new AudioMixerProcessor();
yield return new EditorFormatProcessor(Settings.ProcessingSettings.BundledAssetsExportMode); yield return new EditorFormatProcessor(Settings.ProcessingSettings.BundledAssetsExportMode);
//Static mesh separation goes here //Static mesh separation goes here
if (Settings.ProcessingSettings.EnablePrefabOutlining)
{
yield return new PrefabOutliningProcessor();
}
yield return new LightingDataProcessor();//Needs to be after static mesh separation yield return new LightingDataProcessor();//Needs to be after static mesh separation
yield return new PrefabProcessor(); yield return new PrefabProcessor();
yield return new SpriteProcessor(); yield return new SpriteProcessor();

View File

@ -1,59 +0,0 @@
using AssetRipper.Assets;
using AssetRipper.Assets.Cloning;
using AssetRipper.Assets.Collections;
using AssetRipper.SourceGenerated;
using AssetRipper.SourceGenerated.Classes.ClassID_1;
using AssetRipper.SourceGenerated.Extensions;
namespace AssetRipper.Processing.PrefabOutlining;
public static class GameObjectCloner
{
public static IGameObject Clone(IGameObject source, ProcessedAssetCollection processedCollection)
{
Dictionary<IUnityObjectBase, IUnityObjectBase> clonedAssetDictionary = [];
foreach (IUnityObjectBase asset in source.FetchHierarchy())
{
IUnityObjectBase clonedAsset = processedCollection.CreateAsset(asset.ClassID, AssetFactory.Create);
clonedAssetDictionary.Add(asset, clonedAsset);
}
ClonedAssetResolver resolver = new ClonedAssetResolver(clonedAssetDictionary);
foreach ((IUnityObjectBase asset, IUnityObjectBase clonedAsset) in clonedAssetDictionary)
{
PPtrConverter converter = new PPtrConverter(asset.Collection, clonedAsset.Collection, resolver);
clonedAsset.CopyValues(asset, converter);
}
return (IGameObject)clonedAssetDictionary[source];
}
private sealed class ClonedAssetResolver : IAssetResolver
{
private readonly Dictionary<IUnityObjectBase, IUnityObjectBase> cache;
public ClonedAssetResolver(Dictionary<IUnityObjectBase, IUnityObjectBase> cache)
{
this.cache = cache;
}
public T? Resolve<T>(IUnityObjectBase? asset) where T : IUnityObjectBase
{
if (asset is null)
{
return default;
}
else if (cache.TryGetValue(asset, out IUnityObjectBase? clonedAsset))
{
return TryCast<T>(clonedAsset);
}
else
{
return TryCast<T>(asset);
}
}
private static T? TryCast<T>(IUnityObjectBase asset) where T : IUnityObjectBase
{
return asset is T t ? t : default;
}
}
}

View File

@ -1,172 +0,0 @@
using AssetRipper.Assets.Collections;
using AssetRipper.Assets.Generics;
using AssetRipper.SourceGenerated.Classes.ClassID_1;
using AssetRipper.SourceGenerated.Classes.ClassID_2;
using AssetRipper.SourceGenerated.Classes.ClassID_4;
using AssetRipper.SourceGenerated.Extensions;
using AssetRipper.SourceGenerated.Subclasses.PPtr_Component;
using AssetRipper.SourceGenerated.Subclasses.PPtr_Transform;
namespace AssetRipper.Processing.PrefabOutlining;
internal readonly struct GameObjectInfo : IEquatable<GameObjectInfo>
{
public GameObjectInfo(GameObjectInfo?[] children, int[] components)
{
Children = children;
Components = components;
Hash = CalculateHash(children, components);
}
private GameObjectInfo?[] Children { get; }
private int[] Components { get; }
private int Hash { get; }
public override string ToString()
{
return $"Children: {Children.Length} Components: {Components.Length}";
}
public override bool Equals(object? obj)
{
return obj is GameObjectInfo info && Equals(info);
}
public bool Equals(GameObjectInfo other)
{
return Hash == other.Hash
&& Components.AsSpan().SequenceEqual(other.Components.AsSpan())
&& Children.AsSpan().SequenceEqual(other.Children.AsSpan());
}
public override int GetHashCode() => Hash;
public static bool operator ==(GameObjectInfo left, GameObjectInfo right)
{
return left.Equals(right);
}
public static bool operator !=(GameObjectInfo left, GameObjectInfo right)
{
return !(left == right);
}
private static int CalculateHash(GameObjectInfo?[] children, int[] components)
{
HashCode hashCode = new();
for (int i = 0; i < components.Length; i++)
{
hashCode.Add(components[i]);
}
for (int i = 0; i < children.Length; i++)
{
hashCode.Add(children[i]);
}
return hashCode.ToHashCode();
}
public static GameObjectInfo FromGameObject(IGameObject root)
{
GetTransformAndComponentArray(root, out ITransform? transform, out int[] components);
GameObjectInfo?[] children;
if (transform is null)
{
children = [];
}
else
{
PPtrAccessList<IPPtr_Transform, ITransform> childList = transform.Children_C4P;
if (childList.Count == 0)
{
children = [];
}
else
{
children = new GameObjectInfo?[childList.Count];
for (int i = 0; i < childList.Count; i++)
{
IGameObject? child = childList[i]?.GameObject_C4P;
children[i] = child is null ? null : FromGameObject(child);
}
}
}
return new GameObjectInfo(children, components);
}
public static void AddCollectionToDictionary(AssetCollection collection, Dictionary<IGameObject, GameObjectInfo> dictionary)
{
foreach (IGameObject gameObject in collection.OfType<IGameObject>())
{
AddHierarchyToDictionary(gameObject, dictionary);
}
}
public static GameObjectInfo AddHierarchyToDictionary(IGameObject root, Dictionary<IGameObject, GameObjectInfo> dictionary)
{
if (dictionary.TryGetValue(root, out GameObjectInfo info))
{
return info;
}
GetTransformAndComponentArray(root, out ITransform? transform, out int[] components);
GameObjectInfo?[] children;
if (transform is null)
{
children = [];
}
else
{
PPtrAccessList<IPPtr_Transform, ITransform> childList = transform.Children_C4P;
if (childList.Count == 0)
{
children = [];
}
else
{
children = new GameObjectInfo?[childList.Count];
for (int i = 0; i < childList.Count; i++)
{
IGameObject? child = childList[i]?.GameObject_C4P;
children[i] = child is null ? null : AddHierarchyToDictionary(child, dictionary);
}
}
}
info = new GameObjectInfo(children, components);
dictionary.Add(root, info);
return info;
}
private static void GetTransformAndComponentArray(IGameObject root, out ITransform? transform, out int[] components)
{
transform = null;
PPtrAccessList<IPPtr_Component, IComponent> componentList = root.GetComponentAccessList();
if (componentList.Count == 0)
{
components = [];
}
else
{
components = new int[componentList.Count];
for (int i = 0; i < componentList.Count; i++)
{
IComponent? component = componentList[i];
if (component is not null)
{
components[i] = component.ClassID;
if (component is ITransform t)
{
transform = t;
}
}
else
{
components[i] = -1;
}
}
}
}
}

View File

@ -1,138 +0,0 @@
using AssetRipper.Assets.Bundles;
using AssetRipper.Assets.Collections;
using AssetRipper.Import.Logging;
using AssetRipper.SourceGenerated;
using AssetRipper.SourceGenerated.Classes.ClassID_1;
using AssetRipper.SourceGenerated.Classes.ClassID_4;
using AssetRipper.SourceGenerated.Extensions;
namespace AssetRipper.Processing.PrefabOutlining;
public sealed class PrefabOutliningProcessor : IAssetProcessor
{
//Documentation note: prefab variants were introduced in 2018.3.
//That does not affect this processor currently, but it may have an impact on future improvements.
//https://blog.unity.com/technology/introducing-unity-2018-3
public void Process(GameData gameData)
{
Logger.Info(LogCategory.Processing, "Prefab Outlining");
ProcessedAssetCollection processedCollection = gameData.AddNewProcessedCollection("Outlined Prefabs");
MakeDictionaries(
gameData.GameBundle,
out Dictionary<IGameObject, GameObjectInfo> infoDictionary,
out Dictionary<AssetCollection, bool> sceneInfo);
MakeBoxes(
infoDictionary,
sceneInfo,
out Dictionary<string, Dictionary<GameObjectInfo, List<IGameObject>>> boxes,
out HashSet<IGameObject> prefabRoots);
foreach ((string name, Dictionary<GameObjectInfo, List<IGameObject>> variants) in boxes)
{
if (variants.Count != 1)
{
continue;//We want the simplest implementation to start out
}
(_, List<IGameObject> box) = variants.First();
if (box.Any(g => prefabRoots.Contains(g)))
{
continue;//Prefab already exists
}
Logger.Info(LogCategory.Processing, $"Recreating prefab for {name}");
AddCopyToCollection(name, box[0], processedCollection);
}
}
private static void AddCopyToCollection(string name, IGameObject source, ProcessedAssetCollection collection)
{
//AddPrefabPlaceHolder(name, collection);
AddNewPrefab(name, source, collection);
}
private static void AddNewPrefab(string name, IGameObject source, ProcessedAssetCollection collection)
{
IGameObject root = GameObjectCloner.Clone(source, collection);
root.Name = name;
root.SetIsActive(true);
ITransform transform = root.GetTransform();
transform.LocalPosition_C4.Reset();
transform.LocalRotation_C4.Reset();
transform.LocalScale_C4.SetValues(1, 1, 1);
transform.LocalEulerAnglesHint_C4?.Reset();
transform.RootOrder_C4 = 0;
transform.Father_C4.Reset();
}
private static void AddPrefabPlaceHolder(string name, ProcessedAssetCollection collection)
{
//Place holder code until source gen improves
IGameObject root = CreateNewGameObject(collection);
root.Name = name;
root.SetIsActive(true);
root.TagString = TagManagerConstants.UntaggedTag;
ITransform rootTransform = CreateNewTransform(collection);
rootTransform.GameObject_C4P = root;
rootTransform.RootOrder_C4 = 0;
//Since this Transform has no Father, its RootOrder is zero.
root.AddComponent(ClassIDType.Transform, rootTransform);
IGameObject child = CreateNewGameObject(collection);
child.Name = "This prefab is a placeholder until AssetRipper improves.";
child.SetIsActive(true);
child.TagString = TagManagerConstants.UntaggedTag;
ITransform childTransform = CreateNewTransform(collection);
childTransform.GameObject_C4P = child;
childTransform.RootOrder_C4 = 0;
//Since this Transform is the only child, its RootOrder is zero.
childTransform.Father_C4P = rootTransform;
rootTransform.Children_C4P.Add(childTransform);
child.AddComponent(ClassIDType.Transform, childTransform);
}
private static IGameObject CreateNewGameObject(ProcessedAssetCollection collection)
{
return collection.CreateAsset((int)ClassIDType.GameObject, GameObject.Create);
}
private static ITransform CreateNewTransform(ProcessedAssetCollection collection)
{
return collection.CreateAsset((int)ClassIDType.Transform, Transform.Create);
}
private static void MakeBoxes(Dictionary<IGameObject, GameObjectInfo> infoDictionary, Dictionary<AssetCollection, bool> sceneInfo, out Dictionary<string, Dictionary<GameObjectInfo, List<IGameObject>>> boxes, out HashSet<IGameObject> prefabRoots)
{
boxes = [];
prefabRoots = [];
foreach ((IGameObject gameObject, GameObjectInfo info) in infoDictionary)
{
string name = GameObjectNameCleaner.CleanName(gameObject.Name);
boxes.GetOrAdd(name).GetOrAdd(info).Add(gameObject);
if (!sceneInfo[gameObject.Collection] && gameObject.IsRoot())
{
prefabRoots.Add(gameObject);
}
}
}
private static void MakeDictionaries(GameBundle gameBundle, out Dictionary<IGameObject, GameObjectInfo> infoDictionary, out Dictionary<AssetCollection, bool> sceneInfo)
{
infoDictionary = [];
sceneInfo = [];
foreach (AssetCollection collection in gameBundle.FetchAssetCollections())
{
sceneInfo.Add(collection, collection.IsScene);
GameObjectInfo.AddCollectionToDictionary(collection, infoDictionary);
}
}
}