Skip to content
Snippets Groups Projects
Commit 9f9f8303 authored by Noxim's avatar Noxim
Browse files

Splash

parent 992a82e0
No related branches found
No related tags found
No related merge requests found
......@@ -530,6 +530,9 @@ name = "glam"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42218cb640844e3872cc3c153dc975229e080a6c4733b34709ef445610550226"
dependencies = [
"bytemuck",
]
[[package]]
name = "glow"
......@@ -1356,10 +1359,12 @@ name = "quickgame"
version = "0.1.0"
dependencies = [
"anyhow",
"bytemuck",
"console_error_panic_hook",
"console_log",
"env_logger",
"glam",
"instant",
"log",
"png",
"pollster",
......
......@@ -23,13 +23,15 @@ default = []
webgl = ["wgpu/webgl"]
[dependencies]
glam = "0.24"
glam = { version = "0.24", features = ["bytemuck"] }
wgpu = "0.16"
winit = "0.28"
log = "0.4"
anyhow = "1"
reqwest = "0.11"
png = "0.17"
bytemuck = "1"
instant = "0.1"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
pollster = "0.3"
......
File added
use crate::prelude::*;
pub struct Animation<T> {
keyframes: Vec<T>,
begin: Instant,
duration: Duration,
}
impl<T> Animation<T> {
pub fn new(keyframes: Vec<T>, duration: Duration) -> Self {
Self {
keyframes,
begin: Instant::now(),
duration,
}
}
pub fn get(&self) -> T
where
T: std::ops::Mul<f32, Output = T> + std::ops::Add<Output = T> + Clone,
{
let prog = self.begin.elapsed().as_secs_f32() / self.duration.as_secs_f32();
let scale = prog * self.keyframes.len() as f32;
let low = scale.floor() as usize;
let high = scale.ceil() as usize;
let blend = scale.fract();
match (self.keyframes.get(low), self.keyframes.get(high)) {
(None, None) => self.keyframes.last().unwrap().clone(),
(None, Some(end)) | (Some(end), None) => end.clone(),
(Some(a), Some(b)) => a.clone() * (1.0 - blend) + b.clone() * blend,
}
}
pub fn complete(&self) -> bool {
self.begin.elapsed() >= self.duration
}
pub fn reset(&mut self) {
self.begin = Instant::now();
}
}
mod anim;
mod asset;
pub mod prelude;
mod sprite;
mod state;
use wgpu::*;
use winit::{
......@@ -9,7 +12,7 @@ use winit::{
window::Window,
};
use crate::prelude::*;
use crate::{prelude::*, state::State};
pub async fn run() -> Result<()> {
let event_loop = winit::event_loop::EventLoop::new();
......@@ -55,7 +58,7 @@ pub async fn run() -> Result<()> {
&DeviceDescriptor {
label: Some("GMTK"),
features: Features::empty(),
limits: Limits::downlevel_webgl2_defaults(),
limits: Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
},
None,
)
......@@ -105,10 +108,7 @@ pub async fn run() -> Result<()> {
view_formats: &[],
});
let fg = asset::load_png("splash_fg.png").await?;
let bg = asset::load_png("splash_bg.png").await?;
let _ = (fg, bg);
let mut state = State::new(&queue, &device).await?;
event_loop.run(move |event, _target, control_flow| {
*control_flow = ControlFlow::Poll;
......@@ -177,35 +177,45 @@ pub async fn run() -> Result<()> {
label: Some("Main encoder"),
});
enc.begin_render_pass(&RenderPassDescriptor {
label: Some("Clear"),
color_attachments: &[Some(RenderPassColorAttachment {
view: &framebuffer.create_view(&TextureViewDescriptor {
label: Some("Antialias target"),
format: view_formats.get(0).copied(),
..Default::default()
}),
resolve_target: Some(&swapchain.texture.create_view(
&TextureViewDescriptor {
label: Some("Resolve target"),
format: view_formats.get(0).copied(),
..Default::default()
{
let fb_view = framebuffer.create_view(&TextureViewDescriptor {
label: Some("Antialias target"),
format: view_formats.get(0).copied(),
..Default::default()
});
let rs_view = swapchain.texture.create_view(&TextureViewDescriptor {
label: Some("Resolve target"),
format: view_formats.get(0).copied(),
..Default::default()
});
let db_view = depthbuffer.create_view(&Default::default());
let mut pass = enc.begin_render_pass(&RenderPassDescriptor {
label: Some("Clear"),
color_attachments: &[Some(RenderPassColorAttachment {
view: &fb_view,
resolve_target: Some(&rs_view),
ops: Operations {
load: LoadOp::Clear(Color::BLACK),
store: true,
},
)),
ops: Operations {
load: LoadOp::Clear(Color::RED),
store: true,
},
})],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depthbuffer.create_view(&Default::default()),
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
})],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &db_view,
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
stencil_ops: None,
}),
});
});
let pipeline_fmt = view_formats.get(0).copied().unwrap_or(format);
let aspect =
window.inner_size().width as f32 / window.inner_size().height as f32;
state.update(&queue, &device, &mut pass, pipeline_fmt, aspect);
}
queue.submit([enc.finish()]);
......
pub use anyhow::{Result, anyhow};
pub use glam::{Vec2, Vec3};
pub use crate::{
anim::Animation,
asset::{load, load_png},
sprite::Sprite,
};
pub use anyhow::{anyhow, Result};
pub use glam::{Mat4, Quat, Vec2, Vec3};
pub use instant::{Duration, Instant};
pub use log::{debug, error, info, trace, warn};
pub use wgpu::{
util::*,
{Device, Queue},
};
pub use std::f32::consts::{FRAC_PI_2, PI};
pub trait Mat4Ext {
fn from_2d(position: Vec2, scale: f32, rotation: f32) -> Mat4 {
Mat4::from_scale_rotation_translation(
Vec3::splat(scale),
Quat::from_rotation_z(rotation),
Vec3::new(position.x, position.y, 0.0),
)
}
}
impl Mat4Ext for Mat4 {}
use crate::prelude::*;
use wgpu::*;
pub struct Sprite {
layout: BindGroupLayout,
texture: Texture,
uniforms: Buffer,
bindgroup: BindGroup,
pub transforms: Mat4,
}
impl Sprite {
pub async fn new(name: &str, queue: &Queue, device: &Device, smooth: bool) -> Result<Self> {
let img = load_png(name).await?;
let label = format!("Sprite {name}");
let label = Some(label.as_str());
let texture = device.create_texture_with_data(
queue,
&TextureDescriptor {
label,
size: Extent3d {
width: img.width,
height: img.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
usage: TextureUsages::TEXTURE_BINDING,
view_formats: &[],
},
&img.rgba,
);
let uniforms = device.create_buffer(&BufferDescriptor {
label,
size: 16 * 4,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label,
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
sample_type: TextureSampleType::Float { filterable: true },
view_dimension: TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
});
let filter = smooth
.then_some(FilterMode::Linear)
.unwrap_or(FilterMode::Nearest);
let sampler = device.create_sampler(&SamplerDescriptor {
label,
mag_filter: filter,
min_filter: filter,
..Default::default()
});
let bindgroup = device.create_bind_group(&BindGroupDescriptor {
label,
layout: &layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: uniforms.as_entire_binding(),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(
&texture.create_view(&Default::default()),
),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::Sampler(&sampler),
},
],
});
let transforms = Mat4::IDENTITY;
Ok(Self {
layout,
texture,
uniforms,
bindgroup,
transforms,
})
}
pub fn record<'a, 'b: 'a>(
&'b self,
queue: &Queue,
device: &Device,
pass: &mut RenderPass<'a>,
format: TextureFormat,
aspect: f32,
) {
let mat = Mat4::from_scale(Vec3::new(1.0 / aspect, 1.0, 1.0)) * self.transforms;
queue.write_buffer(
&self.uniforms,
0,
bytemuck::bytes_of(&mat),
);
static PIPELINE: std::sync::OnceLock<RenderPipeline> = std::sync::OnceLock::new();
let pipeline = PIPELINE.get_or_init(|| {
let module = device.create_shader_module(include_wgsl!("../assets/sprite.wgsl"));
let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("Sprite"),
bind_group_layouts: &[&self.layout],
push_constant_ranges: &[],
});
device.create_render_pipeline(&RenderPipelineDescriptor {
label: Some("Sprite"),
layout: Some(&layout),
vertex: VertexState {
module: &module,
entry_point: "vs_main",
buffers: &[],
},
primitive: Default::default(),
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth24Plus,
depth_write_enabled: false,
depth_compare: CompareFunction::Always,
stencil: Default::default(),
bias: Default::default(),
}),
multisample: MultisampleState {
count: 4,
mask: 0xFF,
alpha_to_coverage_enabled: false,
},
fragment: Some(FragmentState {
module: &module,
entry_point: "fs_main",
targets: &[Some(ColorTargetState {
format,
blend: Some(BlendState::ALPHA_BLENDING),
write_mask: ColorWrites::all(),
})],
}),
multiview: None,
})
});
pass.set_pipeline(pipeline);
pass.set_bind_group(0, &self.bindgroup, &[]);
pass.draw(0..6, 0..1);
}
}
use wgpu::{RenderPass, TextureFormat};
use crate::prelude::*;
pub enum State {
Loading {
fg: Sprite,
bg: Sprite,
scale: Animation<f32>,
rot: Animation<f32>,
},
}
impl State {
pub async fn new(queue: &Queue, device: &Device) -> Result<State> {
Ok(State::Loading {
fg: Sprite::new("splash_fg.png", queue, device, true).await?,
bg: Sprite::new("splash_bg.png", queue, device, true).await?,
scale: Animation::new(vec![0.0, 0.1, 0.25], Duration::from_secs(1)),
rot: Animation::new(vec![PI, PI, 0.0], Duration::from_secs(3)),
})
}
pub fn update<'a, 'b: 'a>(
&'b mut self,
queue: &Queue,
device: &Device,
pass: &mut RenderPass<'a>,
format: TextureFormat,
aspect: f32,
) {
match self {
State::Loading { fg, bg, scale, rot } => {
bg.transforms = Mat4::from_2d(Vec2::ZERO, scale.get(), rot.get());
fg.transforms = Mat4::from_2d(Vec2::ZERO, 0.25, 0.0);
bg.record(queue, device, pass, format, aspect);
if scale.complete() {
fg.record(queue, device, pass, format, aspect)
}
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment