Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions app/test_scenarios.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,11 +578,53 @@ namespace
world.addBody(box);
}

Camera spawn_heat_transfer_demo(PhysicsWorld &world)
{
world.thermal_settings.enabled = true;
world.thermal_settings.conduction_rate = 15.0f;
world.thermal_settings.radiation_rate = 0.02f;
world.thermal_settings.ambient_temperature = 295.0f;
world.thermal_settings.ambient_coupling = 0.03f;
world.thermal_settings.radiation_distance = 4.0f;
world.thermal_settings.min_visual_temperature = 240.0f;
world.thermal_settings.max_visual_temperature = 660.0f;
world.thermal_spawn_controls.enabled = true;
world.thermal_spawn_controls.lock_to_basic_shapes = true;
world.thermal_spawn_controls.spawn_temperature = 295.0f;
world.thermal_spawn_controls.spawn_heat_capacity = 930.0f;
world.thermal_spawn_controls.spawn_conductivity = 0.7f;
world.thermal_spawn_controls.spawn_emissivity = 0.9f;

const int boxCount = 9;
const float spacing = 1.0f;
const float startX = -0.5f * spacing * (boxCount - 1);
const float coldTemp = 255.0f;
const float hotTemp = 650.0f;

for (int i = 0; i < boxCount; ++i)
{
float lerp = (boxCount == 1) ? 0.0f : static_cast<float>(i) / static_cast<float>(boxCount - 1);
float temp = coldTemp + lerp * (hotTemp - coldTemp);
Vec3 pos(startX + i * spacing, 0.55f, 0.0f);
Rigidbody body(pos, Vec3(), &g_small_box, 1.8f);
body.thermal_enabled = true;
body.temperature = temp;
body.heat_capacity = 930.0f;
body.thermal_conductivity = 0.75f;
body.thermal_emissivity = 0.88f;
world.addBody(body);
}

return Camera().setPosition(glm::vec3(0.0f, 4.8f, 22.0f));
}

}

Camera LoadSingleTestScenario(PhysicsWorld &world, TestCase test_case)
{
world.enable_buoyancy = false; // only when boyancy testcase
world.thermal_settings = PhysicsWorld::ThermalSettings();
world.thermal_spawn_controls = PhysicsWorld::ThermalSpawnControls();

add_floor(world);

Expand Down Expand Up @@ -628,6 +670,8 @@ Camera LoadSingleTestScenario(PhysicsWorld &world, TestCase test_case)
world.addBody(Rigidbody(Vec3(0.0f, 5.0f, 0.0f), Vec3(), &g_small_sphere, 0.5f));
world.addBody(Rigidbody(Vec3(3.0f, 5.0f, 0.0f), Vec3(), &g_small_box, 0.6f));
return Camera();
case TestCase::HeatTransferDemo:
return spawn_heat_transfer_demo(world);
default:
return spawn_projectile_demo(world);
break;
Expand Down
3 changes: 2 additions & 1 deletion app/test_scenarios.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ enum class TestCase
AngularStack,
CornerCollision,
RollingFriction,
BuoyancyTest
BuoyancyTest,
HeatTransferDemo
};

Camera LoadSingleTestScenario(PhysicsWorld &world, TestCase test_case);
24 changes: 24 additions & 0 deletions engine/core/rigidbody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,27 @@ void Rigidbody::clearAccum()
force_accum = Vec3();
acctork = Vec3();
}

float Rigidbody::getMass() const
{
if (inverse_mass <= 0.0f)
{
return 0.0f;
}
return 1.0f / inverse_mass;
}

float Rigidbody::getThermalMass() const
{
if (!thermal_enabled)
{
return 0.0f;
}
float mass = getMass();
if (mass <= 0.0f)
{
return 0.0f;
}
float cappedHeatCapacity = std::max(heat_capacity, PHYSICS_EPSILON);
return mass * cappedHeatCapacity;
}
7 changes: 7 additions & 0 deletions engine/core/rigidbody.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class Rigidbody {
Vec3 acctork;
Mat3 inverse_inertia_body; //for local
Mat3 inverse_inertia_world; //for world
bool thermal_enabled = false;
float temperature = 293.15f;
float heat_capacity = 900.0f;
float thermal_conductivity = 0.5f;
float thermal_emissivity = 0.85f;

/*
- Collider is a pointer cuz if we just write "Collider collider;" then the collider will always be a generic one
Expand All @@ -46,4 +51,6 @@ class Rigidbody {
void clearForces();
void clearAccum();
void updateworldinvinertia();
float getMass() const;
float getThermalMass() const;
};
109 changes: 109 additions & 0 deletions engine/world/physicsworld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>
#include <cfloat>
#include <algorithm>
#include <cmath>
#include "core/buoyancy.hpp"
PhysicsWorld::PhysicsWorld() : gravity(0.0f, PHYSICS_GRAVITY, 0.0f), next_body_id(1) {}

Expand Down Expand Up @@ -308,6 +309,11 @@ void PhysicsWorld::step(float dt)
body.velocity = Vec3();
}
}

if (thermal_settings.enabled)
{
solve_thermal(dt);
}
}

void PhysicsWorld::generate_manifolds()
Expand Down Expand Up @@ -843,4 +849,107 @@ void PhysicsWorld::warm_start_constraints()
c.a->angvel -= c.a->inverse_inertia_world * rA.cross(impulse);
c.b->angvel += c.b->inverse_inertia_world * rB.cross(impulse);
}
}

void PhysicsWorld::solve_thermal(float dt)
{
applyThermalConduction(dt);
applyThermalRadiation(dt);
applyAmbientCooling(dt);
}

void PhysicsWorld::applyHeat(Rigidbody &body, float energy)
{
if (!thermal_settings.enabled || !body.thermal_enabled)
{
return;
}
float thermalMass = body.getThermalMass();
if (thermalMass <= PHYSICS_EPSILON)
{
return;
}
body.temperature += energy / thermalMass;
}

void PhysicsWorld::applyThermalConduction(float dt)
{
if (!thermal_settings.enabled || thermal_settings.conduction_rate <= 0.0f)
{
return;
}
for (auto &m : manifolds)
{
if (!m.a || !m.b)
continue;
Rigidbody &a = *m.a;
Rigidbody &b = *m.b;
if (!a.thermal_enabled || !b.thermal_enabled)
continue;
float deltaT = b.temperature - a.temperature;
if (std::abs(deltaT) <= PHYSICS_EPSILON)
continue;
int contactCount = std::max(1, m.contact_count);
float conductivity = (a.thermal_conductivity + b.thermal_conductivity) * 0.5f;
float energy = deltaT * conductivity * thermal_settings.conduction_rate * dt * contactCount;
applyHeat(a, energy);
applyHeat(b, -energy);
}
}

void PhysicsWorld::applyThermalRadiation(float dt)
{
if (!thermal_settings.enabled || thermal_settings.radiation_rate <= 0.0f || thermal_settings.radiation_distance <= PHYSICS_EPSILON)
{
return;
}
const float maxDist = thermal_settings.radiation_distance;
const float maxDistSq = maxDist * maxDist;
for (std::size_t i = 0; i < bodies.size(); ++i)
{
Rigidbody &a = bodies[i];
if (!a.thermal_enabled)
continue;
for (std::size_t j = i + 1; j < bodies.size(); ++j)
{
Rigidbody &b = bodies[j];
if (!b.thermal_enabled)
continue;
Vec3 delta = b.position - a.position;
float distSq = delta.dot(delta);
if (distSq <= PHYSICS_EPSILON || distSq > maxDistSq)
continue;
float dist = std::sqrt(distSq);
float falloff = 1.0f - (dist / maxDist);
if (falloff <= 0.0f)
continue;
float deltaT = b.temperature - a.temperature;
if (std::abs(deltaT) <= PHYSICS_EPSILON)
continue;
float emissivity = (a.thermal_emissivity + b.thermal_emissivity) * 0.5f;
float energy = deltaT * emissivity * thermal_settings.radiation_rate * falloff * dt;
applyHeat(a, energy);
applyHeat(b, -energy);
}
}
}

void PhysicsWorld::applyAmbientCooling(float dt)
{
if (!thermal_settings.enabled || thermal_settings.ambient_coupling <= 0.0f)
{
return;
}
for (auto &body : bodies)
{
if (!body.thermal_enabled)
continue;
float deltaT = thermal_settings.ambient_temperature - body.temperature;
if (std::abs(deltaT) <= PHYSICS_EPSILON)
continue;
float inertia = std::max(1.0f, body.getThermalMass());
float massFactor = 1.0f / (1.0f + 0.01f * inertia);
float energy = deltaT * thermal_settings.ambient_coupling * dt * inertia * massFactor;
applyHeat(body, energy);
}
}
27 changes: 27 additions & 0 deletions engine/world/physicsworld.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,36 @@ class PhysicsWorld {
std::vector<DistanceConstraint> constraints;
Vec3 gravity;
BodyID next_body_id;
void solve_thermal(float dt);
void applyHeat(Rigidbody &body, float energy);
void applyThermalConduction(float dt);
void applyThermalRadiation(float dt);
void applyAmbientCooling(float dt);
public:
bool enable_buoyancy = false;
Fluid water_fluid = Fluid(2.0f, 2.0f, 0.3f);
struct ThermalSettings
{
bool enabled = false;
float conduction_rate = 1.0f;
float radiation_rate = 0.02f;
float ambient_temperature = 295.0f;
float ambient_coupling = 0.05f;
float radiation_distance = 3.5f;
float min_visual_temperature = 250.0f;
float max_visual_temperature = 650.0f;
};
struct ThermalSpawnControls
{
bool enabled = false;
bool lock_to_basic_shapes = false;
float spawn_temperature = 320.0f;
float spawn_heat_capacity = 900.0f;
float spawn_conductivity = 0.6f;
float spawn_emissivity = 0.85f;
};
ThermalSettings thermal_settings;
ThermalSpawnControls thermal_spawn_controls;

PhysicsWorld();
Rigidbody* getBodyByID(uint32_t body_id);
Expand Down
Loading
Loading