286 lines
8.0 KiB
C#
286 lines
8.0 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using ComputeShaderUtility;
|
|
using UnityEngine;
|
|
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 float squeakVolume = 0.0f;
|
|
public AudioSource squeakPlayer;
|
|
|
|
public float splatterVolume = 0.0f;
|
|
public AudioSource splatterPlayer;
|
|
|
|
ComputeBuffer particleBuffer;
|
|
ComputeBuffer positionBuffer;
|
|
ComputeBuffer numParticlesConsumedBuffer;
|
|
ComputeBuffer argsBuffer;
|
|
|
|
ComputeBuffer freeParticleBuffer;
|
|
|
|
const int InitDustKernel = 0;
|
|
const int UpdateDustKernel = 1;
|
|
AsyncGPUReadbackRequest readbackRequest;
|
|
|
|
AsyncGPUReadbackRequest freeBloodReadRequest;
|
|
|
|
Mop mop1;
|
|
Mop mop2;
|
|
|
|
public float CleanRadius = 2f;
|
|
|
|
[SerializeField]
|
|
uint bufferLookPointer = 0;
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance != null)
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
Instance = this;
|
|
}
|
|
void Start()
|
|
{
|
|
var mops = FindObjectsByType<Mop>(FindObjectsSortMode.None);
|
|
|
|
mop1 = mops[0];
|
|
mop2 = mops[1];
|
|
|
|
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.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, 2);
|
|
numParticlesConsumedBuffer.SetData(new uint[] { 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);
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void Update()
|
|
{
|
|
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.SetFloat("CleanRadius", CleanRadius);
|
|
|
|
if (readbackRequest.hasError)
|
|
{
|
|
RequestAllBloodStates();
|
|
Debug.Log("Async readback error");
|
|
return;
|
|
}
|
|
|
|
bool putBuffer = false;
|
|
uint[] bufferData = new uint[] {0,0};
|
|
|
|
if (readbackRequest.done)
|
|
{
|
|
bufferData[0] = readbackRequest.GetData<uint>()[0];
|
|
bufferData[1] = readbackRequest.GetData<uint>()[1];
|
|
|
|
// Blood cleaned
|
|
if (bufferData[0] > 0)
|
|
{
|
|
// Debug.Log("Cleaned " + bufferData[0]);
|
|
|
|
activeParticles -= (int)bufferData[0];
|
|
|
|
score += bufferData[0];
|
|
|
|
squeakVolume += 0.1f;
|
|
|
|
// Reset counter
|
|
putBuffer = true;
|
|
bufferData[0] = 0;
|
|
|
|
}
|
|
|
|
// Blood hitting the floor
|
|
if (bufferData[1] > 0) {
|
|
splatterVolume += bufferData[1]/25.0f;
|
|
|
|
// Debug.Log("splat x" + bufferData[1]);
|
|
|
|
putBuffer = true;
|
|
bufferData[1] = 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;
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
}
|
|
|
|
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);
|
|
bloodCompute.SetVector("particleInitPos", loc);
|
|
bloodCompute.SetInt("particlesToInitialize", found);
|
|
|
|
bloodCompute.SetVector("initialVelocity",Random.insideUnitCircle * power);
|
|
|
|
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);
|
|
}
|
|
}
|