312 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using YooAsset;
using Object = UnityEngine.Object;
namespace Unity.HLODSystem.Streaming
{
public class YooAssetHLODController : HLODControllerBase
{
public interface ICustomLoader
{
public void CustomLoad(string key, Action<GameObject> loadDoneAction);
public void CustomUnload(string key);
}
[Serializable]
public class ChildObject
{
public GameObject GameObject;
public string Address;
public Transform Parent;
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Scale;
}
[SerializeField] private List<ChildObject> m_highObjects = new List<ChildObject>();
[SerializeField] private List<string> m_lowObjects = new List<string>();
class LoadInfo
{
public string Key;
public bool LoadFromCustom;
public AssetHandle Handle;
public GameObject Instance;
}
private Dictionary<int, LoadInfo> m_highObjectLoadInfos = new Dictionary<int, LoadInfo>();
private Dictionary<int, LoadInfo> m_lowObjectLoadInfos = new Dictionary<int, LoadInfo>();
private GameObject m_hlodMeshesRoot;
private int m_hlodLayerIndex;
private ICustomLoader m_customLoader;
public event Action<GameObject> HighObjectCreated;
public ICustomLoader CustomLoader
{
set { m_customLoader = value; }
get { return m_customLoader; }
}
#if UNITY_EDITOR
public override GameObject GetHighSceneObject(int id)
{
return m_highObjects[id].GameObject;
}
#endif
public override void OnStart()
{
m_hlodMeshesRoot = new GameObject("HLODMeshesRoot");
m_hlodMeshesRoot.transform.SetParent(transform, false);
m_hlodLayerIndex = LayerMask.NameToLayer(HLOD.HLODLayerStr);
}
public override void OnStop()
{
}
public override void Install()
{
for (int i = 0; i < m_highObjects.Count; ++i)
{
if (string.IsNullOrEmpty(m_highObjects[i].Address) == false)
{
DestoryObject(m_highObjects[i].GameObject);
}
else if (m_highObjects[i].GameObject != null)
{
m_highObjects[i].GameObject.SetActive(false);
}
}
}
public int AddHighObject(string address, GameObject origin)
{
int id = m_highObjects.Count;
ChildObject obj = new ChildObject();
obj.GameObject = origin;
obj.Address = address;
obj.Parent = origin.transform.parent;
obj.Position = origin.transform.localPosition;
obj.Rotation = origin.transform.localRotation;
obj.Scale = origin.transform.localScale;
m_highObjects.Add(obj);
return id;
}
public int AddHighObject(GameObject gameObject)
{
int id = m_highObjects.Count;
ChildObject obj = new ChildObject();
obj.GameObject = gameObject;
m_highObjects.Add(obj);
return id;
}
public int AddLowObject(string address)
{
int id = m_lowObjects.Count;
m_lowObjects.Add(address);
return id;
}
public override int HighObjectCount
{
get => m_highObjects.Count;
}
public override int LowObjectCount
{
get => m_lowObjects.Count;
}
public string GetLowObjectAddr(int index)
{
return m_lowObjects[index];
}
public override void LoadHighObject(int id, Action<GameObject> loadDoneCallback)
{
if (m_highObjects[id].GameObject != null)
{
var gameObject = m_highObjects[id].GameObject;
ChangeLayersRecursively(gameObject.transform, m_hlodLayerIndex);
loadDoneCallback?.Invoke(gameObject);
}
else
{
List<Action<GameObject>> callbacks = new List<Action<GameObject>>();
callbacks.Add(loadDoneCallback);
callbacks.Add(o => { HighObjectCreated?.Invoke(o); });
LoadInfo loadInfo = Load(m_highObjects[id].Address, m_highObjects[id].Parent,
m_highObjects[id].Position, m_highObjects[id].Rotation, m_highObjects[id].Scale, callbacks);
m_highObjectLoadInfos.Add(id, loadInfo);
}
}
public override void LoadLowObject(int id, Action<GameObject> loadDoneCallback)
{
List<Action<GameObject>> callbacks = new List<Action<GameObject>>();
callbacks.Add(loadDoneCallback);
LoadInfo loadInfo = Load(m_lowObjects[id], m_hlodMeshesRoot.transform, Vector3.zero,
Quaternion.identity, Vector3.one, callbacks);
m_lowObjectLoadInfos.Add(id, loadInfo);
}
public override void UnloadHighObject(int id)
{
if (string.IsNullOrEmpty(m_highObjects[id].Address) == true)
{
m_highObjects[id].GameObject.SetActive(false);
}
else
{
if (m_highObjectLoadInfos.TryGetValue(id, out var loadInfo))
{
DestoryObject(loadInfo.Instance);
Unload(loadInfo);
m_highObjectLoadInfos.Remove(id);
}
else
{
Debug.LogError($"HighObject handle not found: {id}");
}
}
}
public override void UnloadLowObject(int id)
{
if (m_lowObjectLoadInfos.TryGetValue(id, out var loadInfo))
{
DestoryObject(loadInfo.Instance);
Unload(loadInfo);
m_lowObjectLoadInfos.Remove(id);
}
else
{
Debug.LogError($"LowObject handle not found: {id}");
}
}
private void DestoryObject(Object obj)
{
#if UNITY_EDITOR
DestroyImmediate(obj);
#else
Destroy(obj);
#endif
}
private static readonly Regex FileNameRegex = new Regex(
@".*/([^/]+)\.hlod\[([^\]]+)\]",
RegexOptions.Compiled
);
private static readonly Regex BracketRegex = new Regex(@"\[([^]]+)\]", RegexOptions.Compiled);
private LoadInfo Load(string address, Transform parent, Vector3 localPosition, Quaternion localRotation,
Vector3 localScale, List<Action<GameObject>> callbacks)
{
LoadInfo loadInfo = new LoadInfo();
address = address.Split('/')[^1];
loadInfo.Key = address;
Action<GameObject> loadDoneAction = (obj) =>
{
GameObject gameObject = Instantiate(obj, parent, false);
gameObject.transform.localPosition = localPosition;
gameObject.transform.localRotation = localRotation;
gameObject.transform.localScale = localScale;
gameObject.SetActive(false);
ChangeLayersRecursively(gameObject.transform, m_hlodLayerIndex);
loadInfo.Instance = gameObject;
foreach (var callback in callbacks)
{
callback?.Invoke(gameObject);
}
};
if (m_customLoader == null)
{
// TODO 这里需要优化提前存储address与subgameobject。
// 提取文件名部分
int lastSlashIndex = address.LastIndexOf('/');
int dotIndex = address.LastIndexOf('.');
string fileName = address.Substring(lastSlashIndex + 1, dotIndex - (lastSlashIndex + 1));
// 提取方括号内容(去掉方括号)
int openBracketIndex = address.IndexOf('[');
int closeBracketIndex = address.IndexOf(']');
string bracketContent = address.Substring(openBracketIndex + 1, closeBracketIndex - openBracketIndex - 1);
loadInfo.LoadFromCustom = false;
loadInfo.Handle = YooAssets.LoadAssetAsync<RootData>(fileName);
loadInfo.Handle.Completed += handle =>
{
if (handle.Status != EOperationStatus.Succeed)
{
Debug.LogError("Failed to load asset: " + address);
return;
}
RootData rootData = handle.AssetObject as RootData;
var ret = rootData.GetRootObject(bracketContent);
loadDoneAction(ret);
};
}
else
{
loadInfo.LoadFromCustom = true;
m_customLoader.CustomLoad(address, loadDoneAction);
}
return loadInfo;
}
private void Unload(LoadInfo info)
{
if (info.LoadFromCustom == false)
{
info.Handle.Dispose();
info.Handle = null;
}
else
{
m_customLoader.CustomUnload(info.Key);
}
}
static void ChangeLayersRecursively(Transform trans, int layer)
{
trans.gameObject.layer = layer;
foreach (Transform child in trans)
{
ChangeLayersRecursively(child, layer);
}
}
}
}