Reworked renderer for new sprite system

master
Mark 2024-01-20 10:04:09 -08:00
parent 73f540d30a
commit fdd481e8f0
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
18 changed files with 365 additions and 327 deletions

View File

@ -1,41 +0,0 @@
// Pick a frame of a sprite animation from an instance.
fn animate(sprite_index: u32, age: f32, offset: f32) -> f32 {
let len = global_sprites[sprite_index].frame_count;
let rep = global_sprites[sprite_index].repeatmode;
let frame_duration = global_sprites[sprite_index].frame_duration;
var frame: f32 = 0.0;
// Once
if rep == u32(1) {
frame = min(
age / frame_duration + offset,
f32(len) - 1.0
);
// Reverse
} else if rep == u32(2) {
let x = age / frame_duration + offset;
let m = f32(len) * 2.0 - 1.0;
// x fmod m
frame = x - floor(x / m) * m;
if frame >= f32(len) {
frame = (
f32(len) + f32(len) - 1.0
// Split integer and fractional part so tweening works properly
- floor(frame)
+ fract(frame)
);
}
// Repeat (default)
} else {
let x = age / frame_duration + offset;
let m = f32(len);
frame = x - floor(x / m) * m;
}
return frame + f32(global_sprites[sprite_index].first_frame);
}

View File

@ -1,8 +1,9 @@
// INCLUDE: global uniform header // INCLUDE: global uniform header
struct InstanceInput { struct InstanceInput {
@location(2) sprite_index: u32, @location(2) texture_index: vec2<u32>,
@location(3) object_index: u32, @location(3) texture_fade: f32,
@location(4) object_index: u32,
}; };
struct VertexInput { struct VertexInput {
@ -25,20 +26,19 @@ var texture_array: binding_array<texture_2d<f32>>;
@group(0) @binding(1) @group(0) @binding(1)
var sampler_array: binding_array<sampler>; var sampler_array: binding_array<sampler>;
fn transform_vertex(obj: ObjectData, vertex_position: vec2<f32>, texture_index: u32) -> vec4<f32> {
// INCLUDE: animate.wgsl
fn transform_vertex(obj: ObjectData, vertex_position: vec2<f32>, sprite_index: u32) -> vec4<f32> {
// Object scale // Object scale
var scale: f32 = obj.size / (global_data.camera_zoom.x * obj.zpos); var scale: f32 = obj.size / (global_data.camera_zoom.x * obj.zpos);
if obj.is_child == 1u { if obj.is_child == 1u {
scale /= objects[obj.parent].zpos; scale /= objects[obj.parent].zpos;
} }
let texture = global_atlas[texture_index];
// Apply scale and sprite aspect // Apply scale and sprite aspect
// Note that our mesh starts centered at (0, 0). This is important! // Note that our mesh starts centered at (0, 0). This is important!
var pos: vec2<f32> = vec2( var pos: vec2<f32> = vec2(
vertex_position.x * scale * global_sprites[sprite_index].aspect, vertex_position.x * scale * (texture.width / texture.height),
vertex_position.y * scale vertex_position.y * scale
); );
@ -113,18 +113,18 @@ fn vertex_main(
) -> VertexOutput { ) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = transform_vertex(
objects[instance.object_index],
out.position = transform_vertex(objects[instance.object_index], vertex.position.xy, instance.sprite_index); vertex.position.xy,
instance.texture_index.x
);
// Compute texture coordinates // Compute texture coordinates
let age = global_data.current_time.x; let age = global_data.current_time.x;
let frame = animate(instance.sprite_index, age, 0.0); out.tween = instance.texture_fade;
out.tween = fract(frame);
let t = global_atlas[u32(floor(frame))]; let t = global_atlas[instance.texture_index.x];
out.texture_index_a = u32(t.atlas_texture); out.texture_index_a = u32(t.atlas_texture);
out.texture_coords_a = vec2(t.xpos, t.ypos); out.texture_coords_a = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {
@ -134,7 +134,7 @@ fn vertex_main(
out.texture_coords_a = out.texture_coords_a + vec2(0.0, t.height); out.texture_coords_a = out.texture_coords_a + vec2(0.0, t.height);
} }
let b = global_atlas[u32(floor(animate(instance.sprite_index, age, 1.0)))]; let b = global_atlas[instance.texture_index.y];
out.texture_index_b = u32(b.atlas_texture); out.texture_index_b = u32(b.atlas_texture);
out.texture_coords_b = vec2(b.xpos, b.ypos); out.texture_coords_b = vec2(b.xpos, b.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {

View File

@ -9,7 +9,8 @@ struct InstanceInput {
@location(7) created: f32, @location(7) created: f32,
@location(8) expires: f32, @location(8) expires: f32,
@location(9) fade: f32, @location(9) fade: f32,
@location(10) sprite_index: u32, @location(10) texture_index: vec2<u32>,
@location(11) texture_fade: f32,
}; };
struct VertexInput { struct VertexInput {
@ -33,9 +34,6 @@ var texture_array: binding_array<texture_2d<f32>>;
@group(0) @binding(1) @group(0) @binding(1)
var sampler_array: binding_array<sampler>; var sampler_array: binding_array<sampler>;
// INCLUDE: animate.wgsl
@vertex @vertex
fn vertex_main( fn vertex_main(
vertex: VertexInput, vertex: VertexInput,
@ -96,9 +94,9 @@ fn vertex_main(
// Compute texture coordinates // Compute texture coordinates
let frame = animate(instance.sprite_index, age, 0.0); let frame = animate(instance.sprite_index, age, 0.0);
out.tween = fract(frame); out.tween = instance.texture_fade;
let t = global_atlas[u32(floor(frame))]; let t = global_atlas[instance.texture_index.x];
out.texture_index_a = u32(t.atlas_texture); out.texture_index_a = u32(t.atlas_texture);
out.texture_coords_a = vec2(t.xpos, t.ypos); out.texture_coords_a = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {
@ -108,7 +106,7 @@ fn vertex_main(
out.texture_coords_a = out.texture_coords_a + vec2(0.0, t.height); out.texture_coords_a = out.texture_coords_a + vec2(0.0, t.height);
} }
let b = global_atlas[u32(floor(animate(instance.sprite_index, age, 1.0)))]; let b = global_atlas[instance.texture_index.y];
out.texture_index_b = u32(b.atlas_texture); out.texture_index_b = u32(b.atlas_texture);
out.texture_coords_b = vec2(b.xpos, b.ypos); out.texture_coords_b = vec2(b.xpos, b.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {
@ -118,7 +116,6 @@ fn vertex_main(
out.texture_coords_b = out.texture_coords_b + vec2(0.0, b.height); out.texture_coords_b = out.texture_coords_b + vec2(0.0, b.height);
} }
return out; return out;
} }

View File

@ -109,8 +109,7 @@ fn vertex_main(
// Starfield sprites may not be animated // Starfield sprites may not be animated
let i = global_sprites[global_data.starfield_sprite.x].first_frame; let t = global_atlas[global_data.starfield_sprite.x];
let t = global_atlas[i];
out.texture_index = u32(t.atlas_texture); out.texture_index = u32(t.atlas_texture);
out.texture_coords = vec2(t.xpos, t.ypos); out.texture_coords = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {

View File

@ -6,8 +6,9 @@ struct InstanceInput {
@location(4) angle: f32, @location(4) angle: f32,
@location(5) size: f32, @location(5) size: f32,
@location(6) color_transform: vec4<f32>, @location(6) color_transform: vec4<f32>,
@location(7) sprite_index: u32, @location(7) texture_index: vec2<u32>,
@location(8) mask_index: vec2<u32>, @location(8) texture_fade: f32,
@location(9) mask_index: vec2<u32>,
}; };
struct VertexInput { struct VertexInput {
@ -30,7 +31,6 @@ var texture_array: binding_array<texture_2d<f32>>;
var sampler_array: binding_array<sampler>; var sampler_array: binding_array<sampler>;
// INCLUDE: animate.wgsl
// INCLUDE: anchor.wgsl // INCLUDE: anchor.wgsl
@vertex @vertex
@ -42,12 +42,15 @@ fn vertex_main(
let window_dim = global_data.window_size / global_data.window_scale.x; let window_dim = global_data.window_size / global_data.window_scale.x;
let scale = instance.size / window_dim.y; let scale = instance.size / window_dim.y;
let sprite_aspect = global_sprites[instance.sprite_index].aspect; let aspect = (
global_atlas[instance.texture_index.x].width /
global_atlas[instance.texture_index.x].height
);
// Apply scale and sprite aspect // Apply scale and sprite aspect
// Note that our mesh starts centered at (0, 0). This is important! // Note that our mesh starts centered at (0, 0). This is important!
var pos: vec2<f32> = vec2( var pos: vec2<f32> = vec2(
vertex.position.x * scale * sprite_aspect, vertex.position.x * scale * aspect,
vertex.position.y * scale vertex.position.y * scale
); );
@ -66,7 +69,7 @@ fn vertex_main(
pos = pos + anchor( pos = pos + anchor(
instance.anchor, instance.anchor,
instance.position, instance.position,
vec2(instance.size * sprite_aspect, instance.size) vec2(instance.size * aspect, instance.size)
); );
var out: VertexOutput; var out: VertexOutput;
@ -77,7 +80,7 @@ fn vertex_main(
// TODO: function to get texture from sprite // TODO: function to get texture from sprite
// Pick texture frame // Pick texture frame
let t = global_atlas[u32(animate(instance.sprite_index, global_data.current_time.x, 0.0))]; let t = global_atlas[instance.texture_index.x];
out.texture_index = u32(t.atlas_texture); out.texture_index = u32(t.atlas_texture);
out.texture_coords = vec2(t.xpos, t.ypos); out.texture_coords = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {
@ -91,8 +94,7 @@ fn vertex_main(
// x coordinate of mask index is either 0 or 1, telling us whether or not to use a mask. // x coordinate of mask index is either 0 or 1, telling us whether or not to use a mask.
// y coordinate is mask sprite index // y coordinate is mask sprite index
if instance.mask_index.x == 1u { if instance.mask_index.x == 1u {
let ms = global_sprites[instance.mask_index.y].first_frame; let m = global_atlas[instance.mask_index.y];
let m = global_atlas[ms];
out.mask_index = vec2(1u, u32(m.atlas_texture)); out.mask_index = vec2(1u, u32(m.atlas_texture));
out.mask_coords = vec2(m.xpos, m.ypos); out.mask_coords = vec2(m.xpos, m.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {

View File

@ -1,12 +1,11 @@
use galactica_util::constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT, SPRITE_LIMIT}; use galactica_util::constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT};
use wgpu; use wgpu;
use super::{object::ObjectLocationArray, AtlasArray, GlobalDataContent, SpriteDataArray}; use super::{object::ObjectLocationArray, AtlasArray, GlobalDataContent};
pub struct GlobalUniform { pub struct GlobalUniform {
pub data_buffer: wgpu::Buffer, pub data_buffer: wgpu::Buffer,
pub atlas_buffer: wgpu::Buffer, pub atlas_buffer: wgpu::Buffer,
pub sprite_buffer: wgpu::Buffer,
pub object_buffer: wgpu::Buffer, pub object_buffer: wgpu::Buffer,
pub bind_group: wgpu::BindGroup, pub bind_group: wgpu::BindGroup,
pub bind_group_layout: wgpu::BindGroupLayout, pub bind_group_layout: wgpu::BindGroupLayout,
@ -70,35 +69,10 @@ impl GlobalUniform {
// `Buffer is bound with size 3456 where the shader expects 5184 in group[1] compact index 2` // `Buffer is bound with size 3456 where the shader expects 5184 in group[1] compact index 2`
// More notes are in datacontent // More notes are in datacontent
// Sprite data
out.push_str(&format!(
r#"
@group({group}) @binding(2)
var<uniform> global_sprites: array<SpriteData, {SPRITE_LIMIT}>;
"#
));
out.push_str("\n");
out.push_str(
r#"
struct SpriteData {
frame_count: u32,
repeatmode: u32,
aspect: f32,
frame_duration: f32,
first_frame: u32,
padding_a: f32,
padding_b: f32,
padding_c: f32,
};
"#,
);
out.push_str("\n");
// Object location data // Object location data
out.push_str(&format!( out.push_str(&format!(
r#" r#"
@group({group}) @binding(3) @group({group}) @binding(2)
var<uniform> objects: array<ObjectData, {OBJECT_SPRITE_INSTANCE_LIMIT}>; var<uniform> objects: array<ObjectData, {OBJECT_SPRITE_INSTANCE_LIMIT}>;
"# "#
)); ));
@ -138,13 +112,6 @@ impl GlobalUniform {
mapped_at_creation: false, mapped_at_creation: false,
}); });
let sprite_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("global uniform sprite buffer"),
size: SpriteDataArray::SIZE,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let object_buffer = device.create_buffer(&wgpu::BufferDescriptor { let object_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("global uniform object buffer"), label: Some("global uniform object buffer"),
size: ObjectLocationArray::SIZE, size: ObjectLocationArray::SIZE,
@ -184,16 +151,6 @@ impl GlobalUniform {
}, },
count: None, count: None,
}, },
wgpu::BindGroupLayoutEntry {
binding: 3,
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"), label: Some("global uniform bind group layout"),
}); });
@ -211,10 +168,6 @@ impl GlobalUniform {
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 2, binding: 2,
resource: sprite_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 3,
resource: object_buffer.as_entire_binding(), resource: object_buffer.as_entire_binding(),
}, },
], ],
@ -224,7 +177,6 @@ impl GlobalUniform {
return Self { return Self {
data_buffer, data_buffer,
atlas_buffer, atlas_buffer,
sprite_buffer,
object_buffer, object_buffer,
bind_group, bind_group,
bind_group_layout, bind_group_layout,

View File

@ -2,10 +2,8 @@ mod atlas;
mod data; mod data;
mod globaluniform; mod globaluniform;
mod object; mod object;
mod sprite;
pub use atlas::{AtlasArray, AtlasImageLocation}; pub use atlas::{AtlasArray, AtlasImageLocation};
pub use data::GlobalDataContent; pub use data::GlobalDataContent;
pub use globaluniform::GlobalUniform; pub use globaluniform::GlobalUniform;
pub use object::ObjectData; pub use object::ObjectData;
pub use sprite::{SpriteData, SpriteDataArray};

View File

@ -1,43 +0,0 @@
use bytemuck::{Pod, Zeroable};
use galactica_util::constants::SPRITE_LIMIT;
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 frame_duration: 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; SPRITE_LIMIT as usize],
}
unsafe impl Pod for SpriteDataArray {}
unsafe impl Zeroable for SpriteDataArray {
fn zeroed() -> Self {
Self {
data: [SpriteData::zeroed(); SPRITE_LIMIT as usize],
}
}
}
impl Default for SpriteDataArray {
fn default() -> Self {
Self::zeroed()
}
}
impl SpriteDataArray {
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
}

View File

@ -20,7 +20,7 @@ pub struct GPUState {
object_pipeline: wgpu::RenderPipeline, object_pipeline: wgpu::RenderPipeline,
starfield_pipeline: wgpu::RenderPipeline, starfield_pipeline: wgpu::RenderPipeline,
particle_pipeline: wgpu::RenderPipeline, //particle_pipeline: wgpu::RenderPipeline,
ui_pipeline: wgpu::RenderPipeline, ui_pipeline: wgpu::RenderPipeline,
radialbar_pipeline: wgpu::RenderPipeline, radialbar_pipeline: wgpu::RenderPipeline,
@ -57,12 +57,7 @@ impl GPUState {
self.state.queue.write_buffer( self.state.queue.write_buffer(
&self.state.global_uniform.atlas_buffer, &self.state.global_uniform.atlas_buffer,
0, 0,
bytemuck::cast_slice(&[self.texture_array.image_locations]), bytemuck::cast_slice(&[self.texture_array.texture_atlas]),
);
self.state.queue.write_buffer(
&self.state.global_uniform.sprite_buffer,
0,
bytemuck::cast_slice(&[self.texture_array.sprite_data]),
); );
self.starfield.update_buffer(ct, &mut self.state); self.starfield.update_buffer(ct, &mut self.state);

View File

@ -167,6 +167,7 @@ impl GPUState {
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();
/*
let particle_pipeline = PipelineBuilder::new("particle", &device) let particle_pipeline = PipelineBuilder::new("particle", &device)
.set_shader(&preprocess_shader( .set_shader(&preprocess_shader(
&include_str!(concat!( &include_str!(concat!(
@ -181,7 +182,7 @@ impl GPUState {
.set_triangle(true) .set_triangle(true)
.set_vertex_buffer(vertex_buffers.get_particle()) .set_vertex_buffer(vertex_buffers.get_particle())
.set_bind_group_layouts(bind_group_layouts) .set_bind_group_layouts(bind_group_layouts)
.build(); .build();*/
let radialbar_pipeline = PipelineBuilder::new("radialbar", &device) let radialbar_pipeline = PipelineBuilder::new("radialbar", &device)
.set_shader(&preprocess_shader( .set_shader(&preprocess_shader(
@ -228,7 +229,7 @@ impl GPUState {
object_pipeline, object_pipeline,
starfield_pipeline, starfield_pipeline,
ui_pipeline, ui_pipeline,
particle_pipeline, //particle_pipeline,
radialbar_pipeline, radialbar_pipeline,
state, state,

View File

@ -22,7 +22,7 @@ impl GPUState {
let ship_pos; let ship_pos;
let ship_ang; let ship_ang;
let ship_cnt; let ship_cnt;
match ship.data.get_state() { match ship.get_data().get_state() {
ShipState::Dead | ShipState::Landed { .. } => continue, ShipState::Dead | ShipState::Landed { .. } => continue,
ShipState::Collapsing { .. } | ShipState::Flying { .. } => { ShipState::Collapsing { .. } | ShipState::Flying { .. } => {
@ -31,7 +31,7 @@ impl GPUState {
ship_pos = Point3::new(pos.x, pos.y, 1.0); ship_pos = Point3::new(pos.x, pos.y, 1.0);
let ship_rot = r.rotation(); let ship_rot = r.rotation();
ship_ang = ship_rot.angle(); ship_ang = ship_rot.angle();
ship_cnt = state.ct.get_ship(ship.data.get_content()); ship_cnt = state.ct.get_ship(ship.get_data().get_content());
} }
ShipState::UnLanding { current_z, .. } | ShipState::Landing { current_z, .. } => { ShipState::UnLanding { current_z, .. } | ShipState::Landing { current_z, .. } => {
@ -40,7 +40,7 @@ impl GPUState {
ship_pos = Point3::new(pos.x, pos.y, *current_z); ship_pos = Point3::new(pos.x, pos.y, *current_z);
let ship_rot = r.rotation(); let ship_rot = r.rotation();
ship_ang = ship_rot.angle(); ship_ang = ship_rot.angle();
ship_cnt = state.ct.get_ship(ship.data.get_content()); ship_cnt = state.ct.get_ship(ship.get_data().get_content());
} }
} }
@ -84,22 +84,23 @@ impl GPUState {
); );
// Push this object's instance // Push this object's instance
let anim_state = ship.get_anim_state();
self.state.push_object_buffer(ObjectInstance { self.state.push_object_buffer(ObjectInstance {
sprite_index: ship_cnt.sprite.get_index(), texture_index: anim_state.texture_index(),
texture_fade: anim_state.fade,
object_index: idx as u32, object_index: idx as u32,
}); });
let flare = ship.data.get_outfits().get_flare_sprite(state.ct);
if { if {
let is_flying = match ship.data.get_state() { let is_flying = match ship.get_data().get_state() {
ShipState::Flying { .. } ShipState::Flying { .. }
| ShipState::UnLanding { .. } | ShipState::UnLanding { .. }
| ShipState::Landing { .. } => true, | ShipState::Landing { .. } => true,
_ => false, _ => false,
}; };
ship.get_controls().thrust && flare.is_some() && is_flying ship.get_controls().thrust && is_flying
} { } {
for engine_point in &ship_cnt.engines { for (engine_point, anim) in ship.iter_engine_anim() {
self.state.queue.write_buffer( self.state.queue.write_buffer(
&self.state.global_uniform.object_buffer, &self.state.global_uniform.object_buffer,
ObjectData::SIZE * self.state.get_object_counter() as u64, ObjectData::SIZE * self.state.get_object_counter() as u64,
@ -122,8 +123,10 @@ impl GPUState {
}]), }]),
); );
let anim_state = anim.get_texture_idx();
self.state.push_object_buffer(ObjectInstance { self.state.push_object_buffer(ObjectInstance {
sprite_index: flare.unwrap().get_index(), texture_index: anim_state.texture_index(),
texture_fade: anim_state.fade,
object_index: self.state.get_object_counter() as u32, object_index: self.state.get_object_counter() as u32,
}); });
} }
@ -182,9 +185,10 @@ impl GPUState {
}]), }]),
); );
// Push this object's instance let anim_state = p.get_anim_state();
self.state.push_object_buffer(ObjectInstance { self.state.push_object_buffer(ObjectInstance {
sprite_index: proj_cnt.sprite.get_index(), texture_index: anim_state.texture_index(),
texture_fade: anim_state.fade,
object_index: idx as u32, object_index: idx as u32,
}); });
} }
@ -235,9 +239,13 @@ impl GPUState {
}]), }]),
); );
let sprite = state.ct.get_sprite(o.sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
// Push this object's instance // Push this object's instance
self.state.push_object_buffer(ObjectInstance { self.state.push_object_buffer(ObjectInstance {
sprite_index: o.sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
object_index: idx as u32, object_index: idx as u32,
}); });
} }

View File

@ -1,5 +1,6 @@
use anyhow::Result; use anyhow::Result;
use bytemuck; use bytemuck;
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
use galactica_util::constants::PARTICLE_SPRITE_INSTANCE_LIMIT; use galactica_util::constants::PARTICLE_SPRITE_INSTANCE_LIMIT;
use glyphon::Resolution; use glyphon::Resolution;
use nalgebra::Point2; use nalgebra::Point2;
@ -12,9 +13,9 @@ use crate::{
RenderInput, RenderInput,
}; };
impl super::GPUState { impl<'a> super::GPUState {
/// Main render function. Draws sprites on a window. /// Render routines while player is flying
pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> { fn render_flying(&'a mut self, input: &RenderInput) -> Result<()> {
let output = self.surface.get_current_texture()?; let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&Default::default()); let view = output.texture.create_view(&Default::default());
@ -45,45 +46,19 @@ impl super::GPUState {
timestamp_writes: None, timestamp_writes: None,
}); });
self.state.frame_reset();
let s = input.ct.get_starfield_handle();
// Update global values
self.state.queue.write_buffer(
&self.state.global_uniform.data_buffer,
0,
bytemuck::cast_slice(&[GlobalDataContent {
camera_position: input.camera_pos.into(),
camera_zoom: [input.camera_zoom, 0.0],
camera_zoom_limits: [
input.ct.get_config().zoom_min,
input.ct.get_config().zoom_max,
],
window_size: [
self.state.window_size.width as f32,
self.state.window_size.height as f32,
],
window_scale: [self.state.window.scale_factor() as f32, 0.0],
window_aspect: [self.state.window_aspect, 0.0],
starfield_sprite: [s.get_index(), 0],
starfield_tile_size: [input.ct.get_config().starfield_size, 0.0],
starfield_size_limits: [
input.ct.get_config().starfield_min_size,
input.ct.get_config().starfield_max_size,
],
current_time: [input.current_time, 0.0],
}]),
);
// Write all new particles to GPU buffer // Write all new particles to GPU buffer
for i in input.particles.iter() { for i in input.particles.iter() {
let sprite = input.ct.get_sprite(i.sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
self.state.push_particle_buffer(ParticleInstance { self.state.push_particle_buffer(ParticleInstance {
position: [i.pos.x, i.pos.y], position: [i.pos.x, i.pos.y],
velocity: i.velocity.into(), velocity: i.velocity.into(),
angle: i.angle, angle: i.angle,
angvel: i.angvel, angvel: i.angvel,
size: i.size, size: i.size,
sprite_index: i.sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
created: input.current_time, created: input.current_time,
expires: input.current_time + i.lifetime, expires: input.current_time + i.lifetime,
fade: i.fade, fade: i.fade,
@ -134,6 +109,7 @@ impl super::GPUState {
0..self.state.get_object_counter(), 0..self.state.get_object_counter(),
); );
/*
// Particle pipeline // Particle pipeline
self.state self.state
.vertex_buffers .vertex_buffers
@ -144,7 +120,7 @@ impl super::GPUState {
0..SPRITE_INDICES.len() as u32, 0..SPRITE_INDICES.len() as u32,
0, 0,
0..PARTICLE_SPRITE_INSTANCE_LIMIT as _, 0..PARTICLE_SPRITE_INSTANCE_LIMIT as _,
); ); */
// Ui pipeline // Ui pipeline
self.state self.state
@ -171,7 +147,7 @@ impl super::GPUState {
0..self.state.get_radialbar_counter(), 0..self.state.get_radialbar_counter(),
); );
let textareas = self.ui.get_textareas(&input, &self.state); let textareas = self.ui.get_textareas_flying(input, &self.state);
self.state self.state
.text_renderer .text_renderer
.prepare( .prepare(
@ -193,13 +169,175 @@ impl super::GPUState {
.render(&self.state.text_atlas, &mut render_pass) .render(&self.state.text_atlas, &mut render_pass)
.unwrap(); .unwrap();
// begin_render_pass borrows encoder mutably, so we can't call finish() // begin_render_pass borrows encoder mutably,
// without dropping this variable. // so we need to drop it before calling finish.
drop(render_pass); drop(render_pass);
self.state.queue.submit(iter::once(encoder.finish())); self.state.queue.submit(iter::once(encoder.finish()));
output.present(); output.present();
Ok(()) return Ok(());
}
/// Render routines while player is landed
fn render_landed(&'a mut self, input: &RenderInput) -> Result<()> {
let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&Default::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("render encoder"),
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("render pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
// Create sprite instances
self.ui.draw(&input, &mut self.state);
// These should match the indices in each shader,
// and should each have a corresponding bind group layout.
render_pass.set_bind_group(0, &self.texture_array.bind_group, &[]);
render_pass.set_bind_group(1, &self.state.global_uniform.bind_group, &[]);
// Starfield pipeline
self.state
.vertex_buffers
.get_starfield()
.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.starfield_pipeline);
render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32,
0,
0..self.state.get_starfield_counter(),
);
// Ui pipeline
self.state
.vertex_buffers
.get_ui()
.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.ui_pipeline);
render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32,
0,
0..self.state.get_ui_counter(),
);
// Radial progress bars
self.state
.vertex_buffers
.get_radialbar()
.set_in_pass(&mut render_pass);
render_pass.set_pipeline(&self.radialbar_pipeline);
render_pass.draw_indexed(
0..SPRITE_INDICES.len() as u32,
0,
0..self.state.get_radialbar_counter(),
);
let textareas = self.ui.get_textareas_landed(input, &self.state);
self.state
.text_renderer
.prepare(
&self.device,
&self.state.queue,
&mut self.state.text_font_system,
&mut self.state.text_atlas,
Resolution {
width: self.state.window_size.width,
height: self.state.window_size.height,
},
textareas,
&mut self.state.text_cache,
)
.unwrap();
self.state
.text_renderer
.render(&self.state.text_atlas, &mut render_pass)
.unwrap();
// begin_render_pass borrows encoder mutably,
// so we need to drop it before calling finish.
drop(render_pass);
self.state.queue.submit(iter::once(encoder.finish()));
output.present();
return Ok(());
}
/// Main render function. Draws sprites on a window.
pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> {
// Update global values
self.state.queue.write_buffer(
&self.state.global_uniform.data_buffer,
0,
bytemuck::cast_slice(&[GlobalDataContent {
camera_position: input.camera_pos.into(),
camera_zoom: [input.camera_zoom, 0.0],
camera_zoom_limits: [
input.ct.get_config().zoom_min,
input.ct.get_config().zoom_max,
],
window_size: [
self.state.window_size.width as f32,
self.state.window_size.height as f32,
],
window_scale: [self.state.window.scale_factor() as f32, 0.0],
window_aspect: [self.state.window_aspect, 0.0],
starfield_sprite: [input.ct.get_starfield_texture(), 0],
starfield_tile_size: [input.ct.get_config().starfield_size, 0.0],
starfield_size_limits: [
input.ct.get_config().starfield_min_size,
input.ct.get_config().starfield_max_size,
],
current_time: [input.current_time, 0.0],
}]),
);
self.state.frame_reset();
match input
.systemsim
.get_ship(&PhysSimShipHandle(input.player.ship.unwrap()))
.unwrap()
.get_data()
.get_state()
{
ShipState::Collapsing
| ShipState::Dead
| ShipState::Landing { .. }
| ShipState::UnLanding { .. }
| ShipState::Flying { .. } => {
self.render_flying(&input).unwrap();
}
ShipState::Landed { .. } => {
self.render_landed(&input).unwrap();
}
}
return Ok(());
} }
} }

View File

@ -13,15 +13,6 @@ pub(crate) fn preprocess_shader(
); );
// Insert common functions // Insert common functions
let shader = shader.replace(
"// INCLUDE: animate.wgsl",
&include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/include/",
"animate.wgsl"
)),
);
let shader = shader.replace( let shader = shader.replace(
"// INCLUDE: anchor.wgsl", "// INCLUDE: anchor.wgsl",
&include_str!(concat!( &include_str!(concat!(

View File

@ -1,4 +1,4 @@
use crate::globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray}; use crate::globaluniform::{AtlasArray, AtlasImageLocation};
use anyhow::Result; use anyhow::Result;
use bytemuck::Zeroable; use bytemuck::Zeroable;
use galactica_content::Content; use galactica_content::Content;
@ -84,8 +84,7 @@ pub struct Texture {
pub struct TextureArray { pub struct TextureArray {
pub bind_group: wgpu::BindGroup, pub bind_group: wgpu::BindGroup,
pub bind_group_layout: BindGroupLayout, pub bind_group_layout: BindGroupLayout,
pub image_locations: AtlasArray, pub texture_atlas: AtlasArray,
pub sprite_data: SpriteDataArray,
} }
impl TextureArray { impl TextureArray {
@ -93,49 +92,36 @@ impl TextureArray {
// Load all textures // Load all textures
let mut texture_data = Vec::new(); let mut texture_data = Vec::new();
for a in ct.atlas_files() { for file in ct.atlas_files() {
println!("opening {a}"); println!("opening {file}");
let p = Path::new(ASSET_CACHE); let p = Path::new(ASSET_CACHE);
let mut f = File::open(p.join(a))?; let mut f = File::open(p.join(file))?;
let mut bytes = Vec::new(); let mut bytes = Vec::new();
f.read_to_end(&mut bytes)?; f.read_to_end(&mut bytes)?;
texture_data.push(RawTexture::from_bytes( texture_data.push(RawTexture::from_bytes(
&device, &device,
&queue, &queue,
&bytes, &bytes,
&format!("Atlas `{a}`"), &format!("Atlas `{file}`"),
)?); )?);
} }
let mut image_locations = AtlasArray::zeroed(); let mut image_locations = AtlasArray::zeroed();
let mut sprite_data = SpriteDataArray::zeroed();
println!("sending to gpu"); println!("sending to gpu");
let mut image_counter = 0; for sprite in &ct.sprites {
let mut sprite_counter = 0; for section in sprite.iter_sections() {
for t in &ct.sprites { for idx in &section.frames {
sprite_data.data[sprite_counter] = SpriteData { let image = ct.get_image(*idx);
frame_count: t.frames.len() as u32, image_locations.data[*idx as usize] = AtlasImageLocation {
repeatmode: t.repeat.as_int(), xpos: image.x,
aspect: t.aspect, ypos: image.y,
frame_duration: t.frame_duration, width: image.w,
first_frame: image_counter, height: image.h,
_padding: Default::default(), atlas_texture: image.atlas,
}; _padding: Default::default(),
sprite_counter += 1; };
}
// Insert texture location data
for path in &t.frames {
let image = ct.get_image(&path);
image_locations.data[image_counter as usize] = AtlasImageLocation {
xpos: image.x,
ypos: image.y,
width: image.w,
height: image.h,
atlas_texture: image.atlas,
_padding: Default::default(),
};
image_counter += 1;
} }
} }
@ -192,8 +178,7 @@ impl TextureArray {
return Ok(Self { return Ok(Self {
bind_group, bind_group,
bind_group_layout, bind_group_layout,
image_locations, texture_atlas: image_locations,
sprite_data,
}); });
} }
} }

View File

@ -32,7 +32,7 @@ impl UiManager {
self.fps.update(input, state); self.fps.update(input, state);
match ship.data.get_state() { match ship.get_data().get_state() {
ShipState::Collapsing ShipState::Collapsing
| ShipState::Dead | ShipState::Dead
| ShipState::Flying { .. } | ShipState::Flying { .. }
@ -48,23 +48,22 @@ impl UiManager {
} }
} }
pub fn get_textareas(&self, input: &RenderInput, state: &RenderState) -> Vec<TextArea> { /// Textareas to show while player is flying
pub fn get_textareas_flying(
&self,
_input: &RenderInput,
_state: &RenderState,
) -> Vec<TextArea> {
let mut v = Vec::with_capacity(5); let mut v = Vec::with_capacity(5);
v.push(self.fps.get_textarea());
let ship_handle = input.player.ship.unwrap(); return v;
let ship = input }
.systemsim
.get_ship(&PhysSimShipHandle(ship_handle))
.unwrap();
match ship.data.get_state() {
ShipState::Collapsing
| ShipState::Dead
| ShipState::Flying { .. }
| ShipState::Landing { .. }
| ShipState::UnLanding { .. } => v.push(self.fps.get_textarea()),
ShipState::Landed { .. } => v.extend(self.planet.get_textarea(input, state)), /// Textareas to show when player is landed
}; pub fn get_textareas_landed(&self, input: &RenderInput, state: &RenderState) -> Vec<TextArea> {
let mut v = Vec::with_capacity(5);
v.extend(self.planet.get_textarea(input, state));
return v; return v;
} }

View File

@ -34,7 +34,7 @@ impl Radar {
)) ))
.unwrap(); .unwrap();
match player_ship.data.get_state() { match player_ship.get_data().get_state() {
ShipState::Dead => {} ShipState::Dead => {}
ShipState::Landed { target } => { ShipState::Landed { target } => {
@ -60,6 +60,9 @@ impl Radar {
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip"); let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow"); let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
let sprite = input.ct.get_sprite(input.ct.get_sprite_handle("ui::radar"));
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
// Push this object's instance // Push this object's instance
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwNw.to_int(), anchor: PositionAnchor::NwNw.to_int(),
@ -67,7 +70,8 @@ impl Radar {
angle: 0.0, angle: 0.0,
size: radar_size, size: radar_size,
color: [1.0, 1.0, 1.0, 1.0], color: [1.0, 1.0, 1.0, 1.0],
sprite_index: input.ct.get_sprite_handle("ui::radar").get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
@ -87,6 +91,9 @@ impl Radar {
continue; continue;
} }
let sprite = input.ct.get_sprite(planet_sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
// Push this object's instance // Push this object's instance
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
@ -96,7 +103,8 @@ impl Radar {
angle: o.angle, angle: o.angle,
size, size,
color: [0.5, 0.5, 0.5, 1.0], color: [0.5, 0.5, 0.5, 1.0],
sprite_index: planet_sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}) })
}; };
@ -104,8 +112,8 @@ impl Radar {
// Draw ships // Draw ships
for (s, r) in input.systemsim.iter_ship_body() { for (s, r) in input.systemsim.iter_ship_body() {
let ship = input.ct.get_ship(s.data.get_content()); let ship = input.ct.get_ship(s.get_data().get_content());
let (color, z_scale) = match s.data.get_state() { let (color, z_scale) = match s.get_data().get_state() {
ShipState::Dead | ShipState::Landed { .. } => { ShipState::Dead | ShipState::Landed { .. } => {
continue; continue;
} }
@ -121,7 +129,7 @@ impl Radar {
([0.2, 0.2, 0.2, 1.0], 1.0) ([0.2, 0.2, 0.2, 1.0], 1.0)
} }
ShipState::Flying { .. } => { ShipState::Flying { .. } => {
let c = input.ct.get_faction(s.data.get_faction()).color; let c = input.ct.get_faction(s.get_data().get_faction()).color;
([c[0], c[1], c[2], 1.0], 1.0) ([c[0], c[1], c[2], 1.0], 1.0)
} }
}; };
@ -144,13 +152,17 @@ impl Radar {
let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0) let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
+ (d * (radar_size / 2.0)); + (d * (radar_size / 2.0));
let sprite = input.ct.get_sprite(ship_sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
position: position.into(), position: position.into(),
angle: r.rotation().angle(), angle: r.rotation().angle(),
size, size,
color, color,
sprite_index: ship_sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
} }
@ -168,6 +180,9 @@ impl Radar {
let sprite = input.ct.get_sprite_handle("ui::radarframe"); let sprite = input.ct.get_sprite_handle("ui::radarframe");
let size = 7.0f32.min((0.8 - m) * 70.0); let size = 7.0f32.min((0.8 - m) * 70.0);
let sprite = input.ct.get_sprite(sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwNw.to_int(), anchor: PositionAnchor::NwNw.to_int(),
position: Point2::new( position: Point2::new(
@ -178,7 +193,8 @@ impl Radar {
angle: to_radians(90.0), angle: to_radians(90.0),
size, size,
color, color,
sprite_index: sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
@ -192,7 +208,8 @@ impl Radar {
angle: to_radians(180.0), angle: to_radians(180.0),
size, size,
color, color,
sprite_index: sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
@ -206,7 +223,8 @@ impl Radar {
angle: to_radians(270.0), angle: to_radians(270.0),
size, size,
color, color,
sprite_index: sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
@ -220,7 +238,8 @@ impl Radar {
angle: to_radians(0.0), angle: to_radians(0.0),
size, size,
color, color,
sprite_index: sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
} }
@ -233,13 +252,17 @@ impl Radar {
let position = Point2::new(10.0 + (radar_size / 2.0), -10.0 - (radar_size / 2.0)) let position = Point2::new(10.0 + (radar_size / 2.0), -10.0 - (radar_size / 2.0))
+ Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0); + Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0);
let sprite = input.ct.get_sprite(arrow_sprite);
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NwC.to_int(), anchor: PositionAnchor::NwC.to_int(),
position: position.into(), position: position.into(),
angle, angle,
size: 10.0, size: 10.0,
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)], color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
sprite_index: arrow_sprite.get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });
} }

View File

@ -26,32 +26,38 @@ impl Status {
)) ))
.unwrap(); .unwrap();
match player_ship.data.get_state() { match player_ship.get_data().get_state() {
ShipState::Dead => { ShipState::Dead => {
current_shields = 0.0; current_shields = 0.0;
current_hull = 0.0; current_hull = 0.0;
max_shields = player_ship.data.get_outfits().get_shield_strength(); max_shields = player_ship.get_data().get_outfits().get_shield_strength();
max_hull = input.ct.get_ship(player_ship.data.get_content()).hull; max_hull = input.ct.get_ship(player_ship.get_data().get_content()).hull;
} }
ShipState::UnLanding { .. } ShipState::UnLanding { .. }
| ShipState::Landing { .. } | ShipState::Landing { .. }
| ShipState::Landed { .. } | ShipState::Landed { .. }
| ShipState::Collapsing { .. } | ShipState::Collapsing { .. }
| ShipState::Flying { .. } => { | ShipState::Flying { .. } => {
current_shields = player_ship.data.get_shields(); current_shields = player_ship.get_data().get_shields();
current_hull = player_ship.data.get_hull(); current_hull = player_ship.get_data().get_hull();
max_shields = player_ship.data.get_outfits().get_shield_strength(); max_shields = player_ship.get_data().get_outfits().get_shield_strength();
max_hull = input.ct.get_ship(player_ship.data.get_content()).hull; max_hull = input.ct.get_ship(player_ship.get_data().get_content()).hull;
} }
} }
let sprite = input
.ct
.get_sprite(input.ct.get_sprite_handle("ui::status"));
let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE
state.push_ui_buffer(UiInstance { state.push_ui_buffer(UiInstance {
anchor: PositionAnchor::NeNe.to_int(), anchor: PositionAnchor::NeNe.to_int(),
position: [-10.0, -10.0], position: [-10.0, -10.0],
angle: 0.0, angle: 0.0,
size: 200.0, size: 200.0,
color: [1.0, 1.0, 1.0, 1.0], color: [1.0, 1.0, 1.0, 1.0],
sprite_index: input.ct.get_sprite_handle("ui::status").get_index(), texture_index: [texture_a, texture_a],
texture_fade: 1.0,
mask_index: [0, 0], mask_index: [0, 0],
}); });

View File

@ -85,7 +85,13 @@ impl BufferObject for StarfieldInstance {
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct ObjectInstance { pub struct ObjectInstance {
/// What texture to use for this instance /// What texture to use for this instance
pub sprite_index: u32, /// Result will be a faded mix of the two textures
/// given here. Only the first will be shown if fade = 0.0,
/// and only the second if fade = 1.0
pub texture_index: [u32; 2],
/// Fade parameter for texture index
/// Must be between 0 and 1.
pub texture_fade: f32,
/// Which object this instance is for /// Which object this instance is for
pub object_index: u32, pub object_index: u32,
@ -100,16 +106,22 @@ impl BufferObject for ObjectInstance {
// instance when the shader starts processing a new instance // instance when the shader starts processing a new instance
step_mode: wgpu::VertexStepMode::Instance, step_mode: wgpu::VertexStepMode::Instance,
attributes: &[ attributes: &[
// Sprite // Texture
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: 0, offset: 0,
shader_location: 2, shader_location: 2,
format: wgpu::VertexFormat::Uint32, format: wgpu::VertexFormat::Uint32x2,
},
// Texture fade
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
shader_location: 3,
format: wgpu::VertexFormat::Float32,
}, },
// Object // Object
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 1]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 3, shader_location: 4,
format: wgpu::VertexFormat::Uint32, format: wgpu::VertexFormat::Uint32,
}, },
], ],
@ -138,12 +150,14 @@ pub struct UiInstance {
/// Fill this array with ones if no recoloring should be done. /// Fill this array with ones if no recoloring should be done.
pub color: [f32; 4], pub color: [f32; 4],
/// What texture to use for this sprite /// What texture to use for this instance
pub sprite_index: u32, pub texture_index: [u32; 2],
/// Fade parameter between textures
pub texture_fade: f32,
/// What mask to use for this sprite /// What texture to use to mask this instance
/// If the first element is not 1, no mask is used. /// If the first element is not 1, no mask is used.
/// Second element is sprite index of mask. /// Second element is a texture index
pub mask_index: [u32; 2], pub mask_index: [u32; 2],
} }
@ -186,16 +200,22 @@ impl BufferObject for UiInstance {
shader_location: 6, shader_location: 6,
format: wgpu::VertexFormat::Float32x4, format: wgpu::VertexFormat::Float32x4,
}, },
// Sprite // Texture
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 9]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 9]>() as wgpu::BufferAddress,
shader_location: 7, shader_location: 7,
format: wgpu::VertexFormat::Uint32, format: wgpu::VertexFormat::Uint32x2,
},
// Texture fade
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
shader_location: 8,
format: wgpu::VertexFormat::Float32,
}, },
// Mask // Mask
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 10]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
shader_location: 8, shader_location: 9,
format: wgpu::VertexFormat::Uint32x2, format: wgpu::VertexFormat::Uint32x2,
}, },
], ],
@ -232,8 +252,10 @@ pub struct ParticleInstance {
/// Fade this particle out over this many seconds as it expires /// Fade this particle out over this many seconds as it expires
pub fade: f32, pub fade: f32,
/// What sprite to use for this particle /// What texture to use for this particle
pub sprite_index: u32, pub texture_index: [u32; 2],
/// Fade parameter for texture index
pub texture_fade: f32,
} }
impl BufferObject for ParticleInstance { impl BufferObject for ParticleInstance {
@ -290,11 +312,17 @@ impl BufferObject for ParticleInstance {
shader_location: 9, shader_location: 9,
format: wgpu::VertexFormat::Float32, format: wgpu::VertexFormat::Float32,
}, },
// Sprite // Texture
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 10]>() as wgpu::BufferAddress, offset: mem::size_of::<[f32; 10]>() as wgpu::BufferAddress,
shader_location: 10, shader_location: 10,
format: wgpu::VertexFormat::Uint32, format: wgpu::VertexFormat::Uint32x2,
},
// Texture fade
wgpu::VertexAttribute {
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
shader_location: 11,
format: wgpu::VertexFormat::Float32,
}, },
], ],
} }