diff --git a/Cargo.toml b/Cargo.toml index 092e850..3cedbb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "opengl_rust" version = "0.1.0" authors = ["_"] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/bin/platformer.rs b/src/bin/platformer.rs index ea10681..83694c7 100644 --- a/src/bin/platformer.rs +++ b/src/bin/platformer.rs @@ -1,6 +1,96 @@ -use opengl_rust::prelude::*; +use std::{ + collections::HashMap, +}; use anyhow::Result; +use maplit::hashmap; + +use opengl_rust::{ + prelude::*, + gl_state::*, + renderable_model::{ + attributes, + RenderableModel, + }, + shader_closure::ShaderLookup, + texture::Texture, +}; + +mod uniforms { +use iota::iota; +iota! { +pub const + MVP: u32 = iota; + , OBJECT_SPACE_LIGHT + , OBJECT_SPACE_SKY + , ALBEDO + , MIN_ALBEDO + , MIN_BRIGHT + , TEXTURE +} +} + +struct GameGraphics { + passes: Vec , + + shaders: Vec , + shader_lookup: HashMap , + + mesh_cube: RenderableModel, + mesh_sky: RenderableModel, + + text_stream: TriangleStream, + + texture_sky: Texture, +} + +impl ShaderLookup for GameGraphics { + fn lookup <'a> (&'a self, id: u32) -> &'a ShaderClosure { + &self.shaders [self.shader_lookup [&id]] + } +} + +impl GameGraphics { + fn draw ( + &self, + gl_state: &mut GlState, + ) { + use uniforms as u; + + let white = color_from_255 ((255.0, 255.0, 255.0)); + let black = color_from_255 ((0.0, 0.0, 0.0)); + + let screen_size = (1280.0, 720.0); + + let proj_mat = Mat4::perspective_rh_gl (30.0f32.to_radians (), screen_size.0 / screen_size.1, 0.125, 200.0); + + let view_mat = proj_mat * Mat4::from_rotation_x (-90.0f32.to_radians ()); + let world_model_mat = Mat4::IDENTITY; + + self.passes [0].with (gl_state, || { + glezz::clear_color (1.0f32, 0.0f32, 1.0f32, 1.0f32); + glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); + }); + + self.passes [1].with_shader (gl_state, self, |shader_vars| { + let attrs = shader_vars.attrs; + let unis = shader_vars.unis; + + { + let sky_mvp_mat = view_mat * Mat4::from_scale ((64.0, 64.0, 64.0).into ()); + + self.texture_sky.bind (); + glezz::uniform_matrix_4fv (unis [&u::MVP], &sky_mvp_mat); + glezz::uniform_3fv (unis [&u::ALBEDO], &white); + glezz::uniform_3fv (unis [&u::MIN_BRIGHT], &white); + glezz::uniform_3fv (unis [&u::MIN_ALBEDO], &black); + glezz::uniform_1i (unis [&u::TEXTURE], 0); + + self.mesh_sky.draw_all (attrs, |_| true); + } + }); + } +} #[tokio::main] async fn main () -> Result <()> { @@ -29,10 +119,140 @@ async fn main () -> Result <()> { let mut time_step = TimeStep::new (60, 1000); let mut graphics_frames = 0; - let shader_program = shader::shader_from_files ("shaders/terrain-vert.glsl", "shaders/terrain-frag.glsl"); - let shader_locations = ShaderLocations::new (&shader_program)?; + let uniform_names = { + vec! [ + (uniforms::MVP, "uni_mvp"), + (uniforms::OBJECT_SPACE_LIGHT, "uni_object_space_light"), + (uniforms::OBJECT_SPACE_SKY, "uni_object_space_sky"), + (uniforms::ALBEDO, "uni_albedo"), + (uniforms::MIN_ALBEDO, "uni_min_albedo"), + (uniforms::MIN_BRIGHT, "uni_min_bright"), + (uniforms::TEXTURE, "uni_texture"), + ] + }; + + let attr_names = { + vec! [ + (attributes::POS, "attr_pos"), + (attributes::UV, "attr_uv"), + (attributes::NORMAL, "attr_normal"), + ] + }; + + let shaders: Vec <_> = [ + ("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"), + ("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"), + ("shaders/terrain-vert.glsl", "shaders/terrain-frag.glsl"), + ].into_iter () + .map (|(v, f)| { + ShaderClosure::new (shader_from_files (v, f), &uniform_names, &attr_names) + }) + .collect (); let mesh_cube = renderable_from_iqm_file ("cube.iqm"); + let mesh_sky = renderable_from_iqm_file ("sky-sphere.iqm"); + + let passes = vec![ + // Clear everything + Pass { + iso: IsoGlState { + shader_id: None, + flags: Default::default (), + front_face: None, + stencil: None, + depth_func: None, + color_mask: Some ((1, 1, 1, 1)), + depth_mask: Some (1), + stencil_mask: Some (255), + }, + }, + // Draw world + Pass { + iso: IsoGlState { + shader_id: Some (shaders [0].get_id ()), + flags: hashmap! { + gl::CULL_FACE => true, + gl::DEPTH_TEST => true, + gl::TEXTURE_2D => true, + gl::STENCIL_TEST => false, + }, + front_face: Some (FrontFace::Cw), + stencil: Some (StencilState { + func: StencilFuncState { + func: StencilFunc::Always, + reference: 0, + mask: 0, + }, + op: StencilOpState { + sfail: StencilOp::Keep, + dpfail: StencilOp::Keep, + dppass: StencilOp::Keep, + }, + }), + depth_func: Some (DepthFunc::Less), + color_mask: Some ((1, 1, 1, 1)), + depth_mask: Some (1), + stencil_mask: Some (0), + }, + }, + // Draw UI + Pass { + iso: IsoGlState { + shader_id: Some (shaders [0].get_id ()), + flags: hashmap! { + gl::CULL_FACE => false, + gl::DEPTH_TEST => false, + gl::TEXTURE_2D => true, + gl::STENCIL_TEST => false, + }, + front_face: None, + stencil: None, + depth_func: Some (DepthFunc::Less), + color_mask: Some ((1, 1, 1, 1)), + depth_mask: Some (1), + stencil_mask: Some (0), + }, + }, + ]; + + let text_stream = TriangleStream { + verts: gpu_buffers::VertexBuffer::streaming (4 * 5 * 6 * 1024), + indices: { + let quad = [ + 0, 1, 2, + 0, 2, 3, + ]; + + let v: Vec = (0u32..1024).map (|i| { + quad.iter ().map (move |j| 4 * i + j) + }).flatten ().collect (); + gpu_buffers::IndexBuffer::from_slice_u32 (&v) + } + }; + + let shader_lookup = HashMap::from_iter (shaders.iter ().enumerate () + .map (|(i, s)| { + (s.get_id (), i) + })); + + shaders [0].with (None, |shader_vars| { + let attrs = shader_vars.attrs; + glezz::enable_vertex_attrib_array (attrs [attributes::POS]); + glezz::enable_vertex_attrib_array (attrs [attributes::UV]); + glezz::enable_vertex_attrib_array (attrs [attributes::NORMAL]); + }); + + let graphics = GameGraphics { + mesh_cube, + mesh_sky, + passes, + shader_lookup, + shaders, + text_stream, + texture_sky: Texture::from_file ("sky.png"), + }; + + let mut gl_state = Default::default (); 'running: loop { let _frames_to_do = time_step.step (); @@ -49,12 +269,10 @@ async fn main () -> Result <()> { window.gl_make_current (&gl_ctx).unwrap (); - glezz::enable (gl::DEPTH_TEST); - - glezz::clear_color (0.392f32, 0.710f32, 0.965f32, 1.0f32); - glezz::clear (gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); + graphics.draw (&mut gl_state); window.gl_swap_window (); + graphics_frames += 1; tokio::time::sleep (Duration::from_millis (15)).await; } @@ -70,7 +288,7 @@ struct ShaderLocations { } impl ShaderLocations { - pub fn new (shader_program: &shader::ShaderProgram) -> anyhow::Result + pub fn new (shader_program: &opengl_rust::shader::ShaderProgram) -> anyhow::Result { let attr = |name: &str| shader_program.get_attribute_location (&CString::new (name.as_bytes ())?).try_into ().context ("Attribute location negative"); let uni = |name: &str| shader_program.get_uniform_location (&CString::new (name.as_bytes ())?).try_into ().context ("Uniform location bad"); @@ -83,3 +301,20 @@ impl ShaderLocations { }) } } + +struct TriangleStream { + pub verts: gpu_buffers::VertexBuffer, + pub indices: gpu_buffers::IndexBuffer, +} + +fn color_from_255 (rgb: V) -> Vec3 +where V: Into +{ + let rgb: Vec3 = rgb.into (); + + Vec3::from (( + rgb.x / 255.0, + rgb.y / 255.0, + rgb.z / 255.0 + )) +} diff --git a/src/prelude.rs b/src/prelude.rs index e1c9c6e..9b6ac1e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -31,6 +31,7 @@ pub use crate::{ network_protocol::*, quinn_common::make_client_endpoint, renderable_model::renderable_from_iqm_file, - shader, + shader::shader_from_files, + shader_closure::ShaderClosure, timestep::TimeStep, };