Compare commits
No commits in common. "c7077fc7018015f15528e4173a733d9fb2db4048" and "7565f09e3b1a9d4be4d3cdde6fa0182dbfcda83f" have entirely different histories.
c7077fc701
...
7565f09e3b
4
TODO.md
4
TODO.md
|
@ -6,10 +6,6 @@
|
||||||
- Sound system
|
- Sound system
|
||||||
- Ship death debris
|
- Ship death debris
|
||||||
- Sprite reels
|
- Sprite reels
|
||||||
- Passive engine glow
|
|
||||||
- Ship death damage and force events
|
|
||||||
- Fix particle inherit velocity
|
|
||||||
- Global shader variable prefix
|
|
||||||
|
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "target"
|
||||||
size = 50.0
|
size = 50.0
|
||||||
|
|
||||||
[effect."blaster impact"]
|
[effect."blaster expire"]
|
||||||
sprite = "particle::blaster"
|
sprite = "particle::blaster"
|
||||||
lifetime = "inherit"
|
lifetime = "inherit"
|
||||||
inherit_velocity = "target"
|
inherit_velocity = "projectile"
|
||||||
size = 3.0
|
size = 3.0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ projectile.force = 0.0
|
||||||
|
|
||||||
projectile.collider.ball.radius = 2.0
|
projectile.collider.ball.radius = 2.0
|
||||||
|
|
||||||
projectile.impact_effect = "blaster impact"
|
projectile.impact_effect = "small explosion"
|
||||||
|
|
||||||
projectile.expire_effect.sprite = "particle::blaster"
|
projectile.expire_effect.sprite = "particle::blaster"
|
||||||
projectile.expire_effect.lifetime = "inherit"
|
projectile.expire_effect.lifetime = "inherit"
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
fn animate(instance: InstanceInput, age: f32) -> u32 {
|
fn animate(instance: InstanceInput, age: f32) -> u32 {
|
||||||
|
|
||||||
let idx = instance.texture_index;
|
let idx = instance.texture_index;
|
||||||
let len = sprites[idx].frame_count;
|
let len = sprites.data[idx].frame_count;
|
||||||
let rep = sprites[idx].repeatmode;
|
let rep = sprites.data[idx].repeatmode;
|
||||||
let fps = sprites[idx].fps;
|
let fps = sprites.data[idx].fps;
|
||||||
var frame: u32 = u32(0);
|
var frame: u32 = u32(0);
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,5 +39,5 @@ fn animate(instance: InstanceInput, age: f32) -> u32 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame + sprites[idx].first_frame;
|
return frame + sprites.data[idx].first_frame;
|
||||||
}
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
// INCLUDE: global uniform header
|
// INCLUDE: global uniform header
|
||||||
|
|
||||||
struct InstanceInput {
|
struct InstanceInput {
|
||||||
@location(2) texture_index: u32,
|
@location(2) transform_matrix_0: vec4<f32>,
|
||||||
@location(3) object_index: u32,
|
@location(3) transform_matrix_1: vec4<f32>,
|
||||||
|
@location(4) transform_matrix_2: vec4<f32>,
|
||||||
|
@location(5) transform_matrix_3: vec4<f32>,
|
||||||
|
@location(6) texture_index: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
|
@ -25,55 +28,23 @@ var sampler_array: binding_array<sampler>;
|
||||||
|
|
||||||
// INCLUDE: animate.wgsl
|
// INCLUDE: animate.wgsl
|
||||||
|
|
||||||
fn transform_vertex(obj: ObjectLocation, vertex: VertexInput, instance: InstanceInput) -> vec4<f32> {
|
|
||||||
// Object scale
|
|
||||||
let scale = obj.size / (global.camera_zoom.x * obj.zpos);
|
|
||||||
|
|
||||||
// Apply scale and sprite aspect
|
|
||||||
// Note that our mesh starts centered at (0, 0). This is important!
|
|
||||||
var pos: vec2<f32> = vec2(
|
|
||||||
vertex.position.x * scale * sprites[instance.texture_index].aspect,
|
|
||||||
vertex.position.y * scale
|
|
||||||
);
|
|
||||||
|
|
||||||
// Apply rotation
|
|
||||||
pos = mat2x2(
|
|
||||||
vec2(cos(obj.angle), sin(obj.angle)),
|
|
||||||
vec2(-sin(obj.angle), cos(obj.angle))
|
|
||||||
) * pos;
|
|
||||||
|
|
||||||
// Correct for screen aspect, preserving height
|
|
||||||
// This must be done AFTER rotation.
|
|
||||||
pos = vec2(
|
|
||||||
pos.x / global.window_aspect.x,
|
|
||||||
pos.y
|
|
||||||
);
|
|
||||||
|
|
||||||
// Distance-adjusted world position
|
|
||||||
let trans = (vec2(obj.xpos, obj.ypos) - global.camera_position) / obj.zpos;
|
|
||||||
|
|
||||||
// Finally, translate
|
|
||||||
//
|
|
||||||
// Note that we divide camera zoom by two.
|
|
||||||
// The height of the viewport is `zoom` in game units,
|
|
||||||
// but it's 2 in screen units (since coordinates range from -1 to 1)
|
|
||||||
pos = pos + vec2(
|
|
||||||
trans.x / (global.camera_zoom.x / 2.0) / global.window_aspect.x,
|
|
||||||
trans.y / (global.camera_zoom.x / 2.0)
|
|
||||||
);
|
|
||||||
|
|
||||||
return vec4<f32>(pos, 0.0, 1.0);;
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vertex_main(
|
fn vertex_main(
|
||||||
vertex: VertexInput,
|
vertex: VertexInput,
|
||||||
instance: InstanceInput,
|
instance: InstanceInput,
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
|
||||||
out.position = transform_vertex(objects[instance.object_index], vertex, instance);
|
|
||||||
|
|
||||||
let t = atlas[animate(instance, global.current_time.x)];
|
let transform = mat4x4<f32>(
|
||||||
|
instance.transform_matrix_0,
|
||||||
|
instance.transform_matrix_1,
|
||||||
|
instance.transform_matrix_2,
|
||||||
|
instance.transform_matrix_3,
|
||||||
|
);
|
||||||
|
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||||
|
|
||||||
|
let t = atlas.data[animate(instance, global.current_time.x)];
|
||||||
out.texture_index = t.atlas_texture;
|
out.texture_index = 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 {
|
||||||
|
|
|
@ -55,7 +55,7 @@ fn vertex_main(
|
||||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
var scale: f32 = instance.size / global.camera_zoom.x;
|
||||||
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
||||||
pos = pos * vec2<f32>(
|
pos = pos * vec2<f32>(
|
||||||
sprites[instance.texture_index].aspect * scale / global.window_aspect.x,
|
sprites.data[instance.texture_index].aspect * scale / global.window_aspect.x,
|
||||||
scale
|
scale
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ fn vertex_main(
|
||||||
|
|
||||||
|
|
||||||
// Compute texture coordinates
|
// Compute texture coordinates
|
||||||
let t = atlas[animate(instance, age)];
|
let t = atlas.data[animate(instance, age)];
|
||||||
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 {
|
||||||
|
|
|
@ -109,8 +109,8 @@ fn vertex_main(
|
||||||
|
|
||||||
|
|
||||||
// Starfield sprites may not be animated
|
// Starfield sprites may not be animated
|
||||||
let i = sprites[global.starfield_sprite.x].first_frame;
|
let i = sprites.data[global.starfield_sprite.x].first_frame;
|
||||||
let t = atlas[i];
|
let t = atlas.data[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 {
|
||||||
|
|
|
@ -48,7 +48,7 @@ fn vertex_main(
|
||||||
out.color_transform = instance.color_transform;
|
out.color_transform = instance.color_transform;
|
||||||
|
|
||||||
// Pick texture frame
|
// Pick texture frame
|
||||||
let t = atlas[animate(instance, global.current_time.x)];
|
let t = atlas.data[animate(instance, global.current_time.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 {
|
||||||
|
|
|
@ -19,12 +19,12 @@ pub struct ImageLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct AtlasArray {
|
pub struct ImageLocationArray {
|
||||||
pub data: [ImageLocation; IMAGE_LIMIT as usize],
|
pub data: [ImageLocation; IMAGE_LIMIT as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Pod for AtlasArray {}
|
unsafe impl Pod for ImageLocationArray {}
|
||||||
unsafe impl Zeroable for AtlasArray {
|
unsafe impl Zeroable for ImageLocationArray {
|
||||||
fn zeroed() -> Self {
|
fn zeroed() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: [ImageLocation::zeroed(); IMAGE_LIMIT as usize],
|
data: [ImageLocation::zeroed(); IMAGE_LIMIT as usize],
|
||||||
|
@ -32,12 +32,18 @@ unsafe impl Zeroable for AtlasArray {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AtlasArray {
|
impl Default for ImageLocationArray {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::zeroed()
|
Self::zeroed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtlasArray {
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
|
||||||
|
pub struct AtlasContent {
|
||||||
|
pub data: ImageLocationArray,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtlasContent {
|
||||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
use galactica_constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT, SPRITE_LIMIT};
|
use galactica_constants::{IMAGE_LIMIT, SPRITE_LIMIT};
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
use super::{object::ObjectLocationArray, AtlasArray, DataContent, SpriteDataArray};
|
use super::{AtlasContent, DataContent, SpriteContent};
|
||||||
|
|
||||||
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 sprite_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,
|
||||||
pub content: DataContent,
|
pub content: DataContent,
|
||||||
|
@ -17,7 +16,6 @@ impl GlobalUniform {
|
||||||
pub fn shader_header(&self, group: u32) -> String {
|
pub fn shader_header(&self, group: u32) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
|
||||||
// Global game data
|
|
||||||
out.push_str(&format!("@group({group}) @binding(0)\n"));
|
out.push_str(&format!("@group({group}) @binding(0)\n"));
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#"
|
r#"
|
||||||
|
@ -37,16 +35,10 @@ impl GlobalUniform {
|
||||||
);
|
);
|
||||||
out.push_str("\n");
|
out.push_str("\n");
|
||||||
|
|
||||||
// Atlas image locations
|
out.push_str(&format!("@group({group}) @binding(1)\n"));
|
||||||
out.push_str(&format!(
|
|
||||||
r#"
|
|
||||||
@group({group}) @binding(1)
|
|
||||||
var<uniform> atlas: array<ImageLocation, {IMAGE_LIMIT}>;
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
out.push_str("\n");
|
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#"
|
r#"
|
||||||
|
var<uniform> atlas: AtlasUniform;
|
||||||
struct ImageLocation {
|
struct ImageLocation {
|
||||||
xpos: f32,
|
xpos: f32,
|
||||||
ypos: f32,
|
ypos: f32,
|
||||||
|
@ -61,6 +53,14 @@ impl GlobalUniform {
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
out.push_str(&format!(
|
||||||
|
r#"
|
||||||
|
struct AtlasUniform {{
|
||||||
|
data: array<ImageLocation, {}>,
|
||||||
|
}};
|
||||||
|
"#,
|
||||||
|
IMAGE_LIMIT,
|
||||||
|
));
|
||||||
out.push_str("\n");
|
out.push_str("\n");
|
||||||
|
|
||||||
// TODO: document
|
// TODO: document
|
||||||
|
@ -69,16 +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!("@group({group}) @binding(2)\n"));
|
||||||
out.push_str(&format!(
|
|
||||||
r#"
|
|
||||||
@group({group}) @binding(2)
|
|
||||||
var<uniform> sprites: array<SpriteData, {SPRITE_LIMIT}>;
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
out.push_str("\n");
|
|
||||||
out.push_str(
|
out.push_str(
|
||||||
r#"
|
r#"
|
||||||
|
var<uniform> sprites: SpriteUniform;
|
||||||
struct SpriteData {
|
struct SpriteData {
|
||||||
frame_count: u32,
|
frame_count: u32,
|
||||||
repeatmode: u32,
|
repeatmode: u32,
|
||||||
|
@ -92,31 +86,14 @@ impl GlobalUniform {
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
out.push_str("\n");
|
|
||||||
|
|
||||||
// Object location data
|
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
r#"
|
r#"
|
||||||
@group({group}) @binding(3)
|
struct SpriteUniform {{
|
||||||
var<uniform> objects: array<ObjectLocation, {OBJECT_SPRITE_INSTANCE_LIMIT}>;
|
data: array<SpriteData, {}>,
|
||||||
"#
|
}};
|
||||||
));
|
|
||||||
out.push_str("\n");
|
|
||||||
out.push_str(
|
|
||||||
r#"
|
|
||||||
struct ObjectLocation {
|
|
||||||
xpos: f32,
|
|
||||||
ypos: f32,
|
|
||||||
zpos: f32,
|
|
||||||
angle: f32,
|
|
||||||
size: f32,
|
|
||||||
|
|
||||||
padding_a: f32,
|
|
||||||
padding_b: f32,
|
|
||||||
padding_c: f32,
|
|
||||||
};
|
|
||||||
"#,
|
"#,
|
||||||
);
|
SPRITE_LIMIT,
|
||||||
|
));
|
||||||
out.push_str("\n");
|
out.push_str("\n");
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -132,21 +109,14 @@ impl GlobalUniform {
|
||||||
|
|
||||||
let atlas_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
let atlas_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("global uniform atlas buffer"),
|
label: Some("global uniform atlas buffer"),
|
||||||
size: AtlasArray::SIZE,
|
size: AtlasContent::SIZE,
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let sprite_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
let sprite_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("global uniform sprite buffer"),
|
label: Some("global uniform sprite buffer"),
|
||||||
size: SpriteDataArray::SIZE,
|
size: SpriteContent::SIZE,
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let object_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: Some("global uniform object buffer"),
|
|
||||||
size: ObjectLocationArray::SIZE,
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
mapped_at_creation: false,
|
mapped_at_creation: false,
|
||||||
});
|
});
|
||||||
|
@ -183,16 +153,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"),
|
||||||
});
|
});
|
||||||
|
@ -212,10 +172,6 @@ impl GlobalUniform {
|
||||||
binding: 2,
|
binding: 2,
|
||||||
resource: sprite_buffer.as_entire_binding(),
|
resource: sprite_buffer.as_entire_binding(),
|
||||||
},
|
},
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 3,
|
|
||||||
resource: object_buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
label: Some("global uniform bind group"),
|
label: Some("global uniform bind group"),
|
||||||
});
|
});
|
||||||
|
@ -224,7 +180,6 @@ impl GlobalUniform {
|
||||||
data_buffer,
|
data_buffer,
|
||||||
atlas_buffer,
|
atlas_buffer,
|
||||||
sprite_buffer,
|
sprite_buffer,
|
||||||
object_buffer,
|
|
||||||
bind_group,
|
bind_group,
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
content: DataContent::default(),
|
content: DataContent::default(),
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
mod atlas;
|
mod atlascontent;
|
||||||
mod data;
|
mod datacontent;
|
||||||
mod globaluniform;
|
mod globaluniform;
|
||||||
mod object;
|
mod spritecontent;
|
||||||
mod sprite;
|
|
||||||
|
|
||||||
pub use atlas::{AtlasArray, ImageLocation};
|
pub use atlascontent::{AtlasContent, ImageLocation, ImageLocationArray};
|
||||||
pub use data::DataContent;
|
pub use datacontent::DataContent;
|
||||||
pub use globaluniform::GlobalUniform;
|
pub use globaluniform::GlobalUniform;
|
||||||
pub use object::ObjectLocation;
|
pub use spritecontent::{SpriteContent, SpriteData, SpriteDataArray};
|
||||||
pub use sprite::{SpriteData, SpriteDataArray};
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
use bytemuck::{Pod, Zeroable};
|
|
||||||
use galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT;
|
|
||||||
use std::mem;
|
|
||||||
use wgpu;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, Default)]
|
|
||||||
pub struct ObjectLocation {
|
|
||||||
pub xpos: f32,
|
|
||||||
pub ypos: f32,
|
|
||||||
pub zpos: f32,
|
|
||||||
pub angle: f32,
|
|
||||||
pub size: f32,
|
|
||||||
|
|
||||||
pub _padding: [f32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectLocation {
|
|
||||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct ObjectLocationArray {
|
|
||||||
pub data: [ObjectLocation; OBJECT_SPRITE_INSTANCE_LIMIT as usize],
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Pod for ObjectLocationArray {}
|
|
||||||
unsafe impl Zeroable for ObjectLocationArray {
|
|
||||||
fn zeroed() -> Self {
|
|
||||||
Self {
|
|
||||||
data: [ObjectLocation::zeroed(); OBJECT_SPRITE_INSTANCE_LIMIT as usize],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ObjectLocationArray {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::zeroed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectLocationArray {
|
|
||||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
|
||||||
}
|
|
|
@ -38,6 +38,12 @@ impl Default for SpriteDataArray {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpriteDataArray {
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Pod, Zeroable)]
|
||||||
|
pub struct SpriteContent {
|
||||||
|
pub data: SpriteDataArray,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteContent {
|
||||||
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
pub const SIZE: u64 = mem::size_of::<Self>() as wgpu::BufferAddress;
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{EuclideanSpace, Matrix2, Matrix4, Point2, Rad, Vector3};
|
use cgmath::{Deg, EuclideanSpace, Matrix2, Matrix4, Point2, Vector3};
|
||||||
use galactica_constants;
|
use galactica_constants;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use std::{iter, rc::Rc};
|
use std::{iter, rc::Rc};
|
||||||
|
@ -9,7 +9,7 @@ use winit::{self, dpi::LogicalSize, window::Window};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
content,
|
content,
|
||||||
globaluniform::{DataContent, GlobalUniform, ObjectLocation},
|
globaluniform::{AtlasContent, DataContent, GlobalUniform, SpriteContent},
|
||||||
pipeline::PipelineBuilder,
|
pipeline::PipelineBuilder,
|
||||||
sprite::ObjectSubSprite,
|
sprite::ObjectSubSprite,
|
||||||
starfield::Starfield,
|
starfield::Starfield,
|
||||||
|
@ -322,11 +322,12 @@ impl GPUState {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Game dimensions of this sprite post-scale.
|
// Game dimensions of this sprite post-scale.
|
||||||
// Post-scale width or height, whichever is larger.
|
// Don't divide by 2, we use this later.
|
||||||
// This is in game units.
|
let height = s.size / s.pos.z;
|
||||||
//
|
|
||||||
// We take the maximum to account for rotated sprites.
|
// Width or height, whichever is larger.
|
||||||
let m = (s.size / s.pos.z) * s.sprite.aspect.max(1.0);
|
// Accounts for sprite rotation.
|
||||||
|
let m = height * s.sprite.aspect.max(1.0);
|
||||||
|
|
||||||
// Don't draw (or compute matrices for)
|
// Don't draw (or compute matrices for)
|
||||||
// sprites that are off the screen
|
// sprites that are off the screen
|
||||||
|
@ -338,27 +339,51 @@ impl GPUState {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write this object's location data
|
// TODO: clean up
|
||||||
self.queue.write_buffer(
|
let scale = height / state.camera_zoom;
|
||||||
&self.global_uniform.object_buffer,
|
|
||||||
ObjectLocation::SIZE * instances.len() as u64,
|
|
||||||
bytemuck::cast_slice(&[ObjectLocation {
|
|
||||||
xpos: s.pos.x,
|
|
||||||
ypos: s.pos.y,
|
|
||||||
zpos: s.pos.z,
|
|
||||||
angle: Rad::from(s.angle).0,
|
|
||||||
size: s.size,
|
|
||||||
_padding: Default::default(),
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Push this object's instance
|
// Note that our mesh starts centered at (0, 0).
|
||||||
instances.push(ObjectInstance {
|
// This is essential---we do not want scale and rotation
|
||||||
sprite_index: s.sprite.get_index(),
|
// changing our sprite's position!
|
||||||
object_index: instances.len() as u32,
|
|
||||||
|
// Apply sprite aspect ratio, preserving height.
|
||||||
|
// This must be done *before* rotation.
|
||||||
|
//
|
||||||
|
// We apply the provided scale here as well as a minor optimization
|
||||||
|
let sprite_aspect_and_scale =
|
||||||
|
Matrix4::from_nonuniform_scale(s.sprite.aspect * scale, scale, 1.0);
|
||||||
|
|
||||||
|
// Apply rotation
|
||||||
|
let rotate = Matrix4::from_angle_z(s.angle);
|
||||||
|
|
||||||
|
// Apply screen aspect ratio, again preserving height.
|
||||||
|
// This must be done AFTER rotation... think about it!
|
||||||
|
let screen_aspect = Matrix4::from_nonuniform_scale(1.0 / self.window_aspect, 1.0, 1.0);
|
||||||
|
|
||||||
|
// After finishing all ops, translate.
|
||||||
|
// This must be done last, all other operations
|
||||||
|
// require us to be at (0, 0).
|
||||||
|
//
|
||||||
|
// Note that we divide camera zoom by two.
|
||||||
|
// THIS IS IMPORTANT!
|
||||||
|
// The height of the viewport is `zoom` in game units,
|
||||||
|
// but it's 2 in screen units! (since coordinates range from -1 to 1)
|
||||||
|
let translate = Matrix4::from_translation(Vector3 {
|
||||||
|
x: pos.x / (state.camera_zoom / 2.0) / self.window_aspect,
|
||||||
|
y: pos.y / (state.camera_zoom / 2.0),
|
||||||
|
z: 0.0,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
/*
|
// Order matters!
|
||||||
|
// The rightmost matrix is applied first.
|
||||||
|
let t =
|
||||||
|
OPENGL_TO_WGPU_MATRIX * translate * screen_aspect * rotate * sprite_aspect_and_scale;
|
||||||
|
|
||||||
|
instances.push(ObjectInstance {
|
||||||
|
transform: t.into(),
|
||||||
|
sprite_index: s.sprite.get_index(),
|
||||||
|
});
|
||||||
|
|
||||||
// Add children
|
// Add children
|
||||||
if let Some(children) = &s.children {
|
if let Some(children) = &s.children {
|
||||||
for c in children {
|
for c in children {
|
||||||
|
@ -408,7 +433,6 @@ impl GPUState {
|
||||||
sprite_index: s.sprite.get_index(),
|
sprite_index: s.sprite.get_index(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/// Create a ObjectInstance for a ui sprite and add it to `instances`
|
/// Create a ObjectInstance for a ui sprite and add it to `instances`
|
||||||
fn push_ui_sprite(&self, instances: &mut Vec<UiInstance>, s: &UiSprite) {
|
fn push_ui_sprite(&self, instances: &mut Vec<UiInstance>, s: &UiSprite) {
|
||||||
|
@ -527,12 +551,16 @@ impl GPUState {
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.global_uniform.atlas_buffer,
|
&self.global_uniform.atlas_buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[self.texture_array.image_locations]),
|
bytemuck::cast_slice(&[AtlasContent {
|
||||||
|
data: self.texture_array.image_locations,
|
||||||
|
}]),
|
||||||
);
|
);
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.global_uniform.sprite_buffer,
|
&self.global_uniform.sprite_buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[self.texture_array.sprite_data]),
|
bytemuck::cast_slice(&[SpriteContent {
|
||||||
|
data: self.texture_array.sprite_data,
|
||||||
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.update_starfield_buffer();
|
self.update_starfield_buffer();
|
||||||
|
@ -573,7 +601,6 @@ impl GPUState {
|
||||||
});
|
});
|
||||||
|
|
||||||
let s = state.content.get_starfield_handle();
|
let s = state.content.get_starfield_handle();
|
||||||
|
|
||||||
// Update global values
|
// Update global values
|
||||||
self.queue.write_buffer(
|
self.queue.write_buffer(
|
||||||
&self.global_uniform.data_buffer,
|
&self.global_uniform.data_buffer,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
content,
|
content,
|
||||||
globaluniform::{AtlasArray, ImageLocation, SpriteData, SpriteDataArray},
|
globaluniform::{ImageLocation, ImageLocationArray, SpriteData, SpriteDataArray},
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck::Zeroable;
|
use bytemuck::Zeroable;
|
||||||
|
@ -86,7 +86,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 image_locations: ImageLocationArray,
|
||||||
pub sprite_data: SpriteDataArray,
|
pub sprite_data: SpriteDataArray,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ impl TextureArray {
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut image_locations = AtlasArray::zeroed();
|
let mut image_locations = ImageLocationArray::zeroed();
|
||||||
let mut sprite_data = SpriteDataArray::zeroed();
|
let mut sprite_data = SpriteDataArray::zeroed();
|
||||||
|
|
||||||
println!("sending to gpu");
|
println!("sending to gpu");
|
||||||
|
|
|
@ -84,11 +84,12 @@ impl BufferObject for StarfieldInstance {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[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
|
/// Extra transformations this sprite
|
||||||
pub sprite_index: u32,
|
/// (rotation, etc)
|
||||||
|
pub transform: [[f32; 4]; 4],
|
||||||
|
|
||||||
/// Which object this instance is for
|
/// What texture to use for this sprite
|
||||||
pub object_index: u32,
|
pub sprite_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferObject for ObjectInstance {
|
impl BufferObject for ObjectInstance {
|
||||||
|
@ -100,16 +101,31 @@ 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
|
// 4 arrays = 1 4x4 matrix
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
shader_location: 2,
|
shader_location: 2,
|
||||||
format: wgpu::VertexFormat::Uint32,
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
},
|
},
|
||||||
// Object
|
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: mem::size_of::<[f32; 1]>() as wgpu::BufferAddress,
|
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||||
shader_location: 3,
|
shader_location: 3,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 4,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 5,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
// Sprite
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 6,
|
||||||
format: wgpu::VertexFormat::Uint32,
|
format: wgpu::VertexFormat::Uint32,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue