Firstborn/Assets/AwesomeTechnologies/VegetationStudioPro/Runtime/Utility/Jobs/FrustumLODJobs.cs

394 lines
17 KiB
C#
Raw Normal View History

using AwesomeTechnologies.VegetationSystem;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
namespace AwesomeTechnologies.Utility
{
[BurstCompile(CompileSynchronously = true)]
public struct VegetationItemLODSplitAndFrustumCullingJob : IJob
{
[ReadOnly]
public NativeList<MatrixInstance> VegetationItemMatrixList;
[ReadOnly]
public NativeArray<Plane> FrustumPlanes;
public NativeList<Matrix4x4> VegetationItemLOD0MatrixList;
public NativeList<Matrix4x4> VegetationItemLOD1MatrixList;
public NativeList<Matrix4x4> VegetationItemLOD2MatrixList;
public NativeList<Matrix4x4> VegetationItemLOD3MatrixList;
public NativeList<Matrix4x4> VegetationItemLOD0ShadowMatrixList;
public NativeList<Matrix4x4> VegetationItemLOD1ShadowMatrixList;
public NativeList<Matrix4x4> VegetationItemLOD2ShadowMatrixList;
public NativeList<Matrix4x4> VegetationItemLOD3ShadowMatrixList;
public NativeList<Vector4> LOD0FadeList;
public NativeList<Vector4> LOD1FadeList;
public NativeList<Vector4> LOD2FadeList;
public NativeList<Vector4> LOD3FadeList;
public Vector3 LightDirection;
public Vector3 PlaneOrigin;
public Vector3 BoundsSize;
public bool ShadowCulling;
public bool NoFrustumCulling;
public float CullDistance;
public float3 CameraPosition;
public float BoundingSphereRadius;
public int VegetationItemDistanceBand;
public float LODFactor;
public float LODBias;
public float LODFadeDistance;
public float LOD1Distance;
public float LOD2Distance;
public float LOD3Distance;
public int LODCount;
public bool LODFadePercentage;
public bool LODFadeCrossfade;
public Vector3 FloatingOriginOffset;
public void Execute()
{
for (int i = VegetationItemMatrixList.Length - 1; i >= 0; i--)
{
MatrixInstance vegetationItemMatrixInstance = VegetationItemMatrixList[i];
vegetationItemMatrixInstance.Matrix = TranslateMatrix(vegetationItemMatrixInstance.Matrix, FloatingOriginOffset);
float distanceFactor = vegetationItemMatrixInstance.DistanceFalloff;
float itemCullDistance = CullDistance * distanceFactor;
float lod1Distance = math.clamp(LOD1Distance * LODFactor * LODBias, 0, itemCullDistance);
float lod2Distance = math.clamp(LOD2Distance * LODFactor * LODBias, 0, itemCullDistance);
float lod3Distance = math.clamp(LOD3Distance * LODFactor * LODBias, 0, itemCullDistance);
switch (LODCount)
{
case 1:
lod1Distance = math.max(lod1Distance, itemCullDistance);
break;
case 2:
lod2Distance = math.max(lod2Distance, itemCullDistance);
break;
case 3:
lod3Distance = math.max(lod3Distance, itemCullDistance);
break;
}
bool useLODFade = true;//LODFadePercentage || LODFadeCrossfade;
float3 position = ExtractTranslationFromMatrix(vegetationItemMatrixInstance.Matrix);
float distance = math.distance(CameraPosition, position);
if (distance > itemCullDistance + LODFadeDistance) continue;
float distanceFade = CalculateDistanceFade(distance, itemCullDistance);
if (NoFrustumCulling)
{
if (distance <= lod1Distance || LODCount == 1)
{
VegetationItemLOD0MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFade(distance, lod1Distance);
float lodFadeQuantified = 1 - Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD0FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
else if (distance <= lod2Distance || LODCount == 2)
{
VegetationItemLOD1MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFade(distance, lod2Distance);
float lodFadeQuantified = 1 - Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD1FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
else if (distance <= lod3Distance || LODCount == 3)
{
VegetationItemLOD2MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFade(distance, lod3Distance);
float lodFadeQuantified = 1 - Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD2FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
else
{
VegetationItemLOD3MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFade(distance, itemCullDistance);
float lodFadeQuantified = 1 - Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD3FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
continue;
}
BoundingSphere boundingSphere = new BoundingSphere(position, BoundingSphereRadius);
if (SphereInFrustum(boundingSphere) == -1)
{
if (VegetationItemDistanceBand == 0 || !ShadowCulling) continue;
//TODO add LODFade for shadows
Bounds vegetationItemBounds = new Bounds(position, BoundsSize);
if (IsShadowVisible(vegetationItemBounds, LightDirection, PlaneOrigin, FrustumPlanes))
{
if (distance <= lod1Distance || LODCount == 1)
{
VegetationItemLOD0ShadowMatrixList.Add(vegetationItemMatrixInstance.Matrix);
}
else if (distance <= lod2Distance || LODCount == 2)
{
VegetationItemLOD1ShadowMatrixList.Add(vegetationItemMatrixInstance.Matrix);
}
else if (distance <= lod3Distance || LODCount == 3)
{
VegetationItemLOD2ShadowMatrixList.Add(vegetationItemMatrixInstance.Matrix);
}
else
{
VegetationItemLOD3ShadowMatrixList.Add(vegetationItemMatrixInstance.Matrix);
}
}
}
else
{
if (LODCount == 1)
{
lod1Distance = math.max(lod1Distance, itemCullDistance);
}
else if (LODCount == 2)
{
lod2Distance = math.max(lod2Distance, itemCullDistance);
}
else if (LODCount == 3)
{
lod3Distance = math.max(lod3Distance, itemCullDistance);
}
if (distance <= lod1Distance + LODFadeDistance)
{
VegetationItemLOD0MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFadeFirst(distance, lod1Distance);
float lodFadeQuantified = 1-Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD0FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
if (distance <= lod2Distance + LODFadeDistance && distance > lod1Distance)
{
VegetationItemLOD1MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFadeMiddle(lod1Distance, distance, lod2Distance);
float lodFadeQuantified = 1-Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD1FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
if (distance <= lod3Distance + LODFadeDistance && distance > lod2Distance)
{
VegetationItemLOD2MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFadeMiddle(lod2Distance, distance, lod3Distance);
float lodFadeQuantified = 1-Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD2FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
if (distance > lod3Distance)
{
VegetationItemLOD3MatrixList.Add(vegetationItemMatrixInstance.Matrix);
if (useLODFade)
{
float lodFade = CalculateLODFadeMiddle(lod3Distance, distance, itemCullDistance);
float lodFadeQuantified = 1 - Mathf.Clamp(Mathf.RoundToInt(lodFade * 16) / 16f, 0.0625f, 1f);
LOD3FadeList.Add(new Vector4(lodFade, lodFadeQuantified, 0, 0));
}
}
}
}
}
float CalculateDistanceFade(float cameraDistance, float cullDistance)
{
// float distance = cullDistance - cameraDistance;
// if (distance <= LODFadeDistance)
// {
// return math.clamp(1f - distance/LODFadeDistance,0f,1f);
// }
return 0;
}
int SphereInFrustum(BoundingSphere boundingSphere)
{
for (int i = 0; i <= FrustumPlanes.Length - 1; i++)
{
float dist = FrustumPlanes[i].normal.x * boundingSphere.position.x +
FrustumPlanes[i].normal.y * boundingSphere.position.y +
FrustumPlanes[i].normal.z * boundingSphere.position.z + FrustumPlanes[i].distance;
if (dist < -boundingSphere.radius)
{
return -1;
}
}
return 1;
}
float3 ExtractTranslationFromMatrix(Matrix4x4 matrix)
{
float3 translate;
translate.x = matrix.m03;
translate.y = matrix.m13;
translate.z = matrix.m23;
return translate;
}
Matrix4x4 TranslateMatrix(Matrix4x4 matrix, float3 offset)
{
Matrix4x4 translatedMatrix = matrix;
translatedMatrix.m03 = matrix.m03 + offset.x;
translatedMatrix.m13 = matrix.m13 + offset.y;
translatedMatrix.m23 = matrix.m23 + offset.z;
return translatedMatrix;
}
float CalculateLODFade(float cameraDistance, float nextLODDistance)
{
float distance = nextLODDistance - cameraDistance;
if (distance <= LODFadeDistance)
{
return Mathf.Clamp01(1 - distance / LODFadeDistance);
}
return 0;
}
float CalculateLODFadeFirst(float cameraDistance, float nextLODDistance)
{
float distance = nextLODDistance + LODFadeDistance - cameraDistance;
if (distance <= LODFadeDistance)
{
return math.clamp(distance / LODFadeDistance, 0, 1) * 2;
}
return 1;
}
float CalculateLODFadeMiddle(float thisLODDistance, float cameraDistance, float nextLODDistance)
{
if (cameraDistance - thisLODDistance < LODFadeDistance)
{
float distance = cameraDistance - thisLODDistance;
return math.clamp(distance / LODFadeDistance, 0, 1) * 2;
}
if (nextLODDistance + LODFadeDistance - cameraDistance <= LODFadeDistance)
{
float distance = nextLODDistance + LODFadeDistance - cameraDistance;
return math.clamp(distance / LODFadeDistance, 0, 1) * 2;
}
return 1;
}
public static bool IsShadowVisible(Bounds objectBounds, Vector3 lightDirection, Vector3 planeOrigin, NativeArray<Plane> frustumPlanes)
{
// ReSharper disable once InlineOutVariableDeclaration
bool hitPlane;
Bounds shadowBounds = GetShadowBounds(objectBounds, lightDirection, planeOrigin, out hitPlane);
return hitPlane && BoundsIntersectsFrustum(frustumPlanes, shadowBounds);
}
public static Bounds GetShadowBounds(Bounds objectBounds, Vector3 lightDirection, Vector3 planeOrigin, out bool hitPlane)
{
Ray p0 = new Ray(new Vector3(objectBounds.min.x, objectBounds.max.y, objectBounds.min.z), lightDirection);
Ray p1 = new Ray(new Vector3(objectBounds.min.x, objectBounds.max.y, objectBounds.max.z), lightDirection);
Ray p2 = new Ray(new Vector3(objectBounds.max.x, objectBounds.max.y, objectBounds.min.z), lightDirection);
Ray p3 = new Ray(objectBounds.max, lightDirection);
// ReSharper disable once InlineOutVariableDeclaration
Vector3 hitPoint;
hitPlane = false;
if (IntersectPlane(p0, planeOrigin, out hitPoint))
{
objectBounds.Encapsulate(hitPoint);
hitPlane = true;
}
if (IntersectPlane(p1, planeOrigin, out hitPoint))
{
objectBounds.Encapsulate(hitPoint);
hitPlane = true;
}
if (IntersectPlane(p2, planeOrigin, out hitPoint))
{
objectBounds.Encapsulate(hitPoint);
hitPlane = true;
}
if (IntersectPlane(p3, planeOrigin, out hitPoint))
{
objectBounds.Encapsulate(hitPoint);
hitPlane = true;
}
return objectBounds;
}
public static bool IntersectPlane(Ray ray, Vector3 planeOrigin, out Vector3 hitPoint)
{
Vector3 planeNormal = -Vector3.up;
float denominator = Vector3.Dot(ray.direction, planeNormal);
if (denominator > 0.00001f)
{
float t = Vector3.Dot(planeOrigin - ray.origin, planeNormal) / denominator;
hitPoint = ray.origin + ray.direction * t;
return true;
}
hitPoint = Vector3.zero;
return false;
}
public static bool BoundsIntersectsFrustum(NativeArray<Plane> planes, Bounds bounds)
{
var center = bounds.center;
var extents = bounds.extents;
for (int i = 0; i <= planes.Length - 1; i++)
{
Vector3 planeNormal = planes[i].normal;
float planeDistance = planes[i].distance;
Vector3 abs = new Vector3(Mathf.Abs(planeNormal.x), Mathf.Abs(planeNormal.y), Mathf.Abs(planeNormal.z));
float r = extents.x * abs.x + extents.y * abs.y + extents.z * abs.z;
float s = planeNormal.x * center.x + planeNormal.y * center.y + planeNormal.z * center.z;
if (s + r < -planeDistance)
{
return false;
}
}
return true;
}
}
}