WIP generating chunks at player position
This commit is contained in:
+17
-16
@@ -1,11 +1,9 @@
|
||||
mod camera_controller;
|
||||
mod player;
|
||||
pub mod world;
|
||||
use std::time::Instant;
|
||||
use crate::renderer::content_view::ContentView;
|
||||
use crate::renderer::renderer_view::RendererView;
|
||||
use cgmath::Point3;
|
||||
use winit::event::Event;
|
||||
use crate::renderer::{camera::CameraTransform};
|
||||
use player::Player;
|
||||
use world::World;
|
||||
|
||||
@@ -18,19 +16,20 @@ pub enum GameState {
|
||||
pub struct Game {
|
||||
state: GameState,
|
||||
player: Player,
|
||||
#[allow(unused)]
|
||||
world: World,
|
||||
|
||||
prev_pos: Point3<i32>
|
||||
prev_pos: Option<Point3<i32>>
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new() -> Self {
|
||||
let mut app = Self {
|
||||
Self {
|
||||
state: GameState::Paused,
|
||||
player: Player::new(),
|
||||
world: World::new("default".to_string()),
|
||||
prev_pos: Point3{ x:0, y:0, z:0 }
|
||||
};
|
||||
prev_pos: None
|
||||
}
|
||||
|
||||
// // Try to load world from file
|
||||
// let timer = Instant::now();
|
||||
@@ -56,24 +55,26 @@ impl Game {
|
||||
// app.world.save();
|
||||
// println!("Saved in {}", timer.elapsed().as_secs_f32());
|
||||
// }
|
||||
|
||||
|
||||
println!("App initialized");
|
||||
app
|
||||
}
|
||||
|
||||
pub fn update(&mut self, delta: f32, content: &mut impl ContentView) {
|
||||
pub fn update(&mut self, delta: f32, renderer: &mut impl RendererView) {
|
||||
// Update camera controller
|
||||
let camera = content.get_camera_transform();
|
||||
let camera = renderer.get_camera_transform();
|
||||
self.player.camera_controller.update(delta, camera);
|
||||
|
||||
// Update chunks
|
||||
let pos = camera.get_position();
|
||||
let pos = Point3{x: pos.x as i32, y: pos.y as i32, z: pos.z as i32};
|
||||
if pos != self.prev_pos {
|
||||
self.prev_pos = pos;
|
||||
if self.prev_pos == None || self.prev_pos != Some(pos) {
|
||||
self.prev_pos = Some(pos);
|
||||
renderer.get_ui().set_text("World", 0, format!("Position: {}, {}, {}", pos.x, pos.y, pos.z));
|
||||
|
||||
// Load sphere of chunks around player
|
||||
// Load sphere of chunks around player (TODO)
|
||||
let chunk_pos = Point3{x: pos.x / 32, y: pos.y / 32, z: pos.z / 32};
|
||||
if self.world.load_chunk(chunk_pos) {
|
||||
|
||||
renderer.write(, block: &WorldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -28,6 +28,7 @@ impl WorldChunk {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> Self {
|
||||
let mut instance = Self::new();
|
||||
|
||||
@@ -59,6 +60,7 @@ impl WorldChunk {
|
||||
instance
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
|
||||
// Header
|
||||
@@ -82,6 +84,7 @@ impl WorldChunk {
|
||||
bytes
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
|
||||
self.nodes.iter().enumerate()
|
||||
.filter(|(_, n)| **n != 0)
|
||||
@@ -101,7 +104,7 @@ impl WorldChunk {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_block(&mut self, block_pos: &Point3<u32>) -> &mut WorldBlock {
|
||||
pub fn get_block_mut(&mut self, block_pos: &Point3<u32>) -> &mut WorldBlock {
|
||||
// Get block index
|
||||
let index = (block_pos.x + SIZE as u32 * (block_pos.y + SIZE as u32 * block_pos.z)) as usize;
|
||||
let mut value = self.nodes[index];
|
||||
@@ -114,4 +117,12 @@ impl WorldChunk {
|
||||
// Return block reference
|
||||
&mut self.blocks[(value-1) as usize]
|
||||
}
|
||||
|
||||
pub fn get_block(&self, block_pos: &Point3<u32>) -> &WorldBlock {
|
||||
// Get block index
|
||||
let index = (block_pos.x + SIZE as u32 * (block_pos.y + SIZE as u32 * block_pos.z)) as usize;
|
||||
let value = self.nodes[index];
|
||||
// Return block reference
|
||||
&self.blocks[(value-1) as usize]
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ impl WorldGen {
|
||||
/*
|
||||
* Generate block
|
||||
*/
|
||||
pub fn generate(block_pos: &Point3<u32>, block: &mut WorldBlock) {
|
||||
pub fn generate(block_pos: &Point3<i32>, block: &mut WorldBlock) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let noise = Perlin::new();
|
||||
|
||||
|
||||
+69
-70
@@ -1,7 +1,6 @@
|
||||
mod generator;
|
||||
pub mod chunk;
|
||||
pub mod block;
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::io::Write;
|
||||
use std::io::Read;
|
||||
@@ -11,29 +10,37 @@ use std::collections::HashMap;
|
||||
use generator::WorldGen;
|
||||
use chunk::WorldChunk;
|
||||
use block::WorldBlock;
|
||||
use yaml_rust::{Yaml, YamlEmitter};
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug)]
|
||||
struct WorldData {
|
||||
#[serde(default)]
|
||||
pub name: String
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
name: String,
|
||||
chunks: HashMap<Point3<u32>, WorldChunk>,
|
||||
data: WorldData,
|
||||
chunks: HashMap<Point3<i32>, WorldChunk>,
|
||||
}
|
||||
|
||||
fn point_abs(p: &Point3<i32>) -> Point3<u32> {
|
||||
Point3 {x: p.x.abs() as u32, y: p.y.abs() as u32, z: p.z.abs() as u32 }
|
||||
}
|
||||
|
||||
impl World {
|
||||
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
data: WorldData { name },
|
||||
chunks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
|
||||
pub fn all_blocks(&self) -> Vec<(Point3<i32>, &WorldBlock)> {
|
||||
self.chunks.iter().map(|(chunk_pos, chunk)| {
|
||||
let off = chunk_pos * chunk::SIZE as u32;
|
||||
let result: Vec<(Point3<u32>, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| {
|
||||
let off = chunk_pos * chunk::SIZE as i32;
|
||||
let result: Vec<(Point3<i32>, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| {
|
||||
(
|
||||
Point3{x: off.x+pos.x, y: off.y+pos.y, z: off.z+pos.z},
|
||||
Point3{x: off.x+pos.x as i32, y: off.y+pos.y as i32, z: off.z+pos.z as i32},
|
||||
*block
|
||||
)
|
||||
}).collect();
|
||||
@@ -58,77 +65,69 @@ impl World {
|
||||
}
|
||||
|
||||
// Save metadata
|
||||
let mut data: LinkedHashMap<Yaml, Yaml> = LinkedHashMap::new();
|
||||
data.insert(Yaml::String("name".to_string()), Yaml::String(self.name.clone()));
|
||||
|
||||
let mut data_str = String::new();
|
||||
let mut emit = YamlEmitter::new(&mut data_str);
|
||||
emit.dump(&Yaml::Hash(data)).expect("Failed to encode yaml");
|
||||
|
||||
let data_path: PathBuf = [dir.to_str().unwrap(), "world.yml"].iter().collect();
|
||||
let data_str = toml::to_string(&self.data).unwrap();
|
||||
let data_path: PathBuf = [dir.to_str().unwrap(), "world.dat"].iter().collect();
|
||||
let mut data_file = fs::File::create(data_path).unwrap();
|
||||
data_file.write_all(data_str.as_bytes()).expect("Failed to write");
|
||||
data_file.write_all(data_str.as_bytes()).expect("Failed to write world metadata");
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads chunk and displays it in the world.
|
||||
* It will try to load the chunk from file, and if that fails it will generate the chunk and save it.
|
||||
*/
|
||||
pub fn load_chunk(&mut self, pos: Point3<i32>) -> Option<&WorldChunk> {
|
||||
// Abort if chunk is already loaded
|
||||
if let Some(_) = self.chunks.get(&pos) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Try loading chunk from file
|
||||
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
|
||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name(filename);
|
||||
if filepath.is_file() {
|
||||
let mut buff = Vec::new();
|
||||
let mut file = fs::File::open(filepath).expect("Failed to open file");
|
||||
file.read_to_end(&mut buff).expect("Failed to read file");
|
||||
let chunk = WorldChunk::from_bytes(buff);
|
||||
self.chunks.insert(pos, chunk);
|
||||
println!("Chunk at {:?} loaded", pos);
|
||||
return Some(&chunk);
|
||||
}
|
||||
|
||||
// Try generating chunk
|
||||
let mut chunk = WorldChunk::new();
|
||||
for x in 0..32 {
|
||||
for z in 0..32 {
|
||||
let bpos = &Point3::<u32>{x,y:0,z};
|
||||
let block = chunk.get_block_mut(&bpos);
|
||||
let wpos = &Point3::<i32>{ x: pos.x+bpos.x as i32, y: pos.y+bpos.y as i32, z: pos.z+bpos.z as i32};
|
||||
WorldGen::generate(wpos, block);
|
||||
}
|
||||
}
|
||||
self.chunks.insert(pos, chunk);
|
||||
println!("Chunk at {:?} generated", pos);
|
||||
Some(&chunk)
|
||||
}
|
||||
|
||||
pub fn load(&mut self) -> bool {
|
||||
// Get directory
|
||||
let mut dir = dirs::data_local_dir().unwrap();
|
||||
dir.push("Voxelgame");
|
||||
if !dir.is_dir() {
|
||||
println!("Not exists: {:?}", dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load all chunks
|
||||
let mut count = 0;
|
||||
for entry in fs::read_dir(dir).unwrap() {
|
||||
let path: PathBuf = entry.unwrap().path();
|
||||
if path.extension().unwrap().to_str() != Some("dat") {
|
||||
continue;
|
||||
}
|
||||
let stem = path.file_stem().unwrap().to_str().unwrap();
|
||||
if !stem.starts_with("chunk") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let s: Vec<&str> = stem.split("_").collect();
|
||||
let pos = Point3{x: s[1].parse::<u32>().unwrap(), y: s[2].parse::<u32>().unwrap(), z: s[3].parse::<u32>().unwrap()};
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
let mut file = fs::File::open(path).expect("Failed to open file");
|
||||
file.read_to_end(&mut bytes).expect("Failed to read file");
|
||||
let chunk = WorldChunk::from_bytes(bytes);
|
||||
|
||||
println!("Load: Chunk {}, {}, {}", pos.x, pos.y, pos.z);
|
||||
self.chunks.insert(pos, chunk);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
// Load metadata
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
pub fn gen_block(&mut self, block_wpos: &Point3<u32>) -> &WorldBlock {
|
||||
// Split world space to chunk and block space
|
||||
let ch_pos = block_wpos / chunk::SIZE as u32;
|
||||
let bl_pos = block_wpos % chunk::SIZE as u32;
|
||||
// Create chunk if does not exist
|
||||
if let None = self.chunks.get(&ch_pos) {
|
||||
self.chunks.insert(ch_pos.clone(), WorldChunk::new());
|
||||
println!("Alloc: Chunk [{}, {}, {}]", ch_pos.x, ch_pos.y, ch_pos.z);
|
||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name("world.dat");
|
||||
if let Ok(mut file) = fs::File::open(filepath) {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
if let Ok(data) = toml::from_slice::<WorldData>(&buf[..]) {
|
||||
self.data = data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Generate block for chunk
|
||||
let chunk = self.chunks.get_mut(&ch_pos).unwrap();
|
||||
let block = chunk.get_block(&bl_pos);
|
||||
WorldGen::generate(&block_wpos, block);
|
||||
block
|
||||
return false;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_block(&mut self, block_wpos: &Point3<u32>) -> Option<&WorldBlock> {
|
||||
pub fn get_block(&mut self, block_wpos: &Point3<i32>) -> Option<&WorldBlock> {
|
||||
// Split world space to chunk and block space
|
||||
let ch_pos = block_wpos / chunk::SIZE as u32;
|
||||
let bl_pos = block_wpos % chunk::SIZE as u32;
|
||||
let ch_pos = block_wpos / chunk::SIZE as i32;
|
||||
let bl_pos = point_abs(block_wpos) % chunk::SIZE as u32;
|
||||
// Return block if exists
|
||||
if let Some(chunk) = self.chunks.get_mut(&ch_pos) {
|
||||
return Some(chunk.get_block(&bl_pos));
|
||||
|
||||
Reference in New Issue
Block a user