diff --git a/src/bin/pumpkin.rs b/src/bin/pumpkin.rs index 601cb22..de13079 100644 --- a/src/bin/pumpkin.rs +++ b/src/bin/pumpkin.rs @@ -149,148 +149,137 @@ struct RenderableArrow { inv_model_mat: Mat4, } -fn main () { - let sdl_context = sdl2::init ().unwrap (); - let video_subsystem = sdl_context.video ().unwrap (); +struct GameGraphics { + shader_diffuse: ShaderClosure, + shader_shadow: ShaderClosure, - let window = video_subsystem.window ("OpenGL? In my Rust?", 1280, 720) - .position_centered () - .opengl () - .build () - .unwrap (); + mesh_pumpkin: RenderableModel, + mesh_sky: RenderableModel, + mesh_pitch: RenderableModel, + mesh_arrow: RenderableModel, - gl::load_with (|s| { - video_subsystem.gl_get_proc_address (s) as *const _ - }); + texture: Texture, - assert! (gl::ClearColor::is_loaded ()); - - let gl_ctx = window.gl_create_context ().unwrap (); - - window.gl_make_current (&gl_ctx).unwrap (); - - let uniform_names = { - use uniforms::*; - vec! [ - (MVP, "uni_mvp"), - (OBJECT_SPACE_LIGHT, "uni_object_space_light"), - (OBJECT_SPACE_SKY, "uni_object_space_sky"), - (ALBEDO, "uni_albedo"), - (MIN_ALBEDO, "uni_min_albedo"), - (MIN_BRIGHT, "uni_min_bright"), - (TEXTURE, "uni_texture"), - ] - }; - - let attr_names = { - use renderable_model::attributes::*; - vec! [ - (POS, "attr_pos"), - (UV, "attr_uv"), - (NORMAL, "attr_normal"), - ] - }; - - let shader_diffuse = ShaderClosure::new (shader_from_files ("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"), &uniform_names, &attr_names); - let shader_shadow = ShaderClosure::new (shader_from_files ("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"), &uniform_names, &attr_names); - - shader_diffuse.with (|shader_vars| { - let attrs = shader_vars.attrs; - use renderable_model::attributes::*; - glezz::enable_vertex_attrib_array (attrs [POS]); - glezz::enable_vertex_attrib_array (attrs [UV]); - glezz::enable_vertex_attrib_array (attrs [NORMAL]); - }); - - let texture = Texture::from_file ("sky.png"); - texture.bind (); - - let mesh_pumpkin = renderable_from_iqm_file ("pumpking.iqm"); - let mesh_sky = renderable_from_iqm_file ("sky-sphere.iqm"); - let mesh_pitch = renderable_from_iqm_file ("pitch.iqm"); - let mesh_arrow = renderable_from_iqm_file ("arrow.iqm"); - - let magenta = color_from_255 ((255.0, 0.0, 255.0)); - let orange = color_from_255 ((210.0, 125.0, 44.0)); - let green = color_from_255 ((52.0, 101.0, 36.0)); - let white = color_from_255 ((255.0, 255.0, 255.0)); - let _off_white = color_from_255 ((222.0, 238.0, 214.0)); - let black = color_from_255 ((0.0, 0.0, 0.0)); - let _off_black = color_from_255 ((20.0, 12.0, 28.0)); - - let pumpkin_colors = vec! [ - orange, - green, - ]; - - let (pitch_colors, grass_index) = { - let silver = (255.0, 255.0, 255.0); - let wood = (133.0, 76.0, 48.0); + pitch_colors: Vec , + grass_index: usize, +} + +impl GameGraphics { + pub fn new () -> Self { + let uniform_names = { + use uniforms::*; + vec! [ + (MVP, "uni_mvp"), + (OBJECT_SPACE_LIGHT, "uni_object_space_light"), + (OBJECT_SPACE_SKY, "uni_object_space_sky"), + (ALBEDO, "uni_albedo"), + (MIN_ALBEDO, "uni_min_albedo"), + (MIN_BRIGHT, "uni_min_bright"), + (TEXTURE, "uni_texture"), + ] + }; - let color_lookup: HashMap <&str, _> = HashMap::from_iter (vec! [ - ("GoalN1", silver), - ("GoalN2", silver), - ("GoalN3", silver), - - ("GoalS1", silver), - ("GoalS2", silver), - ("GoalS3", silver), - - ("TowerNW", wood), - ("TowerNE", wood), - ("TowerSW", wood), - ("TowerSE", wood), - - ("Wall", wood), - - ("Grass", (52.0, 101.0, 36.0)), - ].into_iter ()); + let attr_names = { + use renderable_model::attributes::*; + vec! [ + (POS, "attr_pos"), + (UV, "attr_uv"), + (NORMAL, "attr_normal"), + ] + }; - let mut grass_index = None; - let colors: Vec <_> = (0..mesh_pitch.meshes.len ()).map (|i| { - let name = str::from_utf8 (&mesh_pitch.meshes [i].name).unwrap (); - if name == "Grass" { - grass_index = Some (i); - } + let shader_diffuse = ShaderClosure::new (shader_from_files ("shaders/pumpkin-vert.glsl", "shaders/pumpkin-frag.glsl"), &uniform_names, &attr_names); + let shader_shadow = ShaderClosure::new (shader_from_files ("shaders/shadow-vert.glsl", "shaders/shadow-frag.glsl"), &uniform_names, &attr_names); + + shader_diffuse.with (|shader_vars| { + let attrs = shader_vars.attrs; + use renderable_model::attributes::*; + glezz::enable_vertex_attrib_array (attrs [POS]); + glezz::enable_vertex_attrib_array (attrs [UV]); + glezz::enable_vertex_attrib_array (attrs [NORMAL]); + }); + + let mesh_pumpkin = renderable_from_iqm_file ("pumpking.iqm"); + let mesh_sky = renderable_from_iqm_file ("sky-sphere.iqm"); + let mesh_pitch = renderable_from_iqm_file ("pitch.iqm"); + let mesh_arrow = renderable_from_iqm_file ("arrow.iqm"); + + let texture = Texture::from_file ("sky.png"); + texture.bind (); + + let (pitch_colors, grass_index) = { + let silver = (255.0, 255.0, 255.0); + let wood = (133.0, 76.0, 48.0); - match color_lookup.get (name) { - Some (t) => color_from_255 (*t), - _ => (0.0, 0.0, 0.0).into (), - } - }).collect (); + let color_lookup: HashMap <&str, _> = HashMap::from_iter (vec! [ + ("GoalN1", silver), + ("GoalN2", silver), + ("GoalN3", silver), + + ("GoalS1", silver), + ("GoalS2", silver), + ("GoalS3", silver), + + ("TowerNW", wood), + ("TowerNE", wood), + ("TowerSW", wood), + ("TowerSE", wood), + + ("Wall", wood), + + ("Grass", (52.0, 101.0, 36.0)), + ].into_iter ()); + + let mut grass_index = None; + let colors: Vec <_> = (0..mesh_pitch.meshes.len ()).map (|i| { + let name = str::from_utf8 (&mesh_pitch.meshes [i].name).unwrap (); + if name == "Grass" { + grass_index = Some (i); + } + + match color_lookup.get (name) { + Some (t) => color_from_255 (*t), + _ => (0.0, 0.0, 0.0).into (), + } + }).collect (); + + (colors, grass_index.unwrap ()) + }; - (colors, grass_index.unwrap ()) - }; - - glezz::enable (gl::DEPTH_TEST); - glezz::enable (gl::TEXTURE_2D); - - let mut time_step = TimeStep::new (60, 1000); - let mut state = WorldState::new (); - - let mut event_pump = sdl_context.event_pump ().unwrap (); - 'running: loop { - let frames_to_do = time_step.step (); - - let controller = ControllerState::from_sdl_keyboard (&event_pump.keyboard_state ()); - - for _ in 0..frames_to_do { - state.step (&controller); + Self { + shader_diffuse, + shader_shadow, + + mesh_pumpkin, + mesh_sky, + mesh_pitch, + mesh_arrow, + + texture, + + pitch_colors, + grass_index, } + } + + pub fn draw ( + &self, + state: &WorldState, + arrows: &[RenderableArrow] + ) + { + let magenta = color_from_255 ((255.0, 0.0, 255.0)); + let orange = color_from_255 ((210.0, 125.0, 44.0)); + let green = color_from_255 ((52.0, 101.0, 36.0)); + let white = color_from_255 ((255.0, 255.0, 255.0)); + let _off_white = color_from_255 ((222.0, 238.0, 214.0)); + let black = color_from_255 ((0.0, 0.0, 0.0)); + let _off_black = color_from_255 ((20.0, 12.0, 28.0)); - let _mouse = event_pump.mouse_state (); - - for event in event_pump.poll_iter() { - match event { - Event::Quit {..} | - Event::KeyDown { keycode: Some (Keycode::Escape), .. } => { - break 'running - }, - _ => (), - } - } - - window.gl_make_current (&gl_ctx).unwrap (); + let pumpkin_colors = vec! [ + orange, + green, + ]; let longitude = state.azimuth.to_radians (); let latitude = (state.altitude - 90.0).to_radians (); @@ -326,6 +315,198 @@ fn main () { let world_model_mat = Mat4::identity (); + use uniforms::*; + + glezz::enable (gl::DEPTH_TEST); + glezz::enable (gl::TEXTURE_2D); + + self.shader_diffuse.with (|shader_vars| { + 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); + + let mvp = view_mat * pumpkin_model_mat; + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + + let inverse_pumpkin = pumpkin_model_mat.inverse (); + + let object_space_light = make_object_space_vec (&inverse_pumpkin, &light); + + let object_space_sky = make_object_space_vec (&inverse_pumpkin, &Vec3::from ((0.0, 0.0, 1.0))); + + glezz::uniform_3fv (unis [&MIN_BRIGHT], &black); + glezz::uniform_3fv (unis [&MIN_ALBEDO], &white); + glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); + glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky); + + self.mesh_pumpkin.draw_all (attrs, |i| { + glezz::uniform_3fv (unis [&ALBEDO], &pumpkin_colors [i]); + true + }); + + let mvp = view_mat * world_model_mat; + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + + self.mesh_pitch.draw_all (attrs, |i| { + glezz::uniform_3fv (unis [&ALBEDO], &self.pitch_colors [i]); + i != self.grass_index + }); + + glezz::uniform_3fv (unis [&ALBEDO], &magenta); + for arrow in arrows.iter () { + let mvp = view_mat * arrow.model_mat; + + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + let object_space_light = make_object_space_vec (&arrow.inv_model_mat, &light); + let object_space_sky = make_object_space_vec (&arrow.inv_model_mat, &Vec3::from ((0.0, 0.0, 1.0))); + glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); + glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky); + + self.mesh_arrow.draw_all (attrs, |_| true); + } + + let draw_sky = true; + if draw_sky { + self.texture.bind (); + glezz::uniform_matrix_4fv (unis [&MVP], &sky_mvp_mat); + glezz::uniform_3fv (unis [&ALBEDO], &white); + glezz::uniform_3fv (unis [&MIN_BRIGHT], &white); + glezz::uniform_3fv (unis [&MIN_ALBEDO], &black); + glezz::uniform_1i (unis [&TEXTURE], 0); + + self.mesh_sky.draw_all (attrs, |_| true); + } + }); + + self.shader_shadow.with (|shader_vars| { + 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); + 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); + } + + let view_mat = view_mat * shadow_mat; + + let mvp = view_mat * pumpkin_model_mat; + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + + self.mesh_pumpkin.draw_all (attrs, |_| true); + + let mvp = view_mat * world_model_mat; + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + + self.mesh_pitch.draw_all (attrs, |i| i != self.grass_index); + + for arrow in arrows.iter () { + let mvp = view_mat * arrow.model_mat; + + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + self.mesh_arrow.draw_all (attrs, |_| true); + } + }); + + self.shader_diffuse.with (|shader_vars| { + let unis = shader_vars.unis; + let attrs = shader_vars.attrs; + + // Pass 3: Draw lit ground + 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); + + let object_space_sky = make_object_space_vec (&inverse_pumpkin, &Vec3::from ((0.0, 0.0, 1.0))); + + glezz::uniform_3fv (unis [&MIN_BRIGHT], &black); + glezz::uniform_3fv (unis [&MIN_ALBEDO], &white); + glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky); + + let mvp = view_mat * world_model_mat; + glezz::uniform_matrix_4fv (unis [&MVP], &mvp); + + glezz::uniform_3fv (unis [&ALBEDO], &self.pitch_colors [self.grass_index]); + + 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); + } + glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); + self.mesh_pitch.draw (attrs, self.grass_index); + }); + } +} + +fn main () { + let sdl_context = sdl2::init ().unwrap (); + let video_subsystem = sdl_context.video ().unwrap (); + + let window = video_subsystem.window ("OpenGL? In my Rust?", 1280, 720) + .position_centered () + .opengl () + .build () + .unwrap (); + + gl::load_with (|s| { + video_subsystem.gl_get_proc_address (s) as *const _ + }); + + assert! (gl::ClearColor::is_loaded ()); + + let gl_ctx = window.gl_create_context ().unwrap (); + + window.gl_make_current (&gl_ctx).unwrap (); + + let mut time_step = TimeStep::new (60, 1000); + let mut state = WorldState::new (); + + let graphics = GameGraphics::new (); + + let mut event_pump = sdl_context.event_pump ().unwrap (); + 'running: loop { + let frames_to_do = time_step.step (); + + let controller = ControllerState::from_sdl_keyboard (&event_pump.keyboard_state ()); + + for _ in 0..frames_to_do { + state.step (&controller); + } + + let _mouse = event_pump.mouse_state (); + + for event in event_pump.poll_iter() { + match event { + Event::Quit {..} | + Event::KeyDown { keycode: Some (Keycode::Escape), .. } => { + break 'running + }, + _ => (), + } + } + let arrows = vec![ Arrow { origin: (0.0, 0.0, 1.0).into (), @@ -378,144 +559,9 @@ fn main () { } }).collect (); - use uniforms::*; + window.gl_make_current (&gl_ctx).unwrap (); - shader_diffuse.with (|shader_vars| { - 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); - - let mvp = view_mat * pumpkin_model_mat; - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - - let inverse_pumpkin = pumpkin_model_mat.inverse (); - - let object_space_light = make_object_space_vec (&inverse_pumpkin, &light); - - let object_space_sky = make_object_space_vec (&inverse_pumpkin, &Vec3::from ((0.0, 0.0, 1.0))); - - glezz::uniform_3fv (unis [&MIN_BRIGHT], &black); - glezz::uniform_3fv (unis [&MIN_ALBEDO], &white); - glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); - glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky); - - mesh_pumpkin.draw_all (attrs, |i| { - glezz::uniform_3fv (unis [&ALBEDO], &pumpkin_colors [i]); - true - }); - - let mvp = view_mat * world_model_mat; - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - - mesh_pitch.draw_all (attrs, |i| { - glezz::uniform_3fv (unis [&ALBEDO], &pitch_colors [i]); - i != grass_index - }); - - glezz::uniform_3fv (unis [&ALBEDO], &magenta); - for arrow in &renderable_arrows { - let mvp = view_mat * arrow.model_mat; - - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - let object_space_light = make_object_space_vec (&arrow.inv_model_mat, &light); - let object_space_sky = make_object_space_vec (&arrow.inv_model_mat, &Vec3::from ((0.0, 0.0, 1.0))); - glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); - glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky); - - mesh_arrow.draw_all (attrs, |_| true); - } - - let draw_sky = true; - if draw_sky { - glezz::uniform_matrix_4fv (unis [&MVP], &sky_mvp_mat); - glezz::uniform_3fv (unis [&ALBEDO], &white); - glezz::uniform_3fv (unis [&MIN_BRIGHT], &white); - glezz::uniform_3fv (unis [&MIN_ALBEDO], &black); - glezz::uniform_1i (unis [&TEXTURE], 0); - - mesh_sky.draw_all (attrs, |_| true); - } - }); - - shader_shadow.with (|shader_vars| { - 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); - 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); - } - - let view_mat = view_mat * shadow_mat; - - let mvp = view_mat * pumpkin_model_mat; - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - - mesh_pumpkin.draw_all (attrs, |_| true); - - let mvp = view_mat * world_model_mat; - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - - mesh_pitch.draw_all (attrs, |i| i != grass_index); - - for renderable_arrow in &renderable_arrows { - let mvp = view_mat * renderable_arrow.model_mat; - - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - mesh_arrow.draw_all (attrs, |_| true); - } - }); - - shader_diffuse.with (|shader_vars| { - let unis = shader_vars.unis; - let attrs = shader_vars.attrs; - - // Pass 3: Draw lit ground - 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); - - let object_space_sky = make_object_space_vec (&inverse_pumpkin, &Vec3::from ((0.0, 0.0, 1.0))); - - glezz::uniform_3fv (unis [&MIN_BRIGHT], &black); - glezz::uniform_3fv (unis [&MIN_ALBEDO], &white); - glezz::uniform_3fv (unis [&OBJECT_SPACE_SKY], &object_space_sky); - - let mvp = view_mat * world_model_mat; - glezz::uniform_matrix_4fv (unis [&MVP], &mvp); - - glezz::uniform_3fv (unis [&ALBEDO], &pitch_colors [grass_index]); - - glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &Vec3::from ((0.0, 0.0, 0.0))); - mesh_pitch.draw (attrs, grass_index); - - // Pass 4: Draw shadowed ground - unsafe { - gl::StencilFunc (gl::EQUAL, 0, 1); - gl::StencilOp (gl::KEEP, gl::KEEP, gl::KEEP); - } - glezz::uniform_3fv (unis [&OBJECT_SPACE_LIGHT], &object_space_light); - mesh_pitch.draw (attrs, grass_index); - }); + graphics.draw (&state, &renderable_arrows); window.gl_swap_window ();