refactoring hex coords

This commit is contained in:
2026-03-14 19:55:53 -04:00
parent c5da119109
commit 912ee376c6
36 changed files with 595 additions and 698 deletions

View File

@@ -1,6 +1,7 @@
use bevy::{mesh::MeshVertexAttribute, prelude::*, render::render_resource::VertexFormat};
use hex::{INNER_RADIUS, OUTER_RADIUS};
use crate::hex_utils::{INNER_RADIUS, OUTER_RADIUS};
// use crate::hex_utils::{INNER_RADIUS, OUTER_RADIUS};
pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.);

View File

@@ -1,18 +1,22 @@
use crate::{hex_utils::*, prelude::*};
use crate::prelude::*;
#[cfg(feature = "tracing")]
use bevy::log::*;
use bevy::prelude::*;
use hex::prelude::*;
const CHUNK_TOTAL: usize = Chunk::SIZE * Chunk::SIZE;
pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3]>) {
pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3]>)
{
#[cfg(feature = "tracing")]
let span = info_span!("generate_chunk_collider").entered();
let vertex_count: usize = CHUNK_TOTAL * 6;
let mut verts = Vec::with_capacity(vertex_count);
let mut indices = Vec::with_capacity(vertex_count);
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let height = chunk.heights[x + z * Chunk::SIZE];
let coord = HexCoord::from_grid_pos(x, z);
let neighbors = chunk.get_neighbors(&coord);
@@ -24,9 +28,11 @@ pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3
return (verts, indices);
}
fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>, neighbors: &[f32; 6]) {
fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>, neighbors: &[f32; 6])
{
let idx = verts.len() as u32;
for i in 0..6 {
for i in 0..6
{
let p = pos + HEX_CORNERS[i];
verts.push(p);
}
@@ -37,9 +43,11 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32
indices.push([idx + 2, idx + 4, idx + 5]);
indices.push([idx + 2, idx + 3, idx + 4]);
for i in 0..neighbors.len() {
for i in 0..neighbors.len()
{
let n_height = neighbors[i];
if n_height < pos.y {
if n_height < pos.y
{
create_tile_wall_collider(
idx,
Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z),
@@ -51,7 +59,8 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32
}
}
fn create_tile_wall_collider(idx: u32, pos: Vec3, dir: usize, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>) {
fn create_tile_wall_collider(idx: u32, pos: Vec3, dir: usize, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>)
{
let idx2 = verts.len() as u32;
verts.push(pos + HEX_CORNERS[(dir) % 6]);

View File

@@ -1,12 +1,13 @@
use crate::hex_utils::HexCoord;
use crate::{hex_utils::offset3d_to_world, prelude::*};
use crate::prelude::*;
use bevy::asset::RenderAssetUsages;
#[cfg(feature = "tracing")]
use bevy::log::*;
use bevy::mesh::{Indices, PrimitiveTopology};
use bevy::prelude::*;
use hex::prelude::*;
pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh {
pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh
{
#[cfg(feature = "tracing")]
let span = info_span!("generate_chunk_mesh").entered();
@@ -16,8 +17,10 @@ pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh {
let mut indices = Vec::with_capacity(vertex_count);
let mut normals = Vec::with_capacity(vertex_count);
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let idx = x + z * Chunk::SIZE;
let height = chunk.heights[idx];
let off_pos = Vec3::new(x as f32, height, z as f32);
@@ -59,20 +62,23 @@ fn create_tile(
normals: &mut Vec<Vec3>,
texture_index: u32,
side_texture_index: u32,
) {
)
{
let uv_offset = Vec2::splat(0.5);
let tex_off = Vec2::new(texture_index as f32, 0.);
let side_tex_off = Vec2::new(side_texture_index as f32, 0.);
let idx = verts.len() as u32;
for i in 0..6 {
for i in 0..6
{
let p = pos + HEX_CORNERS[i];
verts.push(p);
let uv = (HEX_CORNERS[i].xz() / 2.) + uv_offset;
uvs.push((uv / TEX_MULTI) + tex_off);
normals.push(Vec3::Y);
}
for i in 0..3 {
for i in 0..3
{
let off = i * 2;
indices.push(off + idx);
indices.push(((off + 1) % 6) + idx);
@@ -82,15 +88,19 @@ fn create_tile(
indices.push(idx + 2);
indices.push(idx + 4);
for i in 0..neighbors.len() {
for i in 0..neighbors.len()
{
let n_height = neighbors[i];
if n_height < pos.y {
if n_height < pos.y
{
create_tile_wall(pos, i, n_height, verts, uvs, indices, normals, side_tex_off);
}
}
}
pub fn generate_chunk_water_mesh(chunk: &MeshChunkData, sealevel: f32, map_width: usize, map_height: usize) -> Mesh {
#[allow(unused)]
pub fn generate_chunk_water_mesh(chunk: &MeshChunkData, sealevel: f32, map_width: usize, map_height: usize) -> Mesh
{
#[cfg(feature = "tracing")]
let _gen_mesh = info_span!("Generate Water Surface Mesh").entered();
let vertex_count: usize = Chunk::SIZE * Chunk::SIZE * 7;
@@ -99,11 +109,14 @@ pub fn generate_chunk_water_mesh(chunk: &MeshChunkData, sealevel: f32, map_width
let mut indices = Vec::with_capacity(vertex_count);
let mut normals = Vec::with_capacity(vertex_count);
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let idx = x + z * Chunk::SIZE;
let height = chunk.heights[idx];
if height > sealevel {
if height > sealevel
{
continue;
}
let off_pos = Vec3::new(x as f32, sealevel, z as f32);
@@ -143,8 +156,10 @@ fn create_tile_water_surface(
uvs: &mut Vec<Vec2>,
indices: &mut Vec<u32>,
normals: &mut Vec<Vec3>,
) {
if !neighbor_has_land {
)
{
if !neighbor_has_land
{
crate_tile_water_inner_surface(pos, dist_to_land, neighbors, verts, uvs, indices, normals);
return;
}
@@ -159,23 +174,29 @@ fn crate_tile_water_inner_surface(
uvs: &mut Vec<Vec2>,
indices: &mut Vec<u32>,
normals: &mut Vec<Vec3>,
) {
)
{
//todo: share verts
let idx = verts.len() as u32;
for i in 0..6 {
for i in 0..6
{
let p = pos + HEX_CORNERS[i];
verts.push(p);
let n1 = if let Some(v) = neighbors[i].1 { v } else { dist_to_land };
let n2 = if let Some(v) = neighbors[(i + 5) % 6].1 {
let n2 = if let Some(v) = neighbors[(i + 5) % 6].1
{
v
} else {
}
else
{
dist_to_land
};
let d = (n1 + n2 + dist_to_land) / 3.0;
uvs.push(Vec2::new(0.0, d.remap(0., 4., 1.0, 0.0)));
normals.push(Vec3::Y);
}
for i in 0..3 {
for i in 0..3
{
let off = i * 2;
indices.push(off + idx);
indices.push(((off + 1) % 6) + idx);
@@ -194,13 +215,15 @@ fn crate_tile_water_shore_surface(
uvs: &mut Vec<Vec2>,
indices: &mut Vec<u32>,
normals: &mut Vec<Vec3>,
) {
)
{
let idx = verts.len() as u32;
//todo: only use triangle fan when on shoreline
verts.push(pos);
uvs.push(Vec2::new(0.0, dist_to_land.remap(0., 4., 1.0, 0.0)));
normals.push(Vec3::Y);
for i in 0..12 {
for i in 0..12
{
let p = pos + WATER_HEX_CORNERS[i];
verts.push(p);
let ni = i / 2;
@@ -208,16 +231,21 @@ fn crate_tile_water_shore_surface(
let nn = neighbors[(ni + 5) % 6];
let mut uv = Vec2::new(0.0, dist_to_land.remap(0., 4., 1.0, 0.0));
if nn.0 > pos.y || n.0 > pos.y {
if nn.0 > pos.y || n.0 > pos.y
{
uv.x = 1.0;
}
if ni * 2 != i {
if n.0 <= pos.y {
if ni * 2 != i
{
if n.0 <= pos.y
{
uv.x = 0.0;
}
let d = if let Some(v) = n.1 { v } else { dist_to_land };
uv.y = ((d + dist_to_land) / 2.0).remap(0., 4., 1.0, 0.0);
} else {
}
else
{
let d = if let Some(v) = n.1 { v } else { dist_to_land };
let d2 = if let Some(v) = nn.1 { v } else { dist_to_land };
uv.y = ((d + d2 + dist_to_land) / 3.0).remap(0., 4., 1.0, 0.0);
@@ -241,7 +269,8 @@ fn create_tile_wall(
indices: &mut Vec<u32>,
normals: &mut Vec<Vec3>,
tex_off: Vec2,
) {
)
{
let p1 = HEX_CORNERS[(dir) % 6] + pos;
let p2 = HEX_CORNERS[(dir + 1) % 6] + pos;
let p3 = Vec3::new(p1.x, height, p1.z);
@@ -275,12 +304,14 @@ fn create_tile_wall(
}
#[cfg(test)]
mod tests {
mod tests
{
use super::*;
#[test]
fn generate_tile_wall() {
fn generate_tile_wall()
{
let mut verts = Vec::new();
let mut uvs = Vec::new();
let mut normals = Vec::new();
@@ -307,7 +338,8 @@ mod tests {
}
#[test]
fn generate_tile() {
fn generate_tile()
{
let mut verts = Vec::new();
let mut uvs = Vec::new();
let mut normals = Vec::new();

View File

@@ -1,19 +1,22 @@
use crate::hex_utils::HexCoord;
use crate::prelude::*;
use bevy::asset::RenderAssetUsages;
use bevy::{
mesh::{Indices, PrimitiveTopology},
prelude::*,
};
use hex::prelude::*;
pub fn generate_packed_chunk_mesh(chunk: &MeshChunkData) -> Mesh {
pub fn generate_packed_chunk_mesh(chunk: &MeshChunkData) -> Mesh
{
let vertex_count: usize = Chunk::SIZE * Chunk::SIZE * 6;
let mut packed_data = Vec::with_capacity(vertex_count);
let mut indices = Vec::with_capacity(vertex_count);
let mut heights = Vec::with_capacity(vertex_count);
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let idx = x + z * Chunk::SIZE;
let height = chunk.heights[idx];
let coord = HexCoord::from_grid_pos(x, z);
@@ -51,12 +54,14 @@ fn create_packed_tile(
heights: &mut Vec<f32>,
texture_index: u32,
side_texture_index: u32,
) {
)
{
let idx = packed_data.len() as u32;
packed_data.push(pack_vertex_data(offset, 0, texture_index));
heights.push(height);
for i in 0..6 {
for i in 0..6
{
packed_data.push(pack_vertex_data(offset, i + 1, texture_index));
indices.push(idx);
indices.push(idx + 1 + i as u32);
@@ -64,9 +69,11 @@ fn create_packed_tile(
heights.push(height);
}
for i in 0..neighbors.len() {
for i in 0..neighbors.len()
{
let n_height = neighbors[i];
if n_height < height {
if n_height < height
{
create_packed_tile_wall(
offset,
height,
@@ -90,7 +97,8 @@ fn create_packed_tile_wall(
indices: &mut Vec<u32>,
heights: &mut Vec<f32>,
side_texture_index: u32,
) {
)
{
let idx = packed_data.len() as u32;
let side_2 = ((side + 1) % 6) + 1;
@@ -113,7 +121,8 @@ fn create_packed_tile_wall(
indices.push(idx + 3);
}
fn pack_vertex_data(offset: UVec2, vert: usize, tex: u32) -> u32 {
fn pack_vertex_data(offset: UVec2, vert: usize, tex: u32) -> u32
{
//6 + 6 bits offset
//4 bits vert
//12 bits texture

View File

@@ -3,6 +3,7 @@ use core::f32;
use bevy::math::{IVec2, UVec2};
use bevy::prelude::{FloatExt, Vec2};
use bevy::utils::default;
use hex::prelude::*;
use noise::{NoiseFn, Simplex, SuperSimplex};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
@@ -10,7 +11,8 @@ use crate::biome_painter::BiomePainter;
use crate::map::biome_map::{BiomeChunk, BiomeData, BiomeMap};
use crate::prelude::*;
pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> (Map, BiomeMap) {
pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePainter) -> (Map, BiomeMap)
{
let biomes = generate_biomes(cfg, seed, painter);
let biomes_borrow = &biomes;
// let mut chunks: Vec<Chunk> = Vec::with_capacity(cfg.size.length_squared() as usize);
@@ -25,11 +27,14 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePain
.collect();
let mut min = f32::MAX;
let mut max = f32::MIN;
for chunk in &chunks {
if chunk.min_level < min {
for chunk in &chunks
{
if chunk.min_level < min
{
min = chunk.min_level;
}
if chunk.max_level > max {
if chunk.max_level > max
{
max = chunk.max_level;
}
}
@@ -48,7 +53,8 @@ pub fn generate_heightmap(cfg: &GenerationConfig, seed: u32, painter: &BiomePain
);
}
pub fn generate_biomes(cfg: &GenerationConfig, seed: u32, biome_painter: &BiomePainter) -> BiomeMap {
pub fn generate_biomes(cfg: &GenerationConfig, seed: u32, biome_painter: &BiomePainter) -> BiomeMap
{
let mut map = BiomeMap::new(cfg.size, biome_painter.biomes.len());
map.chunks = (0..cfg.size.y)
.into_par_iter()
@@ -68,7 +74,8 @@ pub fn generate_biome_chunk(
cfg: &GenerationConfig,
seed: u32,
biome_painter: &BiomePainter,
) -> BiomeChunk {
) -> BiomeChunk
{
let mut chunk = BiomeChunk {
offset: UVec2::new(chunk_x as u32, chunk_y as u32),
data: [BiomeData::default(); Chunk::AREA],
@@ -78,8 +85,10 @@ pub fn generate_biome_chunk(
let noise_t = Simplex::new(seed + 2);
let noise_c = Simplex::new(seed + 3);
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let moisture = sample_point(
x as f64 + chunk_x as f64 * Chunk::SIZE as f64,
z as f64 + chunk_y as f64 * Chunk::SIZE as f64,
@@ -123,14 +132,16 @@ pub fn generate_biome_chunk(
return chunk;
}
pub fn generate_noise_map(size: UVec2, seed: u32, cfg: &NoiseConfig, border_size: f32) -> Vec<f32> {
pub fn generate_noise_map(size: UVec2, seed: u32, cfg: &NoiseConfig, border_size: f32) -> Vec<f32>
{
let noise = SuperSimplex::new(seed);
let data: Vec<_> = (0..(size.y as usize * Chunk::SIZE))
.into_par_iter()
.flat_map(|y| {
let mut row = Vec::with_capacity(size.x as usize * Chunk::SIZE);
for x in 0..row.capacity() {
for x in 0..row.capacity()
{
row.push(sample_point(
x as f64,
y as f64,
@@ -154,21 +165,26 @@ pub fn generate_chunk(
seed: u32,
biome_chunk: &BiomeChunk,
biome_painter: &BiomePainter,
) -> Chunk {
) -> Chunk
{
let mut result: [f32; Chunk::SIZE * Chunk::SIZE] = [0.; Chunk::AREA];
let mut data = [BiomeData::default(); Chunk::AREA];
let mut biome_ids = [0; Chunk::AREA];
let noise = Simplex::new(seed);
let mut min = f32::MAX;
let mut max = f32::MIN;
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let biome_data = biome_chunk.get_biome_data(x, z);
let biome_blend = biome_chunk.get_biome(x, z);
let mut sample = 0.;
for i in 0..biome_blend.len() {
for i in 0..biome_blend.len()
{
let blend = biome_blend[i];
if blend == 0. {
if blend == 0.
{
continue;
}
let biome = &biome_painter.biomes[i];
@@ -185,10 +201,12 @@ pub fn generate_chunk(
let idx = x + z * Chunk::SIZE;
biome_ids[idx] = biome_chunk.get_biome_id_dithered(x, z, &noise, cfg.biome_dither);
result[idx] = sample;
if sample > max {
if sample > max
{
max = sample;
}
if sample < min {
if sample < min
{
min = sample;
}
data[idx] = biome_data.clone();
@@ -212,23 +230,29 @@ fn sample_point(
size: Vec2,
border_size: f32,
border_value: f32,
) -> f32 {
) -> f32
{
let x_s = x / cfg.scale;
let z_s = z / cfg.scale;
let mut elevation: f64 = 0.;
for i in 0..cfg.layers.len() {
for i in 0..cfg.layers.len()
{
let value: f64;
let layer = &cfg.layers[i];
if layer.is_rigid {
if layer.is_rigid
{
value = sample_rigid(x_s, z_s, layer, noise);
} else {
}
else
{
value = sample_simple(x_s, z_s, layer, noise);
}
elevation += value;
}
if border_size == 0.0 {
if border_size == 0.0
{
return elevation as f32;
}
@@ -243,12 +267,14 @@ fn sample_point(
return border_value.lerp(elevation as f32, d);
}
fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64 {
fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64
{
let mut freq: f64 = cfg.base_roughness;
let mut amp: f64 = 1.;
let mut value = 0.;
for _ in 0..cfg.layers {
for _ in 0..cfg.layers
{
let v = noise.get([x * freq, z * freq]);
value += (v + 1.) * 0.5 * amp;
freq *= cfg.roughness;
@@ -257,12 +283,14 @@ fn sample_simple(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64,
value -= cfg.min_value;
return value * cfg.strength;
}
fn sample_rigid(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64 {
fn sample_rigid(x: f64, z: f64, cfg: &GeneratorLayer, noise: &impl NoiseFn<f64, 2>) -> f64
{
let mut freq: f64 = cfg.base_roughness;
let mut amp: f64 = 1.;
let mut value = 0.;
let mut weight = 1.;
for _ in 0..cfg.layers {
for _ in 0..cfg.layers
{
let mut v = 1. - noise.get([x * freq, z * freq]).abs();
v *= v;
v *= weight;

View File

@@ -1,312 +0,0 @@
use std::fmt::Display;
use crate::prelude::Chunk;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
pub const OUTER_RADIUS: f32 = 1.;
pub const INNER_RADIUS: f32 = OUTER_RADIUS * (SQRT_3 / 2.);
pub const SHORT_DIAGONAL: f32 = 1. * SQRT_3;
pub const LONG_DIAGONAL: f32 = 2. * OUTER_RADIUS;
const SQRT_3: f32 = 1.7320508076;
pub fn offset3d_to_world(offset: Vec3) -> Vec3 {
let x = (offset.x + (offset.z * 0.5) - (offset.z / 2.).floor()) * (INNER_RADIUS * 2.);
return Vec3::new(x, offset.y, offset.z * OUTER_RADIUS * 1.5);
}
pub fn offset_to_world(offset: IVec2, height: f32) -> Vec3 {
let off = offset.as_vec2();
let x = (off.x + (off.y * 0.5) - (off.y / 2.).floor()) * (INNER_RADIUS * 2.);
return Vec3::new(x, height, off.y * OUTER_RADIUS * 1.5);
}
pub fn offset_to_hex(offset: IVec2) -> IVec3 {
let mut v = IVec3 {
x: offset.x - (offset.y / 2),
y: offset.y,
z: 0,
};
v.z = -v.x - v.y;
return v;
}
pub fn offset_to_index(offset: IVec2, width: usize) -> usize {
return offset.x as usize + offset.y as usize * width;
}
pub fn snap_to_hex_grid(world_pos: Vec3) -> Vec3 {
return offset_to_world(world_to_offset_pos(world_pos), world_pos.y);
}
pub fn world_to_offset_pos(world_pos: Vec3) -> IVec2 {
let offset = world_pos.z / (OUTER_RADIUS * 3.);
let x = (world_pos.x / (INNER_RADIUS * 2.)) - offset;
let z = -world_pos.x - offset;
let ix = x.round() as i32;
let iz = z.round() as i32;
let ox = ix + iz / 2;
let oz = iz;
return IVec2::new(ox, oz);
}
pub fn tile_to_world_distance(dist: u32) -> f32 {
return dist as f32 * (2. * INNER_RADIUS);
}
pub fn get_tile_count_in_range(radius: usize) -> usize {
return 1 + 3 * (radius + 1) * radius;
}
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
pub struct HexCoord {
pub hex: IVec3,
}
impl Display for HexCoord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("HexCoord{}", self.hex))
}
}
impl HexCoord {
pub const DIRECTIONS: [IVec3; 6] = [
IVec3::new(0, 1, -1),
IVec3::new(1, 0, -1),
IVec3::new(1, -1, 0),
IVec3::new(0, -1, 1),
IVec3::new(-1, 0, 1),
IVec3::new(-1, 1, 0),
];
pub const ZERO: HexCoord = HexCoord { hex: IVec3::ZERO };
pub fn new(x: i32, z: i32) -> Self {
return HexCoord {
hex: IVec3::new(x, z, -x - z),
};
}
pub fn from_hex(hex: IVec2) -> Self {
return HexCoord {
hex: IVec3::new(hex.x, hex.y, -hex.x - hex.y),
};
}
pub fn from_grid_pos(x: usize, z: usize) -> Self {
return HexCoord::new(x as i32 - (z as i32 / 2), z as i32);
}
pub fn from_offset(offset_pos: IVec2) -> Self {
return HexCoord {
hex: offset_to_hex(offset_pos),
};
}
pub fn from_world_pos(world_pos: Vec3) -> Self {
let offset = world_pos.z / (OUTER_RADIUS * 3.);
let mut x = world_pos.x / (INNER_RADIUS * 2.);
let mut z = -x;
z -= offset;
x -= offset;
let i_x = x.round() as i32;
let i_z = (-x - z).round() as i32;
let offset_pos = IVec2::new(i_x + i_z / 2, i_z);
return Self::from_offset(offset_pos);
}
pub fn is_in_bounds(&self, map_height: usize, map_width: usize) -> bool {
let off = self.to_offset();
if off.x < 0 || off.y < 0 {
return false;
}
if off.x >= map_width as i32 || off.y >= map_height as i32 {
return false;
}
return true;
}
pub fn is_on_chunk_edge(&self) -> bool {
let offset = self.to_offset().rem_euclid(IVec2::splat(Chunk::SIZE as i32));
let e = (Chunk::SIZE - 1) as i32;
return offset.x == 0 || offset.y == 0 || offset.x == e || offset.y == e;
}
pub fn to_chunk_pos(&self) -> IVec2 {
let off = self.to_offset();
return IVec2 {
x: (off.x as f32 / Chunk::SIZE as f32).floor() as i32,
y: (off.y as f32 / Chunk::SIZE as f32).floor() as i32,
};
}
/// Converts this coordinate to it's chunk local equivalent
pub fn to_chunk(&self) -> HexCoord {
let c_pos = self.to_chunk_pos();
let off = self.to_offset();
return HexCoord::from_offset(
(
off.x - (c_pos.x * Chunk::SIZE as i32),
off.y - (c_pos.y * Chunk::SIZE as i32),
)
.into(),
);
}
pub fn to_world(&self, height: f32) -> Vec3 {
return offset_to_world(self.to_offset(), height);
}
pub fn to_offset(&self) -> IVec2 {
return IVec2::new(self.hex.x + (self.hex.y / 2), self.hex.y);
}
/// Convert the current coordiante to an index
pub fn to_index(&self, width: usize) -> usize {
return ((self.hex.x + self.hex.y * width as i32) + (self.hex.y / 2)) as usize;
}
/// Gets the index of this coord in the chunk array.
///
/// [`width`] is in number of chunks
pub fn to_chunk_index(&self, width: usize) -> usize {
let pos = self.to_chunk_pos();
return (pos.x + pos.y * width as i32) as usize;
}
/// Gets the index of this tile in the chunk
pub fn to_chunk_local_index(&self) -> usize {
return self.to_chunk().to_index(Chunk::SIZE);
}
pub fn distance(&self, other: &HexCoord) -> i32 {
return (self.hex.x - other.hex.x).abs() + (self.hex.y - other.hex.y).abs() + (self.hex.z - other.hex.z).abs();
}
pub fn rotate_around(&self, center: &HexCoord, angle: i32) -> HexCoord {
if self == center || angle == 0 {
return self.clone();
}
let mut a = angle % 6;
let mut pc = self.hex - center.hex;
if a > 0 {
for _ in 0..a {
pc = Self::slide_right(pc);
}
} else {
a = a.abs();
for _ in 0..a {
pc = Self::slide_left(pc);
}
}
return HexCoord::from_hex(pc.xy() + center.hex.xy());
}
fn slide_left(hex: IVec3) -> IVec3 {
return (hex * -1).yzx();
}
fn slide_right(hex: IVec3) -> IVec3 {
return (hex * -1).zxy();
}
pub fn scale(&self, dir: i32, radius: usize) -> HexCoord {
let s = Self::DIRECTIONS[(dir % 6) as usize] * radius as i32;
return Self::from_hex(self.hex.xy() + s.xy());
}
pub fn get_neighbor(&self, dir: usize) -> HexCoord {
let d = Self::DIRECTIONS[dir % 6];
return Self::from_hex(self.hex.xy() + d.xy());
}
pub fn get_neighbors(&self) -> [HexCoord; 6] {
return [
self.get_neighbor(0),
self.get_neighbor(1),
self.get_neighbor(2),
self.get_neighbor(3),
self.get_neighbor(4),
self.get_neighbor(5),
];
}
pub fn hex_select(&self, radius: usize, include_center: bool) -> Vec<HexCoord> {
assert!(radius != 0, "Radius cannot be zero");
let mut result = Vec::with_capacity(get_tile_count_in_range(radius));
if include_center {
result.push(*self);
}
for k in 0..(radius + 1) {
let mut p = self.scale(4, k);
for i in 0..6 {
for _j in 0..k {
p = p.get_neighbor(i);
result.push(p);
}
}
}
return result;
}
pub fn hex_select_bounded(
&self,
radius: usize,
include_center: bool,
height: usize,
width: usize,
) -> Vec<HexCoord> {
assert!(radius != 0, "Radius cannot be zero");
let mut result = Vec::with_capacity(get_tile_count_in_range(radius));
if include_center {
if self.is_in_bounds(height, width) {
result.push(*self);
}
}
for k in 0..(radius + 1) {
let mut p = self.scale(4, k);
for i in 0..6 {
for _j in 0..k {
p = p.get_neighbor(i);
if p.is_in_bounds(height, width) {
result.push(p);
}
}
}
}
return result;
}
pub fn select_ring(&self, radius: usize) -> Vec<HexCoord> {
assert!(radius != 0, "Radius cannot be zero");
let mut result = Vec::with_capacity(radius * 6);
let mut p = self.scale(4, radius);
// if radius == 1 {
// result.push(*self);
// return result;
// }
for i in 0..6 {
for _j in 0..radius {
result.push(p);
p = p.get_neighbor(i);
}
}
return result;
}
}

View File

@@ -1,11 +1,10 @@
pub mod biome_asset;
pub mod biome_painter;
pub mod consts;
pub mod generators;
pub mod heightmap;
pub mod hex_utils;
pub mod map;
pub mod prelude;
pub mod states;
pub mod tile_manager;
pub mod tile_mapper;
pub mod biome_asset;

View File

@@ -5,10 +5,11 @@ use bevy::{
use noise::NoiseFn;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use super::chunk::Chunk;
use hex::prelude::*;
#[derive(Clone, Resource)]
pub struct BiomeMap {
pub struct BiomeMap
{
pub height: usize,
pub width: usize,
pub size: UVec2,
@@ -17,26 +18,33 @@ pub struct BiomeMap {
}
#[derive(Default, Clone, Copy)]
pub struct BiomeData {
pub struct BiomeData
{
pub moisture: f32,
pub temperature: f32,
pub continentality: f32,
}
impl Into<Vec3> for &BiomeData {
fn into(self) -> Vec3 {
impl Into<Vec3> for &BiomeData
{
fn into(self) -> Vec3
{
return Vec3::new(self.moisture, self.temperature, self.continentality);
}
}
impl Into<Vec3> for BiomeData {
fn into(self) -> Vec3 {
impl Into<Vec3> for BiomeData
{
fn into(self) -> Vec3
{
return Vec3::new(self.moisture, self.temperature, self.continentality);
}
}
impl BiomeMap {
pub fn new(size: UVec2, biome_count: usize) -> Self {
impl BiomeMap
{
pub fn new(size: UVec2, biome_count: usize) -> Self
{
let len = size.x as usize * size.y as usize * Chunk::AREA;
return BiomeMap {
size,
@@ -47,14 +55,17 @@ impl BiomeMap {
};
}
pub fn blend(&mut self, count: usize) {
pub fn blend(&mut self, count: usize)
{
assert!(count != 0, "Count cannot be 0");
for _ in 0..count {
for _ in 0..count
{
self.blend_once();
}
}
fn blend_once(&mut self) {
fn blend_once(&mut self)
{
let c: Vec<BiomeChunk> = (0..self.chunks.len())
.into_par_iter()
.map(|i| &self.chunks[i])
@@ -63,7 +74,8 @@ impl BiomeMap {
.into_par_iter()
.map(|y| {
let mut new_tiles = Vec::with_capacity(self.width);
for x in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE
{
let tx = x as u32 + chunk.offset.x * Chunk::SIZE as u32;
let ty = y as u32 + chunk.offset.y * Chunk::SIZE as u32;
let kernel = self.get_kernel(tx as i32, ty as i32);
@@ -76,7 +88,8 @@ impl BiomeMap {
});
let sum: f32 = r.iter().sum();
if sum == 0. {
if sum == 0.
{
new_tiles.push(vec![0.; self.biome_count]);
continue;
}
@@ -96,7 +109,8 @@ impl BiomeMap {
self.chunks = c;
}
fn get_kernel(&self, x: i32, y: i32) -> [Option<&Vec<f32>>; 9] {
fn get_kernel(&self, x: i32, y: i32) -> [Option<&Vec<f32>>; 9]
{
return [
self.get_biome(x - 1, y - 1),
self.get_biome(x, y - 1),
@@ -110,11 +124,14 @@ impl BiomeMap {
];
}
pub fn get_biome(&self, x: i32, y: i32) -> Option<&Vec<f32>> {
if x < 0 || y < 0 {
pub fn get_biome(&self, x: i32, y: i32) -> Option<&Vec<f32>>
{
if x < 0 || y < 0
{
return None;
}
if x >= self.width as i32 || y >= self.height as i32 {
if x >= self.width as i32 || y >= self.height as i32
{
return None;
}
@@ -125,7 +142,8 @@ impl BiomeMap {
return Some(chunk.get_biome(x as usize - cx * Chunk::SIZE, y as usize - cy * Chunk::SIZE));
}
pub fn get_biome_id(&self, x: usize, y: usize) -> usize {
pub fn get_biome_id(&self, x: usize, y: usize) -> usize
{
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
@@ -134,7 +152,8 @@ impl BiomeMap {
return chunk.get_biome_id(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE));
}
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize {
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize
{
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
@@ -143,7 +162,8 @@ impl BiomeMap {
return chunk.get_biome_id_dithered(x - (cx * Chunk::SIZE), y - (cy * Chunk::SIZE), noise, scale);
}
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData {
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData
{
let cx = (x as f32 / Chunk::SIZE as f32).floor() as usize;
let cy = (y as f32 / Chunk::SIZE as f32).floor() as usize;
@@ -154,28 +174,35 @@ impl BiomeMap {
}
#[derive(Clone)]
pub struct BiomeChunk {
pub struct BiomeChunk
{
pub tiles: Vec<Vec<f32>>,
pub offset: UVec2,
pub data: [BiomeData; Chunk::AREA],
}
impl BiomeChunk {
pub fn get_biome(&self, x: usize, y: usize) -> &Vec<f32> {
impl BiomeChunk
{
pub fn get_biome(&self, x: usize, y: usize) -> &Vec<f32>
{
return &self.tiles[x + y * Chunk::SIZE];
}
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData {
pub fn get_biome_data(&self, x: usize, y: usize) -> &BiomeData
{
return &self.data[x + y * Chunk::SIZE];
}
pub fn get_biome_id(&self, x: usize, y: usize) -> usize {
pub fn get_biome_id(&self, x: usize, y: usize) -> usize
{
let b = self.get_biome(x, y);
let mut max = 0.;
let mut idx = 0;
for i in 0..b.len() {
for i in 0..b.len()
{
let blend = b[i];
if blend > max {
if blend > max
{
max = blend;
idx = i;
}
@@ -183,17 +210,21 @@ impl BiomeChunk {
return idx;
}
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize {
pub fn get_biome_id_dithered(&self, x: usize, y: usize, noise: &impl NoiseFn<f64, 2>, scale: f64) -> usize
{
let mut cur_id = self.get_biome_id(x, y);
let b = self.get_biome(x, y);
let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32 - 0.5)/ 2.0;
let n = (noise.get([x as f64 / scale, y as f64 / scale]) as f32 - 0.5) / 2.0;
let mut max = b[cur_id] + n;
for i in 0..b.len() {
for i in 0..b.len()
{
let blend = b[i];
if blend == 0. {
if blend == 0.
{
continue;
}
if blend > max {
if blend > max
{
max = blend + n;
cur_id = i;
}
@@ -204,18 +235,22 @@ impl BiomeChunk {
}
#[cfg(test)]
mod tests {
mod tests
{
use super::*;
#[test]
fn biome_blend() {
fn biome_blend()
{
let mut biome = BiomeMap::new(UVec2::splat(4), 8);
let w = biome.size.x as usize;
let h = biome.size.y as usize;
for y in 0..h {
for x in 0..w {
for y in 0..h
{
for x in 0..w
{
let mut b = vec![0.; biome.biome_count];
let idx = (x + y) % biome.biome_count;
b[idx] = 1.;
@@ -227,7 +262,8 @@ mod tests {
assert!(biome.chunks.iter().all(|f| f.tiles.len() == Chunk::AREA), "Data Lost");
}
fn generate_chunk(x: usize, y: usize, biome: Vec<f32>) -> BiomeChunk {
fn generate_chunk(x: usize, y: usize, biome: Vec<f32>) -> BiomeChunk
{
let chunk = BiomeChunk {
offset: UVec2::new(x as u32, y as u32),
data: [BiomeData::default(); Chunk::AREA],

View File

@@ -1,79 +0,0 @@
use crate::hex_utils::SHORT_DIAGONAL;
use bevy::prelude::*;
#[derive(Clone)]
pub struct Chunk {
pub heights: [f32; Chunk::AREA],
pub textures: [[u32; 2]; Chunk::AREA],
// 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 {
fn default() -> Self {
Self {
heights: [0.; Chunk::AREA],
textures: [[0; 2]; Chunk::AREA],
// biome_data: [BiomeData::default(); Chunk::AREA],
biome_id: [0; Chunk::AREA],
chunk_offset: Default::default(),
min_level: 0.0,
max_level: 0.0,
}
}
}
impl Chunk {
pub const SIZE: usize = 64;
pub const AREA: usize = Chunk::SIZE * Chunk::SIZE;
pub const WORLD_WIDTH: f32 = Chunk::SIZE as f32 * SHORT_DIAGONAL;
pub const WORLD_HEIGHT: f32 = Chunk::SIZE as f32 * 1.5;
pub const WORLD_SIZE: Vec2 = Vec2::new(Chunk::WORLD_WIDTH, Chunk::WORLD_HEIGHT);
pub fn get_pos_z_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for x in 0..Chunk::SIZE {
let idx = x + (Chunk::SIZE - 1) * Chunk::SIZE;
data[x] = self.heights[idx];
}
return data;
}
pub fn get_neg_z_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for x in 0..Chunk::SIZE {
data[x] = self.heights[x];
}
return data;
}
pub fn get_pos_x_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for z in 0..Chunk::SIZE {
let idx = (Chunk::SIZE - 1) + z * Chunk::SIZE;
data[z] = self.heights[idx];
}
return data;
}
pub fn get_neg_x_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for z in 0..Chunk::SIZE {
let idx = z * Chunk::SIZE;
data[z] = self.heights[idx];
}
return data;
}
}

View File

@@ -2,11 +2,12 @@ use bevy::prelude::*;
use bevy_inspector_egui::InspectorOptions;
use serde::{Deserialize, Serialize};
use super::chunk::Chunk;
use hex::prelude::*;
#[derive(Resource, Reflect, Default, Clone)]
#[reflect(Resource)]
pub struct GenerationConfig {
pub struct GenerationConfig
{
pub sea_level: f64,
pub border_size: f32,
pub biome_blend: usize,
@@ -17,23 +18,28 @@ pub struct GenerationConfig {
pub size: UVec2,
}
impl GenerationConfig {
pub fn get_total_width(&self) -> usize {
impl GenerationConfig
{
pub fn get_total_width(&self) -> usize
{
return self.size.x as usize * Chunk::SIZE;
}
pub fn get_total_height(&self) -> usize {
pub fn get_total_height(&self) -> usize
{
return self.size.y as usize * Chunk::SIZE;
}
}
#[derive(Serialize, Deserialize, Default, Reflect, Clone, Debug)]
pub struct NoiseConfig {
pub struct NoiseConfig
{
pub scale: f64,
pub layers: Vec<GeneratorLayer>,
}
#[derive(Reflect, InspectorOptions, Serialize, Deserialize, Debug, Clone, Default)]
pub struct GeneratorLayer {
pub struct GeneratorLayer
{
pub strength: f64,
pub min_value: f64,
pub base_roughness: f64,

View File

@@ -1,14 +1,11 @@
use bevy::prelude::*;
use hex::prelude::*;
use crate::hex_utils::*;
use super::{
chunk::Chunk,
mesh_chunk::MeshChunkData,
};
use super::mesh_chunk::MeshChunkData;
#[derive(Resource, Clone)]
pub struct Map {
pub struct Map
{
pub chunks: Vec<Chunk>,
pub height: usize,
pub width: usize,
@@ -18,20 +15,25 @@ pub struct Map {
pub biome_count: usize,
}
impl Map {
pub fn get_tile_count(&self) -> usize {
impl Map
{
pub fn get_tile_count(&self) -> usize
{
return self.get_tile_width() * self.get_tile_height();
}
pub fn get_tile_width(&self) -> usize {
pub fn get_tile_width(&self) -> usize
{
return self.width * Chunk::SIZE;
}
pub fn get_tile_height(&self) -> usize {
pub fn get_tile_height(&self) -> usize
{
return self.height * Chunk::SIZE;
}
pub fn get_chunk_mesh_data(&self, chunk_index: usize) -> MeshChunkData {
pub fn get_chunk_mesh_data(&self, chunk_index: usize) -> MeshChunkData
{
#[cfg(feature = "tracing")]
let _spawn_span = info_span!("Chunk Mesh Data").entered();
let chunk = &self.chunks[chunk_index];
@@ -45,36 +47,45 @@ impl Map {
};
}
fn get_distance_from_land(&self, chunk_offset: IVec2, range: usize) -> [f32; Chunk::AREA] {
fn get_distance_from_land(&self, chunk_offset: IVec2, range: usize) -> [f32; Chunk::AREA]
{
#[cfg(feature = "tracing")]
let _spawn_span = info_span!("Chunk Land Dist Data").entered();
let mut dists = [0.0; Chunk::AREA];
let cx = chunk_offset.x as usize * Chunk::SIZE;
let cz = chunk_offset.y as usize * Chunk::SIZE;
for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE {
for z in 0..Chunk::SIZE
{
for x in 0..Chunk::SIZE
{
let coord = HexCoord::from_grid_pos(x + cx, z + cz);
let index = coord.to_chunk_local_index();
if !self.is_in_bounds(&coord) {
if !self.is_in_bounds(&coord)
{
warn!("Coord is not in bounds!?");
}
//Current tile is land tile
if self.sample_height(&coord) > self.sealevel {
if self.sample_height(&coord) > self.sealevel
{
dists[index] = 0.0;
continue;
}
//Find closest land tile
if let Some(d) = self.hex_select_first(&coord, range, false, |_t, h, r| {
if h > self.sealevel {
if h > self.sealevel
{
return Some(r as f32);
}
return None;
}) {
})
{
dists[index] = d;
} else {
}
else
{
dists[index] = range as f32;
}
}
@@ -82,14 +93,17 @@ impl Map {
return dists;
}
pub fn get_neighbors(&self, pos: &HexCoord) -> [Option<f32>; 6] {
pub fn get_neighbors(&self, pos: &HexCoord) -> [Option<f32>; 6]
{
let mut results: [Option<f32>; 6] = [None; 6];
let w = self.width * Chunk::SIZE;
let h = self.height * Chunk::SIZE;
let n_tiles = pos.get_neighbors();
for i in 0..6 {
for i in 0..6
{
let n_tile = n_tiles[i];
if !n_tile.is_in_bounds(h, w) {
if !n_tile.is_in_bounds(h, w)
{
continue;
}
let c_idx = n_tile.to_chunk_index(self.width);
@@ -100,7 +114,8 @@ impl Map {
return results;
}
pub fn sample_height(&self, pos: &HexCoord) -> f32 {
pub fn sample_height(&self, pos: &HexCoord) -> f32
{
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
@@ -110,7 +125,8 @@ impl Map {
return chunk.heights[pos.to_chunk_local_index()];
}
pub fn sample_height_mut(&mut self, pos: &HexCoord) -> &mut f32 {
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"
@@ -120,11 +136,13 @@ impl Map {
return &mut chunk.heights[pos.to_chunk_local_index()];
}
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool {
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool
{
return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE);
}
pub fn get_biome_id(&self, pos: &HexCoord) -> usize {
pub fn get_biome_id(&self, pos: &HexCoord) -> usize
{
assert!(
self.is_in_bounds(pos),
"The provided coordinate is not within the map bounds"
@@ -134,13 +152,15 @@ impl Map {
return chunk.biome_id[pos.to_chunk_local_index()];
}
pub fn get_center(&self) -> Vec3 {
pub fn get_center(&self) -> Vec3
{
let w = self.get_world_width();
let h = self.get_world_height();
return Vec3::new(w / 2., self.sealevel, h / 2.);
}
pub fn get_center_with_height(&self) -> Vec3 {
pub fn get_center_with_height(&self) -> Vec3
{
let w = self.get_world_width();
let h = self.get_world_height();
let mut pos = Vec3::new(w / 2., self.sealevel, h / 2.);
@@ -148,22 +168,27 @@ impl Map {
return pos;
}
pub fn get_world_width(&self) -> f32 {
pub fn get_world_width(&self) -> f32
{
return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL;
}
pub fn get_world_height(&self) -> f32 {
pub fn get_world_height(&self) -> f32
{
return (self.height * Chunk::SIZE) as f32 * 1.5;
}
pub fn get_world_size(&self) -> Vec2 {
pub fn get_world_size(&self) -> Vec2
{
return Vec2::new(self.get_world_width(), self.get_world_height());
}
pub fn set_height(&mut self, pos: &HexCoord, height: f32) {
pub fn set_height(&mut self, pos: &HexCoord, height: f32)
{
self.chunks[pos.to_chunk_index(self.width)].heights[pos.to_chunk_local_index()] = height;
}
pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec<(HexCoord, f32)> {
pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec<(HexCoord, f32)>
{
assert!(radius != 0, "Radius cannot be zero");
let tiles = self.hex_select_mut(pos, radius, true, |p, h, r| {
@@ -184,22 +209,30 @@ impl Map {
{
assert!(radius != 0, "Radius cannot be zero");
let mut result = if include_center {
let mut result = if include_center
{
Vec::with_capacity(get_tile_count_in_range(radius) + 1)
} else {
}
else
{
Vec::with_capacity(get_tile_count_in_range(radius))
};
if include_center {
if include_center
{
let h = self.sample_height(&center);
result.push((op)(center, h, 0));
}
for k in 0..(radius + 1) {
for k in 0..(radius + 1)
{
let mut p = center.scale(4, k);
for i in 0..6 {
for _j in 0..k {
for i in 0..6
{
for _j in 0..k
{
p = p.get_neighbor(i);
if self.is_in_bounds(&p) {
if self.is_in_bounds(&p)
{
let h = self.sample_height(&p);
result.push((op)(&p, h, k));
}
@@ -222,23 +255,30 @@ impl Map {
{
assert!(radius != 0, "Radius cannot be zero");
if include_center {
if include_center
{
let h = self.sample_height(&center);
let r = (op)(center, h, 0);
if r.is_some() {
if r.is_some()
{
return r;
}
}
for k in 0..(radius + 1) {
for k in 0..(radius + 1)
{
let mut p = center.scale(4, k);
for i in 0..6 {
for _j in 0..k {
for i in 0..6
{
for _j in 0..k
{
p = p.get_neighbor(i);
if self.is_in_bounds(&p) {
if self.is_in_bounds(&p)
{
let h = self.sample_height(&p);
let r = (op)(&p, h, k);
if r.is_some() {
if r.is_some()
{
return r;
}
}
@@ -265,15 +305,20 @@ impl Map {
"Start radius cannot be lower than end radius"
);
for k in start_radius..(end_radius + 1) {
for k in start_radius..(end_radius + 1)
{
let mut p = center.scale(4, k);
for i in 0..6 {
for _j in 0..k {
for i in 0..6
{
for _j in 0..k
{
p = p.get_neighbor(i);
if self.is_in_bounds(&p) {
if self.is_in_bounds(&p)
{
let h = self.sample_height(&p);
let r = (op)(&p, h, k);
if r.is_some() {
if r.is_some()
{
return r;
}
}
@@ -296,22 +341,30 @@ impl Map {
{
assert!(radius != 0, "Radius cannot be zero");
let mut result = if include_center {
let mut result = if include_center
{
Vec::with_capacity(get_tile_count_in_range(radius) + 1)
} else {
}
else
{
Vec::with_capacity(get_tile_count_in_range(radius))
};
if include_center {
if include_center
{
let h = self.sample_height_mut(&center);
result.push((op)(center, h, 0));
}
for k in 0..(radius + 1) {
for k in 0..(radius + 1)
{
let mut p = center.scale(4, k);
for i in 0..6 {
for _j in 0..k {
for i in 0..6
{
for _j in 0..k
{
p = p.get_neighbor(i);
if self.is_in_bounds(&p) {
if self.is_in_bounds(&p)
{
let h = self.sample_height_mut(&p);
result.push((op)(&p, h, k));
}

View File

@@ -4,9 +4,9 @@ use bevy::{math::VectorSpace, prelude::*};
use image::ImageBuffer;
use rayon::prelude::*;
use crate::hex_utils::HexCoord;
use hex::prelude::*;
use super::{biome_map::BiomeMap, chunk::Chunk, map::Map};
use super::{biome_map::BiomeMap, map::Map};
pub fn render_image(
size: UVec2,

View File

@@ -2,9 +2,7 @@ use std::collections::VecDeque;
use bevy::math::IVec2;
use crate::hex_utils::HexCoord;
use super::chunk::Chunk;
use hex::prelude::*;
pub struct MeshChunkData
{

View File

@@ -1,6 +1,5 @@
pub mod chunk;
pub mod mesh_chunk;
pub mod biome_map;
pub mod config;
pub mod map;
pub mod biome_map;
pub mod map_utils;
pub mod map_utils;
pub mod mesh_chunk;

View File

@@ -1,5 +1,4 @@
pub use crate::consts::*;
pub use crate::map::chunk::*;
pub use crate::map::config::*;
pub use crate::map::map::*;
pub use crate::map::mesh_chunk::*;