przemyslawzaworski/Unity-GPU-Based-Occlusion-Culling

Fixed the issue with the shadows!

Opened this issue · 0 comments

I have fixed the problem with the shadows , here is the updated code for the HardwareOcclusion.cs file :

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Rendering;
using Unity.Collections;
public class HardwareOcclusion : MonoBehaviour
{
public GameObject[] Targets;
public Shader HardwareOcclusionShader;
public ComputeShader IntersectionShader;
public bool Intersection = true;
public bool Dynamic = false;
public uint Delay = 1;
public bool Debug = false;

private Material _Material;
private ComputeBuffer _Reader;
private ComputeBuffer _Writer;
private Vector4[] _Elements;
private Vector4[] _Cache;
private List<List<MeshRenderer>> _MeshRenderers;
private List<Vector4> _Vertices;

private ComputeBuffer _AABB;
private ComputeBuffer _Intersection;
private Cuboid[] _Cuboids;
private int[] _Reset;
private int _CellIndex = -1;
private Coroutine _Coroutine;


struct Cuboid
{
	public Vector3 Center;
	public Vector3 Scale;
}

Vector3 GetCenterFromCubeVertices(Vector4[] verts)
{
	Vector3 total = Vector3.zero;
	int length = verts.Length;
	for (int i = 0; i < length; i++)
	{
		total += new Vector3(verts[i].x, verts[i].y, verts[i].z);
	}
	return total / length;
}

Vector3 GetScaleFromCubeVertices(Vector4[] verts)
{
	Vector3 min = Vector3.positiveInfinity;
	Vector3 max = Vector3.negativeInfinity;
	for (int i = 0; i < verts.Length; i++)
	{
		Vector3 point = new Vector3(verts[i].x, verts[i].y, verts[i].z);
		min = Vector3.Min(min, point);
		max = Vector3.Max(max, point);
	}
	return (max - min) * 0.5f;
}

Vector4[] GenerateCell(GameObject parent, int index)
{
	BoxCollider bc = parent.AddComponent<BoxCollider>();
	Bounds bounds = new Bounds(Vector3.zero, Vector3.zero);
	bool hasBounds = false;
	MeshRenderer[] renderers = parent.GetComponentsInChildren<MeshRenderer>();
	for (int i = 0; i < renderers.Length; i++)
	{
		if (hasBounds)
		{
			bounds.Encapsulate(renderers[i].bounds);
		}
		else
		{
			bounds = renderers[i].bounds;
			hasBounds = true;
		}
	}
	if (hasBounds)
	{
		bc.center = bounds.center - parent.transform.position;
		bc.size = bounds.size;
	}
	else
	{
		bc.size = bc.center = Vector3.zero;
		bc.size = Vector3.zero;
	}
	bc.size = Vector3.Scale(bc.size, new Vector3(1.01f, 1.01f, 1.01f));
	GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
	cube.transform.position = parent.transform.position + bc.center;
	cube.transform.localScale = bc.size;
	Mesh mesh = cube.GetComponent<MeshFilter>().sharedMesh;
	Vector4[] vertices = new Vector4[mesh.triangles.Length];
	for (int i = 0; i < vertices.Length; i++)
	{
		Vector3 p = cube.transform.TransformPoint(mesh.vertices[mesh.triangles[i]]);
		vertices[i] = new Vector4(p.x, p.y, p.z, index);
	}
	Destroy(bc);
	Destroy(cube);
	return vertices;
}

void GenerateMap()
{
	_Vertices.Clear();
	_Vertices.TrimExcess();
	for (int i = 0; i < Targets.Length; i++)
	{
		try
		{
			Vector4[] aabb = GenerateCell(Targets[i], i);
			_Cuboids[i].Center = GetCenterFromCubeVertices(aabb);
			_Cuboids[i].Scale = GetScaleFromCubeVertices(aabb);
			_Vertices.AddRange(aabb);
		}
		catch
		{
			XUpdateList();
		}

	}
	_Reader.SetData(_Vertices.ToArray());
}

bool ArrayState(Vector4[] a, Vector4[] b)
{
	for (int i = 0; i < a.Length; i++)
	{
		bool x = Vector4.Dot(a[i], a[i]) > 0.0f;
		bool y = Vector4.Dot(b[i], b[i]) > 0.0f;
		if (x != y) return false;
	}
	return true;
}

void ArrayCopy(Vector4[] source, Vector4[] destination)
{
	for (int i = 0; i < source.Length; i++) destination[i] = source[i];
}

void Init()
{
	if (_Material == null)
		_Material = new Material(HardwareOcclusionShader);

	_MeshRenderers = new List<List<MeshRenderer>>();

	int stride = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Cuboid));



	_Writer = new ComputeBuffer(Targets.Length, 16, ComputeBufferType.Default);
	_Elements = new Vector4[Targets.Length];
	_Cache = new Vector4[Targets.Length];
	_Cuboids = new Cuboid[Targets.Length];

	if (_Cache.Length > 0)
		_Cache[0] = Vector4.one;

	_Vertices = new List<Vector4>();

	Graphics.ClearRandomWriteTargets();
	Graphics.SetRandomWriteTarget(1, _Writer, false);

	for (int i = 0; i < Targets.Length; i++)
	{
		try
		{
			_MeshRenderers.Add(Targets[i].GetComponentsInChildren<MeshRenderer>().ToList());
			Vector4[] aabb = GenerateCell(Targets[i], i);
			_Cuboids[i].Center = GetCenterFromCubeVertices(aabb);
			_Cuboids[i].Scale = GetScaleFromCubeVertices(aabb);
			_Vertices.AddRange(aabb);
		}
		catch
		{
			XUpdateList();
		}
	}

	_Reader = new ComputeBuffer(_Vertices.Count, 16, ComputeBufferType.Default);
	_Reader.SetData(_Vertices.ToArray());

	_Material.SetBuffer("_Reader", _Reader);
	_Material.SetBuffer("_Writer", _Writer);
	_Material.SetInt("_Debug", System.Convert.ToInt32(Debug));

	// Adjusted the stride here as well
	_AABB = new ComputeBuffer(_Cuboids.Length, stride, ComputeBufferType.Default);

	_Intersection = new ComputeBuffer(1, sizeof(int), ComputeBufferType.Default);

	IntersectionShader.SetBuffer(0, "_AABB", _AABB);
	IntersectionShader.SetBuffer(0, "_Intersection", _Intersection);

	//// Adjusted the size of _Cuboids to match the stride

	_AABB.SetData(_Cuboids, 0, 0, _Cuboids.Length);

	//// Create an array of int to hold the reset value
	_Reset = new int[1] { -1 };

	// Check if the Intersection coroutine should be started
	//_Coroutine = Intersection ? StartCoroutine(UpdateAsync()) : null;
	StartCoroutine(UpdateAsync());

}

void OnEnable()
{
	if (Targets.Length == 0) return;
	Init();
}

void Update()
{

	if (Targets.Length == 0) return;
	if (Dynamic) GenerateMap();
	if (Time.frameCount % Delay != 0) return;
	_Writer.GetData(_Elements);
	bool state = ArrayState(_Elements, _Cache);
	if (!state)
	{
		for (int i = 0; i < _MeshRenderers.Count; i++)
		{
			for (int j = 0; j < _MeshRenderers[i].Count; j++)
			{
				try
				{
					if (i == _CellIndex)
					{
						_MeshRenderers[i][j].enabled = true;
						if (_MeshRenderers[i][j].shadowCastingMode == ShadowCastingMode.ShadowsOnly)
						{
							_MeshRenderers[i][j].shadowCastingMode = ShadowCastingMode.On;
						}
					}

					else
					{
						if (Vector4.Dot(_Elements[i], _Elements[i]) > 0.0f == false)
						{
							if (_MeshRenderers[i][j].shadowCastingMode == ShadowCastingMode.Off)
							{
								_MeshRenderers[i][j].enabled = false;
							}
							else if (_MeshRenderers[i][j].shadowCastingMode == ShadowCastingMode.On)
							{
								_MeshRenderers[i][j].shadowCastingMode = ShadowCastingMode.ShadowsOnly;
							}
						}
						else
						{
							_MeshRenderers[i][j].enabled = true;
							if (_MeshRenderers[i][j].shadowCastingMode == ShadowCastingMode.ShadowsOnly)
							{
								_MeshRenderers[i][j].shadowCastingMode = ShadowCastingMode.On;
							}

						}
					}

				}
				catch
				{
					XUpdateList();
				}
			}
		}
		ArrayCopy(_Elements, _Cache);
	}
	System.Array.Clear(_Elements, 0, _Elements.Length);
	_Writer.SetData(_Elements);
}

IEnumerator UpdateAsync()
{
	while (true)
	{
		Vector3 position = Camera.main.transform.position;
		IntersectionShader.SetVector("_Point", new Vector4(position.x, position.y, position.z, 0.0f));
		_Intersection.SetData(_Reset);
		int threadGroupsX = (int)Mathf.Ceil(_Cuboids.Length / 8.0f);
		IntersectionShader.Dispatch(0, threadGroupsX, 1, 1);
		AsyncGPUReadbackRequest request = AsyncGPUReadback.Request(_Intersection);
		yield return new WaitUntil(() => request.done);
		_CellIndex = request.GetData<int>()[0];
	}
}

void OnRenderObject()
{
	if (_Vertices == null) return;
	_Material.SetPass(0);
	Graphics.DrawProceduralNow(MeshTopology.Triangles, _Vertices.Count, 1);
}

void OnDisable()
{
	//if (Targets.Length == 0) return;
	//if (_Coroutine != null) StopCoroutine(_Coroutine);
	//_Reader.Release();
	//_Writer.Release();
	//_AABB.Release();
	//_Intersection.Release();
	//for (int i = 0; i < _MeshRenderers.Count; i++)
	//{
	//	for (int j = 0; j < _MeshRenderers[i].Count; j++)
	//	{
	//		_MeshRenderers[i][j].enabled = true;
	//	}
	//}
}

public void XUpdateList()
{
	Targets = GameObject.FindGameObjectsWithTag("XOccludeMe");
}

void HandleLog(string logString, string stackTrace, LogType type)
{

	XUpdateList();
}

}