Skip to content

Commit

Permalink
Flesh out shade mechanics (Leafwing-Studios#846)
Browse files Browse the repository at this point in the history
* Fix starting position of sun

* Account for footprint

* Ghosts should not cast shade!

* i before e except after c

* Fix height computations

* Cast shade from terrain

* Correct for structure height

* Fix ghost shadow behavior

* Store height of structures in StructureData and use for shadows

* Leave TODO about using this data for picking

* Don't shade self

* Account for facing of structures

* Show height of structures in selection details

* Don't double count shadow from tiles with structures

* Swap to a discretized light system

* Play with colors

* Clippy
  • Loading branch information
alice-i-cecile authored May 6, 2023
1 parent 6b5ddca commit 6bf39ce
Show file tree
Hide file tree
Showing 17 changed files with 272 additions and 280 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"conditions": {
"workers_required": 0,
"allowable_light_range": {
"min": 0.2,
"max": 0.6
"min": "DimlyLit",
"max": "BrightlyLit"
}
},
"energy": 20.0
Expand All @@ -46,8 +46,8 @@
"conditions": {
"workers_required": 0,
"allowable_light_range": {
"min": 0.4,
"max": 1.0
"min": "DimlyLit",
"max": "BrightlyLit"
}
},
"energy": 20.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
}
},
"max_workers": 6,
"height": 1,
"passable": false
},
"path": {
Expand All @@ -26,6 +27,7 @@
}
},
"max_workers": 1,
"height": 0,
"passable": true
},
"acacia_seedling": {
Expand Down Expand Up @@ -64,6 +66,7 @@
}
}
},
"height": 0,
"max_workers": 1,
"passable": false
},
Expand Down Expand Up @@ -95,6 +98,7 @@
}
}
},
"height": 1,
"max_workers": 6,
"passable": false
},
Expand Down Expand Up @@ -133,6 +137,7 @@
"construction_strategy": {
"Seedling": "acacia_seedling"
},
"height": 1,
"max_workers": 1,
"passable": false
},
Expand Down Expand Up @@ -164,6 +169,7 @@
"max_depth": 3,
"radius": 2
},
"height": 3,
"max_workers": 6,
"passable": false
},
Expand All @@ -181,6 +187,7 @@
},
"max_workers": 3,
"passable": false,
"height": 2,
"footprint": {
"set": [
{
Expand Down Expand Up @@ -233,6 +240,7 @@
"construction_strategy": "Landmark",
"max_workers": 0,
"passable": false,
"height": 1,
"footprint": {
"set": [
{
Expand Down
16 changes: 7 additions & 9 deletions emergence_lib/src/crafting/recipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::asset_management::manifest::{Id, Manifest};
use crate::items::item_manifest::{Item, ItemManifest};
use crate::items::{inventory::Inventory, ItemCount};
use crate::light::shade::ReceivedLight;
use crate::light::Illuminance;
use crate::{
crafting::components::{InputInventory, OutputInventory},
light::NormalizedIlluminance,
organisms::energy::Energy,
};
use bevy::reflect::{FromReflect, Reflect, TypeUuid};
Expand Down Expand Up @@ -300,7 +300,7 @@ pub struct RecipeConditions {
/// The number of workers required to advance this recipe.
pub workers_required: u8,
/// The range of light levels that are acceptable for this recipe.
pub allowable_light_range: Option<Threshold<NormalizedIlluminance>>,
pub allowable_light_range: Option<Threshold<Illuminance>>,
}

impl Display for RecipeConditions {
Expand All @@ -323,10 +323,7 @@ impl RecipeConditions {
};

/// Creates a new [`RecipeConditions`].
pub const fn new(
workers_required: u8,
allowable_light_range: Threshold<NormalizedIlluminance>,
) -> Self {
pub const fn new(workers_required: u8, allowable_light_range: Threshold<Illuminance>) -> Self {
Self {
workers_required,
allowable_light_range: Some(allowable_light_range),
Expand All @@ -336,9 +333,10 @@ impl RecipeConditions {
/// Are the conditions to craft this recipe met?
fn satisfied(&self, workers: u8, received_light: &ReceivedLight) -> bool {
let work_satisfied = self.workers_required == 0 || workers >= self.workers_required;
let light_satisfied = self.allowable_light_range.as_ref().map_or(true, |range| {
range.contains(received_light.normalized_illuminance)
});
let light_satisfied = self
.allowable_light_range
.as_ref()
.map_or(true, |range| range.contains(received_light.0));

work_satisfied && light_satisfied
}
Expand Down
72 changes: 6 additions & 66 deletions emergence_lib/src/graphics/lighting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use bevy::prelude::*;

use crate::{
graphics::palette::lighting::{LIGHT_MOON, LIGHT_STARS, LIGHT_SUN},
light::Illuminance,
simulation::geometry::Height,
};

Expand All @@ -19,10 +18,6 @@ impl Plugin for LightingPlugin {
brightness: 0.2,
color: LIGHT_STARS,
})
// Controls the resolution of shadows cast by the sun
// FIXME: shadows are blocked on better rendering performance.
// Tracked in https://github.com/Leafwing-Studios/Emergence/issues/726
//.insert_resource(DirectionalLightShadowMap { size: 8192 })
// Need to wait for the player camera to spawn
.add_startup_system(spawn_celestial_bodies.in_base_set(StartupSet::PostStartup))
.add_systems((animate_celestial_body_transform,));
Expand All @@ -47,71 +42,19 @@ pub(crate) struct CelestialBody {
///
/// A value of 0.0 corresponds to east -> west travel.
travel_axis: f32,
/// The base illuminance of the [`DirectionalLight`]
illuminance: f32,
/// The total effect of temporary modifiers to the [`DirectionalLight`]'s illuminance.
///
/// This defaults to 1.0, and is multiplied by the base illuminance.
light_level: f32,
}

impl CelestialBody {
/// The default angle that the sun is offset from the zenith in radians.
const DEFAULT_NOON_RADIANS: f32 = 23.5 / 360.;

/// Sets the `light_level` of this celestial body.
pub(crate) fn set_light_level(&mut self, light_level: f32) {
self.light_level = light_level;
}

/// Computes the total irradiance produced by this celestial body based on its position in the sky.
pub(crate) fn compute_light(&self) -> Illuminance {
CelestialBody::compute_illuminance(
self.light_level,
self.hour_angle,
self.declination,
self.illuminance,
)
}

/// Computes the maximum total irradiance produced by this celestial body based on its brightest possible position in the sky.
///
/// This is used to determine the maximum brightness of the directional light.
pub(crate) fn compute_max_light(&self) -> Illuminance {
CelestialBody::compute_illuminance(
1.0,
CelestialBody::DEFAULT_NOON_RADIANS,
self.declination,
self.illuminance,
)
}

/// Computes the total irradiance produced by a celestial body given `hour_angle`, `declination`, and `illuminance`.
fn compute_illuminance(
light_level: f32,
hour_angle: f32,
declination: f32,
illuminance: f32,
) -> Illuminance {
// Computes the total angle formed by the celestial body and the horizon
//
// We cannot simply use the progress, as the inclination also needs to be taken into account.
// See https://en.wikipedia.org/wiki/Solar_zenith_angle
// We're treating the latitude here as equatorial.
let cos_solar_zenith_angle = hour_angle.cos() * declination.cos();
let solar_zenith_angle = cos_solar_zenith_angle.acos();
Illuminance(light_level * illuminance * solar_zenith_angle.cos().max(0.))
}
const HOUR_ANGLE_AT_NOON: f32 = 23.5 / 360.;

/// The starting settings for the sun
fn sun() -> CelestialBody {
CelestialBody {
height: 2. * Height::MAX.into_world_pos(),
hour_angle: -PI / 4.,
declination: CelestialBody::DEFAULT_NOON_RADIANS * PI / 2.,
hour_angle: CelestialBody::HOUR_ANGLE_AT_NOON,
declination: -PI / 4.,
travel_axis: 0.,
illuminance: 8e4,
light_level: 1.0,
}
}

Expand All @@ -120,10 +63,8 @@ impl CelestialBody {
CelestialBody {
height: 2. * Height::MAX.into_world_pos(),
hour_angle: 0.,
declination: CelestialBody::DEFAULT_NOON_RADIANS * PI / 2.,
declination: CelestialBody::HOUR_ANGLE_AT_NOON,
travel_axis: PI / 6.,
illuminance: 3e4,
light_level: 1.0,
}
}
}
Expand All @@ -137,14 +78,13 @@ pub(crate) struct Sun;
pub(crate) struct Moon;

/// Spawns a directional light source to illuminate the scene
#[allow(dead_code)]
fn spawn_celestial_bodies(mut commands: Commands) {
let sun = CelestialBody::sun();
commands
.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
color: LIGHT_SUN,
illuminance: sun.illuminance,
illuminance: 8e4,
shadows_enabled: false,
..Default::default()
},
Expand All @@ -158,7 +98,7 @@ fn spawn_celestial_bodies(mut commands: Commands) {
.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
color: LIGHT_MOON,
illuminance: moon.illuminance,
illuminance: 3e4,
shadows_enabled: false,
..Default::default()
},
Expand Down
24 changes: 17 additions & 7 deletions emergence_lib/src/graphics/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
pub(crate) mod infovis {
use bevy::prelude::Color;

use crate::signals::SignalKind;
use crate::{light::Illuminance, signals::SignalKind};

/// The alpha value used for selection/hovering/other UI overlay
pub(crate) const OVERLAY_ALPHA: f32 = 0.5;

/// The alpha value used for discretized overlays.
///
/// This is more opaque to ensure that the number of gradations is clear.
pub(crate) const DISCRETE_OVERLAY_ALPHA: f32 = 0.8;

/// The hue of selected objects
pub(crate) const SELECTION_HUE: f32 = 100.;
/// The saturation of selected objects
Expand Down Expand Up @@ -148,12 +153,17 @@ pub(crate) mod infovis {
/// The color used to indicate that water is near the surface.
pub(crate) const WATER_TABLE_COLOR_LOW: Color = Color::hsla(195., 0.7, 0.2, OVERLAY_ALPHA);

/// The color used to indicate that it is dark.
pub(crate) const LIGHT_LEVEL_COLOR_LOW: Color = Color::hsla(232., 0.68, 0.4, OVERLAY_ALPHA);
/// The color used to indicate that it is bright.
// This is very hard to see at low alpha, so we use a higher alpha here.
pub(crate) const LIGHT_LEVEL_COLOR_HIGH: Color =
Color::hsla(52., 0.7, 0.95, (1.0 + OVERLAY_ALPHA) / 2.);
impl Illuminance {
/// The color used to describe the illuminance of a tile.
pub(crate) fn info_vis_color(&self) -> Color {
// Because these are discretized, they're easier to understand with a fully opaque color
match self {
Illuminance::Dark => Color::hsla(232., 0.6, 0.4, DISCRETE_OVERLAY_ALPHA),
Illuminance::DimlyLit => Color::hsla(232., 0.6, 0.6, DISCRETE_OVERLAY_ALPHA),
Illuminance::BrightlyLit => Color::hsla(40., 0.9, 0.85, DISCRETE_OVERLAY_ALPHA),
}
}
}
}

/// Colors used for the world's environment
Expand Down
Loading

0 comments on commit 6bf39ce

Please sign in to comment.