organization and buidings db
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -1321,6 +1321,14 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "buildings"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"world_generation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
@@ -3246,6 +3254,7 @@ dependencies = [
|
||||
"bevy",
|
||||
"bevy-inspector-egui 0.23.4",
|
||||
"bevy_rapier3d",
|
||||
"buildings",
|
||||
"iyes_perf_ui",
|
||||
"noise 0.8.2",
|
||||
"rayon",
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
resolver = "2"
|
||||
members = [
|
||||
"game/main",
|
||||
"game/buildings",
|
||||
"engine/world_generation",
|
||||
"engine/asset_loader"]
|
||||
"engine/asset_loader", "game/buildings"]
|
||||
|
||||
# Enable a small amount of optimization in debug mode
|
||||
[profile.dev]
|
||||
@@ -14,4 +15,4 @@ opt-level = 1
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
codegen-units = 1
|
||||
|
||||
34
engine/world_generation/src/consts.rs
Normal file
34
engine/world_generation/src/consts.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use bevy::{
|
||||
prelude::*,
|
||||
render::{mesh::MeshVertexAttribute, render_resource::VertexFormat},
|
||||
};
|
||||
|
||||
use crate::hex_utils::{INNER_RADIUS, OUTER_RADIUS};
|
||||
|
||||
pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.);
|
||||
|
||||
pub const HEX_CORNERS: [Vec3; 6] = [
|
||||
Vec3::new(0., 0., OUTER_RADIUS),
|
||||
Vec3::new(INNER_RADIUS, 0., 0.5 * OUTER_RADIUS),
|
||||
Vec3::new(INNER_RADIUS, 0., -0.5 * OUTER_RADIUS),
|
||||
Vec3::new(0., 0., -OUTER_RADIUS),
|
||||
Vec3::new(-INNER_RADIUS, 0., -0.5 * OUTER_RADIUS),
|
||||
Vec3::new(-INNER_RADIUS, 0., 0.5 * OUTER_RADIUS),
|
||||
];
|
||||
|
||||
pub const HEX_NORMALS: [Vec3; 6] = [
|
||||
Vec3::new(INNER_RADIUS / 2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / 2.),
|
||||
Vec3::Z,
|
||||
Vec3::new(INNER_RADIUS / -2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / 2.),
|
||||
Vec3::new(INNER_RADIUS / -2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / -2.),
|
||||
Vec3::NEG_Z,
|
||||
Vec3::new(INNER_RADIUS / 2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / -2.),
|
||||
];
|
||||
|
||||
pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("PackedVertexData", 988540817, VertexFormat::Uint32);
|
||||
pub const ATTRIBUTE_VERTEX_HEIGHT: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("VertexHeight", 988540717, VertexFormat::Float32);
|
||||
|
||||
pub const ATTRIBUTE_TEXTURE_INDEX: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("TextureIndex", 988540917, VertexFormat::Uint32);
|
||||
3
engine/world_generation/src/generators/mod.rs
Normal file
3
engine/world_generation/src/generators/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod chunk_colliders;
|
||||
pub mod mesh_generator;
|
||||
pub mod packed_mesh_generator;
|
||||
@@ -1,333 +1,9 @@
|
||||
pub mod biome_painter;
|
||||
pub mod chunk_colliders;
|
||||
pub mod consts;
|
||||
pub mod generators;
|
||||
pub mod heightmap;
|
||||
pub mod hex_utils;
|
||||
pub mod mesh_generator;
|
||||
pub mod packed_mesh_generator;
|
||||
pub mod map;
|
||||
pub mod prelude;
|
||||
pub mod tile_manager;
|
||||
pub mod tile_mapper;
|
||||
|
||||
pub mod prelude {
|
||||
use crate::hex_utils::{get_tile_count, HexCoord, INNER_RADIUS, OUTER_RADIUS, SHORT_DIAGONAL};
|
||||
use bevy::math::{IVec2, UVec2, Vec2, Vec3};
|
||||
use bevy::prelude::Resource;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::mesh::MeshVertexAttribute;
|
||||
use bevy::render::render_resource::VertexFormat;
|
||||
use bevy_inspector_egui::InspectorOptions;
|
||||
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||
pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.);
|
||||
|
||||
pub const HEX_CORNERS: [Vec3; 6] = [
|
||||
Vec3::new(0., 0., OUTER_RADIUS),
|
||||
Vec3::new(INNER_RADIUS, 0., 0.5 * OUTER_RADIUS),
|
||||
Vec3::new(INNER_RADIUS, 0., -0.5 * OUTER_RADIUS),
|
||||
Vec3::new(0., 0., -OUTER_RADIUS),
|
||||
Vec3::new(-INNER_RADIUS, 0., -0.5 * OUTER_RADIUS),
|
||||
Vec3::new(-INNER_RADIUS, 0., 0.5 * OUTER_RADIUS),
|
||||
];
|
||||
|
||||
pub const HEX_NORMALS: [Vec3; 6] = [
|
||||
Vec3::new(INNER_RADIUS / 2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / 2.),
|
||||
Vec3::Z,
|
||||
Vec3::new(INNER_RADIUS / -2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / 2.),
|
||||
Vec3::new(INNER_RADIUS / -2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / -2.),
|
||||
Vec3::NEG_Z,
|
||||
Vec3::new(INNER_RADIUS / 2., 0., (OUTER_RADIUS + 0.5 * OUTER_RADIUS) / -2.),
|
||||
];
|
||||
|
||||
#[derive(Resource, Reflect, Default)]
|
||||
#[reflect(Resource)]
|
||||
pub struct GenerationConfig {
|
||||
pub noise_scale: f64,
|
||||
pub sea_level: f64,
|
||||
pub border_size: f32,
|
||||
pub size: UVec2,
|
||||
pub layers: Vec<GeneratorLayer>,
|
||||
}
|
||||
|
||||
impl GenerationConfig {
|
||||
pub fn get_total_width(&self) -> usize {
|
||||
return self.size.x as usize * Chunk::SIZE;
|
||||
}
|
||||
pub fn get_total_height(&self) -> usize {
|
||||
return self.size.y as usize * Chunk::SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Reflect, InspectorOptions)]
|
||||
pub struct GeneratorLayer {
|
||||
pub strength: f64,
|
||||
pub min_value: f64,
|
||||
pub base_roughness: f64,
|
||||
pub roughness: f64,
|
||||
pub persistence: f64,
|
||||
pub is_rigid: bool,
|
||||
pub weight: f64,
|
||||
pub weight_multi: f64,
|
||||
pub layers: usize,
|
||||
pub first_layer_mask: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Chunk {
|
||||
pub heights: [f32; Chunk::AREA],
|
||||
pub textures: [[u32; 2]; Chunk::AREA],
|
||||
pub moisture: [f32; Chunk::AREA],
|
||||
pub temperature: [f32; Chunk::AREA],
|
||||
pub chunk_offset: IVec2,
|
||||
}
|
||||
|
||||
impl Default for Chunk {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
heights: [0.; Chunk::AREA],
|
||||
textures: [[0; 2]; Chunk::AREA],
|
||||
moisture: [0.; Chunk::AREA],
|
||||
temperature: [0.; Chunk::AREA],
|
||||
chunk_offset: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MeshChunkData {
|
||||
pub heights: [f32; Chunk::AREA],
|
||||
pub textures: [[u32; 2]; Chunk::AREA],
|
||||
}
|
||||
|
||||
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 {
|
||||
let n = n_tiles[i];
|
||||
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) {
|
||||
continue;
|
||||
}
|
||||
data[i] = self.heights[n.to_index(Chunk::SIZE)];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Clone)]
|
||||
pub struct Map {
|
||||
pub chunks: Vec<Chunk>,
|
||||
pub height: usize,
|
||||
pub width: usize,
|
||||
pub sea_level: f32,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
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];
|
||||
|
||||
return MeshChunkData {
|
||||
heights: chunk.heights.clone(),
|
||||
textures: chunk.textures.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
let n_tile = n_tiles[i];
|
||||
if !n_tile.is_in_bounds(h, w) {
|
||||
continue;
|
||||
}
|
||||
let c_idx = n_tile.to_chunk_index(self.width);
|
||||
let chunk = &self.chunks[c_idx];
|
||||
let local = n_tile.to_chunk_local_index();
|
||||
results[i] = Some(chunk.heights[local]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
pub fn sample_height(&self, pos: &HexCoord) -> f32 {
|
||||
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 {
|
||||
let chunk = &mut self.chunks[pos.to_chunk_index(self.width)];
|
||||
return &mut chunk.heights[pos.to_chunk_local_index()];
|
||||
}
|
||||
|
||||
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_moisture(&self, pos: &HexCoord) -> f32 {
|
||||
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
|
||||
return chunk.moisture[pos.to_chunk_local_index()];
|
||||
}
|
||||
|
||||
pub fn get_tempurature(&self, pos: &HexCoord) -> f32 {
|
||||
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
|
||||
return chunk.temperature[pos.to_chunk_local_index()];
|
||||
}
|
||||
|
||||
pub fn get_center(&self) -> Vec3 {
|
||||
let w = self.get_world_width();
|
||||
let h = self.get_world_height();
|
||||
return Vec3::new(w / 2., self.sea_level, h / 2.);
|
||||
}
|
||||
|
||||
pub fn get_world_width(&self) -> f32 {
|
||||
return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL;
|
||||
}
|
||||
pub fn get_world_height(&self) -> f32 {
|
||||
return (self.height * Chunk::SIZE) as f32 * 1.5;
|
||||
}
|
||||
|
||||
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) {
|
||||
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<usize> {
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
let width = self.width;
|
||||
|
||||
let mut chunks = self.hex_select_mut(pos, radius, true, |p, h, r| {
|
||||
let d = (r as f32) / (radius as f32);
|
||||
let cur = *h;
|
||||
let h2 = cur - depth;
|
||||
*h = h2.lerp(cur, d * d);
|
||||
|
||||
return p.to_chunk_index(width);
|
||||
});
|
||||
|
||||
chunks.dedup();
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
pub fn hex_select<OP, Ret>(&self, center: &HexCoord, radius: usize, include_center: bool, op: OP) -> Vec<Ret>
|
||||
where
|
||||
OP: Fn(&HexCoord, f32, usize) -> Ret + Sync + Send,
|
||||
{
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
|
||||
if include_center {
|
||||
let h = self.sample_height(¢er);
|
||||
(op)(¢er, h, 0);
|
||||
}
|
||||
|
||||
let mut result = Vec::with_capacity(get_tile_count(radius));
|
||||
|
||||
for k in 0..(radius + 1) {
|
||||
let mut p = center.scale(4, k);
|
||||
for i in 0..6 {
|
||||
for _j in 0..k {
|
||||
p = p.get_neighbor(i);
|
||||
let h = self.sample_height(&p);
|
||||
result.push((op)(&p, h, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn hex_select_mut<OP, Ret>(
|
||||
&mut self,
|
||||
center: &HexCoord,
|
||||
radius: usize,
|
||||
include_center: bool,
|
||||
op: OP,
|
||||
) -> Vec<Ret>
|
||||
where
|
||||
OP: Fn(&HexCoord, &mut f32, usize) -> Ret + Sync + Send,
|
||||
{
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
|
||||
if include_center {
|
||||
let h = self.sample_height_mut(¢er);
|
||||
(op)(¢er, h, 0);
|
||||
}
|
||||
|
||||
let mut result = Vec::with_capacity(get_tile_count(radius));
|
||||
|
||||
for k in 0..(radius + 1) {
|
||||
let mut p = center.scale(4, k);
|
||||
for i in 0..6 {
|
||||
for _j in 0..k {
|
||||
p = p.get_neighbor(i);
|
||||
let h = self.sample_height_mut(&p);
|
||||
result.push((op)(&p, h, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("PackedVertexData", 988540817, VertexFormat::Uint32);
|
||||
pub const ATTRIBUTE_VERTEX_HEIGHT: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("VertexHeight", 988540717, VertexFormat::Float32);
|
||||
|
||||
pub const ATTRIBUTE_TEXTURE_INDEX: MeshVertexAttribute =
|
||||
MeshVertexAttribute::new("TextureIndex", 988540917, VertexFormat::Uint32);
|
||||
}
|
||||
|
||||
74
engine/world_generation/src/map/chunk.rs
Normal file
74
engine/world_generation/src/map/chunk.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
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 moisture: [f32; Chunk::AREA],
|
||||
pub temperature: [f32; Chunk::AREA],
|
||||
pub chunk_offset: IVec2,
|
||||
}
|
||||
|
||||
impl Default for Chunk {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
heights: [0.; Chunk::AREA],
|
||||
textures: [[0; 2]; Chunk::AREA],
|
||||
moisture: [0.; Chunk::AREA],
|
||||
temperature: [0.; Chunk::AREA],
|
||||
chunk_offset: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
37
engine/world_generation/src/map/config.rs
Normal file
37
engine/world_generation/src/map/config.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_inspector_egui::InspectorOptions;
|
||||
|
||||
use super::chunk::Chunk;
|
||||
|
||||
#[derive(Resource, Reflect, Default)]
|
||||
#[reflect(Resource)]
|
||||
pub struct GenerationConfig {
|
||||
pub noise_scale: f64,
|
||||
pub sea_level: f64,
|
||||
pub border_size: f32,
|
||||
pub size: UVec2,
|
||||
pub layers: Vec<GeneratorLayer>,
|
||||
}
|
||||
|
||||
impl GenerationConfig {
|
||||
pub fn get_total_width(&self) -> usize {
|
||||
return self.size.x as usize * Chunk::SIZE;
|
||||
}
|
||||
pub fn get_total_height(&self) -> usize {
|
||||
return self.size.y as usize * Chunk::SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Reflect, InspectorOptions)]
|
||||
pub struct GeneratorLayer {
|
||||
pub strength: f64,
|
||||
pub min_value: f64,
|
||||
pub base_roughness: f64,
|
||||
pub roughness: f64,
|
||||
pub persistence: f64,
|
||||
pub is_rigid: bool,
|
||||
pub weight: f64,
|
||||
pub weight_multi: f64,
|
||||
pub layers: usize,
|
||||
pub first_layer_mask: bool,
|
||||
}
|
||||
167
engine/world_generation/src/map/map.rs
Normal file
167
engine/world_generation/src/map/map.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::hex_utils::*;
|
||||
|
||||
use super::{chunk::Chunk, mesh_chunk::MeshChunkData};
|
||||
|
||||
#[derive(Resource, Clone)]
|
||||
pub struct Map {
|
||||
pub chunks: Vec<Chunk>,
|
||||
pub height: usize,
|
||||
pub width: usize,
|
||||
pub sea_level: f32,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
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];
|
||||
|
||||
return MeshChunkData {
|
||||
heights: chunk.heights.clone(),
|
||||
textures: chunk.textures.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
let n_tile = n_tiles[i];
|
||||
if !n_tile.is_in_bounds(h, w) {
|
||||
continue;
|
||||
}
|
||||
let c_idx = n_tile.to_chunk_index(self.width);
|
||||
let chunk = &self.chunks[c_idx];
|
||||
let local = n_tile.to_chunk_local_index();
|
||||
results[i] = Some(chunk.heights[local]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
pub fn sample_height(&self, pos: &HexCoord) -> f32 {
|
||||
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 {
|
||||
let chunk = &mut self.chunks[pos.to_chunk_index(self.width)];
|
||||
return &mut chunk.heights[pos.to_chunk_local_index()];
|
||||
}
|
||||
|
||||
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_moisture(&self, pos: &HexCoord) -> f32 {
|
||||
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
|
||||
return chunk.moisture[pos.to_chunk_local_index()];
|
||||
}
|
||||
|
||||
pub fn get_tempurature(&self, pos: &HexCoord) -> f32 {
|
||||
let chunk = &self.chunks[pos.to_chunk_index(self.width)];
|
||||
return chunk.temperature[pos.to_chunk_local_index()];
|
||||
}
|
||||
|
||||
pub fn get_center(&self) -> Vec3 {
|
||||
let w = self.get_world_width();
|
||||
let h = self.get_world_height();
|
||||
return Vec3::new(w / 2., self.sea_level, h / 2.);
|
||||
}
|
||||
|
||||
pub fn get_world_width(&self) -> f32 {
|
||||
return (self.width * Chunk::SIZE) as f32 * SHORT_DIAGONAL;
|
||||
}
|
||||
pub fn get_world_height(&self) -> f32 {
|
||||
return (self.height * Chunk::SIZE) as f32 * 1.5;
|
||||
}
|
||||
|
||||
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) {
|
||||
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<usize> {
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
let width = self.width;
|
||||
|
||||
let mut chunks = self.hex_select_mut(pos, radius, true, |p, h, r| {
|
||||
let d = (r as f32) / (radius as f32);
|
||||
let cur = *h;
|
||||
let h2 = cur - depth;
|
||||
*h = h2.lerp(cur, d * d);
|
||||
|
||||
return p.to_chunk_index(width);
|
||||
});
|
||||
|
||||
chunks.dedup();
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
pub fn hex_select<OP, Ret>(&self, center: &HexCoord, radius: usize, include_center: bool, op: OP) -> Vec<Ret>
|
||||
where
|
||||
OP: Fn(&HexCoord, f32, usize) -> Ret + Sync + Send,
|
||||
{
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
|
||||
if include_center {
|
||||
let h = self.sample_height(¢er);
|
||||
(op)(¢er, h, 0);
|
||||
}
|
||||
|
||||
let mut result = Vec::with_capacity(get_tile_count(radius));
|
||||
|
||||
for k in 0..(radius + 1) {
|
||||
let mut p = center.scale(4, k);
|
||||
for i in 0..6 {
|
||||
for _j in 0..k {
|
||||
p = p.get_neighbor(i);
|
||||
let h = self.sample_height(&p);
|
||||
result.push((op)(&p, h, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn hex_select_mut<OP, Ret>(
|
||||
&mut self,
|
||||
center: &HexCoord,
|
||||
radius: usize,
|
||||
include_center: bool,
|
||||
op: OP,
|
||||
) -> Vec<Ret>
|
||||
where
|
||||
OP: Fn(&HexCoord, &mut f32, usize) -> Ret + Sync + Send,
|
||||
{
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
|
||||
if include_center {
|
||||
let h = self.sample_height_mut(¢er);
|
||||
(op)(¢er, h, 0);
|
||||
}
|
||||
|
||||
let mut result = Vec::with_capacity(get_tile_count(radius));
|
||||
|
||||
for k in 0..(radius + 1) {
|
||||
let mut p = center.scale(4, k);
|
||||
for i in 0..6 {
|
||||
for _j in 0..k {
|
||||
p = p.get_neighbor(i);
|
||||
let h = self.sample_height_mut(&p);
|
||||
result.push((op)(&p, h, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
24
engine/world_generation/src/map/mesh_chunk.rs
Normal file
24
engine/world_generation/src/map/mesh_chunk.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::hex_utils::HexCoord;
|
||||
|
||||
use super::chunk::Chunk;
|
||||
|
||||
pub struct MeshChunkData {
|
||||
pub heights: [f32; Chunk::AREA],
|
||||
pub textures: [[u32; 2]; Chunk::AREA],
|
||||
}
|
||||
|
||||
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 {
|
||||
let n = n_tiles[i];
|
||||
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) {
|
||||
continue;
|
||||
}
|
||||
data[i] = self.heights[n.to_index(Chunk::SIZE)];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
4
engine/world_generation/src/map/mod.rs
Normal file
4
engine/world_generation/src/map/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod chunk;
|
||||
pub mod mesh_chunk;
|
||||
pub mod config;
|
||||
pub mod map;
|
||||
5
engine/world_generation/src/prelude.rs
Normal file
5
engine/world_generation/src/prelude.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
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::*;
|
||||
13
game/buildings/Cargo.toml
Normal file
13
game/buildings/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "buildings"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bevy = "0.13.2"
|
||||
world_generation = {path = "../../engine/world_generation"}
|
||||
|
||||
[features]
|
||||
tracing = []
|
||||
65
game/buildings/src/buildings_database.rs
Normal file
65
game/buildings/src/buildings_database.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use bevy::prelude::*;
|
||||
use world_generation::hex_utils::HexCoord;
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct BuildingDatabase {
|
||||
pub chunks: Vec<BuildingChunk>,
|
||||
}
|
||||
|
||||
impl BuildingDatabase {
|
||||
pub fn new(size: UVec2) -> Self {
|
||||
let mut db = BuildingDatabase {
|
||||
chunks: Vec::with_capacity(size.length_squared() as usize),
|
||||
};
|
||||
|
||||
for y in 0..size.y as i32 {
|
||||
for x in 0..size.x as i32 {
|
||||
let offset = IVec2::new(x, y);
|
||||
let index = (x + y * size.x as i32) as usize;
|
||||
db.chunks.push(BuildingChunk::new(offset, index));
|
||||
}
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
pub fn get_buildings_in_range(&self, coord: &HexCoord, radius: usize) -> Option<Vec<&BuildingEntry>> {
|
||||
assert!(radius != 0, "Radius cannot be zero");
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BuildingChunk {
|
||||
pub entries: Vec<BuildingChunk>,
|
||||
pub index: usize,
|
||||
pub offset: IVec2,
|
||||
}
|
||||
|
||||
impl BuildingChunk {
|
||||
pub fn new(offset: IVec2, index: usize) -> Self {
|
||||
return BuildingChunk {
|
||||
entries: Vec::new(),
|
||||
index,
|
||||
offset,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_building(&self, coord: &HexCoord) -> Option<&BuildingEntry> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BuildingEntry {
|
||||
pub coord: HexCoord,
|
||||
pub entity: Entity,
|
||||
}
|
||||
|
||||
impl BuildingEntry {
|
||||
pub fn new(coord: HexCoord, entity: Entity) -> Self {
|
||||
return BuildingEntry { coord, entity };
|
||||
}
|
||||
}
|
||||
1
game/buildings/src/lib.rs
Normal file
1
game/buildings/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod buildings_database;
|
||||
@@ -14,7 +14,7 @@ noise = "0.8.2"
|
||||
world_generation ={path="../../engine/world_generation"}
|
||||
bevy_rapier3d = { version = "0.25.0", features = [ "simd-stable","parallel" ] }
|
||||
rayon = "1.10.0"
|
||||
|
||||
buildings = {path="../buildings"}
|
||||
|
||||
[features]
|
||||
tracing = ["bevy/trace_tracy", "world_generation/tracing"]
|
||||
tracing = ["bevy/trace_tracy", "world_generation/tracing", "buildings/tracing"]
|
||||
@@ -10,9 +10,8 @@ use bevy_rapier3d::geometry::{Collider, TriMeshFlags};
|
||||
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||
use world_generation::{
|
||||
biome_painter::BiomePainterAsset,
|
||||
chunk_colliders::generate_chunk_collider,
|
||||
hex_utils::{offset_to_index, offset_to_world},
|
||||
mesh_generator::generate_chunk_mesh,
|
||||
generators::{chunk_colliders::generate_chunk_collider, mesh_generator::generate_chunk_mesh},
|
||||
hex_utils::offset_to_world,
|
||||
prelude::{Chunk, Map, MeshChunkData},
|
||||
tile_manager::TileAsset,
|
||||
tile_mapper::TileMapperAsset,
|
||||
|
||||
@@ -34,7 +34,6 @@ impl Default for RenderDistanceSettings {
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct RenderDistanceVisibility {
|
||||
pub distance_multiplier: f32,
|
||||
pub offset: Vec3,
|
||||
}
|
||||
|
||||
@@ -43,19 +42,11 @@ impl RenderDistanceVisibility {
|
||||
self.offset = offset;
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn with_multiplier(mut self, distance_multiplier: f32) -> Self {
|
||||
self.distance_multiplier = distance_multiplier;
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RenderDistanceVisibility {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
distance_multiplier: 1.,
|
||||
offset: Vec3::ZERO,
|
||||
}
|
||||
Self { offset: Vec3::ZERO }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +57,7 @@ fn render_distance_system(
|
||||
) {
|
||||
let camera = camera_query.single();
|
||||
for (t, mut vis, r) in objects.iter_mut() {
|
||||
let dist = (camera.translation - (t.translation + r.offset)).length() * r.distance_multiplier;
|
||||
let dist = (camera.translation - (t.translation + r.offset)).length();
|
||||
if settings.render_distance < dist {
|
||||
*vis = Visibility::Hidden;
|
||||
} else {
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
hard_tabs=true
|
||||
max_width=120
|
||||
hard_tabs = true
|
||||
max_width = 120
|
||||
brace_style = "AlwaysNextLine"
|
||||
control_brace_style = "AlwaysNextLine"
|
||||
Reference in New Issue
Block a user