From 21295e7eb5df61278702a5641e07dfd4e59fad15 Mon Sep 17 00:00:00 2001 From: jusax23 Date: Tue, 18 Apr 2023 14:17:53 +0200 Subject: [PATCH] init --- .gitignore | 4 + Cargo.lock | 245 +++++++++++++++++ Cargo.toml | 16 ++ assets/shader/default.fsh.glsl | 20 ++ assets/shader/default.vsh.glsl | 26 ++ assets/shader/post.fsh.glsl | 29 ++ assets/shader/post.vsh.glsl | 8 + src/game.rs | 88 ++++++ src/game/block.rs | 53 ++++ src/game/chunk.rs | 113 ++++++++ src/game/world.rs | 245 +++++++++++++++++ src/graphics.rs | 58 ++++ src/jucraft.rs | 108 ++++++++ src/jucraft/default_solid_block.rs | 79 ++++++ src/jucraft/default_solid_shader.rs | 26 ++ src/main.rs | 27 ++ src/player.rs | 218 +++++++++++++++ src/shader.rs | 141 ++++++++++ src/shader/post.rs | 26 ++ src/stage.rs | 400 ++++++++++++++++++++++++++++ src/tools.rs | 71 +++++ src/world_generator.rs | 7 + 22 files changed, 2008 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 assets/shader/default.fsh.glsl create mode 100644 assets/shader/default.vsh.glsl create mode 100644 assets/shader/post.fsh.glsl create mode 100644 assets/shader/post.vsh.glsl create mode 100644 src/game.rs create mode 100644 src/game/block.rs create mode 100644 src/game/chunk.rs create mode 100644 src/game/world.rs create mode 100644 src/graphics.rs create mode 100644 src/jucraft.rs create mode 100644 src/jucraft/default_solid_block.rs create mode 100644 src/jucraft/default_solid_shader.rs create mode 100644 src/main.rs create mode 100644 src/player.rs create mode 100644 src/shader.rs create mode 100644 src/shader/post.rs create mode 100644 src/stage.rs create mode 100644 src/tools.rs create mode 100644 src/world_generator.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..deac3b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +assets/blocks +assets/break +assets/font diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2b447b4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,245 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide 0.6.2", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gl_matrix" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df64d0245c589931a0b5a385a63e7db2aeff209bdd471df0417e0f230a4c33ae" +dependencies = [ + "rand", +] + +[[package]] +name = "jucraft" +version = "0.1.0" +dependencies = [ + "gl_matrix", + "miniquad", + "opensimplex_noise_rs", + "png", + "quad-rand", +] + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "miniquad" +version = "0.3.15" +dependencies = [ + "libc", + "ndk-sys", + "objc", + "winapi", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "opensimplex_noise_rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba77c545f2291bb66897afe90c8583c4fdc427fae9cb552db032e74d7526e8b5" + +[[package]] +name = "png" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "quad-rand" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658fa1faf7a4cc5f057c9ee5ef560f717ad9d8dc66d975267f709624d6e1ab88" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5920b8f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "jucraft" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gl_matrix = "0.0.2" +miniquad = { path = '../../jucraft/miniquad' } +opensimplex_noise_rs = "0.3.0" +png = "0.17.8" +quad-rand = "0.2.1" + +# [replace] +# "miniquad:0.3.14" = { path = '../../jucraft/miniquad' } diff --git a/assets/shader/default.fsh.glsl b/assets/shader/default.fsh.glsl new file mode 100644 index 0000000..b8c2b4e --- /dev/null +++ b/assets/shader/default.fsh.glsl @@ -0,0 +1,20 @@ +#version 100 +precision mediump float; + +varying vec2 texcoord; +varying float v_position; +uniform sampler2D u_image0; +uniform sampler2D u_image1; +uniform vec2 fog; + +void main() { + + float fogAmount = smoothstep(fog.x, fog.y, v_position); + + gl_FragColor = + texture2D(u_image0, texcoord) + * texture2D(u_image1, texcoord) + * vec4(1.0 - fogAmount, 1.0 - fogAmount, 1.0 - fogAmount, 1.0 - fogAmount); + if(gl_FragColor.a < 0.0000001) + discard; +} \ No newline at end of file diff --git a/assets/shader/default.vsh.glsl b/assets/shader/default.vsh.glsl new file mode 100644 index 0000000..8757f94 --- /dev/null +++ b/assets/shader/default.vsh.glsl @@ -0,0 +1,26 @@ +#version 100 +precision mediump float; + +attribute vec3 pos; +attribute vec2 uv; +attribute vec3 wpos; + +varying vec2 texcoord; +varying float v_position; +uniform mat4 view; +uniform mat4 proj; + + +void main() { + mat4 drawProj = mat4( + vec4(-1.0, 0.0, 0.0, 0.0), + vec4(0.0, 1.0, 0.0, 0.0), + vec4(0.0, 0.0, -1.0, 0.0), + vec4(-wpos.x, wpos.y, -wpos.z, 1.0) + ); + texcoord = uv; + + gl_Position = proj * view * drawProj * vec4(pos, 1.0); + + v_position = length((view * drawProj * vec4(pos, 1.0)).xyz); +} \ No newline at end of file diff --git a/assets/shader/post.fsh.glsl b/assets/shader/post.fsh.glsl new file mode 100644 index 0000000..b383f5c --- /dev/null +++ b/assets/shader/post.fsh.glsl @@ -0,0 +1,29 @@ +#version 100 +precision lowp float; +varying vec2 texcoord; +uniform sampler2D tex; +uniform vec2 wobble; +uniform vec3 invert; +uniform vec2 blur; + + + + // Source: https://github.com/Jam3/glsl-fast-gaussian-blur/blob/master/5.glsl +vec4 blur5(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3333333333333333) * direction; + color += texture2D(image, uv) * 0.29411764705882354; + color += texture2D(image, uv + (off1 / resolution)) * 0.35294117647058826; + color += texture2D(image, uv - (off1 / resolution)) * 0.35294117647058826; + return color; +} +void main() { + vec2 offset = wobble.x * vec2( + sin(wobble.y + texcoord.y) * 0.1, + cos(wobble.y + texcoord.x) * 0.1 + ); + vec4 col = texture2D(tex, texcoord + offset * (1.0 - length(texcoord - 0.5) )); + gl_FragColor = (1.0 - col) * vec4(invert, 1.0) + col * (1.0 - vec4(invert, 1.0));//blur5(tex, texcoord, blur, vec2(3.0)); +} + + diff --git a/assets/shader/post.vsh.glsl b/assets/shader/post.vsh.glsl new file mode 100644 index 0000000..a6420fe --- /dev/null +++ b/assets/shader/post.vsh.glsl @@ -0,0 +1,8 @@ +#version 100 +attribute vec2 pos; +attribute vec2 uv; +varying lowp vec2 texcoord; +void main() { + gl_Position = vec4(pos, 0, 1); + texcoord = uv; +} \ No newline at end of file diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..2a411ba --- /dev/null +++ b/src/game.rs @@ -0,0 +1,88 @@ +use crate::{ + tools::{WPos, Sides}, graphics::DrawType, +}; + +use self::{block::BlockType, world::World}; + +pub mod block; +pub mod chunk; +pub mod world; + + +pub struct Game { + worlds: Vec, + types: Vec>, + type_names: Vec, +} + +impl Game { + pub fn new() -> Self { + let mut g = Game { + worlds: vec![], + types: vec![], + type_names: vec![], + }; + g.add_type(Box::new(Air { + id: "air".to_string(), + })); + g + } + + pub fn get_type(&self, id: usize) -> Option<&Box> { + self.types.get(id) + } + + pub fn find_type(&self, id: String) -> Option<(usize, &Box)> { + let index = self.type_names.iter().position(|r| r.eq(&id)); + if let Some(i) = index { + return Some((i,&self.types[i])); + } + None + } + + pub fn do_tick(&mut self, instant: bool) -> usize { + let mut num = 0; + for w in 0..self.worlds.len() { + num += self.worlds[w].do_tick(instant, &self.types); + } + num + } + pub fn do_tex_tick(&mut self) { + for w in 0..self.worlds.len() { + self.worlds[w].do_tex_tick(&self.types); + } + } + pub fn add_type(&mut self, mut block_type: Box) -> usize { + self.type_names.push(block_type.get_id().to_string()); + let index = self.types.len(); + block_type.set_index(index); + self.types.push(block_type); + index + } + pub fn add_world(&mut self) -> usize { + self.worlds.push(World::new()); + self.worlds.len() - 1 + } + pub fn get_world(&mut self, id: usize) -> Option<&mut World> { + self.worlds.get_mut(id) + } + pub fn get_world_im(&self, id: usize) -> Option<&World> { + self.worlds.get(id) + } +} + +#[derive(Debug)] +struct Air { + id: String, +} +impl BlockType for Air { + fn get_id(&self) -> String { + self.id.to_string() + } + fn is_sided(&self) -> bool { + false + } + fn texture(&self) -> &DrawType { + &DrawType::None() + } +} diff --git a/src/game/block.rs b/src/game/block.rs new file mode 100644 index 0000000..aca5b9d --- /dev/null +++ b/src/game/block.rs @@ -0,0 +1,53 @@ +use std::collections::HashMap; + +use crate::{graphics::{Draw, DrawType}, tools::PrimitiveData}; + +use super::{WPos, world::World}; + +#[derive(Debug)] +pub struct Block { + //pub pos: WPos, + pub block_type: usize, + + pub solid: bool, + pub destroy: u8, + + string_state: HashMap, +} + + + +impl Block { + pub fn new(block_type: usize/*, pos: WPos*/) -> Self { + Block { + //pos, + block_type: block_type, + solid: true, + string_state: HashMap::new(), + destroy: 0, + } + } + + pub fn get_state(&self, name: &String) -> Option<&PrimitiveData> { + match self.string_state.get(name) { + Some((data, save)) => Some(data), + None => None, + } + } + + pub fn set_state(&mut self, name: &String, value: PrimitiveData, save: bool) { + self.string_state.insert(name.to_string(), (value, save)); + } +} + + +pub trait BlockType { + fn get_id(&self) -> String; + fn is_sided(&self) -> bool; + + fn set_index(&mut self, _id: usize) {} + fn create(&self, _world: &mut World, _block: WPos, _types: &Vec>, _instant: bool) {} + fn update(&self, _world: &mut World, _block: WPos, _types: &Vec>, _instant: bool) {} + fn render(&self, _world: &mut World, _block: WPos, _types: &Vec>) -> Draw { Draw::None() } + fn texture(&self) -> &DrawType; +} \ No newline at end of file diff --git a/src/game/chunk.rs b/src/game/chunk.rs new file mode 100644 index 0000000..d249189 --- /dev/null +++ b/src/game/chunk.rs @@ -0,0 +1,113 @@ +use std::collections::HashMap; + +use gl_matrix::{common::*, vec2::clone}; + +use crate::{tools::Sides, world_generator::WorldGenerator}; + +use super::{block::Block, WPos}; + +#[derive(Debug)] +pub enum Instances { + Single(Vec), + Sided([Vec; 6]), +} + +#[derive(Debug)] +pub struct Chunk { + pub blocks: HashMap<(u8, u8, u8), Block>, //[[[Block; 16]; 256]; 16], + pub instances: HashMap, +} + +impl Chunk { + pub fn new(_chunk_x: i32, _chunk_z: i32) -> Self { + Self { + #[rustfmt::skip] + blocks: HashMap::new(), + instances: HashMap::new(), + } + } + pub fn new_gen(generator: Box,_chunk_x: i32, _chunk_z: i32) -> Self { + let mut c = Self { + blocks: HashMap::new(), + instances: HashMap::new(), + }; + generator.generateChunk(&mut c); + c + } + + pub fn set_block(&mut self, pos: &WPos, block_type: usize) { + let inner_x = pos[0].rem_euclid(16) as u8; + let inner_z = pos[2].rem_euclid(16) as u8; + self.blocks.insert( + (inner_x, pos[1] as u8, inner_z), + Block::new(block_type/*, *pos*/), + ); + } + + pub fn remove_pos(&mut self, block_type: usize, pos: &WPos) { + if let Some(data) = self.instances.get_mut(&block_type) { + match data { + Instances::Single(d) => { + d.retain(|f| pos[0] != f[0] || pos[1] != f[1] || pos[2] != f[2]); + if d.len() == 0 { + self.instances.remove(&block_type); + } + } + Instances::Sided(d) => { + for i in 0..6 { + d[i].retain(|f| pos[0] != f[0] || pos[1] != f[1] || pos[2] != f[2]); + } + if d[0].len() == 0 + && d[1].len() == 0 + && d[2].len() == 0 + && d[3].len() == 0 + && d[4].len() == 0 + && d[5].len() == 0 + { + self.instances.remove(&block_type); + } + } + } + } + } + pub fn add_tex_pos(&mut self, block_type: usize, pos: &WPos) { + if let Some(inst) = self.instances.get_mut(&block_type) { + if let Instances::Single(vec) = inst { + if !vec.contains(&pos) { + vec.push(pos.clone()); + } + } else { + panic!("Wrong side Type") + } + } else { + self.instances + .insert(block_type, Instances::Single(vec![pos.clone()])); + } + } + pub fn add_tex_pos_sided(&mut self, block_type: usize, pos: &WPos, sides: Sides) { + if let Some(inst) = self.instances.get_mut(&block_type) { + + if let Instances::Sided(vec) = inst { + for i in 0..6 { + if sides[i] { + if !vec[i].contains(&pos) { + vec[i].push(pos.clone()); + } + } else { + vec[i].retain(|f| pos[0] != f[0] || pos[1] != f[1] || pos[2] != f[2]); + } + } + } else { + panic!("Wrong side Type") + } + } else { + let mut new_arr: [Vec<[i32; 3]>; 6] = [vec![], vec![], vec![], vec![], vec![], vec![]]; + for i in 0..6 { + if sides[i] { + new_arr[i].push(pos.clone()); + } + } + self.instances.insert(block_type, Instances::Sided(new_arr)); + } + } +} diff --git a/src/game/world.rs b/src/game/world.rs new file mode 100644 index 0000000..f69fef7 --- /dev/null +++ b/src/game/world.rs @@ -0,0 +1,245 @@ +use std::collections::HashMap; + +use crate::{graphics::Draw, world_generator::WorldGenerator}; + +use super::{block::Block, chunk::Chunk, BlockType, Sides, WPos}; + +pub enum GetBlock { + Some(T), + None, + No, +} + +//#[derive(Debug)] +pub struct World { + pub chunks: HashMap<(i32, i32), Chunk>, + + generator: Box, + + updates: Vec, + i_updates: Vec, + t_updates: Vec, +} + +impl World { + pub fn new() -> Self { + World { + chunks: HashMap::new(), + updates: vec![], + i_updates: vec![], + t_updates: vec![], + generator: Box::new(EmptyGenerator {}), + } + } + + pub fn get_block(&mut self, pos: &WPos) -> GetBlock<&mut Block> { + if pos[1] < 0 || pos[1] > 256 { + return GetBlock::None; + } + let chunk_x = pos[0].div_euclid(16); + let chunk_z = pos[2].div_euclid(16); + + let inner_x = pos[0].rem_euclid(16); + let inner_z = pos[2].rem_euclid(16); + + let chunk = self.chunks.get_mut(&(chunk_x, chunk_z)); + match chunk { + Some(c) => match c + .blocks + .get_mut(&(inner_x as u8, pos[1] as u8, inner_z as u8)) + { + Some(b) => GetBlock::Some(b), + None => GetBlock::No, + }, + None => GetBlock::None, + } + } + pub fn get_block_im(&self, pos: &WPos) -> GetBlock<&Block> { + if pos[1] < 0 || pos[1] > 256 { + return GetBlock::None; + } + let chunk_x = pos[0].div_euclid(16); + let chunk_z = pos[2].div_euclid(16); + + let inner_x = pos[0].rem_euclid(16); + let inner_z = pos[2].rem_euclid(16); + + let chunk = self.chunks.get(&(chunk_x, chunk_z)); + match chunk { + Some(c) => match c.blocks.get(&(inner_x as u8, pos[1] as u8, inner_z as u8)) { + Some(b) => GetBlock::Some(b), + None => GetBlock::No, + }, + None => GetBlock::None, + } + } + + fn loadChunk(&mut self, chunk_x: i32, chunk_z: i32) { + if !self.chunks.contains_key(&(chunk_x, chunk_z)) { + self.chunks + .insert((chunk_x, chunk_z), Chunk::new(chunk_x, chunk_z)); + for x in 0..16 { + for y in 0..256 { + self.update_texture(&[chunk_x * 16 + x, y, chunk_z * 16 - 1]); + self.update_texture(&[chunk_x * 16 + x, y, chunk_z * 16 + 16]); + self.update_texture(&[chunk_x * 16 - 1, y, chunk_z * 16 + x]); + self.update_texture(&[chunk_x * 16 + 16, y, chunk_z * 16 + x]); + } + } + } + } + + pub fn set_block( + &mut self, + pos: &WPos, + block_type: usize, + update: Sides, + update_self: bool, + ) { + if pos[1] < 0 { + return; + } + let chunk_x = pos[0].div_euclid(16); + let chunk_z = pos[2].div_euclid(16); + + //let mut new_c = false; + if !self.chunks.contains_key(&(chunk_x, chunk_z)) { + self.chunks + .insert((chunk_x, chunk_z), Chunk::new(chunk_x, chunk_z)); + //new_c = true; + } + + /*if new_c { + for x in 0..16 { + for y in 0..256 { + self.update(&[chunk_x * 16 + x, y, chunk_z * 16 - 1], false); + self.update(&[chunk_x * 16 + x, y, chunk_z * 16 + 16], false); + self.update(&[chunk_x * 16 - 1, y, chunk_z * 16 + x], false); + self.update(&[chunk_x * 16 + 16, y, chunk_z * 16 + x], false); + } + } + }*/ + + if let Some(chunk) = self.chunks.get_mut(&(chunk_x, chunk_z)) { + chunk.set_block(pos, block_type); + + self.update_texture(&[pos[0], pos[1], pos[2]]); + self.update_texture(&[pos[0] + 1, pos[1], pos[2]]); + self.update_texture(&[pos[0] - 1, pos[1], pos[2]]); + self.update_texture(&[pos[0], pos[1] + 1, pos[2]]); + self.update_texture(&[pos[0], pos[1] - 1, pos[2]]); + self.update_texture(&[pos[0], pos[1], pos[2] + 1]); + self.update_texture(&[pos[0], pos[1], pos[2] - 1]); + + if update_self { + self.update(&[pos[0], pos[1], pos[2]], true); + } + if update.right { + self.update(&[pos[0] - 1, pos[1], pos[2]], false); + } + if update.left { + self.update(&[pos[0] + 1, pos[1], pos[2]], false); + } + if update.top { + self.update(&[pos[0], pos[1] + 1, pos[2]], false); + } + if update.bottom { + self.update(&[pos[0], pos[1] - 1, pos[2]], false); + } + if update.front { + self.update(&[pos[0], pos[1], pos[2] + 1], false); + } + if update.back { + self.update(&[pos[0], pos[1], pos[2] - 1], false); + } + } + } + + pub fn update_texture(&mut self, pos: &WPos) { + self.t_updates.push(pos.clone()); + } + + pub fn update(&mut self, pos: &WPos, instant: bool) { + if instant { + self.i_updates.push(pos.clone()) + } else { + self.updates.push(pos.clone()) + } + } + + pub fn update_surr(&mut self, pos: &WPos, instant: bool) { + self.update(&[pos[0] + 1, pos[1], pos[2]], instant); + self.update(&[pos[0] - 1, pos[1], pos[2]], instant); + self.update(&[pos[0], pos[1] + 1, pos[2]], instant); + self.update(&[pos[0], pos[1] - 1, pos[2]], instant); + self.update(&[pos[0], pos[1], pos[2] + 1], instant); + self.update(&[pos[0], pos[1], pos[2] - 1], instant); + } + + pub fn do_tick(&mut self, instant: bool, types: &Vec>) -> usize { + let upd = if instant { + let o = self.i_updates.clone(); + self.i_updates = vec![]; + o + } else { + let o = self.updates.clone(); + self.updates = vec![]; + o + }; + + for p in 0..upd.len() { + let cord = upd[p].clone(); + if let GetBlock::Some(block) = self.get_block(&cord) { + if let Some(b_t) = types.get(block.block_type) { + b_t.update(self, cord.clone(), types, instant); + } + } + } + upd.len() + } + pub fn do_tex_tick(&mut self, types: &Vec>) { + let upd = self.t_updates.clone(); + self.t_updates = vec![]; + for p in 0..upd.len() { + let pos = upd[p]; + + let chunk_x = pos[0].div_euclid(16); + let chunk_z = pos[2].div_euclid(16); + + if let GetBlock::Some(block) = self.get_block(&pos) { + let block_type_num = block.block_type; + + if let Some(block_type) = types.get(block_type_num) { + let draw = block_type.render(self, pos.clone(), types); + + if let Some(chunk) = self.chunks.get_mut(&(chunk_x, chunk_z)) { + match draw { + Draw::Sides(s) => { + if block_type.is_sided() { + chunk.add_tex_pos_sided(block_type_num, &pos, s); + } else { + chunk.add_tex_pos(block_type_num, &pos); + } + } + Draw::All() => { + if block_type.is_sided() { + chunk.add_tex_pos_sided(block_type_num, &pos, Sides::all(true)); + } else { + chunk.add_tex_pos(block_type_num, &pos); + } + } + Draw::None() => { + chunk.remove_pos(block_type_num, &pos); + } + } + } + } + } + } + } +} + +struct EmptyGenerator {} +impl WorldGenerator for EmptyGenerator { + fn generateChunk(&self, chunk: &mut Chunk) {} +} diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..987a4b5 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,58 @@ +use std::collections::HashMap; + +use miniquad::*; + +use crate::tools::Sides; + +#[derive(Debug)] +pub enum Draw { + All(), //single Model + None(), + Sides(Sides), +} + +pub enum DrawType { + All(Texture), //single Model + None(), + Sides(Sides), +} + +pub struct Graphics { + pub textures: HashMap, + pub pipelines: HashMap, +} + +impl Graphics { + pub fn new() -> Self { + Graphics { textures: HashMap::new(), pipelines: HashMap::new() } + } + pub fn load_texture(&mut self, ctx: &mut Context, bytes: &[u8], id: String) -> Texture { + let decoder = png::Decoder::new(bytes); + let mut reader = decoder.read_info().unwrap(); + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf).unwrap(); + let bytes = &buf[..info.buffer_size()]; + let text = Texture::from_data_and_format( + ctx, + bytes, + TextureParams { + format: TextureFormat::RGBA8, + wrap: TextureWrap::Clamp, + filter: FilterMode::Nearest, + width: info.width, + height: info.height, + }, + ); + + //text.set_filter_min_mag(ctx, FilterMode::Linear, FilterMode::Nearest); + + self.textures.insert(id, text); + + text + } + pub fn addPipeline(&mut self, pipeline: Pipeline, id:String) { + self.pipelines.insert(id, pipeline); + } +} + + diff --git a/src/jucraft.rs b/src/jucraft.rs new file mode 100644 index 0000000..bd645ce --- /dev/null +++ b/src/jucraft.rs @@ -0,0 +1,108 @@ +use crate::{game::*, graphics::*, tools::Sides}; + +use default_solid_block::DefaultSolidBlock; +use miniquad::*; + +mod default_solid_block; +pub mod default_solid_shader; + +pub fn register(ctx: &mut Context, game: &mut Game, graphics: &mut Graphics) { + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/empty_overlay.png")), + "d-1".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_0.png")), + "d0".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_1.png")), + "d1".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_2.png")), + "d2".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_3.png")), + "d3".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_4.png")), + "d4".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_5.png")), + "d5".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_6.png")), + "d6".to_string(), + ); + graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/break/destroy_stage_7.png")), + "d7".to_string(), + ); + let stone_tex = graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/blocks/stone.png")), + "stone".to_string(), + ); + let dirt_tex = graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/blocks/dirt.png")), + "dirt".to_string(), + ); + let cobble_tex = graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/blocks/cobblestone.png")), + "cobblestone".to_string(), + ); + let grass_top = graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/blocks/grass_top.png")), + "grass_top".to_string(), + ); + let grass_side = graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/blocks/grass_side.png")), + "grass_side".to_string(), + ); + let grass_bottom = graphics.load_texture( + ctx, + include_bytes!(concat!("../assets/blocks/grass_bottom.png")), + "grass_bottom".to_string(), + ); + let stone = game.add_type(Box::new(DefaultSolidBlock { + id: "stone".to_string(), + texture: DrawType::All(stone_tex), + })); + let dirt = game.add_type(Box::new(DefaultSolidBlock { + id: "dirt".to_string(), + texture: DrawType::All(dirt_tex), + })); + let cobblestone = game.add_type(Box::new(DefaultSolidBlock { + id: "cobblestone".to_string(), + texture: DrawType::All(cobble_tex), + })); + let grassblock = game.add_type(Box::new(DefaultSolidBlock { + id: "grassblock".to_string(), + texture: DrawType::Sides(Sides { + top: grass_top, + bottom: grass_bottom, + left: grass_side, + right: grass_side, + front: grass_side, + back: grass_side, + }), + })); +} diff --git a/src/jucraft/default_solid_block.rs b/src/jucraft/default_solid_block.rs new file mode 100644 index 0000000..f56123b --- /dev/null +++ b/src/jucraft/default_solid_block.rs @@ -0,0 +1,79 @@ + +use crate::{ + game::{block::*, world::*}, + graphics::*, + tools::*, +}; + +pub struct DefaultSolidBlock { + pub id: String, + pub texture: DrawType, +} +impl BlockType for DefaultSolidBlock { + fn get_id(&self) -> String { + self.id.to_string() + } + fn create( + &self, + world: &mut World, + block: WPos, + _types: &Vec>, + _instant: bool, + ) { + if let GetBlock::Some(b) = world.get_block(&block) { + if !b.solid { + b.solid = true; + } + } + } + fn render( + &self, + world: &mut World, + block: WPos, + _types: &Vec>, + ) -> Draw { + let mut sides = Sides::all(true); + sides.top = match world.get_block(&[block[0], block[1] + 1, block[2]]) { + GetBlock::Some(bt) => !bt.solid, + GetBlock::None => false, + GetBlock::No => true, + }; + sides.bottom = match world.get_block(&[block[0], block[1] - 1, block[2]]) { + GetBlock::Some(bt) => !bt.solid, + GetBlock::None => false, + GetBlock::No => true, + }; + sides.right = match world.get_block(&[block[0] - 1, block[1], block[2]]) { + GetBlock::Some(bt) => !bt.solid, + GetBlock::None => false, + GetBlock::No => true, + }; + sides.left = match world.get_block(&[block[0] + 1, block[1], block[2]]) { + GetBlock::Some(bt) => !bt.solid, + GetBlock::None => false, + GetBlock::No => true, + }; + sides.front = match world.get_block(&[block[0], block[1], block[2] - 1]) { + GetBlock::Some(bt) => !bt.solid, + GetBlock::None => false, + GetBlock::No => true, + }; + sides.back = match world.get_block(&[block[0], block[1], block[2] + 1]) { + GetBlock::Some(bt) => !bt.solid, + GetBlock::None => false, + GetBlock::No => true, + }; + if sides.are_all(false) { + Draw::None() + } else { + Draw::Sides(sides) + } + } + fn texture(&self) -> &DrawType { + &self.texture + } + + fn is_sided(&self) -> bool { + true + } +} diff --git a/src/jucraft/default_solid_shader.rs b/src/jucraft/default_solid_shader.rs new file mode 100644 index 0000000..589cefa --- /dev/null +++ b/src/jucraft/default_solid_shader.rs @@ -0,0 +1,26 @@ +use gl_matrix::common::*; +use miniquad::*; + +pub const VERTEX: &str = include_str!(concat!("../../assets/shader/default.vsh.glsl")); + +pub const FRAGMENT: &str = include_str!(concat!("../../assets/shader/default.fsh.glsl")); + +pub fn meta() -> ShaderMeta { + ShaderMeta { + images: vec!["u_image0".to_string(), "u_image1".to_string()], + uniforms: UniformBlockLayout { + uniforms: vec![ + UniformDesc::new("view", UniformType::Mat4), + UniformDesc::new("proj", UniformType::Mat4), + UniformDesc::new("fog", UniformType::Float2), + ], + }, + } +} + +#[repr(C)] +pub struct Uniforms { + pub view: Mat4, + pub proj: Mat4, + pub fog: Vec2, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4b5b34e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,27 @@ +use miniquad::*; +use stage::Stage; + +mod game; +mod shader; +mod stage; +mod graphics; +mod tools; +mod jucraft; +mod player; +mod world_generator; + +fn main() { + start(conf::Conf{ + window_title: "Jucraft".to_string(), + window_height: 900, + window_width: 900* 16 / 9, + ..Default::default() + + }, |mut ctx| { + Box::new(Stage::new(&mut ctx)) + }); +} + + +// to read: for Map generation: https://www.redblobgames.com/maps/terrain-from-noise/ +// with opensimplex_noise_rs crate \ No newline at end of file diff --git a/src/player.rs b/src/player.rs new file mode 100644 index 0000000..badd171 --- /dev/null +++ b/src/player.rs @@ -0,0 +1,218 @@ +use gl_matrix::{common::*, *}; + +use crate::{ + game::{world::GetBlock, Game}, + tools::*, +}; + +pub struct Player { + pub rot: Vec2, + pub pos: Vec3, + pub motion: Vec3, + pub eyes: f32, + pub size: Vec3, + pub flying: bool, + pub max_speed: f32, + pub world: usize, + + // retundant + pub speed: f32, + pub on_gound: bool, +} + +impl Player { + pub fn new(rot: Vec2, pos: Vec3) -> Self { + Self { + rot, + pos, + motion: [0., 0., 0.], + eyes: 1.7, + size: [0.8, 1.99, 0.8], + flying: true, + max_speed: 10., + world: 0, + + speed: 0., + on_gound: false, + } + } + pub fn update(&mut self, game: &mut Game, s_move: &Sides, delta: f32) { + if self.flying { + if s_move.top { + self.motion[1] += delta * 100.0; + } + if s_move.bottom { + self.motion[1] -= delta * 100.0; + } + let decal = 70. * delta; + if self.motion[1].abs() > decal { + if self.motion[1] > 0. { + self.motion[1] -= decal; + } else { + self.motion[1] += decal; + } + } else { + self.motion[1] = 0.; + } + } else { + if self.on_gound && s_move.top { + self.motion[1] = 10.; + } + self.motion[1] -= delta * 30.; + if self.motion[1].abs() > 30. { + if self.motion[1] > 0. { + self.motion[1] = 30.; + } else { + self.motion[1] = -30.; + } + } + } + + let decalerate: f32 = if self.on_gound || self.flying { + 50. + } else { + 1. + }; + let acceleration: f32 = if self.on_gound || self.flying { + 100. * 50. + } else { + 10. + 1. + }; + + if s_move.front { + self.motion[2] += self.rot[1].to_radians().cos() * delta * acceleration; + self.motion[0] -= self.rot[1].to_radians().sin() * delta * acceleration; + } + if s_move.back { + self.motion[2] -= self.rot[1].to_radians().cos() * delta * acceleration; + self.motion[0] += self.rot[1].to_radians().sin() * delta * acceleration; + } + if s_move.left { + self.motion[2] += self.rot[1].to_radians().sin() * delta * acceleration; + self.motion[0] += self.rot[1].to_radians().cos() * delta * acceleration; + } + if s_move.right { + self.motion[2] -= self.rot[1].to_radians().sin() * delta * acceleration; + self.motion[0] -= self.rot[1].to_radians().cos() * delta * acceleration; + } + + self.speed = (self.motion[0].powi(2) + self.motion[2].powi(2)).sqrt(); + + if self.speed > self.max_speed { + let modifier = self.max_speed / self.speed; + self.motion[0] = self.motion[0] * modifier; + self.motion[2] = self.motion[2] * modifier; + } else { + if self.speed > 0.1 { + let modi_x = delta * decalerate * self.motion[0] / self.speed; + if (self.motion[0] > 0.) == (self.motion[0] - modi_x > 0.) { + self.motion[0] -= modi_x; + } else { + self.motion[0] = 0.; + } + + let modi_z = delta * decalerate * self.motion[2] / self.speed; + if (self.motion[2] > 0.) == (self.motion[2] - modi_z > 0.) { + self.motion[2] -= modi_z; + } else { + self.motion[2] = 0.; + } + } else { + self.motion[0] = 0.; + self.motion[2] = 0.; + } + } + + //self.pos[0] += self.motion[0] * delta; + //self.pos[1] += self.motion[1] * delta; + //self.pos[2] += self.motion[2] * delta; + self.on_gound = false; + for cord in 0..3 { + let mut dist = self.motion[cord] * delta; + while dist.abs() > 0. { + let do_dist = if dist.abs() < 0.5 { + let d = dist; + dist = 0.; + d + } else { + if dist > 0. { + dist -= 0.5; + 0.5 + } else { + dist += 0.5; + -0.5 + } + }; + + if self.is_clipped(game).is_none() { + self.pos[cord] += do_dist; + if let Some((b_pos, b_size)) = self.is_clipped(game) { + let self_offset = if cord == 1 { 0. } else { self.size[cord] / 2. }; + if do_dist > 0. { + self.pos[cord] = b_pos[cord] - self_offset - 0.001; + } else if do_dist < 0. { + self.pos[cord] = b_pos[cord] + b_size[cord] + self_offset + 0.001; + if cord == 1 { + self.on_gound = true; + } + } + self.motion[cord] = 0.; + } + } else { + self.pos[cord] += do_dist; + } + } + } + } + + pub fn is_clipped(&self, game: &mut Game) -> Option<(Vec3, Vec3)> { + if let Some(world) = game.get_world_im(self.world) { + let mx = self.pos[0].floor() as i32; + let my = self.pos[1].floor() as i32; + let mz = self.pos[2].floor() as i32; + let vx = (self.size[0] / 2.).ceil() as i32; + let vy = (self.size[1]).ceil() as i32; + let vz = (self.size[2] / 2.).ceil() as i32; + for x in mx - vx..=mx + vy { + for z in mz - 1..=mz + vz { + for y in my - vy..=my + vy { + if let GetBlock::Some(_block) = world.get_block_im(&[x, y, z]) { + // TODO: block size + //println!("inner clip"); + if check_overlap( + [self.pos[0], self.pos[1] + (self.size[1] / 2.0), self.pos[2]], + [ + x as f32 + 1.0 / 2.0, + y as f32 + 1.0 / 2.0, + z as f32 + 1.0 / 2.0, + ], + self.size, + [1., 1., 1.], + ) { + return Some(([x as f32, y as f32, z as f32], [1., 1., 1.])); + } + } + } + } + } + } + None + } + + pub fn rotate(&mut self, x: f32, y: f32) { + self.rot[0] -= y / 10.0; + self.rot[1] += x / 10.0; + while self.rot[0] > 90.0 { + self.rot[0] = 90.; + } + while self.rot[0] < -90.0 { + self.rot[0] = -90.; + } + while self.rot[1] > 180.0 { + self.rot[1] -= 360.; + } + while self.rot[1] < -180.0 { + self.rot[1] += 360.; + } + } +} diff --git a/src/shader.rs b/src/shader.rs new file mode 100644 index 0000000..d644c5d --- /dev/null +++ b/src/shader.rs @@ -0,0 +1,141 @@ +use std::f64::consts::PI; + +use gl_matrix::{common::*, *}; +use miniquad::*; + +pub mod post; + +pub struct RenderHelper { + post_processing_pipeline: Pipeline, + post_processing_bind: Bindings, + offscreen_pass: RenderPass, + post_enabled: bool, +} + +impl RenderHelper { + pub fn new(ctx: &mut Context) -> Self { + let (w, h) = ctx.screen_size(); + let color_img = Texture::new_render_texture( + ctx, + TextureParams { + width: w as _, + height: h as _, + format: TextureFormat::RGBA8, + ..Default::default() + }, + ); + let depth_img = Texture::new_render_texture( + ctx, + TextureParams { + width: w as _, + height: h as _, + format: TextureFormat::Depth, + ..Default::default() + }, + ); + let offscreen_pass = RenderPass::new(ctx, color_img, depth_img); + + #[rustfmt::skip] + let post_vertices: &[f32] = &[ + /* pos uvs */ + -1.0, -1.0, 0.1, 0.1, + 1.0, -1.0, 0.9, 0.1, + 1.0, 1.0, 0.9, 0.9, + -1.0, 1.0, 0.1, 0.9, + ]; + + let post_vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, &post_vertices); + + let post_indices: &[u16] = &[0, 1, 2, 0, 2, 3]; + + let post_index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, &post_indices); + + let post_processing_bind = Bindings { + vertex_buffers: vec![post_vertex_buffer], + index_buffer: post_index_buffer, + images: vec![color_img], + }; + let post_shader = Shader::new(ctx, post::VERTEX, post::FRAGMENT, post::meta()).unwrap(); + + let post_processing_pipeline = Pipeline::new( + ctx, + &[BufferLayout::default()], + &[ + VertexAttribute::new("pos", VertexFormat::Float2), + VertexAttribute::new("uv", VertexFormat::Float2), + ], + post_shader, + ); + + Self { + post_processing_pipeline, + post_processing_bind, + offscreen_pass, + post_enabled: true, + } + } + + pub fn resize(&mut self, ctx: &mut Context, width: f32, height: f32) { + let color_img = Texture::new_render_texture( + ctx, + TextureParams { + width: width as _, + height: height as _, + format: TextureFormat::RGBA8, + ..Default::default() + }, + ); + let depth_img = Texture::new_render_texture( + ctx, + TextureParams { + width: width as _, + height: height as _, + format: TextureFormat::Depth, + ..Default::default() + }, + ); + + let offscreen_pass = RenderPass::new(ctx, color_img, depth_img); + + self.offscreen_pass.delete(ctx); + self.offscreen_pass = offscreen_pass; + self.post_processing_bind.images[0] = color_img; + } + + pub fn begin_world(&self, ctx: &mut Context) { + if self.post_enabled { + ctx.begin_pass( + self.offscreen_pass, + PassAction::clear_color(0.75, 0.85, 0.8, 1.0), + ); + } else { + ctx.begin_default_pass(PassAction::clear_color(0.75, 0.85, 0.8, 1.0)); + } + } + + pub fn do_post_begin_ui(&self, ctx: &mut Context) { + ctx.end_render_pass(); + if self.post_enabled { + let t = date::now(); + ctx.begin_default_pass(PassAction::Nothing); + ctx.apply_pipeline(&self.post_processing_pipeline); + ctx.apply_bindings(&self.post_processing_bind); + + let invert = ((t).sin()/2.0 + 0.5) as f32; + ctx.apply_uniforms(&post::Uniforms { + wobble: [1., ((t * 6.0) % (2.0 * PI)) as f32], + invert: [invert, invert, invert], + blur: [1., 1.], + }); + ctx.draw(0, 6, 1); + + ctx.end_render_pass(); + } + ctx.begin_default_pass(PassAction::Nothing); + } + + pub fn end_ui(&self, ctx: &mut Context) { + ctx.end_render_pass(); + ctx.commit_frame(); + } +} diff --git a/src/shader/post.rs b/src/shader/post.rs new file mode 100644 index 0000000..d5b2d00 --- /dev/null +++ b/src/shader/post.rs @@ -0,0 +1,26 @@ +use gl_matrix::common::*; +use miniquad::*; + +pub const VERTEX: &str = include_str!(concat!("../../assets/shader/post.vsh.glsl")); + +pub const FRAGMENT: &str = include_str!(concat!("../../assets/shader/post.fsh.glsl")); + +pub fn meta() -> ShaderMeta { + ShaderMeta { + images: vec!["tex".to_string()], + uniforms: UniformBlockLayout { + uniforms: vec![ + UniformDesc::new("wobble", UniformType::Float2), + UniformDesc::new("invert", UniformType::Float3), + UniformDesc::new("blur", UniformType::Float2), + ], + }, + } +} + +#[repr(C)] +pub struct Uniforms { + pub wobble: Vec2, + pub invert: Vec3, + pub blur: Vec2, +} diff --git a/src/stage.rs b/src/stage.rs new file mode 100644 index 0000000..09cd402 --- /dev/null +++ b/src/stage.rs @@ -0,0 +1,400 @@ + +use gl_matrix::{mat4, common::*}; +use miniquad::*; + +use crate::{game::{Game, chunk::*}, graphics::{Graphics, DrawType}, jucraft::{*, self}, shader::RenderHelper, tools::*, player::Player}; + +const MAX_INSTANT_TICKS: i32 = 50; + +pub struct Stage { + game: Game, + graphics: Graphics, + + render_helper: RenderHelper, + + last_tick: f64, + delta: f64, + last_frame: f64, + + pipeline: Pipeline, + bindings: Bindings, + + + fullscreen: bool, + player: Player, + mouse_grab: bool, + s_move: Sides, + + x: i32, + y: i32, + z: i32, + + t_fp:f64, +} + +impl Stage { + pub fn new(ctx: &mut Context) -> Stage { + let mut game = Game::new(); + let mut graphics = Graphics::new(); + let world = game.add_world(); + + jucraft::register(ctx,&mut game, &mut graphics); + + let (grass,_) = game.find_type("grassblock".to_string()).unwrap(); + let (stone,_) = game.find_type("stone".to_string()).unwrap(); + let (cobblestone,_) = game.find_type("cobblestone".to_string()).unwrap(); + + if let Some(world) = game.get_world(world){ + world.set_block(&[162,65,161], cobblestone, Sides::all(true), true); + world.set_block(&[162,64,161], grass, Sides::all(true), true); + world.set_block(&[162,64,162], grass, Sides::all(true), true); + + world.set_block(&[163,64,161], stone, Sides::all(true), true); + world.set_block(&[161,64,161], stone, Sides::all(true), true); + } + + let mut player = Player::new( + [0.,0.], + [16.*10.,63.,16.*10.], + ); + player.world = world; + + let post = RenderHelper::new(ctx); + + #[rustfmt::skip] + let vertices = vec![ + Vertex{pos:[0.0, 0.0, 1.0], uv:[0.0, 1.0]}, + Vertex{pos:[1.0, 0.0, 1.0], uv:[1.0, 1.0]}, + Vertex{pos:[0.0, 1.0, 1.0], uv:[0.0, 0.0]}, + Vertex{pos:[1.0, 1.0, 1.0], uv:[1.0, 0.0]}, + + Vertex{pos:[0.0, 0.0, 0.0], uv:[0.0, 1.0]}, + Vertex{pos:[0.0, 0.0, 1.0], uv:[1.0, 1.0]}, + Vertex{pos:[0.0, 1.0, 0.0], uv:[0.0, 0.0]}, + Vertex{pos:[0.0, 1.0, 1.0], uv:[1.0, 0.0]}, + + Vertex{pos:[1.0, 0.0, 0.0], uv:[0.0, 1.0]}, + Vertex{pos:[0.0, 0.0, 0.0], uv:[1.0, 1.0]}, + Vertex{pos:[1.0, 1.0, 0.0], uv:[0.0, 0.0]}, + Vertex{pos:[0.0, 1.0, 0.0], uv:[1.0, 0.0]}, + + Vertex{pos:[1.0, 0.0, 1.0], uv:[0.0, 1.0]}, + Vertex{pos:[1.0, 0.0, 0.0], uv:[1.0, 1.0]}, + Vertex{pos:[1.0, 1.0, 1.0], uv:[0.0, 0.0]}, + Vertex{pos:[1.0, 1.0, 0.0], uv:[1.0, 0.0]}, + + Vertex{pos:[0.0, 1.0, 0.0], uv:[0.0, 1.0]}, + Vertex{pos:[0.0, 1.0, 1.0], uv:[1.0, 1.0]}, + Vertex{pos:[1.0, 1.0, 0.0], uv:[0.0, 0.0]}, + Vertex{pos:[1.0, 1.0, 1.0], uv:[1.0, 0.0]}, + + Vertex{pos:[0.0, 0.0, 0.0], uv:[0.0, 1.0]}, + Vertex{pos:[1.0, 0.0, 0.0], uv:[1.0, 1.0]}, + Vertex{pos:[0.0, 0.0, 1.0], uv:[0.0, 0.0]}, + Vertex{pos:[1.0, 0.0, 1.0], uv:[1.0, 0.0]} + ]; + let vertex_buffer = Buffer::immutable(ctx, BufferType::VertexBuffer, &vertices); + + let indices = vec![ + 16, 17, 18, // top + 19, 18, 17, + + 20, 21, 22, // bottom + 23, 22, 21, + + 12, 13, 14, // left + 15, 14, 13, + + 4, 5, 6, //right + 7, 6, 5, + + 8, 9, 10, // front + 11, 10, 9, + + 0, 1, 2, // back + 3, 2, 1, + + ]; + let index_buffer = Buffer::immutable(ctx, BufferType::IndexBuffer, &indices); + + let positions_vertex_buffer = Buffer::stream( + ctx, + BufferType::VertexBuffer, + 16*16*256 * std::mem::size_of::(), + ); + + let bindings = Bindings { + vertex_buffers: vec![vertex_buffer, positions_vertex_buffer], + index_buffer: index_buffer, + images: vec![graphics.textures["stone"], graphics.textures["d-1"]], + }; + + let shader = Shader::new(ctx, default_solid_shader::VERTEX, default_solid_shader::FRAGMENT, default_solid_shader::meta()).unwrap(); + + let pipeline = Pipeline::with_params( + ctx, + &[BufferLayout::default(), BufferLayout { + step_func: VertexStep::PerInstance, + ..Default::default() + },], + &[ + VertexAttribute::with_buffer("pos", VertexFormat::Float3,0), + VertexAttribute::with_buffer("uv", VertexFormat::Float2,0), + VertexAttribute::with_buffer("wpos", VertexFormat::Int3, 1), + ], + shader, + PipelineParams { + depth_test: Comparison::LessOrEqual, + depth_write: true, + cull_face: CullFace::Back, + front_face_order: FrontFaceOrder::CounterClockwise, + color_blend: Some(BlendState::new(Equation::Add, BlendFactor::One, BlendFactor::OneMinusValue(BlendValue::SourceAlpha))), + alpha_blend: Some(BlendState::new(Equation::Add, BlendFactor::One, BlendFactor::Zero)), + ..Default::default() + } + ); + + println!("created"); + + Self { + game, + graphics, + last_tick: date::now(), + delta: 0.1, + last_frame: date::now(), + render_helper: post, + pipeline, + bindings, + + fullscreen: false, + + player, + + mouse_grab: false, + s_move: Sides::all(false), + x: 2, + y: 1, + z: 1, + + t_fp: 20., + } + } +} +impl EventHandler for Stage { + fn update(&mut self, _ctx: &mut Context) { + let t = date::now(); + self.delta = t - self.last_frame; + self.last_frame = t; + + let mut l_a = 1; + let mut count = 0; + while l_a > 0 { + if count > MAX_INSTANT_TICKS { + eprintln!("Instant tick overflow"); + break; + } + l_a = self.game.do_tick(true); + count += 1; + } + if self.last_tick < t - 1. / 20. { + self.last_tick += 1. / 20.; + self.game.do_tick(false); + + //eprintln!("fps: {}|{}", 1. / self.delta, self.t_fp); + + if self.last_tick < t - 5. { + self.last_tick = t - 1. / 20.; + } + } + self.game.do_tex_tick(); + + self.player.update(&mut self.game, &self.s_move, self.delta as f32); + + + let (index,_) = self.game.find_type("stone".to_string()).unwrap(); + + if self.z < 16*20 { + if let Some(world) = self.game.get_world(0) { + for _ in 0..10000 { + if self.z >= 16*20 {break;} + world.set_block(&[self.x, self.y, self.z], index+((self.x+ self.y + self.z) as usize)%4, Sides::all(true), true); + self.x+=1; + if self.x >= 16*20 { + self.x = 0; + self.y += 1; + if self.y > 63{ + self.y = 0; + self.z += 1; + } + } + } + + } + } + } + + fn draw(&mut self, ctx: &mut Context) { + self.render_helper.begin_world(ctx); + + let t = date::now(); + + ctx.apply_pipeline(&self.pipeline); + ctx.apply_bindings(&self.bindings); + + let (w,h) = ctx.screen_size(); + let mut proj_matrix = mat4::create(); + mat4::perspective(&mut proj_matrix, 90.0f32.to_radians(), w as f32 / h as f32, 0.1, Some(10000.0)); + + let mut cammat = mat4::create(); + let mut cammat1 = mat4::create(); + + mat4::identity(&mut cammat); + mat4::rotate_x(&mut cammat1, &cammat, -(self.player.rot[0] / 180. * PI)); + mat4::rotate_y(&mut cammat, &cammat1, self.player.rot[1] / 180. * PI); + mat4::translate(&mut cammat1, &cammat, &[self.player.pos[0], -self.player.pos[1]- self.player.eyes, self.player.pos[2]]); + + let mut view_matrix = mat4::create(); + mat4::look_at(&mut view_matrix, &[0., 0., 0.], &[0., 0., 0.], &[0., 0., 0.]); + mat4::multiply(&mut cammat, &cammat1, &view_matrix); + + ctx.apply_uniforms(&default_solid_shader::Uniforms { + proj: proj_matrix, + view: cammat, + fog: [140.0, 160.0] + }); + + if let Some(world) = self.game.get_world_im(self.player.world){ + for (_,chunk) in &world.chunks { + for (b_type, inst) in &chunk.instances{ + if let Some(b_t) = self.game.get_type(*b_type){ + let tex = b_t.texture(); + if let DrawType::All(ttt) = tex { + self.bindings.images[0] = *ttt; + ctx.apply_bindings(&self.bindings); + } + match inst { + Instances::Single(c) => { + if c.len() > 0{ + self.bindings.vertex_buffers[1].update(ctx, &c); + ctx.draw(0, 36, c.len() as i32); + } + }, + Instances::Sided(s) => { + for i in 0..6 { + let len = s[i].len() as i32; + if len > 0{ + if let DrawType::Sides(ttt) = tex { + self.bindings.images[0] = ttt[i]; + ctx.apply_bindings(&self.bindings); + } + self.bindings.vertex_buffers[1].update(ctx, &s[i]); + ctx.draw(6 * i as i32, 6, len); + } + } + }, + } + } + + } + } + } + + self.render_helper.do_post_begin_ui(ctx); + + let end = date::now(); + + self.t_fp = self.t_fp *0.9 + 0.1/(end-t); + + // TODO: render 2D UI + + self.render_helper.end_ui(ctx); + } + + fn resize_event(&mut self, ctx: &mut Context, width: f32, height: f32) { + self.render_helper.resize(ctx, width, height); + } + + fn key_down_event(&mut self, _ctx: &mut Context, keycode: KeyCode, _keymods: KeyMods,_repeatt: bool) { + if keycode == KeyCode::Space{ + self.s_move.top = true; + }else if keycode == KeyCode::LeftShift{ + self.s_move.bottom = true; + }else if keycode == KeyCode::W{ + self.s_move.front = true; + }else if keycode == KeyCode::A{ + self.s_move.left = true; + }else if keycode == KeyCode::S{ + self.s_move.back = true; + }else if keycode == KeyCode::D{ + self.s_move.right = true; + } + } + + fn key_up_event(&mut self, ctx: &mut Context, keycode: KeyCode, _keymods: KeyMods) { + + if keycode == KeyCode::F11 { + if self.fullscreen { + ctx.set_fullscreen(false); + ctx.set_window_size(900* 16 / 9, 900); + self.fullscreen = false; + }else { + ctx.set_fullscreen(true); + self.fullscreen = true; + } + if self.mouse_grab { + //println!("reloc"); + ctx.set_cursor_grab(false); + ctx.show_mouse(true); + self.mouse_grab = false; + } + }else if keycode == KeyCode::Escape { + ctx.set_cursor_grab(false); + ctx.show_mouse(true); + self.mouse_grab = false; + + }else if keycode == KeyCode::Space{ + self.s_move.top = false; + }else if keycode == KeyCode::LeftShift{ + self.s_move.bottom = false; + }else if keycode == KeyCode::W{ + self.s_move.front = false; + }else if keycode == KeyCode::A{ + self.s_move.left = false; + }else if keycode == KeyCode::S{ + self.s_move.back = false; + }else if keycode == KeyCode::D{ + self.s_move.right = false; + }else if keycode == KeyCode::F{ + self.player.flying = !self.player.flying; + } + + } + + fn window_minimized_event(&mut self, ctx: &mut Context) { + ctx.set_cursor_grab(false); + ctx.show_mouse(true); + self.mouse_grab = false; + } + + fn mouse_button_down_event(&mut self,ctx: &mut Context,button: MouseButton,_x: f32,_y: f32) { + if button == MouseButton::Left { + if !self.mouse_grab { + ctx.set_cursor_grab(true); + ctx.show_mouse(false); + self.mouse_grab = true; + } + + + } + } + + fn raw_mouse_motion(&mut self, _ctx: &mut Context, x: f32, y: f32) { + if self.mouse_grab { + self.player.rotate(x, y); + } + } + + fn touch_event(&mut self, ctx: &mut Context, phase: TouchPhase, _id: u64, x: f32, y: f32) { + + } +} diff --git a/src/tools.rs b/src/tools.rs new file mode 100644 index 0000000..7ab09c1 --- /dev/null +++ b/src/tools.rs @@ -0,0 +1,71 @@ +use std::ops::Index; + +use gl_matrix::common::*; + +pub type WPos = [i32; 3]; + +#[derive(Debug)] +pub struct Sides { + pub top: T, + pub bottom: T, + pub left: T, + pub right: T, + pub front: T, + pub back: T, +} + +impl Sides { + pub fn all(v: T) -> Sides { + Sides { + top: v, + bottom: v, + left: v, + right: v, + front: v, + back: v, + } + } + pub fn are_all(&self, v: T) -> bool { + self.top == v + && self.bottom == v + && self.left == v + && self.right == v + && self.front == v + && self.back == v + } +} + +impl Index for Sides { + type Output = T; + + fn index(&self, i: usize) -> &Self::Output { + match i { + 0 => {&self.top}, + 1 => {&self.bottom}, + 2 => {&self.left}, + 3 => {&self.right}, + 4 => {&self.front}, + 5 => {&self.back}, + _ => {panic!("SidesOutOfBoundsException");} + } + } +} + +#[repr(C)] +pub struct Vertex { + pub pos: Vec3, + pub uv: Vec2, +} + +pub fn check_overlap(pos1: Vec3, pos2: Vec3, size1: Vec3, size2: Vec3) -> bool { + (pos1[0] - pos2[0]).abs() < (size1[0] + size2[0]) / 2. + && (pos1[1] - pos2[1]).abs() < (size1[1] + size2[1]) / 2. + && (pos1[2] - pos2[2]).abs() < (size1[2] + size2[2]) / 2. +} + +#[derive(Debug)] +pub enum PrimitiveData { + String(String), + Number(i32), + Boolean(bool) +} \ No newline at end of file diff --git a/src/world_generator.rs b/src/world_generator.rs new file mode 100644 index 0000000..a526e01 --- /dev/null +++ b/src/world_generator.rs @@ -0,0 +1,7 @@ +use crate::game::chunk::Chunk; + + + +pub trait WorldGenerator { + fn generateChunk(&self, chunk: &mut Chunk); +} \ No newline at end of file