~starkingdoms/starkingdoms

313a1a723ad3f815623d50227db50393b94b8e3e — ghostly_zsh 15 hours ago 30a38bc
feat: thermal radiation + specific heat
M crates/unified/assets/config/parts/chassis.part.toml => crates/unified/assets/config/parts/chassis.part.toml +2 -1
@@ 2,6 2,7 @@
name = "Chassis"
sprite_connected = "textures/chassis.png"
sprite_disconnected = "textures/chassis.png"
emissivity = 1.0

[physics]
width = 50


@@ 27,4 28,4 @@ snap = { translation = [ 0.0, -25.0, 0.0 ], rotation = 0.0 }
[[joint]]
id = "Left"
target = { translation = [ -50.0, 0.0, 0.0 ], rotation = -270.0 }
snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 }
\ No newline at end of file
snap = { translation = [ -25.0, 0.0, 0.0 ], rotation = 0.0 }

M crates/unified/assets/config/parts/hearty.part.toml => crates/unified/assets/config/parts/hearty.part.toml +6 -4
@@ 2,6 2,8 @@
name = "Hearty"
sprite_connected = "textures/hearty.png"
sprite_disconnected = "textures/hearty.png"
emissivity = 0.1
specific_heat = 100.0

[physics]
width = 50


@@ 13,28 15,28 @@ id = "bottom left"
apply_force_at_local = [ -25.0, -25.0 ]
thrust_vector = [ 0.0, 2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1
heat_constant = 10.0

[[thruster]]
id = "bottom right"
apply_force_at_local = [ 25.0, -25.0 ]
thrust_vector = [ 0.0, 2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1
heat_constant = 10.0

[[thruster]]
id = "top left"
apply_force_at_local = [ -25.0, 25.0 ]
thrust_vector = [ 0.0, -2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1
heat_constant = 10.0

[[thruster]]
id = "top right"
apply_force_at_local = [ 25.0, 25.0 ]
thrust_vector = [ 0.0, -2500.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1
heat_constant = 10.0

[[joint]]
id = "Top"

M crates/unified/assets/config/parts/housing.part.toml => crates/unified/assets/config/parts/housing.part.toml +3 -1
@@ 2,6 2,8 @@
name = "Housing"
sprite_connected = "textures/thruster_on.png"
sprite_disconnected = "textures/thruster_off.png"
emissivity = 0.2
specific_heat = 100.0

[physics]
width = 50


@@ 13,7 15,7 @@ id = "main"
apply_force_at_local = [ 0, 0 ]
thrust_vector = [ 0.0, -20000.0 ]
exhaust_temperature = 1000.0
heat_constant = 0.1
heat_constant = 10.0

[[joint]]
id = "Top"

M crates/unified/src/config/part.rs => crates/unified/src/config/part.rs +2 -0
@@ 21,6 21,8 @@ pub struct PartPartConfig {
    pub name: String,
    pub sprite_connected: String,
    pub sprite_disconnected: String,
    pub emissivity: f32,
    pub specific_heat: f32,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct PartPhysicsConfig {

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +5 -0
@@ 124,3 124,8 @@ pub struct Cooler {
    pub cool_temperature: f32,
    pub heat_cooling_constant: f32,
}
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct Radiator {
    pub emissivity: f32,
    pub surface_area: f32,
}

A crates/unified/src/server/heat/conduction.rs => crates/unified/src/server/heat/conduction.rs +5 -0
@@ 0,0 1,5 @@
use bevy::app::App;

pub fn heat_conduction_plugin(app: &mut App) {

}

M crates/unified/src/server/heat/mod.rs => crates/unified/src/server/heat/mod.rs +2 -0
@@ 1,1 1,3 @@
pub mod cooling;
pub mod radiation;
pub mod conduction;

A crates/unified/src/server/heat/radiation.rs => crates/unified/src/server/heat/radiation.rs +44 -0
@@ 0,0 1,44 @@
use crate::{attachment::PartInShip, ecs::{Part, Player, Radiator, Temperature}, prelude::*};

const STEFAN_BOLTZMANN: f32 = 5.670374419E-8;
const T_ENV: f32 = 4.0; // units: Kelvin

pub fn heat_radiation_plugin(app: &mut App) {
    app.add_systems(Update, part_radiation);
}

fn part_radiation(
    time: Res<Time>,
    parts: Query<(&mut Temperature, &Radiator, &Part)>
) {
    for (mut temperature, radiator, part) in parts {
        // time for the implicit euler method
        // e = emissivity, o = stefan-boltzmann constant, m = mass,
        // c = specific heat, A = surface area
        // k = (eoA)/(mc)
        // dT/dt = -k(T^4 - T_env^4)
        // T_env = 200.0 K
        // T_n+1 = T_n + dt*f(T_n+1)
        // T_n+1 = T_n - k*dt*((T_n+1)^4 - T_env^4)
        // g(T_n+1) = T_n+1 + k*dt*(T_n+1)^4 - T_n - k*dt*(T_env)^4
        // g'(T_n+1) = 1 + 4*k*dt*(T_n+1)^3
        // newton's method:
        // x_n+1 = x_n - g(x_n)/g'(x_n)
        // where x_n is the previous guess for T_n+1
        let initial_temp = temperature.0;
        // temporary specific heat (c) of 1000.0
        let k = (radiator.emissivity * STEFAN_BOLTZMANN * radiator.surface_area)
            / (part.strong_config.physics.mass * part.strong_config.part.specific_heat);
        let dt = time.delta_secs();

        // initial guess
        let mut next_temp = initial_temp;
        for _ in 0..3 {
            let g = next_temp + k*dt*next_temp.powi(4) - initial_temp - k*dt*T_ENV;
            let g_prime = 1.0 + 4.0*k*dt*(next_temp).powi(3);

            next_temp = next_temp - g/g_prime;
        }
        temperature.0 = next_temp;
    }
}

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +2 -0
@@ 9,6 9,7 @@ mod system_sets;
use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::gravity::newtonian_gravity_plugin;
use crate::server::heat::cooling::heat_cooling_plugin;
use crate::server::heat::radiation::heat_radiation_plugin;
use crate::server::part::part_management_plugin;
use crate::server::planets::planets_plugin;
use crate::server::player::player_management_plugin;


@@ 51,6 52,7 @@ impl Plugin for ServerPlugin {
        .add_plugins(part_management_plugin)
        .add_plugins(server_thrust_plugin)
        .add_plugins(heat_cooling_plugin)
        .add_plugins(heat_radiation_plugin)
        .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet));
        //.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend));
    }

M crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +6 -1
@@ 1,6 1,6 @@
use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoint};
use crate::config::part::{CoolingConfig, CraftingConfig, JointConfig, PartConfig};
use crate::ecs::{CanCraft, Cooler, Part, PartHandle, Temperature};
use crate::ecs::{CanCraft, Cooler, Part, PartHandle, Radiator, Temperature};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart};


@@ 128,6 128,10 @@ fn calculate_bundle(config: &PartConfig, handle: &Handle<PartConfig>) -> impl Bu
    let collider = Collider::rectangle(config.physics.width, config.physics.height);
    let mass = Mass(config.physics.mass);
    let temperature = Temperature(298.0); // note that this is 25 degrees C
    let radiator = Radiator {
        emissivity: config.part.emissivity,
        surface_area: config.physics.width * config.physics.height,
    };

    (
        part,


@@ 135,6 139,7 @@ fn calculate_bundle(config: &PartConfig, handle: &Handle<PartConfig>) -> impl Bu
        collider,
        mass,
        temperature,
        radiator,
    )
}
fn crafting_bundle(entity: &mut EntityCommands, config: &CraftingConfig) {