-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Snowflakes: A hello world project for rendering application.
- Loading branch information
KennFatt
committed
Jul 31, 2020
1 parent
790c3e5
commit a612314
Showing
7 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "snowflakes" | ||
version = "0.0.1" | ||
authors = ["KennFatt <me@kennan.xyz>"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
piston = "0.51.0" | ||
piston_window = "0.110.0" | ||
piston2d-graphics = "0.36.0" | ||
piston2d-opengl_graphics = "0.72.0" | ||
pistoncore-glutin_window = "0.65.0" | ||
|
||
rand = "0.7.3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Snowflakes | ||
|
||
![](./.assets/demo.gif) | ||
|
||
This project is part of my boredum in quarantine, so I have to learn something new (Rust language) and then implement it with something fun! | ||
|
||
Ikr, this is bad. My first attempt with Rust tho. | ||
|
||
## How to | ||
**build** | ||
``` | ||
cargo build --release | ||
``` | ||
|
||
**run** | ||
``` | ||
cargo run --release | ||
``` | ||
_Considering to use release mode._ | ||
|
||
### Notes | ||
If somebody or myself in the future wondering why I wrote this code: | ||
```rust | ||
pub struct Snowflakes { | ||
capacity: usize, | ||
radius: Vec<f64>, | ||
velocity: Vec<f64>, | ||
x: Vec<f64>, | ||
y: Vec<f64>, | ||
} | ||
|
||
pub struct Snowflake { | ||
pub id: usize, | ||
pub radius: f64, | ||
pub velocity: f64, | ||
pub x: f64, | ||
pub y: f64, | ||
} | ||
``` | ||
|
||
here is my explanation, first the struct `Snowflakes` is implement the SoA pattern. I'm not sure if it will perform well on heap allocation (those vectors). I want to use something like [FixedSizeArray](https://doc.rust-lang.org/beta/core/array/trait.FixedSizeArray.html) in the future if it's finally arrive as the stable features. | ||
|
||
Lastly, the struct `Snowflake` is just a template for me to manipulate the data that I got from `Snowflakes` to more specific. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
use opengl_graphics::{GlGraphics, OpenGL}; | ||
use glutin_window::GlutinWindow; | ||
use piston::input::{RenderEvent, UpdateEvent, ButtonEvent}; | ||
use piston::window::WindowSettings; | ||
use piston::event_loop::{Events, EventSettings}; | ||
|
||
use super::core::Core; | ||
|
||
|
||
pub struct AppDelegate { | ||
events: Events, | ||
core: Core, | ||
gl: GlGraphics, | ||
window: GlutinWindow, | ||
} | ||
|
||
|
||
impl AppDelegate { | ||
|
||
/// Setup the driver and instantiate all its fields and states. | ||
/// | ||
/// This method is required since you can't run the driver before initializing it | ||
/// by using this `setup` method. | ||
/// | ||
/// ```rust | ||
/// let driver = AppDelegate::setup(); | ||
/// ``` | ||
pub fn setup() -> Self { | ||
/* Setting up the window */ | ||
let opengl = OpenGL::V3_1; | ||
let window_settings = | ||
WindowSettings::new("Snowflakes", [640, 480]) | ||
.samples(8) | ||
.vsync(true) | ||
.resizable(false) | ||
.exit_on_esc(true) | ||
.graphics_api(opengl); | ||
let window: GlutinWindow = window_settings.build().unwrap(); | ||
|
||
/* Instantiate new GL back-end */ | ||
let gl = GlGraphics::new(opengl); | ||
|
||
/* Instantiate events controller */ | ||
let mut event_settings = EventSettings::new(); | ||
event_settings.max_fps = 60; | ||
let events = Events::new(event_settings); | ||
|
||
/* Instantiate our core state */ | ||
let core = Core::new(window_settings).setup(); | ||
|
||
Self { events, core, gl, window } | ||
} | ||
|
||
/// Starting the main-loop of the application. | ||
/// It will listen for some events in order: | ||
/// 1. Button | ||
/// 2. Render | ||
/// 3. Update | ||
/// | ||
/// And after that, it will continue the event to the Core to do | ||
/// more advance task. | ||
/// | ||
/// ```rust | ||
/// let driver = AppDelegate::setup().start(); | ||
/// ``` | ||
/// | ||
pub fn start(&mut self) { | ||
while let Some(ev) = self.events.next(&mut self.window) { | ||
/* Button event */ | ||
if let Some(args) = ev.button_args() { | ||
self.core.keypress(&args); | ||
} | ||
|
||
/* Render event */ | ||
if let Some(args) = ev.render_args() { | ||
/* | ||
I have to call gl.draw_begin() and gl.draw_end() manually. | ||
This is basically how gl.draw() works. | ||
*/ | ||
let c = self.gl.draw_begin(args.viewport()); | ||
|
||
/* --- Render start --- */ | ||
self.core.render(&args, c, &mut self.gl); | ||
|
||
/* --- Render end --- */ | ||
|
||
self.gl.draw_end(); | ||
} | ||
|
||
/* Update event */ | ||
if let Some(args) = ev.update_args() { | ||
self.core.update(&args); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use piston::input::{ | ||
self, | ||
Button, | ||
ButtonArgs, | ||
RenderArgs, | ||
UpdateArgs, | ||
}; | ||
use piston::window::WindowSettings; | ||
use opengl_graphics::GlGraphics; | ||
use graphics::Context; | ||
|
||
use super::entity::Snowflakes; | ||
|
||
|
||
pub struct Core { | ||
window_settings: WindowSettings, | ||
snowflakes: Snowflakes, | ||
} | ||
|
||
|
||
impl Core { | ||
pub fn new(window_settings: WindowSettings) -> Self { | ||
Self { | ||
window_settings, | ||
snowflakes: Snowflakes::new(100) | ||
} | ||
} | ||
|
||
pub fn setup(mut self) -> Self { | ||
/* Randomize the snowflakes */ | ||
self.snowflakes.randomize_vectors( | ||
self.window_settings.get_size().width, | ||
self.window_settings.get_size().height | ||
); | ||
|
||
self | ||
} | ||
|
||
pub fn keypress(&mut self, args: &ButtonArgs) { | ||
if args.state != input::ButtonState::Press { return () } | ||
|
||
match args.button { | ||
Button::Keyboard(key) => { | ||
match key { | ||
input::Key::R => { | ||
self.snowflakes.randomize_vectors( | ||
self.window_settings.get_size().width, | ||
self.window_settings.get_size().height | ||
); | ||
} | ||
|
||
_ => () | ||
} | ||
} | ||
|
||
_ => () | ||
} | ||
} | ||
|
||
pub fn render(&self, args: &RenderArgs, c: Context, g: &mut GlGraphics) { | ||
// Render the background canvas | ||
graphics::clear(graphics::color::hex("373472"), g); | ||
|
||
// Render the snowflakes | ||
for i in 0..self.snowflakes.get_capacity() { | ||
if let Some(s) = self.snowflakes.get_snowflake(i) { | ||
let rect: [f64; 4] = s.into(); | ||
graphics::ellipse( | ||
graphics::color::hex("ffffff"), | ||
rect, c.transform, g | ||
); | ||
} | ||
} | ||
} | ||
|
||
pub fn update(&mut self, args: &UpdateArgs) { | ||
for i in 0..self.snowflakes.get_capacity() { | ||
if let Some(s) = self.snowflakes.get_snowflake(i) { | ||
if s.y > self.window_settings.get_size().height + s.radius { | ||
self.snowflakes.update_pair(i, (s.x, 0. - s.radius)).ok(); | ||
continue; | ||
} | ||
|
||
self.snowflakes.update_pair(i, (s.x, s.y + s.velocity)).ok(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
mod snowflakes; | ||
|
||
|
||
pub use snowflakes::Snowflakes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
use rand::Rng; | ||
|
||
|
||
pub enum UpdateError { | ||
OutOfIndex, | ||
} | ||
|
||
pub enum UpdateSuccess { | ||
PairUpdated, | ||
} | ||
|
||
|
||
pub struct Snowflakes { | ||
capacity: usize, | ||
radius: Vec<f64>, | ||
velocity: Vec<f64>, | ||
x: Vec<f64>, | ||
y: Vec<f64>, | ||
} | ||
|
||
pub struct Snowflake { | ||
pub id: usize, | ||
pub radius: f64, | ||
pub velocity: f64, | ||
pub x: f64, | ||
pub y: f64, | ||
} | ||
|
||
|
||
impl Into<[f64; 4]> for Snowflake { | ||
fn into(self) -> [f64; 4] { | ||
[ | ||
self.x, self.y, self.radius, self.radius | ||
] | ||
} | ||
} | ||
|
||
|
||
impl Snowflakes { | ||
/// | ||
/// Create a new instance and fill the vectors with `0.0_f64` values. | ||
/// | ||
/// ```rust | ||
/// let snowflakes = Snowflakes::new(100); | ||
/// ``` | ||
/// | ||
pub fn new(capacity: usize) -> Self { | ||
let mut x: Vec<f64> = vec![]; | ||
let mut y: Vec<f64> = vec![]; | ||
let mut radius: Vec<f64> = vec![]; | ||
let mut velocity: Vec<f64> = vec![]; | ||
|
||
let mut gen = rand::thread_rng(); | ||
|
||
for _ in 0..capacity { | ||
x.push(0.); | ||
y.push(0.); | ||
|
||
radius.push(gen.gen_range(5.0f64, 15.0f64)); | ||
velocity.push(gen.gen_range(0.5f64, 1.0f64)); | ||
} | ||
|
||
Self { capacity, radius, velocity, x, y } | ||
} | ||
|
||
#[inline(always)] | ||
pub fn get_capacity(&self) -> usize { | ||
self.capacity | ||
} | ||
|
||
pub fn get_snowflake(&self, index: usize) -> Option<Snowflake> { | ||
if index > self.capacity { | ||
return None | ||
} | ||
|
||
Some(Snowflake { | ||
id: index, | ||
radius: self.radius[index], | ||
velocity: self.velocity[index], | ||
x: self.x[index], | ||
y: self.y[index], | ||
}) | ||
} | ||
|
||
pub fn update_pair(&mut self, index: usize, new_val: (f64, f64)) -> Result<UpdateSuccess, UpdateError> { | ||
if index > self.capacity { | ||
return Err(UpdateError::OutOfIndex) | ||
} | ||
|
||
self.x[index] = new_val.0; | ||
self.y[index] = new_val.1; | ||
|
||
Ok(UpdateSuccess::PairUpdated) | ||
} | ||
|
||
pub fn randomize_vectors(&mut self, max_x: f64, max_y: f64) { | ||
let mut gen = rand::thread_rng(); | ||
|
||
for i in 0..self.capacity { | ||
let new_x = gen.gen_range(0., max_x); | ||
let new_y = gen.gen_range(0., max_y); | ||
|
||
self.update_pair(i, (new_x, new_y)).ok(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
mod appdelegate; | ||
mod core; | ||
mod entity; | ||
|
||
|
||
fn main() { | ||
/* Start the driver */ | ||
appdelegate::AppDelegate::setup().start(); | ||
} |