Skip to content

Commit

Permalink
Snowflakes: A hello world project for rendering application.
Browse files Browse the repository at this point in the history
  • Loading branch information
KennFatt committed Jul 31, 2020
1 parent 790c3e5 commit a612314
Show file tree
Hide file tree
Showing 7 changed files with 360 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Cargo.toml
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"
43 changes: 43 additions & 0 deletions README.md
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.
96 changes: 96 additions & 0 deletions src/appdelegate.rs
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);
}
}
}
}
88 changes: 88 additions & 0 deletions src/core.rs
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();
}
}
}
}
4 changes: 4 additions & 0 deletions src/entity/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod snowflakes;


pub use snowflakes::Snowflakes;
106 changes: 106 additions & 0 deletions src/entity/snowflakes.rs
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();
}
}
}
9 changes: 9 additions & 0 deletions src/main.rs
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();
}

0 comments on commit a612314

Please sign in to comment.