1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
//! # Introduction
//! ## Window
//! ### Creating the Window
//! To create a simple window you can do:
//!
//! ```rust
//! use tinystorm::window::{Window, WindowBuilder};
//!
//! let mut window: Window = WindowBuilder::default().build();
//! while window.is_running() {
//! window.poll_events();
//! window.swap_buffers();
//! }
//! ```
//!
//! But if you want more percise control over the window creation, you can do:
//!
//! ```rust
//! let mut window: Window = WindowBuilder::default()
//! .with_size(720, 480) // Default: 960, 540
//! .with_title("Ne chitay eto :}") // Default: "Titled window, lol"
//! .with_vsync(false) // Default: true
//! .with_max_fps(144 * 5) // Default: WindowBuilder::NO_MAX_FPS
//! .with_msaa(4) // (aka. 4 samples per pixel) Default: WindowBuilder::NO_MSAA
//! .build();
//! ```
//!
//! Let's breakdown trough each setting:
//! - ``with_size(width: u32, height: u32)`` - sets size of the window.
//! - ``with_title(title: String)`` - sets the title of the window.
//! - ``with_vsync(vsync: bool)`` - enables/disables vertical synchronization
//! (if your display refresh rate is 60, FPS would clamp to it).
//! - ``with_max_fps(max_fps: u32)`` - sets maximum FPS.
//! But if vsync enabled and display refresh rate is lower than max FPS - it would clamp to vsync instead.
//! You can use ``WindowBuilder::NO_MAX_FPS`` for better readability.
//! - ``with_msaa(msaa_quality: u32)`` - if greater than 0, enables msaa with ``msaa_quality`` samples.
//! You can use ``WindowBuilder::NO_MSAA`` for better readability.
//! ### Working with the Window
//! Here's a simple example of working with certain parts of the Window
//!
//! ```rust
//! use tinystorm::{window::{Window, WindowBuilder}, glfw::Key, glfw::MouseButton};
//!
//! let mut window: Window = WindowBuilder::default().build();
//! while window.is_running() {
//! window.poll_events();
//!
//! // Keyboard input handling
//! if window.is_key_pressed(Key::Space) {
//! println!("It would print this message each frame while Space key is pressed.");
//! }
//! if window.is_key_just_pressed(Key::Escape) {
//! println!("It would print this message only at the frame when Escape key was pressed.");
//! }
//!
//! // Mouse button input handling
//! if window.is_mouse_button_pressed(MouseButton::Left) {
//! println!("The same thing as window.is_key_pressed but with mouse buttons instead.");
//! }
//! if window.is_mouse_button_just_pressed(MouseButton::Middle) {
//! println!("The same thing as window.is_key_just_pressed but with mouse buttons instead.");
//! }
//!
//! // Mouse cursor input handling
//! println!(
//! "Mouse position in pixels from top-left corner of the window: X {}, Y {}.",
//! window.get_mouse_x(),
//! window.get_mouse_y(),
//! );
//! println!(
//! "Mouse movement on this frame: X {}, Y {}.",
//! window.get_mouse_dx(),
//! window.get_mouse_dy(),
//! );
//! println!("Is mouse grabbed: {}.", window.is_mouse_grabbed());
//!
//! // Working with time
//! // ps. You can also do window.get_delta_raw().as_secs_f32()
//! println!("Current frame delta time in seconds: {}.", window.get_delta());
//!
//! // Getting window info
//! println!("Window size in pixels: Width {}, Height {}.", window.get_width(), window.get_height());
//! println!(
//! "Window position in pixels from top-left corner: X {}, Y {}.",
//! window.get_x(),
//! window.get_y(),
//! );
//!
//! println!("Window title: {}.", window.get_title());
//! println!("Window aspect: {}.", window.get_aspect());
//!
//! window.swap_buffers();
//! }
//! ```
//!
//! Example usage
//!
//! ```rust
//! if window.is_key_just_pressed(Key::E) {
//! window.toggle_mouse(); // If mouse is grabbed - it would be released, else - it would be grabbed back.
//! }
//! if window.is_key_just_pressed(Key::Q) {
//! window.grab_mouse();
//! }
//! if window.is_key_just_pressed(Key::W) {
//! window.release_mouse();
//! }
//! if window.is_key_just_pressed(Key::R) {
//! window.set_title(String::from("Oh no! You've just changed the window title!"));
//! }
//! if window.is_key_pressed(Key::T) {
//! // ps. sets the window position from top-left corner.
//! window.set_position(50, 100); // You can set x/y separately using window.set_x/y(value);
//! }
//! if window.is_key_pressed(Key::Y) {
//! // ps. sets the window size.
//! window.set_size(100, 300); // You can set width/height separately using window.set_width/height(value);
//! }
//! ```
//!
//! ## Mesh
//! ### Creating the Mesh
//! To create a simple mesh you can do:
//!
//! ```rust
//! use tinystorm::{mesh::{Layout, Mesh}, gl};
//!
//! // Create the window first
//! let mesh = Mesh::new::<f32>(&[
//! -0.5, -0.5,
//! 0.5, -0.5,
//! 0.0, 0.5,
//! ], &Layout::basic_2d(), gl::TRIANGLES); // A simple triangle mesh
//! ```
//!
//! You can also define your own layout:
//!
//! ```rust
//! use tinystorm::{mesh::{Attribute, Layout, Mesh}, gl};
//!
//! // Create the window first
//! // X, Y, U, V, R, G, B,
//! let mesh = Mesh::new::<f32>(&[
//! -0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 0.0,
//! 0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 0.0,
//! 0.5, 0.5, 1.0, 1.0, 0.0, 0.0, 1.0,
//! -0.5, 0.5, 0.0, 1.0, 1.0, 1.0, 0.0,
//! ], &Layout::default()
//! .next_attribute(Attribute::Vec2) // XY. In GLSL: layout(location=0) in vec2 a_Position;
//! .next_attribute(Attribute::Vec2) // UV. In GLSL: layout(location=1) in vec2 a_TexCoord;
//! .next_attribute(Attribute::Vec3) // RGB. In GLSL: layout(location=2) in vec3 a_Color;
//! , gl::TRIANGLE_FAN);
//! ```
//!
//! Also you're allowed to send custom vertex structs!
//!
//! ```rust
//! #[repr(C)]
//! struct Vertex {
//! position: (f32, f32),
//! texcoord: (f32, f32),
//! color: (f32, f32, f32),
//! }
//! impl Vertex {
//! pub fn new(position: (f32, f32), texcoord: (f32, f32), color: (f32, f32, f32)) -> Self {
//! Self { position, texcoord, color }
//! }
//! }
//!
//! let mesh = Mesh::new::<Vertex>(&[ // IMPORTANT! You need to declare ::<Vertex> for proper data packing
//! Vertex::new((-0.5, -0.5), (0.0, 0.0), (1.0, 0.0, 0.0)),
//! Vertex::new(( 0.5, -0.5), (1.0, 0.0), (0.0, 1.0, 0.0)),
//! Vertex::new(( 0.5, 0.5), (1.0, 1.0), (0.0, 0.0, 1.0)),
//! Vertex::new((-0.5, 0.5), (0.0, 1.0), (1.0, 1.0, 0.0)),
//! ], &Layout::default()
//! .next_attribute(Attribute::Vec2) // XY. In GLSL: layout(location=0) in vec2 a_Position;
//! .next_attribute(Attribute::Vec2) // UV. In GLSL: layout(location=1) in vec2 a_TexCoord;
//! .next_attribute(Attribute::Vec3) // RGB. In GLSL: layout(location=2) in vec3 a_Color;
//! , gl::TRIANGLE_FAN);
//! ```
//!
//! #### If you want to use indices in your mesh, just use ``IndexedMesh`` instead and add your indices as the first argument.
//!
//! ```rust
//! use tinystorm::mesh::{IndexedMesh, Layout};
//!
//! /* Indices visualized:
//! * 4
//! / \
//! / \
//! 3 *---* 2
//! | |
//! 0 *---* 1
//! */
//! let mesh = IndexedMesh::new::<f32>(&[
//! 0, 1, 3, // Bottom-left triangle of the wall
//! 2, 3, 1, // Top-right triangle of the wall
//! 3, 2, 4, // Roof triangle
//! ], &[
//! -0.5, -0.5,
//! 0.5, -0.5,
//! 0.5, 0.5,
//! -0.5, 0.5,
//! 0.0, 1.0,
//! ], &Layout::basic_2d(), gl::TRIANGLES);
//! ```
//!
//! ### Rendering the mesh
//! To render the mesh you can just call ``yourmesh.draw();``
//!
//! But it's not enough, to see anything on your screen you need to call:
//! ``unsafe { gl::Clear(gl::COLOR_BUFFER_BIT); }``
//! Or, if you want to have a 3D game:
//! ``unsafe { gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); }``
//! #### Example:
//! ```rust
//! while window.is_running() {
//! window.poll_events();
//! unsafe { gl::Clear(gl::COLOR_BUFFER_BIT); }
//!
//! mesh1.draw();
//! mesh2.draw();
//! // etc. (It's allowed to draw the same mesh multiple times with no issues!)
//!
//! window.swap_buffers();
//! }
//! ```
//!
//! ## Shader
//! ### Loading shaders
//! To load vertex and fragment shaders from files you can do:
//! ```rust
//! use tinystorm::shader::Shader;
//!
//! // Create the window first.
//! // Vertex shader path Fragment shader path
//! let shader = Shader::new("./assets/shaders/test.vert", "./assets/shaders/test.frag");
//! ```
//!
//! ### Using shaders
//! ```rust
//! while window.is_running() {
//! window.poll_events();
//! unsafe { gl::Clear(gl::COLOR_BUFFER_BIT); }
//!
//! shader.bind();
//! shader.set_float("u_Aspect", window.get_aspect()); // There's many other uniform types you can use.
//!
//! // If you'll draw mesh while certain shader is bound, this shader would apply to the mesh.
//! mesh.draw(); // In this case there's "shader" shader bound.
//! Shader::unbind(); // You can use that if you really need to have no shaders bound at all.
//!
//! mesh.draw(); // In this case there's no shader bound.
//! window.swap_buffers();
//! }
//! ```
//!
//! ## Texture
//! ### Loading textures
//! To load texture from file you can do:
//! ```rust
//! use tinystorm::{texture::Texture, gl};
//!
//! // Create the window first.
//! let texture = Texture::load_from_file("./assets/textures/super_mario.png", gl::NEAREST, gl::CLAMP_TO_EDGE);
//! ```
//!
//! ### Using textures
//! To use texture you need to bind it before rendering the mesh to a certain texture slot.
//! #### Example
//!
//! ```rust
//! while window.is_running() {
//! window.poll_events();
//! unsafe { gl::Clear(gl::COLOR_BUFFER_BIT); }
//!
//! shader.bind();
//! shader.set_int("u_ColorSampler", 0); // Just bind uniform sampler2D u_ColorSampler; to texture slot 0.
//!
//! texture.bind(0); // Binding texture to a slot 0.
//! mesh.draw();
//! Texture::unbind(); // You can use that if you really need to have no textures bound at all.
//! }
//! ```
pub use glfw;
pub use gl;
pub use nalgebra;