fgm24/Assets/Scripts/Blood/BloodComputeShader.cs

343 lines
9.6 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using ComputeShaderUtility;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Rendering;
public struct Droplet
{
public Vector3 position;
public Vector3 velocity;
public uint enabled;
public uint airborne;
}
public class BloodComputeShader : MonoBehaviour
{
public static BloodComputeShader Instance { get; private set; }
public int numParticles = 1000;
public ComputeShader bloodCompute;
public Mesh mesh;
public Material instancedMaterial;
public float size;
public float scoreMult = 1.0f;
public int activeParticles = 0;
public long score = 0;
public int mop1Clean = 0;
public int mop2Clean = 0;
public float squeakVolume = 0.0f;
public AudioSource squeakPlayer;
public float splatterVolume = 0.0f;
public AudioSource splatterPlayer;
public float RumbleAmount = 100;
ComputeBuffer particleBuffer;
ComputeBuffer positionBuffer;
ComputeBuffer numParticlesConsumedBuffer;
ComputeBuffer argsBuffer;
ComputeBuffer freeParticleBuffer;
const int InitDustKernel = 0;
const int UpdateDustKernel = 1;
const int CollectAllKernel = 2;
AsyncGPUReadbackRequest readbackRequest;
AsyncGPUReadbackRequest freeBloodReadRequest;
public Mop mop1;
public Mop mop2;
public float CleanRadius = 2f;
[SerializeField]
uint bufferLookPointer = 0;
private bool gameHasStarted = false;
private void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
GameManager.OnPlayersReady += OnPlayersReady;
}
void Start()
{
ComputeHelper.CreateStructuredBuffer<Droplet>(ref particleBuffer, numParticles);
ComputeHelper.CreateStructuredBuffer<Vector4>(ref positionBuffer, numParticles);
Droplet[] particles = new Droplet[numParticles];
for (int i = 0; i < numParticles; i++)
{
particles[i] = new Droplet() { position = Vector3.zero, velocity = Vector3.zero, enabled = 0, airborne = 0 };
}
particleBuffer.SetData(particles);
bloodCompute.SetBuffer(UpdateDustKernel, "particles", particleBuffer);
bloodCompute.SetBuffer(UpdateDustKernel, "positions", positionBuffer);
// Init dust particle positions
bloodCompute.SetBuffer(InitDustKernel, "particles", particleBuffer);
bloodCompute.SetBuffer(InitDustKernel, "positions", positionBuffer);
bloodCompute.SetBuffer(CollectAllKernel, "particles", particleBuffer);
bloodCompute.SetBuffer(CollectAllKernel, "positions", positionBuffer);
bloodCompute.SetInt("numParticles", numParticles);
// Create args buffer
uint[] args = new uint[5];
args[0] = (uint)mesh.GetIndexCount(0);
args[1] = (uint)numParticles;
args[2] = (uint)mesh.GetIndexStart(0);
args[3] = (uint)mesh.GetBaseVertex(0);
args[4] = 0; // offset
argsBuffer = new ComputeBuffer(1, 5 * sizeof(uint), ComputeBufferType.IndirectArguments);
argsBuffer.SetData(args);
ComputeHelper.CreateStructuredBuffer<uint>(ref numParticlesConsumedBuffer, 3);
numParticlesConsumedBuffer.SetData(new uint[] { 0, 0, 0 });
bloodCompute.SetBuffer(UpdateDustKernel, "numParticlesConsumed", numParticlesConsumedBuffer);
// Initialize with empty data
ComputeHelper.CreateStructuredBuffer<uint>(ref freeParticleBuffer, numParticles);
RequestAsyncReadback();
instancedMaterial.SetBuffer("positionBuffer", positionBuffer);
}
void RequestAsyncReadback()
{
readbackRequest = AsyncGPUReadback.Request(numParticlesConsumedBuffer);
}
void RequestAllBloodStates()
{
freeBloodReadRequest = AsyncGPUReadback.Request(freeParticleBuffer);
}
void OnPlayersReady(GameObject[] players)
{
mop1 = players[0].GetComponent<Mop>();
mop2 = players[1].GetComponent<Mop>();
gameHasStarted = true;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.B))
createBloodTest(1000);
//if (!gameHasStarted) return;
//Assert.IsNotNull(mop1);
//Assert.IsNotNull(mop2);
bloodCompute.SetFloat("deltaTime", Time.deltaTime);
bloodCompute.SetInt("numParticles", numParticles);
bloodCompute.SetFloat("size", size);
bloodCompute.SetFloat("gravity", 9.8f);
//bloodCompute.SetVector("mop1Pos", mop1.transform.position);
//bloodCompute.SetVector("mop2Pos", mop2.transform.position);
bloodCompute.SetVector("mop1Pos", Vector3.zero); // Debug
bloodCompute.SetVector("mop2Pos", Vector3.zero);
bloodCompute.SetFloat("CleanRadius", CleanRadius);
// if (Input.GetKeyUp(KeyCode.Alpha9)) {
// cleanAllBlood();
// }
if (readbackRequest.hasError)
{
RequestAllBloodStates();
Debug.Log("Async readback error");
return;
}
bool putBuffer = false;
uint[] bufferData = {0,0,0};
if (readbackRequest.done)
{
bufferData = readbackRequest.GetData<uint>().ToArray();
// Blood cleaned
if (bufferData[0] > 0 || bufferData[1] > 0)
{
// Debug.Log("Cleaned " + bufferData[0]);
uint totalBloodCleaned = bufferData[0] + bufferData[1];
activeParticles -= (int)totalBloodCleaned;
score += totalBloodCleaned;
// this doesnt exist but ok
// float mappedRumble = Convert.ToSingle(bufferData[0]).Remap(0, RumbleAmount, 0, 0.1f);
//RumbleManager.StartRumble(-1, 0, mappedRumble, 0.1f);
squeakVolume += 0.3f;
mop1Clean += (int)bufferData[0];
mop2Clean += (int)bufferData[1];
// Reset counter
putBuffer = true;
bufferData[0] = 0;
bufferData[1] = 0;
}
// Blood hitting the floor
if (bufferData[1] > 0)
{
splatterVolume += bufferData[1] / 10.0f;
// Debug.Log("splat x" + bufferData[1]);
putBuffer = true;
bufferData[2] = 0;
}
RequestAsyncReadback();
}
if (putBuffer)
{
numParticlesConsumedBuffer.SetData(bufferData);
bloodCompute.SetBuffer(UpdateDustKernel, "numParticlesConsumed", numParticlesConsumedBuffer);
}
ComputeHelper.Dispatch(bloodCompute, numParticles, 1, 1, UpdateDustKernel);
Graphics.DrawMeshInstancedIndirect(mesh, 0, instancedMaterial, new Bounds(Vector3.zero, Vector3.one * 1000), argsBuffer);
splatterVolume *= 0.9f;
splatterPlayer.volume = splatterVolume;
squeakPlayer.volume = squeakVolume;
squeakVolume *= 0.8f;
if (splatterVolume < 0.001)
splatterVolume = 0;
if (squeakVolume < 0.001)
squeakVolume = 0;
mop1Clean = (int)(mop1Clean * 0.8f);
mop2Clean = (int)(mop2Clean * 0.8f);
}
void FixedUpdate()
{
}
public void cleanAllBlood() {
ComputeHelper.Dispatch(bloodCompute, numParticles, 1, 1, CollectAllKernel);
}
public void createBlood(Vector3 wher, int muchies, float powah)
{
StartCoroutine(penisBlood(wher, muchies, powah));
}
IEnumerator penisBlood(Vector3 loc, int amount, float power)
{
RequestAllBloodStates();
// Wait until we get the state of all the particles from the gpu
while (!freeBloodReadRequest.done)
{
yield return new WaitForEndOfFrame();
}
// Find N particles which are disabled
uint i = bufferLookPointer;
int found = 0;
Droplet[] particles = new Droplet[numParticles];
var as_Particles = freeBloodReadRequest.GetData<Droplet>().ToArray();
int length = as_Particles.Length;
// particleBuffer.GetData(particles);
uint[] particleIndeces = new uint[amount];
// oof
while (i < numParticles)
{
if (as_Particles[i % (length - 1)].enabled == 0)
{ // Found unused particle
particleIndeces[found] = i;
found++;
if (found >= amount)
break;
}
i++;
}
bufferLookPointer = (uint)((bufferLookPointer + amount) % numParticles);
// Debug.Log(string.Join(", ", particleIndeces));
// send data to gpu
freeParticleBuffer.SetData(particleIndeces);
bloodCompute.SetBuffer(InitDustKernel, "freeParticles", freeParticleBuffer);
// Test for race conditions
// yield return new WaitForSeconds(1.0f);
bloodCompute.SetFloat("particleVel", power / 4.0f);
bloodCompute.SetVector("particleInitPos", loc);
bloodCompute.SetInt("particlesToInitialize", found);
Vector3 pow = UnityEngine.Random.insideUnitSphere * power;
pow.z = Mathf.Abs(pow.z);
bloodCompute.SetVector("initialVelocity", pow);
ComputeHelper.Dispatch(bloodCompute, amount, 1, 1, InitDustKernel);
activeParticles += found;
yield return null;
}
void OnDestroy()
{
ComputeHelper.Release(particleBuffer, positionBuffer, argsBuffer, numParticlesConsumedBuffer, freeParticleBuffer);
}
public void createBloodTest(int amount)
{
createBlood(Vector3.zero, amount, 10.0f);
}
}