use std::{ collections::HashMap, }; use opengl_rust::{ prelude::*, gl_state::{ FrontFace, GlState, Pass, }, renderable_model::{ attributes, RenderableModel, }, shader_closure::ShaderLookup, texture::Texture, }; use crate::{ GameState, TriangleStream, }; mod uniforms { use iota::iota; iota! { pub const MVP: u32 = iota; , OBJECT_SPACE_LIGHT , OBJECT_SPACE_SKY , ALBEDO , MIN_ALBEDO , MIN_BRIGHT , TEXTURE } } pub struct Graphics { passes: Vec , shaders: Vec , shader_lookup: HashMap , mesh_cube: RenderableModel, mesh_sky: RenderableModel, mesh_sphere: RenderableModel, text_stream: TriangleStream, texture_crate: Texture, texture_earth: Texture, texture_sky: Texture, texture_white: Texture, pub frames: u64, } impl ShaderLookup for Graphics { fn lookup <'a> (&'a self, id: u32) -> &'a ShaderClosure { &self.shaders [self.shader_lookup [&id]] } } impl Graphics { pub fn new () -> Self { 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 mesh_sphere = renderable_from_iqm_file ("sphere.iqm"); let passes = vec![ // Clear everything Pass::default () .color_mask ([1, 1, 1, 1]) .depth_mask (1) .clone (), // Draw world Pass::default () .shader (&shaders [0]) .flags ([ (gl::CULL_FACE, false), (gl::DEPTH_TEST, true), (gl::TEXTURE_2D, true), (gl::STENCIL_TEST, false), ].into_iter ()) .front_face (FrontFace::Ccw) .color_mask ([1, 1, 1, 1]) .depth_mask (1) .clone (), ]; 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]); }); Graphics { mesh_cube, mesh_sky, mesh_sphere, passes, shader_lookup, shaders, text_stream, texture_crate: Texture::from_file ("crate.png"), texture_earth: Texture::from_file ("earth.png"), texture_sky: Texture::from_file ("sky.png"), texture_white: Texture::from_file ("white.png"), frames: 0, } } pub fn draw ( &self, state: &GameState, gl_state: &mut GlState, level: &crate::LoadedLevel, camera: &crate::Camera, ) { 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 shadow_blue = color_from_255 ((68.0, 36.0, 52.0)); let screen_size = (320.0, 240.0); let fov = 39.6f32 * screen_size.1 / screen_size.0; let proj_mat = Mat4::perspective_rh_gl (fov.to_radians (), screen_size.0 / screen_size.1, 0.125, 200.0); let view_mat = proj_mat * camera.mat; let world_model_mat = Mat4::IDENTITY; self.passes [0].with (gl_state, || { glezz::clear_color (109f32 / 255.0, 194f32 / 255.0, 202f32 / 255.0, 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; glezz::uniform_3fv (unis [&u::ALBEDO], &white); glezz::uniform_3fv (unis [&u::MIN_BRIGHT], &white); if true { self.texture_earth.bind (); let mvp = view_mat * Mat4::from_translation (state.player.pos) * Mat4::from_scale ((0.5, 0.5, 0.5).into ()); glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp); glezz::uniform_3fv (unis [&u::ALBEDO], &white); self.mesh_sphere.draw_all (attrs, |_| { true }); } self.texture_crate.bind (); glezz::uniform_3fv (unis [&u::ALBEDO], &white); if true { use opengl_rust::renderable_model::attributes as a; let scene = level.level.scenes ().next ().unwrap (); unsafe { gl::BindBuffer (gl::ARRAY_BUFFER, 0); gl::BindBuffer (gl::ELEMENT_ARRAY_BUFFER, 0); } for node in scene.nodes () { let mesh = match node.mesh () { None => continue, Some (x) => x, }; let mvp = view_mat * Mat4::from_cols_array_2d (&node.transform ().matrix ()); glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp); for prim in mesh.primitives () { use gltf::{ Semantic, }; let positions = match prim.get (&Semantic::Positions) { None => continue, Some (x) => x, }; let pos_view = match positions.view () { None => continue, Some (x) => x, }; let uv_coords = match prim.get (&Semantic::TexCoords (0)) { None => continue, Some (x) => x, }; let uv_view = match uv_coords.view () { None => continue, Some (x) => x, }; let indices = match prim.indices () { None => continue, Some (x) => x, }; let indices_view = match indices.view () { None => continue, Some (x) => x, }; unsafe { gl::VertexAttribPointer ( attrs [a::POS].unwrap (), 3, gl::FLOAT, 0, 4 * 3, &level.buffer [positions.offset () + pos_view.offset ()] as *const u8 as *const c_void, ); gl::VertexAttribPointer ( attrs [a::UV].unwrap (), 2, gl::FLOAT, 0, 4 * 2, &level.buffer [uv_coords.offset () + uv_view.offset ()] as *const u8 as *const c_void, ); gl::DrawElements ( gl::TRIANGLES, indices.count ().try_into ().unwrap (), gl::UNSIGNED_SHORT, &level.buffer [indices.offset () + indices_view.offset ()] as *const u8 as *const c_void, ); } } } } glezz::uniform_3fv (unis [&u::ALBEDO], &shadow_blue); if true { // Raycast for player shadow let coll = opengl_rust::physics::get_candidate ( &state.phys_tris, &state.aabbs, state.player.pos, state.player.pos + Vec3::new (0.0, 0.0, -100.0), 0.0 ); self.texture_white.bind (); if coll.t <= 1.0 { let mvp = view_mat * Mat4::from_translation (coll.p_impact + Vec3::new (0.0, 0.0, 0.0625)) * Mat4::from_scale ((0.5, 0.5, 0.0).into ()); glezz::uniform_matrix_4fv (unis [&u::MVP], &mvp); self.mesh_sphere.draw_all (attrs, |_| { true }); } } if false { 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); } }); } } 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 )) }