Advertisement
JontePonte

Job verlet

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