use std::{ collections::HashMap, }; use anyhow::Result; use maplit::hashmap; use opengl_rust::{ prelude::*, gl_state::*, physics::PhysicsBody, renderable_model::{ attributes, RenderableModel, }, shader_closure::ShaderLookup, texture::Texture, }; mod graphics; use graphics::Graphics; pub struct GameState { player: PhysicsBody, aabbs: Vec , } impl Default for GameState { fn default () -> Self { let player = Default::default (); let aabbs = [ ((-4.0, -4.0, -3.0), (4.0, 4.0, -1.0)), ((-1.5, 1.0, -1.0), (-0.5, 2.0, 0.0)), ((-0.5, 1.0, 0.0), (0.5, 2.0, 1.0)), ((0.5, 1.0, 1.0), (1.5, 2.0, 2.0)), ].into_iter () .map (|(min, max)| { opengl_rust::physics::Aabb { min: min.into (), max: max.into (), } }) .collect (); Self { player, aabbs, } } } #[tokio::main] async fn main () -> Result <()> { tracing_subscriber::fmt::fmt () .with_env_filter (tracing_subscriber::EnvFilter::from_default_env()) .with_span_events (tracing_subscriber::fmt::format::FmtSpan::CLOSE) .init (); let sdl_context = sdl2::init ().map_err (|e| anyhow! ("Can't init SDL: {}", e))?; let video_subsystem = sdl_context.video ().map_err (|e| anyhow! ("Can't get SDL video subsystem: {}", e))?; let window = video_subsystem.window ("3D platformer", 320, 240) .position_centered () .opengl () .build () ?; gl::load_with (|s| { video_subsystem.gl_get_proc_address (s) as *const _ }); if ! gl::ClearColor::is_loaded () { bail! ("gl::ClearColor didn't load, something is wrong with OpenGL"); } let gl_ctx = window.gl_create_context ().map_err (|e| anyhow! ("Can't create OpenGL context: {}", e))?; window.gl_make_current (&gl_ctx).map_err (|e| anyhow! ("Can't make OpenGL context current: {}", e))?; let mut event_pump = sdl_context.event_pump ().unwrap (); let mut time_step = TimeStep::new (60, 1000); let level = gltf::Gltf::open ("gltf/level-00.gltf")?; for scene in level.scenes () { for node in scene.nodes () { println! ( "Node #{} has {} children", node.index(), node.children().count(), ); } } let mut graphics = Graphics::new (); let mut gl_state = Default::default (); let mut game_state = GameState::default (); let phys_params = opengl_rust::physics::Params { dt: 1.0 / 60.0, gravity: (0.0, 0.0, -0.25).into (), margin: 0.00125, }; let player_speed = 2.0; let player_jump_speed = 8.0; let mut player_jump_vec: Option = None; 'running: loop { let _frames_to_do = time_step.step (); let mut player_wants_to_jump = false; for event in event_pump.poll_iter () { match event { Event::Quit {..} | Event::KeyDown { keycode: Some (Keycode::Escape), .. } => { break 'running }, Event::KeyDown { keycode: Some (Keycode::R), .. } => { game_state = Default::default (); }, Event::KeyDown { keycode: Some (Keycode::Space), .. } => { player_wants_to_jump = true; }, _ => (), } } let kb_state = event_pump.keyboard_state (); let player_speed = if player_jump_vec.is_some () { game_state.player.vel.x = 0.0; game_state.player.vel.y = 0.0; player_speed } else { 0.125 }; if kb_state.is_scancode_pressed (Scancode::Left) { game_state.player.vel.x -= player_speed; } if kb_state.is_scancode_pressed (Scancode::Right) { game_state.player.vel.x += player_speed; } if kb_state.is_scancode_pressed (Scancode::Up) { game_state.player.vel.y += player_speed; } if kb_state.is_scancode_pressed (Scancode::Down) { game_state.player.vel.y -= player_speed; } if player_wants_to_jump { if let Some (normal) = player_jump_vec.clone () { game_state.player.vel += normal * player_jump_speed; } } let phys_result = opengl_rust::physics::step (&phys_params, &[], &game_state.aabbs, 0.5, &game_state.player); game_state.player = phys_result.body; // tracing::debug! ("player pos: {}", game_state.player.pos); // dbg! (player_jump_vec); player_jump_vec = None; for normal in &phys_result.normals_hit { player_jump_vec = Some (match player_jump_vec { None => *normal, Some (old) => { if normal.z > old.z { *normal } else { old } }, }); } window.gl_make_current (&gl_ctx).unwrap (); graphics.draw (&game_state, &mut gl_state); graphics.frames += 1; window.gl_swap_window (); tokio::time::sleep (Duration::from_millis (15)).await; } Ok (()) } struct ShaderLocations { attr_pos: u32, attr_normal: u32, attr_color: u32, uni_mvp: i32, } impl ShaderLocations { 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"); Ok (Self { attr_pos: attr ("attr_pos")?, attr_normal: attr ("attr_normal")?, attr_color: attr ("attr_color")?, uni_mvp: uni ("uni_mvp")?, }) } } struct TriangleStream { pub verts: gpu_buffers::VertexBuffer, pub indices: gpu_buffers::IndexBuffer, }