Firstborn/Assets/AwesomeTechnologies/VegetationStudioPro/Runtime/VegetationSystemPro/Biomes/Masks/PolygonBiomeMask.cs

364 lines
13 KiB
C#
Raw Normal View History

using System.Collections.Generic;
using UnityEngine;
using AwesomeTechnologies.Utility;
using AwesomeTechnologies.Utility.Quadtree;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace AwesomeTechnologies.VegetationSystem.Biomes
{
public class BiomeMaskSortOrderComparer : IComparer<PolygonBiomeMask>
{
public int Compare(PolygonBiomeMask x, PolygonBiomeMask y)
{
if (x != null && y != null)
{
return x.BiomeSortOrder.CompareTo(y.BiomeSortOrder);
}
else
{
return 0;
}
}
}
[BurstCompile(CompileSynchronously = true)]
public struct FilterBiomeSpawnLocationsJob : IJobParallelFor
{
public NativeArray<VegetationSpawnLocationInstance> SpawnLocationList;
[ReadOnly]
public NativeArray<float> CurveArray;
[ReadOnly]
public NativeArray<float> InverseCurveArray;
[ReadOnly]
public NativeArray<Vector2> PolygonArray;
[ReadOnly]
public NativeArray<LineSegment2D> SegmentArray;
public bool Include;
public bool UseNoise;
public float NoiseScale;
public float BlendDistance;
public Rect PolygonRect;
public void Execute(int index)
{
VegetationSpawnLocationInstance vegetationSpawnLocationInstance = SpawnLocationList[index];
float originalSpawnChance = vegetationSpawnLocationInstance.SpawnChance;
Vector2 point = new Vector2(SpawnLocationList[index].Position.x, SpawnLocationList[index].Position.z);
if (!PolygonRect.Contains(point)) return;
if (IsInPolygon(point))
{
vegetationSpawnLocationInstance.SpawnChance = math.select(
vegetationSpawnLocationInstance.SpawnChance =
math.min(0, vegetationSpawnLocationInstance.SpawnChance),
vegetationSpawnLocationInstance.SpawnChance =
math.max(1, vegetationSpawnLocationInstance.SpawnChance), Include);
float distanceToEdge = DistanceToEdge(point);
vegetationSpawnLocationInstance.BiomeDistance = math.select(vegetationSpawnLocationInstance.BiomeDistance, math.min(distanceToEdge, vegetationSpawnLocationInstance.BiomeDistance),Include);
if (distanceToEdge < BlendDistance)
{
float perlinNoise = math.select(1, Mathf.PerlinNoise(point.x / NoiseScale, point.y / NoiseScale), UseNoise);
perlinNoise = math.select(perlinNoise, 0, !Include && !UseNoise);
vegetationSpawnLocationInstance.SpawnChance = math.select(math.max((SampleInverseCurveArray(distanceToEdge / BlendDistance)) * (1 - perlinNoise), vegetationSpawnLocationInstance.SpawnChance), math.min(SampleCurveArray(distanceToEdge / BlendDistance) * perlinNoise, vegetationSpawnLocationInstance.SpawnChance), Include);
vegetationSpawnLocationInstance.SpawnChance = math.select(
math.min(vegetationSpawnLocationInstance.SpawnChance, originalSpawnChance),
math.max(vegetationSpawnLocationInstance.SpawnChance, originalSpawnChance), Include);
}
SpawnLocationList[index] = vegetationSpawnLocationInstance;
}
}
private float SampleCurveArray(float value)
{
if (CurveArray.Length == 0) return 0f;
int index = Mathf.RoundToInt((value) * CurveArray.Length);
index = Mathf.Clamp(index, 0, CurveArray.Length - 1);
return CurveArray[index];
}
private float SampleInverseCurveArray(float value)
{
if (InverseCurveArray.Length == 0) return 0f;
int index = Mathf.RoundToInt((value) * InverseCurveArray.Length);
index = Mathf.Clamp(index, 0, InverseCurveArray.Length - 1);
return InverseCurveArray[index];
}
private float DistanceToEdge(Vector2 point)
{
float distance = float.MaxValue;
for (int i = 0; i < SegmentArray.Length; i++)
{
if (SegmentArray[i].DisableEdge == 0)
{
distance = math.min(distance, SegmentArray[i].DistanceToPoint(point));
}
}
return distance;
}
private bool IsInPolygon(Vector2 p)
{
bool inside = false;
if (PolygonArray.Length < 3)
{
return false;
}
var oldPoint = new Vector2(
PolygonArray[PolygonArray.Length - 1].x, PolygonArray[PolygonArray.Length - 1].y);
for (int i = 0; i < PolygonArray.Length; i++)
{
var newPoint = new Vector2(PolygonArray[i].x, PolygonArray[i].y);
Vector2 p1;
Vector2 p2;
if (newPoint.x > oldPoint.x)
{
p1 = oldPoint;
p2 = newPoint;
}
else
{
p1 = newPoint;
p2 = oldPoint;
}
if ((newPoint.x < p.x) == (p.x <= oldPoint.x)
&& (p.y - (long)p1.y) * (p2.x - p1.x)
< (p2.y - (long)p1.y) * (p.x - p1.x))
{
inside = !inside;
}
oldPoint = newPoint;
}
return inside;
}
}
public class PolygonBiomeMask
{
public Bounds MaskBounds;
public BiomeType BiomeType;
public float BlendDistance;
public bool UseNoise;
public float NoiseScale;
public int BiomeSortOrder;
private Rect _polygonRect;
public delegate void MultionMaskDeleteDelegate(PolygonBiomeMask maskArea);
public MultionMaskDeleteDelegate OnMaskDeleteDelegate;
public void CallDeleteEvent()
{
OnMaskDeleteDelegate?.Invoke(this);
}
private Vector2[] _points2D;
private LineSegment2D[] _segments;
private Vector3[] _points3D;
public NativeArray<Vector2> PolygonArray;
public NativeArray<LineSegment2D> SegmentArray;
public NativeArray<float> CurveArray;
public NativeArray<float> InverseCurveArray;
public NativeArray<float> TextureCurveArray;
private bool[] _disableEdges;
//public NativeArray<float> TextureInverseCurveArray;
public void AddPolygon(List<Vector3> pointList, List<bool> disableEdgeList)
{
_disableEdges = disableEdgeList.ToArray();
_points2D = new Vector2[pointList.Count];
_points3D = new Vector3[pointList.Count];
for (int i = 0; i <= pointList.Count - 1; i++)
{
_points2D[i] = new Vector2(pointList[i].x, pointList[i].z);
_points3D[i] = pointList[i];
}
MaskBounds = GetMaskBounds();
PolygonArray = new NativeArray<Vector2>(_points2D.Length,Allocator.Persistent);
PolygonArray.CopyFrom(_points2D);
CreateSegments();
// Bounds bounds = new Bounds();
// for (int i = 0; i <= pointList.Count - 1; i++)
// {
// if (i == 0)
// {
// bounds = new Bounds(pointList[i],Vector3.zero);
// }
// else
// {
// bounds.Encapsulate(pointList[i]);
// }
// }
_polygonRect = RectExtension.CreateRectFromBounds(MaskBounds);
}
public void SetCurve(float[] curveArray)
{
CurveArray = new NativeArray<float>(curveArray.Length, Allocator.Persistent);
CurveArray.CopyFrom(curveArray);
}
public void SetInverseCurve(float[] curveArray)
{
InverseCurveArray = new NativeArray<float>(curveArray.Length, Allocator.Persistent);
InverseCurveArray.CopyFrom(curveArray);
}
public void SetTextureCurve(float[] curveArray)
{
TextureCurveArray = new NativeArray<float>(curveArray.Length, Allocator.Persistent);
TextureCurveArray.CopyFrom(curveArray);
}
//public void SetTextureInverseCurve(float[] curveArray)
//{
// TextureInverseCurveArray = new NativeArray<float>(curveArray.Length, Allocator.Persistent);
// TextureInverseCurveArray.CopyFrom(curveArray);
//}
void CreateSegments()
{
_segments = new LineSegment2D[_points2D.Length];
for (int i = 0; i <= _points2D.Length - 2; i++)
{
LineSegment2D lineSegment2D = new LineSegment2D(_points2D[i], _points2D[i+1]);
_segments[i] = lineSegment2D;
if (_disableEdges[i] && _disableEdges[i + 1])
{
_segments[i].DisableEdge = 1;
}
}
if (_points2D.Length > 0)
{
LineSegment2D lineSegment2D = new LineSegment2D(_points2D[0], _points2D[_points2D.Length -1]);
_segments[_points2D.Length - 1] = lineSegment2D;
if (_disableEdges[0] && _disableEdges[_points2D.Length -1])
{
_segments[_points2D.Length - 1].DisableEdge = 1;
}
}
SegmentArray = new NativeArray<LineSegment2D>(_segments.Length, Allocator.Persistent);
SegmentArray.CopyFrom(_segments);
}
public bool Contains(Vector3 point)
{
if (!PolygonArray.IsCreated) return false;
Vector2 point2D = new Vector2(point.x,point.z);
return IsInPolygon(point2D);
}
public JobHandle FilterSpawnLocations(NativeList<VegetationSpawnLocationInstance> spawnLocationList, BiomeType currentBiomeType, int sampleCount,JobHandle dependsOn)
{
FilterBiomeSpawnLocationsJob filterBiomeSpawnLocationsJob =
new FilterBiomeSpawnLocationsJob
{
SpawnLocationList = spawnLocationList,
PolygonArray = PolygonArray,
SegmentArray = SegmentArray,
Include = currentBiomeType == BiomeType,
BlendDistance = BlendDistance,
UseNoise = UseNoise,
NoiseScale = NoiseScale,
CurveArray = CurveArray,
InverseCurveArray = InverseCurveArray,
PolygonRect = _polygonRect
};
dependsOn = filterBiomeSpawnLocationsJob.Schedule(sampleCount, 64, dependsOn);
return dependsOn;
}
private Bounds GetMaskBounds()
{
var expandedBounds = _points3D.Length > 0 ? new Bounds(_points3D[0], new Vector3(1, 1, 1)) : new Bounds(new Vector3(0, 0, 0), new Vector3(1, 1, 1));
for (int i = 0; i <= _points3D.Length - 1; i++)
{
expandedBounds.Encapsulate(_points3D[i]);
}
return expandedBounds;
}
public void Dispose()
{
if (PolygonArray.IsCreated) PolygonArray.Dispose();
if (SegmentArray.IsCreated) SegmentArray.Dispose();
if (CurveArray.IsCreated) CurveArray.Dispose();
if (InverseCurveArray.IsCreated) InverseCurveArray.Dispose();
if (TextureCurveArray.IsCreated) TextureCurveArray.Dispose();
//if (TextureInverseCurveArray.IsCreated) TextureInverseCurveArray.Dispose();
}
bool IsInPolygon(Vector2 p)
{
bool inside = false;
if (PolygonArray.Length < 3)
{
return false;
}
var oldPoint = new Vector2(
PolygonArray[PolygonArray.Length - 1].x, PolygonArray[PolygonArray.Length - 1].y);
for (int i = 0; i < PolygonArray.Length; i++)
{
var newPoint = new Vector2(PolygonArray[i].x, PolygonArray[i].y);
Vector2 p1;
Vector2 p2;
if (newPoint.x > oldPoint.x)
{
p1 = oldPoint;
p2 = newPoint;
}
else
{
p1 = newPoint;
p2 = oldPoint;
}
if ((newPoint.x < p.x) == (p.x <= oldPoint.x)
&& (p.y - (long)p1.y) * (p2.x - p1.x)
< (p2.y - (long)p1.y) * (p.x - p1.x))
{
inside = !inside;
}
oldPoint = newPoint;
}
return inside;
}
}
}