Added sprite data uniform

master
Mark 2024-01-04 18:15:30 -08:00
parent 613245b92e
commit a4ca62e1dc
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
16 changed files with 219 additions and 134 deletions

View File

@ -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,

View File

@ -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

View File

@ -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,
};

View File

@ -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,
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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(),

View File

@ -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};

View File

@ -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;
}

View File

@ -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.

View File

@ -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,
}

View File

@ -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,
aspect: t.aspect,
fps: t.fps,
len: t.frames.len() as u32,
repeat: t.repeat.as_int(),
location: vec![loc.clone()],
},
);
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,
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,
});
}
}

View File

@ -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,
},
],
}