forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			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
 | 
						|
 | 
						|
*/ |