Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using UnityEngine;
- using System.Collections;
- using System.Diagnostics;
- using TMPro;
- using Unity.Collections;
- using Unity.Jobs;
- using Unity.Mathematics;
- using Debug = UnityEngine.Debug;
- using Random = UnityEngine.Random;
- public class VerletIntegration : MonoBehaviour
- {
- public int numParticles;
- public float radius;
- public float gravity;
- public float constraintRadius;
- public int collisionSubSteps;
- public Material objectMaterial;
- public ComputeShader verletShader;
- NativeArray<Particle> particles;
- ComputeBuffer particlesBuffer;
- NativeArray<int> grid;
- ComputeBuffer gridBuffer;
- NativeList<JobHandle> jobHandles;
- new ParticleRenderer renderer;
- TextMeshProUGUI latencyText;
- TextMeshProUGUI numParticlesText;
- int currentNumParticles;
- float radius2;
- int gridSize;
- void Start()
- {
- Application.targetFrameRate = 50;
- particles = new NativeArray<Particle>(numParticles, Allocator.Persistent);
- float cellSize = radius * 2;
- gridSize = Mathf.CeilToInt(10 / cellSize);
- grid = new NativeArray<int>(gridSize * gridSize * 3, Allocator.Persistent); // The maximum number of circles in one cell is 3
- jobHandles = new NativeList<JobHandle>(Allocator.Persistent);
- radius2 = radius * 2;
- latencyText = GameObject.Find("Latency").GetComponent<TextMeshProUGUI>();
- numParticlesText = GameObject.Find("NumParticles").GetComponent<TextMeshProUGUI>();
- GameObject.Find("ConstraintCircle").transform.localScale = new Vector3(constraintRadius * 2 + radius, constraintRadius * 2 + radius, 0);
- renderer = new(numParticles, objectMaterial, radius);
- StartCoroutine(SpawnObjects());
- }
- void FixedUpdate()
- {
- Stopwatch watch = new();
- watch.Start();
- UpdatePositions();
- RebuildGrid();
- particlesBuffer.SetData(particles);
- gridBuffer.SetData(grid);
- watch.Stop();
- latencyText.text = watch.ElapsedMilliseconds + " ms";
- if (watch.ElapsedMilliseconds > 16)
- {
- Debug.Log(numParticlesText);
- return;
- }
- }
- void Update()
- {
- renderer.Render(currentNumParticles, particles);
- if (!Application.isPlaying)
- {
- renderer.ReleaseBuffers();
- particles.Dispose();
- grid.Dispose();
- jobHandles.Dispose();
- }
- }
- void UpdatePositions()
- {
- float dt = Time.fixedDeltaTime / collisionSubSteps;
- for (int i = 0; i < currentNumParticles; i++)
- {
- float2 position = particles[i].position;
- //Perform verlet integration
- float2 positionBeforeUpdate = position;
- position += position - particles[i].prevPosition;
- position += gravity * dt * dt * new float2(0, -1);
- //Constraint object inside a circle
- float2 constraintPosition = Vector2.zero;
- float2 constraintDiff = position - constraintPosition;
- float len = math.length(constraintDiff);
- if (len > constraintRadius - radius)
- {
- float2 n = constraintDiff / len;
- position = constraintPosition + n * (constraintRadius - radius);
- }
- particles[i] = new Particle
- {
- position = position,
- prevPosition = positionBeforeUpdate
- };
- }
- }
- void SolveCollisions()
- {
- for (int y = 1; y < gridSize - 1; y++)
- {
- for (int x = 1; x < gridSize - 1; x++)
- {
- int index = (x + y * gridSize) * 3;
- for (int yOffset = -1; yOffset <= 1; yOffset++)
- {
- for (int xOffset = -1; xOffset <= 1; xOffset++)
- {
- int otherIndex = (x + xOffset + (y + yOffset) * gridSize) * 3;
- SolveCellCollisions(index, otherIndex);
- }
- }
- }
- }
- }
- void SolveCellCollisions(int cell1Index, int cell2Index)
- {
- for (int i = 0; i < 3; i++)
- {
- int gridIndex1 = cell1Index + i;
- int particleIndex1 = grid[gridIndex1];
- if(particleIndex1 == -1) continue;
- for (int j = 0; j < 3; j++)
- {
- int gridIndex2 = cell2Index + j;
- int particleIndex2 = grid[gridIndex2];
- if (particleIndex2 == -1) continue;
- if(particleIndex1 == particleIndex2) continue;
- var object1 = particles[particleIndex1];
- var object2 = particles[particleIndex2];
- Vector2 collisionAxis = object1.position - object2.position;
- float distSqr = collisionAxis.x * collisionAxis.x + collisionAxis.y * collisionAxis.y;
- float minDist = radius2;
- if(distSqr < minDist * minDist)
- {
- float dist = Mathf.Sqrt(distSqr);
- float2 n = collisionAxis / dist; // Normalize collision axis
- float delta = minDist - dist;
- // Apply corrections to both objects
- object1.position += 0.5f * delta * n;
- object2.position -= 0.5f * delta * n;
- particles[particleIndex1] = object1;
- particles[particleIndex2] = object2;
- }
- }
- }
- }
- void RebuildGrid()
- {
- for (int y = 0; y < gridSize; y++)
- {
- for (int x = 0; x < gridSize; x++)
- {
- int baseIndex = (x + y * gridSize) * 3;
- grid[baseIndex] = -1;
- grid[baseIndex + 1] = -1;
- grid[baseIndex + 2] = -1;
- }
- }
- for (int i = 0; i < currentNumParticles; i++)
- {
- float xPos01 = (particles[i].position.x + 5 - radius) / 10f;
- float yPos01 = (particles[i].position.y + 5 - radius) / 10f;
- if (xPos01 < 0 || xPos01 > 1 || yPos01 < 0 || yPos01 > 1)
- {
- Debug.LogWarning("X: " + xPos01 + ", Y: " + yPos01);
- }
- int x = Mathf.FloorToInt(xPos01 * gridSize);
- int y = Mathf.FloorToInt(yPos01 * gridSize);
- int baseIndex = (x + y * gridSize) * 3;
- if(grid[baseIndex] == -1) grid[baseIndex] = i;
- else if(grid[baseIndex + 1] == -1) grid[baseIndex + 1] = i;
- else if (grid[baseIndex + 2] == -1) grid[baseIndex + 2] = i;
- else Debug.LogWarning("More than 3 circles in one cell!");
- }
- }
- IEnumerator SpawnObjects()
- {
- while (true)
- {
- // Vector2 mousePositionWorld = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
- Vector2 pos = new(Random.Range(-1f, 1f), 3);
- Particle particle = new()
- {
- position = pos,
- prevPosition = pos,
- };
- particles[currentNumParticles] = particle;
- currentNumParticles++;
- numParticlesText.text = currentNumParticles + " particles";
- if (currentNumParticles == numParticles) break;
- yield return new WaitForSeconds(0.07f);
- }
- }
- }
- public struct Particle
- {
- public float2 position, prevPosition;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement