grass-rendering-prototype/Assets/TerrainTool/ChunkGameObject.cs
2025-09-25 15:22:35 +02:00

394 lines
14 KiB
C#

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class ChunkGameObject : MonoBehaviour
{
static float basisChunkGroese = 5f;
Renderer meshRenderer;
MeshFilter meshFilter;
List<Vector3> verts = new List<Vector3>();
List<int> indes = new List<int>();
List<Vector4> tipOffsets_missingInNextLOD = new List<Vector4>();
GameObject GrasChunkPrefab;
public GrasFeld grasFeld;
public Vector2Int pos;
public Texture2D DebugGrasTextur;
public bool hasChanged = false;
public int myLOD = 0;
public ChunkGameObject[] childChunks = new ChunkGameObject[4];
[SerializeField] public List<Material> materials = new List<Material>();
public Mesh LOD_a;
public Mesh LOD_b;
public bool doUpdate = false;
private void OnEnable()
{
meshRenderer = GetComponent<Renderer>();
meshFilter = GetComponent<MeshFilter>();
return;
if (myLOD != 0 && transform.childCount == 4)
{
childChunks[0] = transform.GetChild(0).GetComponent<ChunkGameObject>();
childChunks[1] = transform.GetChild(1).GetComponent<ChunkGameObject>();
childChunks[2] = transform.GetChild(2).GetComponent<ChunkGameObject>();
childChunks[3] = transform.GetChild(3).GetComponent<ChunkGameObject>();
}
else
childChunks = null;
}
public void SetUpdate(Vector2Int chunkPos)
{
doUpdate = false;
if (ContainsChunk(chunkPos) == false)
return;
doUpdate = true;
if(myLOD > 0)
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
{
int childLodSize = (int)basisChunkGroese * (int)Mathf.Pow(2, myLOD - 1);
Vector2Int currentChildPos = new Vector2Int((int)transform.position.x + i * childLodSize,
(int)transform.position.z + j * childLodSize);
if (ContainsChunk(chunkPos, currentChildPos, myLOD - 1) == false)
continue;
var currenChild = childChunks[i * 2 + j];
if (currenChild == null)
{
if (GrasChunkPrefab == null)
GrasChunkPrefab = Resources.Load("GrasChunk") as GameObject;
GameObject tempGO = Instantiate(GrasChunkPrefab);
tempGO.transform.position = new Vector3(currentChildPos.x, 0, currentChildPos.y);
tempGO.transform.parent = transform;
tempGO.name = "Chunk LOD" + (myLOD - 1);
var chunkObject = tempGO.GetComponent<ChunkGameObject>();
chunkObject.myLOD = myLOD - 1;
chunkObject.grasFeld = this.grasFeld;
//chunkObject.SetPosToGrid();
childChunks[i * 2 + j] = chunkObject;
currenChild = chunkObject;
}
currenChild.SetUpdate(chunkPos);
}
}
public void SetLOD(Vector3 cameraPosition)
{
if (meshRenderer == null) meshRenderer = GetComponent<MeshRenderer>();
float LODSize = basisChunkGroese * Mathf.Pow(2f, myLOD);
Vector3 nearestPointToCamera = new Vector3(
Mathf.Min(transform.position.x + LODSize, Mathf.Max(transform.position.x, cameraPosition.x)),
Mathf.Min(transform.position.y + LODSize, Mathf.Max(transform.position.y, cameraPosition.y)),
Mathf.Min(transform.position.z + LODSize, Mathf.Max(transform.position.z, cameraPosition.z))
);
float distanceToNearestCameraPosition = Vector3.Distance(nearestPointToCamera, cameraPosition);
if(distanceToNearestCameraPosition < LODSize / 2f)
{
if (distanceToNearestCameraPosition < LODSize)
GetComponent<MeshFilter>().sharedMesh = LOD_a;
else
GetComponent<MeshFilter>().sharedMesh = LOD_b;
if (myLOD == 0)
{
meshRenderer.enabled = true;
return;
}
meshRenderer.enabled = false;
for (int i = 0; i < 4; i++)
{
if (childChunks[i] == null) continue;
childChunks[i].gameObject.SetActive(true);
childChunks[i].SetLOD(cameraPosition);
}
}
else
{
if(distanceToNearestCameraPosition < LODSize)
GetComponent<MeshFilter>().sharedMesh = LOD_a;
else
GetComponent<MeshFilter>().sharedMesh = LOD_b;
meshRenderer.enabled = true;
for (int i = 0; i < 4; i++)
{
if (childChunks[i] == null) continue;
childChunks[i].gameObject.SetActive(false);
}
}
}
public void DoMeshUpdate()
{
if (grasFeld == null) return;
if (doUpdate == false) return;
doUpdate = false;
verts.Clear();
indes.Clear();
tipOffsets_missingInNextLOD.Clear();
if (myLOD > 0)
{
for(int i = 0; i < 4; i++)
{
if (childChunks[i] != null)
childChunks[i].DoMeshUpdate();
}
//Das Gras der Kindobjekte in den aktuellen Chunk hinzufuegen
for (int i = 0; i < 4; i++)
{
if (childChunks[i] != null)
{
foreach (Vector3 v in childChunks[i].LOD_b.vertices)
{
verts.Add(v + childChunks[i].transform.position - transform.position);
}
List<Vector4> tempUvs = new List<Vector4>();
childChunks[i].LOD_b.GetUVs(0, tempUvs);
tipOffsets_missingInNextLOD.AddRange(tempUvs);
}
}
for (int i = 0; i < verts.Count - 1; i++)
{
indes.Add(i);
}
if (LOD_a == null) LOD_a = new Mesh();
LOD_a.Clear();
LOD_a.vertices = verts.ToArray();
LOD_a.SetUVs(0, tipOffsets_missingInNextLOD);
LOD_a.SetIndices(indes.ToArray(), MeshTopology.Points, 0);
LOD_a.RecalculateBounds();
GetComponent<MeshFilter>().sharedMesh = LOD_a;
GetComponent<MeshRenderer>().sharedMaterial = materials[myLOD];
//Die helfte des grases entfernen
int initCount = verts.Count / 2;
for (int i = 0; i < initCount; i++)
{
int randomIndex = Random.Range(0, verts.Count - 1);
verts.RemoveAt(randomIndex);
tipOffsets_missingInNextLOD.RemoveAt(randomIndex);
}
indes.Clear();
for (int i=0; i < verts.Count - 1; i++)
{
indes.Add(i);
}
if (LOD_b == null) LOD_b = new Mesh();
LOD_b.Clear();
LOD_b.vertices = verts.ToArray();
LOD_b.SetUVs(0, tipOffsets_missingInNextLOD);
LOD_b.SetIndices(indes.ToArray(), MeshTopology.Points, 0);
LOD_b.RecalculateBounds();
}
else
CreateMeshForLOD_0();
}
private void CreateMeshForLOD_0()
{
//TODO: Die anderen grassorten (nicht nur index 0) in den submeshes generieren
if (LOD_a == null) LOD_a = new Mesh();
LOD_a.Clear();
verts = new List<Vector3>();
indes = new List<int>();
tipOffsets_missingInNextLOD = new List<Vector4>();
float myLodSize = basisChunkGroese * Mathf.Pow(2, myLOD);
Vector2Int chunckPos = WorldToChunkPos(transform.position);
if (grasFeld.allChunckData.TryGetValue(chunckPos, out var value))
{
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
for(int x =0; x < 3; x++)
{
Vector3 targetCreatePos = new Vector3(i * (basisChunkGroese / 16f), 0, j * (basisChunkGroese / 16f)) + new Vector3(Random.Range(-1f, 1f), 0, Random.Range(-1f, 1f));
byte currenPixelValue = value.Data[i * 16 + j][0];
if (currenPixelValue == 0)
continue;
verts.Add(targetCreatePos);
tipOffsets_missingInNextLOD.Add(new Vector4(Random.Range(-1f, 1f), Random.Range(.7f, 1.4f), Random.Range(-1f, 1f), 0));
indes.Add(verts.Count - 1);
}
}
}
}
LOD_a.vertices = verts.ToArray();
LOD_a.SetIndices(indes.ToArray(), MeshTopology.Points, 0);
LOD_a.SetUVs(0, tipOffsets_missingInNextLOD);
LOD_a.RecalculateBounds();
GetComponent<MeshFilter>().sharedMesh = LOD_a;
GetComponent<MeshRenderer>().sharedMaterial = materials[0];
var otherUV = new List<Vector4>();
otherUV.AddRange(tipOffsets_missingInNextLOD);
int initCount = verts.Count / 2;
for (int i = 0; i < initCount; i++)
{
int randomIndex = Random.Range(0, verts.Count - 1);
verts.RemoveAt(randomIndex);
otherUV.RemoveAt(randomIndex);
tipOffsets_missingInNextLOD[randomIndex] += new Vector4(0,0,0,1f);
}
indes.Clear();
for (int i = 0; i < verts.Count - 1; i++)
{
indes.Add(i);
}
if (LOD_b == null) LOD_b = new Mesh();
LOD_b.vertices = verts.ToArray();
LOD_b.SetUVs(0, otherUV);
LOD_b.SetIndices(indes.ToArray(), MeshTopology.Points, 0);
LOD_b.RecalculateBounds();
}
private Vector2Int WorldToChunkPos(Vector3 pos)
{
return new Vector2Int(Mathf.FloorToInt(pos.x / (int)basisChunkGroese) * (int)basisChunkGroese, Mathf.FloorToInt(pos.z / (int)basisChunkGroese) * (int)basisChunkGroese);
}
private Vector2Int InChunkFromLocalPos(Vector3 pos)
{
return new Vector2Int(Mathf.FloorToInt(pos.x / basisChunkGroese) * 15, Mathf.FloorToInt(pos.z / basisChunkGroese) * 15);
}
public void SetPosToGrid()
{
float myChunkSize = basisChunkGroese * Mathf.Pow(2f, myLOD);
transform.position = new Vector3(Mathf.FloorToInt(transform.position.x / myChunkSize) * (int)myChunkSize
,0
,Mathf.FloorToInt(transform.position.z / myChunkSize) * (int)myChunkSize);
}
public bool ContainsChunk(Vector2Int chunkPos)
{
float myChunkSize = basisChunkGroese * Mathf.Pow(2f, myLOD);
Vector2Int gridChunkPos = new Vector2Int(Mathf.FloorToInt(chunkPos.x / myChunkSize) * (int)myChunkSize,
Mathf.FloorToInt(chunkPos.y / myChunkSize) * (int)myChunkSize);
Vector2Int myGridPos = new Vector2Int(Mathf.FloorToInt(transform.position.x / myChunkSize) * (int)myChunkSize,
Mathf.FloorToInt(transform.position.z / myChunkSize) * (int)myChunkSize);
if(myGridPos == gridChunkPos)
return true;
return false;
}
public bool ContainsChunk(Vector2Int chunkPos, Vector2Int pos, int lod)
{
float myChunkSize = basisChunkGroese * Mathf.Pow(2f, lod);
Vector2Int gridChunkPos = new Vector2Int(Mathf.FloorToInt(chunkPos.x / myChunkSize) * (int)myChunkSize,
Mathf.FloorToInt(chunkPos.y / myChunkSize) * (int)myChunkSize);
Vector2Int myGridPos = new Vector2Int(Mathf.FloorToInt(pos.x / myChunkSize) * (int)myChunkSize,
Mathf.FloorToInt(pos.y / myChunkSize) * (int)myChunkSize);
if (myGridPos == gridChunkPos)
return true;
return false;
}
private void OnDrawGizmos()
{
return;
Vector2Int chukPosInt = WorldToChunkPos(transform.position);
if(false)
for (int i = 0; i < 16; i++)
for (int j = 0; j < 16; j++)
{
if(grasFeld.allChunckData != null && grasFeld.allChunckData.TryGetValue(chukPosInt, out var t))
{
byte b = t.Data[i * 16 + j][0];
Gizmos.color = new Color(b, b, b);
Gizmos.DrawCube(transform.position + new Vector3(i, 0, j) * (basisChunkGroese / 16f), Vector3.one * 0.1f);
}
}
#if UNITY_EDITOR
if(false)
{
Vector3 cameraPosition = SceneView.currentDrawingSceneView.camera.transform.position;
float LODSize = basisChunkGroese * Mathf.Pow(2f, myLOD);
Vector3 nearestPointToCamera = new Vector3(
Mathf.Min(transform.position.x + LODSize, Mathf.Max(transform.position.x, cameraPosition.x)),
Mathf.Min(transform.position.y + LODSize, Mathf.Max(transform.position.y, cameraPosition.y)),
Mathf.Min(transform.position.z + LODSize, Mathf.Max(transform.position.z, cameraPosition.z))
);
Gizmos.color = Color.red;
Gizmos.DrawCube(nearestPointToCamera, Vector3.one);
}
#endif
//if (myLOD != 0)
// return;
float size = basisChunkGroese * Mathf.Pow(2, myLOD);
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(transform.position + Vector3.one * size / 2f, Vector3.one * size);
if(Vector3.Distance(transform.position, Camera.current.transform.position) < 40)
if(DebugGrasTextur != null)
for(int i = 0; i < 16; i++)
for(int j = 0; j < 16; j++)
{
var pixelColor = DebugGrasTextur.GetPixel(i, j);
Gizmos.color = pixelColor;
Gizmos.DrawCube(transform.position + new Vector3(i, 0, j) * (1f / 16f) * basisChunkGroese - new Vector3(4.75f, 0, 4.75f), Vector3.one * (1f / 16f));
}
}
}