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) {