thin_engine/lib.rs
1//! A thin game engine (hence the name). Drawing done with `glium`, game variables done with
2//! `glium-types`, windowing done with `winit` and input done with `winit-input-map`. It has easy fxaa
3//! support and low boilerplate despite having lots of control.
4//! ```
5//! use thin_engine::{prelude::*, meshes::screen};
6//! use std::{cell::RefCell, rc::Rc};
7//! use Action::*;
8//!
9//! #[derive(Hash, PartialEq, Eq, Clone, Copy)]
10//! enum Action {
11//! Left,
12//! Right,
13//! Jump,
14//! Exit
15//! }
16//! let event_loop = EventLoop::new().unwrap();
17//! event_loop.set_control_flow(ControlFlow::Poll);
18//! let mut input = { use base_input_codes::*; input_map!(
19//! (Left, KeyA, MouseButton::Left, ArrowLeft, DPadLeft),
20//! (Right, KeyD, MouseButton::Right, ArrowRight, DPadRight),
21//! (Jump, KeyW, ArrowUp, Space, GamepadInput::South),
22//! // square brackets mean that all input codes must be pressed for the bind to be pressed
23//! (Exit, [ControlLeft, Escape], GamepadInput::Start)
24//! )};
25//!
26//! struct Graphics {
27//! box_indices: IndexBuffer<u32>,
28//! box_vertices: VertexBuffer<Vertex>,
29//! box_shader: Program
30//! }
31//! let graphics: Rc<RefCell<Option<Graphics>>> = Rc::default();
32//! let graphics_setup = graphics.clone();
33//!
34//! let mut player_pos = Vec2::ZERO;
35//! let mut player_gravity = 0.0;
36//! let mut player_can_jump = true;
37//!
38//! // camera matrix must be inverse
39//! let camera = Mat4::from_scale(Vec3::splat(10.0)).inverse();
40//!
41//! let settings = Settings::from_fps(60); // target of 60 fps
42//! let mut frame_start = Instant::now();
43//! thin_engine::builder(input).with_setup(move |display, window, _| {
44//! let (box_indices, box_vertices) = mesh!(
45//! display, &screen::INDICES, &screen::VERTICES
46//! ).unwrap();
47//! let box_shader = Program::from_source(
48//! display,
49//! "#version 140
50//! in vec3 position;
51//!
52//! uniform mat4 perspective;
53//! uniform mat4 camera;
54//! uniform mat4 model;
55//!
56//! void main() {
57//! gl_Position = perspective * camera * model * vec4(position, 1);
58//! }",
59//! "#version 140
60//! out vec4 colour;
61//! void main() {
62//! colour = vec4(1.0, 0.0, 0.0, 1.0);
63//! }", None
64//! ).unwrap();
65//! graphics_setup.replace(Some(Graphics {
66//! box_vertices, box_indices, box_shader
67//! }));
68//! }).with_update(move |input, display, _settings, target, window| {
69//! // gets time between frames
70//! let delta_time = frame_start.elapsed().as_secs_f32();
71//! frame_start = Instant::now();
72//!
73//! if input.pressed(Exit) { target.exit() }
74//!
75//! // game logic
76//! player_pos.x += input.axis(Right, Left) * 10.0 * delta_time;
77//! player_gravity += delta_time * 50.0;
78//! player_pos.y -= player_gravity * delta_time;
79//! if player_pos.y < 0.0 {
80//! player_pos.y = 0.0;
81//! player_can_jump = true;
82//! }
83//! if player_can_jump && input.pressed(Jump) {
84//! player_gravity = -20.0;
85//! player_can_jump = false;
86//! }
87//!
88//! let graphics = graphics.borrow();
89//! let Graphics {
90//! box_vertices, box_indices, box_shader
91//! } = graphics.as_ref().unwrap();
92//! // set up frame
93//! let mut frame = display.draw();
94//! let perspective = Mat4::perspective_2d(frame.get_dimensions());
95//!
96//! // draw
97//! frame.clear_color(0.0, 0.0, 0.0, 1.0);
98//! frame.draw(
99//! box_vertices, box_indices,
100//! box_shader, &uniform! {
101//! perspective: perspective, camera: camera,
102//! model: Mat4::from_pos(player_pos.extend(0.0)),
103//! }, &DrawParameters::default()
104//! );
105//! window.pre_present_notify();
106//! frame.finish().unwrap();
107//! }).with_settings(Settings::from_fps(60))
108//! .build(event_loop).unwrap();
109//! ```
110
111#![allow(deprecated)]
112use glium::backend::glutin::SimpleWindowBuilder;
113pub use gilrs;
114use gilrs::Gilrs;
115pub use glium;
116pub use glium_types;
117pub use winit;
118pub use winit_input_map as input_map;
119use std::time::Duration;
120
121/// Run time settings for thin engine including gamepad settings (through gilrs) and fps settings.
122/// when running `default()` the gamepads may fail to initialise and the program will continue
123/// running after printing the error. If this is undesirable use `with_gamepads()` instead.
124pub struct Settings {
125 pub gamepads: Option<Gilrs>,
126 pub min_frame_duration: Option<Duration>
127}
128impl Settings {
129 pub fn new(gamepads: Option<Gilrs>, min_frame_duration: Option<Duration>) -> Self {
130 Self { gamepads, min_frame_duration }
131 }
132 /// creates settings with the minimum frame duration set to 1 / fps.
133 pub fn from_fps(fps: u32) -> Self {
134 let gamepads = Gilrs::new().map_err(|i| eprintln!("{i}")).ok();
135 let min_frame_duration = Some(Duration::from_secs_f32(1.0/fps as f32));
136
137 Self::new(gamepads, min_frame_duration)
138 }
139 /// guarantees gamepads will be set instead of printing an error and moving on.
140 pub fn with_gamepads() -> Result<Self, gilrs::Error> {
141 let gilrs = Gilrs::new()?;
142 Ok(Self::new(Some(gilrs), None))
143 }
144 /// sets the minimum frame duration to 1 / fps or none if inputed.
145 pub fn set_target_fps(&mut self, target_fps: Option<u32>) {
146 let min_duration = target_fps.map(|i| Duration::from_secs_f32(1.0/i as f32));
147 self.min_frame_duration = min_duration;
148 }
149 pub fn get_fps(&self) -> Option<u32> {
150 self.min_frame_duration.map(|i| (1.0 / i.as_secs_f64()).round() as u32)
151 }
152}
153impl Default for Settings {
154 fn default() -> Self {
155 let gamepads = Gilrs::new().map_err(|i| println!("{i}")).ok();
156 Self::new(gamepads, None)
157 }
158}
159pub mod meshes;
160pub mod shaders;
161pub mod application;
162#[cfg(feature = "text")]
163pub mod text_renderer;
164
165pub type Display = glium::Display<glium::glutin::surface::WindowSurface>;
166
167use winit_input_map::InputMap;
168use crate::application::ThinBuilder;
169use std::hash::Hash;
170pub fn builder<'a, H: Hash + Eq + Copy>(input_map: InputMap<H>) -> ThinBuilder<'a, H> {
171 ThinBuilder::<'a, H>::new(input_map)
172}
173
174pub mod prelude {
175 pub use crate::application::*;
176 pub use glium::{
177 draw_parameters, IndexBuffer, self,
178 VertexBuffer, Program, Texture2d,
179 uniform, Surface, Frame, DrawParameters,
180 backend::glutin::simple_window_builder::SimpleWindowBuilder
181 };
182 pub use crate::Settings;
183 pub use std::time::{Duration, Instant};
184 pub use std::thread;
185 pub use glium_types::prelude::*;
186 pub use crate::{meshes, shaders};
187 pub use winit::{self, event::MouseButton, keyboard::KeyCode};
188 pub use gilrs::ev::{Button as GamepadButton, Axis as GamepadAxis};
189 pub use winit::{event_loop::*, window::{Fullscreen, CursorGrabMode}};
190 pub use crate::input_map::*;
191}
192/// resizable depth texture. recomended to use with gliums `SimpleFrameBuffer` to draw onto a texture you can use
193/// in another shader! usefull for fxaa
194#[derive(Default)]
195pub struct ResizableTexture2d {
196 pub size: (u32, u32),
197 pub texture: Option<glium::Texture2d>
198}
199impl ResizableTexture2d {
200 pub fn resize(&mut self, display: &Display, new_size: (u32, u32)) {
201 if self.size.0 != new_size.0 || self.size.1 != new_size.1 {
202 self.texture = glium::Texture2d::empty(display, new_size.0, new_size.1).ok();
203 self.size = new_size;
204 }
205 }
206 pub fn resize_to_display(&mut self, display: &Display) {
207 let new_size = display.get_framebuffer_dimensions();
208 if self.size.0 != new_size.0 || self.size.1 != new_size.1 {
209 self.texture = glium::Texture2d::empty(display, new_size.0, new_size.1).ok();
210 self.size = new_size;
211 }
212 }
213 /// borrows the texture or panics. to handle failed borrows use `self.texture.as_ref()` instead
214 pub fn texture(&self) -> &glium::Texture2d {
215 self.texture.as_ref().expect("texture was not initialised. maybe use 'new()' instead of 'default()'")
216 }
217 pub fn new(size: (u32, u32), display: &Display) -> Self {
218 Self { size, texture: glium::Texture2d::empty(display, size.0, size.1).ok() }
219 }
220}
221/// resizable depth texture. use with gliums `SimpleFrameBuffer::WithDepthTexture()`
222/// to create a texture you can draw on! usefull for things like fog and fxaa.
223#[derive(Default)]
224pub struct ResizableDepthTexture2d {
225 size: (u32, u32),
226 pub texture: Option<glium::texture::DepthTexture2d>
227}
228impl ResizableDepthTexture2d {
229 pub fn resize(&mut self, display: &Display, new_size: (u32, u32)) {
230 if self.size.0 != new_size.0 || self.size.1 != new_size.1 {
231 self.texture = glium::texture::DepthTexture2d::empty(display, new_size.0, new_size.1).ok();
232 self.size = new_size;
233 }
234 }
235 /// borrows the texture or panics. to handle failed borrows use `self.texture.as_ref()` instead
236 pub fn texture(&self) -> &glium::texture::DepthTexture2d {
237 self.texture.as_ref().expect("texture was not initialised. maybe use 'new()' instead of 'default()'")
238 }
239 pub fn new(size: (u32, u32), display: &Display) -> Self {
240 Self { size, texture: glium::texture::DepthTexture2d::empty(display, size.0, size.1).ok() }
241 }
242 pub fn resize_to_display(&mut self, display: &Display) {
243 let new_size = display.get_framebuffer_dimensions();
244 if self.size.0 != new_size.0 || self.size.1 != new_size.1 {
245 self.texture = glium::texture::DepthTexture2d::empty(display, new_size.0, new_size.1).ok();
246 self.size = new_size;
247 }
248 }
249}