Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DeferPostprocessAsset API #229

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Added `BuildContext.DeferPostprocessAsset` (#229)

### Changed

### Deprecated

### Removed
- `BuildContext.Serialize()`, which was accidentally made public for use in a unit test, is now an `[Obsolete]` no-op.

### Fixed

### Security


## [1.4.0] - [2024-03-27]

### Added
Expand Down
69 changes: 65 additions & 4 deletions Editor/API/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using nadena.dev.ndmf.reporting;
using nadena.dev.ndmf.runtime;
using nadena.dev.ndmf.ui;
Expand Down Expand Up @@ -52,37 +53,50 @@ public sealed partial class BuildContext
internal readonly ObjectRegistry _registry;
internal readonly ErrorReport _report;

[PublicAPI]
public ObjectRegistry ObjectRegistry => _registry;
[PublicAPI]
public ErrorReport ErrorReport => _report;

/// <summary>
/// The root GameObject of the avatar being built.
/// </summary>

[PublicAPI]
public GameObject AvatarRootObject => _avatarRootObject;

/// <summary>
/// The root Transform of the avatar being built.
/// </summary>
[PublicAPI]
public Transform AvatarRootTransform => _avatarRootTransform;

/// <summary>
/// An asset container that can be used to store generated assets. NDMF will automatically add any objects
/// referenced by the avatar to this container when the build completes, but in some cases it can be necessary
/// to manually save assets (e.g. when using AnimatorController builtins).
/// </summary>
[PublicAPI]
public UnityObject AssetContainer { get; private set; }

public bool Successful => !_report.Errors.Any(e => e.TheError.Severity >= ErrorSeverity.Error);

private Dictionary<Type, object> _state = new Dictionary<Type, object>();
private Dictionary<Type, IExtensionContext> _extensions = new Dictionary<Type, IExtensionContext>();
private Dictionary<Type, IExtensionContext> _activeExtensions = new Dictionary<Type, IExtensionContext>();
private readonly Dictionary<Type, object> _state = new Dictionary<Type, object>();
private readonly Dictionary<Type, IExtensionContext> _extensions = new Dictionary<Type, IExtensionContext>();
private readonly Dictionary<Type, IExtensionContext> _activeExtensions = new Dictionary<Type, IExtensionContext>();

private readonly List<(UnityEngine.Object, PostprocessAssetDelegate)> _assetPostprocessors
= new List<(UnityEngine.Object, PostprocessAssetDelegate)>();

public delegate void PostprocessAssetDelegate(UnityEngine.Object asset);

[PublicAPI]
public T GetState<T>() where T : new()
{
return GetState(_ => new T());
}

[PublicAPI]
public T GetState<T>(Func<BuildContext, T> init)
{
if (_state.TryGetValue(typeof(T), out var value))
Expand All @@ -95,6 +109,7 @@ public T GetState<T>(Func<BuildContext, T> init)
return (T)value;
}

[PublicAPI]
public T Extension<T>() where T : IExtensionContext
{
if (!_activeExtensions.TryGetValue(typeof(T), out var value))
Expand All @@ -105,6 +120,7 @@ public T Extension<T>() where T : IExtensionContext
return (T)value;
}

[PublicAPI]
public BuildContext(GameObject obj, string assetRootPath, bool isClone = true)
{
BuildEvent.Dispatch(new BuildEvent.BuildStarted(obj));
Expand Down Expand Up @@ -200,13 +216,44 @@ internal static string FilterAvatarName(string avatarName)
return avatarName;
}

[PublicAPI]
public bool IsTemporaryAsset(UnityObject obj)
{
return !EditorUtility.IsPersistent(obj)
|| AssetDatabase.GetAssetPath(obj) == AssetDatabase.GetAssetPath(AssetContainer);
}

/// <summary>
/// Processes a Unity asset after the build completes, but only if it is still referenced. This can be used to
/// e.g. compress textures only if they are not replaced by other NDMF plugins.
///
/// Postprocess callbacks are run in order of registration. Note that the set of assets to be postprocessed is
/// determined prior to invoking callbacks; as such, if you change an asset to add or remove references to
/// assets during a postprocess callback, this will not impact which subsequent callbacks are invoked.
/// </summary>
/// <param name="asset">The asset to postprocess</param>
/// <param name="postprocess">The postprocess callback</param>
/// <returns></returns>
[PublicAPI]
public void DeferPostprocessAsset(
UnityEngine.Object asset,
PostprocessAssetDelegate postprocess
)
{
_assetPostprocessors.Add((asset, postprocess));
}

/// <summary>
/// No-op. Retained for API compatibility.
/// </summary>
[Obsolete("Serialize() was not meant to be public in the first place")]
[PublicAPI]
public void Serialize()
{

}

internal void SerializeInternal()
{
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(AssetContainer)))
{
Expand Down Expand Up @@ -257,6 +304,16 @@ public void Serialize()
}
}

foreach (var pair in _assetPostprocessors)
{
var (asset, postprocess) = pair;

if (_savedObjects.Contains(asset))
{
postprocess(asset);
}
}

// SaveAssets to make sub-assets visible on the Project window
AssetDatabase.SaveAssets();

Expand All @@ -282,11 +339,13 @@ public void Serialize()
}
}

[PublicAPI]
public void DeactivateExtensionContext<T>() where T : IExtensionContext
{
DeactivateExtensionContext(typeof(T));
}

[PublicAPI]
public void DeactivateExtensionContext(Type t)
{
using (new ExecutionScope(this))
Expand Down Expand Up @@ -371,11 +430,13 @@ internal void RunPass(ConcretePass pass)
}
}

[PublicAPI]
public T ActivateExtensionContext<T>() where T : IExtensionContext
{
return (T)ActivateExtensionContext(typeof(T));
}

[PublicAPI]
public IExtensionContext ActivateExtensionContext(Type ty)
{
using (new ExecutionScope(this))
Expand Down Expand Up @@ -439,7 +500,7 @@ internal void Finish()
_activeExtensions.Remove(kvp.Key);
}

Serialize();
SerializeInternal();
sw.Stop();

BuildEvent.Dispatch(new BuildEvent.BuildEnded(sw.ElapsedMilliseconds, true));
Expand Down
50 changes: 50 additions & 0 deletions UnitTests~/DeferPostprocessTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Generic;
using nadena.dev.ndmf;
using NUnit.Framework;
using UnityEngine;

namespace UnitTests
{
public class DeferPostprocessTest : TestBase
{
[Test]
public void Test()
{
var root = CreateRoot("root");

var meshRenderer = root.AddComponent<MeshRenderer>();
var material = new Material(Shader.Find("Standard"));
var tex = new Texture2D(1, 1);
var tex2 = new Texture2D(1, 1);

material.mainTexture = tex;
meshRenderer.material = material;

var log = new List<string>();

BuildContext bc = CreateContext(root);
bc.DeferPostprocessAsset(tex, obj =>
{
Assert.AreSame(tex, obj);
log.Add("1");
});
bc.DeferPostprocessAsset(tex2, _ =>
{
log.Add("2");
});
bc.DeferPostprocessAsset(material, obj =>
{
Assert.AreSame(material, obj);
log.Add("3");
});
bc.DeferPostprocessAsset(tex, _ =>
{
log.Add("4");
});

bc.SerializeInternal();

Assert.AreEqual(new List<string> { "1", "3", "4" }, log);
}
}
}
3 changes: 3 additions & 0 deletions UnitTests~/DeferPostprocessTest.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion UnitTests~/SerializationSweepTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void testSerialization()
testScriptable3.ref2 = testScriptable4;

BuildContext bc = CreateContext(root);
bc.Serialize();
bc.SerializeInternal();

var path = AssetDatabase.GetAssetPath(testScriptable1);
Assert.IsFalse(string.IsNullOrEmpty(path));
Expand Down
Loading