Files
AR-Menu/Library/PackageCache/com.needle.engine-exporter@8c046140a1d9/Gltf/Editor/UnityGltf/UnityGltfPersistentAssetExtension.cs
2025-11-30 08:35:03 +02:00

168 lines
4.6 KiB
C#

using System.Collections.Generic;
using System.Reflection;
using GLTF.Schema;
using Needle.Engine.Serialization;
using Needle.Engine.Utils;
using Newtonsoft.Json.Linq;
using UnityEngine;
namespace Needle.Engine.Gltf.UnityGltf
{
public class UnityGltfPersistentAssetExtension : IExtension, IAssetExtension
{
public const string EXTENSION_NAME = "NEEDLE_persistent_assets";
private readonly GltfExportContext context;
private readonly List<Object> assets = new List<Object>();
private readonly List<Object> failedToSerialize = new List<Object>();
// AnimatorControllerValueResolver needs to store the model until the whole scene was parsed
// for cases when the controller is used on multiple objects for example
// because we need to register the pointers per animator using the same controller
private readonly List<ISerializablePersistentAssetModel> models = new List<ISerializablePersistentAssetModel>();
private readonly List<object> currentlySerializing = new List<object>();
private readonly List<JToken> serialized = new List<JToken>();
public bool CanAdd(object owner, Object asset)
{
// var go = asset as GameObject;
// if (asset is Transform t) go = t.gameObject;
// if (go)
// {
// if(!go.TryGetComponent<IExportableObject>(out _))
// return false;
// if (ReferenceEquals(asset, owner))
// return false;
// if (owner is Component comp && comp.transform == asset)
// return false;
// }
return true;
}
public object GetPathOrAdd(Object asset, object owner, MemberInfo member)
{
if (failedToSerialize.Contains(asset))
return null;
var index = this.assets.IndexOf(asset);
if (index < 0)
{
var i = this.assets.Count;
this.assets.Add(asset);
try
{
currentlySerializing.Add(asset);
this.models.Add(null);
this.serialized.Add(null);
// currently just used for playable asset
object obj = asset;
foreach (var valueResolver in context.ValueResolvers)
{
if (valueResolver.TryGetValue(context, owner, member, ref obj))
break;
}
if (obj is ISerializablePersistentAssetModel ser)
{
this.models[i] = ser;
}
else
{
if (obj == null)
{
// if an object can not be serialized we dont want to add it to the extension at all
// we also have to remove the entries in the list to make sure the pointer indices are correct
failedToSerialize.Add(asset);
var assetIndex = this.assets.IndexOf(asset);
this.assets.RemoveAt(assetIndex);
this.models.RemoveAt(assetIndex);
this.serialized.RemoveAt(assetIndex);
return null;
}
else this.serialized[i] = Serialize(obj);
}
}
finally
{
currentlySerializing.RemoveAt(currentlySerializing.Count - 1);
}
return EXTENSION_NAME.AsExtensionPointer(i);
}
// if the asset was previously seen or is currently being serialized
if (currentlySerializing.Contains(asset) || models[index] != null || serialized[index] != null)
{
if (models[index] != null)
{
var existing = models[index];
if (existing != null)
{
existing.OnNewObjectDiscovered(asset, owner, member, context);
}
}
return EXTENSION_NAME.AsExtensionPointer(index);
}
return asset;
}
public void AddExtension(IGltfBridge bridge)
{
if (assets.Count > 0)
{
bridge.AddExtension(EXTENSION_NAME, this);
for (var index = 0; index < models.Count; index++)
{
var val = models[index];
if (val == null) continue;
var ser = Serialize(val);
if (ser == null) continue;
serialized[index] = ser;
}
}
}
public UnityGltfPersistentAssetExtension(GltfExportContext context)
{
this.context = context;
}
public JProperty Serialize()
{
var obj = new JObject();
var arr = new JArray();
obj.Add("assets", arr);
for (var index = 0; index < serialized.Count; index++)
{
var ser = serialized[index];
if (ser != null)
{
arr.Add(ser);
var asset = assets[index];
if (asset && ser is JObject)
{
ser["__type"] = asset.GetType().GetTypeInformation();
if (ser["guid"] == null)
ser["guid"] = asset.GetId();
}
}
}
return new JProperty(EXTENSION_NAME, obj);
}
public IExtension Clone(GLTFRoot root)
{
var ext = new UnityGltfPersistentAssetExtension(context);
ext.assets.AddRange(this.assets);
ext.models.AddRange(this.models);
return ext;
}
private JToken Serialize(object obj)
{
var ser = context.Serializer.Serialize(obj);
var res = JToken.Parse(ser);
return res;
}
}
}