312 lines
9.8 KiB
C#
312 lines
9.8 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
} |