594 lines
23 KiB
C#
594 lines
23 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class GrassLoader : MonoBehaviour {
|
|
|
|
public static GrassLoader instance;
|
|
void Awake () {
|
|
instance = this;
|
|
}
|
|
|
|
[Header("Requiered variables")]
|
|
public Mesh grassMesh;
|
|
public Mesh boxMesh;
|
|
public Material instancedGrassMaterial;
|
|
public Material instancedConsumeGrassMaterial;
|
|
public GrassPositionScriptableObject grassStorage;
|
|
public ComputeShader cullShader;
|
|
public ComputeShader convertArrayShader;
|
|
|
|
[Header("Tilemap (Occlusion culling)")]
|
|
public Vector3 bottomLeftCorner;
|
|
public float cellSideLengh = 1f;
|
|
public float totalLength = 100f;
|
|
|
|
[Header("3D culling")]
|
|
public Vector2 distanceBounds;
|
|
public Texture2D ditherTexture;
|
|
public float ditherSize;
|
|
|
|
[Header("Manual controls")]
|
|
public bool update;
|
|
public bool updateData;
|
|
public bool bakeCells;
|
|
public bool bakeOcclusion;
|
|
|
|
[Header("Testing")]
|
|
public bool debugTilemap1;
|
|
public bool debugTilemap2;
|
|
public bool debugTilemap3;
|
|
public bool debugTilemap4;
|
|
public bool showTilemap;
|
|
|
|
[Space]
|
|
public bool doCells;
|
|
public bool doCulling;
|
|
public bool doOcclusionCulling;
|
|
|
|
//[Header("Testing")]
|
|
//public bool update = false;
|
|
|
|
//data types
|
|
struct grassElement {
|
|
public Vector3 position;
|
|
public float rotation;
|
|
public float size;
|
|
public float colorBlend;//0-1
|
|
public grassElement (Vector3 position, float rotation, float size, float colorBlend) {
|
|
this.position = position;
|
|
this.rotation = rotation;
|
|
this.size = size;
|
|
this.colorBlend = colorBlend;
|
|
}
|
|
//Total size of is 4*3 + 3*4 = 24
|
|
}
|
|
|
|
struct grassCell {
|
|
public uint[] grassElementIndexes;//map to grass elements
|
|
public grassCell (uint[] grassElementIndexes) {
|
|
this.grassElementIndexes = grassElementIndexes;
|
|
}
|
|
public grassCell(grassCell x) {
|
|
grassElementIndexes = new uint[x.grassElementIndexes.Length];
|
|
for(int i = 0;i < x.grassElementIndexes.Length;i++){
|
|
grassElementIndexes[i] = x.grassElementIndexes[i] * 1;
|
|
}
|
|
}
|
|
//Total size = 4*length = must be calculated
|
|
}
|
|
|
|
struct grassOcclusionCell {
|
|
public uint grassCellIndex;
|
|
public uint[] visibleCellIndexes;//Map to occlusion cells
|
|
public grassOcclusionCell (uint grassCellIndex, uint[] visibleCellIndexes) {
|
|
this.grassCellIndex = grassCellIndex;
|
|
this.visibleCellIndexes = visibleCellIndexes;
|
|
}
|
|
//Total size = 4 + 4*length = must be calculated
|
|
}
|
|
|
|
//buffers
|
|
uint[] arguments = new uint[5] {0,0,0,0,0};
|
|
//main
|
|
ComputeBuffer grassBuffer;
|
|
ComputeBuffer argumentsBuffer;
|
|
ComputeBuffer allArgumentsBuffer;
|
|
//culling
|
|
ComputeBuffer workBuffer;//Append/Consume buffer for grass elements
|
|
ComputeBuffer grassCellBuffer;
|
|
ComputeBuffer cellWorkBuffer;
|
|
ComputeBuffer convertSizeBuffer;
|
|
|
|
|
|
public bool isOn = true;
|
|
|
|
|
|
int elementCount = 0;
|
|
int cellCount = 0;
|
|
|
|
bool startupDone;
|
|
|
|
grassElement[] grassElements;
|
|
Camera cam;
|
|
|
|
public void BakeCells () {
|
|
if(grassElements == null) UpdateMainBuffers();
|
|
bakeCells = false;
|
|
//Maps points into cells
|
|
|
|
// 0. Create tilemap
|
|
var tilemap = new Dictionary<int,List<int>>();
|
|
int cellsCount = Mathf.CeilToInt(totalLength/cellSideLengh);
|
|
for(int x = 0;x < cellsCount;x++) for(int y = 0;y < cellsCount;y++) tilemap.Add(x + y*cellsCount,new List<int>());
|
|
|
|
// 1. Set grassObjects into tiles
|
|
for(int i = 0;i < elementCount;i++) tilemap[GetTileIndex(grassElements[i].position)].Add(i);
|
|
|
|
//Debug
|
|
if(debugTilemap1) for(int x = 0;x < cellsCount;x++){
|
|
for(int y = 0;y < cellsCount;y++){
|
|
var pos = bottomLeftCorner + (x * Vector3.forward + y * Vector3.right)*cellSideLengh + Vector3.one * cellSideLengh/2f;
|
|
int i = GetTileIndex(pos);
|
|
if(tilemap.ContainsKey(i)) foreach(var k in tilemap[i]) Debug.DrawLine(grassElements[k].position + Vector3.right* 0.2f, pos,Color.green,100f);
|
|
}
|
|
}
|
|
|
|
|
|
// 2. Cull empty tiles
|
|
var removables = new Queue<int>();
|
|
foreach(var x in tilemap) if(x.Value.Count == 0) removables.Enqueue(x.Key);
|
|
foreach(var x in removables) tilemap.Remove(x);
|
|
|
|
//Debug
|
|
if(debugTilemap2) for(int x = 0;x < cellsCount;x++){
|
|
for(int y = 0;y < cellsCount;y++){
|
|
var pos = bottomLeftCorner + (x * Vector3.forward + y * Vector3.right)*cellSideLengh + Vector3.one * cellSideLengh/2f + Vector3.up;
|
|
int i = GetTileIndex(pos);
|
|
if(tilemap.ContainsKey(i)) foreach(var k in tilemap[i]) {Debug.DrawLine(grassElements[k].position + Vector3.right* 0.2f, pos,Color.magenta,100f); Debug.DrawRay(pos, Vector3.up,Color.magenta,100f);}
|
|
}
|
|
}
|
|
|
|
// 3. Get cell average grass count - not needed anymore
|
|
//float total = 0; float count = 0;
|
|
//foreach(var x in tilemap) { total += x.Value.Count; count++; }
|
|
//int mean = (int)(total/count);
|
|
|
|
//TODO 4. Convert tiles into cells of 512 elements (512 bc byte limit is 2048 and 1 index = 4 bytes so 512*4 = 2048)
|
|
|
|
var cells = new List<grassCell>();
|
|
var cellIdTilemap = new Dictionary<int,List<int>>();
|
|
foreach(var x in tilemap) {
|
|
//foreach entry convert indexes into how many cells are needed
|
|
var tileCellIDs = new List<int>();
|
|
|
|
var cellGrassIndexes = x.Value;
|
|
int leftToProcess = cellGrassIndexes.Count;
|
|
|
|
while(leftToProcess > 0) {
|
|
//Create new cell
|
|
var grassIndexArray = new uint[512];
|
|
for(int i = 0;i < grassIndexArray.Length;i++) {
|
|
if(cellGrassIndexes.Count > 0) {
|
|
grassIndexArray[i] = (uint)(cellGrassIndexes[0]);
|
|
cellGrassIndexes.RemoveAt(0);
|
|
leftToProcess--;
|
|
}
|
|
else grassIndexArray[i] = 0;
|
|
}
|
|
cells.Add( new grassCell(grassIndexArray) );
|
|
//Remember id
|
|
tileCellIDs.Add(cells.Count-1);
|
|
}
|
|
//Save cell ids so they can be used in occlusion culling
|
|
cellIdTilemap.Add(x.Key,tileCellIDs);
|
|
}
|
|
|
|
|
|
//Debug, foreach cell show which cells they map to by each cells connections
|
|
if(debugTilemap3) for(int x = 0;x < cellsCount;x++){
|
|
for(int y = 0;y < cellsCount;y++){
|
|
var pos = bottomLeftCorner + (x * Vector3.forward + y * Vector3.right)*cellSideLengh + Vector3.one * cellSideLengh/2f + Vector3.up * 2f;
|
|
int i = GetTileIndex(pos);
|
|
if(cellIdTilemap.ContainsKey(i)) {
|
|
//foreach cell get center
|
|
foreach(var cellIndex in cellIdTilemap[i]) {
|
|
var cell = cells[cellIndex];
|
|
Vector3 center = Vector3.zero;
|
|
float count = 0;
|
|
foreach(var grassIndex in cell.grassElementIndexes) {
|
|
if(grassIndex > 0) {center += grassElements[grassIndex].position; count++; Debug.DrawLine(grassElements[grassIndex].position, pos,Color.white,100f);}
|
|
}
|
|
Debug.DrawLine(center / count, pos,Color.blue,100f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Convert cells into array (bc array in a struct is not bittable...)
|
|
cellCount = cells.Count;
|
|
|
|
var convertedArray = new uint[cellCount * 512];
|
|
for(int i = 0;i < cellCount;i++){
|
|
var c = cells[i];
|
|
for(int j = 0;j < 512;j++) { convertedArray[j + i * 512] = c.grassElementIndexes[j]; if(debugTilemap4) Debug.DrawRay(grassElements[c.grassElementIndexes[j]].position, Vector3.up,Color.magenta,100f); }
|
|
}
|
|
|
|
//Wipe old buffers
|
|
if(grassCellBuffer != null) grassCellBuffer.Release();
|
|
if(cellWorkBuffer != null) cellWorkBuffer.Release();
|
|
if(convertSizeBuffer != null) convertSizeBuffer.Release();
|
|
|
|
//Create new buffers
|
|
grassCellBuffer = new ComputeBuffer(cellCount, 2048);
|
|
cellWorkBuffer = new ComputeBuffer(cellCount, 4, ComputeBufferType.Append);
|
|
convertSizeBuffer = new ComputeBuffer(1, 12, ComputeBufferType.IndirectArguments);
|
|
|
|
//Upload data
|
|
grassCellBuffer.SetData(convertedArray);
|
|
cellWorkBuffer.SetCounterValue(0);
|
|
convertSizeBuffer.SetData(new uint[]{1,1,1});
|
|
|
|
//Send buffers to shaders
|
|
cullShader.SetBuffer(1, "grassBuffer", grassBuffer);
|
|
cullShader.SetBuffer(1, "grassCellBuffer", grassCellBuffer);
|
|
cullShader.SetBuffer(1, "outCellIndexBuffer", cellWorkBuffer);
|
|
//cullShader.SetBuffer(1, "outGrassBuffer", workBuffer);
|
|
|
|
convertArrayShader.SetBuffer(0, "grassCellBuffer", grassCellBuffer);
|
|
convertArrayShader.SetBuffer(0, "outGrassBuffer", workBuffer);
|
|
convertArrayShader.SetBuffer(0, "grassCellIndexBuffer", cellWorkBuffer);
|
|
|
|
//Set buffer values
|
|
cullShader.SetInt("grassCellCount", cellCount);
|
|
cullShader.SetInt("grassCellSize", Mathf.CeilToInt(cellSideLengh));
|
|
cullShader.SetVector("distanceBounts", distanceBounds);
|
|
cullShader.SetTexture(0, "ditherTex", ditherTexture);
|
|
cullShader.SetTexture(1, "ditherTex", ditherTexture);
|
|
cullShader.SetFloat("ditherTexSizeX", ditherTexture.width);
|
|
cullShader.SetFloat("ditherSize", ditherSize);
|
|
|
|
Debug.Log("GRASS: Created " + cellCount.ToString() + " cells");
|
|
}
|
|
|
|
public void BakeOcclusionCells () {
|
|
/*
|
|
bakeOcclusion = false;
|
|
//Creates occlusion cells
|
|
|
|
//OCCLUSION GENERATION (from created cell structs)
|
|
|
|
// 1. Create target low and full objects
|
|
var grassCollider = new GameObject();
|
|
grassCollider.transform.name = "TEMP_GRASS_COLLIDER";
|
|
grassCollider.AddComponent(typeof(BoxCollider));
|
|
var gMeshFilter = grassCollider.AddComponent(typeof(MeshFilter)) as MeshFilter;
|
|
var gMeshRenderer = grassCollider.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
|
|
gMeshFilter.mesh = boxMesh;
|
|
grassCollider.transform.localScale = new Vector3(cellSideLengh,0.5f,cellSideLengh);
|
|
|
|
var fallableCollider = new GameObject();
|
|
fallableCollider.transform.name = "TEMP_FALLABLE_COLLIDER";
|
|
fallableCollider.AddComponent(typeof(BoxCollider));
|
|
var fMeshFilter = fallableCollider.AddComponent(typeof(MeshFilter)) as MeshFilter;
|
|
var fMeshRenderer = fallableCollider.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
|
|
fMeshFilter.mesh = boxMesh;
|
|
fallableCollider.transform.localScale = new Vector3(cellSideLengh,20f,cellSideLengh);
|
|
|
|
// 2. Foreach cell try to see each other cell, if not already marked visible
|
|
|
|
//Create full tilemap of all possible places where camera might be
|
|
var fullTilemap = new HashSet<int>();
|
|
for(int x = 0;x < cellCount;x++){
|
|
for(int y = 0;y < cellCount;y++){
|
|
var pos = bottomLeftCorner + (x * Vector3.forward + y * Vector3.right)*cellSideLengh;
|
|
if(Physics.Raycast(pos + Vector3.up*100f, -Vector3.up, Mathf.Infinity)) fullTilemap.Add(GetTileIndex(pos));
|
|
}
|
|
}
|
|
|
|
var occlusionCells = new Dictionary<int,List<int>>();
|
|
foreach(var x in tilemap) occlusionCells.Add(x.Key,new List<int>());
|
|
foreach(var x in fullTilemap) if(!occlusionCells.ContainsKey(x)) occlusionCells.Add(x,new List<int>());
|
|
|
|
void PositionColliderAtPostion (Vector3 pos, Transform collider) {
|
|
RaycastHit hit;
|
|
if(Physics.Raycast(pos + Vector3.up*100f, -Vector3.up, out hit, Mathf.Infinity)) {
|
|
collider.position = new Vector3(pos.x,hit.point.y + collider.localScale.y/2f,pos.z);
|
|
}
|
|
else collider.position = new Vector3(pos.x,0,pos.z);
|
|
}
|
|
bool DoRaycastTest (Vector3 from, Vector3 to, string targetName) {
|
|
//Will shoot rays 1. from the bottom of from, then from middle and then from top, if hit then returns true, else false
|
|
bool ShootRays (Vector3 pos) {
|
|
RaycastHit hit;
|
|
for(int i = 0;i < 5;i++){
|
|
var start = pos + new Vector3(Random.Range(0,1f),Random.Range(0,.5f),Random.Range(0,1f));
|
|
var target = to + new Vector3(Random.Range(0,1f),Random.Range(0,.5f),Random.Range(0,1f));
|
|
if( Physics.Raycast(start, target-start, out hit, Mathf.Infinity) ) if(hit.transform.name == targetName) return true;
|
|
}
|
|
return false;
|
|
}
|
|
if(ShootRays(from)) return true;
|
|
if(ShootRays(from + Vector3.up * 2f)) return true;
|
|
if(ShootRays(from + Vector3.up * 4f)) return true;
|
|
return false;
|
|
}
|
|
void TestVisibilityBetweenCells (int i, int j) {
|
|
//0. see if already done
|
|
var oI = occlusionCells[i];
|
|
var oJ = occlusionCells[j];
|
|
if(!oI.Contains(j)) {
|
|
//1. Get both positions
|
|
var posI = bottomLeftCorner + (i % cellCount) * Vector3.forward * cellSideLengh + Mathf.FloorToInt(i/cellCount) * Vector3.right * cellSideLengh;
|
|
var posJ = bottomLeftCorner + (j % cellCount) * Vector3.forward * cellSideLengh + Mathf.FloorToInt(j/cellCount) * Vector3.right * cellSideLengh;
|
|
//2. Try visibility form i -> j
|
|
PositionColliderAtPostion(posJ, grassCollider.transform);
|
|
if(DoRaycastTest(posI, posJ, "TEMP_GRASS_COLLIDER")) {
|
|
//3. If possible then register both as possible
|
|
oI.Add(j);
|
|
oJ.Add(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
int k = 0;
|
|
float t = fullTilemap.Count * tilemap.Count;
|
|
foreach(var x in fullTilemap) foreach(var y in tilemap) if(x != y.Key) {TestVisibilityBetweenCells(x,y.Key); k++; if(k%30000==0) {yield return 0;Debug.Log((k/t).ToString() + "% done");} } //Do all possible tests
|
|
Destroy(grassCollider);
|
|
Destroy(fallableCollider);
|
|
|
|
//Debug show connections
|
|
|
|
|
|
//TODO 3. Get cell average seeable length.
|
|
|
|
//TODO 4. Convert all cells into structs, if more seeables then create more structs per cell
|
|
|
|
//TODO Give cell and occulsion data to cullShader
|
|
|
|
|
|
//1. Create walkable area
|
|
|
|
//2. Foreach cell in walkable area find all grass cells that it sees
|
|
|
|
//3. Save data to storage
|
|
*/
|
|
}
|
|
|
|
public void Setup () {
|
|
cam = Camera.main;
|
|
//arguments buffer
|
|
argumentsBuffer = new ComputeBuffer(1, arguments.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
|
|
allArgumentsBuffer = new ComputeBuffer(1, arguments.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
|
|
//Main buffer
|
|
UpdateMainBuffers();
|
|
//Cells
|
|
BakeCells();
|
|
//Occlusion
|
|
//BakeOcclusionCells();
|
|
}
|
|
|
|
void DebugWorkBufferCount () {
|
|
var tempBuffer = new ComputeBuffer(1, sizeof(uint), ComputeBufferType.IndirectArguments);
|
|
uint[] x = new uint[1];
|
|
ComputeBuffer.CopyCount(workBuffer,tempBuffer,0);
|
|
tempBuffer.GetData(x);
|
|
var s = "";
|
|
foreach(var k in x) s += " " + k.ToString();
|
|
Debug.Log(s);
|
|
tempBuffer.Release();
|
|
}
|
|
|
|
void DebugWorkCellBufferCount () {
|
|
var tempBuffer = new ComputeBuffer(1, sizeof(uint), ComputeBufferType.IndirectArguments);
|
|
uint[] x = new uint[1];
|
|
ComputeBuffer.CopyCount(cellWorkBuffer,tempBuffer,0);
|
|
tempBuffer.GetData(x);
|
|
var s = "";
|
|
foreach(var k in x) s += " " + k.ToString();
|
|
Debug.Log(s);
|
|
tempBuffer.Release();
|
|
}
|
|
|
|
public void Run () {
|
|
if(!startupDone) return;//Wait for startup do complete
|
|
//0. manual update
|
|
if(update) UpdateMainBuffers();
|
|
if(bakeCells) BakeCells();
|
|
|
|
//1. UpdateCullBuffer
|
|
if(doCulling) UpdateCullBuffer();
|
|
|
|
//Draw
|
|
if(isOn) Graphics.DrawMeshInstancedIndirect(grassMesh, 0, doCulling?instancedConsumeGrassMaterial:instancedGrassMaterial, new Bounds(Vector3.zero, new Vector3(1000.0f, 1000.0f, 1000.0f)), doCulling?argumentsBuffer:allArgumentsBuffer);
|
|
|
|
//Testing
|
|
if(showTilemap) {
|
|
int cellsCount = Mathf.CeilToInt(totalLength/cellSideLengh);
|
|
for(int x = 0;x < cellsCount;x++){
|
|
for(int y = 0;y < cellsCount;y++){
|
|
var pos = bottomLeftCorner + (x * Vector3.forward + y * Vector3.right)*cellSideLengh;
|
|
Debug.DrawRay(pos, Vector3.up * 5f,Color.blue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateCullBuffer () {
|
|
//1. Update frustrum culling information
|
|
var cam = Camera.main;
|
|
var mat = cam.projectionMatrix * cam.worldToCameraMatrix;
|
|
var normals = new Vector3[4];
|
|
var normalsMatrix = new float[12];
|
|
|
|
Vector3 temp;
|
|
//left
|
|
temp.x = mat.m30 + mat.m00;
|
|
temp.y = mat.m31 + mat.m01;
|
|
temp.z = mat.m32 + mat.m02;
|
|
normals[0] = temp;
|
|
//right
|
|
temp.x = mat.m30 - mat.m00;
|
|
temp.y = mat.m31 - mat.m01;
|
|
temp.z = mat.m32 - mat.m02;
|
|
normals[1] = temp;
|
|
//bottom
|
|
temp.x = mat.m30 + mat.m10;
|
|
temp.y = mat.m31 + mat.m11;
|
|
temp.z = mat.m32 + mat.m12;
|
|
normals[2] = temp;
|
|
//top
|
|
temp.x = mat.m30 - mat.m10;
|
|
temp.y = mat.m31 - mat.m11;
|
|
temp.z = mat.m32 - mat.m12;
|
|
normals[3] = temp;
|
|
|
|
for (int i = 0; i < 4; i++){
|
|
//Debug.DrawRay(camera.transform.position, _planes[i].normal * 10f, Color.yellow);
|
|
normalsMatrix[i + 0] = normals[i].x;
|
|
normalsMatrix[i + 4] = normals[i].y;
|
|
normalsMatrix[i + 8] = normals[i].z;
|
|
}
|
|
|
|
cullShader.SetFloats("cameraPos", cam.transform.position.x, cam.transform.position.y, cam.transform.position.z);
|
|
cullShader.SetInt("doOcclusion",doOcclusionCulling?1:0);
|
|
cullShader.SetFloats("cameraFrustumNormals", normalsMatrix);
|
|
|
|
//Reset
|
|
if(cellWorkBuffer != null) cellWorkBuffer.SetCounterValue(0);
|
|
workBuffer.SetCounterValue(0);
|
|
|
|
//2. Dispatch culler
|
|
if(doCells && cellCount > 0 && cellWorkBuffer != null) {
|
|
|
|
//1. Get cell indexes
|
|
int batchCount = Mathf.CeilToInt(cellCount/64f);
|
|
cullShader.Dispatch(1,batchCount,1,1);
|
|
//2. Get object indexes
|
|
//convertSizeBuffer.SetData(new uint[] {1,1,1});
|
|
ComputeBuffer.CopyCount(cellWorkBuffer,convertSizeBuffer,0);
|
|
|
|
convertArrayShader.DispatchIndirect(0,convertSizeBuffer);
|
|
}
|
|
else {
|
|
int batchCount = Mathf.CeilToInt(elementCount/1024f);
|
|
cullShader.Dispatch(0,batchCount,1,1);
|
|
}
|
|
|
|
//3. Copy work count -> arguments
|
|
ComputeBuffer.CopyCount(workBuffer,argumentsBuffer,4);
|
|
}
|
|
|
|
int GetTileIndex (Vector3 pos) {
|
|
int cellsCount = Mathf.CeilToInt(totalLength/cellSideLengh);
|
|
//1. relative pos
|
|
var p = pos - bottomLeftCorner;
|
|
//2. safety
|
|
if(p.x < 0 || p.x > totalLength || p.z < 0 || p.z > totalLength) return 0;
|
|
//3. get index
|
|
return Mathf.CeilToInt(p.x/cellSideLengh) + cellsCount * Mathf.CeilToInt(p.z/cellSideLengh);
|
|
}
|
|
|
|
void UpdateMainBuffers () {
|
|
startupDone = false;
|
|
update = false;
|
|
|
|
//Debug.Log("Updating main");
|
|
elementCount = grassStorage.points.Count;
|
|
|
|
//Reset buffers
|
|
if(workBuffer != null) workBuffer.Release();
|
|
if(grassBuffer != null) grassBuffer.Release();
|
|
grassBuffer = new ComputeBuffer(elementCount, 24);
|
|
workBuffer = new ComputeBuffer(elementCount, 4, ComputeBufferType.Append);
|
|
workBuffer.SetCounterValue(0);//effectivly this clears the buffer
|
|
|
|
//Create grass elements from data
|
|
grassElements = new grassElement[elementCount];
|
|
|
|
for(int i = 0;i < elementCount;i++){
|
|
grassElements[i] = new grassElement(
|
|
grassStorage.points[i],
|
|
Random.Range(0f,360f),
|
|
Random.Range(0.9f,1f),
|
|
Random.Range(0,1f)
|
|
);
|
|
//Debug.DrawRay(grassStorage.points[i] + Vector3.right* 0.2f, Vector3.up * 10f,Color.yellow,100f);
|
|
}
|
|
|
|
Debug.Log("GRASS: Working with a total of " + grassElements.Length.ToString() + " grass objects");
|
|
|
|
//Save data to buffer
|
|
grassBuffer.SetData(grassElements);
|
|
|
|
distanceBounds = new Vector2(Mathf.Min(distanceBounds.y, distanceBounds.x),Mathf.Max(distanceBounds.y, distanceBounds.x)) ;
|
|
|
|
//Set buffers
|
|
cullShader.SetInt("grassCount", elementCount);
|
|
cullShader.SetFloat("maxDistance", distanceBounds.y);
|
|
cullShader.SetBuffer(0, "grassBuffer", grassBuffer);
|
|
cullShader.SetBuffer(0, "outGrassBuffer", workBuffer);
|
|
|
|
instancedConsumeGrassMaterial.SetBuffer("grassBuffer", grassBuffer);
|
|
instancedConsumeGrassMaterial.SetBuffer("inGrassBuffer", workBuffer);
|
|
|
|
instancedGrassMaterial.SetBuffer("grassBuffer", grassBuffer);
|
|
instancedGrassMaterial.SetFloat("fadeStartDist", distanceBounds.x - cellSideLengh);
|
|
instancedGrassMaterial.SetFloat("fadeEndDist", distanceBounds.y - cellSideLengh);
|
|
|
|
//Set arguments
|
|
arguments[0] = (grassMesh != null) ? (uint)grassMesh.GetIndexCount(0) : 0;
|
|
arguments[1] = (uint)elementCount;
|
|
argumentsBuffer.SetData(arguments);
|
|
allArgumentsBuffer.SetData(arguments);
|
|
|
|
if(doCulling) UpdateCullBuffer();
|
|
startupDone = true;
|
|
}
|
|
|
|
void OnDisable() {
|
|
if(grassBuffer != null) grassBuffer.Release(); grassBuffer = null;
|
|
if(argumentsBuffer != null) argumentsBuffer.Release(); argumentsBuffer = null;
|
|
if(allArgumentsBuffer != null) allArgumentsBuffer.Release(); allArgumentsBuffer = null;
|
|
if(workBuffer != null) workBuffer.Release(); workBuffer = null;
|
|
if(grassCellBuffer != null) grassCellBuffer.Release(); grassCellBuffer = null;
|
|
if(cellWorkBuffer != null) cellWorkBuffer.Release(); cellWorkBuffer = null;
|
|
if(convertSizeBuffer != null) convertSizeBuffer.Release(); convertSizeBuffer = null;
|
|
}
|
|
|
|
void Start () {
|
|
Setup();
|
|
}
|
|
|
|
void Update () {
|
|
Run();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
TODOS
|
|
|
|
1. Occlusion
|
|
- Switch to quads instead of traingles for grass
|
|
- Try shape that is more similar to cutout
|
|
- Add Wind
|
|
- Second billboard shader
|
|
- Billboard switch range
|
|
- Standard cull two outputs
|
|
- Cell cull two outputs
|
|
- Cell cull second converter
|
|
- Free camera
|
|
- Cell optimizations
|
|
- Distance dither cull? (optional)
|
|
|
|
2. Fallable particles (Rain, snow)
|
|
- Plan
|
|
|
|
2.1 Rain drop splashes
|
|
|
|
3. Volumetric fog with moving 3D noise and godrays?
|
|
|
|
4. Rain puddles, shininess
|
|
|
|
*/ |