20 if (threadRunning.exchange(
true))
return;
21 physicsThread = std::thread(&PhysicsSystem::physicsLoop,
this);
25 threadRunning =
false;
30 if (physicsThread.joinable())
35 std::lock_guard<std::mutex> lock(bodiesMutex);
38 if (body->getID() == id)
return body;
44 if (!snapshotReady.load(std::memory_order_acquire))
47 std::lock_guard<std::mutex> lk(snapshotMutex);
49 if (currentSnapshots.empty()) {
53 if (previousSnapshots.empty() || previousSnapshots.size() != currentSnapshots.size()) {
54 return currentSnapshots;
57 float t0 = previousSnapshots[0].time;
58 float t1 = currentSnapshots[0].time;
60 if (!std::isfinite(t0) || !std::isfinite(t1) || t1 <= t0) {
61 return currentSnapshots;
64 if (renderSimTime <= t0) {
65 return previousSnapshots;
67 if (renderSimTime >= t1) {
68 return currentSnapshots;
71 float alpha = (renderSimTime - t0) / (t1 - t0);
73 std::vector<ObjectSnapshot> out;
74 out.reserve(currentSnapshots.size());
75 for (
size_t i = 0; i < currentSnapshots.size(); ++i) {
76 const auto &A = previousSnapshots[i];
77 const auto &B = currentSnapshots[i];
79 if (A.body != B.body) {
86 C.
time = renderSimTime;
87 C.
position = glm::mix(A.position, B.position, alpha);
88 C.
velocity = glm::mix(A.velocity, B.velocity, alpha);
89 C.
temperature = glm::mix(A.temperature, B.temperature, alpha);
97void Physics::PhysicsSystem::physicsLoop() {
98 constexpr float kBaseDt = 1.0f / 1000.0f;
99 constexpr int kMaxCatchUpSteps = 8;
100 constexpr float kMaxAdaptiveDt = 86400.0f;
102 float accumulator = 0.0f;
103 auto lastTime = std::chrono::high_resolution_clock::now();
104 while (threadRunning.load()) {
105 auto now = std::chrono::high_resolution_clock::now();
106 float frameTime = std::chrono::duration<float>(now - lastTime).count();
109 if (!physicsEnabled.load()) {
110 std::this_thread::sleep_for(std::chrono::milliseconds(1));
112 lastTime = std::chrono::high_resolution_clock::now();
116 accumulator += frameTime * getSimSpeed();
118 std::vector<ObjectSnapshot> localSnaps;
120 std::lock_guard<std::mutex> lock(bodiesMutex);
121 if (accumulator >= kBaseDt) {
122 const int neededSteps =
static_cast<int>(std::ceil(accumulator / kBaseDt));
123 const int stepsToRun = std::max(1, std::min(neededSteps, kMaxCatchUpSteps));
124 const float dt = std::min(std::max(accumulator /
static_cast<float>(stepsToRun), kBaseDt), kMaxAdaptiveDt);
126 for (
int i = 0; i < stepsToRun && accumulator >= kBaseDt; ++i) {
131 accumulator -= std::min(dt, accumulator);
134 if (dt >= kMaxAdaptiveDt && accumulator >= kMaxAdaptiveDt * kMaxCatchUpSteps) {
139 localSnaps.reserve(bodies.size());
140 for (
auto* body : bodies) {
144 std::lock_guard<std::mutex> lk(snapshotMutex);
145 previousSnapshots = std::move(currentSnapshots);
146 currentSnapshots = std::move(localSnaps);
147 snapshotReady.store(
true, std::memory_order_release);
153 std::lock_guard<std::mutex> lock(bodiesMutex);
156 bodies.push_back(body);
160 std::lock_guard<std::mutex> lock(bodiesMutex);
161 auto it = std::remove(bodies.begin(), bodies.end(), body);
162 if (it != bodies.end()) {
163 bodies.erase(it, bodies.end());
164 resetState.erase(body);
166 std::lock_guard<std::mutex> snapshotLock(snapshotMutex);
167 auto removeSnapshotForBody = [body](std::vector<ObjectSnapshot>& snapshots) {
169 std::remove_if(snapshots.begin(), snapshots.end(), [body](
const ObjectSnapshot& snapshot) {
170 return snapshot.body == body;
175 removeSnapshotForBody(currentSnapshots);
176 removeSnapshotForBody(previousSnapshots);
177 if (currentSnapshots.empty() && previousSnapshots.empty()) {
178 snapshotReady.store(
false, std::memory_order_relaxed);
182 std::cerr <<
"[PhysicsSystem] Warning: Tried to remove a body not in the system.\n";
186void Physics::PhysicsSystem::advancePhysics(
float dt) {
187 float targetTime = simTime + dt;
188 std::vector<PhysicsBody*> collidableBodies;
190 PhysicsSystem::octree.build(bodies);
191 for (
auto body : bodies) {
192 std::unique_lock<std::mutex> guard = body->lockState();
193 if (body->getCollider() !=
nullptr) {
194 collidableBodies.push_back(body);
197 glm::vec3 nBodyGravity = PhysicsSystem::octree.computeForce(body, getGravitationalConstant());
198 glm::vec3 globalGravity =
static_cast<float>(body->getMass(
BodyLock::NOLOCK)) * getGlobalAcceleration();
199 glm::vec3 totalGravity = nBodyGravity + globalGravity;
204 if (simTime == 0.0f) {
207 if (resetState.find(body) == resetState.end()) {
208 body->withFrames(
BodyLock::NOLOCK, [
this, body](
const std::vector<ObjectSnapshot>& fr) {
209 if (!fr.empty()) resetState[body] = fr.front();
215 const double area = body->getSurfaceArea();
217 const double ambientTemp = getAmbientTemperature();
218 const double proximityRadiation = PhysicsSystem::octree.computeHeat(body);
229 if (std::isfinite(props.
tempK)) {
241 bvh.build(collidableBodies);
242 for (
const auto[a, b] : bvh.getPotentialCollisions()) {
246 if (a->collidesWith(*b)) {
247 a->resolveCollisionWith(dt, *b);
252 simTime = targetTime;
256 if (solver && solver->stepFrame()) {
257 std::cout <<
"Solver Converged!" << std::endl;
259 float finalDuration = this->simTime - (dt * 0.5f);
261 for (
auto body : bodies) {
262 body->withFrames(
BodyLock::LOCK, [
this, body](
const std::vector<ObjectSnapshot>& frames) {
263 if (!frames.empty()) {
265 resetState[body] = frames.front();
271 float bakeTimer = 0.0f;
273 while (bakeTimer < finalDuration) {
279 physicsEnabled =
false;
288 solver = router.makeSolver(body, knowns, unknown);
291 std::cout <<
"Solver Started: " << unknown << std::endl;
292 physicsEnabled =
true;
294 std::cerr <<
"Solver Error: No recipe found for " << unknown << std::endl;
301 for (
auto [body, initialState] : resetState) {
308 std::lock_guard<std::mutex> bodiesLock(bodiesMutex);
314 std::lock_guard<std::mutex> snapshotLock(snapshotMutex);
315 currentSnapshots.clear();
316 previousSnapshots.clear();
318 snapshotReady.store(
false, std::memory_order_relaxed);
322 physicsEnabled.store(
true);
323 snapshotReady.store(
false, std::memory_order_relaxed);
327 physicsEnabled.store(
false);
Physics::PhysicsBody * body
virtual double getMass(BodyLock lock) const
void setForce(const std::string &name, const glm::vec3 &force, BodyLock lock)
std::optional< std::vector< ObjectSnapshot > > fetchLatestSnapshot(float renderSimTime)
void solveProblem(PhysicsBody *body, const std::unordered_map< std::string, double > &knowns, const std::string &unknown="")
void addBody(PhysicsBody *body)
void removeBody(PhysicsBody *body)
PhysicsBody * getBodyById(uint32_t id) const
PhysicsSystem(const glm::vec3 &globalAccel=glm::vec3(0.0f, -Constants::STANDARD_GRAVITY, 0.0f))
double convectionHeatRate(const ThermalProperties &props, double areaM2, double ambientTempK)
void integrateTemperature(ThermalProperties &props, double massKg, double dt, HeatRateFn &&heatRateAtTemp)
double externalHeatFluxRate(const ThermalProperties &props, double areaM2)
double ambientRadiationHeatRate(const ThermalProperties &props, double areaM2, double ambientTempK)