343 lines
9.6 KiB
C#
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);
|
|
}
|
|
}
|