Added generation of images based on the map

Tweaks to consistency with DIRECTIONS and HEX_CORNERS
Misc debug visualizations
This commit is contained in:
2024-08-04 21:45:23 -04:00
parent bde25435ec
commit f7a3a56a0a
13 changed files with 671 additions and 21 deletions

View File

@@ -4,6 +4,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
use super::chunk::Chunk;
#[derive(Clone)]
pub struct BiomeMap {
pub height: usize,
pub width: usize,

View File

@@ -10,6 +10,8 @@ pub struct Chunk {
pub biome_data: [BiomeData; Chunk::AREA],
pub biome_id: [usize; Chunk::AREA],
pub chunk_offset: IVec2,
pub min_level: f32,
pub max_level: f32,
}
impl Default for Chunk {
@@ -20,6 +22,8 @@ impl Default for Chunk {
biome_data: [BiomeData::default(); Chunk::AREA],
biome_id: [0; Chunk::AREA],
chunk_offset: Default::default(),
min_level: 0.0,
max_level: 0.0,
}
}
}

View File

@@ -2,7 +2,11 @@ use bevy::prelude::*;
use crate::hex_utils::*;
use super::{chunk::Chunk, mesh_chunk::MeshChunkData};
use super::{
biome_map::{BiomeData, BiomeMap},
chunk::Chunk,
mesh_chunk::MeshChunkData,
};
#[derive(Resource, Clone)]
pub struct Map {
@@ -10,6 +14,8 @@ pub struct Map {
pub height: usize,
pub width: usize,
pub sea_level: f32,
pub min_level: f32,
pub max_level: f32,
}
impl Map {
@@ -43,11 +49,21 @@ impl Map {
}
pub fn sample_height(&self, pos: &HexCoord) -> f32 {
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
);
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
return chunk.heights[pos.to_chunk_local_index()];
}
pub fn sample_height_mut(&mut self, pos: &HexCoord) -> &mut f32 {
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
);
let chunk = &mut self.chunks[pos.to_chunk_index(self.width)];
return &mut chunk.heights[pos.to_chunk_local_index()];
}
@@ -56,16 +72,46 @@ impl Map {
return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE);
}
pub fn get_biome(&self, pos: &HexCoord) -> &BiomeData {
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
);
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
return &chunk.biome_data[pos.to_chunk_local_index()];
}
pub fn get_moisture(&self, pos: &HexCoord) -> f32 {
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
);
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
return chunk.biome_data[pos.to_chunk_local_index()].moisture;
}
pub fn get_tempurature(&self, pos: &HexCoord) -> f32 {
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
);
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
return chunk.biome_data[pos.to_chunk_local_index()].temperature;
}
pub fn get_continentality(&self, pos: &HexCoord) -> f32 {
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
);
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
return chunk.biome_data[pos.to_chunk_local_index()].continentality;
}
pub fn get_center(&self) -> Vec3 {
let w = self.get_world_width();
let h = self.get_world_height();
@@ -96,7 +142,7 @@ impl Map {
let h2 = cur - depth;
*h = h2.lerp(cur, d * d).max(0.);
return (*p, *h);
return (*p, *h);
});
return tiles;

View File

@@ -0,0 +1,115 @@
use bevy::{math::VectorSpace, prelude::*};
use image::ImageBuffer;
use rayon::prelude::*;
use crate::hex_utils::HexCoord;
use super::{biome_map::BiomeMap, chunk::Chunk, map::Map};
pub fn render_image(
size: UVec2,
data: &Vec<f32>,
color1: LinearRgba,
color2: LinearRgba,
) -> ImageBuffer<image::Rgb<u8>, Vec<u8>> {
let mut image = ImageBuffer::new(size.x * Chunk::SIZE as u32, size.y * Chunk::SIZE as u32);
update_image(size, data, color1, color2, &mut image);
return image;
}
pub fn update_image(
size: UVec2,
data: &Vec<f32>,
color1: LinearRgba,
color2: LinearRgba,
image: &mut ImageBuffer<image::Rgb<u8>, Vec<u8>>,
) {
let min = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&0.0);
let max = *data.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap_or(&1.0);
let w = size.x * Chunk::SIZE as u32;
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
let idx = (y * w + x) as usize;
let v = data[idx];
let t = v.remap(min, max, 0.0, 1.0);
let col = LinearRgba::lerp(&color1, color2, t);
*pixel = to_pixel(&col);
});
}
fn to_pixel(col: &LinearRgba) -> image::Rgb<u8> {
return image::Rgb([
(col.red * 255.0) as u8,
(col.green * 255.0) as u8,
(col.blue * 255.0) as u8,
]);
}
pub fn render_map(map: &Map, smooth: f32) -> ImageBuffer<image::Rgb<u8>, Vec<u8>> {
let mut image = ImageBuffer::new(
map.width as u32 * Chunk::SIZE as u32,
map.height as u32 * Chunk::SIZE as u32,
);
update_map(map, smooth, &mut image);
return image;
}
pub fn update_map(map: &Map, smooth: f32, image: &mut ImageBuffer<image::Rgb<u8>, Vec<u8>>) {
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
let coord = HexCoord::from_grid_pos(x as usize, y as usize);
let right = coord.get_neighbor(1);
let height = map.sample_height(&coord);
let mut color = Hsla::hsl(138.0, 1.0, 0.4);
if height < map.sea_level {
color.hue = 217.0;
}
if map.is_in_bounds(&right) {
let h2 = map.sample_height(&right);
let mut d = h2 - height;
if smooth == 0.0 || d.abs() > smooth {
if d > 0.0 {
color.lightness += 0.1;
} else if d < 0.0 {
color.lightness -= 0.1;
}
} else {
if d.abs() <= smooth {
d /= smooth;
if d > 0.0 {
let c2: LinearRgba = color.with_lightness(color.lightness + 0.1).into();
color = LinearRgba::lerp(&color.into(), c2, d).into();
} else {
let c2: LinearRgba = color.with_lightness(color.lightness - 0.1).into();
color = LinearRgba::lerp(&color.into(), c2, d.abs()).into();
}
}
}
}
*pixel = to_pixel(&color.into());
});
}
pub fn render_biome_map(map: &Map) -> ImageBuffer<image::Rgb<u8>, Vec<u8>> {
let mut image = ImageBuffer::new(
map.width as u32 * Chunk::SIZE as u32,
map.height as u32 * Chunk::SIZE as u32,
);
update_biome_map(map, &mut image);
return image;
}
pub fn update_biome_map(map: &Map, image: &mut ImageBuffer<image::Rgb<u8>, Vec<u8>>) {
image.par_enumerate_pixels_mut().for_each(|(x, y, pixel)| {
let coord = HexCoord::from_grid_pos(x as usize, y as usize);
let tile = map.get_biome(&coord);
let color = LinearRgba::rgb(
tile.temperature / 100.0,
tile.continentality / 100.0,
tile.moisture / 100.0,
);
*pixel = to_pixel(&color);
});
}

View File

@@ -11,7 +11,7 @@ impl MeshChunkData {
pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6] {
let mut data = [0.; 6];
let n_tiles = coord.get_neighbors();
for i in 0..6 {
for i in 6..0 {
let n = n_tiles[i];
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) {
continue;

View File

@@ -2,4 +2,5 @@ pub mod chunk;
pub mod mesh_chunk;
pub mod config;
pub mod map;
pub mod biome_map;
pub mod biome_map;
pub mod map_utils;