Added sprite data uniform
parent
613245b92e
commit
a4ca62e1dc
|
@ -11,7 +11,11 @@ use std::{cmp::Eq, hash::Hash};
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SpriteHandle {
|
||||
/// The index of this sprite in content.sprites
|
||||
pub(crate) index: usize,
|
||||
/// This must be public, since render uses this to
|
||||
/// select sprites.
|
||||
///
|
||||
/// This is a u32 for that same reason, too.
|
||||
pub index: u32,
|
||||
|
||||
/// The aspect ratio of this sprite (width / height)
|
||||
pub aspect: f32,
|
||||
|
|
|
@ -250,7 +250,7 @@ impl Content {
|
|||
pub fn get_sprite(&self, h: SpriteHandle) -> &Sprite {
|
||||
// In theory, this could fail if h has a bad index, but that shouldn't ever happen.
|
||||
// The only handles that exist should be created by this crate.
|
||||
return &self.sprites[h.index];
|
||||
return &self.sprites[h.index as usize];
|
||||
}
|
||||
|
||||
/// Get a sprite from a path
|
||||
|
|
|
@ -105,7 +105,7 @@ impl crate::Build for Sprite {
|
|||
})?;
|
||||
|
||||
let h = SpriteHandle {
|
||||
index: ct.sprites.len(),
|
||||
index: ct.sprites.len() as u32,
|
||||
aspect: dim.0 as f32 / dim.1 as f32,
|
||||
};
|
||||
|
||||
|
@ -162,7 +162,7 @@ impl crate::Build for Sprite {
|
|||
let dim = dim.unwrap();
|
||||
|
||||
let h = SpriteHandle {
|
||||
index: ct.sprites.len(),
|
||||
index: ct.sprites.len() as u32,
|
||||
aspect: dim.0 as f32 / dim.1 as f32,
|
||||
};
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@ impl Game {
|
|||
ui_sprites: self.get_ui_sprites(),
|
||||
new_particles: &mut self.new_particles,
|
||||
current_time: self.start_instant.elapsed().as_secs_f32(),
|
||||
content: &self.content,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,9 @@ fn vertex_main(
|
|||
var out: VertexOutput;
|
||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||
|
||||
let i = sprites.data[instance.texture_index].first_frame;
|
||||
let t = atlas.data[i];
|
||||
out.texture_index = u32(0);
|
||||
let t = atlas.texture_locations[instance.texture_index];
|
||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||
if vertex.texture_coords.x == 1.0 {
|
||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||
|
|
|
@ -8,8 +8,7 @@ struct InstanceInput {
|
|||
@location(6) size: f32,
|
||||
@location(7) created: f32,
|
||||
@location(8) expires: f32,
|
||||
@location(9) texture_index_len_rep: vec3<u32>,
|
||||
@location(10) texture_aspect_fps: vec2<f32>,
|
||||
@location(9) texture_index: u32,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
|
@ -51,25 +50,29 @@ fn vertex_main(
|
|||
|
||||
let age = global.current_time.x - instance.created;
|
||||
|
||||
let len = sprites.data[instance.texture_index].frame_count;
|
||||
let rep = sprites.data[instance.texture_index].repeatmode;
|
||||
let fps = sprites.data[instance.texture_index].fps;
|
||||
var frame: u32 = u32(0);
|
||||
if instance.texture_index_len_rep.z == u32(1) {
|
||||
if rep == u32(1) {
|
||||
// Repeat
|
||||
frame = u32(fmod(
|
||||
(age / instance.texture_aspect_fps.y),
|
||||
f32(instance.texture_index_len_rep.y)
|
||||
(age / fps),
|
||||
f32(len)
|
||||
));
|
||||
} else {
|
||||
// Once
|
||||
frame = u32(min(
|
||||
(age / instance.texture_aspect_fps.y),
|
||||
f32(instance.texture_index_len_rep.y) - 1.0
|
||||
(age / fps),
|
||||
f32(len) - 1.0
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
// Pick image
|
||||
frame = frame + sprites.data[instance.texture_index].first_frame;
|
||||
let t = atlas.data[frame];
|
||||
out.texture_index = u32(0);
|
||||
let t = atlas.texture_locations[instance.texture_index_len_rep.x + frame];
|
||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||
if vertex.texture_coords.x == 1.0 {
|
||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||
|
@ -84,7 +87,7 @@ fn vertex_main(
|
|||
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
||||
|
||||
pos = pos * vec2<f32>(
|
||||
instance.texture_aspect_fps.x * scale / global.window_aspect.x,
|
||||
sprites.data[instance.texture_index].aspect * scale / global.window_aspect.x,
|
||||
scale
|
||||
);
|
||||
pos = rotation * pos;
|
||||
|
|
|
@ -108,8 +108,9 @@ fn vertex_main(
|
|||
|
||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
||||
|
||||
let i = sprites.data[global.starfield_sprite.x].first_frame;
|
||||
let t = atlas.data[i];
|
||||
out.texture_index = u32(0);
|
||||
let t = atlas.texture_locations[global.starfield_texture.x];
|
||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||
if vertex.texture_coords.x == 1.0 {
|
||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||
|
|
|
@ -46,8 +46,9 @@ fn vertex_main(
|
|||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||
out.color_transform = instance.color_transform;
|
||||
|
||||
let i = sprites.data[instance.texture_index].first_frame;
|
||||
let t = atlas.data[i];
|
||||
out.texture_index = u32(0);
|
||||
let t = atlas.texture_locations[instance.texture_index];
|
||||
out.texture_coords = vec2(t.xpos, t.ypos);
|
||||
if vertex.texture_coords.x == 1.0 {
|
||||
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
|
||||
|
|
|
@ -5,21 +5,20 @@ use wgpu;
|
|||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
||||
pub struct ImageLocation {
|
||||
/// Format: [x, y, w, h], in texture coordinates
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
// Image box, in texture coordinates
|
||||
pub xpos: f32,
|
||||
pub ypos: f32,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ImageLocations {
|
||||
/// Format: [x, y, w, h], in texture coordinates
|
||||
pub struct ImageLocationArray {
|
||||
pub data: [ImageLocation; 108],
|
||||
}
|
||||
|
||||
unsafe impl Pod for ImageLocations {}
|
||||
unsafe impl Zeroable for ImageLocations {
|
||||
unsafe impl Pod for ImageLocationArray {}
|
||||
unsafe impl Zeroable for ImageLocationArray {
|
||||
fn zeroed() -> Self {
|
||||
Self {
|
||||
data: [ImageLocation::zeroed(); 108],
|
||||
|
@ -27,7 +26,7 @@ unsafe impl Zeroable for ImageLocations {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ImageLocations {
|
||||
impl Default for ImageLocationArray {
|
||||
fn default() -> Self {
|
||||
Self::zeroed()
|
||||
}
|
||||
|
@ -36,7 +35,7 @@ impl Default for ImageLocations {
|
|||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
|
||||
pub struct AtlasContent {
|
||||
pub locations: ImageLocations,
|
||||
pub data: ImageLocationArray,
|
||||
}
|
||||
|
||||
impl AtlasContent {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use wgpu;
|
||||
|
||||
use super::{AtlasContent, DataContent};
|
||||
use super::{AtlasContent, DataContent, SpriteContent};
|
||||
|
||||
pub struct GlobalUniform {
|
||||
pub data_buffer: wgpu::Buffer,
|
||||
pub atlas_buffer: wgpu::Buffer,
|
||||
pub sprite_buffer: wgpu::Buffer,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||
pub content: DataContent,
|
||||
|
@ -24,7 +25,7 @@ impl GlobalUniform {
|
|||
camera_zoom_limits: vec2<f32>,
|
||||
window_size: vec2<f32>,
|
||||
window_aspect: vec2<f32>,
|
||||
starfield_texture: vec2<u32>,
|
||||
starfield_sprite: vec2<u32>,
|
||||
starfield_tile_size: vec2<f32>,
|
||||
starfield_size_limits: vec2<f32>,
|
||||
current_time: vec2<f32>,
|
||||
|
@ -37,14 +38,42 @@ impl GlobalUniform {
|
|||
out.push_str(
|
||||
r#"
|
||||
var<uniform> atlas: AtlasUniform;
|
||||
struct TextureLocation {
|
||||
struct ImageLocation {
|
||||
xpos: f32,
|
||||
ypos: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
};
|
||||
struct AtlasUniform {
|
||||
texture_locations: array<TextureLocation, 108>,
|
||||
data: array<ImageLocation, 108>,
|
||||
};
|
||||
"#,
|
||||
);
|
||||
out.push_str("\n");
|
||||
|
||||
// TODO: document
|
||||
// wgpu uniforms require constant item sizes.
|
||||
// if you get an error like the following,check!
|
||||
// `Buffer is bound with size 3456 where the shader expects 5184 in group[1] compact index 2`
|
||||
// More notes are in datacontent
|
||||
|
||||
out.push_str(&format!("@group({group}) @binding(2)\n"));
|
||||
out.push_str(
|
||||
r#"
|
||||
var<uniform> sprites: SpriteUniform;
|
||||
struct SpriteData {
|
||||
frame_count: u32,
|
||||
repeatmode: u32,
|
||||
aspect: f32,
|
||||
fps: f32,
|
||||
first_frame: u32,
|
||||
|
||||
padding_a: f32,
|
||||
padding_b: f32,
|
||||
padding_c: f32,
|
||||
};
|
||||
struct SpriteUniform {
|
||||
data: array<SpriteData, 108>,
|
||||
};
|
||||
"#,
|
||||
);
|
||||
|
@ -68,6 +97,13 @@ impl GlobalUniform {
|
|||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let sprite_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("global uniform sprite buffer"),
|
||||
size: SpriteContent::SIZE,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
|
@ -90,6 +126,16 @@ impl GlobalUniform {
|
|||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("global uniform bind group layout"),
|
||||
});
|
||||
|
@ -105,6 +151,10 @@ impl GlobalUniform {
|
|||
binding: 1,
|
||||
resource: atlas_buffer.as_entire_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: sprite_buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: Some("global uniform bind group"),
|
||||
});
|
||||
|
@ -112,6 +162,7 @@ impl GlobalUniform {
|
|||
return Self {
|
||||
data_buffer,
|
||||
atlas_buffer,
|
||||
sprite_buffer,
|
||||
bind_group,
|
||||
bind_group_layout,
|
||||
content: DataContent::default(),
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
mod atlascontent;
|
||||
mod datacontent;
|
||||
mod globaluniform;
|
||||
mod spritecontent;
|
||||
|
||||
pub use atlascontent::{AtlasContent, ImageLocation, ImageLocations};
|
||||
pub use atlascontent::{AtlasContent, ImageLocation, ImageLocationArray};
|
||||
pub use datacontent::DataContent;
|
||||
pub use globaluniform::GlobalUniform;
|
||||
pub use spritecontent::{SpriteContent, SpriteData, SpriteDataArray};
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
use std::mem;
|
||||
use wgpu;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
||||
pub struct SpriteData {
|
||||
// Animation parameters
|
||||
pub frame_count: u32,
|
||||
pub repeatmode: u32,
|
||||
pub aspect: f32,
|
||||
pub fps: f32,
|
||||
|
||||
// Index of first frame in ImageLocationArray
|
||||
pub first_frame: u32,
|
||||
// stride must be a multiple of 16
|
||||
pub _padding: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct SpriteDataArray {
|
||||
pub data: [SpriteData; 108],
|
||||
}
|
||||
|
||||
unsafe impl Pod for SpriteDataArray {}
|
||||
unsafe impl Zeroable for SpriteDataArray {
|
||||
fn zeroed() -> Self {
|
||||
Self {
|
||||
data: [SpriteData::zeroed(); 108],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SpriteDataArray {
|
||||
fn default() -> Self {
|
||||
Self::zeroed()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
|
||||
pub struct SpriteContent {
|
||||
pub data: SpriteDataArray,
|
||||
}
|
||||
|
||||
impl SpriteContent {
|
||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||
}
|
|
@ -8,7 +8,7 @@ use winit::{self, dpi::LogicalSize, window::Window};
|
|||
|
||||
use crate::{
|
||||
content,
|
||||
globaluniform::{AtlasContent, DataContent, GlobalUniform},
|
||||
globaluniform::{AtlasContent, DataContent, GlobalUniform, SpriteContent},
|
||||
pipeline::PipelineBuilder,
|
||||
sprite::ObjectSubSprite,
|
||||
starfield::Starfield,
|
||||
|
@ -292,8 +292,7 @@ impl GPUState {
|
|||
/// Also handles child sprites.
|
||||
fn push_object_sprite(
|
||||
&self,
|
||||
camera_zoom: f32,
|
||||
camera_pos: Point2<f32>,
|
||||
state: &RenderState,
|
||||
instances: &mut Vec<ObjectInstance>,
|
||||
clip_ne: Point2<f32>,
|
||||
clip_sw: Point2<f32>,
|
||||
|
@ -305,10 +304,9 @@ impl GPUState {
|
|||
(Point2 {
|
||||
x: s.pos.x,
|
||||
y: s.pos.y,
|
||||
} - camera_pos.to_vec())
|
||||
} - state.camera_pos.to_vec())
|
||||
/ s.pos.z
|
||||
};
|
||||
let texture = self.texture_array.get_texture(s.sprite);
|
||||
|
||||
// Game dimensions of this sprite post-scale.
|
||||
// Don't divide by 2, we use this later.
|
||||
|
@ -316,7 +314,7 @@ impl GPUState {
|
|||
|
||||
// Width or height, whichever is larger.
|
||||
// Accounts for sprite rotation.
|
||||
let m = height * texture.aspect.max(1.0);
|
||||
let m = height * s.sprite.aspect.max(1.0);
|
||||
|
||||
// Don't draw (or compute matrices for)
|
||||
// sprites that are off the screen
|
||||
|
@ -329,7 +327,7 @@ impl GPUState {
|
|||
}
|
||||
|
||||
// TODO: clean up
|
||||
let scale = height / camera_zoom;
|
||||
let scale = height / state.camera_zoom;
|
||||
|
||||
// Note that our mesh starts centered at (0, 0).
|
||||
// This is essential---we do not want scale and rotation
|
||||
|
@ -340,7 +338,7 @@ impl GPUState {
|
|||
//
|
||||
// We apply the provided scale here as well as a minor optimization
|
||||
let sprite_aspect_and_scale =
|
||||
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
||||
Matrix4::from_nonuniform_scale(s.sprite.aspect * scale, scale, 1.0);
|
||||
|
||||
// Apply rotation
|
||||
let rotate = Matrix4::from_angle_z(s.angle);
|
||||
|
@ -358,8 +356,8 @@ impl GPUState {
|
|||
// The height of the viewport is `zoom` in game units,
|
||||
// but it's 2 in screen units! (since coordinates range from -1 to 1)
|
||||
let translate = Matrix4::from_translation(Vector3 {
|
||||
x: pos.x / (camera_zoom / 2.0) / self.window_aspect,
|
||||
y: pos.y / (camera_zoom / 2.0),
|
||||
x: pos.x / (state.camera_zoom / 2.0) / self.window_aspect,
|
||||
y: pos.y / (state.camera_zoom / 2.0),
|
||||
z: 0.0,
|
||||
});
|
||||
|
||||
|
@ -370,13 +368,13 @@ impl GPUState {
|
|||
|
||||
instances.push(ObjectInstance {
|
||||
transform: t.into(),
|
||||
sprite_index: texture.index,
|
||||
sprite_index: s.sprite.index,
|
||||
});
|
||||
|
||||
// Add children
|
||||
if let Some(children) = &s.children {
|
||||
for c in children {
|
||||
self.push_object_subsprite(camera_zoom, instances, c, pos, s.angle);
|
||||
self.push_object_subsprite(&state, instances, c, pos, s.angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -385,29 +383,28 @@ impl GPUState {
|
|||
/// Only called by `self.push_object_sprite`.
|
||||
fn push_object_subsprite(
|
||||
&self,
|
||||
camera_zoom: f32,
|
||||
state: &RenderState,
|
||||
instances: &mut Vec<ObjectInstance>,
|
||||
s: &ObjectSubSprite,
|
||||
parent_pos: Point2<f32>,
|
||||
parent_angle: Deg<f32>,
|
||||
) {
|
||||
let texture = self.texture_array.get_texture(s.sprite);
|
||||
let scale = s.size / (s.pos.z * camera_zoom);
|
||||
let scale = s.size / (s.pos.z * state.camera_zoom);
|
||||
let sprite_aspect_and_scale =
|
||||
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
||||
Matrix4::from_nonuniform_scale(s.sprite.aspect * scale, scale, 1.0);
|
||||
let rotate = Matrix4::from_angle_z(s.angle);
|
||||
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
|
||||
|
||||
let ptranslate = Matrix4::from_translation(Vector3 {
|
||||
x: parent_pos.x / (camera_zoom / 2.0) / self.window_aspect,
|
||||
y: parent_pos.y / (camera_zoom / 2.0),
|
||||
x: parent_pos.x / (state.camera_zoom / 2.0) / self.window_aspect,
|
||||
y: parent_pos.y / (state.camera_zoom / 2.0),
|
||||
z: 0.0,
|
||||
});
|
||||
let protate = Matrix4::from_angle_z(parent_angle);
|
||||
|
||||
let translate = Matrix4::from_translation(Vector3 {
|
||||
x: s.pos.x / (camera_zoom / 2.0) / self.window_aspect,
|
||||
y: s.pos.y / (camera_zoom / 2.0),
|
||||
x: s.pos.x / (state.camera_zoom / 2.0) / self.window_aspect,
|
||||
y: s.pos.y / (state.camera_zoom / 2.0),
|
||||
z: 0.0,
|
||||
});
|
||||
|
||||
|
@ -420,7 +417,7 @@ impl GPUState {
|
|||
|
||||
instances.push(ObjectInstance {
|
||||
transform: t.into(),
|
||||
sprite_index: texture.index,
|
||||
sprite_index: s.sprite.index,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -429,7 +426,6 @@ impl GPUState {
|
|||
let logical_size: LogicalSize<f32> =
|
||||
self.window_size.to_logical(self.window.scale_factor());
|
||||
|
||||
let texture = self.texture_array.get_texture(s.sprite);
|
||||
let width = s.dimensions.x;
|
||||
let height = s.dimensions.y;
|
||||
|
||||
|
@ -473,7 +469,7 @@ impl GPUState {
|
|||
|
||||
instances.push(UiInstance {
|
||||
transform: (OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * scale).into(),
|
||||
sprite_index: texture.index,
|
||||
sprite_index: s.sprite.index,
|
||||
color: s.color.unwrap_or([1.0, 1.0, 1.0, 1.0]),
|
||||
});
|
||||
}
|
||||
|
@ -481,23 +477,16 @@ impl GPUState {
|
|||
/// Make an instance for all the game's sprites
|
||||
/// (Objects and UI)
|
||||
/// This will Will panic if any X_SPRITE_INSTANCE_LIMIT is exceeded.
|
||||
fn update_sprite_instances(&self, framestate: RenderState) -> (usize, usize) {
|
||||
fn update_sprite_instances(&self, state: &RenderState) -> (usize, usize) {
|
||||
let mut object_instances: Vec<ObjectInstance> = Vec::new();
|
||||
|
||||
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
||||
// Used to skip off-screen sprites.
|
||||
let clip_ne = Point2::from((-self.window_aspect, 1.0)) * framestate.camera_zoom;
|
||||
let clip_sw = Point2::from((self.window_aspect, -1.0)) * framestate.camera_zoom;
|
||||
let clip_ne = Point2::from((-self.window_aspect, 1.0)) * state.camera_zoom;
|
||||
let clip_sw = Point2::from((self.window_aspect, -1.0)) * state.camera_zoom;
|
||||
|
||||
for s in framestate.object_sprites {
|
||||
self.push_object_sprite(
|
||||
framestate.camera_zoom,
|
||||
framestate.camera_pos,
|
||||
&mut object_instances,
|
||||
clip_ne,
|
||||
clip_sw,
|
||||
&s,
|
||||
);
|
||||
for s in &state.object_sprites {
|
||||
self.push_object_sprite(state, &mut object_instances, clip_ne, clip_sw, &s);
|
||||
}
|
||||
|
||||
// Enforce sprite limit
|
||||
|
@ -514,7 +503,7 @@ impl GPUState {
|
|||
|
||||
let mut ui_instances: Vec<UiInstance> = Vec::new();
|
||||
|
||||
for s in framestate.ui_sprites {
|
||||
for s in &state.ui_sprites {
|
||||
self.push_ui_sprite(&mut ui_instances, &s);
|
||||
}
|
||||
|
||||
|
@ -550,7 +539,14 @@ impl GPUState {
|
|||
&self.global_uniform.atlas_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[AtlasContent {
|
||||
locations: self.texture_array.data,
|
||||
data: self.texture_array.image_locations,
|
||||
}]),
|
||||
);
|
||||
self.queue.write_buffer(
|
||||
&self.global_uniform.sprite_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[SpriteContent {
|
||||
data: self.texture_array.sprite_data,
|
||||
}]),
|
||||
);
|
||||
|
||||
|
@ -558,7 +554,7 @@ impl GPUState {
|
|||
}
|
||||
|
||||
/// Main render function. Draws sprites on a window.
|
||||
pub fn render(&mut self, framestate: RenderState) -> Result<(), wgpu::SurfaceError> {
|
||||
pub fn render(&mut self, state: RenderState) -> Result<(), wgpu::SurfaceError> {
|
||||
let output = self.surface.get_current_texture()?;
|
||||
let view = output
|
||||
.texture
|
||||
|
@ -591,32 +587,32 @@ impl GPUState {
|
|||
timestamp_writes: None,
|
||||
});
|
||||
|
||||
let s = state.content.get_starfield_handle();
|
||||
// Update global values
|
||||
self.queue.write_buffer(
|
||||
&self.global_uniform.data_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[DataContent {
|
||||
camera_position: framestate.camera_pos.into(),
|
||||
camera_zoom: [framestate.camera_zoom, 0.0],
|
||||
camera_position: state.camera_pos.into(),
|
||||
camera_zoom: [state.camera_zoom, 0.0],
|
||||
camera_zoom_limits: [galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX],
|
||||
window_size: [
|
||||
self.window_size.width as f32,
|
||||
self.window_size.height as f32,
|
||||
],
|
||||
window_aspect: [self.window_aspect, 0.0],
|
||||
starfield_sprite: [self.texture_array.get_starfield_texture().index, 0],
|
||||
starfield_sprite: [s.index, 0],
|
||||
starfield_tile_size: [galactica_constants::STARFIELD_SIZE as f32, 0.0],
|
||||
starfield_size_limits: [
|
||||
galactica_constants::STARFIELD_SIZE_MIN,
|
||||
galactica_constants::STARFIELD_SIZE_MAX,
|
||||
],
|
||||
current_time: [framestate.current_time, 0.0],
|
||||
current_time: [state.current_time, 0.0],
|
||||
}]),
|
||||
);
|
||||
|
||||
// Write all new particles to GPU buffer
|
||||
for i in framestate.new_particles.iter() {
|
||||
let texture = self.texture_array.get_texture(i.sprite);
|
||||
for i in state.new_particles.iter() {
|
||||
self.queue.write_buffer(
|
||||
&self.vertex_buffers.particle.instances,
|
||||
ParticleInstance::SIZE * self.vertex_buffers.particle_array_head,
|
||||
|
@ -625,10 +621,9 @@ impl GPUState {
|
|||
velocity: i.velocity.into(),
|
||||
rotation: Matrix2::from_angle(i.angle).into(),
|
||||
size: i.size,
|
||||
texture_index_len_rep: [texture.index, texture.len, texture.repeat],
|
||||
texture_aspect_fps: [texture.aspect, texture.fps],
|
||||
created: framestate.current_time,
|
||||
expires: framestate.current_time + i.lifetime,
|
||||
sprite_index: i.sprite.index,
|
||||
created: state.current_time,
|
||||
expires: state.current_time + i.lifetime,
|
||||
}]),
|
||||
);
|
||||
self.vertex_buffers.particle_array_head += 1;
|
||||
|
@ -638,10 +633,10 @@ impl GPUState {
|
|||
self.vertex_buffers.particle_array_head = 0;
|
||||
}
|
||||
}
|
||||
framestate.new_particles.clear();
|
||||
state.new_particles.clear();
|
||||
|
||||
// Create sprite instances
|
||||
let (n_object, n_ui) = self.update_sprite_instances(framestate);
|
||||
let (n_object, n_ui) = self.update_sprite_instances(&state);
|
||||
|
||||
// These should match the indices in each shader,
|
||||
// and should each have a corresponding bind group layout.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use cgmath::Point2;
|
||||
use galactica_content::Content;
|
||||
|
||||
use crate::{ObjectSprite, ParticleBuilder, UiSprite};
|
||||
|
||||
|
@ -23,4 +24,7 @@ pub struct RenderState<'a> {
|
|||
// TODO: handle overflow
|
||||
/// The current time, in seconds
|
||||
pub current_time: f32,
|
||||
|
||||
/// Game content
|
||||
pub content: &'a Content,
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::{
|
||||
content,
|
||||
globaluniform::{ImageLocation, ImageLocations},
|
||||
globaluniform::{ImageLocation, ImageLocationArray, SpriteData, SpriteDataArray},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use bytemuck::Zeroable;
|
||||
use galactica_packer::SpriteAtlasImage;
|
||||
use image::GenericImageView;
|
||||
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
|
||||
use std::{fs::File, io::Read, num::NonZeroU32};
|
||||
use wgpu::BindGroupLayout;
|
||||
|
||||
pub(crate) struct RawTexture {
|
||||
|
@ -85,27 +85,14 @@ pub struct Texture {
|
|||
pub struct TextureArray {
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
pub bind_group_layout: BindGroupLayout,
|
||||
starfield_handle: content::SpriteHandle,
|
||||
sprites: HashMap<content::SpriteHandle, Texture>,
|
||||
pub data: ImageLocations,
|
||||
pub image_locations: ImageLocationArray,
|
||||
pub sprite_data: SpriteDataArray,
|
||||
}
|
||||
|
||||
impl TextureArray {
|
||||
pub fn get_starfield_texture(&self) -> &Texture {
|
||||
self.sprites.get(&self.starfield_handle).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_texture(&self, handle: content::SpriteHandle) -> &Texture {
|
||||
match self.sprites.get(&handle) {
|
||||
Some(x) => x,
|
||||
None => unreachable!("Tried to get a texture that doesn't exist"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &content::Content) -> Result<Self> {
|
||||
// Load all textures
|
||||
let mut texture_data = Vec::new();
|
||||
let mut sprites = HashMap::new();
|
||||
|
||||
println!("opening image");
|
||||
let mut f = File::open("atlas-0.bmp")?;
|
||||
|
@ -113,35 +100,31 @@ impl TextureArray {
|
|||
f.read_to_end(&mut bytes)?;
|
||||
texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, "Atlas")?);
|
||||
|
||||
let mut tx = ImageLocations::zeroed();
|
||||
let mut image_locations = ImageLocationArray::zeroed();
|
||||
let mut sprite_data = SpriteDataArray::zeroed();
|
||||
|
||||
println!("sending to gpu");
|
||||
let mut i = 0;
|
||||
let mut image_counter = 0;
|
||||
for t in &ct.sprites {
|
||||
let loc = ct.get_image(&t.frames[0]);
|
||||
|
||||
sprites.insert(
|
||||
t.handle,
|
||||
Texture {
|
||||
index: i,
|
||||
sprite_data.data[image_counter as usize] = SpriteData {
|
||||
frame_count: t.frames.len() as u32,
|
||||
repeatmode: t.repeat.as_int(),
|
||||
aspect: t.aspect,
|
||||
fps: t.fps,
|
||||
len: t.frames.len() as u32,
|
||||
repeat: t.repeat.as_int(),
|
||||
location: vec![loc.clone()],
|
||||
},
|
||||
);
|
||||
first_frame: image_counter,
|
||||
_padding: Default::default(),
|
||||
};
|
||||
|
||||
// Insert texture location data
|
||||
for path in &t.frames {
|
||||
let image = ct.get_image(&path);
|
||||
tx.data[i as usize] = ImageLocation {
|
||||
x: image.x,
|
||||
y: image.y,
|
||||
w: image.w,
|
||||
h: image.h,
|
||||
image_locations.data[image_counter as usize] = ImageLocation {
|
||||
xpos: image.x,
|
||||
ypos: image.y,
|
||||
width: image.w,
|
||||
height: image.h,
|
||||
};
|
||||
i += 1;
|
||||
image_counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,9 +181,8 @@ impl TextureArray {
|
|||
return Ok(Self {
|
||||
bind_group,
|
||||
bind_group_layout,
|
||||
sprites,
|
||||
starfield_handle: ct.get_starfield_handle(),
|
||||
data: tx,
|
||||
image_locations,
|
||||
sprite_data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,9 +219,8 @@ pub struct ParticleInstance {
|
|||
/// Time is kept by a variable in the global uniform.
|
||||
pub expires: f32,
|
||||
|
||||
/// What texture to use for this particle
|
||||
pub texture_index_len_rep: [u32; 3],
|
||||
pub texture_aspect_fps: [f32; 2],
|
||||
/// What sprite to use for this particle
|
||||
pub sprite_index: u32,
|
||||
}
|
||||
|
||||
impl BufferObject for ParticleInstance {
|
||||
|
@ -271,17 +270,11 @@ impl BufferObject for ParticleInstance {
|
|||
shader_location: 8,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
// Texture index / len / repeat
|
||||
// Sprite
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
|
||||
shader_location: 9,
|
||||
format: wgpu::VertexFormat::Uint32x3,
|
||||
},
|
||||
// Texture aspect / fps
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 14]>() as wgpu::BufferAddress,
|
||||
shader_location: 10,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
format: wgpu::VertexFormat::Uint32,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue