rebuild the repulsion force grid every n frames

This commit is contained in:
2026-02-18 23:12:02 +01:00
parent 1dad350f7d
commit 55ff0f3490
5 changed files with 47 additions and 17 deletions

View File

@ -1 +1 @@
./cmake-build-release/.clangd ./cmake-build-debug/.clangd

View File

@ -1 +1 @@
./cmake-build-release/compile_commands.json ./cmake-build-debug/compile_commands.json

View File

@ -27,12 +27,13 @@ constexpr float ROT_SPEED = 1.0;
// Physics Engine // Physics Engine
constexpr float MASS = 1.0; constexpr float MASS = 1.0;
constexpr float SPRING_CONSTANT = 1.0; constexpr float SPRING_CONSTANT = 1.5;
constexpr float DAMPENING_CONSTANT = 0.8; constexpr float DAMPENING_CONSTANT = 0.8;
constexpr float REST_LENGTH = 1.0; constexpr float REST_LENGTH = 1.0;
constexpr float REPULSION_FORCE = 0.1; constexpr float REPULSION_FORCE = 0.1;
constexpr float REPULSION_RANGE = 3.0 * REST_LENGTH; constexpr float REPULSION_RANGE = 3.0 * REST_LENGTH;
constexpr float VERLET_DAMPENING = 0.02; // [0, 1] constexpr int REPULSION_GRID_REFRESH = 5; // Frames between grid rebuilds
constexpr float VERLET_DAMPENING = 0.01; // [0, 1]
// Graph Drawing // Graph Drawing
constexpr float VERTEX_SIZE = 0.1; constexpr float VERTEX_SIZE = 0.1;

View File

@ -5,6 +5,7 @@
#include <raymath.h> #include <raymath.h>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector>
class Mass { class Mass {
public: public:
@ -83,12 +84,20 @@ public:
}; };
class MassSpringSystem { class MassSpringSystem {
private:
std::vector<Mass *> mass_vec;
std::vector<int> indices;
std::vector<int64_t> cell_ids;
int last_build;
int last_masses_count;
int last_springs_count;
public: public:
std::unordered_map<std::string, Mass> masses; std::unordered_map<std::string, Mass> masses;
std::unordered_map<std::string, Spring> springs; std::unordered_map<std::string, Spring> springs;
public: public:
MassSpringSystem() {}; MassSpringSystem() : last_build(1000) {};
MassSpringSystem(const MassSpringSystem &copy) = delete; MassSpringSystem(const MassSpringSystem &copy) = delete;
MassSpringSystem &operator=(const MassSpringSystem &copy) = delete; MassSpringSystem &operator=(const MassSpringSystem &copy) = delete;
@ -97,6 +106,9 @@ public:
~MassSpringSystem() {}; ~MassSpringSystem() {};
private:
auto BuildGrid() -> void;
public: public:
auto AddMass(float mass, Vector3 position, bool fixed, auto AddMass(float mass, Vector3 position, bool fixed,
const std::string &state) -> void; const std::string &state) -> void;

View File

@ -123,15 +123,15 @@ auto MassSpringSystem::CalculateSpringForces() -> void {
} }
} }
auto MassSpringSystem::CalculateRepulsionForces() -> void { auto MassSpringSystem::BuildGrid() -> void {
const float INV_CELL = 1.0f / REPULSION_RANGE; const float INV_CELL = 1.0f / REPULSION_RANGE;
const int n = masses.size(); const int n = masses.size();
// Collect pointers // Collect pointers
std::vector<Mass *> massVec; mass_vec.clear();
massVec.reserve(n); mass_vec.reserve(n);
for (auto &[state, mass] : masses) { for (auto &[state, mass] : masses) {
massVec.push_back(&mass); mass_vec.push_back(&mass);
} }
// Assign each particle a cell index // Assign each particle a cell index
@ -145,21 +145,38 @@ auto MassSpringSystem::CalculateRepulsionForces() -> void {
}; };
// Sort particles by cell // Sort particles by cell
std::vector<int> indices(n); indices.clear();
indices.resize(n);
std::iota(indices.begin(), indices.end(), 0); std::iota(indices.begin(), indices.end(), 0);
std::sort(indices.begin(), indices.end(), [&](int a, int b) { std::sort(indices.begin(), indices.end(), [&](int a, int b) {
return cellID(massVec[a]->position) < cellID(massVec[b]->position); return cellID(mass_vec[a]->position) < cellID(mass_vec[b]->position);
}); });
// Build cell start/end table // Build cell start/end table
std::vector<int64_t> cellIDs(n); cell_ids.clear();
cell_ids.resize(n);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
cellIDs[i] = cellID(massVec[indices[i]]->position); cell_ids[i] = cellID(mass_vec[indices[i]]->position);
} }
}
auto MassSpringSystem::CalculateRepulsionForces() -> void {
const float INV_CELL = 1.0f / REPULSION_RANGE;
const int n = masses.size();
if (last_build >= REPULSION_GRID_REFRESH ||
masses.size() != last_masses_count ||
springs.size() != last_springs_count) {
BuildGrid();
last_build = 0;
last_masses_count = masses.size();
last_springs_count = springs.size();
}
last_build++;
#pragma omp parallel for #pragma omp parallel for
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
Mass *mass = massVec[indices[i]]; Mass *mass = mass_vec[indices[i]];
int cx = (int)std::floor(mass->position.x * INV_CELL); int cx = (int)std::floor(mass->position.x * INV_CELL);
int cy = (int)std::floor(mass->position.y * INV_CELL); int cy = (int)std::floor(mass->position.y * INV_CELL);
int cz = (int)std::floor(mass->position.z * INV_CELL); int cz = (int)std::floor(mass->position.z * INV_CELL);
@ -174,11 +191,11 @@ auto MassSpringSystem::CalculateRepulsionForces() -> void {
(int64_t)((cz + dz) & 0xFFFFF); (int64_t)((cz + dz) & 0xFFFFF);
// Binary search for this neighbor cell in sorted array // Binary search for this neighbor cell in sorted array
auto lo = std::lower_bound(cellIDs.begin(), cellIDs.end(), nid); auto lo = std::lower_bound(cell_ids.begin(), cell_ids.end(), nid);
auto hi = std::upper_bound(cellIDs.begin(), cellIDs.end(), nid); auto hi = std::upper_bound(cell_ids.begin(), cell_ids.end(), nid);
for (auto it = lo; it != hi; ++it) { for (auto it = lo; it != hi; ++it) {
Mass *m = massVec[indices[it - cellIDs.begin()]]; Mass *m = mass_vec[indices[it - cell_ids.begin()]];
if (m == mass) { if (m == mass) {
continue; continue;
} }