Files
Fishing2/Assets/KriptoFX/WaterSystem2/WaterResources/Scripts/FeaturesHD/Runtime/Ocean/MeshQuadTree.cs
2025-11-16 22:37:55 +08:00

729 lines
32 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Profiling;
namespace KWS
{
internal class MeshQuadTree
{
internal Dictionary<Camera, QuadTreeInstance> Instances = new Dictionary<Camera, QuadTreeInstance>();
internal class QuadTreeInstance : KW_Extensions.ICacheCamera
{
public bool CanRender => InstanceMeshesArgs.Count > 0 && InstanceMeshesArgs[0] != null;
public int ActiveMeshDetailingInstanceIndex = 0;
public List<GraphicsBuffer> InstanceMeshesArgs = new List<GraphicsBuffer>();
public List<GPUChunkInstance> VisibleGPUChunks = new List<GPUChunkInstance>();
public ComputeBuffer VisibleChunksComputeBuffer;
internal HashSet<Node> VisibleNodes = new HashSet<Node>();
internal HashSet<Node> VisibleZoneNodes = new HashSet<Node>();
internal Vector3 _lastCameraPosition = Vector3.positiveInfinity;
internal Vector3 _lastCameraForward;
internal int _lastCameraFarPlane;
internal int _lastCameraFOV;
public float _lastOceanLevel = float.PositiveInfinity;
internal Vector3 _camPos;
internal Vector3 _camForward;
internal int _camFarPlane;
internal int _camFOV;
internal float _maxDynamicZoneHeightOffset;
public Plane[] FrustumPlanes = new Plane[6];
public Vector3[] FrustumCorners = new Vector3[8];
public void Release()
{
for (var i = 0; i < InstanceMeshesArgs.Count; i++)
{
if (InstanceMeshesArgs[i] != null) InstanceMeshesArgs[i].Release();
InstanceMeshesArgs[i] = null;
}
InstanceMeshesArgs.Clear();
if (VisibleChunksComputeBuffer != null) VisibleChunksComputeBuffer.Release();
VisibleChunksComputeBuffer = null;
ActiveMeshDetailingInstanceIndex = 0;
_lastCameraPosition = Vector3.positiveInfinity;
_lastCameraForward = Vector3.forward;
_lastOceanLevel = float.PositiveInfinity;
}
}
public struct GPUChunkInstance
{
public Vector3 Position;
public Vector3 Size;
public uint DownSeam;
public uint LeftSeam;
public uint TopSeam;
public uint RightSeam;
public uint DownInf;
public uint LeftInf;
public uint TopInf;
public uint RightInf;
}
public struct QuadTreeRenderingContext
{
public ComputeBuffer visibleChunksComputeBuffer;
public GraphicsBuffer visibleChunksArgs;
public Mesh chunkInstance;
public Mesh underwaterMesh;
internal HashSet<Node> visibleNodes;
internal int activeMeshDetailingInstanceIndex;
}
public bool TryGetRenderingContext(Camera cam, bool isSimpleInstance, out QuadTreeRenderingContext context)
{
#if KWS_DEBUG
if ((KWS_Ocean.Instance != null && KWS_Ocean.Instance.DebugQuadtree)) cam = Camera.main;
#endif
if (cam != null && Instances.TryGetValue(cam, out var instance) && instance.CanRender)
{
var level = isSimpleInstance ? Mathf.Max(0, instance.ActiveMeshDetailingInstanceIndex - 3) : instance.ActiveMeshDetailingInstanceIndex;
context = new QuadTreeRenderingContext()
{
visibleChunksComputeBuffer = instance.VisibleChunksComputeBuffer,
visibleChunksArgs = instance.InstanceMeshesArgs[level],
chunkInstance = InstanceMeshes[level],
underwaterMesh = BottomUnderwaterSkirt,
visibleNodes = instance.VisibleNodes,
activeMeshDetailingInstanceIndex = instance.ActiveMeshDetailingInstanceIndex
};
return true;
}
else
{
context = default;
return false;
}
}
internal List<Mesh> InstanceMeshes = new List<Mesh>();
Mesh BottomUnderwaterSkirt;
List<float> _lodDistances;
private Bounds _quadTreeBounds;
private float _maxDynamicZoneHeightOffset;
private float _maxWavesHeight;
private float _waterLevel;
private int[] _lastQuadTreeChunkSizesRelativeToWind;
private Node _root;
private float _currentDisplacementOffset;
private bool _wideAngleMode;
static readonly int maxFiniteLevels = 8;
static readonly Vector3 InfiniteChunkMaxDistance = new Vector3(1000000, 0, 1000000);
private const int MaxQuadtreeInstancesCount = 10;
public bool IsInitialized = false;
Bounds GetQuadTreeOceanBounds()
{
var farDist = WaterSystem.QualitySettings.MeshDetailingFarDistance;
return new Bounds(new Vector3(0, 0-KWS_Settings.Mesh.MaxInfiniteOceanDepth, 0), new Vector3(farDist, KWS_Settings.Mesh.MaxInfiniteOceanDepth * 2, farDist));
}
public void Initialize(WaterSystem waterInstance)
{
Release();
_quadTreeBounds = GetQuadTreeOceanBounds();
_lodDistances = InitialiseInfiniteLodDistances(_quadTreeBounds.size, KWS_Settings.Mesh.QuadtreeInfiniteOceanMinDistance);
var leveledNodes = new List<LeveledNode>();
for (int i = 0; i < _lodDistances.Count; i++) leveledNodes.Add(new LeveledNode());
_maxWavesHeight = waterInstance.CurrentMaxOceanWaveHeight;
_wideAngleMode = WaterSystem.QualitySettings.WideAngleCameraRenderingMode;
_root = new Node(this, leveledNodes, 0, _quadTreeBounds.center, _quadTreeBounds.size);
InitializeNeighbors(leveledNodes);
_lastQuadTreeChunkSizesRelativeToWind = KWS_Settings.Water.QuadTreeChunkQuailityLevelsInfinite[WaterSystem.QualitySettings.WaterMeshDetailing];
for (int lodIndex = 0; lodIndex < _lastQuadTreeChunkSizesRelativeToWind.Length; lodIndex++)
{
var currentResolution = GetChunkLodResolution(_quadTreeBounds, lodIndex);
var instanceMesh = MeshUtils.GenerateInstanceMesh(currentResolution);
InstanceMeshes.Add(instanceMesh);
}
BottomUnderwaterSkirt = MeshUtils.GenerateUnderwaterBottomSkirt(Vector2Int.one);
IsInitialized = true;
this.WaterLog($"Initialized Quadtree waterInstance: {waterInstance}", KW_Extensions.WaterLogMessageType.Initialize);
}
public void UpdateQuadTree(Camera cam, WaterSystem waterInstance, bool forceUpdate = false)
{
if (_root == null) return;
var instance = Instances.GetCameraCache(cam, MaxQuadtreeInstancesCount);
var camT = cam.transform;
instance._camPos = camT.position;
instance._camForward = camT.forward;
instance._camFarPlane = (int)cam.farClipPlane;
instance._camFOV = (int)cam.fieldOfView;
if (!KWS_UpdateManager.FrustumCaches.TryGetValue(cam, out var cache))
{
Debug.LogError($"FrustumCaches doesn't have camera {cam}");
return;
}
instance.FrustumPlanes = cache.FrustumPlanes;
if (KWS_TileZoneManager.VisibleDynamicWavesZones.Count > 0 || KWS_TileZoneManager.VisibleLocalWaterZones.Count > 0) _maxDynamicZoneHeightOffset = KWS_TileZoneManager.MaxZoneHeight - waterInstance.WaterLevel;
else _maxDynamicZoneHeightOffset = 0;
if (!forceUpdate && !IsRequireUpdateQuadTree(instance, waterInstance)) return;
instance.VisibleNodes.Clear();
instance.VisibleGPUChunks.Clear();
instance.VisibleZoneNodes.Clear();
_waterLevel = waterInstance.WaterLevel;
_maxWavesHeight = waterInstance.CurrentMaxOceanWaveHeight;
_wideAngleMode = WaterSystem.QualitySettings.WideAngleCameraRenderingMode;
_currentDisplacementOffset = KWS_Settings.Mesh.UpdateQuadtreeEveryMetersForward + _maxWavesHeight * KWS_Settings.Mesh.QuadTreeAmplitudeDisplacementMultiplier;
_root.UpdateVisibleNodes(instance, this);
var camOffset = instance._camPos;
camOffset.y = _waterLevel;
var halfOffset = _quadTreeBounds.size.y * 0.5f;
if (KWS_Ocean.Instance == false)
{
var visibleZones = KWS_TileZoneManager.VisibleDynamicWavesZones;
foreach (var visibleNode in instance.VisibleNodes)
{
foreach (var iZone in visibleZones)
{
var zone = (KWS_DynamicWavesSimulationZone)iZone;
var updateDistanceThreshold = KWS_Settings.Mesh.UpdateQuadtreeEveryMetersForward;
var nodeBounds = new Bounds(visibleNode.ChunkCenter + camOffset + new Vector3(0, halfOffset, 0), visibleNode.ChunkSize + new Vector3(updateDistanceThreshold, 0, updateDistanceThreshold));
if (zone.ZoneType != KWS_DynamicWavesSimulationZone.SimulationZoneTypeMode.BakedSimulation && iZone.OrientedBounds.Intersects(nodeBounds))
{
instance.VisibleZoneNodes.Add(visibleNode);
}
}
}
}
// else
// {
// var visibleBakedZones = KWS_TileZoneManager.VisibleBakedDynamicWavesZones;
//
// foreach (var visibleNode in instance.VisibleNodes)
// {
// foreach (var zone in visibleBakedZones)
// {
// var updateDistanceThreshold = KWS_Settings.Mesh.UpdateQuadtreeEveryMetersForward;
// var nodeBounds = new Bounds(visibleNode.ChunkCenter + camOffset + new Vector3(0, halfOffset, 0), visibleNode.ChunkSize - new Vector3(updateDistanceThreshold, 0, updateDistanceThreshold));
// var zoneBounds = ((KWS_TileZoneManager.IWaterZone)zone).OrientedBounds;
// zoneBounds.size *= 0.5f;
// if (zoneBounds.Intersects(nodeBounds) == false)
// {
// instance.VisibleZoneNodes.Add(visibleNode);
// }
// }
// }
// }
var visibleNodes = KWS_Ocean.Instance != null ? instance.VisibleNodes : instance.VisibleZoneNodes;
foreach (var visibleNode in visibleNodes)
{
var meshData = new GPUChunkInstance();
var center = visibleNode.ChunkCenter;
meshData.Position = center;
meshData.Position.y += halfOffset;
meshData.Position += camOffset;
meshData.Size = visibleNode.ChunkSize;
meshData.Size.y = 1;
meshData = InitializeSeamDataRelativeToNeighbors(instance, ref meshData, visibleNode);
instance.VisibleGPUChunks.Add(meshData);
}
instance._lastCameraPosition = instance._camPos;
instance._lastCameraForward = instance._camForward;
instance._lastCameraFarPlane = instance._camFarPlane;
instance._lastCameraFOV = instance._camFOV;
instance._maxDynamicZoneHeightOffset = _maxDynamicZoneHeightOffset;
instance._lastOceanLevel = waterInstance.WaterLevel;
if (instance.InstanceMeshesArgs.Count == 0) instance.InstanceMeshesArgs.AddDefaultValues(InstanceMeshes.Count, null);
for (int i = 0; i < InstanceMeshes.Count; i++)
{
instance.InstanceMeshesArgs[i] = MeshUtils.InitializeInstanceArgsGraphicsBuffer(InstanceMeshes[i], instance.VisibleGPUChunks.Count, instance.InstanceMeshesArgs[i], KWS_CoreUtils.SinglePassStereoEnabled);
}
MeshUtils.InitializePropertiesBuffer(instance.VisibleGPUChunks, ref instance.VisibleChunksComputeBuffer, KWS_CoreUtils.SinglePassStereoEnabled);
UpdateQuadTreeDetailingRelativeToWind(instance, KWS_Ocean.Instance != null ? KWS_Ocean.Instance.WindSpeed : 0);
#if KWS_DEBUG
//this.WaterLog($"Update Quadtree waterInstance: {waterInstance} camera: {cam}", KW_Extensions.WaterLogMessageType.DynamicUpdate);
#endif
}
bool IsRequireUpdateQuadTree(QuadTreeInstance instance, WaterSystem waterInstance)
{
var distanceToCamera = Vector3.Distance(instance._camPos, instance._lastCameraPosition);
if (KW_Extensions.SqrMagnitudeFast(instance._camForward - instance._lastCameraForward) > KWS_Settings.Mesh.QuadtreeRotationThresholdForUpdate ||
distanceToCamera >= KWS_Settings.Mesh.UpdateQuadtreeEveryMetersForward ||
(IsCameraMoveBackwards(instance, instance._camPos, instance._camForward) && distanceToCamera >= KWS_Settings.Mesh.UpdateQuadtreeEveryMetersBackward) ||
Mathf.Abs(instance._lastOceanLevel - waterInstance.WaterLevel) > 0.001f ||
Math.Abs(_maxWavesHeight - waterInstance.CurrentMaxOceanWaveHeight) > 0.001f ||
instance._camFarPlane != instance._lastCameraFarPlane ||
instance._camFOV != instance._lastCameraFOV ||
!instance.CanRender
|| Math.Abs(instance._maxDynamicZoneHeightOffset - _maxDynamicZoneHeightOffset) > 0.1f) return true;
return false;
}
bool IsCameraMoveBackwards(QuadTreeInstance instance, Vector3 cameraPos, Vector3 forwardVector)
{
var direction = (cameraPos - instance._lastCameraPosition).normalized;
var angle = Vector3.Dot(direction, forwardVector);
return angle < -0.1;
}
public void UpdateQuadTreeDetailingRelativeToWind(QuadTreeInstance instance, float windSpeed)
{
var lodOffset = KWS_TileZoneManager.VisibleDynamicWavesZones.Count > 0 ? KWS_Settings.Water.QuadTreeChunkLodOffsetForDynamicWaves : 0;
var windScales = KWS_Settings.Water.QuadTreeChunkLodRelativeToWind;
var maxInstanceIdx = instance.InstanceMeshesArgs.Count - 1;
for (int i = 0; i < windScales.Length; i++)
{
if (windSpeed < windScales[i])
{
instance.ActiveMeshDetailingInstanceIndex = Mathf.Clamp(lodOffset + i, 0, maxInstanceIdx);
return;
}
}
instance.ActiveMeshDetailingInstanceIndex = maxInstanceIdx;
}
public void Release()
{
Instances.ReleaseCameraCache();
foreach (var instance in InstanceMeshes) KW_Extensions.SafeDestroy(instance);
InstanceMeshes.Clear();
IsInitialized = false;
KW_Extensions.SafeDestroy(BottomUnderwaterSkirt);
}
Vector2Int GetChunkLodResolution(Bounds bounds, int lodIndex)
{
Vector2Int chunkRes = Vector2Int.one;
int lodRes = _lastQuadTreeChunkSizesRelativeToWind[lodIndex];
var quarterRes = lodRes * 4;
chunkRes *= quarterRes;
return chunkRes;
}
void InitializeNeighbors(List<LeveledNode> leveledNodes)
{
foreach (var leveledNode in leveledNodes)
{
foreach (var pair in leveledNode.Chunks)
{
var chunk = pair.Value;
chunk.NeighborLeft = leveledNode.GetLeftNeighbor(chunk.UV);
chunk.NeighborRight = leveledNode.GetRightNeighbor(chunk.UV);
chunk.NeighborTop = leveledNode.GetTopNeighbor(chunk.UV);
chunk.NeighborDown = leveledNode.GetDownNeighbor(chunk.UV);
}
}
}
List<float> InitialiseInfiniteLodDistances(Vector3 size, float minLodDistance)
{
var maxSize = Mathf.Max(size.x, size.z);
var lodDistances = new List<float>();
var divider = 2f;
lodDistances.Add(float.MaxValue);
while (lodDistances[lodDistances.Count - 1] > minLodDistance)
{
lodDistances.Add(maxSize / divider);
divider *= 2;
}
return lodDistances;
}
internal class LeveledNode
{
public Dictionary<uint, Node> Chunks = new Dictionary<uint, Node>();
public void AddNodeToArray(Vector2Int uv, Node node)
{
node.UV = uv;
//long hashIdx = uv.x + uv.y * MaxLevelsRange;
var hashIdx = GetHashFromUV(uv);
if (!Chunks.ContainsKey(hashIdx)) Chunks.Add(hashIdx, node);
}
public Node GetLeftNeighbor(Vector2Int uv)
{
//long hashIdx = (uv.x - 1) + uv.y * MaxLevelsRange;
uv.x -= 1;
var hashIdx = GetHashFromUV(uv);
return Chunks.ContainsKey(hashIdx) ? Chunks[hashIdx] : null;
}
public Node GetRightNeighbor(Vector2Int uv)
{
//long hashIdx = (uv.x + 1) + uv.y * MaxLevelsRange;
uv.x += 1;
var hashIdx = GetHashFromUV(uv);
return Chunks.ContainsKey(hashIdx) ? Chunks[hashIdx] : null;
}
public Node GetTopNeighbor(Vector2Int uv)
{
// long hashIdx = uv.x + (uv.y + 1) * MaxLevelsRange;
uv.y += 1;
var hashIdx = GetHashFromUV(uv);
return Chunks.ContainsKey(hashIdx) ? Chunks[hashIdx] : null;
}
public Node GetDownNeighbor(Vector2Int uv)
{
//long hashIdx = uv.x + (uv.y - 1) * MaxLevelsRange;
uv.y -= 1;
var hashIdx = GetHashFromUV(uv);
return Chunks.ContainsKey(hashIdx) ? Chunks[hashIdx] : null;
}
uint GetHashFromUV(Vector2Int uv)
{
return (((uint)uv.x & 0xFFFF) << 16) | ((uint)uv.y & 0xFFFF);
}
}
GPUChunkInstance InitializeSeamDataRelativeToNeighbors(QuadTreeInstance instance, ref GPUChunkInstance meshData, Node node)
{
var topNeighbor = node.NeighborTop;
if (topNeighbor == null || !instance.VisibleNodes.Contains(topNeighbor) && instance.VisibleNodes.Contains(topNeighbor.Parent))
{
meshData.TopSeam = 1;
}
var leftNeighbor = node.NeighborLeft;
if (leftNeighbor == null || !instance.VisibleNodes.Contains(leftNeighbor) && instance.VisibleNodes.Contains(leftNeighbor.Parent))
{
meshData.LeftSeam = 1;
}
var downNeighbor = node.NeighborDown;
if (downNeighbor == null || !instance.VisibleNodes.Contains(downNeighbor) && instance.VisibleNodes.Contains(downNeighbor.Parent))
{
meshData.DownSeam = 1;
}
var rightNeighbor = node.NeighborRight;
if (rightNeighbor == null || !instance.VisibleNodes.Contains(rightNeighbor) && instance.VisibleNodes.Contains(rightNeighbor.Parent))
{
meshData.RightSeam = 1;
}
if (node.CurrentLevel <= 2)
{
if (topNeighbor == null)
{
meshData.TopInf = 1;
meshData.TopSeam = 0;
}
if (leftNeighbor == null)
{
meshData.LeftInf = 1;
meshData.LeftSeam = 0;
}
if (downNeighbor == null)
{
meshData.DownInf = 1;
meshData.DownSeam = 0;
}
if (rightNeighbor == null)
{
meshData.RightInf = 1;
meshData.RightSeam = 0;
}
}
return meshData;
}
static Vector2Int PositionToUV(Vector3 pos, Vector3 quadSize, int chunksCounts)
{
var uv = new Vector2(pos.x / quadSize.x, pos.z / quadSize.z); //range [-1.0 - 1.0]
var x = (int)((uv.x * 0.5f + 0.5f) * chunksCounts * 0.999);
var y = (int)((uv.y * 0.5f + 0.5f) * chunksCounts * 0.999);
x = Mathf.Clamp(x, 0, chunksCounts - 1);
y = Mathf.Clamp(y, 0, chunksCounts - 1);
return new Vector2Int(x, y);
}
internal class Node
{
public int CurrentLevel;
public Vector3 ChunkCenter;
public Vector3 ChunkSize;
public Node Parent;
public Node[] Children;
public Node NeighborLeft;
public Node NeighborRight;
public Node NeighborTop;
public Node NeighborDown;
public Vector2Int UV;
internal Node(MeshQuadTree root, List<LeveledNode> leveledNodes, int currentLevel, Vector3 quadTreeCenter, Vector3 quadTreeStartSize, Node parent = null)
{
Parent = parent ?? this;
ChunkCenter = (quadTreeCenter);
ChunkSize = quadTreeStartSize;
CurrentLevel = currentLevel;
// if (WaterSystem.QualitySettings.UseDetailedMeshAtDistance == false)
{
var maxDistanceForLevel = root._lodDistances[CurrentLevel];
if ((ChunkCenter - root._quadTreeBounds.center).magnitude > maxDistanceForLevel) return;
}
if (currentLevel < root._lodDistances.Count - 1)
{
Subdivide(root, leveledNodes);
}
}
void Subdivide(MeshQuadTree root, List<LeveledNode> leveledNodes)
{
var nextLevel = CurrentLevel + 1;
var quarterSize = ChunkSize / 4f;
var quadTreeHalfSize = new Vector3(ChunkSize.x / 2f, ChunkSize.y, ChunkSize.z / 2f);
var quadTreeRootHalfSize = new Vector3(root._quadTreeBounds.extents.x, root._quadTreeBounds.size.y, root._quadTreeBounds.extents.z);
int chunksCounts = (int)Mathf.Pow(2, nextLevel);
var level = leveledNodes[nextLevel];
Children = new Node[4];
AddQuadNodes(root, leveledNodes, quarterSize, nextLevel, quadTreeHalfSize, level, quadTreeRootHalfSize, chunksCounts);
}
private void AddQuadNodes(MeshQuadTree root, List<LeveledNode> leveledNodes, Vector3 quarterSize, int nextLevel, Vector3 quadTreeHalfSize, LeveledNode level, Vector3 quadTreeRootHalfSize, int chunksCounts)
{
var center = new Vector3(ChunkCenter.x - quarterSize.x, ChunkCenter.y, ChunkCenter.z + quarterSize.z);
Children[0] = new Node(root, leveledNodes, nextLevel, center, quadTreeHalfSize, this);
var uv1 = PositionToUV(center, quadTreeRootHalfSize, chunksCounts);
level.AddNodeToArray(uv1, Children[0]);
center = new Vector3(ChunkCenter.x + quarterSize.x, ChunkCenter.y, ChunkCenter.z + quarterSize.z); //right up
Children[1] = new Node(root, leveledNodes, nextLevel, center, quadTreeHalfSize, this);
var uv2 = PositionToUV(center, quadTreeRootHalfSize, chunksCounts);
level.AddNodeToArray(uv2, Children[1]);
center = new Vector3(ChunkCenter.x - quarterSize.x, ChunkCenter.y, ChunkCenter.z - quarterSize.z); //left down
Children[2] = new Node(root, leveledNodes, nextLevel, center, quadTreeHalfSize, this);
var uv3 = PositionToUV(center, quadTreeRootHalfSize, chunksCounts);
level.AddNodeToArray(uv3, Children[2]);
center = new Vector3(ChunkCenter.x + quarterSize.x, ChunkCenter.y, ChunkCenter.z - quarterSize.z); //right down
Children[3] = new Node(root, leveledNodes, nextLevel, center, quadTreeHalfSize, this);
var uv4 = PositionToUV(center, quadTreeRootHalfSize, chunksCounts);
level.AddNodeToArray(uv4, Children[3]);
}
internal enum ChunkVisibilityEnum
{
Visible,
NotVisibile,
NotVisibleLod,
PartialVisible
}
internal ChunkVisibilityEnum UpdateVisibleNodes(QuadTreeInstance instance, MeshQuadTree root)
{
var currentSize = ChunkSize;
//currentSize.y += Mathf.Max(root._maxWavesHeight, root._maxDynamicZoneHeightOffset);
var currentCenter = ChunkCenter;
currentCenter.y = root._waterLevel - ChunkSize.y * 0.5f;
RecalculateChunkDataRelativeToFrustum(root, instance._camPos, ref currentCenter, ref currentSize, out var min, out var max);
if (!root._wideAngleMode && !KW_Extensions.IsBoxVisibleApproximated(ref instance.FrustumPlanes, min, max)) return ChunkVisibilityEnum.NotVisibile;
var surfaceHeight = root._waterLevel + root._maxWavesHeight;
var distanceToCamera = Mathf.Abs(surfaceHeight - instance._camPos.y);
if (KWS_TileZoneManager.VisibleDynamicWavesZones.Count > 0)
{
float zoneMaxQualityLevelOffset = 20;
float zoneSurfaceHeight = root._waterLevel + Mathf.Max(root._maxWavesHeight, root._maxDynamicZoneHeightOffset) + zoneMaxQualityLevelOffset;
if (instance._camPos.y < zoneSurfaceHeight && instance._camPos.y > surfaceHeight) distanceToCamera = 1;
else
{
distanceToCamera = Mathf.Min(distanceToCamera, Mathf.Abs(zoneSurfaceHeight - instance._camPos.y));
}
}
var maxDistanceForLevel = root._lodDistances[CurrentLevel];
if (distanceToCamera > maxDistanceForLevel) return ChunkVisibilityEnum.NotVisibleLod;
// if (WaterSystem.QualitySettings.UseDetailedMeshAtDistance)
// {
// var size = currentSize.x;
// var maxLevelSubdivide = Mathf.Clamp((size / 20f), 4, 7);
// bool forceSubdivide = false;
//
//
// if (CurrentLevel > 2 && CurrentLevel < maxLevelSubdivide)
// {
// foreach (var zone in KWS_TileZoneManager.VisibleDynamicWavesZones)
// {
// var nodeBounds = new Bounds(currentCenter, currentSize);
// if (zone.Bounds.Intersects(nodeBounds))
// {
// var chunkPos = new Vector3(currentCenter.x, zone.Position.y, currentCenter.z);
// float dynamicZoneLevelNormalized = Mathf.Clamp01(((chunkPos - instance._camPos).magnitude) * 0.001f);
// float currentZoneLevelOffset = Mathf.Lerp(maxLevelSubdivide, 4, dynamicZoneLevelNormalized);
//
// if (CurrentLevel < currentZoneLevelOffset)
// {
// forceSubdivide = true;
// break;
// }
// }
// }
// }
//
//
// if ((ChunkCenter - root._quadTreeBounds.center).magnitude > maxDistanceForLevel && !forceSubdivide) return ChunkVisibilityEnum.NotVisibleLod;
// if (distanceToCamera > maxDistanceForLevel && !forceSubdivide) return ChunkVisibilityEnum.NotVisibleLod;
//
// }
// else
// {
// if (distanceToCamera > maxDistanceForLevel) return ChunkVisibilityEnum.NotVisibleLod;
// }
if (Children == null)
{
if (distanceToCamera < instance._camFarPlane * 2) instance.VisibleNodes.Add(this);
return ChunkVisibilityEnum.Visible;
}
foreach (var child in Children)
{
if (child != null && child.UpdateVisibleNodes(instance, root) == ChunkVisibilityEnum.NotVisibleLod)
{
if (distanceToCamera < instance._camFarPlane * 2) instance.VisibleNodes.Add(child);
}
}
return ChunkVisibilityEnum.PartialVisible;
}
private void RecalculateChunkDataRelativeToFrustum(MeshQuadTree root, Vector3 camPos, ref Vector3 currentCenter, ref Vector3 currentSize, out Vector3 min, out Vector3 max)
{
currentCenter.x += camPos.x;
currentCenter.z += camPos.z;
if (CurrentLevel <= 2 )
{
var virtualCenter = currentCenter;
var offset = InfiniteChunkMaxDistance;
var halfOffset = offset * 0.5f;
currentSize += offset;
if (NeighborLeft == null && NeighborTop == null) virtualCenter += new Vector3(-halfOffset.x, 0, halfOffset.z);
if (NeighborRight == null && NeighborTop == null) virtualCenter += new Vector3(halfOffset.x, 0, halfOffset.z);
if (NeighborLeft == null && NeighborDown == null) virtualCenter += new Vector3(-halfOffset.x, 0, -halfOffset.z);
if (NeighborRight == null && NeighborDown == null) virtualCenter += new Vector3(halfOffset.x, 0, -halfOffset.z);
GetMinMax(root, currentSize, virtualCenter, out min, out max);
}
else
{
GetMinMax(root, currentSize, currentCenter, out min, out max);
}
}
private void GetMinMax(MeshQuadTree root, Vector3 currentSize, Vector3 center, out Vector3 min, out Vector3 max)
{
var halfSize = currentSize / 2f;
halfSize.x += root._currentDisplacementOffset;
halfSize.z += root._currentDisplacementOffset;
halfSize.y += Mathf.Max(root._maxWavesHeight, root._maxDynamicZoneHeightOffset);
min = center - halfSize;
max = center + halfSize;
}
}
}
}