Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
257 lines (194 sloc)
5.5 KB
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
#![feature(box_syntax)] | |
extern crate wasm_toys as engine; | |
use engine::prelude::*; | |
fn main() { | |
engine::init_engine(SnowApp::new); | |
} | |
fn rand() -> f32 { | |
unsafe { | |
engine::imports::util::math_random() | |
} | |
} | |
// world space particle size / 2 | |
const PARTICLE_EXTENT: f32 = 1.0 / 30.0; | |
struct SnowApp { | |
camera: Camera, | |
particles: Vec<Particle>, | |
spawn_timer: f32, | |
wind_phase: f32, | |
wind_fm_phase: f32, | |
scene_shader: Shader, | |
snow_shader: Shader, | |
scene_mesh: BasicDynamicMesh<SceneVertex>, | |
snow_mesh: BasicDynamicMesh<ParticleVertex>, | |
} | |
impl SnowApp { | |
fn new() -> Self { | |
let mut camera = Camera::new(); | |
camera.set_near_far(0.1, 100.0); | |
let scene_shader = Shader::from_combined( | |
include_str!("scene.glsl"), | |
&["position"] | |
); | |
let snow_shader = Shader::from_combined( | |
include_str!("snow.glsl"), | |
&["position", "sprite_stage"] | |
); | |
let snow_mesh = BasicDynamicMesh::new(); | |
let mut scene_mesh = BasicDynamicMesh::new(); | |
let ground_size = 10.0; | |
scene_mesh.add_quad(&[ | |
SceneVertex(Vec3::new(-ground_size,-1.0, ground_size)), | |
SceneVertex(Vec3::new( ground_size,-1.0, ground_size)), | |
SceneVertex(Vec3::new( ground_size,-1.0,-ground_size)), | |
SceneVertex(Vec3::new(-ground_size,-1.0,-ground_size)), | |
]); | |
SnowApp { | |
camera, | |
particles: Vec::new(), | |
spawn_timer: 0.0, | |
wind_phase: 0.0, | |
wind_fm_phase: 0.0, | |
scene_shader, snow_shader, | |
scene_mesh, snow_mesh, | |
} | |
} | |
fn update_particles(&mut self) { | |
let wind_angle = PI/3.0 + ((self.wind_fm_phase + self.wind_phase)/5.0).sin()*PI/6.0; | |
let wind_dir = Vec3::from_y_angle(wind_angle); | |
let wind_perp = Vec3::from_y_angle(wind_angle + PI/2.0); | |
self.spawn_timer -= engine::DT; | |
if self.spawn_timer < 0.0 { | |
let spread = 2.0; | |
let pos = wind_dir * spread * (rand()*2.0 - 1.0 - 0.2) | |
+ wind_perp * spread * (rand()*2.0 - 1.0); | |
let pos = Vec3{ y: 2.0, ..pos }; | |
let spawn_density = (self.wind_fm_phase/17.0).sin(); | |
self.particles.push(Particle { | |
pos, | |
lifetime: -rand() * 20.0, | |
state: ParticleState::Falling, | |
}); | |
self.spawn_timer = rand() * 0.12 * (1.0 - spawn_density * 0.1); | |
} | |
self.wind_fm_phase += engine::DT / 5.0; | |
let wind_fm = (self.wind_fm_phase + (self.wind_fm_phase/5.0).cos()).sin()*0.8 + 0.1; | |
self.wind_phase += engine::DT * (1.0 + wind_fm) * 0.8; | |
for p in self.particles.iter_mut() { | |
p.lifetime += engine::DT; | |
match p.state { | |
ParticleState::Falling => { | |
let wind_amt = (p.pos.dot(wind_perp) + self.wind_phase*0.8).sin() * 1.8 + 0.2; | |
let gravity = Vec3::from_y(-0.1 * (1.0 - wind_amt*0.1)); | |
let wind = wind_dir * 0.09 * wind_amt; | |
p.pos += (gravity + wind) * engine::DT; | |
let resting_y = -1.0 + PARTICLE_EXTENT; | |
if p.pos.y < resting_y { | |
p.pos.y = resting_y; | |
p.state = ParticleState::Resting; | |
p.lifetime = 0.0; | |
} | |
} | |
ParticleState::Resting => { | |
if p.lifetime > 40.0 { | |
p.state = ParticleState::Dead; | |
} | |
} | |
ParticleState::Dead => {} | |
} | |
} | |
self.particles.retain(|p| p.state != ParticleState::Dead); | |
let cam_pos = self.camera.position(); | |
let cam_fwd = self.camera.orientation().forward(); | |
self.particles.sort_by_key(|p| Ordified(-(p.pos - cam_pos).dot(cam_fwd))); | |
} | |
fn render(&mut self, ctx: engine::UpdateContext) { | |
unsafe { | |
let (r,g,b,_) = Color::hsv(193.0, 0.15, 0.9).to_tuple(); | |
gl::clear_color(r, g, b, 1.0); | |
gl::clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); | |
} | |
let time = ctx.ticks as f32 * engine::DT; | |
let quat = Quat::new(Vec3::from_y(1.0), (time/3.0).sin()*PI/24.0); | |
let position = quat * Vec3::from_z(3.0) + Vec3::from_y(0.0); | |
self.camera.update(ctx.viewport); | |
self.camera.set_orientation(quat); | |
self.camera.set_position(position); | |
let ground_color = Color::hsv(105.0, 0.4, 0.8); | |
let particle_scale = ctx.viewport.y.min(ctx.viewport.x) as f32 * PARTICLE_EXTENT; | |
self.scene_shader.bind(); | |
self.scene_shader.set_uniform("u_color", ground_color); | |
self.scene_shader.set_uniform("u_proj_view", self.camera.projection_view()); | |
self.scene_mesh.draw(gl::DrawMode::Triangles); | |
self.snow_shader.bind(); | |
self.snow_shader.set_uniform("u_particle_scale", particle_scale); | |
self.snow_shader.set_uniform("u_proj_view", self.camera.projection_view()); | |
self.snow_mesh.clear(); | |
for p in self.particles.iter() { | |
let sprite_stage = match p.state { | |
ParticleState::Falling => { | |
if p.lifetime < 0.0 { | |
-1.0 | |
} else { | |
// cycle [0, 2) | |
p.lifetime/2.0 % 2.0 | |
} | |
} | |
ParticleState::Resting => { | |
10.0 | |
} | |
ParticleState::Dead => { | |
100.0 | |
} | |
}; | |
self.snow_mesh.add_vertex(ParticleVertex { | |
pos: p.pos, | |
sprite_stage | |
}) | |
} | |
self.snow_mesh.draw(gl::DrawMode::Points); | |
} | |
} | |
#[derive(Copy, Clone, PartialEq)] | |
enum ParticleState { | |
Falling, | |
Resting, | |
Dead, | |
} | |
struct Particle { | |
pos: Vec3, | |
lifetime: f32, | |
state: ParticleState, | |
} | |
impl EngineClient for SnowApp { | |
fn uses_passive_input(&self) -> bool { false } | |
fn init(&mut self) { | |
// Prebake some particles | |
for _ in 0..600 { | |
self.update_particles(); | |
} | |
} | |
fn update(&mut self, ctx: engine::UpdateContext) { | |
self.update_particles(); | |
self.render(ctx); | |
} | |
} | |
#[derive(Copy, Clone, Debug)] | |
#[repr(C)] | |
struct ParticleVertex { | |
pos: Vec3, | |
sprite_stage: f32, | |
} | |
impl vertex::Vertex for ParticleVertex { | |
fn descriptor() -> vertex::Descriptor { | |
vertex::Descriptor::from(&[3, 1]) | |
} | |
} | |
#[derive(Copy, Clone, Debug)] | |
#[repr(C)] | |
struct SceneVertex (Vec3); | |
impl vertex::Vertex for SceneVertex { | |
fn descriptor() -> vertex::Descriptor { | |
vertex::Descriptor::from(&[3]) | |
} | |
} |