394 lines
14 KiB
C#
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));
|
|
}
|
|
}
|
|
|
|
}
|