1

I have a simple 2D simulation which has particles travelling at given velocities and could collide with each other and the walls. But the fps drops when there are more than 500 particles. I want to simulate at least 5000 particles smoothly because I'll be adding more features to the program. Is there a different and efficient approach to this? Thanks!

gas.cpp:

#include<cstdlib>
#include<vector>
#include<glut.h>
#include "gas.h"
#include<time.h>


float t = 1;        //time step

void gas::p(float pos_x, float pos_y, float vx, float vy, float mass)
{
    srand(time(0));
    m = mass;
    x = pos_x;
    y = pos_y;
    velx = vx;
    vely = vy;
    size = 3;
}


void gas::draw()
{
    glColor3f(1, 1, 1);
    glVertex2f(x, y);

}
void gas::move(float t)
{
    x += velx * t;
    y += vely * t;
}

float temp;

//Function to be ran at every frame:
void run(std::vector <gas>& bn)
{
    int it = 0;
    for (gas& i : bn)
    {
        int jt = 0;
        for (gas& j : bn)
        {

            if (it != jt)
            {   
                //Handling collisions:
                if (abs(i.y - (j.y + j.size)) < 1 && (abs(j.x - i.x) <= i.size + 1) && i.vely < 0)
                {
                    temp = i.vely;
                    i.vely = j.vely;
                    j.vely = temp;

                }
                if (abs(j.y - (i.y + i.size)) < 1 && (abs(i.x - j.x) <= j.size + 1) && i.vely > 0)
                {
                    temp = i.vely;
                    i.vely = j.vely;
                    j.vely = temp;  
                }
                if (abs(j.x - (i.x + i.size)) < 1 && (abs(i.y - j.y) <= i.size + 1) && i.velx > 0)
                {
                    temp = i.velx;
                    i.velx = j.velx;
                    j.velx = temp;
                    
                }
                if (abs(i.x - (j.x + j.size)) < 1 && (abs(j.y - i.y) <= i.size + 1) && i.velx < 0)
                {
                    temp = i.velx;
                    i.velx = j.velx;
                    j.velx = temp;
                }
            }

            jt += 1;
        }

        //Boundary Collisions:
        if (i.x > 600 - i.size) { i.x = 600 - i.size; i.velx = -i.velx; }
        if (i.x < i.size) { i.x = i.size; i.velx = -i.velx; }
        if (i.y > 600 - i.size) { i.y = 600 - i.size; i.vely = -i.vely; }
        if (i.y < i.size) { i.y = i.size; i.vely = -i.vely; }

        
        i.move(t);
        it += 1;

    }
}

gas.h:

#pragma once

class gas
{
public:

    float m = 1;
    float x = 0;
    float y = 0;
    float velx = 0;
    float vely = 0;
    float size = 3;
    float density = 100;
    float r = 1; float g = 1; float b = 1;


    void p(float pos_x, float pos_y, float vx, float vy, float mass);
    void draw();
    void move(float t);
};

void run(std::vector<gas>& bn);

simulate.cpp (main file):

#include<cstdlib>
#include<glut.h>
#include<vector>
#include "gas.h"
#include<thread>
#include<time.h>

void display();
void reshape(int, int);

const int n = 600;       //Number of particles

void init()
{
    glClearColor(0, 0, 0, 1);
}

std::vector <gas> b(n);

void timer(int)
{
    run(b);
    glutPostRedisplay();
    glutTimerFunc(1000 / 60, timer, 0);
}

void show(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInitWindowPosition(300, 60);
    glutInitWindowSize(600, 600);


    glutCreateWindow("Particles");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);

    glutTimerFunc(1000, timer, 0);      // Args: Time delay per frame in milliseconds, function to be called
            
    init();
    glutMainLoop();
}

int main(int argc, char** argv)
{   
    int it = 0;
    for (gas& i : b)
    {
        srand(it);
        i.p(rand() % 600, rand() % 600, (rand() % 10) * pow(-1, it + 1), (rand() % 10) * pow(-1, it), 1);
        it += 1;

    }
    show(argc, argv);
}


void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();

    glPointSize(3);
    glBegin(GL_POINTS);

    for (gas& i : b)
    {
        i.draw();
    }
    glEnd();
    glutSwapBuffers();
}

void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 600, 0, 600);
    glMatrixMode(GL_MODELVIEW);
}


  • 1
    Are you sure its the simulation that takes the most time and not the draw? (Have you profiled your code?) I would think theres a faster way to draw a lot of particles than using that old OpenGL 1.0 API. Secondly, what are your compiler flags? Did you enable optimizations? – Borgleader Jul 04 '22 at 05:25
  • btw, unrelated but for readability purposes you can replace all: `temp = a; a = b; b = temp;` with `std::swap(a,b);` – Borgleader Jul 04 '22 at 05:28
  • 2
    You can start with not processing each pair of points twice. – n. m. could be an AI Jul 04 '22 at 05:58
  • 1
    Next step, check [stackoverflow](https://stackoverflow.com/questions/13046033/an-efficient-way-to-simulate-many-particle-collisions). – n. m. could be an AI Jul 04 '22 at 06:41
  • Is the problem the drawing of the particles or the updating? Is it still slow if you don't check for collusions? – BDL Jul 04 '22 at 07:09
  • You should call `srand` only once. It's usually done early in `main`. Also, do you really want the initial values to be the same on each run? – molbdnilo Jul 04 '22 at 08:09
  • @BDL Updating takes way more time compared to drawing. If there are no collisions, the simulation runs smoothly even when there there are more than 20,000 particles. – Amarnath4040 Jul 04 '22 at 11:05
  • Did you enable compiler optimizations? When using gcc you could use the `-march=native` and `-Ofast` flags. (Or `-Og` when debugging). Also consider moving to a more modern way of using opengl, by using vertex array objects and a matrix manipulation library such as GLM. – Lanting Jul 04 '22 at 11:11
  • @molbdnilo Thanks for the comment. I used srand in a loop with the parameter 'it' coz I need a different value at every iteration. Right now I don't mind about rand() giving same set of values for each run as I have other stuff to fix and add before making a true random generator. – Amarnath4040 Jul 04 '22 at 11:11
  • 1
    You are checking each particle against all other particles to detect collision. That is O(n^2) and can slow down your code with increasing number of particles. (Drawing, etc. also take time, but they scale linearly O(n) with number of particles.) A solution is to have less collision detection, e.g. only compare the particles that you know are close to each other. You can use a data-structure like quad-tree for this purpose. This is a video (which is not very optimized) showing the concept: https://youtu.be/OJxEcs0w_kE Of course you can search for quadtree and find more resources. – KMot Jul 04 '22 at 12:15
  • If you're looking for different, consider [this paper](https://informs-sim.org/wsc05papers/118.pdf) which outlines how to do movement (and sensing) using discrete event simulation modeling. It is more efficient than the time-stepped approach you're using if the expected number of collisions per clock tick is less than the number of particles. It also has the advantage that collisions happen when they happen, rather than being rounded to the nearest time tick ([which can introduce artifacts that can affect the model's accuracy](https://www.informs-sim.org/wsc11papers/218.pdf)). – pjs Jul 06 '22 at 03:03

0 Answers0