Adapted renderer for new texture indexing
parent
c382431747
commit
7cde4f2346
|
@ -1,3 +1,5 @@
|
||||||
|
// INCLUDE: global uniform header
|
||||||
|
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) transform_matrix_0: vec4<f32>,
|
@location(2) transform_matrix_0: vec4<f32>,
|
||||||
@location(3) transform_matrix_1: vec4<f32>,
|
@location(3) transform_matrix_1: vec4<f32>,
|
||||||
|
@ -9,27 +11,12 @@ struct InstanceInput {
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@location(1) texture_coords: vec2<f32>,
|
@location(1) texture_coords: vec2<f32>,
|
||||||
}
|
};
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position) position: vec4<f32>,
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) texture_coords: vec2<f32>,
|
@location(0) texture_coords: vec2<f32>,
|
||||||
@location(1) texture_index: u32,
|
@location(1) texture_index: u32,
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> global: GlobalUniform;
|
|
||||||
struct GlobalUniform {
|
|
||||||
camera_position: vec2<f32>,
|
|
||||||
camera_zoom: vec2<f32>,
|
|
||||||
camera_zoom_limits: vec2<f32>,
|
|
||||||
window_size: vec2<f32>,
|
|
||||||
window_aspect: vec2<f32>,
|
|
||||||
starfield_texture: vec2<u32>,
|
|
||||||
starfield_tile_size: vec2<f32>,
|
|
||||||
starfield_size_limits: vec2<f32>,
|
|
||||||
current_time: vec2<f32>,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,8 +43,17 @@ fn vertex_main(
|
||||||
|
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||||
out.texture_coords = vertex.texture_coords;
|
|
||||||
out.texture_index = instance.texture_index;
|
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);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// INCLUDE: global uniform header
|
||||||
|
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) position: vec2<f32>,
|
@location(2) position: vec2<f32>,
|
||||||
@location(3) velocity: vec2<f32>,
|
@location(3) velocity: vec2<f32>,
|
||||||
|
@ -22,21 +24,6 @@ struct VertexOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> global: GlobalUniform;
|
|
||||||
struct GlobalUniform {
|
|
||||||
camera_position: vec2<f32>,
|
|
||||||
camera_zoom: vec2<f32>,
|
|
||||||
camera_zoom_limits: vec2<f32>,
|
|
||||||
window_size: vec2<f32>,
|
|
||||||
window_aspect: vec2<f32>,
|
|
||||||
starfield_texture: vec2<u32>,
|
|
||||||
starfield_tile_size: vec2<f32>,
|
|
||||||
starfield_size_limits: vec2<f32>,
|
|
||||||
current_time: vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var texture_array: binding_array<texture_2d<f32>>;
|
var texture_array: binding_array<texture_2d<f32>>;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
|
@ -57,7 +44,7 @@ fn vertex_main(
|
||||||
out.texture_coords = vertex.texture_coords;
|
out.texture_coords = vertex.texture_coords;
|
||||||
|
|
||||||
if instance.expires < global.current_time.x {
|
if instance.expires < global.current_time.x {
|
||||||
out.texture_index = instance.texture_index_len_rep.x;
|
out.texture_index = u32(0);
|
||||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +67,17 @@ fn vertex_main(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
out.texture_index = instance.texture_index_len_rep.x + frame;
|
// Pick image
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
||||||
|
}
|
||||||
|
|
||||||
let rotation = mat2x2(instance.rotation_0, instance.rotation_1);
|
let rotation = mat2x2(instance.rotation_0, instance.rotation_1);
|
||||||
|
|
||||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
var scale: f32 = instance.size / global.camera_zoom.x;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// INCLUDE: global uniform header
|
||||||
|
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) position: vec3<f32>,
|
@location(2) position: vec3<f32>,
|
||||||
@location(3) size: f32,
|
@location(3) size: f32,
|
||||||
|
@ -12,24 +14,10 @@ struct VertexInput {
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position) position: vec4<f32>,
|
@builtin(position) position: vec4<f32>,
|
||||||
@location(0) texture_coords: vec2<f32>,
|
@location(0) texture_coords: vec2<f32>,
|
||||||
@location(1) tint: vec2<f32>,
|
@location(1) texture_index: u32,
|
||||||
|
@location(2) tint: vec2<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> global: GlobalUniform;
|
|
||||||
struct GlobalUniform {
|
|
||||||
camera_position: vec2<f32>,
|
|
||||||
camera_zoom: vec2<f32>,
|
|
||||||
camera_zoom_limits: vec2<f32>,
|
|
||||||
window_size: vec2<f32>,
|
|
||||||
window_aspect: vec2<f32>,
|
|
||||||
starfield_texture: vec2<u32>,
|
|
||||||
starfield_tile_size: vec2<f32>,
|
|
||||||
starfield_size_limits: vec2<f32>,
|
|
||||||
current_time: vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var texture_array: binding_array<texture_2d<f32>>;
|
var texture_array: binding_array<texture_2d<f32>>;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
|
@ -49,7 +37,6 @@ fn vertex_main(
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
|
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.texture_coords = vertex.texture_coords;
|
|
||||||
out.tint = instance.tint;
|
out.tint = instance.tint;
|
||||||
|
|
||||||
// Center of the tile the camera is currently in, in game coordinates.
|
// Center of the tile the camera is currently in, in game coordinates.
|
||||||
|
@ -120,6 +107,17 @@ fn vertex_main(
|
||||||
);
|
);
|
||||||
|
|
||||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +134,7 @@ fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let c_del = c_bot - c_top;
|
let c_del = c_bot - c_top;
|
||||||
|
|
||||||
return textureSampleLevel(
|
return textureSampleLevel(
|
||||||
texture_array[global.starfield_texture.x],
|
texture_array[in.texture_index],
|
||||||
sampler_array[0],
|
sampler_array[0],
|
||||||
in.texture_coords,
|
in.texture_coords,
|
||||||
0.0
|
0.0
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// INCLUDE: global uniform header
|
||||||
|
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) transform_matrix_0: vec4<f32>,
|
@location(2) transform_matrix_0: vec4<f32>,
|
||||||
@location(3) transform_matrix_1: vec4<f32>,
|
@location(3) transform_matrix_1: vec4<f32>,
|
||||||
|
@ -19,22 +21,6 @@ struct VertexOutput {
|
||||||
@location(2) color_transform: vec4<f32>,
|
@location(2) color_transform: vec4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> global: GlobalUniform;
|
|
||||||
struct GlobalUniform {
|
|
||||||
camera_position: vec2<f32>,
|
|
||||||
camera_zoom: vec2<f32>,
|
|
||||||
camera_zoom_limits: vec2<f32>,
|
|
||||||
window_size: vec2<f32>,
|
|
||||||
window_aspect: vec2<f32>,
|
|
||||||
starfield_texture: vec2<u32>,
|
|
||||||
starfield_tile_size: vec2<f32>,
|
|
||||||
starfield_size_limits: vec2<f32>,
|
|
||||||
current_time: vec2<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var texture_array: binding_array<texture_2d<f32>>;
|
var texture_array: binding_array<texture_2d<f32>>;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
|
@ -58,9 +44,18 @@ fn vertex_main(
|
||||||
|
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||||
out.texture_coords = vertex.texture_coords;
|
|
||||||
out.texture_index = instance.texture_index;
|
|
||||||
out.color_transform = instance.color_transform;
|
out.color_transform = instance.color_transform;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if vertex.texture_coords.y == 1.0 {
|
||||||
|
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
|
||||||
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
use bytemuck;
|
|
||||||
use std::mem;
|
|
||||||
use wgpu;
|
|
||||||
|
|
||||||
pub struct GlobalData {
|
|
||||||
pub buffer: wgpu::Buffer,
|
|
||||||
pub bind_group: wgpu::BindGroup,
|
|
||||||
pub bind_group_layout: wgpu::BindGroupLayout,
|
|
||||||
pub content: GlobalDataContent,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
// Uniforms require uniform alignment.
|
|
||||||
// Since the largest value in this array is a [f32; 2],
|
|
||||||
// all smaller values must be padded.
|
|
||||||
// also, [f32; 3] are aligned as [f32; 4]
|
|
||||||
// (since alignments must be powers of two)
|
|
||||||
pub struct GlobalDataContent {
|
|
||||||
/// Camera position, in game units
|
|
||||||
pub camera_position: [f32; 2],
|
|
||||||
|
|
||||||
/// Camera zoom value, in game units.
|
|
||||||
/// Second component is ignored.
|
|
||||||
pub camera_zoom: [f32; 2],
|
|
||||||
|
|
||||||
/// Camera zoom min and max.
|
|
||||||
pub camera_zoom_limits: [f32; 2],
|
|
||||||
|
|
||||||
/// Size ratio of window, in physical pixels
|
|
||||||
pub window_size: [f32; 2],
|
|
||||||
|
|
||||||
/// Aspect ratio of window
|
|
||||||
/// Second component is ignored.
|
|
||||||
pub window_aspect: [f32; 2],
|
|
||||||
|
|
||||||
/// Texture index of starfield sprites
|
|
||||||
/// Second component is ignored.
|
|
||||||
pub starfield_texture: [u32; 2],
|
|
||||||
|
|
||||||
// Size of (square) starfield tiles, in game units
|
|
||||||
/// Second component is ignored.
|
|
||||||
pub starfield_tile_size: [f32; 2],
|
|
||||||
|
|
||||||
/// Min and max starfield star size, in game units
|
|
||||||
pub starfield_size_limits: [f32; 2],
|
|
||||||
|
|
||||||
/// Current game time, in seconds.
|
|
||||||
/// Second component is ignored.
|
|
||||||
pub current_time: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalDataContent {
|
|
||||||
const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalData {
|
|
||||||
pub fn new(device: &wgpu::Device) -> Self {
|
|
||||||
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: GlobalDataContent::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 {
|
|
||||||
binding: 0,
|
|
||||||
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("globaldata bind group layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout: &bind_group_layout,
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: buffer.as_entire_binding(),
|
|
||||||
}],
|
|
||||||
label: Some("globaldata bind group"),
|
|
||||||
});
|
|
||||||
|
|
||||||
return Self {
|
|
||||||
buffer,
|
|
||||||
bind_group,
|
|
||||||
bind_group_layout,
|
|
||||||
content: GlobalDataContent::default(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use std::mem;
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ImageLocations {
|
||||||
|
/// Format: [x, y, w, h], in texture coordinates
|
||||||
|
pub data: [ImageLocation; 108],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Pod for ImageLocations {}
|
||||||
|
unsafe impl Zeroable for ImageLocations {
|
||||||
|
fn zeroed() -> Self {
|
||||||
|
Self {
|
||||||
|
data: [ImageLocation::zeroed(); 108],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ImageLocations {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::zeroed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
|
||||||
|
pub struct AtlasContent {
|
||||||
|
pub locations: ImageLocations,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtlasContent {
|
||||||
|
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
use bytemuck;
|
||||||
|
use std::mem;
|
||||||
|
use wgpu;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
// Uniforms require uniform alignment.
|
||||||
|
// Since the largest value in this array is a [f32; 2],
|
||||||
|
// all smaller values must be padded.
|
||||||
|
// also, [f32; 3] are aligned as [f32; 4]
|
||||||
|
// (since alignments must be powers of two)
|
||||||
|
pub struct DataContent {
|
||||||
|
/// Camera position, in game units
|
||||||
|
pub camera_position: [f32; 2],
|
||||||
|
|
||||||
|
/// Camera zoom value, in game units.
|
||||||
|
/// Second component is ignored.
|
||||||
|
pub camera_zoom: [f32; 2],
|
||||||
|
|
||||||
|
/// Camera zoom min and max.
|
||||||
|
pub camera_zoom_limits: [f32; 2],
|
||||||
|
|
||||||
|
/// Size ratio of window, in physical pixels
|
||||||
|
pub window_size: [f32; 2],
|
||||||
|
|
||||||
|
/// Aspect ratio of window
|
||||||
|
/// Second component is ignored.
|
||||||
|
pub window_aspect: [f32; 2],
|
||||||
|
|
||||||
|
/// Index of starfield sprite
|
||||||
|
/// Second component is ignored.
|
||||||
|
pub starfield_sprite: [u32; 2],
|
||||||
|
|
||||||
|
// Size of (square) starfield tiles, in game units
|
||||||
|
/// Second component is ignored.
|
||||||
|
pub starfield_tile_size: [f32; 2],
|
||||||
|
|
||||||
|
/// Min and max starfield star size, in game units
|
||||||
|
pub starfield_size_limits: [f32; 2],
|
||||||
|
|
||||||
|
/// Current game time, in seconds.
|
||||||
|
/// Second component is ignored.
|
||||||
|
pub current_time: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataContent {
|
||||||
|
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
use wgpu;
|
||||||
|
|
||||||
|
use super::{AtlasContent, DataContent};
|
||||||
|
|
||||||
|
pub struct GlobalUniform {
|
||||||
|
pub data_buffer: wgpu::Buffer,
|
||||||
|
pub atlas_buffer: wgpu::Buffer,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pub content: DataContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalUniform {
|
||||||
|
pub fn shader_header(&self, group: u32) -> String {
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
out.push_str(&format!("@group({group}) @binding(0)\n"));
|
||||||
|
out.push_str(
|
||||||
|
r#"
|
||||||
|
var<uniform> global: GlobalUniform;
|
||||||
|
struct GlobalUniform {
|
||||||
|
camera_position: vec2<f32>,
|
||||||
|
camera_zoom: vec2<f32>,
|
||||||
|
camera_zoom_limits: vec2<f32>,
|
||||||
|
window_size: vec2<f32>,
|
||||||
|
window_aspect: vec2<f32>,
|
||||||
|
starfield_texture: vec2<u32>,
|
||||||
|
starfield_tile_size: vec2<f32>,
|
||||||
|
starfield_size_limits: vec2<f32>,
|
||||||
|
current_time: vec2<f32>,
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
out.push_str("\n");
|
||||||
|
|
||||||
|
out.push_str(&format!("@group({group}) @binding(1)\n"));
|
||||||
|
out.push_str(
|
||||||
|
r#"
|
||||||
|
var<uniform> atlas: AtlasUniform;
|
||||||
|
struct TextureLocation {
|
||||||
|
xpos: f32,
|
||||||
|
ypos: f32,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
};
|
||||||
|
struct AtlasUniform {
|
||||||
|
texture_locations: array<TextureLocation, 108>,
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
out.push_str("\n");
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("global uniform data buffer"),
|
||||||
|
size: DataContent::SIZE,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let atlas_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("global uniform atlas buffer"),
|
||||||
|
size: AtlasContent::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 {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
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"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: data_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: atlas_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: Some("global uniform bind group"),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Self {
|
||||||
|
data_buffer,
|
||||||
|
atlas_buffer,
|
||||||
|
bind_group,
|
||||||
|
bind_group_layout,
|
||||||
|
content: DataContent::default(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
mod atlascontent;
|
||||||
|
mod datacontent;
|
||||||
|
mod globaluniform;
|
||||||
|
|
||||||
|
pub use atlascontent::{AtlasContent, ImageLocation, ImageLocations};
|
||||||
|
pub use datacontent::DataContent;
|
||||||
|
pub use globaluniform::GlobalUniform;
|
|
@ -8,7 +8,7 @@ use winit::{self, dpi::LogicalSize, window::Window};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
content,
|
content,
|
||||||
globaldata::{GlobalData, GlobalDataContent},
|
globaluniform::{AtlasContent, DataContent, GlobalUniform},
|
||||||
pipeline::PipelineBuilder,
|
pipeline::PipelineBuilder,
|
||||||
sprite::ObjectSubSprite,
|
sprite::ObjectSubSprite,
|
||||||
starfield::Starfield,
|
starfield::Starfield,
|
||||||
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
types::{ObjectInstance, ParticleInstance, StarfieldInstance, TexturedVertex, UiInstance},
|
types::{ObjectInstance, ParticleInstance, StarfieldInstance, TexturedVertex, UiInstance},
|
||||||
BufferObject, VertexBuffer,
|
BufferObject, VertexBuffer,
|
||||||
},
|
},
|
||||||
FrameState, ObjectSprite, UiSprite, OPENGL_TO_WGPU_MATRIX,
|
ObjectSprite, RenderState, UiSprite, OPENGL_TO_WGPU_MATRIX,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A high-level GPU wrapper. Consumes game state,
|
/// A high-level GPU wrapper. Consumes game state,
|
||||||
|
@ -44,7 +44,7 @@ pub struct GPUState {
|
||||||
|
|
||||||
starfield: Starfield,
|
starfield: Starfield,
|
||||||
texture_array: TextureArray,
|
texture_array: TextureArray,
|
||||||
global_data: GlobalData,
|
global_uniform: GlobalUniform,
|
||||||
vertex_buffers: VertexBuffers,
|
vertex_buffers: VertexBuffers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,19 @@ struct VertexBuffers {
|
||||||
particle: Rc<VertexBuffer>,
|
particle: Rc<VertexBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Preprocess shader files
|
||||||
|
fn preprocess_shader(
|
||||||
|
shader: &str,
|
||||||
|
global_uniform: &GlobalUniform,
|
||||||
|
global_uniform_group: u32,
|
||||||
|
) -> String {
|
||||||
|
// Insert common headers
|
||||||
|
shader.replace(
|
||||||
|
"// INCLUDE: global uniform header",
|
||||||
|
&global_uniform.shader_header(global_uniform_group),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
/// Make a new GPUState that draws on `window`
|
/// Make a new GPUState that draws on `window`
|
||||||
pub async fn new(window: Window, ct: &content::Content) -> Result<Self> {
|
pub async fn new(window: Window, ct: &content::Content) -> Result<Self> {
|
||||||
|
@ -161,22 +174,26 @@ impl GPUState {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load uniforms
|
// Load uniforms
|
||||||
let global_data = GlobalData::new(&device);
|
let global_uniform = GlobalUniform::new(&device);
|
||||||
let texture_array = TextureArray::new(&device, &queue, ct)?;
|
let texture_array = TextureArray::new(&device, &queue, ct)?;
|
||||||
|
|
||||||
// Make sure these match the indices in each shader
|
// Make sure these match the indices in each shader
|
||||||
let bind_group_layouts = &[
|
let bind_group_layouts = &[
|
||||||
&texture_array.bind_group_layout,
|
&texture_array.bind_group_layout,
|
||||||
&global_data.bind_group_layout,
|
&global_uniform.bind_group_layout,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create render pipelines
|
// Create render pipelines
|
||||||
let object_pipeline = PipelineBuilder::new("object", &device)
|
let object_pipeline = PipelineBuilder::new("object", &device)
|
||||||
.set_shader(include_str!(concat!(
|
.set_shader(&preprocess_shader(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
&include_str!(concat!(
|
||||||
"/shaders/",
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
"object.wgsl"
|
"/shaders/",
|
||||||
)))
|
"object.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(&vertex_buffers.object)
|
.set_vertex_buffer(&vertex_buffers.object)
|
||||||
|
@ -184,11 +201,15 @@ impl GPUState {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
||||||
.set_shader(include_str!(concat!(
|
.set_shader(&preprocess_shader(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
&include_str!(concat!(
|
||||||
"/shaders/",
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
"starfield.wgsl"
|
"/shaders/",
|
||||||
)))
|
"starfield.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(&vertex_buffers.starfield)
|
.set_vertex_buffer(&vertex_buffers.starfield)
|
||||||
|
@ -196,11 +217,11 @@ impl GPUState {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let ui_pipeline = PipelineBuilder::new("ui", &device)
|
let ui_pipeline = PipelineBuilder::new("ui", &device)
|
||||||
.set_shader(include_str!(concat!(
|
.set_shader(&preprocess_shader(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
&include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/", "ui.wgsl")),
|
||||||
"/shaders/",
|
&global_uniform,
|
||||||
"ui.wgsl"
|
1,
|
||||||
)))
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(&vertex_buffers.ui)
|
.set_vertex_buffer(&vertex_buffers.ui)
|
||||||
|
@ -208,11 +229,15 @@ impl GPUState {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let particle_pipeline = PipelineBuilder::new("particle", &device)
|
let particle_pipeline = PipelineBuilder::new("particle", &device)
|
||||||
.set_shader(include_str!(concat!(
|
.set_shader(&preprocess_shader(
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
&include_str!(concat!(
|
||||||
"/shaders/",
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
"particle.wgsl"
|
"/shaders/",
|
||||||
)))
|
"particle.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
.set_format(config.format)
|
.set_format(config.format)
|
||||||
.set_triangle(true)
|
.set_triangle(true)
|
||||||
.set_vertex_buffer(&vertex_buffers.particle)
|
.set_vertex_buffer(&vertex_buffers.particle)
|
||||||
|
@ -239,7 +264,7 @@ impl GPUState {
|
||||||
|
|
||||||
starfield,
|
starfield,
|
||||||
texture_array,
|
texture_array,
|
||||||
global_data,
|
global_uniform,
|
||||||
vertex_buffers,
|
vertex_buffers,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -283,7 +308,7 @@ impl GPUState {
|
||||||
} - camera_pos.to_vec())
|
} - camera_pos.to_vec())
|
||||||
/ s.pos.z
|
/ s.pos.z
|
||||||
};
|
};
|
||||||
let texture = self.texture_array.get_texture(s.texture);
|
let texture = self.texture_array.get_texture(s.sprite);
|
||||||
|
|
||||||
// Game dimensions of this sprite post-scale.
|
// Game dimensions of this sprite post-scale.
|
||||||
// Don't divide by 2, we use this later.
|
// Don't divide by 2, we use this later.
|
||||||
|
@ -345,7 +370,7 @@ impl GPUState {
|
||||||
|
|
||||||
instances.push(ObjectInstance {
|
instances.push(ObjectInstance {
|
||||||
transform: t.into(),
|
transform: t.into(),
|
||||||
texture_index: texture.index,
|
sprite_index: texture.index,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add children
|
// Add children
|
||||||
|
@ -366,7 +391,7 @@ impl GPUState {
|
||||||
parent_pos: Point2<f32>,
|
parent_pos: Point2<f32>,
|
||||||
parent_angle: Deg<f32>,
|
parent_angle: Deg<f32>,
|
||||||
) {
|
) {
|
||||||
let texture = self.texture_array.get_texture(s.texture);
|
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 * camera_zoom);
|
||||||
let sprite_aspect_and_scale =
|
let sprite_aspect_and_scale =
|
||||||
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
Matrix4::from_nonuniform_scale(texture.aspect * scale, scale, 1.0);
|
||||||
|
@ -395,7 +420,7 @@ impl GPUState {
|
||||||
|
|
||||||
instances.push(ObjectInstance {
|
instances.push(ObjectInstance {
|
||||||
transform: t.into(),
|
transform: t.into(),
|
||||||
texture_index: texture.index,
|
sprite_index: texture.index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,7 +429,7 @@ impl GPUState {
|
||||||
let logical_size: LogicalSize<f32> =
|
let logical_size: LogicalSize<f32> =
|
||||||
self.window_size.to_logical(self.window.scale_factor());
|
self.window_size.to_logical(self.window.scale_factor());
|
||||||
|
|
||||||
let texture = self.texture_array.get_texture(s.texture);
|
let texture = self.texture_array.get_texture(s.sprite);
|
||||||
let width = s.dimensions.x;
|
let width = s.dimensions.x;
|
||||||
let height = s.dimensions.y;
|
let height = s.dimensions.y;
|
||||||
|
|
||||||
|
@ -448,7 +473,7 @@ impl GPUState {
|
||||||
|
|
||||||
instances.push(UiInstance {
|
instances.push(UiInstance {
|
||||||
transform: (OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * scale).into(),
|
transform: (OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * scale).into(),
|
||||||
texture_index: texture.index,
|
sprite_index: texture.index,
|
||||||
color: s.color.unwrap_or([1.0, 1.0, 1.0, 1.0]),
|
color: s.color.unwrap_or([1.0, 1.0, 1.0, 1.0]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -456,7 +481,7 @@ impl GPUState {
|
||||||
/// Make an instance for all the game's sprites
|
/// Make an instance for all the game's sprites
|
||||||
/// (Objects and UI)
|
/// (Objects and UI)
|
||||||
/// This will Will panic if any X_SPRITE_INSTANCE_LIMIT is exceeded.
|
/// This will Will panic if any X_SPRITE_INSTANCE_LIMIT is exceeded.
|
||||||
fn update_sprite_instances(&self, framestate: FrameState) -> (usize, usize) {
|
fn update_sprite_instances(&self, framestate: RenderState) -> (usize, usize) {
|
||||||
let mut object_instances: Vec<ObjectInstance> = Vec::new();
|
let mut object_instances: Vec<ObjectInstance> = Vec::new();
|
||||||
|
|
||||||
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
||||||
|
@ -518,8 +543,22 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize the rendering engine
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
// Update global values
|
||||||
|
self.queue.write_buffer(
|
||||||
|
&self.global_uniform.atlas_buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&[AtlasContent {
|
||||||
|
locations: self.texture_array.data,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.update_starfield_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
/// Main render function. Draws sprites on a window.
|
/// Main render function. Draws sprites on a window.
|
||||||
pub fn render(&mut self, framestate: FrameState) -> Result<(), wgpu::SurfaceError> {
|
pub fn render(&mut self, framestate: RenderState) -> Result<(), wgpu::SurfaceError> {
|
||||||
let output = self.surface.get_current_texture()?;
|
let output = self.surface.get_current_texture()?;
|
||||||
let view = output
|
let view = output
|
||||||
.texture
|
.texture
|
||||||
|
@ -554,9 +593,9 @@ impl GPUState {
|
||||||
|
|
||||||
// Update global values
|
// Update global values
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.global_data.buffer,
|
&self.global_uniform.data_buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[GlobalDataContent {
|
bytemuck::cast_slice(&[DataContent {
|
||||||
camera_position: framestate.camera_pos.into(),
|
camera_position: framestate.camera_pos.into(),
|
||||||
camera_zoom: [framestate.camera_zoom, 0.0],
|
camera_zoom: [framestate.camera_zoom, 0.0],
|
||||||
camera_zoom_limits: [galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX],
|
camera_zoom_limits: [galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX],
|
||||||
|
@ -565,7 +604,7 @@ impl GPUState {
|
||||||
self.window_size.height as f32,
|
self.window_size.height as f32,
|
||||||
],
|
],
|
||||||
window_aspect: [self.window_aspect, 0.0],
|
window_aspect: [self.window_aspect, 0.0],
|
||||||
starfield_texture: [self.texture_array.get_starfield_texture().index, 0],
|
starfield_sprite: [self.texture_array.get_starfield_texture().index, 0],
|
||||||
starfield_tile_size: [galactica_constants::STARFIELD_SIZE as f32, 0.0],
|
starfield_tile_size: [galactica_constants::STARFIELD_SIZE as f32, 0.0],
|
||||||
starfield_size_limits: [
|
starfield_size_limits: [
|
||||||
galactica_constants::STARFIELD_SIZE_MIN,
|
galactica_constants::STARFIELD_SIZE_MIN,
|
||||||
|
@ -577,7 +616,7 @@ impl GPUState {
|
||||||
|
|
||||||
// Write all new particles to GPU buffer
|
// Write all new particles to GPU buffer
|
||||||
for i in framestate.new_particles.iter() {
|
for i in framestate.new_particles.iter() {
|
||||||
let texture = self.texture_array.get_texture(i.texture);
|
let texture = self.texture_array.get_texture(i.sprite);
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.vertex_buffers.particle.instances,
|
&self.vertex_buffers.particle.instances,
|
||||||
ParticleInstance::SIZE * self.vertex_buffers.particle_array_head,
|
ParticleInstance::SIZE * self.vertex_buffers.particle_array_head,
|
||||||
|
@ -607,7 +646,7 @@ impl GPUState {
|
||||||
// These should match the indices in each shader,
|
// These should match the indices in each shader,
|
||||||
// and should each have a corresponding bind group layout.
|
// 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(0, &self.texture_array.bind_group, &[]);
|
||||||
render_pass.set_bind_group(1, &self.global_data.bind_group, &[]);
|
render_pass.set_bind_group(1, &self.global_uniform.bind_group, &[]);
|
||||||
|
|
||||||
// Starfield pipeline
|
// Starfield pipeline
|
||||||
self.vertex_buffers.starfield.set_in_pass(&mut render_pass);
|
self.vertex_buffers.starfield.set_in_pass(&mut render_pass);
|
||||||
|
|
|
@ -7,18 +7,18 @@
|
||||||
//! and the only one external code should interact with.
|
//! and the only one external code should interact with.
|
||||||
//! (Excluding data structs, like [`ObjectSprite`])
|
//! (Excluding data structs, like [`ObjectSprite`])
|
||||||
|
|
||||||
mod framestate;
|
mod globaluniform;
|
||||||
mod globaldata;
|
|
||||||
mod gpustate;
|
mod gpustate;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
|
mod renderstate;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
mod starfield;
|
mod starfield;
|
||||||
mod texturearray;
|
mod texturearray;
|
||||||
mod vertexbuffer;
|
mod vertexbuffer;
|
||||||
|
|
||||||
pub use framestate::FrameState;
|
|
||||||
use galactica_content as content;
|
use galactica_content as content;
|
||||||
pub use gpustate::GPUState;
|
pub use gpustate::GPUState;
|
||||||
|
pub use renderstate::RenderState;
|
||||||
pub use sprite::{AnchoredUiPosition, ObjectSprite, ObjectSubSprite, ParticleBuilder, UiSprite};
|
pub use sprite::{AnchoredUiPosition, ObjectSprite, ObjectSubSprite, ParticleBuilder, UiSprite};
|
||||||
|
|
||||||
use cgmath::Matrix4;
|
use cgmath::Matrix4;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use cgmath::Point2;
|
||||||
use crate::{ObjectSprite, ParticleBuilder, UiSprite};
|
use crate::{ObjectSprite, ParticleBuilder, UiSprite};
|
||||||
|
|
||||||
/// Bundles parameters passed to a single call to GPUState::render
|
/// Bundles parameters passed to a single call to GPUState::render
|
||||||
pub struct FrameState<'a> {
|
pub struct RenderState<'a> {
|
||||||
/// Camera position, in world units
|
/// Camera position, in world units
|
||||||
pub camera_pos: Point2<f32>,
|
pub camera_pos: Point2<f32>,
|
||||||
|
|
|
@ -3,8 +3,8 @@ use cgmath::{Deg, Point2, Point3, Vector2};
|
||||||
|
|
||||||
/// Instructions to create a new particle
|
/// Instructions to create a new particle
|
||||||
pub struct ParticleBuilder {
|
pub struct ParticleBuilder {
|
||||||
/// The texture to use for this particle
|
/// The sprite to use for this particle
|
||||||
pub texture: content::TextureHandle,
|
pub sprite: content::SpriteHandle,
|
||||||
|
|
||||||
/// This object's center, in world coordinates.
|
/// This object's center, in world coordinates.
|
||||||
pub pos: Point2<f32>,
|
pub pos: Point2<f32>,
|
||||||
|
@ -18,7 +18,7 @@ pub struct ParticleBuilder {
|
||||||
/// This particle's lifetime, in seconds
|
/// This particle's lifetime, in seconds
|
||||||
pub lifetime: f32,
|
pub lifetime: f32,
|
||||||
|
|
||||||
/// The size of this sprite,
|
/// The size of this particle,
|
||||||
/// given as height in world units.
|
/// given as height in world units.
|
||||||
pub size: f32,
|
pub size: f32,
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,8 @@ pub enum AnchoredUiPosition {
|
||||||
/// A sprite that represents a ui element
|
/// A sprite that represents a ui element
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UiSprite {
|
pub struct UiSprite {
|
||||||
/// The texture to use for this sprite
|
/// The sprite to draw
|
||||||
pub texture: content::TextureHandle,
|
pub sprite: content::SpriteHandle,
|
||||||
|
|
||||||
/// This object's position, in logical (dpi-adjusted) pixels
|
/// This object's position, in logical (dpi-adjusted) pixels
|
||||||
pub pos: AnchoredUiPosition,
|
pub pos: AnchoredUiPosition,
|
||||||
|
@ -75,8 +75,8 @@ pub struct UiSprite {
|
||||||
/// Ships, planets, debris, etc
|
/// Ships, planets, debris, etc
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjectSprite {
|
pub struct ObjectSprite {
|
||||||
/// The texture to use for this sprite
|
/// The sprite to draw
|
||||||
pub texture: content::TextureHandle,
|
pub sprite: content::SpriteHandle,
|
||||||
|
|
||||||
/// This object's center, in world coordinates.
|
/// This object's center, in world coordinates.
|
||||||
pub pos: Point3<f32>,
|
pub pos: Point3<f32>,
|
||||||
|
@ -97,8 +97,8 @@ pub struct ObjectSprite {
|
||||||
/// A sprite that is drawn relative to an ObjectSprite.
|
/// A sprite that is drawn relative to an ObjectSprite.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ObjectSubSprite {
|
pub struct ObjectSubSprite {
|
||||||
/// The sprite texture to draw
|
/// The sprite to draw
|
||||||
pub texture: content::TextureHandle,
|
pub sprite: content::SpriteHandle,
|
||||||
|
|
||||||
/// This object's position, in world coordinates.
|
/// This object's position, in world coordinates.
|
||||||
/// This is relative to this sprite's parent.
|
/// This is relative to this sprite's parent.
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::content;
|
use crate::{
|
||||||
|
content,
|
||||||
|
globaluniform::{ImageLocation, ImageLocations},
|
||||||
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use bytemuck::Zeroable;
|
||||||
|
use galactica_packer::SpriteAtlasImage;
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
|
use std::{collections::HashMap, fs::File, io::Read, num::NonZeroU32};
|
||||||
use wgpu::BindGroupLayout;
|
use wgpu::BindGroupLayout;
|
||||||
|
@ -67,30 +72,32 @@ impl RawTexture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
pub index: u32, // Index in texture array
|
pub index: u32, // Index in texture array
|
||||||
pub len: u32, // Number of frames
|
pub len: u32, // Number of frames
|
||||||
pub fps: f32, // Frames per second
|
pub fps: f32, // Frames per second
|
||||||
pub aspect: f32, // width / height
|
pub aspect: f32, // width / height
|
||||||
pub repeat: u32, // How to re-play this texture
|
pub repeat: u32, // How to re-play this texture
|
||||||
|
pub location: Vec<SpriteAtlasImage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
starfield_handle: content::TextureHandle,
|
starfield_handle: content::SpriteHandle,
|
||||||
textures: HashMap<content::TextureHandle, Texture>,
|
sprites: HashMap<content::SpriteHandle, Texture>,
|
||||||
|
pub data: ImageLocations,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureArray {
|
impl TextureArray {
|
||||||
pub fn get_starfield_texture(&self) -> Texture {
|
pub fn get_starfield_texture(&self) -> &Texture {
|
||||||
*self.textures.get(&self.starfield_handle).unwrap()
|
self.sprites.get(&self.starfield_handle).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_texture(&self, handle: content::TextureHandle) -> Texture {
|
pub fn get_texture(&self, handle: content::SpriteHandle) -> &Texture {
|
||||||
match self.textures.get(&handle) {
|
match self.sprites.get(&handle) {
|
||||||
Some(x) => *x,
|
Some(x) => x,
|
||||||
None => unreachable!("Tried to get a texture that doesn't exist"),
|
None => unreachable!("Tried to get a texture that doesn't exist"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,26 +105,44 @@ impl TextureArray {
|
||||||
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &content::Content) -> Result<Self> {
|
pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, ct: &content::Content) -> Result<Self> {
|
||||||
// Load all textures
|
// Load all textures
|
||||||
let mut texture_data = Vec::new();
|
let mut texture_data = Vec::new();
|
||||||
let mut textures = HashMap::new();
|
let mut sprites = HashMap::new();
|
||||||
|
|
||||||
for t in &ct.textures {
|
println!("opening image");
|
||||||
let index = texture_data.len() as u32;
|
let mut f = File::open("atlas-0.bmp")?;
|
||||||
for f in &t.frames {
|
let mut bytes = Vec::new();
|
||||||
let mut f = File::open(&f)?;
|
f.read_to_end(&mut bytes)?;
|
||||||
let mut bytes = Vec::new();
|
texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, "Atlas")?);
|
||||||
f.read_to_end(&mut bytes)?;
|
|
||||||
texture_data.push(RawTexture::from_bytes(&device, &queue, &bytes, &t.name)?);
|
let mut tx = ImageLocations::zeroed();
|
||||||
}
|
|
||||||
textures.insert(
|
println!("sending to gpu");
|
||||||
|
let mut i = 0;
|
||||||
|
for t in &ct.sprites {
|
||||||
|
let loc = ct.get_image(&t.frames[0]);
|
||||||
|
|
||||||
|
sprites.insert(
|
||||||
t.handle,
|
t.handle,
|
||||||
Texture {
|
Texture {
|
||||||
index,
|
index: i,
|
||||||
aspect: t.handle.aspect,
|
aspect: t.aspect,
|
||||||
fps: t.fps,
|
fps: t.fps,
|
||||||
len: t.frames.len() as u32,
|
len: t.frames.len() as u32,
|
||||||
repeat: t.repeat.as_int(),
|
repeat: t.repeat.as_int(),
|
||||||
|
location: vec![loc.clone()],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
@ -136,7 +161,7 @@ impl TextureArray {
|
||||||
// Texture data
|
// Texture data
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
ty: wgpu::BindingType::Texture {
|
ty: wgpu::BindingType::Texture {
|
||||||
multisampled: false,
|
multisampled: false,
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
@ -147,7 +172,7 @@ impl TextureArray {
|
||||||
// Texture sampler
|
// Texture sampler
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 1,
|
binding: 1,
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
count: NonZeroU32::new(1),
|
count: NonZeroU32::new(1),
|
||||||
},
|
},
|
||||||
|
@ -161,7 +186,6 @@ impl TextureArray {
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
// Array of all views
|
|
||||||
resource: wgpu::BindingResource::TextureViewArray(&views),
|
resource: wgpu::BindingResource::TextureViewArray(&views),
|
||||||
},
|
},
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
|
@ -174,8 +198,9 @@ impl TextureArray {
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
bind_group,
|
bind_group,
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
textures: textures,
|
sprites,
|
||||||
starfield_handle: ct.get_starfield_handle(),
|
starfield_handle: ct.get_starfield_handle(),
|
||||||
|
data: tx,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ pub struct ObjectInstance {
|
||||||
pub transform: [[f32; 4]; 4],
|
pub transform: [[f32; 4]; 4],
|
||||||
|
|
||||||
/// What texture to use for this sprite
|
/// What texture to use for this sprite
|
||||||
pub texture_index: u32,
|
pub sprite_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferObject for ObjectInstance {
|
impl BufferObject for ObjectInstance {
|
||||||
|
@ -122,7 +122,7 @@ impl BufferObject for ObjectInstance {
|
||||||
shader_location: 5,
|
shader_location: 5,
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
},
|
},
|
||||||
// Texture
|
// Sprite
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||||
shader_location: 6,
|
shader_location: 6,
|
||||||
|
@ -146,7 +146,7 @@ pub struct UiInstance {
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
|
|
||||||
/// What texture to use for this sprite
|
/// What texture to use for this sprite
|
||||||
pub texture_index: u32,
|
pub sprite_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferObject for UiInstance {
|
impl BufferObject for UiInstance {
|
||||||
|
@ -185,7 +185,7 @@ impl BufferObject for UiInstance {
|
||||||
shader_location: 6,
|
shader_location: 6,
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
},
|
},
|
||||||
// Texture
|
// Sprite
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 20]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 20]>() as wgpu::BufferAddress,
|
||||||
shader_location: 7,
|
shader_location: 7,
|
||||||
|
|
Loading…
Reference in New Issue