176 lines
4.9 KiB
C#
176 lines
4.9 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BITKit.SceneManagement;
|
|
using Cysharp.Threading.Tasks;
|
|
using Quadtree;
|
|
using UnityEngine;
|
|
using UnityEngine.Pool;
|
|
|
|
|
|
namespace BITKit.OpenWorld
|
|
{
|
|
public class WorldChunkService<T> : MonoBehaviour
|
|
where T : WorldChunkService<T>
|
|
{
|
|
|
|
public static T Singleton { get; private set; }
|
|
private readonly ConcurrentQueue<IWorldChunkObject> _registerQueue = new();
|
|
private readonly ConcurrentQueue<IWorldChunkObject> _unregisterQueue = new();
|
|
public void Register(IWorldChunkObject obj) => _registerQueue.Enqueue(obj);
|
|
public void Unregister(IWorldChunkObject obj) => _unregisterQueue.Enqueue(obj);
|
|
protected QuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> _quadtree;
|
|
private readonly ConcurrentDictionary<int, IWorldChunkObject> dictionary=new();
|
|
|
|
[SerializeReference, SubclassSelector] private ITicker ticker;
|
|
[SerializeField, ReadOnly] private int count;
|
|
[SerializeField, ReadOnly] private int tickTaskCount;
|
|
[SerializeField] private bool drawBounds;
|
|
[SerializeField, Range(0, 1024)] private int[] lodDistances;
|
|
[SerializeField] private Vector3 size;
|
|
private Camera _camera;
|
|
private readonly HashSet<int> cacheList = new();
|
|
private readonly Queue<(int, IWorldChunkObject)> lodQueue=new();
|
|
private bool isBusy;
|
|
protected virtual void Awake()
|
|
{
|
|
Singleton = (T) this;
|
|
}
|
|
|
|
protected virtual void Start()
|
|
{
|
|
ticker.Add(OnTick);
|
|
destroyCancellationToken.Register(Dispose);
|
|
_quadtree = new QuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>>(transform.position, size);
|
|
_camera = Camera.main;
|
|
}
|
|
protected virtual void Dispose()
|
|
{
|
|
ticker.Remove(OnTick);
|
|
_registerQueue.Clear();
|
|
_unregisterQueue.Clear();
|
|
}
|
|
protected virtual int CalculateLod(IWorldChunkObject value,Vector3 cameraPosition,int lod)=>lod;
|
|
protected virtual async void OnTick(float deltaTime)
|
|
{
|
|
if (!enabled) return;
|
|
if (isBusy) return;
|
|
|
|
isBusy = true;
|
|
while (_unregisterQueue.TryDequeue(out var obj))
|
|
{
|
|
_quadtree.Remove(obj);
|
|
dictionary.Remove(obj.Id, out _);
|
|
}
|
|
|
|
while (_registerQueue.TryDequeue(out var obj))
|
|
{
|
|
obj.Id = count++;
|
|
_quadtree.Insert(obj);
|
|
dictionary.TryAdd(obj.Id, obj);
|
|
}
|
|
|
|
var cameraPosition = _camera.transform.position;
|
|
|
|
var cycle = 0;
|
|
|
|
await UniTask.SwitchToThreadPool();
|
|
|
|
var items = _quadtree.Find(new Bounds(cameraPosition, Vector3.one * (lodDistances[^1] + lodDistances[0])));
|
|
foreach (var chunkObject in _quadtree.Find(new Bounds(cameraPosition, Vector3.one * (lodDistances[^1]+lodDistances[0]))))
|
|
{
|
|
if (cycle++ > 64)
|
|
{
|
|
cycle = 0;
|
|
await UniTask.NextFrame();
|
|
if (destroyCancellationToken.IsCancellationRequested) return;
|
|
}
|
|
|
|
var distance = Vector3.Distance(cameraPosition, chunkObject.GetBounds().center);
|
|
//var distance = Vector3.Distance(cameraPosition, chunkObject.GetBounds().ClosestPoint(cameraPosition));
|
|
if (!cacheList.Add(chunkObject.Id)) continue;
|
|
|
|
var lod = -1;
|
|
|
|
if (chunkObject.GetBounds().Contains(cameraPosition))
|
|
{
|
|
lod = 0;
|
|
}
|
|
else
|
|
{
|
|
for (var i = 0; i < lodDistances.Length; i++)
|
|
{
|
|
if (!(distance < lodDistances[i])) continue;
|
|
lod = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
lod = CalculateLod(chunkObject,cameraPosition,lod);
|
|
|
|
if(chunkObject.Lod==lod)continue;
|
|
//chunkObject.Lod = lod;
|
|
lodQueue.Enqueue((lod,chunkObject));
|
|
}
|
|
// for (var index = 0; index < lodDistances.Length; index++)
|
|
// {
|
|
// var distance = lodDistances[index];
|
|
// foreach (var chunkObject in _quadtree.Find(new Bounds(cameraPosition, Vector3.one * distance)))
|
|
// {
|
|
// if (cacheList.Contains(chunkObject.Id)) continue;
|
|
// cacheList.Add(chunkObject.Id);
|
|
//
|
|
// var lod = chunkObject.Lod;
|
|
// if (lod == index) continue;
|
|
// try
|
|
// {
|
|
// chunkObject.Lod = index;
|
|
// }
|
|
// catch (Exception e)
|
|
// {
|
|
// BIT4Log.LogException(e);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
await UniTask.SwitchToMainThread();
|
|
tickTaskCount = lodQueue.Count;
|
|
if (destroyCancellationToken.IsCancellationRequested) return;
|
|
|
|
cycle = 0;
|
|
while (lodQueue.TryDequeue(out var value))
|
|
{
|
|
if (cycle++ > 8)
|
|
{
|
|
cycle = 0;
|
|
await UniTask.NextFrame();
|
|
if (destroyCancellationToken.IsCancellationRequested) return;
|
|
}
|
|
value.Item2.Lod = value.Item1;
|
|
}
|
|
|
|
isBusy = false;
|
|
cacheList.Clear();
|
|
}
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
var pos = transform.position;
|
|
Gizmos.DrawWireCube(pos,transform.rotation * size);
|
|
|
|
if(drawBounds)
|
|
_quadtree.CurrentRootNode.DrawBounds(true);
|
|
|
|
if(!_camera)return;
|
|
|
|
var cameraPosition = _camera.transform.position;
|
|
|
|
foreach (var VARIABLE in lodDistances)
|
|
{
|
|
Gizmos.DrawWireSphere(cameraPosition, VARIABLE);
|
|
}
|
|
}
|
|
}
|
|
}
|