Advertisement
JontePonte

Verlet gpu

Nov 6th, 2024
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.93 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Diagnostics;
  4. using TMPro;
  5. using Unity.Collections;
  6. using Unity.Jobs;
  7. using Unity.Mathematics;
  8. using Debug = UnityEngine.Debug;
  9. using Random = UnityEngine.Random;
  10.  
  11.  
  12. public class VerletIntegration : MonoBehaviour
  13. {
  14.     public int numParticles;
  15.     public float radius;
  16.     public float gravity;
  17.     public float constraintRadius;
  18.     public int collisionSubSteps;
  19.     public Material objectMaterial;
  20.     public ComputeShader verletShader;
  21.  
  22.     public int maxCount;
  23.     public float cellSize;
  24.    
  25.     Particle[] particles;
  26.     int[] grid;
  27.     ComputeBuffer particlesBuffer;
  28.     ComputeBuffer gridBuffer;
  29.    
  30.     new ParticleRenderer renderer;
  31.     TextMeshProUGUI latencyText;
  32.     TextMeshProUGUI numParticlesText;
  33.  
  34.     int currentNumParticles;
  35.     float radius2;
  36.     int gridSize;
  37.    
  38.     void Start()
  39.     {
  40.         Application.targetFrameRate = 50;
  41.  
  42.         particles = new Particle[numParticles];
  43.  
  44.         float cellSize = radius * (2f + this.cellSize);
  45.         gridSize = Mathf.CeilToInt(10 / cellSize);
  46.         grid = new int[gridSize * gridSize * maxCount]; // The maximum number of circles in one cell is 6
  47.         radius2 = radius * 2;
  48.        
  49.         latencyText = GameObject.Find("Latency").GetComponent<TextMeshProUGUI>();
  50.         numParticlesText = GameObject.Find("NumParticles").GetComponent<TextMeshProUGUI>();
  51.         GameObject.Find("ConstraintCircle").transform.localScale = new Vector3(constraintRadius * 2 + radius, constraintRadius * 2 + radius, 0);
  52.        
  53.         renderer = new(numParticles, objectMaterial, radius);
  54.        
  55.         verletShader.SetInt("gridSize", gridSize);
  56.         verletShader.SetFloat("radius2", radius2);
  57.         particlesBuffer = new ComputeBuffer(numParticles, sizeof(float) * 4);
  58.         gridBuffer = new ComputeBuffer(grid.Length, sizeof(int));
  59.        
  60.         StartCoroutine(SpawnObjects());
  61.     }
  62.  
  63.     void FixedUpdate()
  64.     {
  65.         Stopwatch watch = new();
  66.         watch.Start();
  67.  
  68.         for (int i = 0; i < collisionSubSteps; i++)
  69.         {
  70.             UpdatePositions();
  71.             RebuildGrid();
  72.            
  73.             particlesBuffer.SetData(particles);
  74.             gridBuffer.SetData(grid);
  75.        
  76.             verletShader.SetBuffer(0, "particles", particlesBuffer);
  77.             verletShader.SetBuffer(0, "grid", gridBuffer);
  78.             verletShader.Dispatch(0, (gridSize - 2) / 8, (gridSize - 2) / 8, 1);
  79.        
  80.             particlesBuffer.GetData(particles);
  81.             gridBuffer.GetData(grid);
  82.         }
  83.        
  84.         watch.Stop();
  85.         latencyText.text = watch.ElapsedMilliseconds + " ms";
  86.  
  87.         if (watch.ElapsedMilliseconds > 16)
  88.         {
  89.             Debug.Log(numParticlesText);
  90.             return;
  91.         }
  92.     }
  93.  
  94.     void Update()
  95.     {
  96.         renderer.Render(currentNumParticles, particles);
  97.         if (!Application.isPlaying)
  98.         {
  99.             renderer.ReleaseBuffers();
  100.         }
  101.     }
  102.  
  103.     void UpdatePositions()
  104.     {
  105.         float dt = Time.fixedDeltaTime / collisionSubSteps;
  106.  
  107.         for (int i = 0; i < currentNumParticles; i++)
  108.         {
  109.             float2 position = particles[i].position;
  110.            
  111.             //Perform verlet integration
  112.             float2 positionBeforeUpdate = position;
  113.             position += position - particles[i].prevPosition;
  114.             position += gravity * dt * dt * new float2(0, -1);
  115.            
  116.             //Constraint object inside a circle
  117.             float2 constraintPosition = Vector2.zero;
  118.             float2 constraintDiff = position - constraintPosition;
  119.             float len = math.length(constraintDiff);
  120.            
  121.             if (len > constraintRadius - radius)
  122.             {
  123.                 float2 n = constraintDiff / len;
  124.                 position = constraintPosition + n * (constraintRadius - radius);
  125.             }
  126.  
  127.             particles[i] = new Particle
  128.             {
  129.                 position = position,
  130.                 prevPosition = positionBeforeUpdate
  131.             };
  132.         }
  133.     }
  134.  
  135.     void SolveCollisions()
  136.     {
  137.         for (int y = 1; y < gridSize - 1; y++)
  138.         {
  139.             for (int x = 1; x < gridSize - 1; x++)
  140.             {
  141.                 int index = (x + y * gridSize) * 3;
  142.        
  143.                 for (int yOffset = -1; yOffset <= 1; yOffset++)
  144.                 {
  145.                     for (int xOffset = -1; xOffset <= 1; xOffset++)
  146.                     {
  147.                         int otherIndex = (x + xOffset + (y + yOffset) * gridSize) * 3;
  148.                         SolveCellCollisions(index, otherIndex);
  149.                     }
  150.                 }
  151.             }
  152.         }
  153.     }
  154.    
  155.     void SolveCellCollisions(int cell1Index, int cell2Index)
  156.     {
  157.         for (int i = 0; i < 3; i++)
  158.         {
  159.             int gridIndex1 = cell1Index + i;
  160.             int particleIndex1 = grid[gridIndex1];
  161.            
  162.             if(particleIndex1 == -1) continue;
  163.            
  164.             for (int j = 0; j < 3; j++)
  165.             {
  166.                 int gridIndex2 = cell2Index + j;
  167.                 int particleIndex2 = grid[gridIndex2];
  168.                
  169.                 if (particleIndex2 == -1) continue;
  170.                 if(particleIndex1 == particleIndex2) continue;
  171.                
  172.                 var object1 = particles[particleIndex1];
  173.                 var object2 = particles[particleIndex2];
  174.    
  175.                 Vector2 collisionAxis = object1.position - object2.position;
  176.                 float distSqr = collisionAxis.x * collisionAxis.x + collisionAxis.y * collisionAxis.y;
  177.                 float minDist = radius2;
  178.            
  179.                 if(distSqr < minDist * minDist)
  180.                 {
  181.                     float dist = Mathf.Sqrt(distSqr);
  182.                     float2 n = collisionAxis / dist; // Normalize collision axis
  183.                     float delta = minDist - dist;
  184.            
  185.                     // Apply corrections to both objects
  186.                     object1.position += 0.5f * delta * n;
  187.                     object2.position -= 0.5f * delta * n;
  188.                    
  189.                     particles[particleIndex1] = object1;
  190.                     particles[particleIndex2] = object2;
  191.                 }
  192.             }  
  193.         }
  194.     }
  195.    
  196.     void RebuildGrid()
  197.     {
  198.         for (int y = 0; y < gridSize; y++)
  199.         {
  200.             for (int x = 0; x < gridSize; x++)
  201.             {
  202.                 int baseIndex = (x + y * gridSize) * 3;
  203.                 // grid[baseIndex] = -1;
  204.                 // grid[baseIndex + 1] = -1;
  205.                 // grid[baseIndex + 2] = -1;
  206.  
  207.                 for (int i = 0; i < maxCount; i++)
  208.                 {
  209.                     grid[baseIndex + i] = -1;
  210.                 }
  211.             }
  212.         }
  213.        
  214.         for (int i = 0; i < currentNumParticles; i++)
  215.         {
  216.             float xPos01 = (particles[i].position.x + 5 - radius) / 10f;
  217.             float yPos01 = (particles[i].position.y + 5 - radius) / 10f;
  218.             if (xPos01 < 0 || xPos01 > 1 || yPos01 < 0 || yPos01 > 1)
  219.             {
  220.                 Debug.LogWarning("X: " + xPos01 + ", Y: " + yPos01);
  221.             }
  222.            
  223.             int x = Mathf.FloorToInt(xPos01 * gridSize);
  224.             int y =  Mathf.FloorToInt(yPos01 * gridSize);
  225.  
  226.             int baseIndex = (x + y * gridSize) * 3;
  227.             // if(grid[baseIndex] == -1) grid[baseIndex] = i;
  228.             // else if(grid[baseIndex + 1] == -1) grid[baseIndex + 1] = i;
  229.             // else if (grid[baseIndex + 2] == -1) grid[baseIndex + 2] = i;
  230.             // else if (grid[baseIndex + 3] == -1) grid[baseIndex + 3] = i;
  231.             // else if (grid[baseIndex + 4] == -1) grid[baseIndex + 4] = i;
  232.             // else if (grid[baseIndex + 5] == -1) grid[baseIndex + 5] = i;
  233.  
  234.             bool b = false;
  235.             for (int j = 0; j < maxCount; j++)
  236.             {
  237.                 if (grid[baseIndex + j] == -1)
  238.                 {
  239.                     grid[baseIndex + j] = i;
  240.                     b = true;
  241.                     break;
  242.                 }
  243.              
  244.             }
  245.             if(!b) Debug.LogWarning("Overflow");
  246.         }
  247.     }
  248.  
  249.  
  250.     IEnumerator SpawnObjects()
  251.     {
  252.         while (true)
  253.         {
  254.             // Vector2 mousePositionWorld = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0));
  255.             Vector2 pos = new(Random.Range(-1f, 1f), 3);
  256.             Particle particle = new()
  257.             {
  258.                 position = pos,
  259.                 prevPosition = pos,
  260.             };
  261.            
  262.             particles[currentNumParticles] = particle;
  263.             currentNumParticles++;
  264.             numParticlesText.text = currentNumParticles + " particles";
  265.            
  266.             if (currentNumParticles == numParticles) break;
  267.            
  268.             yield return new WaitForSeconds(0.07f);
  269.         }
  270.     }
  271. }
  272.  
  273. public struct Particle
  274. {
  275.     public float2 position, prevPosition;
  276. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement