230 lines
6.6 KiB
C#
230 lines
6.6 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 class BloodComputeShader : MonoBehaviour
|
|
{
|
|
public int numParticles = 1000;
|
|
|
|
public ComputeShader bloodCompute;
|
|
public Mesh mesh;
|
|
public Material instancedMaterial;
|
|
public float size;
|
|
|
|
public int activeParticles = 0;
|
|
public int score = 0;
|
|
|
|
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;
|
|
|
|
// Start is called before the first frame update
|
|
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 };
|
|
|
|
}
|
|
|
|
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, 1);
|
|
numParticlesConsumedBuffer.SetData(new uint[] { 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;
|
|
}
|
|
|
|
if (readbackRequest.done)
|
|
{
|
|
uint data = readbackRequest.GetData<uint>()[0];
|
|
|
|
if (data > 0)
|
|
{
|
|
|
|
Debug.Log("Cleaned " + data);
|
|
|
|
activeParticles -= (int)data;
|
|
|
|
score += (int)data;
|
|
|
|
// Reset counter
|
|
numParticlesConsumedBuffer.SetData(new uint[] { 0 });
|
|
bloodCompute.SetBuffer(UpdateDustKernel, "numParticlesConsumed", numParticlesConsumedBuffer);
|
|
|
|
}
|
|
RequestAsyncReadback();
|
|
}
|
|
|
|
ComputeHelper.Dispatch(bloodCompute, numParticles, 1, 1, UpdateDustKernel);
|
|
|
|
Graphics.DrawMeshInstancedIndirect(mesh, 0, instancedMaterial, new Bounds(Vector3.zero, Vector3.one * 1000), argsBuffer);
|
|
}
|
|
|
|
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();
|
|
// particleBuffer.GetData(particles);
|
|
|
|
uint[] particleIndeces = new uint[amount];
|
|
|
|
// oof
|
|
while (i < numParticles)
|
|
{
|
|
if (as_Particles[i % numParticles].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);
|
|
|
|
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);
|
|
}
|
|
}
|