Firstborn/Assets/AwesomeTechnologies/VegetationStudioPro/Runtime/VegetationSystemPro/Terrains/UnityTerrain.cs

854 lines
30 KiB
C#
Raw Permalink Normal View History

using System;
using System.Collections.Generic;
using AwesomeTechnologies.Utility;
using AwesomeTechnologies.Utility.Quadtree;
using AwesomeTechnologies.Vegetation;
using AwesomeTechnologies.VegetationStudio;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Serialization;
namespace AwesomeTechnologies.VegetationSystem
{
[BurstCompile(CompileSynchronously = true)]
public struct UnityTerrainSampleConcaveJob : IJobParallelForDefer
{
[NativeDisableParallelForRestriction]
public NativeList<byte> Excluded;
[NativeDisableParallelForRestriction]
public NativeList<float3> Position;
[ReadOnly] public NativeArray<float> InputHeights;
public float Distance;
public float MinHeightDifference;
public bool Inverse;
public bool Average;
public int HeightmapWidth;
public int HeightmapHeight;
public Vector3 HeightMapScale;
public Vector3 Size;
public Vector3 TerrainPosition;
public void Execute(int index)
{
if (Excluded[index] == 1) return;
Vector3 worldPosition = Position[index];
Vector3 terrainSpacePositon = worldPosition - TerrainPosition;
float2 heightmapPosition = new float2(terrainSpacePositon.x / HeightMapScale.x,
terrainSpacePositon.z / HeightMapScale.z);
int x = Mathf.RoundToInt(heightmapPosition.x);
int z = Mathf.RoundToInt(heightmapPosition.y);
int sampleDistance = Mathf.RoundToInt(Distance / HeightMapScale.x);
float centerHeight = GetHeight(x, z);
float height1 = GetHeight(x - sampleDistance, z - sampleDistance);
float height2 = GetHeight(x, z - sampleDistance);
float height3 = GetHeight(x + sampleDistance, z - sampleDistance);
float height4 = GetHeight(x - sampleDistance, z);
float height5 = GetHeight(x + sampleDistance, z);
float height6 = GetHeight(x - sampleDistance, z + sampleDistance);
float height7 = GetHeight(x, z + sampleDistance);
float height8 = GetHeight(x + sampleDistance, z + sampleDistance);
float edgeHeight;
if (Average)
{
edgeHeight = (height1 + height2 + height3 + height4 + height5 + height6 + height7 + height8) / 8f;
}
else
{
edgeHeight = GetMinimumHeight(height1, height2, height3, height4, height5, height6, height7, height8);
}
bool remove = edgeHeight < centerHeight + MinHeightDifference;
if (Inverse) remove = !remove;
if (!remove) return;
Excluded[index] = 1;
}
float GetMinimumHeight(float height1, float height2, float height3, float height4, float height5, float height6,
float height7, float height8)
{
float minHeight = math.min(height1, height2);
minHeight = math.min(minHeight, height3);
minHeight = math.min(minHeight, height4);
minHeight = math.min(minHeight, height5);
minHeight = math.min(minHeight, height6);
minHeight = math.min(minHeight, height7);
minHeight = math.min(minHeight, height8);
return minHeight;
}
float GetHeight(int x, int y)
{
x = math.clamp(x, 0, HeightmapWidth - 1);
y = math.clamp(y, 0, HeightmapHeight - 1);
return InputHeights[y * HeightmapWidth + x] * HeightMapScale.y;
}
}
[BurstCompile(CompileSynchronously = true)]
public struct UnityTerranCellSampleJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float> InputHeights;
public NativeArray<Bounds> VegetationCellBoundsList;
public int HeightmapWidth;
public int HeightmapHeight;
//public Vector3 Scale;
public Vector3 HeightMapScale;
public Rect TerrainRect;
public Vector3 TerrainPosition;
public float WorldspaceHeightCutoff;
public void Execute(int index)
{
Bounds vegetationCellBounds = VegetationCellBoundsList[index];
Rect cellRect = RectExtension.CreateRectFromBounds(vegetationCellBounds);
if (!TerrainRect.Overlaps(cellRect)) return;
float2 worldspaceCellCorner = new float2(vegetationCellBounds.center.x - vegetationCellBounds.extents.x,
vegetationCellBounds.center.z - vegetationCellBounds.extents.z);
float2 terrainspaceCellCorner = new float2(worldspaceCellCorner.x - TerrainPosition.x,
worldspaceCellCorner.y - TerrainPosition.z);
float2 heightmapPosition = new float2(terrainspaceCellCorner.x / HeightMapScale.x,
terrainspaceCellCorner.y / HeightMapScale.z);
int xCount = Mathf.CeilToInt(cellRect.width / HeightMapScale.x);
int zCount = Mathf.CeilToInt(cellRect.height / HeightMapScale.z);
int xStart = Mathf.FloorToInt(heightmapPosition.x);
int zStart = Mathf.FloorToInt(heightmapPosition.y);
float minHeight = float.MaxValue;
float maxHeight = float.MinValue;
for (int x = xStart; x <= xStart + xCount; x++)
{
for (int z = zStart; z <= zStart + zCount; z++)
{
float heightSample = GetHeight(x, z);
if (heightSample < minHeight) minHeight = heightSample;
if (heightSample > maxHeight) maxHeight = heightSample;
}
}
if (maxHeight + TerrainPosition.y < WorldspaceHeightCutoff) return;
float centerY = (maxHeight + minHeight) / 2f;
float height = maxHeight - minHeight;
vegetationCellBounds =
new Bounds(
new Vector3(vegetationCellBounds.center.x, centerY + TerrainPosition.y,
vegetationCellBounds.center.z),
new Vector3(vegetationCellBounds.size.x, height, vegetationCellBounds.size.z));
VegetationCellBoundsList[index] = vegetationCellBounds;
}
float GetHeight(int x, int y)
{
x = math.clamp(x, 0, HeightmapWidth - 1);
y = math.clamp(y, 0, HeightmapHeight - 1);
return InputHeights[y * HeightmapWidth + x] * HeightMapScale.y;
}
}
// [BurstCompile(CompileSynchronously = true)]
// public struct UnityTerrainSampleJob : IJobParallelFor
// {
// [ReadOnly] public NativeArray<float> InputHeights;
// [ReadOnly] public NativeArray<VegetationSpawnLocationInstance> SpawnLocationList;
// public NativeArray<VegetationInstance> InstanceList;
// public int HeightmapWidth;
// public int HeightmapHeight;
// public Vector3 Scale;
// public Vector3 Size;
// public Vector3 HeightMapScale;
// public Vector3 TerrainPosition;
// public byte TerrainSourceID;
//
// public void Execute(int index)
// {
// VegetationInstance vegetationInstance = InstanceList[index];
// if (vegetationInstance.HeightmapSampled == 1) return;
//
// VegetationSpawnLocationInstance spawnLocation = SpawnLocationList[index];
//
// if (spawnLocation.SpawnChance < 0)
// {
// vegetationInstance.Excluded = 1;
// vegetationInstance.HeightmapSampled = 1;
// InstanceList[index] = vegetationInstance;
// return;
// }
//
// Vector3 worldPosition = spawnLocation.Position;
// Vector3 terrainSpacePositon = worldPosition - TerrainPosition;
// float2 interpolatedPosition = new float2(terrainSpacePositon.x / Size.x, terrainSpacePositon.z / Size.z);
//
// if (interpolatedPosition.x < 0 || interpolatedPosition.x > 1 || interpolatedPosition.y < 0 ||
// interpolatedPosition.y > 1)
// {
// {
// vegetationInstance.Excluded = 1;
// InstanceList[index] = vegetationInstance;
// return;
// }
// }
//
// float height = GetTriangleInterpolatedHeight(interpolatedPosition.x, interpolatedPosition.y);
// vegetationInstance.Position = new float3(spawnLocation.Position.x, height + TerrainPosition.y,
// spawnLocation.Position.z);
// vegetationInstance.TerrainNormal =
// GetInterpolatedNormal(interpolatedPosition.x, interpolatedPosition.y);
// vegetationInstance.Scale = new float3(1, 1, 1);
// vegetationInstance.Rotation = Quaternion.Euler(0, 0, 0);
// vegetationInstance.RandomNumberIndex = spawnLocation.RandomNumberIndex;
// vegetationInstance.BiomeDistance = spawnLocation.BiomeDistance;
// vegetationInstance.DistanceFalloff = 1;
// vegetationInstance.TerrainSourceID = TerrainSourceID;
// vegetationInstance.Excluded = 0;
// vegetationInstance.HeightmapSampled = 1;
// InstanceList[index] = vegetationInstance;
// }
//
// float GetTriangleInterpolatedHeight(float x, float y)
// {
// float fx = x * (HeightmapWidth - 1);
// float fy = y * (HeightmapHeight - 1);
// int lx = (int) fx;
// int ly = (int) fy;
//
// float u = fx - lx;
// float v = fy - ly;
// if (u > v)
// {
// float z00 = GetHeight(lx + 0, ly + 0);
// float z01 = GetHeight(lx + 1, ly + 0);
// float z11 = GetHeight(lx + 1, ly + 1);
// return z00 + (z01 - z00) * u + (z11 - z01) * v;
// }
// else
// {
// float z00 = GetHeight(lx + 0, ly + 0);
// float z10 = GetHeight(lx + 0, ly + 1);
// float z11 = GetHeight(lx + 1, ly + 1);
// return z00 + (z11 - z10) * u + (z10 - z00) * v;
// }
// }
//
// float GetHeight(int x, int y)
// {
// x = math.clamp(x, 0, HeightmapWidth - 1);
// y = math.clamp(y, 0, HeightmapHeight - 1);
// return InputHeights[y * HeightmapWidth + x] * HeightMapScale.y;
// }
//
// public float3 GetInterpolatedNormal(float x, float y)
// {
// float fx = x * (HeightmapWidth - 1);
// float fy = y * (HeightmapHeight - 1);
// int lx = (int) fx;
// int ly = (int) fy;
//
// float3 n00 = CalculateNormalSobel(lx + 0, ly + 0);
// float3 n10 = CalculateNormalSobel(lx + 1, ly + 0);
// float3 n01 = CalculateNormalSobel(lx + 0, ly + 1);
// float3 n11 = CalculateNormalSobel(lx + 1, ly + 1);
//
// float u = fx - lx;
// float v = fy - ly;
//
// float3 s = math.lerp(n00, n10, u);
// float3 t = math.lerp(n01, n11, u);
// float3 value = math.lerp(s, t, v);
// return math.normalize(value);
// }
//
// float3 CalculateNormalSobel(int x, int y)
// {
// float3 normal;
// var dX = GetHeight(x - 1, y - 1) * -1.0F;
// dX += GetHeight(x - 1, y) * -2.0F;
// dX += GetHeight(x - 1, y + 1) * -1.0F;
// dX += GetHeight(x + 1, y - 1) * 1.0F;
// dX += GetHeight(x + 1, y) * 2.0F;
// dX += GetHeight(x + 1, y + 1) * 1.0F;
//
// dX /= Scale.x;
//
// var dY = GetHeight(x - 1, y - 1) * -1.0F;
// dY += GetHeight(x, y - 1) * -2.0F;
// dY += GetHeight(x + 1, y - 1) * -1.0F;
// dY += GetHeight(x - 1, y + 1) * 1.0F;
// dY += GetHeight(x, y + 1) * 2.0F;
// dY += GetHeight(x + 1, y + 1) * 1.0F;
// dY /= Scale.z;
//
// normal.x = -dX;
// normal.y = 8;
// normal.z = -dY;
// return math.normalize(normal);
// }
// }
[BurstCompile(CompileSynchronously = true)]
public struct UnityTerrainSampleJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float> InputHeights;
[ReadOnly] public NativeArray<VegetationSpawnLocationInstance> SpawnLocationList;
//public NativeArray<VegetationInstance> InstanceList;
public NativeArray<float3> Position;
public NativeArray<quaternion> Rotation;
public NativeArray<float3> Scales;
public NativeArray<float3> TerrainNormal;
public NativeArray<float> BiomeDistance;
public NativeArray<byte> TerrainTextureData;
public NativeArray<int> RandomNumberIndex;
public NativeArray<float> DistanceFalloff;
public NativeArray<float> VegetationMaskDensity;
public NativeArray<float> VegetationMaskScale;
public NativeArray<byte> TerrainSourceIDs;
public NativeArray<byte> TextureMaskData;
public NativeArray<byte> Excluded;
public NativeArray<byte> HeightmapSampled;
public int HeightmapWidth;
public int HeightmapHeight;
public Vector3 Scale;
public Vector3 Size;
public Vector3 HeightMapScale;
public Vector3 TerrainPosition;
public byte TerrainSourceID;
public void Execute(int index)
{
if (HeightmapSampled[index] == 1) return;
VegetationSpawnLocationInstance spawnLocation = SpawnLocationList[index];
if (spawnLocation.SpawnChance < 0)
{
Excluded[index] = 1;
HeightmapSampled[index] = 1;
return;
}
Vector3 worldPosition = spawnLocation.Position;
Vector3 terrainSpacePositon = worldPosition - TerrainPosition;
float2 interpolatedPosition = new float2(terrainSpacePositon.x / Size.x, terrainSpacePositon.z / Size.z);
if (interpolatedPosition.x < 0 || interpolatedPosition.x > 1 || interpolatedPosition.y < 0 ||
interpolatedPosition.y > 1)
{
{
Excluded[index] = 1;
return;
}
}
float height = GetTriangleInterpolatedHeight(interpolatedPosition.x, interpolatedPosition.y);
Position[index] = new float3(spawnLocation.Position.x, height + TerrainPosition.y,spawnLocation.Position.z);
TerrainNormal[index] = GetInterpolatedNormal(interpolatedPosition.x, interpolatedPosition.y);
Scales[index] = new float3(1, 1, 1);
Rotation[index] = Quaternion.Euler(0, 0, 0);
RandomNumberIndex[index] = spawnLocation.RandomNumberIndex;
BiomeDistance[index] = spawnLocation.BiomeDistance;
DistanceFalloff[index] = 1;
TerrainSourceIDs[index] = TerrainSourceID;
Excluded[index] = 0;
HeightmapSampled[index] = 1;
TerrainTextureData[index] = 0;
//TODO make sure we do not need to init these 2 here
VegetationMaskDensity[index] = 0;
VegetationMaskScale[index] = 0;
TextureMaskData[index] = 0;
}
float GetTriangleInterpolatedHeight(float x, float y)
{
float fx = x * (HeightmapWidth - 1);
float fy = y * (HeightmapHeight - 1);
int lx = (int) fx;
int ly = (int) fy;
float u = fx - lx;
float v = fy - ly;
if (u > v)
{
float z00 = GetHeight(lx + 0, ly + 0);
float z01 = GetHeight(lx + 1, ly + 0);
float z11 = GetHeight(lx + 1, ly + 1);
return z00 + (z01 - z00) * u + (z11 - z01) * v;
}
else
{
float z00 = GetHeight(lx + 0, ly + 0);
float z10 = GetHeight(lx + 0, ly + 1);
float z11 = GetHeight(lx + 1, ly + 1);
return z00 + (z11 - z10) * u + (z10 - z00) * v;
}
}
float GetHeight(int x, int y)
{
x = math.clamp(x, 0, HeightmapWidth - 1);
y = math.clamp(y, 0, HeightmapHeight - 1);
return InputHeights[y * HeightmapWidth + x] * HeightMapScale.y;
}
public float3 GetInterpolatedNormal(float x, float y)
{
float fx = x * (HeightmapWidth - 1);
float fy = y * (HeightmapHeight - 1);
int lx = (int) fx;
int ly = (int) fy;
float3 n00 = CalculateNormalSobel(lx + 0, ly + 0);
float3 n10 = CalculateNormalSobel(lx + 1, ly + 0);
float3 n01 = CalculateNormalSobel(lx + 0, ly + 1);
float3 n11 = CalculateNormalSobel(lx + 1, ly + 1);
float u = fx - lx;
float v = fy - ly;
float3 s = math.lerp(n00, n10, u);
float3 t = math.lerp(n01, n11, u);
float3 value = math.lerp(s, t, v);
return math.normalize(value);
}
float3 CalculateNormalSobel(int x, int y)
{
float3 normal;
var dX = GetHeight(x - 1, y - 1) * -1.0F;
dX += GetHeight(x - 1, y) * -2.0F;
dX += GetHeight(x - 1, y + 1) * -1.0F;
dX += GetHeight(x + 1, y - 1) * 1.0F;
dX += GetHeight(x + 1, y) * 2.0F;
dX += GetHeight(x + 1, y + 1) * 1.0F;
dX /= Scale.x;
var dY = GetHeight(x - 1, y - 1) * -1.0F;
dY += GetHeight(x, y - 1) * -2.0F;
dY += GetHeight(x + 1, y - 1) * -1.0F;
dY += GetHeight(x - 1, y + 1) * 1.0F;
dY += GetHeight(x, y + 1) * 2.0F;
dY += GetHeight(x + 1, y + 1) * 1.0F;
dY /= Scale.z;
normal.x = -dX;
normal.y = 8;
normal.z = -dY;
return math.normalize(normal);
}
}
[BurstCompile]
public struct AddToInstanceListJob : IJob
{
[DeallocateOnJobCompletion] public NativeArray<VegetationInstance> SourceInstanceArray;
public NativeList<VegetationInstance> TargetInstanceList;
public void Execute()
{
for (int i = 0; i <= SourceInstanceArray.Length - 1; i++)
{
VegetationInstance vegetationInstance = SourceInstanceArray[i];
if (vegetationInstance.Excluded == 1) continue;
TargetInstanceList.Add(vegetationInstance);
}
}
}
[AwesomeTechnologiesScriptOrder(-100)]
[ExecuteInEditMode]
public partial class UnityTerrain : MonoBehaviour, IVegetationStudioTerrain
{
public NativeArray<float> Heights;
[FormerlySerializedAs("_terrain")] public Terrain Terrain;
private int _heightmapHeight;
private int _heightmapWidth;
private Vector3 _size;
private Vector3 _scale;
private Vector3 _heightmapScale;
private Rect _terrainRect;
private readonly List<NativeArray<ARGBBytes>> _splatMapArrayList = new List<NativeArray<ARGBBytes>>();
private readonly List<int> _splatMapFormatList = new List<int>();
#if !UNITY_2019_2_OR_NEWER
private Terrain.MaterialType _originalTerrainMaterialType = Terrain.MaterialType.BuiltInStandard;
#endif
private Material _originalTerrainMaterial;
private float _originalTerrainheightmapPixelError;
public bool TerrainMaterialOverridden;
private bool _originalTerrainInstanced;
private float _originalBasemapDistance;
[NonSerialized] public Material TerrainHeatmapMaterial;
public bool DisableTerrainTreesAndDetails = true;
public bool AutoAddToVegegetationSystem;
private bool _initDone;
public TerrainSourceID TerrainSourceID;
public Vector3 TerrainPosition = Vector3.zero;
// ReSharper disable once UnusedMember.Local
void Reset()
{
FindTerrain();
TerrainPosition = transform.position;
}
void FindTerrain()
{
if (!Terrain)
{
Terrain = gameObject.GetComponent<Terrain>();
}
}
// ReSharper disable once UnusedMember.Local
void Awake()
{
FindTerrain();
}
void Start()
{
if (Terrain && DisableTerrainTreesAndDetails)
{
Terrain.drawTreesAndFoliage = false;
}
}
//private ManagedNativeFloatArray _nativeManagedFloatArray;
void LoadHeightData()
{
var terrainData = Terrain.terrainData;
_heightmapScale = terrainData.heightmapScale;
_heightmapHeight = terrainData.heightmapResolution;
_heightmapWidth = terrainData.heightmapResolution;
_size = terrainData.size;
_scale.x = _size.x / (_heightmapWidth - 1);
_scale.y = _size.y;
_scale.z = _size.z / (_heightmapHeight - 1);
Vector2 terrainCenter = new Vector2(TerrainPosition.x, TerrainPosition.z);
Vector2 terrainSize = new Vector2(_size.x, _size.z);
_terrainRect = new Rect(terrainCenter, terrainSize);
float[,] hs = Terrain.terrainData.GetHeights(0, 0, _heightmapWidth, _heightmapHeight);
if (Heights.IsCreated) Heights.Dispose();
Heights = new NativeArray<float>(_heightmapWidth * _heightmapHeight, Allocator.Persistent);
Heights.CopyFromFast(hs);
}
public JobHandle SampleTerrain(NativeList<VegetationSpawnLocationInstance> spawnLocationList,
VegetationInstanceData instanceData, int sampleCount, Rect spawnRect, JobHandle dependsOn)
{
if (!_initDone) return dependsOn;
if (spawnRect.Overlaps(_terrainRect))
{
UnityTerrainSampleJob unityTerrainSampleJob = new UnityTerrainSampleJob
{
InputHeights = Heights,
SpawnLocationList = spawnLocationList,
Position = instanceData.Position,
Rotation = instanceData.Rotation,
Scales = instanceData.Scale,
TerrainNormal= instanceData.TerrainNormal,
BiomeDistance= instanceData.BiomeDistance,
TerrainTextureData= instanceData.TerrainTextureData,
RandomNumberIndex= instanceData.RandomNumberIndex,
DistanceFalloff= instanceData.DistanceFalloff,
VegetationMaskDensity= instanceData.VegetationMaskDensity,
VegetationMaskScale= instanceData.VegetationMaskScale,
TerrainSourceIDs= instanceData.TerrainSourceID,
TextureMaskData= instanceData.TextureMaskData,
Excluded= instanceData.Excluded,
HeightmapSampled= instanceData.HeightmapSampled,
HeightMapScale = _heightmapScale,
HeightmapHeight = _heightmapHeight,
HeightmapWidth = _heightmapWidth,
TerrainPosition = TerrainPosition,
Scale = _scale,
Size = _size,
TerrainSourceID = (byte) TerrainSourceID
};
JobHandle handle = unityTerrainSampleJob.Schedule(sampleCount,64,dependsOn);
return handle;
}
return dependsOn;
}
public void RefreshTerrainData()
{
LoadHeightData();
}
public void RefreshTerrainData(Bounds bounds)
{
Rect terrainRect = RectExtension.CreateRectFromBounds(TerrainBounds);
Rect updateRect = RectExtension.CreateRectFromBounds(bounds);
if (!updateRect.Overlaps(terrainRect))
{
LoadHeightData();
}
}
public JobHandle SampleCellHeight(NativeArray<Bounds> vegetationCellBoundsList, float worldspaceHeightCutoff,
Rect cellBoundsRect, JobHandle dependsOn = default(JobHandle))
{
if (!_initDone) return dependsOn;
if (!Heights.IsCreated) LoadHeightData();
if (cellBoundsRect.Overlaps(_terrainRect))
{
UnityTerranCellSampleJob unityTerranCellSampleJob = new UnityTerranCellSampleJob
{
InputHeights = Heights,
VegetationCellBoundsList = vegetationCellBoundsList,
HeightMapScale = _heightmapScale,
HeightmapHeight = _heightmapHeight,
HeightmapWidth = _heightmapWidth,
TerrainPosition = TerrainPosition,
WorldspaceHeightCutoff = worldspaceHeightCutoff,
TerrainRect = RectExtension.CreateRectFromBounds(TerrainBounds)
};
JobHandle handle = unityTerranCellSampleJob.Schedule(vegetationCellBoundsList.Length, 32, dependsOn);
return handle;
}
return dependsOn;
}
public JobHandle SampleConcaveLocation(VegetationInstanceData instanceData, float minHeightDifference,
float distance, bool inverse, bool average, Rect spawnRect,
JobHandle dependsOn)
{
if (!_initDone) return dependsOn;
if (spawnRect.Overlaps(_terrainRect))
{
UnityTerrainSampleConcaveJob unityTerrainSampleConcaveLocationJob = new UnityTerrainSampleConcaveJob
{
InputHeights = Heights,
Excluded = instanceData.Excluded,
Position = instanceData.Position,
HeightMapScale = _heightmapScale,
HeightmapHeight = _heightmapHeight,
HeightmapWidth = _heightmapWidth,
TerrainPosition = TerrainPosition,
Size = _size,
Distance = distance,
MinHeightDifference = minHeightDifference,
Inverse = inverse,
Average = average
};
JobHandle handle = unityTerrainSampleConcaveLocationJob.Schedule(instanceData.Excluded, 64, dependsOn);
return handle;
}
return dependsOn;
}
public void Init()
{
if (!Heights.IsCreated) LoadHeightData();
//if (_nativeManagedFloatArray == null) LoadHeightData();
}
public void DisposeTemporaryMemory()
{
}
public bool HasTerrainTextures()
{
return true;
}
public Texture2D GetTerrainTexture(int index)
{
if (!Terrain) return null;
if (!Terrain.terrainData) return null;
#if UNITY_2018_3_OR_NEWER
if (Terrain.terrainData.terrainLayers.Length > index)
{
if (Terrain.terrainData.terrainLayers[index])
{
return Terrain.terrainData.terrainLayers[index].diffuseTexture;
}
else
{
return null;
}
}
#else
if (Terrain.terrainData.splatPrototypes.Length > index)
{
return Terrain.terrainData.splatPrototypes[index].texture;
}
#endif
return null;
}
#if UNITY_2018_3_OR_NEWER
public TerrainLayer[] GetTerrainLayers()
{
if (!Terrain) return new TerrainLayer[0];
return Terrain.terrainData.terrainLayers;
}
public void SetTerrainLayers(TerrainLayer[] terrainLayers)
{
if (Terrain)
{
Terrain.terrainData.terrainLayers = terrainLayers;
}
}
#else
public SplatPrototype[] GetSplatPrototypes()
{
if (!Terrain) return new SplatPrototype[0];
return Terrain.terrainData.splatPrototypes;
}
public void SetSplatPrototypes(SplatPrototype[] splatPrototypes)
{
if (Terrain)
{
Terrain.terrainData.splatPrototypes = splatPrototypes;
}
}
#endif
//// ReSharper disable once UnusedMember.Local
void OnEnable()
{
RefreshSplatMaps();
_initDone = true;
if (AutoAddToVegegetationSystem && Application.isPlaying)
{
AddTerrainToVegetationSystem();
}
else
{
VegetationStudioManager.RefreshTerrainArea(TerrainBounds);
}
}
public void AddTerrainToVegetationSystem()
{
VegetationStudioManager.AddTerrain(gameObject, false);
}
// ReSharper disable once UnusedMember.Local
void OnDisable()
{
_initDone = false;
if (AutoAddToVegegetationSystem && Application.isPlaying)
{
VegetationStudioManager.RemoveTerrain(gameObject);
}
else
{
VegetationStudioManager.RefreshTerrainArea(TerrainBounds);
}
Dispose();
}
public void RefreshTerrainArea()
{
VegetationStudioManager.RefreshTerrainArea(TerrainBounds);
}
public void Dispose()
{
//_nativeManagedFloatArray?.Dispose();
if (Heights.IsCreated)
{
Heights.Dispose();
}
}
public string TerrainType => "Unity terrain";
public Bounds TerrainBounds
{
get
{
if (Terrain)
{
var terrainData = Terrain.terrainData;
return new Bounds(terrainData.bounds.center + TerrainPosition,
terrainData.bounds.size);
}
return new Bounds(Vector3.zero, Vector3.zero);
}
}
// ReSharper disable once UnusedMember.Local
void OnDrawGizmosSelected()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(TerrainBounds.center, TerrainBounds.size);
}
void Update()
{
if (!Application.isPlaying)
{
TerrainPosition = transform.position;
}
}
}
}