Cache rendered maps

Plus some small tweaks
This commit is contained in:
Tobias Berger 2022-11-16 13:55:30 +01:00
parent 6cc04b48fd
commit 7d3e97da5a
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
4 changed files with 78 additions and 18 deletions

View file

@ -32,7 +32,7 @@ impl From<BiomeType> for BiomeStats {
BiomeType::IceCap => BiomeStats { BiomeType::IceCap => BiomeStats {
name: "Ice Cap".into(), name: "Ice Cap".into(),
#[cfg(feature = "render")] #[cfg(feature = "render")]
color: Color::rgb_u8(253, 244, 235), color: Color::WHITE,
min_altitude: World::MIN_ALTITUDE, min_altitude: World::MIN_ALTITUDE,
max_altitude: World::MAX_ALTITUDE, max_altitude: World::MAX_ALTITUDE,
min_rainfall: World::MIN_RAINFALL, min_rainfall: World::MIN_RAINFALL,

View file

@ -35,6 +35,7 @@ impl Display for CartesianError {
} }
} }
#[must_use]
pub fn cartesian_coordinates( pub fn cartesian_coordinates(
alpha: f32, alpha: f32,
mut beta: f32, mut beta: f32,
@ -61,6 +62,7 @@ pub fn cartesian_coordinates(
)) ))
} }
#[must_use]
pub fn random_point_in_sphere(rng: &mut StdRng, radius: f32) -> Vec3A { pub fn random_point_in_sphere(rng: &mut StdRng, radius: f32) -> Vec3A {
// https://karthikkaranth.me/blog/generating-random-points-in-a-sphere/#better-choice-of-spherical-coordinates // https://karthikkaranth.me/blog/generating-random-points-in-a-sphere/#better-choice-of-spherical-coordinates
@ -85,14 +87,19 @@ pub fn random_point_in_sphere(rng: &mut StdRng, radius: f32) -> Vec3A {
) )
} }
#[inline(always)]
#[must_use]
pub fn mix_values(a: f32, b: f32, weight_b: f32) -> f32 { pub fn mix_values(a: f32, b: f32, weight_b: f32) -> f32 {
(b * weight_b) + (a * (1.0 - weight_b)) (b * weight_b) + (a * (1.0 - weight_b))
} }
pub trait RepeatNum { pub trait RepeatNum {
#[must_use]
fn repeat(self, length: Self) -> Self; fn repeat(self, length: Self) -> Self;
} }
impl RepeatNum for f32 { impl RepeatNum for f32 {
#[inline(always)]
#[must_use]
fn repeat(self, length: f32) -> f32 { fn repeat(self, length: f32) -> f32 {
f32::clamp(self - (self / length).floor() * length, 0.0, length) f32::clamp(self - (self / length).floor() * length, 0.0, length)
} }

View file

@ -233,6 +233,7 @@ fn update_gui(world: &mut World) {
fn redraw_map( fn redraw_map(
mut should_redraw: ResMut<ShouldRedraw>, mut should_redraw: ResMut<ShouldRedraw>,
world_manager: Res<WorldManager>, world_manager: Res<WorldManager>,
mut world_renderer: ResMut<WorldRenderer>,
render_settings: Res<WorldRenderSettings>, render_settings: Res<WorldRenderSettings>,
mut images: ResMut<Assets<Image>>, mut images: ResMut<Assets<Image>>,
mut map_sprite: Query<&mut Sprite>, mut map_sprite: Query<&mut Sprite>,
@ -267,7 +268,7 @@ fn redraw_map(
height: world.height, height: world.height,
..default() ..default()
}); });
map_image.data = world_manager.map_color_bytes(render_settings); map_image.data = world_renderer.map_color_bytes(world_manager, render_settings);
map_sprite.single_mut().custom_size = Some(Vec2 { map_sprite.single_mut().custom_size = Some(Vec2 {
x: (world.width * WORLD_SCALE as u32) as f32, x: (world.width * WORLD_SCALE as u32) as f32,
y: (world.height * WORLD_SCALE as u32) as f32, y: (world.height * WORLD_SCALE as u32) as f32,
@ -297,6 +298,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.insert_resource(OpenedWindows::default()) .insert_resource(OpenedWindows::default())
.insert_resource(WorldRenderSettings::default()) .insert_resource(WorldRenderSettings::default())
.insert_resource(ShouldRedraw::default()) .insert_resource(ShouldRedraw::default())
.insert_resource(WorldRenderer::default())
.add_startup_system(generate_graphics) .add_startup_system(generate_graphics)
.add_system(update_gui) .add_system(update_gui)
.add_system(update_cursor_map_position) .add_system(update_cursor_map_position)

View file

@ -1,10 +1,6 @@
use { use {
crate::macros::iterable_enum_stringify, crate::macros::iterable_enum_stringify,
bevy::{ bevy::{asset::HandleId, prelude::*, utils::HashSet},
asset::HandleId,
prelude::{Color, Resource},
utils::HashSet,
},
planet::{BiomeStats, TerrainCell, World, WorldManager}, planet::{BiomeStats, TerrainCell, World, WorldManager},
}; };
@ -166,34 +162,89 @@ fn coastline_color(world: &World, cell: &TerrainCell) -> Color {
COASTLINE_PALETTE[0] COASTLINE_PALETTE[0]
} }
} }
pub trait WorldRenderer {
fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8>; const CACHE_SIZE: usize =
fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color; WorldOverlay::ITEM_COUNT * WorldOverlay::ITEM_COUNT * WorldView::ITEM_COUNT;
#[derive(Default, Resource)]
pub struct WorldRenderer {
cached_world_seed: u32,
cache: [Option<Vec<u8>>; CACHE_SIZE],
} }
impl WorldRenderer for WorldManager {
impl WorldRenderer {
fn cache_index(render_settings: &WorldRenderSettings) -> usize {
let view_num = match render_settings.view {
WorldView::Biomes => 0,
WorldView::Topography => 1,
WorldView::Coastlines => 2,
};
let mut overlay_num = 0;
for overlay in render_settings.visible_overlays.iter() {
overlay_num |= match overlay {
WorldOverlay::Temperature => 1,
WorldOverlay::Rainfall => 2,
};
}
#[cfg(feature = "logging")]
debug!(overlay_num, view_num);
(view_num << WorldOverlay::ITEM_COUNT) | overlay_num
}
#[must_use] #[must_use]
fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8> { pub fn map_color_bytes(
let Some(world) = self.get_world() else { &mut self,
world_manager: &WorldManager,
render_settings: &WorldRenderSettings,
) -> Vec<u8> {
let Some(world) = world_manager.get_world() else {
return vec![]; return vec![];
}; };
world
let cache_index = WorldRenderer::cache_index(render_settings);
#[cfg(feature = "logging")]
debug!(cache_index);
assert!(
cache_index < CACHE_SIZE,
"Generated cache index too large for render cache"
);
if world.seed != self.cached_world_seed {
self.cache = default();
self.cached_world_seed = world.seed;
}
if let Some(cached) = &self.cache[cache_index] {
return cached.clone();
}
let bytes: Vec<_> = world
.terrain .terrain
.iter() .iter()
.rev() .rev()
.flatten() .flatten()
.flat_map(|cell| { .flat_map(|cell| {
self.generate_color(cell, render_settings) self.generate_color(world_manager, cell, render_settings)
.as_rgba_f32() .as_rgba_f32()
.iter() .iter()
.flat_map(|num| num.to_le_bytes()) .flat_map(|num| num.to_le_bytes())
.collect::<Vec<u8>>() .collect::<Vec<u8>>()
}) })
.collect() .collect();
let result = bytes.clone();
self.cache[cache_index] = Some(bytes);
result
} }
#[must_use] #[must_use]
fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color { pub fn generate_color(
let world = self.get_world().expect("No world in generate_color"); &self,
world_manager: &WorldManager,
cell: &TerrainCell,
render_settings: &WorldRenderSettings,
) -> Color {
let world = world_manager
.get_world()
.expect("No world in generate_color");
let base_color = match render_settings.view { let base_color = match render_settings.view {
WorldView::Biomes => biome_color(world, cell), WorldView::Biomes => biome_color(world, cell),
WorldView::Topography => altitude_contour_color(world, cell.altitude), WorldView::Topography => altitude_contour_color(world, cell.altitude),