959e80cf72
assets upload description.
852 lines
33 KiB
C#
852 lines
33 KiB
C#
using System.Diagnostics.Contracts;
|
|
using AwesomeTechnologies.Utility;
|
|
using AwesomeTechnologies.Vegetation.PersistentStorage;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
|
|
// ReSharper disable RedundantNameQualifier
|
|
|
|
namespace AwesomeTechnologies.VegetationSystem
|
|
{
|
|
[BurstCompile]
|
|
public struct ClearInstanceMemoryJob : IJobParallelFor
|
|
{
|
|
public NativeArray<VegetationInstance> VegetationInstanceList;
|
|
public void Execute(int index)
|
|
{
|
|
VegetationInstanceList[index] = new VegetationInstance();
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
public struct InitInstanceData : IJobParallelFor
|
|
{
|
|
public NativeArray<byte> HeightmapSampled;
|
|
public NativeArray<byte> Excluded;
|
|
public void Execute(int index)
|
|
{
|
|
HeightmapSampled[index] = 0;
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
public struct FilterIncludeMaskJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> TextureMaskData;
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
if (TextureMaskData[index] == 0)
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct VegetationInstance
|
|
{
|
|
public float3 Position; //12 bytes ok
|
|
public quaternion Rotation; //16 bytes ok
|
|
public float3 Scale; //12 bytes ok
|
|
public float3 TerrainNormal; //12 bytes ok
|
|
public float BiomeDistance; //4 bytes ok
|
|
public byte TerrainTextureData; //1 bytes ok
|
|
public int RandomNumberIndex; //4 bytes ok
|
|
public float DistanceFalloff; //4 bytes ok
|
|
public float VegetationMaskDensity; //4 bytes ok
|
|
public float VegetationMaskScale; //4 bytes ok
|
|
public byte TerrainSourceID; //1 byte ok
|
|
public byte TextureMaskData; //1 byte ok
|
|
public byte Excluded; //1 byte ok
|
|
public byte HeightmapSampled; //1 byte ok
|
|
}
|
|
|
|
|
|
|
|
|
|
public struct VegetationSpawnLocationInstance
|
|
{
|
|
public float3 Position;
|
|
public float SpawnChance;
|
|
public int RandomNumberIndex;
|
|
public float BiomeDistance;
|
|
}
|
|
|
|
// public struct VegetationInstanceTerrainInfo
|
|
// {
|
|
// public float TerrainHeight;
|
|
// public float3 TerrainNormal;
|
|
// }
|
|
|
|
// [BurstCompile(CompileSynchronously = true)]
|
|
// public struct OffsetAndRotateScaleVegetationInstanceJob : IJobParallelFor
|
|
// {
|
|
// public NativeArray<VegetationInstance> VegetationInstanceList;
|
|
// [ReadOnly] public NativeArray<float> RandomNumbers;
|
|
// public VegetationRotationType VegetationRotationType;
|
|
//
|
|
// public float MinScale;
|
|
// public float MaxScale;
|
|
//
|
|
// public Vector3 Offset;
|
|
// public Vector3 RotationOffset;
|
|
// public Vector3 ScaleMultiplier;
|
|
//
|
|
// public void Execute(int index)
|
|
// {
|
|
// VegetationInstance vegetationInstance = VegetationInstanceList[index];
|
|
// Vector3 lookAt;
|
|
// switch (VegetationRotationType)
|
|
// {
|
|
// case VegetationRotationType.RotateY:
|
|
// vegetationInstance.Rotation = UnityEngine.Quaternion.Euler(new Vector3(0,
|
|
// RandomRange(vegetationInstance.RandomNumberIndex, 0, 365f), 0));
|
|
// vegetationInstance.RandomNumberIndex++;
|
|
// break;
|
|
// case VegetationRotationType.RotateXYZ:
|
|
// vegetationInstance.Rotation = UnityEngine.Quaternion.Euler(new Vector3(
|
|
// RandomRange(vegetationInstance.RandomNumberIndex, 0, 365f),
|
|
// RandomRange(vegetationInstance.RandomNumberIndex + 1, 0, 365f),
|
|
// RandomRange(vegetationInstance.RandomNumberIndex + 2, 0, 365f)));
|
|
// vegetationInstance.RandomNumberIndex += 3;
|
|
// break;
|
|
// case VegetationRotationType.FollowTerrain:
|
|
// lookAt = math.cross(-vegetationInstance.TerrainNormal, new Vector3(1, 0, 0));
|
|
// if (lookAt.y < 0) lookAt = -lookAt;
|
|
// vegetationInstance.Rotation =
|
|
// UnityEngine.Quaternion.LookRotation(lookAt, vegetationInstance.TerrainNormal);
|
|
// vegetationInstance.Rotation *= UnityEngine.Quaternion.AngleAxis(
|
|
// RandomRange(vegetationInstance.RandomNumberIndex, 0, 365f), new Vector3(0, 1, 0));
|
|
// vegetationInstance.RandomNumberIndex++;
|
|
// break;
|
|
// case VegetationRotationType.FollowTerrainScale:
|
|
// lookAt = math.cross(-vegetationInstance.TerrainNormal, new Vector3(1, 0, 0));
|
|
// if (lookAt.y < 0) lookAt = -lookAt;
|
|
// vegetationInstance.Rotation =
|
|
// UnityEngine.Quaternion.LookRotation(lookAt, vegetationInstance.TerrainNormal);
|
|
// vegetationInstance.Rotation *= UnityEngine.Quaternion.AngleAxis(
|
|
// RandomRange(vegetationInstance.RandomNumberIndex, 0, 365f), new Vector3(0, 1, 0));
|
|
// vegetationInstance.RandomNumberIndex++;
|
|
//
|
|
// var slopeCos = math.dot(vegetationInstance.TerrainNormal, new float3(0, 1, 0));
|
|
// float slopeAngle = math.acos(slopeCos) * Mathf.Rad2Deg;
|
|
//
|
|
// float newScale = Mathf.Clamp01(slopeAngle / 45f);
|
|
// float3 angleScale = new Vector3(newScale, 0, newScale);
|
|
// vegetationInstance.Scale += angleScale;
|
|
// break;
|
|
// }
|
|
//
|
|
// float randomScale = RandomRange(vegetationInstance.RandomNumberIndex, MinScale, MaxScale);
|
|
// vegetationInstance.RandomNumberIndex++;
|
|
// float3 randomVectorScale = new float3(randomScale, randomScale, randomScale);
|
|
// vegetationInstance.Scale *= randomVectorScale;
|
|
// vegetationInstance.Scale *= ScaleMultiplier;
|
|
//
|
|
// UnityEngine.Quaternion rotationOffset = UnityEngine.Quaternion.Euler(RotationOffset);
|
|
// vegetationInstance.Rotation *= rotationOffset;
|
|
//
|
|
// UnityEngine.Quaternion rotation = vegetationInstance.Rotation;
|
|
// // ReSharper disable once RedundantCast
|
|
// vegetationInstance.Position += (float3) (rotation * (Offset * vegetationInstance.Scale));
|
|
// VegetationInstanceList[index] = vegetationInstance;
|
|
// }
|
|
//
|
|
// public float RandomRange(int randomNumberIndex, float min, float max)
|
|
// {
|
|
// while (randomNumberIndex > 9999)
|
|
// randomNumberIndex = randomNumberIndex - 10000;
|
|
// return math.lerp(min, max, RandomNumbers[randomNumberIndex]);
|
|
// }
|
|
// }
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct OffsetAndRotateScaleVegetationInstanceMathJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> Position;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<int> RandomNumberIndex;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<quaternion> Rotation;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> Scale;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> TerrainNormal;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
|
|
[ReadOnly] public NativeArray<float> RandomNumbers;
|
|
public VegetationRotationType VegetationRotationType;
|
|
|
|
public float MinScale;
|
|
public float MaxScale;
|
|
|
|
public float3 Offset;
|
|
public float3 RotationOffset;
|
|
public float3 ScaleMultiplier;
|
|
|
|
public float MinUpOffset;
|
|
public float MaxUpOffset;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
Vector3 lookAt;
|
|
float3 up = new float3(0,1,0);
|
|
|
|
switch (VegetationRotationType)
|
|
{
|
|
case VegetationRotationType.RotateY:
|
|
Rotation[index] = quaternion.Euler(new float3(0,
|
|
RandomRange(RandomNumberIndex[index], 0, 6.28f), 0));
|
|
RandomNumberIndex[index]++;
|
|
break;
|
|
case VegetationRotationType.RotateXYZ:
|
|
Rotation[index] = quaternion.Euler(new float3(
|
|
RandomRange(RandomNumberIndex[index], 0, 6.28f),
|
|
RandomRange(RandomNumberIndex[index] + 1, 0, 6.28f),
|
|
RandomRange(RandomNumberIndex[index] + 2, 0, 6.28f)));
|
|
RandomNumberIndex[index] += 3;
|
|
up = TerrainNormal[index];
|
|
break;
|
|
case VegetationRotationType.FollowTerrain:
|
|
lookAt = math.cross(-TerrainNormal[index], new float3(1, 0, 0));
|
|
if (lookAt.y < 0) lookAt = -lookAt;
|
|
Rotation[index] = UnityEngine.Quaternion.LookRotation(lookAt, TerrainNormal[index]);
|
|
//vegetationInstance.Rotation = quaternion.LookRotation(lookAt, vegetationInstance.TerrainNormal);
|
|
Rotation[index] = math.mul(Rotation[index], quaternion.AxisAngle(new float3(0, 1, 0),
|
|
RandomRange(RandomNumberIndex[index], 0, 365f)));
|
|
RandomNumberIndex[index]++;
|
|
up = TerrainNormal[index];
|
|
break;
|
|
case VegetationRotationType.FollowTerrainScale:
|
|
lookAt = math.cross(-TerrainNormal[index], new float3(1, 0, 0));
|
|
if (lookAt.y < 0) lookAt = -lookAt;
|
|
Rotation[index] = UnityEngine.Quaternion.LookRotation(lookAt, TerrainNormal[index]);
|
|
//vegetationInstance.Rotation = quaternion.LookRotation(lookAt, vegetationInstance.TerrainNormal);
|
|
Rotation[index] = math.mul(Rotation[index] ,quaternion.AxisAngle(new float3(0, 1, 0),
|
|
RandomRange(RandomNumberIndex[index], 0, 365f)));
|
|
RandomNumberIndex[index]++;
|
|
|
|
var slopeCos = math.dot(TerrainNormal[index], new float3(0, 1, 0));
|
|
float slopeAngle = math.degrees(math.acos(slopeCos));
|
|
|
|
float newScale = math.clamp(slopeAngle / 45f, 0, 1);
|
|
float3 angleScale = new float3(newScale, 0, newScale);
|
|
Scale[index] += angleScale;
|
|
up = TerrainNormal[index];
|
|
break;
|
|
}
|
|
|
|
float randomScale = RandomRange(RandomNumberIndex[index], MinScale, MaxScale);
|
|
RandomNumberIndex[index]++;
|
|
float3 randomVectorScale = new float3(randomScale, randomScale, randomScale);
|
|
Scale[index] *= randomVectorScale;
|
|
Scale[index] *= ScaleMultiplier;
|
|
|
|
quaternion rotationOffset = quaternion.Euler(math.radians(RotationOffset));
|
|
Rotation[index] = math.mul(Rotation[index], rotationOffset);
|
|
|
|
quaternion rotation = Rotation[index];
|
|
|
|
float3 scaledOffset = Offset * Scale[index];
|
|
Position[index] += math.mul(rotation, scaledOffset);
|
|
|
|
float yScale = Scale[index].y;
|
|
float upOffset = RandomRange(RandomNumberIndex[index], MinUpOffset * yScale , MaxUpOffset * yScale);
|
|
RandomNumberIndex[index]++;
|
|
Position[index] += up * upOffset;
|
|
}
|
|
|
|
public float RandomRange(int randomNumberIndex, float min, float max)
|
|
{
|
|
while (randomNumberIndex > 9999)
|
|
randomNumberIndex = randomNumberIndex - 10000;
|
|
return math.lerp(min, max, RandomNumbers[randomNumberIndex]);
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct CreateInstanceMatrixJob : IJob
|
|
{
|
|
[ReadOnly] public NativeList<VegetationInstance> InstanceList;
|
|
public NativeList<MatrixInstance> VegetationInstanceMatrixList;
|
|
|
|
public void Execute()
|
|
{
|
|
for (int i = 0; i < InstanceList.Length; i++)
|
|
{
|
|
VegetationInstance vegetationInstance = InstanceList[i];
|
|
if (vegetationInstance.Excluded == 1) continue;
|
|
|
|
MatrixInstance matrixInstance = new MatrixInstance
|
|
{
|
|
Matrix = Matrix4x4.TRS(InstanceList[i].Position,
|
|
InstanceList[i].Rotation, InstanceList[i].Scale),
|
|
DistanceFalloff = InstanceList[i].DistanceFalloff
|
|
};
|
|
VegetationInstanceMatrixList.Add(matrixInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct NewCreateInstanceMatrixJob : IJob
|
|
{
|
|
[ReadOnly]public NativeList<byte> Excluded;
|
|
[ReadOnly]public NativeList<float3> Position;
|
|
[ReadOnly]public NativeList<quaternion> Rotation;
|
|
[ReadOnly]public NativeList<float3> Scale;
|
|
[ReadOnly]public NativeList<float> DistanceFalloff;
|
|
public NativeList<MatrixInstance> VegetationInstanceMatrixList;
|
|
|
|
public void Execute()
|
|
{
|
|
for (int i = 0; i < Excluded.Length; i++)
|
|
{
|
|
if (Excluded[i] == 1) continue;
|
|
|
|
MatrixInstance matrixInstance = new MatrixInstance
|
|
{
|
|
Matrix = Matrix4x4.TRS(Position[i],
|
|
Rotation[i], Scale[i]),
|
|
DistanceFalloff = DistanceFalloff[i]
|
|
};
|
|
VegetationInstanceMatrixList.Add(matrixInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct LoadPersistentStorageToMatrixJob : IJob
|
|
{
|
|
[ReadOnly] [DeallocateOnJobCompletion] public NativeArray<PersistentVegetationItem> InstanceList;
|
|
public NativeList<MatrixInstance> VegetationInstanceMatrixList;
|
|
public Vector3 VegetationSystemPosition;
|
|
|
|
public void Execute()
|
|
{
|
|
for (int i = 0; i < InstanceList.Length; i++)
|
|
{
|
|
MatrixInstance matrixInstance = new MatrixInstance
|
|
{
|
|
Matrix = Matrix4x4.TRS(InstanceList[i].Position + VegetationSystemPosition,
|
|
InstanceList[i].Rotation, InstanceList[i].Scale),
|
|
DistanceFalloff = InstanceList[i].DistanceFalloff
|
|
};
|
|
|
|
matrixInstance.DistanceFalloff = InstanceList[i].DistanceFalloff;
|
|
|
|
VegetationInstanceMatrixList.Add(matrixInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct LoadPersistentStorageToMatrixWideJob : IJobParallelForDefer
|
|
{
|
|
[ReadOnly] [DeallocateOnJobCompletion] public NativeArray<PersistentVegetationItem> InstanceList;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<MatrixInstance> VegetationInstanceMatrixList;
|
|
public Vector3 VegetationSystemPosition;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
MatrixInstance matrixInstance = new MatrixInstance
|
|
{
|
|
Matrix = Matrix4x4.TRS(InstanceList[index].Position + VegetationSystemPosition,
|
|
InstanceList[index].Rotation, InstanceList[index].Scale),
|
|
DistanceFalloff = InstanceList[index].DistanceFalloff
|
|
};
|
|
|
|
matrixInstance.DistanceFalloff = InstanceList[index].DistanceFalloff;
|
|
VegetationInstanceMatrixList[index] = matrixInstance;
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct DistanceFalloffJob : IJobParallelForDefer
|
|
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<int> RandomNumberIndex;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float> DistanceFalloff;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[ReadOnly] public NativeArray<float> RandomNumbers;
|
|
[ReadOnly] public float DistanceFalloffStartDistance;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
DistanceFalloff[index] =
|
|
math.clamp(
|
|
DistanceFalloffStartDistance + RandomRange(RandomNumberIndex[index], 0,
|
|
1 - DistanceFalloffStartDistance), 0, 1);
|
|
RandomNumberIndex[index]++;
|
|
}
|
|
|
|
public float RandomRange(int randomNumberIndex, float min, float max)
|
|
{
|
|
while (randomNumberIndex > 9999)
|
|
randomNumberIndex = randomNumberIndex - 10000;
|
|
|
|
return Mathf.Lerp(min, max, RandomNumbers[randomNumberIndex]);
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct PerlinNoiseCutoffJob : IJobParallelFor
|
|
{
|
|
public NativeArray<VegetationSpawnLocationInstance> SpawnLocationList;
|
|
public float PerlinCutoff;
|
|
public float PerlinScale;
|
|
public bool InversePerlinMask;
|
|
public float2 Offset;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
VegetationSpawnLocationInstance vegetationSpawnLocationInstance = SpawnLocationList[index];
|
|
if (vegetationSpawnLocationInstance.SpawnChance < float.Epsilon) return;
|
|
|
|
float perlin = noise.cnoise(new float2(
|
|
(vegetationSpawnLocationInstance.Position.x + Offset.x) / PerlinScale,
|
|
(vegetationSpawnLocationInstance.Position.z + Offset.y) / PerlinScale));
|
|
perlin += 1f;
|
|
perlin /= 2f;
|
|
perlin = math.clamp(perlin, 0, 1);
|
|
perlin = math.@select(perlin, 1 - perlin, InversePerlinMask);
|
|
if (perlin <= PerlinCutoff)
|
|
{
|
|
vegetationSpawnLocationInstance.SpawnChance = 0;
|
|
}
|
|
|
|
SpawnLocationList[index] = vegetationSpawnLocationInstance;
|
|
}
|
|
}
|
|
|
|
// [BurstCompile(CompileSynchronously = true)]
|
|
// public struct FillInstanceListJob : IJob
|
|
// {
|
|
// public NativeList<VegetationInstance> InstanceList;
|
|
// public int SampleCount;
|
|
//
|
|
// public void Execute()
|
|
// {
|
|
// VegetationInstance vegetationInstance = new VegetationInstance {Excluded = 1};
|
|
//
|
|
// for (int i = 0; i <= SampleCount - 1; i++)
|
|
// {
|
|
// InstanceList.Add(vegetationInstance);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct PerlinNoiseDensityJob : IJobParallelFor
|
|
{
|
|
public NativeArray<VegetationSpawnLocationInstance> SpawnLocationList;
|
|
public float PerlinScale;
|
|
public bool InversePerlinMask;
|
|
public float2 Offset;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
VegetationSpawnLocationInstance vegetationSpawnLocationInstance = SpawnLocationList[index];
|
|
if (vegetationSpawnLocationInstance.SpawnChance > float.Epsilon)
|
|
{
|
|
float perlin = noise.cnoise(new float2(
|
|
(vegetationSpawnLocationInstance.Position.x + Offset.x) / PerlinScale,
|
|
(vegetationSpawnLocationInstance.Position.z + Offset.y) / PerlinScale));
|
|
perlin += 1f;
|
|
perlin /= 2f;
|
|
perlin = math.clamp(perlin, 0, 1);
|
|
perlin = math.@select(perlin, 1 - perlin, InversePerlinMask);
|
|
vegetationSpawnLocationInstance.SpawnChance *= perlin;
|
|
SpawnLocationList[index] = vegetationSpawnLocationInstance;
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct PerlinNoiseScaleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> Position;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> Scale;
|
|
public float PerlinScale;
|
|
public bool InversePerlinMask;
|
|
public float2 Offset;
|
|
public float MinScale;
|
|
public float MaxScale;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
float perlin = noise.cnoise(new float2(
|
|
(Position[index].x + Offset.x) / PerlinScale,
|
|
(Position[index].z + Offset.y) / PerlinScale));
|
|
perlin += 1f;
|
|
perlin /= 2f;
|
|
perlin = math.clamp(perlin, 0, 1);
|
|
perlin = math.@select(perlin, 1 - perlin, InversePerlinMask);
|
|
|
|
float scaleMultiplier = math.lerp(MinScale, MaxScale, Mathf.Clamp(perlin, 0, 1));
|
|
float3 scaleVector = new float3(scaleMultiplier, scaleMultiplier, scaleMultiplier);
|
|
Scale[index] *= scaleVector;
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct TerrainSourceExcludeRuleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> TerrainSourceID;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
|
|
public TerrainSourceRule TerrainSourceRule;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
if (TerrainSourceRule[TerrainSourceID[index]])
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct TerrainSourceIncludeRuleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> TerrainSourceID;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
public TerrainSourceRule TerrainSourceRule;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
if (!TerrainSourceRule[TerrainSourceID[index]])
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct InstanceHeightRuleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> Position;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<int> RandomNumberIndex;
|
|
|
|
public float MinHeight;
|
|
public float MaxHeight;
|
|
[ReadOnly] public NativeArray<float> HeightRuleCurveArray;
|
|
[ReadOnly] public NativeArray<float> RandomNumbers;
|
|
public bool Advanced;
|
|
public float MaxCurveHeight;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
if (Advanced)
|
|
{
|
|
float relativeHeight = Position[index].y - MinHeight;
|
|
float heightNormalized = relativeHeight / MaxCurveHeight;
|
|
float spawnChance = SampleCurveArray(heightNormalized);
|
|
if (RandomCutoff(spawnChance, RandomNumberIndex[index]))
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
|
|
RandomNumberIndex[index]++;
|
|
}
|
|
else
|
|
{
|
|
if (Position[index].y < MinHeight || Position[index].y > MaxHeight)
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool RandomCutoff(float value, int randomNumberIndex)
|
|
{
|
|
float randomNumber = RandomRange(randomNumberIndex, 0, 1);
|
|
return !(value > randomNumber);
|
|
}
|
|
|
|
public float RandomRange(int randomNumberIndex, float min, float max)
|
|
{
|
|
while (randomNumberIndex > 9999)
|
|
randomNumberIndex = randomNumberIndex - 10000;
|
|
|
|
return Mathf.Lerp(min, max, RandomNumbers[randomNumberIndex]);
|
|
}
|
|
|
|
private float SampleCurveArray(float value)
|
|
{
|
|
if (HeightRuleCurveArray.Length == 0) return 0f;
|
|
int index = Mathf.RoundToInt((value) * HeightRuleCurveArray.Length);
|
|
index = Mathf.Clamp(index, 0, HeightRuleCurveArray.Length - 1);
|
|
return HeightRuleCurveArray[index];
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct InstanceSteepnessRuleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> TerrainNormal;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<int> RandomNumberIndex;
|
|
|
|
[ReadOnly] public NativeArray<float> SteepnessRuleCurveArray;
|
|
[ReadOnly] public NativeArray<float> RandomNumbers;
|
|
public bool Advanced;
|
|
|
|
public float MinSteepness;
|
|
public float MaxSteepness;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
var slopeCos = math.dot(TerrainNormal[index], new float3(0, 1, 0));
|
|
float slopeAngle = math.acos(slopeCos) * Mathf.Rad2Deg;
|
|
//TODO replace with math degrees
|
|
|
|
if (Advanced)
|
|
{
|
|
float slopeNormalized = slopeAngle / 90;
|
|
float spawnChance = SampleCurveArray(slopeNormalized);
|
|
if (RandomCutoff(spawnChance, RandomNumberIndex[index]))
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
|
|
RandomNumberIndex[index]++;
|
|
}
|
|
else
|
|
{
|
|
if (slopeAngle < MinSteepness || slopeAngle > MaxSteepness)
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool RandomCutoff(float value, int randomNumberIndex)
|
|
{
|
|
float randomNumber = RandomRange(randomNumberIndex, 0, 1);
|
|
return !(value > randomNumber);
|
|
}
|
|
|
|
public float RandomRange(int randomNumberIndex, float min, float max)
|
|
{
|
|
while (randomNumberIndex > 9999)
|
|
randomNumberIndex = randomNumberIndex - 10000;
|
|
|
|
return Mathf.Lerp(min, max, RandomNumbers[randomNumberIndex]);
|
|
}
|
|
|
|
private float SampleCurveArray(float value)
|
|
{
|
|
if (SteepnessRuleCurveArray.Length == 0) return 0f;
|
|
int index = Mathf.RoundToInt((value) * SteepnessRuleCurveArray.Length);
|
|
index = Mathf.Clamp(index, 0, SteepnessRuleCurveArray.Length - 1);
|
|
return SteepnessRuleCurveArray[index];
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct BiomeEdgeDistanceScaleRuleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float3> Scale;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float> BiomeDistance;
|
|
|
|
public float MaxDistance;
|
|
public float MinScale;
|
|
public float MaxScale;
|
|
public bool InverseScale;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
|
|
if (BiomeDistance[index] < MaxDistance)
|
|
{
|
|
float scaleMultiplier =
|
|
math.@select(math.lerp(MinScale, MaxScale, BiomeDistance[index] / MaxDistance),
|
|
math.lerp(MaxScale, MinScale, BiomeDistance[index] / MaxDistance),
|
|
InverseScale);
|
|
Scale[index] *= new float3(scaleMultiplier, scaleMultiplier, scaleMultiplier);
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct BiomeEdgeDistanceIncludeRuleJob : IJobParallelForDefer
|
|
{
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<byte> Excluded;
|
|
[NativeDisableParallelForRestriction]
|
|
public NativeList<float> BiomeDistance;
|
|
public float MaxDistance;
|
|
public bool Inverse;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
if (Excluded[index] == 1) return;
|
|
if (Inverse)
|
|
{
|
|
if (BiomeDistance[index] < MaxDistance)
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (BiomeDistance[index] > MaxDistance)
|
|
{
|
|
Excluded[index] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct AddSpawnLocations : IJob
|
|
{
|
|
[WriteOnly] public NativeList<VegetationSpawnLocationInstance> SpawnLocations;
|
|
public int SampleCount;
|
|
|
|
public void Execute()
|
|
{
|
|
for (int i = 0; i <= SampleCount - 1; i++)
|
|
{
|
|
SpawnLocations.Add(new VegetationSpawnLocationInstance());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
public struct CalculateCellSpawnLocationsWideJob : IJobParallelFor
|
|
{
|
|
[WriteOnly] public NativeArray<VegetationSpawnLocationInstance> SpawnLocations;
|
|
[ReadOnly] public NativeArray<float> RandomNumbers;
|
|
public Vector3 CellCorner;
|
|
public Vector3 CellSize;
|
|
public Rect CellRect;
|
|
public int CellIndex;
|
|
public float SampleDistance;
|
|
public float Density;
|
|
public float DefaultSpawnChance;
|
|
public int Seed;
|
|
public bool UseSamplePointOffset;
|
|
public float SamplePointMinOffset;
|
|
public float SamplePointMaxOffset;
|
|
public bool RandomizePosition;
|
|
public float CalculatedSampleDistance;
|
|
public int XSamples;
|
|
public int ZSamples;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
// ReSharper disable once RedundantCast
|
|
int z = Mathf.FloorToInt((float) index / XSamples);
|
|
int x = index - (z * XSamples);
|
|
|
|
Vector3 samplePosition = new Vector3(CellCorner.x + x * CalculatedSampleDistance, CellCorner.y + 10,
|
|
CellCorner.z + z * CalculatedSampleDistance);
|
|
VegetationSpawnLocationInstance vegetationSpawnLocationInstance =
|
|
new VegetationSpawnLocationInstance
|
|
{
|
|
Position = samplePosition,
|
|
SpawnChance = DefaultSpawnChance,
|
|
BiomeDistance = 1000000f
|
|
};
|
|
|
|
int randomNumberIndex = x + z * ZSamples + CellIndex + Seed;
|
|
while (randomNumberIndex > 9999)
|
|
randomNumberIndex = randomNumberIndex - 10000;
|
|
vegetationSpawnLocationInstance.RandomNumberIndex = randomNumberIndex;
|
|
|
|
if (RandomizePosition)
|
|
{
|
|
float3 offset = GetRandomOffset(CalculatedSampleDistance / 2f,
|
|
vegetationSpawnLocationInstance.RandomNumberIndex);
|
|
vegetationSpawnLocationInstance.RandomNumberIndex =
|
|
vegetationSpawnLocationInstance.RandomNumberIndex + 2;
|
|
vegetationSpawnLocationInstance.Position += offset;
|
|
}
|
|
|
|
if (UseSamplePointOffset)
|
|
{
|
|
float randomOffset = RandomRange(vegetationSpawnLocationInstance.RandomNumberIndex,
|
|
SamplePointMinOffset, SamplePointMaxOffset);
|
|
vegetationSpawnLocationInstance.RandomNumberIndex += 1;
|
|
float randomRotation =
|
|
math.frac(SamplePointMinOffset) *
|
|
365;
|
|
UnityEngine.Quaternion samplePointOffsetRotation = UnityEngine.Quaternion.Euler(0, randomRotation, 0);
|
|
float3 samplePointOffset = samplePointOffsetRotation * new Vector3(randomOffset, 0, 0);
|
|
vegetationSpawnLocationInstance.Position += samplePointOffset;
|
|
}
|
|
|
|
if (!CellRect.Contains(new Vector2(vegetationSpawnLocationInstance.Position.x,
|
|
vegetationSpawnLocationInstance.Position.z)))
|
|
{
|
|
vegetationSpawnLocationInstance.SpawnChance = 0;
|
|
}
|
|
|
|
SpawnLocations[index] = vegetationSpawnLocationInstance;
|
|
}
|
|
|
|
float3 GetRandomOffset(float distance, int randomNumberIndex)
|
|
{
|
|
return new float3(RandomRange(randomNumberIndex, -distance, distance), 0,
|
|
RandomRange(randomNumberIndex + 1, -distance, distance));
|
|
}
|
|
|
|
public float RandomRange(int randomNumberIndex, float min, float max)
|
|
{
|
|
while (randomNumberIndex > 9999)
|
|
randomNumberIndex = randomNumberIndex - 10000;
|
|
|
|
return Mathf.Lerp(min, max, RandomNumbers[randomNumberIndex]);
|
|
}
|
|
}
|
|
} |