Skip to content

Commit

Permalink
Updated first 4 chapters
Browse files Browse the repository at this point in the history
  • Loading branch information
Melchizedek6809 committed May 4, 2023
1 parent 8b7e70c commit ed1f468
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 274 deletions.
164 changes: 30 additions & 134 deletions book/tuto-01-getting-started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Creating a project

To start this tutorial, we will create a new project from scratch. Even though it's highly recommended to be familiar with Rust and Cargo before starting, some little reminders are always good. Let's start by running:
To start this tutorial, we will create a new project from scratch. Even though it is highly recommended to be familiar with Rust and Cargo before starting, some little reminders are always good. Let's start by running:

```sh
cargo new --bin my_project
Expand All @@ -9,46 +9,42 @@ cd my_project

The directory you have just created should contain a `Cargo.toml` file which contains our project's metadata, plus a `src/main.rs` file which contains the Rust source code. If you have `src/lib.rs` file instead, that means that you forgot the `--bin` flag ; just rename `lib.rs` to `main.rs` then.

In order to use glium, we need to add a couple of dependencies to our `Cargo.toml` file:
In order to use glium, you need to add it as a dependency to your `Cargo.toml` file.

```toml
[dependencies]
winit = "0.28"
raw-window-handle = "0.5"
glutin = "0.30"
glutin-winit = "0.3"
glium = "0.33"
```

I'll explain what these libraries do as we use them to build your program. Your `src/main.rs` file should now look something like this:
By default glium pulls in everything necessary to get you started. You should now have a `src/main.rs` file that looks similar to this:

```rust
fn main() {
println!("Hello, world!");
println!("Hello, World!");
}
```

It is now time to start filling in the `main` function!
It is now time to start work on the main function!

# Creating a window

The first step when creating a graphical application is to create a window. If you have ever worked with OpenGL before, you know how hard it is to do this correctly. Both window creation and context creation are platform-specific, and they are sometimes weird and tedious.
The first step when creating a graphical application is to create a window. If you have ever worked with OpenGL before, you know how hard it is to do this correctly. Both window creation and context creation are platform-specific, and they are sometimes complex and tedious.

We start off by using **winit** to open a window.
Initializing a simple OpenGL window with the default winit/glutin backend can be done using the following 2 steps:

```rust
use winit::event_loop::EventLoopBuilder;
use winit::window::WindowBuilder;
1. Creating an `EventLoop` for handling window and device events.
2. Call `glium::backend::glutin::simple_winit_window` with a reference to the event_loop and a title for the window.

fn main() {
let event_loop = EventLoopBuilder::new().build();
let window_builder = WindowBuilder::new();
This will open a new window, register it with the given event_loop and create a (glutin) OpenGL context and glium Display while finally returning both the window and display to you.

let window = window_builder.build(&event_loop).unwrap();
```rust
fn main() {
let event_loop = winit::event_loop::EventLoopBuilder::new().build();
let (_window, display) = glium::backend::glutin::simple_winit_window(&event_loop, "Glium tutorial #1");
}
```

If you try to run this example with `cargo run` you'll encounter a problem: as soon as the window has been created, our main function exits and the window is closed. To prevent this, we need to loop until we receive a `CloseRequested` event. We do this by calling `event_loop.run`:
If you try to run this example with `cargo run` you'll encounter a problem: as soon as the window has been created, our main function exits and the window is closed. To prevent this, we need to wait until we receive a `CloseRequested` event. We do this by calling `event_loop.run`:

```rust
event_loop.run(move |ev, _, control_flow| {
Expand All @@ -64,79 +60,13 @@ event_loop.run(move |ev, _, control_flow| {
});
```

If you run the program now you should see an nice little window. The content of the window, however, is not not very appealing. Depending on your system, it can appear black, show a random image, or just some snow. We are expected to draw on the window, so the system doesn't bother initializing its color to a specific value.

Since we want to draw using OpenGL we'll leave it like this for now.

# Making it OpenGL capable

To make use of OpenGL we first need a context, in these examples we'll be using **glutin** for that, we also need the **glutin-winit** and **raw-window-handle** crates to connect the context to our **winit** window.

First we import 2 new types above our main function:

```rust
use glutin_winit::DisplayBuilder;
use glutin::config::ConfigTemplateBuilder;
```

Now we can change the way we open our window to make it capable of supporting an OpenGL context. To accomplish that replace the `let window = ...` line with the following:

```rust
let display_builder = DisplayBuilder::new().with_window_builder(Some(window_builder));
let config_template_builder = ConfigTemplateBuilder::new();
let (window, gl_config) = display_builder
.build(&event_loop, config_template_builder, |mut configs| {
// Just use the first configuration since we don't have any special preferences right now
configs.next().unwrap()
})
.unwrap();
let window = window.unwrap();
```

This might seem complicated but it allows for great flexibility in how the window is created which is necessary to support mobile platforms. If you run the program now you won't see a difference, but the window can now support an OpenGL context.

# Creating an OpenGL context

First off we need to use some more libraries, to do that add the following above the main function:

```rust
use glutin::prelude::*;
use glutin::display::GetGlDisplay;
use raw_window_handle::HasRawWindowHandle;
use std::num::NonZeroU32;
```

To create a glium context we need a glutin `Surface`, to do that add the following after the `let window = ...` statement:

```rust
let (width, height): (u32, u32) = window.inner_size().into();
let attrs = glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new().build(
window.raw_window_handle(),
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
);
let surface = unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };
```

Here we first get the size of our window and the create a `Surface` with those dimensions.

After that we can finally create a glutin context and glium `Display` by adding the following:

```rust
let context_attributes = glutin::context::ContextAttributesBuilder::new().build(Some(window.raw_window_handle()));
let current_context = Some(unsafe {
gl_config.display().create_context(&gl_config, &context_attributes).expect("failed to create context")
}).unwrap().make_current(&surface).unwrap();
let display = glium::Display::from_context_surface(current_context, surface).unwrap();
```

Now we are ready to use glium.
If you run the program now you should see an nice little window. The contents of the window, however, are not not very appealing. Depending on your system, it can appear black, show a random image, or just noise. We are expected to draw on the window, so the system doesn't initialize its color to a specific value.

# Drawing on the window

Glium and the OpenGL API work similarly to drawing software like Microsoft Paint or GIMP. We begin with an empty image, then draw an object on it, then another object, then another object, etc. until we are satisfied with the result. But contrary to drawing software, you don't want your users to see the intermediate steps. Only the final result should be shown.
Glium and the OpenGL API work similarly to drawing software like Paint or GIMP. We begin with an empty image, then draw an object on it, then another object, then another object, etc. until we are satisfied with the result. But contrary to these programs, you don't want your users to see the intermediate steps. Only the final result should be displayed.

To accomplish this, OpenGL uses what is called *double buffering*. Instead of drawing directly to the window, we are drawing to an image stored in memory. Once we are finished drawing, this image is copied into the window.
To accomplish this, OpenGL uses what is called *double buffering*. Instead of drawing directly on the window, we are drawing to an image stored in memory. Once we are finished drawing, this image is copied into the window.
This is represented in glium by the `Frame` object. When you want to start drawing something on a window, you must first call `display.draw()` to produce a new `Frame`:

```rust
Expand All @@ -157,7 +87,7 @@ use glium::Surface;

The four values that we pass to `clear_color` represent the four components of our color: red, green, blue and alpha. Only values between `0.0` and `1.0` are valid. Here we are drawing an opaque blue color.

Like I explained above, the user doesn't immediately see the blue color on the screen. At this point if we were in a real application, we would most likely draw our characters, their weapons, the ground, the sky, etc. But in this tutorial we will just stop here:
As explained above, the user doesn't immediately see the blue color on the screen. At this point if we were in a game, we would most likely draw our characters, their weapons, the ground, the sky, etc. But in this tutorial we will just stop here:

```rust
target.finish().unwrap();
Expand All @@ -168,58 +98,24 @@ This call to `finish()` means that we have finished drawing. It destroys the `Fr
Here is our full program:

```rust
use winit::event_loop::EventLoopBuilder;
use winit::window::WindowBuilder;
use glutin_winit::DisplayBuilder;
use glutin::config::ConfigTemplateBuilder;
use glutin::prelude::*;
use glutin::display::GetGlDisplay;
use raw_window_handle::HasRawWindowHandle;
use std::num::NonZeroU32;
use glium::Surface;

fn main() {
let event_loop = EventLoopBuilder::new().build();

let window_builder = WindowBuilder::new();
let display_builder = DisplayBuilder::new().with_window_builder(Some(window_builder));
let config_template_builder = ConfigTemplateBuilder::new();
let (window, gl_config) = display_builder
.build(&event_loop, config_template_builder, |mut configs| {
// Just use the first configuration since we don't have any special preferences right now
configs.next().unwrap()
})
.unwrap();
let window = window.unwrap();

let (width, height): (u32, u32) = window.inner_size().into();
let attrs = glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new().build(
window.raw_window_handle(),
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
);
let surface = unsafe { gl_config.display().create_window_surface(&gl_config, &attrs).unwrap() };

let context_attributes = glutin::context::ContextAttributesBuilder::new().build(Some(window.raw_window_handle()));
let current_context = Some(unsafe {
gl_config.display().create_context(&gl_config, &context_attributes).expect("failed to create context")
}).unwrap().make_current(&surface).unwrap();
let display = glium::Display::from_context_surface(current_context, surface).unwrap();

let mut target = display.draw();
target.clear_color(0.0, 0.0, 1.0, 1.0);
target.finish().unwrap();

event_loop.run(move |ev, _, control_flow| {
match ev {
let event_loop = winit::event_loop::EventLoopBuilder::new().build();
let (_window, display) = glium::backend::glutin::simple_winit_window(&event_loop, "Glium tutorial #1");

let mut frame = display.draw();
frame.clear_color(0.0, 0.0, 1.0, 1.0);
frame.finish().unwrap();

event_loop.run(move |event, _, control_flow| {
match event {
winit::event::Event::WindowEvent { event, .. } => match event {
winit::event::WindowEvent::CloseRequested => {
*control_flow = winit::event_loop::ControlFlow::Exit;
},
winit::event::WindowEvent::CloseRequested => control_flow.set_exit(),
_ => (),
},
_ => (),
}
};
});
}
```
Loading

0 comments on commit ed1f468

Please sign in to comment.