Added generation of images based on the map
Tweaks to consistency with DIRECTIONS and HEX_CORNERS Misc debug visualizations
This commit is contained in:
@@ -4,6 +4,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
use super::chunk::Chunk;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BiomeMap {
|
||||
pub height: usize,
|
||||
pub width: usize,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
115
engine/world_generation/src/map/map_utils.rs
Normal file
115
engine/world_generation/src/map/map_utils.rs
Normal 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);
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user