Advertisement
JontePonte

subtree

Nov 19th, 2024
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 13.73 KB | None | 0 0
  1. using System;
  2. using System.Diagnostics;
  3. using System.Linq;
  4. using TMPro;
  5. using Unity.Collections;
  6. using Unity.Jobs;
  7. using Unity.VisualScripting;
  8. using UnityEngine;
  9. using UnityEngine.Profiling;
  10. using Unity.Mathematics;
  11. using UnityEngine.Serialization;
  12. using Debug = UnityEngine.Debug;
  13. using Random = UnityEngine.Random;
  14.  
  15. public class Simulation : MonoBehaviour
  16. {
  17.     public struct Particle
  18.     {
  19.         public float2 position, velocity;
  20.         public float mass;
  21.         public Color color;
  22.     }
  23.     public int particleCount;
  24.     public float radius;
  25.     public float mass;
  26.     public float timeScale;
  27.     [Range(1, 2)]
  28.     public float centerConcentration;
  29.     public float galaxySize;
  30.     public Gradient velocityGradient;
  31.    
  32.     [Range(0, 1)] public float approximationThreshold;
  33.     [Range(0, 1)] public float approximationSmoothing;
  34.     public int treeSize;
  35.  
  36.     [Header("")]
  37.     public bool centerAttraction;
  38.     public float centerMass;
  39.  
  40.     public float particleForce;
  41.  
  42.     public Material material;
  43.  
  44.     NativeArray<Particle> particles;
  45.     NativeArray<Particle> renderParticles;
  46.     QuadTree quadTree;
  47.     new ParticleRenderer renderer;
  48.     Particle center;
  49.     TextMeshProUGUI latency;
  50.     System.Random random;
  51.     NativeArray<Color> sampledGradient;
  52.     QuadTree[,] trees;
  53.  
  54.     JobHandle handle;
  55.  
  56.     public static int HalfScreenSize = 7;
  57.  
  58.     void Start()
  59.     {
  60.         center = new Particle
  61.         {
  62.             position = Vector2.zero,
  63.             velocity = Vector2.zero,
  64.             mass = centerMass
  65.         };
  66.        
  67.         random = new System.Random(0);
  68.         particles = new NativeArray<Particle>(particleCount + 1, Allocator.Persistent);
  69.         renderParticles = new NativeArray<Particle>(particleCount + 1, Allocator.Persistent);
  70.         particles[0] = center;
  71.         trees = new QuadTree[treeSize, treeSize];
  72.         sampledGradient = SampleGradient(velocityGradient, 256);
  73.  
  74.         for (int i = 1; i < particleCount; i++)
  75.         {
  76.             particles[i] = ParticlePositionAndVelocity();
  77.         }
  78.  
  79.         //latency = GameObject.Find("Latency").GetComponent<TextMeshProUGUI>();
  80.         renderer = new(particles, material, radius);
  81.     }
  82.  
  83.     Particle ParticlePositionAndVelocity()
  84.     {
  85.         Particle particle;
  86.         while (true)
  87.         {
  88.             // Generate radius using a quadratic distribution for higher central density
  89.             float radius = Mathf.Pow(Random.value, centerConcentration) * galaxySize; // Adjust power for density control
  90.             float angle = Random.Range(0f, Mathf.PI * 2f); // Uniform angle
  91.  
  92.             // Convert polar coordinates to Cartesian
  93.             Vector2 pos = new Vector2(radius * Mathf.Cos(angle), radius * Mathf.Sin(angle));
  94.             float centerDist = pos.magnitude;
  95.  
  96.             // Calculate circular orbit velocity
  97.             float force = Mathf.Sqrt(centerMass / centerDist) * particleForce;
  98.             Vector2 tangentDirection = new Vector2(-pos.y, pos.x).normalized; // Tangential direction
  99.             Vector2 velocity = tangentDirection * force;
  100.  
  101.             // Skip if position is too close to the center to avoid instabilities
  102.             float noise = Mathf.PerlinNoise((pos.x + 1000) * 4, (pos.y + 1000) * 4);
  103.             if (centerDist > 0.2f && (noise > .8f || random.NextDouble() < .1f))
  104.             {
  105.                 particle = new Particle
  106.                 {
  107.                     position = pos,
  108.                     velocity = velocity,
  109.                     mass = mass,
  110.                     color = velocityGradient.Evaluate(noise + 1 - centerDist / galaxySize)
  111.                 };
  112.                 break;
  113.             }
  114.         }
  115.  
  116.         return particle;
  117.     }
  118.    
  119.     void FixedUpdate()
  120.     {
  121.         RebuildQuadTree();
  122.         float dt = Time.fixedDeltaTime * timeScale;
  123.         UpdateVelocities(dt);
  124.         UpdatePositions(dt);
  125.     }
  126.  
  127.     void Update()
  128.     {
  129.         renderer.Render(particles);
  130.         foreach (var tree in trees)
  131.         {
  132.             tree.DrawTree();
  133.         }
  134.     }
  135.  
  136.     void OnDestroy()
  137.     {
  138.         handle.Complete();
  139.         renderer.ReleaseBuffers();
  140.         particles.Dispose();
  141.         renderParticles.Dispose();
  142.         sampledGradient.Dispose();
  143.     }
  144.  
  145.     void RebuildQuadTree()
  146.     {
  147.         for (int y = 0; y < treeSize; y++)
  148.         {
  149.             for (int x = 0; x < treeSize; x++)
  150.             {
  151.                 float size = (float)HalfScreenSize * 2 / treeSize;
  152.                 trees[x, y] = new(size, new Vector2(-HalfScreenSize + x * size, -HalfScreenSize + y * size));
  153.             }
  154.         }
  155.  
  156.         foreach (var particle in particles)
  157.         {
  158.             foreach (var tree in trees)
  159.             {
  160.                 tree.Insert(particle);
  161.             }
  162.         }
  163.  
  164.         foreach (var tree in trees)
  165.         {
  166.             tree.Propagate();
  167.         }
  168.     }
  169.  
  170.     void UpdateVelocities(float deltaTime)
  171.     {
  172.         for (int i = 0; i < particles.Length; i++)
  173.         {
  174.             Particle p = particles[i];
  175.             p.velocity += CalculateAcceleration(particles[i], deltaTime);
  176.             particles[i] = p;
  177.         }
  178.     }
  179.  
  180.     float2 CalculateAcceleration(Particle particle, float deltaTime)
  181.     {
  182.         Vector2 acceleration = new();
  183.         foreach (var tree in trees)
  184.         {
  185.             acceleration +=
  186.                 tree.CalculateAcceleration(particle, approximationThreshold, approximationSmoothing, deltaTime);
  187.         }
  188.  
  189.         return acceleration;
  190.     }
  191.    
  192.     void UpdatePositions(float deltaTime)
  193.     {
  194.         float minPosX = -HalfScreenSize + radius;
  195.         float maxPosX = HalfScreenSize - radius;
  196.         float minPosY = -HalfScreenSize + radius;
  197.         float maxPosY = HalfScreenSize - radius;
  198.  
  199.         UpdateParticlesJob job = new()
  200.         {
  201.             particles = particles,
  202.             gradient = sampledGradient,
  203.             gradientResolution = sampledGradient.Length,
  204.             deltaTime = deltaTime,
  205.             minPosX = minPosX,
  206.             maxPosX = maxPosX,
  207.             minPosY = minPosY,
  208.             maxPosY = maxPosY,
  209.             rng = new Unity.Mathematics.Random(1)
  210.         };
  211.         job.Schedule(particles.Length, 64).Complete();
  212.     }
  213.    
  214.     NativeArray<Color> SampleGradient(Gradient gradient, int resolution)
  215.     {
  216.         NativeArray<Color> gradientArray = new(resolution, Allocator.Persistent);
  217.  
  218.         for (int i = 0; i < resolution; i++)
  219.         {
  220.             gradientArray[i] = gradient.Evaluate((float)i / resolution);
  221.         }
  222.  
  223.         return gradientArray;
  224.     }
  225. }
  226. using System.Collections.Generic;
  227. using Unity.VisualScripting;
  228. using UnityEngine;
  229. using Unity.Mathematics;
  230. using Unity.Collections;
  231. using Unity.Jobs;
  232. using Unity.Burst;
  233. using Particle = Simulation.Particle;
  234.  
  235. public struct Node
  236. {
  237.     public Quad boundary;
  238.     public float mass;
  239.     public float2 centerOfMass;
  240.     public int childIndex;
  241.     public int next;
  242. }
  243. public struct Quad
  244. {
  245.     //Bottom left of rectangle
  246.     public Vector2 position;
  247.     public float size;
  248.    
  249.     public int FindNode(Vector2 pos)
  250.     {
  251.         bool wX = pos.x - position.x < size / 2;
  252.         bool sY = pos.y - position.y < size / 2;
  253.  
  254.         if (wX && !sY) return 0;
  255.         if (!wX && !sY) return 1;
  256.         if (wX && sY) return 2;
  257.         if(!wX && sY) return 3;
  258.  
  259.         return 0;
  260.     }
  261.    
  262.     public bool Intersects(Vector2 pos)
  263.     {
  264.         return pos.x > position.x && pos.x < position.x + size &&
  265.                pos.y > position.y && pos.y < position.y + size;
  266.     }
  267. }
  268.  
  269. public class QuadTree
  270. {
  271.     public NativeList<Node> nodes;
  272.  
  273.     public QuadTree(float size, Vector2 position)
  274.     {
  275.         nodes = new(Allocator.Persistent);
  276.         Quad boundary = new() { position = position, size = size };
  277.         nodes.Add(new Node
  278.         {
  279.             boundary = boundary,
  280.             next = 0
  281.         });
  282.     }
  283.  
  284.     ~QuadTree()
  285.     {
  286.         nodes.Dispose();
  287.     }
  288.  
  289.     public void DrawTree()
  290.     {
  291.         foreach (var node in nodes)
  292.         {
  293.             DrawSquare(node.boundary.position, node.boundary.size);
  294.         }
  295.     }
  296.     static void DrawSquare(Vector2 position, float size)
  297.     {
  298.         Debug.DrawLine(position, new Vector2(position.x, position.y + size), Color.red);
  299.         Debug.DrawLine(new Vector2(position.x, position.y + size), new Vector2(position.x + size, position.y + size), Color.red);
  300.         Debug.DrawLine(new Vector2(position.x + size, position.y + size), new Vector2(position.x + size, position.y), Color.red);
  301.         Debug.DrawLine(new Vector2(position.x + size, position.y), new Vector2(position.x, position.y), Color.red);
  302.     }
  303.    
  304.     public Vector2 CalculateAcceleration(Particle particle, float theta, float epsilon, float deltaTime)
  305.     {
  306.         Vector2 acceleration = new();
  307.         float thetaSqr = theta * theta;
  308.         float epsilonSqr = epsilon * epsilon;
  309.  
  310.         int nodeIndex = 0;
  311.         while (true)
  312.         {
  313.             Node node = nodes[nodeIndex];
  314.             Vector2 diff = node.centerOfMass - particle.position;
  315.             float sqrDst = Mathf.Max(diff.sqrMagnitude, 0.05f);
  316.  
  317.             if (node.childIndex == 0 || node.boundary.size * node.boundary.size < sqrDst * thetaSqr)
  318.             {
  319.                 float denom = (sqrDst + epsilonSqr) * Mathf.Sqrt(sqrDst);
  320.                 acceleration += diff * node.mass / denom * deltaTime;
  321.  
  322.                 if (node.next == 0) return acceleration;
  323.                 nodeIndex = node.next;
  324.             }
  325.             else nodeIndex = node.childIndex;
  326.         }
  327.     }
  328.  
  329.     public void Propagate()
  330.     {
  331.         int count = nodes.Length - 1;
  332.         for (int i = count; i > 0; i--)
  333.         {
  334.             int child = nodes[i].childIndex;
  335.             if(child == 0) continue;
  336.  
  337.             Node node = nodes[i];
  338.             Node nw = nodes[child];
  339.             Node ne = nodes[child + 1];
  340.             Node sw = nodes[child + 2];
  341.             Node se = nodes[child + 3];
  342.             node.centerOfMass = nw.centerOfMass * nw.mass
  343.                                 + ne.centerOfMass * ne.mass
  344.                                 + sw.centerOfMass * sw.mass
  345.                                 + se.centerOfMass * se.mass;
  346.             node.mass = nw.mass
  347.                         + ne.mass
  348.                         + sw.mass
  349.                         + se.mass;
  350.             node.centerOfMass /= node.mass;
  351.             nodes[i] = node;
  352.  
  353.         }
  354.     }
  355.  
  356.     public void Insert(Particle particle)
  357.     {
  358.         if (!nodes[0].boundary.Intersects(particle.position)) return;
  359.        
  360.         int nodeIndex = 0;
  361.         Node node = nodes[0];
  362.         while (node.childIndex != 0)
  363.         {
  364.             int quad = node.boundary.FindNode(particle.position);
  365.             nodeIndex = quad + node.childIndex;
  366.             node = nodes[nodeIndex];
  367.         }
  368.  
  369.         if (node.mass == 0)
  370.         {
  371.             node.centerOfMass = particle.position;
  372.             node.mass = particle.mass;
  373.             nodes[nodeIndex] = node;
  374.             return;
  375.         }
  376.  
  377.         if (Equals(node.centerOfMass, particle.position))
  378.         {
  379.             node.mass += particle.mass;  
  380.             nodes[nodeIndex] = node;
  381.             return;
  382.         }
  383.        
  384.         Vector2 nodePos = node.centerOfMass;
  385.         float nodeMass = node.mass;
  386.         while (true)
  387.         {
  388.             Subdivide(nodeIndex);
  389.             node = nodes[nodeIndex];
  390.             int childIndex = node.childIndex;
  391.             int q1 = node.boundary.FindNode(nodePos);
  392.             int q2 = node.boundary.FindNode(particle.position);
  393.  
  394.             if (q1 == q2)
  395.             {
  396.                 nodeIndex = q1 + childIndex;
  397.             }
  398.             else
  399.             {
  400.                 int n1 = q1 + childIndex;
  401.                 int n2 = q2 + childIndex;
  402.                    
  403.                 nodes[n1] = new Node
  404.                 {
  405.                     centerOfMass = nodePos,
  406.                     mass = nodeMass,
  407.                     boundary = nodes[n1].boundary,
  408.                     next = nodes[n1].next
  409.                 };
  410.                 nodes[n2] = new Node
  411.                 {
  412.                     centerOfMass = particle.position,
  413.                     mass = particle.mass,
  414.                     boundary = nodes[n2].boundary,
  415.                     next = nodes[n2].next
  416.                 };
  417.                 return;
  418.             }
  419.         }
  420.     }
  421.    
  422.     void Subdivide(int parentIndex)
  423.     {
  424.         Node parent = nodes[parentIndex];
  425.         parent.childIndex = nodes.Length;
  426.         nodes[parentIndex] = parent;
  427.         Vector2 parentPos = parent.boundary.position;
  428.         Vector2 nwPosition = new(parentPos.x, parentPos.y + parent.boundary.size / 2);
  429.         Vector2 nePosition = new(parentPos.x + parent.boundary.size / 2, parentPos.y + parent.boundary.size / 2);
  430.         Vector2 swPosition = new(parentPos.x, parentPos.y);
  431.         Vector2 sePosition = new(parentPos.x + parent.boundary.size / 2, parentPos.y);
  432.  
  433.         Node nw = new()
  434.         {
  435.             boundary = new Quad { position = nwPosition, size = parent.boundary.size / 2 },
  436.             next = parent.childIndex + 1
  437.         };
  438.         Node ne = new()
  439.         {
  440.             boundary = new Quad { position = nePosition, size = parent.boundary.size / 2 },
  441.             next = parent.childIndex + 2
  442.         };
  443.         Node sw = new()
  444.         {
  445.             boundary = new Quad { position = swPosition, size = parent.boundary.size / 2 },
  446.             next = parent.childIndex + 3
  447.         };
  448.         Node se = new()
  449.         {
  450.             boundary = new Quad { position = sePosition, size = parent.boundary.size / 2 },
  451.             next = parent.next
  452.         };
  453.        
  454.         nodes.Add(nw);
  455.         nodes.Add(ne);
  456.         nodes.Add(sw);
  457.         nodes.Add(se);
  458.     }
  459. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement