digging/src/main.rs

263 lines
7.8 KiB
Rust

use std::time::{Duration, Instant};
use anymap::AnyMap;
use cgmath::Matrix3;
use glium::{
glutin, index::PrimitiveType, texture::Texture2d, uniform, Display, Frame, IndexBuffer,
Surface, VertexBuffer,
};
use glium_glyph::{glyph_brush::Section, GlyphBrush};
use glutin::{
event::{Event, WindowEvent},
event_loop::ControlFlow,
};
use resources::Shaders;
use simplelog::{TermLogger, TerminalMode};
use state::GameState;
pub mod resources;
pub mod state;
const TIMESTEP: f32 = 1.0 / 20.0;
const SCREEN_WIDTH: f32 = 320.0;
const SCREEN_HEIGHT: f32 = 240.0;
#[derive(Clone, Copy)]
struct Vertex {
position: [f32; 2],
tex_coords: [f32; 2],
}
glium::implement_vertex!(Vertex, position, tex_coords);
// Where overall state is kept.
struct State {
current_fps: u32,
render_texture: Texture2d,
render_vertices: VertexBuffer<Vertex>,
screen_scale: (f32, f32),
current_state: Box<dyn GameState>,
next_state: Option<Box<dyn GameState>>,
resources: AnyMap,
}
impl State {
fn new(display: &Display) -> State {
// FPS thingies
let current_fps = 0;
let render_texture =
Texture2d::empty(display, SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32).unwrap();
let render_vertices = VertexBuffer::new(
display,
&[
Vertex {
position: [-1.0, -1.0],
tex_coords: [0.0, 0.0],
},
Vertex {
position: [-1.0, 1.0],
tex_coords: [0.0, 1.0],
},
Vertex {
position: [1.0, 1.0],
tex_coords: [1.0, 1.0],
},
Vertex {
position: [1.0, -1.0],
tex_coords: [1.0, 0.0],
},
],
)
.unwrap();
let screen_scale = {
let size = display.gl_window().window().inner_size();
(
size.height as f32 / SCREEN_HEIGHT * SCREEN_WIDTH / size.width as f32,
size.width as f32 / SCREEN_WIDTH * SCREEN_HEIGHT / size.height as f32,
)
};
// Set initial state
let current_state = Box::new(state::TestState);
let next_state = None;
// The resources map
let mut resources = AnyMap::new();
resources::load_resources(display, &mut resources);
// Package it all together UwU
State {
current_fps,
render_texture,
render_vertices,
screen_scale,
current_state,
next_state,
resources,
}
}
fn input(&mut self, event: &Event<()>) {
self.current_state.input(&mut self.resources, event);
}
fn update(&mut self, dt: f32) {
// Swap state if new one is present
let mut next_state = None;
std::mem::swap(&mut next_state, &mut self.next_state);
if let Some(state) = next_state {
self.current_state = state;
}
// Run current state's update
self.next_state = self.current_state.update(&mut self.resources, dt);
}
fn render(&mut self, display: &Display, target: &mut Frame) {
{
let mut texture_target = self.render_texture.as_surface();
texture_target.clear_color_srgb(0.0, 0.0, 0.0, 1.0);
self.current_state
.render(&mut self.resources, display, &mut texture_target);
}
let render_indices =
IndexBuffer::new(display, PrimitiveType::TriangleStrip, &[1, 2, 0, 3 as u16]).unwrap();
let program = self.resources.get::<Shaders>().unwrap().get("crt").unwrap();
let mut scale = (1.0, 1.0);
if self.screen_scale.0 < self.screen_scale.1 {
scale.0 = self.screen_scale.0;
} else {
scale.1 = self.screen_scale.1;
}
let scale_matrix = Matrix3::from_nonuniform_scale(scale.0, scale.1);
let uniforms = uniform! {
matrix: cgmath::conv::array3x3(scale_matrix),
texture: self.render_texture.sampled().magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
screen_resolution: [SCREEN_WIDTH, SCREEN_HEIGHT],
/*curvature: [3.0, 3.0 as f32],
scanline_opacity: [0.5, 0.5 as f32],
brightness: 2.0 as f32,*/
};
target
.draw(
&self.render_vertices,
&render_indices,
program,
&uniforms,
&Default::default(),
)
.unwrap();
}
}
// It is the entrypoint, duh.
fn main() {
// Set up the logger
TermLogger::init(
log::LevelFilter::Info,
simplelog::Config::default(),
TerminalMode::Mixed,
)
.unwrap();
log::info!("Logger is set up!");
// Set up windowing and graphics context
let event_loop = glutin::event_loop::EventLoop::new();
let display = {
let wb = glutin::window::WindowBuilder::new();
let cb = glutin::ContextBuilder::new().with_vsync(true);
Display::new(wb, cb, &event_loop).unwrap()
};
// The main state of program or whatever
let mut state = State::new(&display);
// Timing stuff
let mut last_update = Instant::now();
let mut update_accumulator = 0.0;
let mut next_fps_check = Instant::now() + Duration::from_secs(1);
let mut frame_counter: u32 = 0;
// Run the main event loop
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
// Stuff to happen every frame.
Event::MainEventsCleared => {
// Game timestep and state update stuff.
update_accumulator += last_update.elapsed().as_secs_f32();
last_update = Instant::now();
while update_accumulator >= TIMESTEP {
update_accumulator -= TIMESTEP;
state.update(TIMESTEP);
}
// Measure framerate
let now = Instant::now();
frame_counter += 1;
if now >= next_fps_check {
state.current_fps = frame_counter;
frame_counter = 0;
next_fps_check = now + Duration::from_secs(1);
}
// Let us render some shit!
let mut target = display.draw();
target.clear_color_srgb(0.0, 0.0, 0.0, 1.0);
state.render(&display, &mut target);
// Draw the FPS Indicator
let glyph_brush = state.resources.get_mut::<GlyphBrush>().unwrap();
glyph_brush.queue(Section {
text: &format!("FPS: {:?}", state.current_fps),
screen_position: (0.0, 0.0),
color: [1.0, 1.0, 1.0, 1.0],
..Default::default()
});
glyph_brush.draw_queued(&display, &mut target);
target.finish().unwrap();
}
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
state.screen_scale = (
size.height as f32 / SCREEN_HEIGHT * SCREEN_WIDTH / size.width as f32,
size.width as f32 / SCREEN_WIDTH * SCREEN_HEIGHT / size.height as f32,
);
}
// Quit if window wants to be closed
// TODO: Check state to see if allowed to exit.
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
*control_flow = ControlFlow::Exit;
}
// Cleanup here
Event::LoopDestroyed => {}
event => {
state.input(&event);
}
}
});
}