331 lines
10 KiB
C#
331 lines
10 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.Burst.CompilerServices;
|
|
using Unity.Collections;
|
|
using Unity.VisualScripting;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
public class GrasFeld : MonoBehaviour
|
|
{
|
|
float grasDichteProMeter = 1.6f;
|
|
static float basisChunkGroese = 5f;
|
|
|
|
public GameObject GrasChunkPrefab;
|
|
|
|
public Dictionary<Vector2Int, ChunkData> allChunckData;
|
|
List<Vector2Int> ChunksModifyed = new List<Vector2Int>();
|
|
List<Vector2Int> ActiveChunks;
|
|
|
|
public List<ChunkGameObject> AlleChunkObjekte = new List<ChunkGameObject>();
|
|
|
|
#if UNITY_EDITOR
|
|
void OnEnable()
|
|
{
|
|
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
|
|
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
|
|
AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
|
|
}
|
|
|
|
|
|
public void OnBeforeAssemblyReload()
|
|
{
|
|
ClearAllChunkData();
|
|
}
|
|
|
|
public void ClearAllChunkData()
|
|
{
|
|
if (allChunckData == null) return;
|
|
|
|
var enumerator = allChunckData.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
var pair = enumerator.Current;
|
|
pair.Value.Data.Dispose();
|
|
}
|
|
|
|
allChunckData.Clear();
|
|
}
|
|
#endif
|
|
|
|
public void OnAfterAssemblyReload()
|
|
{
|
|
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
Vector3 cameraPosition = Camera.main.transform.position;
|
|
foreach (var chunk in AlleChunkObjekte)
|
|
{
|
|
chunk.SetLOD(cameraPosition);
|
|
}
|
|
}
|
|
|
|
|
|
public void GrasZeichnen(GrassortenScriptableObject sorte, Vector2 pos, float pinselRadius, bool zeichenInverter)
|
|
{
|
|
if (allChunckData == null) allChunckData = new Dictionary<Vector2Int, ChunkData>();
|
|
List<ChunkData> zuÄnderndeChunks = new List<ChunkData>();
|
|
|
|
|
|
|
|
//Alle chuncks finden in die Gezeichnet werden könnte
|
|
ActiveChunks = GetActiveChunckPositions(pinselRadius, pos);
|
|
foreach (var potential in ActiveChunks)
|
|
{
|
|
if(allChunckData.TryGetValue(potential, out var chunckData))
|
|
{
|
|
zuÄnderndeChunks.Add(chunckData);
|
|
}
|
|
else
|
|
{
|
|
allChunckData.Add(potential, new ChunkData(potential));
|
|
if (allChunckData.TryGetValue(potential, out chunckData))
|
|
zuÄnderndeChunks.Add(chunckData);
|
|
else
|
|
Debug.LogError("Konnte Chunk nich adden " + potential);
|
|
}
|
|
}
|
|
|
|
ChunksModifyed.Clear();
|
|
foreach(ChunkData chunk in zuÄnderndeChunks)
|
|
{
|
|
allChunckData[chunk.pos] = ZeichneInChunk(chunk, sorte, pos, pinselRadius, zeichenInverter);
|
|
ChunksModifyed.Add(chunk.pos);
|
|
|
|
if (allChunckData[chunk.pos].isEmpty == true)
|
|
{
|
|
allChunckData[chunk.pos].Data.Dispose();
|
|
allChunckData.Remove(chunk.pos);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
MakeChunks();
|
|
}
|
|
|
|
public void MakeChunks()
|
|
{
|
|
|
|
List<ChunkGameObject> ChunkObjectsToRecalculate = new List<ChunkGameObject>();
|
|
foreach(Vector2Int chunkPos in ChunksModifyed)
|
|
{
|
|
ChunkGameObject chunkObject;
|
|
foreach (ChunkGameObject currenChunckObject in AlleChunkObjekte)
|
|
{
|
|
if (currenChunckObject.ContainsChunk(chunkPos))
|
|
{
|
|
chunkObject = currenChunckObject;
|
|
goto ChunkExistiert;
|
|
}
|
|
}
|
|
|
|
//Kein Chunk gefunden
|
|
GameObject tempGO = Instantiate(GrasChunkPrefab);
|
|
tempGO.transform.position = new Vector3(chunkPos.x, 0, chunkPos.y);
|
|
tempGO.transform.parent = transform;
|
|
tempGO.name = "Chunk LOD3";
|
|
chunkObject = tempGO.GetComponent<ChunkGameObject>();
|
|
chunkObject.SetPosToGrid();
|
|
chunkObject.grasFeld = this;
|
|
AlleChunkObjekte.Add(chunkObject);
|
|
|
|
ChunkExistiert:
|
|
chunkObject.SetUpdate(chunkPos);
|
|
if(ChunkObjectsToRecalculate.Contains(chunkObject) == false)
|
|
ChunkObjectsToRecalculate.Add(chunkObject);
|
|
}
|
|
|
|
foreach(var chunkObj in ChunkObjectsToRecalculate)
|
|
{
|
|
chunkObj.DoMeshUpdate();
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
int count = 0;
|
|
while (transform.childCount > count)
|
|
{
|
|
var checkPos = transform.GetChild(count).gameObject.GetComponent<ChunkGameObject>().pos;
|
|
if (ChunksModifyed.Contains(checkPos))
|
|
{
|
|
DestroyImmediate(transform.GetChild(count).gameObject);
|
|
}
|
|
else
|
|
count++;
|
|
}
|
|
|
|
foreach (var chunk in allChunckData)
|
|
{
|
|
if (ChunksModifyed.Contains(chunk.Key) == false)
|
|
continue;
|
|
|
|
Texture2D tempTextur = new Texture2D(16,16);
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
for (int j = 0; j < 16; j++)
|
|
{
|
|
tempTextur.SetPixel(i, j, new Color(chunk.Value.Data[i * 16 + j][0] * 256, 0, 0));
|
|
}
|
|
|
|
tempTextur.Apply();
|
|
|
|
|
|
GameObject chunkObject = Instantiate(GrasChunkPrefab);
|
|
chunkObject.transform.position = new Vector3(chunk.Key.x, 0, chunk.Key.y);
|
|
chunkObject.transform.parent = transform;
|
|
chunkObject.GetComponent<ChunkGameObject>().DebugGrasTextur = tempTextur;
|
|
chunkObject.GetComponent<ChunkGameObject>().pos = chunk.Key;
|
|
chunkObject.GetComponent<ChunkGameObject>().hasChanged = ChunksModifyed.Contains(chunk.Key);
|
|
chunkObject.name = "" + chunk.Key;
|
|
}
|
|
|
|
ChunksModifyed.Clear();
|
|
}
|
|
|
|
|
|
static ChunkData ZeichneInChunk(ChunkData chunk, GrassortenScriptableObject grassorte, Vector2 pos, float brushSize, bool shift)
|
|
{
|
|
int grassortenIndex = -1;
|
|
|
|
if (chunk.VerwendeteGrassorten.Contains(grassorte.grassorteId) == false)
|
|
chunk.VerwendeteGrassorten.Add(grassorte.grassorteId);
|
|
|
|
grassortenIndex = chunk.VerwendeteGrassorten.IndexOf(grassorte.grassorteId);
|
|
|
|
int emptyCount = 0;
|
|
for(int i = 0; i < 16; i++)
|
|
for(int j = 0; j < 16; j++)
|
|
{
|
|
var currentValue = chunk.Data[i * 16 + j];
|
|
|
|
byte val = currentValue[grassortenIndex];
|
|
Vector2 inchunckPos = new Vector2(chunk.pos.x + i * (basisChunkGroese / 16f), chunk.pos.y + j * (basisChunkGroese / 16f));
|
|
|
|
if (Vector2.Distance(inchunckPos, pos) < brushSize)
|
|
val = shift ? (byte)0 : (byte)255;
|
|
|
|
currentValue[grassortenIndex] = val;
|
|
if (currentValue[grassortenIndex] == 0)
|
|
emptyCount++;
|
|
|
|
|
|
chunk.Data[i * 16 + j] = currentValue;
|
|
}
|
|
|
|
if (emptyCount == (16 * 16))
|
|
chunk.isEmpty = true;
|
|
|
|
return chunk;
|
|
}
|
|
|
|
|
|
List<Vector2Int> GetActiveChunckPositions(float brushRadius, Vector2 brushPos)
|
|
{
|
|
List<Vector2Int> activeChuncksTemp = new List<Vector2Int>();
|
|
|
|
int minPosX = Mathf.FloorToInt((brushPos.x - brushRadius) / basisChunkGroese) * (int)basisChunkGroese;
|
|
int minPosY = Mathf.FloorToInt((brushPos.y - brushRadius) / basisChunkGroese) * (int)basisChunkGroese;
|
|
int maxPosX = Mathf.CeilToInt((brushPos.x + brushRadius) / basisChunkGroese) * (int)basisChunkGroese;
|
|
int maxPosY = Mathf.CeilToInt((brushPos.y + brushRadius) / basisChunkGroese) * (int)basisChunkGroese;
|
|
|
|
for (int x = minPosX; x < maxPosX; x += (int)basisChunkGroese)
|
|
{
|
|
for (int y = minPosY; y < maxPosY; y += (int)basisChunkGroese)
|
|
{
|
|
Vector2Int chunckPos = new Vector2Int(x, y);
|
|
if (chunckPos.x < 0 || chunckPos.x > 1000 || chunckPos.y < 0 || chunckPos.y > 1000)
|
|
continue;
|
|
|
|
if (IsChunckInRadius(brushPos, brushRadius, chunckPos, basisChunkGroese))
|
|
activeChuncksTemp.Add(chunckPos);
|
|
}
|
|
}
|
|
|
|
return activeChuncksTemp;
|
|
}
|
|
|
|
static bool IsChunckInRadius(Vector2 pos, float radius, Vector2Int chunckPos, float chunckSize)
|
|
{
|
|
// Find the closest point to the circle within the rectangle
|
|
Vector2 closestPosInChunck = new Vector2(Mathf.Clamp(pos.x, chunckPos.x, chunckPos.x + chunckSize), Mathf.Clamp(pos.y, chunckPos.y, chunckPos.y + chunckSize));
|
|
|
|
// Calculate the distance between the circle's center and this closest point
|
|
Vector2 brushToPoint = pos - closestPosInChunck;
|
|
|
|
return brushToPoint.sqrMagnitude < radius * radius;
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnDrawGizmos()
|
|
{
|
|
if (Application.isEditor)
|
|
{
|
|
Vector3 cameraPosition = SceneView.lastActiveSceneView.camera.transform.position;
|
|
foreach (var chunk in AlleChunkObjekte)
|
|
{
|
|
chunk.SetLOD(cameraPosition);
|
|
}
|
|
}
|
|
|
|
if(false)
|
|
if (ActiveChunks != null)
|
|
foreach(var p in ActiveChunks)
|
|
{
|
|
Gizmos.DrawWireCube(new Vector3(p.x + basisChunkGroese / 2f, 0, p.y + basisChunkGroese / 2f), Vector3.one * basisChunkGroese);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public struct ChunkData
|
|
{
|
|
public bool isEmpty;
|
|
public Vector2Int pos;
|
|
public NativeArray<ChunkPixel> Data;
|
|
public FixedList32Bytes<int> VerwendeteGrassorten;
|
|
|
|
public ChunkData(Vector2Int pos)
|
|
{
|
|
isEmpty = false;
|
|
this.pos = pos;
|
|
Data = new NativeArray<ChunkPixel>(256, Allocator.Persistent);
|
|
VerwendeteGrassorten = new FixedList32Bytes<int>();
|
|
}
|
|
}
|
|
|
|
public struct ChunkPixel
|
|
{
|
|
byte p1, p2, p3, p4;
|
|
|
|
public byte this[int index]
|
|
{
|
|
get => index switch
|
|
{
|
|
0 => p1,
|
|
1 => p2,
|
|
2 => p3,
|
|
3 => p4,
|
|
_ => throw new IndexOutOfRangeException()
|
|
};
|
|
set => _ = index switch
|
|
{
|
|
0 => (p1 = value),
|
|
1 => (p2 = value),
|
|
2 => (p3 = value),
|
|
3 => (p4 = value),
|
|
_ => throw new IndexOutOfRangeException()
|
|
};
|
|
}
|
|
}
|