Skip to content

Commit

Permalink
feat: Initial integration with NDMF
Browse files Browse the repository at this point in the history
This is a draft integration with the NDMF framework. For now, this removes the old Apply On Play logic; not sure if we want to put this back for a transition period.
  • Loading branch information
bdunderscore committed Sep 10, 2023
1 parent 340cd30 commit d5b53ef
Show file tree
Hide file tree
Showing 39 changed files with 48 additions and 1,033 deletions.
98 changes: 34 additions & 64 deletions Editor/OptimizerProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@

using System;
using Anatawa12.ApplyOnPlay;
using System.Collections.Immutable;
using Anatawa12.AvatarOptimizer;
using Anatawa12.AvatarOptimizer.ErrorReporting;
using UnityEngine;
using nadena.dev.ndmf;
using nadena.dev.ndmf.builtin;
using nadena.dev.ndmf.fluent;
using VRC.SDK3.Avatars.Components;
using VRC.SDKBase.Editor.BuildPipeline;

[assembly: ExportsPlugin(typeof(OptimizerPlugin))]

namespace Anatawa12.AvatarOptimizer
{
internal class OptimizerPlugin : Plugin<OptimizerPlugin>
{
public override string QualifiedName => "Anatawa12.AvatarOptimizer";
public override string DisplayName => "Anatawa12's Avatar Optimizer";

protected override void Configure()
{
InPhase(BuildPhase.Resolving)
.Run(EarlyOptimizerProcessor.Instance)
.BeforePass(RemoveEditorOnlyPass.Instance);

InPhase(BuildPhase.Optimizing)
.AfterPlugin("nadena.dev.modular-avatar")
.Run(OptimizerProcessor.Instance);
}
}

/// <summary>
/// the Processor runs before removing EditorOnly
/// </summary>
internal class EarlyOptimizerProcessor : IVRCSDKPreprocessAvatarCallback, IApplyOnPlayCallback
internal class EarlyOptimizerProcessor : Pass<EarlyOptimizerProcessor>
{
public int callbackOrder => -2048;
public string CallbackName => "Avatar Optimizer Early (Before IEditorOnly Deletion)";
public string CallbackId => "com.anatawa12.avatar-optimizer.early";

public bool ApplyOnPlay(GameObject avatarGameObject, ApplyReason reason)
{
ProcessObject(new OptimizerSession(avatarGameObject, Utils.CreateOutputAssetFile(avatarGameObject, reason),
reason == ApplyReason.EnteringPlayMode));
return true;
}

public bool OnPreprocessAvatar(GameObject avatarGameObject)
protected override void Execute(BuildContext context)
{
try
{
ProcessObject(new OptimizerSession(avatarGameObject, true, false));
return true;
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
ProcessObject(new OptimizerSession(context.AvatarRootObject, false, false));
}

private static bool _processing;


internal static void ProcessObject(OptimizerSession session)
{
if (_processing) return;
using (Utils.StartEditingScope(true))
//using (Utils.StartEditingScope(true))
using (BuildReport.ReportingOnAvatar(session.GetRootComponent<VRCAvatarDescriptor>()))
{
try
{
_processing = true;
DoProcessObject(session);
}
catch (Exception e)
Expand All @@ -58,7 +55,6 @@ internal static void ProcessObject(OptimizerSession session)
}
finally
{
_processing = false;
session.MarkDirtyAll();
}
}
Expand All @@ -72,44 +68,20 @@ private static void DoProcessObject(OptimizerSession session)
}
}

internal class OptimizerProcessor : IVRCSDKPreprocessAvatarCallback, IVRCSDKPostprocessAvatarCallback, IApplyOnPlayCallback
internal class OptimizerProcessor : Pass<OptimizerProcessor>
{
public int callbackOrder => 0;
public string CallbackName => "Avatar Optimizer Main";
public string CallbackId => "com.anatawa12.avatar-optimizer.main";

public bool ApplyOnPlay(GameObject avatarGameObject, ApplyReason reason)
{
ProcessObject(new OptimizerSession(avatarGameObject, Utils.CreateOutputAssetFile(avatarGameObject, reason),
reason == ApplyReason.EnteringPlayMode));
return true;
}

public bool OnPreprocessAvatar(GameObject avatarGameObject)
{
try
{
ProcessObject(new OptimizerSession(avatarGameObject, true, false));
return true;
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
}

public void OnPostprocessAvatar()

protected override void Execute(BuildContext context)
{
Utils.DeleteTemporalDirectory();
ProcessObject(new OptimizerSession(context.AvatarRootObject, false, false));
}

private static bool _processing;

public static void ProcessObject(OptimizerSession session)
{
if (_processing) return;
using (Utils.StartEditingScope(true))
//using (Utils.StartEditingScope(true))
using (BuildReport.ReportingOnAvatar(session.GetRootComponent<VRCAvatarDescriptor>()))
{
try
Expand Down Expand Up @@ -145,8 +117,6 @@ private static void DoProcessObject(OptimizerSession session)
new Processors.MakeChildrenProcessor(early: false).Process(session);

new Processors.ApplyObjectMapping().Apply(session);

session.MarkDirtyAll();
}
}
}
9 changes: 8 additions & 1 deletion Editor/OptimizerSession.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
Expand All @@ -15,8 +16,14 @@ internal class OptimizerSession
public bool IsTest { get; }
public ObjectMappingBuilder MappingBuilder { get; }

public OptimizerSession(BuildContext context)
: this(context.AvatarRootObject, null, false)
{

}

public OptimizerSession(GameObject rootObject, bool addToAsset, bool isTest) :
this(rootObject, addToAsset ? Utils.CreateAssetFile() : null, isTest)
this(rootObject, null, isTest)
{
}

Expand Down
63 changes: 0 additions & 63 deletions Editor/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Anatawa12.ApplyOnPlay;
using JetBrains.Annotations;
using Unity.Collections;
using UnityEditor;
Expand Down Expand Up @@ -312,68 +311,6 @@ public static GameObject NewGameObject(string name, Transform parent)
return rootObject;
}

private const string TemporalDirPath = "Assets/9999-OptimizerGeneratedTemporalAssets";
private const string OutputDirPath = "Assets/AvatarOptimizerOutput";

public static void DeleteTemporalDirectory()
{
AssetDatabase.SaveAssets();
AssetDatabase.DeleteAsset(TemporalDirPath);
FileUtil.DeleteFileOrDirectory(TemporalDirPath);
}

[CanBeNull]
public static DummyObject CreateOutputAssetFile(GameObject avatarGameObject, ApplyReason reason)
{
switch (reason)
{
case ApplyReason.EnteringPlayMode:
return ApplyOnPlayConfig.Generate ? CreateAssetFile() : null;
case ApplyReason.ManualBake:
default:
return CreateOutputAssetFile(avatarGameObject);
}
}

public static DummyObject CreateAssetFile()
{
var obj = ScriptableObject.CreateInstance<DummyObject>();
Directory.CreateDirectory(TemporalDirPath);
AssetDatabase.CreateAsset(obj, $"{TemporalDirPath}/{GUID.Generate()}.asset");
return obj;
}

public static DummyObject CreateOutputAssetFile(GameObject avatar)
{
var name = avatar.name;
if (name.EndsWith("(Clone)", StringComparison.Ordinal))
name = name.Substring(0, name.Length - "(Clone)".Length);
return CreateOutputAssetFile(name);
}

public static DummyObject CreateOutputAssetFile(string name)
{
Directory.CreateDirectory(OutputDirPath);
name = string.Join("_", name.Split(Path.GetInvalidFileNameChars()));
var path = GetUniqueFileName($"{OutputDirPath}/{name}", "asset");
var obj = ScriptableObject.CreateInstance<DummyObject>();
AssetDatabase.CreateAsset(obj, path);
return obj;
}

private static string GetUniqueFileName(string name, string extension)
{
// TOCTOU is allowed for now
string PathIfNotExists(string path) => File.Exists(path) || Directory.Exists(path) ? null : path;

if (PathIfNotExists($"{name}.{extension}") is string firstTry) return firstTry;

for (var number = 0; ; number++)
{
if (PathIfNotExists($"{name} ({number}).{extension}") is string otherTry) return otherTry;
}
}

public static ZipWithNextEnumerable<T> ZipWithNext<T>(this IEnumerable<T> enumerable) =>
new ZipWithNextEnumerable<T>(enumerable);

Expand Down
6 changes: 4 additions & 2 deletions Editor/com.anatawa12.avatar-optimizer.editor.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"GUID:f69eeb3e25674f4a9bd20e6d7e69e0e6",
"GUID:2633ab9fa94544a69517fc9a1bc143c9",
"GUID:b9880ca0b6584453a2627bd3c038759f",
"GUID:8542dbf824204440a818dbc2377cb4d6"
"GUID:8542dbf824204440a818dbc2377cb4d6",
"GUID:62ced99b048af7f4d8dfe4bed8373d76"
],
"includePlatforms": [
"Editor"
Expand All @@ -25,7 +26,8 @@
"VRCSDK3A.dll",
"VRCSDKBase-Editor.dll",
"VRCSDKBase.dll",
"System.Memory.dll"
"System.Memory.dll",
"System.Collections.Immutable.dll"
],
"autoReferenced": false,
"defineConstraints": [],
Expand Down
3 changes: 0 additions & 3 deletions Internal/ApplyOnPlay.meta

This file was deleted.

3 changes: 0 additions & 3 deletions Internal/ApplyOnPlay/Editor.meta

This file was deleted.

104 changes: 0 additions & 104 deletions Internal/ApplyOnPlay/Editor/ApplyOnPlayCallbackRegistry.cs

This file was deleted.

This file was deleted.

Loading

0 comments on commit d5b53ef

Please sign in to comment.