From 55ff0f3490eac8d3a5931b86bb5b8d5adbf6a1d5 Mon Sep 17 00:00:00 2001 From: Christoph Urlacher Date: Wed, 18 Feb 2026 23:12:02 +0100 Subject: [PATCH] rebuild the repulsion force grid every n frames --- .clangd | 2 +- compile_commands.json | 2 +- include/config.hpp | 5 +++-- include/mass_springs.hpp | 14 +++++++++++++- src/mass_springs.cpp | 41 ++++++++++++++++++++++++++++------------ 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/.clangd b/.clangd index 602b81e..72fbebe 120000 --- a/.clangd +++ b/.clangd @@ -1 +1 @@ -./cmake-build-release/.clangd \ No newline at end of file +./cmake-build-debug/.clangd \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json index fd9db9d..66636ac 120000 --- a/compile_commands.json +++ b/compile_commands.json @@ -1 +1 @@ -./cmake-build-release/compile_commands.json \ No newline at end of file +./cmake-build-debug/compile_commands.json \ No newline at end of file diff --git a/include/config.hpp b/include/config.hpp index 605e504..c3d9b92 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -27,12 +27,13 @@ constexpr float ROT_SPEED = 1.0; // Physics Engine 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 REST_LENGTH = 1.0; constexpr float REPULSION_FORCE = 0.1; 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 constexpr float VERTEX_SIZE = 0.1; diff --git a/include/mass_springs.hpp b/include/mass_springs.hpp index 81bcab8..adf1803 100644 --- a/include/mass_springs.hpp +++ b/include/mass_springs.hpp @@ -5,6 +5,7 @@ #include #include #include +#include class Mass { public: @@ -83,12 +84,20 @@ public: }; class MassSpringSystem { +private: + std::vector mass_vec; + std::vector indices; + std::vector cell_ids; + int last_build; + int last_masses_count; + int last_springs_count; + public: std::unordered_map masses; std::unordered_map springs; public: - MassSpringSystem() {}; + MassSpringSystem() : last_build(1000) {}; MassSpringSystem(const MassSpringSystem ©) = delete; MassSpringSystem &operator=(const MassSpringSystem ©) = delete; @@ -97,6 +106,9 @@ public: ~MassSpringSystem() {}; +private: + auto BuildGrid() -> void; + public: auto AddMass(float mass, Vector3 position, bool fixed, const std::string &state) -> void; diff --git a/src/mass_springs.cpp b/src/mass_springs.cpp index 0495cb8..babac49 100644 --- a/src/mass_springs.cpp +++ b/src/mass_springs.cpp @@ -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 int n = masses.size(); // Collect pointers - std::vector massVec; - massVec.reserve(n); + mass_vec.clear(); + mass_vec.reserve(n); for (auto &[state, mass] : masses) { - massVec.push_back(&mass); + mass_vec.push_back(&mass); } // Assign each particle a cell index @@ -145,21 +145,38 @@ auto MassSpringSystem::CalculateRepulsionForces() -> void { }; // Sort particles by cell - std::vector indices(n); + indices.clear(); + indices.resize(n); std::iota(indices.begin(), indices.end(), 0); 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 - std::vector cellIDs(n); + cell_ids.clear(); + cell_ids.resize(n); 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 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 cy = (int)std::floor(mass->position.y * 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); // Binary search for this neighbor cell in sorted array - auto lo = std::lower_bound(cellIDs.begin(), cellIDs.end(), nid); - auto hi = std::upper_bound(cellIDs.begin(), cellIDs.end(), nid); + auto lo = std::lower_bound(cell_ids.begin(), cell_ids.end(), nid); + auto hi = std::upper_bound(cell_ids.begin(), cell_ids.end(), nid); 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) { continue; }