wip: draw physics thread ups

This commit is contained in:
2026-02-24 18:15:14 +01:00
parent 90e2dc2186
commit 11617104c5
6 changed files with 52 additions and 21 deletions

View File

@ -29,19 +29,17 @@ constexpr float ROT_SPEED = 1.0;
constexpr float CAMERA_SMOOTH_SPEED = 15.0;
// Physics Engine
constexpr float SIM_SPEED = 1.0; // How large each update should be
constexpr float TIMESTEP = 1.0 / 90; // Do 90 physics updates per second
constexpr float MASS = 1.0; // Mass spring system
constexpr float SPRING_CONSTANT = 5.0; // Mass spring system
constexpr float DAMPENING_CONSTANT = 1.0; // Mass spring system
constexpr float REST_LENGTH = 2.0; // Mass spring system
constexpr float VERLET_DAMPENING = 0.05; // [0, 1]
constexpr float BH_FORCE = 2.0; // BH: [1.0, 3.0]
constexpr float THETA = 1.0; // Barnes-Hut [0.5, 1.0]
constexpr float SOFTENING = 0.01; // Barnes-Hut [0.01, 1.0]
constexpr float GRID_FORCE = 0.02; // Grid: [0.0, ~0.05]
constexpr float REPULSION_RANGE = 5.0 * REST_LENGTH; // Grid
constexpr int REPULSION_GRID_REFRESH = 5; // Grid rebuild freq
constexpr float TARGET_UPS = 90; // How often to update physics
constexpr float TIMESTEP = 1.0 / TARGET_UPS; // Update interval in seconds
constexpr float SIM_SPEED = 4.0; // How large each update should be
constexpr float MASS = 1.0; // Mass spring system
constexpr float SPRING_CONSTANT = 5.0; // Mass spring system
constexpr float DAMPENING_CONSTANT = 1.0; // Mass spring system
constexpr float REST_LENGTH = 2.0; // Mass spring system
constexpr float VERLET_DAMPENING = 0.05; // [0, 1]
constexpr float BH_FORCE = 2.0; // Barnes-Hut [1.0, 3.0]
constexpr float THETA = 0.9; // Barnes-Hut [0.5, 1.0]
constexpr float SOFTENING = 0.01; // Barnes-Hut [0.01, 1.0]
// Graph Drawing
constexpr float VERTEX_SIZE = 0.5;

View File

@ -122,6 +122,7 @@ class ThreadedPhysics {
TracyLockable(std::mutex, data_mtx);
std::condition_variable_any data_ready_cnd;
std::condition_variable_any data_consumed_cnd;
unsigned int ups = 0;
bool data_ready = false;
bool data_consumed = true;
std::vector<Vector3> masses; // Read by renderer

View File

@ -77,7 +77,7 @@ public:
const std::vector<std::pair<std::size_t, std::size_t>> &springs)
-> void;
auto DrawTextures() -> void;
auto DrawTextures(float ups) -> void;
};
#endif

View File

@ -44,6 +44,7 @@ auto main(int argc, char *argv[]) -> int {
OrbitCamera3D camera;
Renderer renderer(camera, state, input);
unsigned int ups;
std::vector<Vector3> masses; // Read from physics
std::vector<std::pair<std::size_t, std::size_t>> springs; // Read from physics
@ -61,6 +62,8 @@ auto main(int argc, char *argv[]) -> int {
{
std::unique_lock<LockableBase(std::mutex)> lock(physics.state.data_mtx);
ups = physics.state.ups;
// Only copy data if any has been produced
if (physics.state.data_ready) {
masses = physics.state.masses;
@ -88,7 +91,7 @@ auto main(int argc, char *argv[]) -> int {
renderer.DrawMassSprings(masses, springs);
renderer.DrawKlotski();
renderer.DrawMenu(masses, springs);
renderer.DrawTextures();
renderer.DrawTextures(ups);
FrameMarkEnd("MainThread");
}

View File

@ -5,8 +5,10 @@
#include <BS_thread_pool.hpp>
#include <algorithm>
#include <cfloat>
#include <chrono>
#include <cstddef>
#include <mutex>
#include <ratio>
#include <raylib.h>
#include <raymath.h>
#include <tracy/Tracy.hpp>
@ -190,8 +192,21 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
[&](const struct ClearGraph &cg) { mass_springs.Clear(); },
};
std::chrono::time_point last = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> accumulator(0);
std::chrono::duration<double> update_accumulator(0);
unsigned int updates = 0;
while (state.running.load()) {
FrameMarkStart("PhysicsThread");
// Time tracking
std::chrono::time_point now = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> deltatime = now - last;
accumulator += deltatime;
update_accumulator += deltatime;
last = now;
// Handle queued commands
{
std::lock_guard<LockableBase(std::mutex)> lock(state.command_mtx);
@ -208,11 +223,15 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
}
// Physics update
// TODO: Respect thread-local deltatime + calculate UPS
mass_springs.ClearForces();
mass_springs.CalculateSpringForces();
mass_springs.CalculateRepulsionForces();
mass_springs.VerletUpdate(TIMESTEP * SIM_SPEED);
if (accumulator.count() > TIMESTEP) {
mass_springs.ClearForces();
mass_springs.CalculateSpringForces();
mass_springs.CalculateRepulsionForces();
mass_springs.VerletUpdate(TIMESTEP * SIM_SPEED);
++updates;
accumulator -= std::chrono::duration<double>(TIMESTEP);
}
// Publish the positions for the renderer (copy)
FrameMarkStart("PhysicsThreadProduceLock");
@ -225,6 +244,13 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
break;
}
if (update_accumulator.count() > 1.0) {
// Update each second
state.ups = updates;
updates = 0;
update_accumulator = std::chrono::duration<double>(0);
}
state.masses.clear();
state.masses.reserve(mass_springs.masses.size());
for (const auto &mass : mass_springs.masses) {

View File

@ -316,7 +316,7 @@ auto Renderer::DrawMenu(
EndTextureMode();
}
auto Renderer::DrawTextures() -> void {
auto Renderer::DrawTextures(float ups) -> void {
BeginDrawing();
DrawTextureRec(menu_target.texture,
Rectangle(0, 0, menu_target.texture.width,
@ -330,6 +330,9 @@ auto Renderer::DrawTextures() -> void {
Rectangle(0, 0, render_target.texture.width,
-1 * render_target.texture.height),
Vector2(GetScreenWidth() / 2.0, MENU_HEIGHT), WHITE);
DrawFPS(GetScreenWidth() / 2 + 10, MENU_HEIGHT + 10);
DrawText(TextFormat("%.0f UPS", ups), GetScreenWidth() / 2 + 120,
MENU_HEIGHT + 10, 20, ORANGE);
EndDrawing();
}