diff --git a/Cargo.lock b/Cargo.lock index 57dc6bd..4fc68a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,7 @@ name = "steven" version = "0.0.1" dependencies = [ "byteorder 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "cgmath 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -74,6 +75,16 @@ dependencies = [ "libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cgmath" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cocoa" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index f329edc..6f7c829 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ time = "0.1.32" rand = "0.3.11" rustc-serialize = "0.3" log = "0.3.2" +cgmath = "0.3.1" [dependencies.steven_gl] path = "./gl" diff --git a/src/esc.rs b/src/esc.rs new file mode 100644 index 0000000..157437f --- /dev/null +++ b/src/esc.rs @@ -0,0 +1,121 @@ +use std::any::*; +use std::collections::HashMap; +use std::collections::hash_state::HashState; +use std::hash::Hasher; +use std::ptr; +use std::mem; + +fn main() { + let mut c = Container::new(); + { + c.put("Hello world"); + println!("{}", c.get::<&str>()); + } + { + c.put(55); + println!("{}", c.get::()); + } + println!("{}", c.get::<&str>()); + println!("{}", c.get::()); + + { + *c.get_mut::() = 82; + } + + println!("{}", c.get::<&str>()); + println!("{}", c.get::()); +} + +struct TypeIdState; + +impl HashState for TypeIdState { + type Hasher = TypeIdHasher; + + fn hasher(&self) -> TypeIdHasher { + TypeIdHasher { value: 0 } + } +} + +struct TypeIdHasher { + value: u64, +} + +impl Hasher for TypeIdHasher { + fn finish(&self) -> u64 { + self.value + } + fn write(&mut self, bytes: &[u8]) { + unsafe { + ptr::copy_nonoverlapping(&mut self.value, mem::transmute(&bytes[0]), 1) + } + } +} + +pub struct Container { + elems: HashMap, TypeIdState>, +} + +impl Container { + pub fn new() -> Container { + Container { + elems: HashMap::with_hash_state(TypeIdState), + } + } + + pub fn put(&mut self, data: T) { + self.elems.insert( + TypeId::of::(), + Box::new(data) + ); + } + + pub fn get(&self) -> &T { + self.elems.get(&TypeId::of::()).unwrap().downcast_ref::().unwrap() + } + + pub fn get_mut(&mut self) -> & mut T { + self.elems.get_mut(&TypeId::of::()).unwrap().downcast_mut::().unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use test::Bencher; + use test; + + #[bench] + fn bench_put(b: &mut Bencher) { + let mut c = Container::new(); + b.iter(|| c.put("Hello world")); + } + + #[bench] + fn bench_get(b: &mut Bencher) { + let mut c = Container::new(); + c.put("Hello world"); + c.put(55); + b.iter(|| c.get::<&str>()); + } + + #[bench] + fn bench_get_int(b: &mut Bencher) { + let mut c = Container::new(); + c.put("Hello world"); + c.put(55); + b.iter(|| c.get::()); + } + + #[bench] + fn bench_get_mut(b: &mut Bencher) { + let mut c = Container::new(); + c.put("Hello world"); + c.put(55); + b.iter(|| *c.get_mut::()); + } + + #[bench] + fn bench_alloc(b: &mut Bencher) { + b.iter(|| test::black_box(Container::new())); + } +} diff --git a/src/gl/mod.rs b/src/gl/mod.rs index 599a04a..ab49010 100644 --- a/src/gl/mod.rs +++ b/src/gl/mod.rs @@ -652,3 +652,116 @@ impl Drop for MappedBuffer { mem::forget(mem::replace(&mut self.inner, Vec::new())); } } + +// Frame buffers + +pub type Attachment = u32; +pub const COLOR_ATTACHMENT_0: Attachment = gl::COLOR_ATTACHMENT0; +pub const COLOR_ATTACHMENT_1: Attachment = gl::COLOR_ATTACHMENT1; +pub const COLOR_ATTACHMENT_2: Attachment = gl::COLOR_ATTACHMENT2; +pub const DEPTH_ATTACHMENT: Attachment = gl::DEPTH_ATTACHMENT; + +pub struct Framebuffer(u32); + +impl Framebuffer { + pub fn new() -> Framebuffer { + let mut fb = Framebuffer(0); + unsafe { + gl::GenFramebuffers(1, &mut fb.0); + } + fb + } + + pub fn bind(&self) { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, self.0); + } + } + + pub fn bind_read(&self) { + unsafe { + gl::BindFramebuffer(gl::READ_FRAMEBUFFER, self.0); + } + } + + pub fn bind_draw(&self) { + unsafe { + gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, self.0); + } + } + + pub fn texture_2d(&self, attachment: Attachment, target: TextureTarget, tex: &Texture, level: i32) { + unsafe { + gl::FramebufferTexture2D(gl::FRAMEBUFFER, attachment, target, tex.0, level); + } + } +} + +impl Drop for Framebuffer { + fn drop(&mut self) { + unsafe { + gl::DeleteFramebuffers(1, &self.0); + } + } +} + +pub fn unbind_framebuffer() { + unsafe { + gl::BindFramebuffer(gl::FRAMEBUFFER, 0); + } +} + +pub fn unbind_framebuffer_read() { + unsafe { + gl::BindFramebuffer(gl::READ_FRAMEBUFFER, 0); + } +} + +pub fn unbind_framebuffer_draw() { + unsafe { + gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0); + } +} + +pub fn draw_buffers(bufs: &[Attachment]) { + unsafe { + gl::DrawBuffers( + bufs.len() as i32, + bufs.as_ptr() + ); + } +} + +pub fn bind_frag_data_location(p: &Program, cn: u32, name: &str) { + unsafe { + gl::BindFragDataLocation(p.0, cn, ffi::CString::new(name).unwrap().as_ptr()); + } +} + +pub fn blit_framebuffer( + sx0: i32, sy0: i32, sx1: i32, sy1: i32, + dx0: i32, dy0: i32, dx1: i32, dy1: i32, + mask: ClearFlags, filter: TextureValue) { + unsafe { + gl::BlitFramebuffer( + sx0, sy0, sx1, sy1, + dx0, dy0, dx1, dy1, + mask.internal(), filter as u32 + ); + } +} + +pub fn read_buffer(a: Attachment) { + unsafe { + gl::ReadBuffer(a); + } +} + +pub type TargetBuffer = u32; +pub const COLOR: TargetBuffer = gl::COLOR; + +pub fn clear_buffer(buffer: TargetBuffer, draw_buffer: i32, values: &[f32]) { + unsafe { + gl::ClearBufferfv(buffer, draw_buffer, values.as_ptr()); + } +} diff --git a/src/main.rs b/src/main.rs index b9d27dd..3b4f1d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![feature(test)] +#![feature(hashmap_hasher)] +extern crate test; +mod esc; + pub mod protocol; pub mod format; pub mod nbt; @@ -35,6 +40,7 @@ extern crate hyper; extern crate flate2; extern crate rand; extern crate rustc_serialize; +extern crate cgmath; #[macro_use] extern crate log; diff --git a/src/render/mod.rs b/src/render/mod.rs index 9408c12..f667816 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -26,6 +26,7 @@ use image; use image::GenericImage; use byteorder::{WriteBytesExt, NativeEndian}; use serde_json; +use cgmath; const ATLAS_SIZE: usize = 1024; @@ -38,10 +39,61 @@ pub struct Renderer { gl_texture: gl::Texture, texture_layers: usize, + chunk_shader: ChunkShader, + chunk_shader_alpha: ChunkShaderAlpha, + + perspective_matrix: cgmath::Matrix4, + + trans: Option, + last_width: u32, last_height: u32, } +init_shader! { + Program ChunkShader { + vert = "chunk_vertex", + frag = "chunk_frag", + attribute = { + position => "aPosition", + texture_info => "aTextureInfo", + texture_offset => "aTextureOffset", + color => "aColor", + lighting => "aLighting", + }, + uniform = { + perspective_matrix => "perspectiveMatrix", + camera_matrix => "cameraMatrix", + offset => "offset", + texture => "textures", + light_level => "lightLevel", + sky_offset => "sky_offset", + }, + } +} + +init_shader! { + Program ChunkShaderAlpha { + vert = "chunk_vertex", + frag = "chunk_frag", #alpha + attribute = { + position => "aPosition", + texture_info => "aTextureInfo", + texture_offset => "aTextureOffset", + color => "aColor", + lighting => "aLighting", + }, + uniform = { + perspective_matrix => "perspectiveMatrix", + camera_matrix => "cameraMatrix", + offset => "offset", + texture => "textures", + light_level => "lightLevel", + sky_offset => "sky_offset", + }, + } +} + impl Renderer { pub fn new(res: Arc>) -> Renderer { let version = { @@ -73,12 +125,14 @@ impl Renderer { gl::cull_face(gl::BACK); gl::front_face(gl::CLOCK_WISE); - // Shaders + // Shaders + let chunk_shader = ChunkShader::new(&greg); + let chunk_shader_alpha = ChunkShaderAlpha::new(&greg); - // UI - // Line Drawer - // Models - // Clouds + // UI + // Line Drawer + // Models + // Clouds gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); @@ -89,8 +143,16 @@ impl Renderer { resources: res, gl_texture: tex, texture_layers: 1, + + chunk_shader: chunk_shader, + chunk_shader_alpha: chunk_shader_alpha, + last_width: 0, last_height: 0, + + perspective_matrix: cgmath::Matrix4::zero(), + + trans: None, } } @@ -110,21 +172,34 @@ impl Renderer { self.last_width = width; self.last_height = height; gl::viewport(0, 0, width as i32, height as i32); + + self.perspective_matrix = cgmath::Matrix4::from( + cgmath::PerspectiveFov { + fovy: cgmath::Deg{s: 90f32}, + aspect: (width / height) as f32, + near: 0.1f32, + far: 500.0f32, + } + ); + + self.init_trans(width, height); } gl::active_texture(0); self.gl_texture.bind(gl::TEXTURE_2D_ARRAY); + gl::enable(gl::MULTISAMPLE); + gl::clear_color(14.0 / 255.0, 48.0 / 255.0, 92.0 / 255.0, 1.0); gl::clear(gl::ClearFlags::Color | gl::ClearFlags::Depth); self.ui.tick(width, height); } - fn update_textures(&mut self, delta: f64) { - self.gl_texture.bind(gl::TEXTURE_2D_ARRAY); + fn do_pending_textures(&mut self) { let len = { let tex = self.textures.read().unwrap(); + // Rebuild the texture if it needs resizing if self.texture_layers != tex.atlases.len() { let len = ATLAS_SIZE * ATLAS_SIZE * 4 * tex.atlases.len(); let mut data = Vec::with_capacity(len); @@ -149,6 +224,7 @@ impl Renderer { tex.pending_uploads.len() }; if len > 0 { + // Upload pending changes let mut tex = self.textures.write().unwrap(); for upload in &tex.pending_uploads { let atlas = upload.0; @@ -168,6 +244,11 @@ impl Renderer { } tex.pending_uploads.clear(); } + } + + fn update_textures(&mut self, delta: f64) { + self.gl_texture.bind(gl::TEXTURE_2D_ARRAY); + self.do_pending_textures(); for ani in &mut self.textures.write().unwrap().animated_textures { if ani.remaining_time <= 0.0 { @@ -194,6 +275,11 @@ impl Renderer { } + fn init_trans(&mut self, width: u32, height: u32) { + self.trans = None; + self.trans = Some(TransInfo::new(width, height)); + } + pub fn get_textures(&self) -> Arc> { self.textures.clone() } @@ -224,8 +310,8 @@ impl Renderer { Some(val) => val, None => { let mut t = textures.write().unwrap(); - // Make sure it hasn't already been loaded since we switched - // locks. + // Make sure it hasn't already been loaded since we switched + // locks. if let Some(val) = t.get_texture(name) { val } else { @@ -237,6 +323,48 @@ impl Renderer { } } +struct TransInfo { + main: gl::Framebuffer, + fbColor: gl::Texture, + fbDepth: gl::Texture, + trans: gl::Framebuffer, + accum: gl::Texture, + revealage: gl::Texture, + depth: gl::Texture, + + prgoram: TransShader, + array: gl::VertexArray, + buffer: gl::Buffer, +} + +init_shader! { + Program TransShader { + vert = "trans_vertex", + frag = "trans_frag", + attribute = { + position => "aPosition", + }, + uniform = { + accum => "taccum", + revealage => "trevealage", + color => "tcolor", + samples => "samples", + }, + } +} + +impl TransInfo { + pub fn new(width: u32, height: u32) -> TransInfo { + let trans = gl::Framebuffer::new(); + trans.bind(); + + let accum = gl::Texture::new(); + accum.bind(gl::TEXTURE_2D); + + unimplemented!() + } +} + pub struct TextureManager { textures: HashMap, version: usize, @@ -273,18 +401,18 @@ impl TextureManager { 2, 2, vec![ - 0, 0, 0, 255, - 255, 0, 255, 255, - 255, 0, 255, 255, - 0, 0, 0, 255, - ]); + 0, 0, 0, 255, + 255, 0, 255, 255, + 255, 0, 255, 255, + 0, 0, 0, 255, + ]); self.put_texture("steven", "solid", 1, 1, vec![ - 255, 255, 255, 255, - ]); + 255, 255, 255, 255, + ]); } fn update_textures(&mut self, version: usize) { @@ -321,7 +449,7 @@ impl TextureManager { val.read_to_end(&mut data).unwrap(); if let Ok(img) = image::load_from_memory(&data) { let (width, height) = img.dimensions(); - // Might be animated + // Might be animated if (name.starts_with("blocks/") || name.starts_with("items/")) && width != height { let id = img.to_rgba().into_vec(); let frame = id[..(width * width * 4) as usize].to_owned(); @@ -364,9 +492,9 @@ impl TextureManager { }) } else { out.push(AnimationFrame{ - index: frame.find("index").unwrap().as_i64().unwrap() as usize, - time: frame_time * frame.find("frameTime").unwrap().as_i64().unwrap(), - }) + index: frame.find("index").unwrap().as_i64().unwrap() as usize, + time: frame_time * frame.find("frameTime").unwrap().as_i64().unwrap(), + }) } } out diff --git a/src/render/shaders.rs b/src/render/shaders.rs index c950227..ee89c31 100644 --- a/src/render/shaders.rs +++ b/src/render/shaders.rs @@ -22,14 +22,29 @@ pub fn add_shaders(reg: &mut glsl::Registry) { reg.register("ui_vertex", include_str!("shaders/ui_vertex.glsl")); reg.register("ui_frag", include_str!("shaders/ui_frag.glsl")); + + reg.register("chunk_vertex", include_str!("shaders/chunk_vertex.glsl")); + reg.register("chunk_frag", include_str!("shaders/chunk_frag.glsl")); + + reg.register("trans_vertex", include_str!("shaders/trans_vertex.glsl")); + reg.register("trans_frag", include_str!("shaders/trans_frag.glsl")); +} + +macro_rules! get_shader { + ($reg:ident, $name:expr) => ( + $reg.get($name) + ); + ($reg:ident, $name:expr, $def:expr) => ( + $reg.get_define($name, $def) + ) } #[macro_export] macro_rules! init_shader { ( Program $name:ident { - vert = $vert:expr, - frag = $frag:expr, + vert = $vert:expr, $(#$vdef:ident)* + frag = $frag:expr, $(#$fdef:ident)* attribute = { $( $field:ident => $glname:expr, @@ -42,6 +57,7 @@ macro_rules! init_shader { }, } ) => ( + #[allow(dead_code)] struct $name { program: gl::Program, $( @@ -54,8 +70,8 @@ macro_rules! init_shader { impl $name { pub fn new(reg: &glsl::Registry) -> $name { - let v = reg.get($vert); - let f = reg.get($frag); + let v = get_shader!(reg, $vert $(,stringify!($vdef))*); + let f = get_shader!(reg, $frag $(,stringify!($fdef))*); let shader = shaders::create_program(&v, &f); $name { $( diff --git a/src/render/shaders/chunk_frag.glsl b/src/render/shaders/chunk_frag.glsl new file mode 100644 index 0000000..9bff17e --- /dev/null +++ b/src/render/shaders/chunk_frag.glsl @@ -0,0 +1,36 @@ +uniform sampler2DArray textures; + +in vec3 vColor; +in vec4 vTextureInfo; +in vec2 vTextureOffset; +in float vAtlas; +in vec3 vLighting; + +#ifndef alpha +out vec4 fragColor; +#else +out vec4 accum; +out float revealage; +#endif + +#include lookup_texture + +void main() { + vec4 col = atlasTexture(); + #ifndef alpha + if (col.a < 0.5) discard; + #endif + col *= vec4(vColor, 1.0); + col.rgb *= vLighting; + + #ifndef alpha + fragColor = col; + #else + float z = gl_FragCoord.z; + float al = col.a; + float weight = pow(al + 0.01f, 4.0f) + + max(0.01f, min(3000.0f, 0.3f / (0.00001f + pow(abs(z) / 800.0f, 4.0f)))); + accum = vec4(col.rgb * al * weight, al); + revealage = weight * al; + #endif +} diff --git a/src/render/shaders/chunk_vertex.glsl b/src/render/shaders/chunk_vertex.glsl new file mode 100644 index 0000000..1a0f080 --- /dev/null +++ b/src/render/shaders/chunk_vertex.glsl @@ -0,0 +1,32 @@ +in vec3 aPosition; +in vec4 aTextureInfo; +in vec3 aTextureOffset; +in vec3 aColor; +in vec2 aLighting; + +uniform mat4 perspectiveMatrix; +uniform mat4 cameraMatrix; +uniform ivec3 offset; +uniform float lightLevel; +uniform float skyOffset; + +out vec3 vColor; +out vec4 vTextureInfo; +out vec2 vTextureOffset; +out float vAtlas; +out vec3 vLighting; + +#include get_light + +void main() { + vec3 pos = vec3(aPosition.x, -aPosition.y, aPosition.z); + vec3 o = vec3(offset.x, -offset.y / 4096.0, offset.z); + gl_Position = perspectiveMatrix * cameraMatrix * vec4(pos + o * 16.0, 1.0); + + vColor = aColor; + vTextureInfo = aTextureInfo; + vTextureOffset = aTextureOffset.xy / 16.0; + vAtlas = aTextureOffset.z; + + vLighting = getLight(aLighting / (4000.0)); +} diff --git a/src/render/shaders/trans_frag.glsl b/src/render/shaders/trans_frag.glsl new file mode 100644 index 0000000..5b83741 --- /dev/null +++ b/src/render/shaders/trans_frag.glsl @@ -0,0 +1,28 @@ +uniform sampler2D taccum; +uniform sampler2D trevealage; +uniform sampler2DMS tcolor; + +uniform int samples; + +out vec4 fragColor; + +void main() { + ivec2 C = ivec2(gl_FragCoord.xy); + vec4 accum = texelFetch(taccum, C, 0); + float aa = texelFetch(trevealage, C, 0).r; + vec4 col = texelFetch(tcolor, C, 0); + + for (int i = 1; i < samples; i++) { + col += texelFetch(tcolor, C, i); + } + col /= float(samples); + + float r = accum.a; + accum.a = aa; + if (r >= 1.0) { + fragColor = vec4(col.rgb, 0.0); + } else { + vec3 alp = clamp(accum.rgb / clamp(accum.a, 1e-4, 5e4), 0.0, 1.0); + fragColor = vec4(col.rgb * r + alp * (1.0 - r), 0.0); + } +} diff --git a/src/render/shaders/trans_vertex.glsl b/src/render/shaders/trans_vertex.glsl new file mode 100644 index 0000000..ec4e70d --- /dev/null +++ b/src/render/shaders/trans_vertex.glsl @@ -0,0 +1,5 @@ +in vec2 aPosition; + +void main() { + gl_Position = vec4(aPosition,0,1); +}