diff --git a/Cargo.lock b/Cargo.lock index 9327f59..11b3a1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,11 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memchr" version = "2.2.1" @@ -199,6 +204,7 @@ dependencies = [ "gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "glam 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "iota 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -423,6 +429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum nom 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c433f4d505fe6ce7ff78523d2fa13a0b9f2690e181fc26168bcbe5ccc5d14e07" diff --git a/Cargo.toml b/Cargo.toml index f6d7bb0..cfbb842 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ glam = "0.8.5" iota = "0.2.1" +maplit = "1.0.2" # TODO: Drop nom depend. It's way overkill for iqm. nom = "5.1.0" diff --git a/src/bin/pumpkin.rs b/src/bin/pumpkin.rs index de13079..950a72c 100644 --- a/src/bin/pumpkin.rs +++ b/src/bin/pumpkin.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate maplit; + use glam::{Mat4, Vec3, Vec4}; use sdl2::event::Event; @@ -149,6 +152,161 @@ struct RenderableArrow { inv_model_mat: Mat4, } +#[derive (Copy, Clone)] +pub enum FrontFace { + Cw, + Ccw, +} + +impl From for u32 { + fn from (v: FrontFace) -> Self { + use FrontFace::*; + { + use gl::*; + match v { + Cw => CW, + Ccw => CCW, + } + } + } +} + +#[derive (Copy, Clone)] +pub enum StencilOp { + Keep, + Zero, + Replace, + Incr, + Decr, + Invert, + IncrWrap, + DecrWrap, +} + +impl From for u32 { + fn from (v: StencilOp) -> Self { + use StencilOp::*; + { + use gl::*; + match v { + Keep => KEEP, + Zero => ZERO, + Replace => REPLACE, + Incr => INCR, + Decr => DECR, + Invert => INVERT, + IncrWrap => INCR_WRAP, + DecrWrap => DECR_WRAP, + } + } + } +} + +#[derive (Copy, Clone)] +pub enum StencilFunc { + Never, + Always, + Less, + LessEqual, + Equal, + Greater, + GreaterEqual, + NotEqual, +} + +impl From for u32 { + fn from (v: StencilFunc) -> Self { + use StencilFunc::*; + { + use gl::*; + match v { + Never => NEVER, + Always => ALWAYS, + Less => LESS, + LessEqual => LEQUAL, + Equal => EQUAL, + Greater => GREATER, + GreaterEqual => GEQUAL, + NotEqual => NOTEQUAL, + } + } + } +} + +pub struct StencilOpState { + sfail: StencilOp, + dpfail: StencilOp, + dppass: StencilOp, +} + +pub struct StencilFuncState { + func: StencilFunc, + reference: i32, + mask: u32, +} + +pub struct StencilState { + op: StencilOpState, + func: StencilFuncState, +} + +// Anything that's None is "unknown" + +pub struct GlState { + flags: HashMap , + front_face: Option , + stencil: Option , +} + +pub struct Pass <'a> { + shader: &'a ShaderClosure, + + // Anything that's None is "don't care" + + required_state: GlState, +} + +impl Pass <'_> { + pub fn apply_slow (&self) { + let state = &self.required_state; + + for (flag, value) in state.flags.iter () { + if *value { + glezz::enable (*flag); + } + else { + glezz::disable (*flag); + } + } + + match state.front_face { + Some (v) => glezz::front_face (v.into ()), + _ => (), + } + + match &state.stencil { + Some (v) => { + let func = &v.func; + let op = &v.op; + + unsafe { + gl::StencilFunc ( + func.func.into (), + func.reference.into (), + func.mask.into () + ); + gl::StencilOp ( + op.sfail.into (), + op.dpfail.into (), + op.dppass.into () + ); + } + }, + _ => (), + } + } +} + struct GameGraphics { shader_diffuse: ShaderClosure, shader_shadow: ShaderClosure, @@ -305,6 +463,83 @@ impl GameGraphics { 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); + + let passes = vec! [ + Pass { + shader: &self.shader_diffuse, + required_state: GlState { + flags: hashmap! { + gl::STENCIL_TEST => false, + }, + front_face: Some (FrontFace::Cw), + stencil: None, + }, + }, + Pass { + shader: &self.shader_shadow, + required_state: GlState { + flags: hashmap! { + gl::STENCIL_TEST => true, + }, + front_face: Some (FrontFace::Ccw), + stencil: Some (StencilState { + func: StencilFuncState { + func: StencilFunc::Always, + reference: 1, + mask: 1, + }, + op: StencilOpState { + sfail: StencilOp::Keep, + dpfail: StencilOp::Keep, + dppass: StencilOp::Replace, + }, + }), + }, + }, + Pass { + shader: &self.shader_shadow, + required_state: GlState { + flags: hashmap! { + gl::STENCIL_TEST => true, + }, + front_face: Some (FrontFace::Cw), + stencil: Some (StencilState { + func: StencilFuncState { + func: StencilFunc::NotEqual, + reference: 0, + mask: 1, + }, + op: StencilOpState { + sfail: StencilOp::Keep, + dpfail: StencilOp::Keep, + dppass: StencilOp::Keep, + }, + }), + }, + }, + Pass { + shader: &self.shader_shadow, + required_state: GlState { + flags: hashmap! { + gl::STENCIL_TEST => true, + }, + front_face: Some (FrontFace::Cw), + stencil: Some (StencilState { + func: StencilFuncState { + func: StencilFunc::Equal, + reference: 0, + mask: 1, + }, + op: StencilOpState { + sfail: StencilOp::Keep, + dpfail: StencilOp::Keep, + dppass: StencilOp::Keep, + }, + }), + }, + }, + ]; + glezz::enable (gl::CULL_FACE); let pumpkin_model_mat = @@ -324,9 +559,8 @@ impl GameGraphics { let unis = shader_vars.unis; let attrs = shader_vars.attrs; - // Pass 1 - Draw the world except the ground plane - glezz::disable (gl::STENCIL_TEST); - glezz::front_face (gl::CW); + // Pass 0 - Draw the world except the ground plane + passes [0].apply_slow (); let mvp = view_mat * pumpkin_model_mat; glezz::uniform_matrix_4fv (unis [&MVP], &mvp); @@ -385,13 +619,9 @@ impl GameGraphics { let unis = shader_vars.unis; let attrs = shader_vars.attrs; - // Pass 2: Draw shadows into stencil buffer - glezz::front_face (gl::CCW); - glezz::enable (gl::STENCIL_TEST); + // Pass 1: Draw shadows into stencil buffer + passes [1].apply_slow (); unsafe { - gl::StencilFunc (gl::ALWAYS, 1, 1); - gl::StencilOp (gl::KEEP, gl::KEEP, gl::REPLACE); - gl::ColorMask (0, 0, 0, 0); gl::DepthMask (0); gl::StencilMask (1); @@ -421,16 +651,13 @@ impl GameGraphics { let unis = shader_vars.unis; let attrs = shader_vars.attrs; - // Pass 3: Draw lit ground + // Pass 2: Draw lit ground + passes [2].apply_slow (); unsafe { gl::ColorMask (255, 255, 255, 255); gl::DepthMask (1); - gl::StencilFunc (gl::NOTEQUAL, 0, 1); - gl::StencilOp (gl::KEEP, gl::KEEP, gl::KEEP); } - glezz::front_face (gl::CW); - let inverse_pumpkin = pumpkin_model_mat.inverse (); let object_space_light = make_object_space_vec (&inverse_pumpkin, &light); @@ -449,11 +676,9 @@ impl GameGraphics { glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &Vec3::from ((0.0, 0.0, 0.0))); self.mesh_pitch.draw (attrs, self.grass_index); - // Pass 4: Draw shadowed ground - unsafe { - gl::StencilFunc (gl::EQUAL, 0, 1); - gl::StencilOp (gl::KEEP, gl::KEEP, gl::KEEP); - } + // Pass 3: Draw shadowed ground + passes [3].apply_slow (); + glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); self.mesh_pitch.draw (attrs, self.grass_index); }); @@ -574,7 +799,11 @@ mod tests { use super::*; #[test] - pub fn iqm () { + pub fn sizes () { + use std::mem; + assert_eq! (8, mem::size_of::