This is my attempt at building a particle simulator. I made this to practice applying physics to code and to practice algorithms. But probably most of all this just felt like a fun project to make. My goal is to be able to run the 2D simulation with 100k particles at 60 fps.
I will eventually add a 3D mode to the simulation and GUI portion for cooler stuff.
The UI uses SFML 3.0.1, so make sure you have all the dependencies installed:
sudo apt update
sudo apt install \
libxrandr-dev \
libxcursor-dev \
libxi-dev \
libudev-dev \
libfreetype-dev \
libflac-dev \
libvorbis-dev \
libgl1-mesa-dev \
libegl1-mesa-dev \
libfreetype-dev
sudo dnf update
sudo dnf install \
libXrandr-devel \
libXcursor-devel \
libXi-devel \
systemd-devel \
freetype-devel \
flac-devel \
libvorbis-devel \
mesa-libGL-devel \
mesa-libEGL-devel
sudo pacman -Sy
sudo pacman -S \
libxrandr \
libxcursor \
libxi \
systemd \
freetype2 \
flac \
libvorbis \
mesa
After all dependencies are installed, just configure the project as normal:
cmake -B build . # from project root
cmake .. # from build/
From here you can either build the debug (RPEngineDebug) or release
(RPEngine) executables:
# from project root
make -C build release (configure for release and build)
make -C build debug (configure for debug and build)
# from build/
make release
make debug
FOR WINDOWS USERS: I got it running on my windows system, but I had already gone through getting SFML to work prior. Tbh, I don't remember what I did. That said, I got the simulation running on windows. You're on your own here. Feel free to find out and open a PR making changes to these instructions.
FOR MACOS USERS: I don't have access to a mac, so idk what is needed to get it running. That said, macOS is close to unix so I'd imagine the same commands for getting it running on Ubuntu/Debian would be nearly the same. Feel free to find out and open a PR making changes to these instructions.
Unit tests live in tests/ and cover simulation logic using GoogleTest.
Configure and build the test binary:
cmake -B build . # from project root
cmake --build build --target rp_tests -j
Then run the tests, either directly:
./build/bin/rp_tests
or through ctest, for per-case pass/fail output:
ctest --test-dir build --output-on-failure
Tests are built by default. To skip them (to avoid the GoogleTest fetch/build),
configure with -DBUILD_TESTS=OFF.
In main.cpp, you'll find the initialization of the simulator. Here is an
explanation of each parameter:
Simulator sim(
{0.0f, 0.0f}, // container dimensions
50.0f, // maximum particle radius
0.0f, // magnitude of gravity
0.0f, // magnitude of coefficient of restitution
0.0f, // delta time step for simulation
14000, // max amount of particles allowed in the
// simulator
IntegrationType::Verlet, // integration type
BroadphaseType::SpatialGrid, // broadphase type
);Again, these are the initialization parameters for the Simulator class. The
reason I leave these parameters is because eventually I will enable running the
simulation without the GUI, it will take in particle data and it will stream
particle data out to different files. That will take some time but yeah.
The reason that a bunch of these parameters are set to garbage values in
main.cpp is because the Renderer ends up manipulating them upon
initialization.
- NOTE: only
maxParticleRadiusandmaxParticlesdon't get changed by theRenderer. So you do need to think about these ones.
You can further tune the simulation at runtime by using the panel below:
To apply a force:
-
Select the force you want.
-
Left-click to apply it; the force will be applied for as long as you hold down the left mouse.
To spawn particles:
-
Select the spawning method.
-
If you selected Manual spawn, you right-click to activate it. For all other spawning methods, press <Space> to toggle it on/off.
If you forget these instructions, the Help dropdown in the panel will tell you:
My laptop is an Asus VivoBook with AMD Ryzen 5800HS processor (integrated graphics), 12 GB RAM. Currently
-
w/ Verlet Integration:
- Debug: 14k particles at 60 fps.
- Release: 55k particles at 60 fps. 100k at ~31 fps.
-
w/ Euler Integration:
- Debug: 14k particles at 60 fps.
- Release: 49k particle at 60 fps. 100k at ~28 fps
- look into forward declarations for certain classes that rely on others
- make a callback system for event handling. right now
Renderer::drawFrameis responsible for executing events - see about optimizing
Renderer::drawParticles - rearchitect the codebase (cuz why not)
- make transition between integration types clean. simulation crashes from Euler -> Verlet
- improve
Simulator::radialPushto work with any broadphase - implement hot-reloading for quicker debugging
- add 3D particle simulation
- MAYBE add orbiting
- add multithreading
- add rigidbody mechanics
- MAYBE improve wall collision code by only checking particles along the walls or something
- add some kind of profiler that runs a simulation without UI
- optimize spatial grid broadphase
- do a different broadphase for differently sized particles
- fix particle collision instability; they violate particle bounds A LOT
- fix massive performance degradation in
SpatialGridwhen lots of small particles but large cell size - fix particle rightward drift during
SpatialGridbroadphase when tightly packed
- use ImGui to enable simulation configuration
- figure out why we can't press None radio button in Spawn dropdown when Forces dropdown is also open
- make the panel cleaner looking
- explain all controls in GUI once ImGui controls implemented
- add spawning methods to ImGui
- add forces options
- add magnitude param for
radialPush - make it so that forces activated on mouse click don't get applied when hovering over the ImGui menu
- add radius param for
radialPush - add particle radius field
- add a UI option for toggling between Euler-Impulse and Verlet-Position based collisions
- add a UI option for toggling between broad phase methods for collision detection
- add
gtesttesting suite to ensure physical accuracy - fix the downsizing radius issue
- fix particles exploding when compacted w/ Verlet integration
- change particle drawing to vertex-based
- implement SpatialGrid class and move some stuff out of
Simulator::spatialGridBroadphase() - add debug and release builds
- implement spatial grid broad-phase
- add a Verlet integration based resolver to
Simulator - implement proper resizing
- move UI stuff into its own rendering engine class
- decouple simulation code from UI code
- optimize number capacity of
QuadTreepartition - change implementation
QuadTreeto useAABBinstead of SFMLFloatRect - make custom
AABBstruct independent of SFML - implement working
QuadTree



