Changed project name (placeholder), some renames

This commit is contained in:
Piotrek
2021-05-10 10:43:14 +02:00
parent 3a45d4e0de
commit 3bd9fa942e
13 changed files with 43 additions and 37 deletions
+126
View File
@@ -0,0 +1,126 @@
use winit::event::Event;
use winit::event::{WindowEvent, DeviceEvent, KeyboardInput, ElementState, VirtualKeyCode};
use cgmath::{InnerSpace, Matrix4, Rad, Deg, Transform, Vector3, Point3};
use crate::renderer::camera::CameraTransform;
#[derive(Default)]
struct CameraMovement {
pub up: bool,
pub down: bool,
pub fwd: bool,
pub back: bool,
pub left: bool,
pub right: bool,
pub shift: bool,
pub speed: f32,
}
#[derive(Default)]
struct CameraRotation {
pub yaw: f32, // vertical axis
pub pitch: f32, // horizontal axis
pub sensitivity: f32,
}
impl CameraRotation {
fn yaw_rad(&mut self) -> Rad<f32> { Rad::from(Deg(self.yaw)) }
fn pitch_rad(&mut self) -> Rad<f32> { Rad::from(Deg(self.pitch)) }
fn yaw_rot(&mut self) -> Matrix4<f32> { Matrix4::from_angle_y(-self.yaw_rad()) }
fn pitch_rot(&mut self)-> Matrix4<f32> { Matrix4::from_angle_x(-self.pitch_rad()) }
}
pub struct CameraController {
enabled: bool,
movement: CameraMovement,
rotation: CameraRotation,
cam_position: Point3<f32>,
}
impl CameraController {
pub fn new() -> Self {
Self {
enabled: false,
movement: CameraMovement { speed: 2.0, .. CameraMovement::default()},
rotation: CameraRotation { yaw: 135.0, sensitivity: 0.2, .. CameraRotation::default() },
cam_position: (-1.0, 2.0, -1.0).into(),
}
}
pub fn set_enabled(&mut self, enabled: bool) { self.enabled = enabled; }
pub fn handle_input(&mut self, event: &Event<()>) -> bool {
// Abort if not enabled
if !self.enabled { return false; }
match event {
Event::WindowEvent { ref event, .. } => {
match event {
WindowEvent::KeyboardInput { input: KeyboardInput { state, virtual_keycode: Some(keycode), .. }, .. } => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Q => { self.movement.down = is_pressed; true }
VirtualKeyCode::E | VirtualKeyCode::Space => { self.movement.up = is_pressed; true }
VirtualKeyCode::W | VirtualKeyCode::Up => { self.movement.fwd = is_pressed; true }
VirtualKeyCode::A | VirtualKeyCode::Left => { self.movement.left = is_pressed; true }
VirtualKeyCode::S | VirtualKeyCode::Down => { self.movement.back = is_pressed; true }
VirtualKeyCode::D | VirtualKeyCode::Right => { self.movement.right = is_pressed; true }
VirtualKeyCode::LShift => { self.movement.shift = is_pressed; true }
_ => false,
}
}
_ => false
}
},
Event::DeviceEvent { device_id: _, event } => {
match event {
DeviceEvent::MouseMotion { delta } => {
// Increment
self.rotation.yaw += delta.0 as f32 * self.rotation.sensitivity;
self.rotation.pitch -= delta.1 as f32 * self.rotation.sensitivity;
// Clamp
if self.rotation.yaw > 360.0 { self.rotation.yaw -= 360.0; }
if self.rotation.yaw < 0.0 { self.rotation.yaw += 360.0; }
self.rotation.pitch = self.rotation.pitch.clamp(-70.0, 70.0);
true
}
_ => false
}
}
_ => false
}
}
pub fn update(&mut self, delta_time: f32, camera: &mut dyn CameraTransform) {
// Abort if not enabled
if !self.enabled { return; }
// Rotation
let yaw_rot = self.rotation.yaw_rot();
let pitch_rot = self.rotation.pitch_rot();
let cam_forward = yaw_rot.transform_vector(pitch_rot.transform_vector(Vector3::unit_z()));
let cam_up = yaw_rot.transform_vector(pitch_rot.transform_vector(Vector3::unit_y()));
// Movement amount
let mut amount = delta_time * self.movement.speed;
if self.movement.shift { amount *= 4.0; }
// Forward and backwards
if self.movement.fwd && cam_forward.magnitude() > amount { self.cam_position += cam_forward.normalize() * amount; }
if self.movement.back { self.cam_position -= cam_forward.normalize() * amount; }
// Up and down
if self.movement.up { self.cam_position += cam_up.normalize() * amount; }
if self.movement.down { self.cam_position -= cam_up.normalize() * amount; }
// Left and right
let right = cam_forward.cross(cam_up);
if self.movement.right { self.cam_position += right * amount; }
if self.movement.left { self.cam_position -= right * amount; }
// Update camerea
camera.update(self.cam_position, cam_forward, cam_up);
}
}
+78
View File
@@ -0,0 +1,78 @@
mod camera_controller;
mod player;
pub mod world;
use std::time::Instant;
use crate::renderer::content_view::ContentView;
use cgmath::{SquareMatrix, Point3, InnerSpace};
use winit::event::Event;
use crate::renderer::{camera::CameraTransform};
use player::Player;
use world::World;
pub enum GameState {
Paused,
Running
}
pub struct Game {
player: Player,
world: World
}
impl Game {
pub fn new(content: &mut dyn ContentView) -> Self {
let mut app = Self {
player: Player::new(),
world: World::new("default".to_string())
};
let timer = Instant::now();
for x in 0..64 {
for z in 0..64 {
let pos = &Point3{x,y:0,z};
let block = app.world.gen_block(pos);
content.write(pos, block);
}
}
println!("Generated in {}", timer.elapsed().as_secs_f32());
let timer = Instant::now();
app.world.save();
println!("Saved in {}", timer.elapsed().as_secs_f32());
// let timer = Instant::now();
// app.world.load();
// for (pos, block) in app.world.all_blocks() {
// content.write(&pos, block);
// }
// println!("Loaded in {}", timer.elapsed().as_secs_f32());
println!("App initialized");
app
}
pub fn input(&mut self, event: &Event<()>) {
// Update camera controller
self.player.camera_controller.handle_input(event);
}
pub fn update_state(&mut self, requested_state: GameState) -> GameState {
match requested_state {
GameState::Paused => {
self.player.camera_controller.set_enabled(false);
}
GameState::Running => {
self.player.camera_controller.set_enabled(true);
}
};
// Currently we are not preventing any state changes, just return requested state
requested_state
}
pub fn update_camera(&mut self, delta: f32, camera_transform: &mut dyn CameraTransform) {
self.player.camera_controller.update(delta, camera_transform);
}
}
+13
View File
@@ -0,0 +1,13 @@
use super::camera_controller::CameraController;
pub struct Player {
pub camera_controller: CameraController
}
impl Player {
pub fn new() -> Self {
Self {
camera_controller: CameraController::new()
}
}
}
+25
View File
@@ -0,0 +1,25 @@
use cgmath::Point3;
use std::iter::FromIterator;
use std::io::Write;
use flate2::{write::DeflateEncoder, Compression};
pub const SIZE: usize = 32;
pub const SIZE_QB: usize = SIZE*SIZE*SIZE;
#[derive(Clone)]
pub struct WorldBlock {
pub materials: [u8;SIZE_QB]
}
impl WorldBlock {
pub fn new() -> Self {
Self {
materials: [0_u8; SIZE_QB]
}
}
pub fn set(&mut self, voxel_pos: Point3<usize>, value: u8) {
self.materials[voxel_pos.x + SIZE * (voxel_pos.y + SIZE * voxel_pos.z)] = value;
}
}
+108
View File
@@ -0,0 +1,108 @@
use std::time::Instant;
use flate2::Compression;
use flate2::write::DeflateEncoder;
use flate2::read::DeflateDecoder;
use byteorder::LittleEndian;
use byteorder::ByteOrder;
use cgmath::Point3;
use std::io::Write;
use std::convert::TryInto;
use std::io::Read;
use crate::game::world::{WorldBlock, block};
pub const SIZE: usize = 32;
const SIZE_SQ: usize = SIZE*SIZE;
const SIZE_QB: usize = SIZE*SIZE*SIZE;
pub struct WorldChunk {
nodes: Box<[u32]>,
blocks: Vec<WorldBlock>
}
impl WorldChunk {
pub fn new() -> Self {
Self {
nodes: vec![0_u32; SIZE_QB as usize].into_boxed_slice(),
blocks: Vec::new()
}
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
let mut instance = Self::new();
let (node_bytes, block_bytes) = bytes.split_at((SIZE_QB*4) as usize);
// Nodes
let mut num_blocks = 0;
for i in 0..SIZE_QB {
let idx = (i*4) as usize;
let val = u32::from_le_bytes([node_bytes[idx], node_bytes[idx+1], node_bytes[idx+2], node_bytes[idx+3]]);
instance.nodes[i as usize] = val;
if val > 0 { num_blocks+=1; }
}
// Blocks
let mut decoder = DeflateDecoder::new(block_bytes);
let mut block_bytes = Vec::with_capacity(SIZE_QB);
decoder.read_to_end(&mut block_bytes).expect("Failed to decode");
for i in 0..num_blocks {
let mut block = WorldBlock::new();
let idx = i * block::SIZE_QB;
block.materials.clone_from_slice(&block_bytes[idx..idx+block::SIZE_QB]);
instance.blocks.push(block);
}
instance
}
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
self.nodes.iter().enumerate()
.filter(|(_, n)| **n != 0)
.map(|(index, node)| {
let mut idx = index;
let z = idx / SIZE_SQ;
idx -= z * SIZE_SQ;
let y = idx / SIZE;
let x = idx % SIZE;
let pos = Point3{x: x as u32, y: y as u32, z: z as u32};
let block = &self.blocks[(*node-1) as usize];
(pos, block)
})
.collect()
}
pub fn get_block(&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];
// Allocate new block
if value == 0 {
value = (self.blocks.len() + 1) as u32;
self.nodes[index] = value;
self.blocks.push(WorldBlock::new());
}
// Return block reference
&mut self.blocks[(value-1) as usize]
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(self.nodes.len()*4 + self.blocks.len()* 32*32*32);
// Nodes
let mut node_bytes = [0_u8; (SIZE_QB*4) as usize];
LittleEndian::write_u32_into(&self.nodes, &mut node_bytes);
bytes.extend(node_bytes.iter());
// Blocks
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::fast());
for block in self.blocks.iter() {
encoder.write_all(&block.materials).unwrap();
}
bytes.extend(encoder.finish().unwrap());
bytes
}
}
+60
View File
@@ -0,0 +1,60 @@
use cgmath::Point3;
use noise::{Perlin, NoiseFn};
use rand::{Rng, prelude::ThreadRng};
use crate::game::world::{block, block::WorldBlock};
const SCALE: f64 = 150.0;
pub struct WorldGen {
}
impl WorldGen {
/*
* Generate height map for given block position
*/
fn gen_height_map(x: usize, z: usize, rand: &mut ThreadRng, noise: &Perlin) -> ([[usize;block::SIZE]; block::SIZE], usize) {
let x = x * block::SIZE;
let z = z * block::SIZE;
// Generate map
let mut max: usize = 0;
let mut map = [[0_usize; block::SIZE]; block::SIZE];
for lx in 0..block::SIZE {
for lz in 0..block::SIZE {
let value = (noise.get([(x + lx) as f64 / SCALE, (z + lz) as f64 /SCALE]) + 1.0) / 2.0;
let mut h = (value * 30.0) as usize;
h += (rand.gen::<f32>() * 2.0) as usize;
if h == 0 { h = 1; }
if h > max { max = h; }
map[lx][lz] = h;
}
}
(map, max)
}
/*
* Generate block
*/
pub fn generate(block_pos: &Point3<u32>, block: &mut WorldBlock) {
let mut rng = rand::thread_rng();
let noise = Perlin::new();
// Generate height map
let (map, max) = WorldGen::gen_height_map(block_pos.x as usize, block_pos.z as usize, &mut rng, &noise);
// Fill
for x in 0..block::SIZE {
for z in 0..block::SIZE {
let h = map[x][z];
//let h = 16; // testing
for y in 0..h {
block.set(Point3{x, y, z}, 2);
}
}
}
}
}
+138
View File
@@ -0,0 +1,138 @@
mod generator;
pub mod chunk;
pub mod block;
use std::time::Instant;
use linked_hash_map::LinkedHashMap;
use std::path::PathBuf;
use std::path::Path;
use std::io::Write;
use std::io::Read;
use std::fs;
use cgmath::{Point3, InnerSpace};
use std::collections::HashMap;
use generator::WorldGen;
use chunk::WorldChunk;
use block::WorldBlock;
use yaml_rust::{Yaml, YamlLoader, YamlEmitter};
pub struct World {
name: String,
chunks: HashMap<Point3<u32>, WorldChunk>,
}
impl World {
pub fn new(name: String) -> Self {
Self {
name,
chunks: HashMap::new(),
}
}
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &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)| {
(
Point3{x: off.x+pos.x, y: off.y+pos.y, z: off.z+pos.z},
*block
)
}).collect();
result
}).flatten().collect()
}
pub fn save(&self) {
// Get directory
let mut dir = dirs::data_local_dir().unwrap();
dir.push("Voxelgame");
fs::create_dir_all(&dir).expect("Failed to create directory");
// Save all chunks
for (pos, chunk) in self.chunks.iter() {
let bytes = chunk.to_bytes();
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
let path: PathBuf = [dir.to_str().unwrap(), &filename].iter().collect();
let mut file = fs::File::create(path).expect("Failed to create file");
file.write(&bytes).unwrap();
}
// 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 mut data_file = fs::File::create(data_path).unwrap();
data_file.write_all(data_str.as_bytes()).expect("Failed to write");
}
pub fn load(&mut self) {
// Get directory
let mut dir = dirs::data_local_dir().unwrap();
dir.push("Voxelgame");
if !dir.is_dir() {
println!("Not exists: {:?}", dir);
return;
}
// Load all chunks
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!("loaded {:?}", pos);
self.chunks.insert(pos, chunk);
}
// Load metadata
}
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);
}
// 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
}
#[allow(unused)]
pub fn get_block(&mut self, block_wpos: &Point3<u32>) -> 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;
// Return block if exists
if let Some(chunk) = self.chunks.get_mut(&ch_pos) {
return Some(chunk.get_block(&bl_pos));
}
None
}
}