Added particle foundation
parent
e5a96621a4
commit
0e38fdb21e
|
@ -50,5 +50,8 @@ pub const OBJECT_SPRITE_INSTANCE_LIMIT: u64 = 500;
|
|||
/// We can draw at most this many ui sprites on the screen.
|
||||
pub const UI_SPRITE_INSTANCE_LIMIT: u64 = 100;
|
||||
|
||||
/// The size of our circular particle buffer. When we create particles, the oldest ones are replaced.
|
||||
pub const PARTICLE_SPRITE_INSTANCE_LIMIT: u64 = 1000;
|
||||
|
||||
/// Must be small enough to fit in an i32
|
||||
pub const STARFIELD_SPRITE_INSTANCE_LIMIT: u64 = STARFIELD_COUNT * 24;
|
||||
|
|
|
@ -56,7 +56,6 @@ impl crate::Build for Texture {
|
|||
index: ct.textures.len(),
|
||||
aspect: dim.0 as f32 / dim.1 as f32,
|
||||
};
|
||||
ct.texture_index.insert(texture_name.clone(), h);
|
||||
|
||||
if texture_name == ct.starfield_texture_name {
|
||||
if ct.starfield_handle.is_none() {
|
||||
|
@ -67,9 +66,10 @@ impl crate::Build for Texture {
|
|||
}
|
||||
}
|
||||
|
||||
ct.texture_index.insert(texture_name.clone(), h);
|
||||
ct.textures.push(Self {
|
||||
name: texture_name,
|
||||
path: path,
|
||||
path,
|
||||
handle: h,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ struct InstanceInput {
|
|||
@location(3) transform_matrix_1: vec4<f32>,
|
||||
@location(4) transform_matrix_2: vec4<f32>,
|
||||
@location(5) transform_matrix_3: vec4<f32>,
|
||||
@location(6) texture_idx: u32,
|
||||
@location(6) texture_index: u32,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
|
@ -14,7 +14,7 @@ struct VertexInput {
|
|||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) texture_coords: vec2<f32>,
|
||||
@location(1) index: u32,
|
||||
@location(1) texture_index: u32,
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@ struct GlobalUniform {
|
|||
starfield_texture: vec2<u32>,
|
||||
starfield_tile_size: vec2<f32>,
|
||||
starfield_size_limits: vec2<f32>,
|
||||
current_time: vec2<f32>,
|
||||
};
|
||||
|
||||
|
||||
|
@ -56,7 +57,7 @@ fn vertex_main(
|
|||
var out: VertexOutput;
|
||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||
out.texture_coords = vertex.texture_coords;
|
||||
out.index = instance.texture_idx;
|
||||
out.texture_index = instance.texture_index;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -64,7 +65,7 @@ fn vertex_main(
|
|||
@fragment
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSampleLevel(
|
||||
texture_array[in.index],
|
||||
texture_array[in.texture_index],
|
||||
sampler_array[0],
|
||||
in.texture_coords,
|
||||
0.0
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
struct InstanceInput {
|
||||
@location(2) position: vec3<f32>,
|
||||
@location(3) size: f32,
|
||||
@location(4) expires: f32,
|
||||
@location(5) texture_index: u32,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) texture_coords: vec2<f32>,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) texture_coords: vec2<f32>,
|
||||
@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>,
|
||||
};
|
||||
|
||||
|
||||
@group(0) @binding(0)
|
||||
var texture_array: binding_array<texture_2d<f32>>;
|
||||
@group(0) @binding(1)
|
||||
var sampler_array: binding_array<sampler>;
|
||||
|
||||
|
||||
|
||||
|
||||
@vertex
|
||||
fn vertex_main(
|
||||
vertex: VertexInput,
|
||||
instance: InstanceInput,
|
||||
) -> VertexOutput {
|
||||
|
||||
var out: VertexOutput;
|
||||
out.texture_coords = vertex.texture_coords;
|
||||
out.texture_index = instance.texture_index;
|
||||
|
||||
if instance.expires < global.current_time.x {
|
||||
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
var scale: f32 = instance.size / global.camera_zoom.x;
|
||||
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
|
||||
|
||||
pos = pos * vec2<f32>(
|
||||
1.0 * scale / global.window_aspect.x,
|
||||
scale
|
||||
);
|
||||
|
||||
var ipos: vec2<f32> = vec2(instance.position.x, instance.position.y) - global.camera_position;
|
||||
pos = pos + vec2<f32>(
|
||||
ipos.x / (global.camera_zoom.x/2.0) / global.window_aspect.x,
|
||||
ipos.y / (global.camera_zoom.x/2.0)
|
||||
);
|
||||
|
||||
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@fragment
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSampleLevel(
|
||||
texture_array[in.texture_index],
|
||||
sampler_array[0],
|
||||
in.texture_coords,
|
||||
0.0
|
||||
).rgba;
|
||||
}
|
|
@ -26,6 +26,7 @@ struct GlobalUniform {
|
|||
starfield_texture: vec2<u32>,
|
||||
starfield_tile_size: vec2<f32>,
|
||||
starfield_size_limits: vec2<f32>,
|
||||
current_time: vec2<f32>,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ struct InstanceInput {
|
|||
@location(4) transform_matrix_2: vec4<f32>,
|
||||
@location(5) transform_matrix_3: vec4<f32>,
|
||||
@location(6) color_transform: vec4<f32>,
|
||||
@location(7) texture_idx: u32,
|
||||
@location(7) texture_index: u32,
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
|
@ -15,7 +15,7 @@ struct VertexInput {
|
|||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) texture_coords: vec2<f32>,
|
||||
@location(1) index: u32,
|
||||
@location(1) texture_index: u32,
|
||||
@location(2) color_transform: vec4<f32>,
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ struct GlobalUniform {
|
|||
starfield_texture: vec2<u32>,
|
||||
starfield_tile_size: vec2<f32>,
|
||||
starfield_size_limits: vec2<f32>,
|
||||
current_time: vec2<f32>,
|
||||
};
|
||||
|
||||
|
||||
|
@ -58,7 +59,7 @@ fn vertex_main(
|
|||
var out: VertexOutput;
|
||||
out.position = transform * vec4<f32>(vertex.position, 1.0);
|
||||
out.texture_coords = vertex.texture_coords;
|
||||
out.index = instance.texture_idx;
|
||||
out.texture_index = instance.texture_index;
|
||||
out.color_transform = instance.color_transform;
|
||||
return out;
|
||||
}
|
||||
|
@ -67,7 +68,7 @@ fn vertex_main(
|
|||
@fragment
|
||||
fn fragment_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSampleLevel(
|
||||
texture_array[in.index],
|
||||
texture_array[in.texture_index],
|
||||
sampler_array[0],
|
||||
in.texture_coords,
|
||||
0.0
|
||||
|
|
|
@ -30,7 +30,7 @@ pub struct GlobalDataContent {
|
|||
/// Size ratio of window, in physical pixels
|
||||
pub window_size: [f32; 2],
|
||||
|
||||
// Aspect ration of window
|
||||
/// Aspect ratio of window
|
||||
/// Second component is ignored.
|
||||
pub window_aspect: [f32; 2],
|
||||
|
||||
|
@ -42,8 +42,12 @@ pub struct GlobalDataContent {
|
|||
/// Second component is ignored.
|
||||
pub starfield_tile_size: [f32; 2],
|
||||
|
||||
// Min and max starfield star size, in game units
|
||||
/// 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 {
|
||||
|
@ -70,7 +74,7 @@ impl GlobalData {
|
|||
},
|
||||
count: None,
|
||||
}],
|
||||
label: Some("camera_bind_group_layout"),
|
||||
label: Some("globaldata bind group layout"),
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
|
@ -79,7 +83,7 @@ impl GlobalData {
|
|||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}],
|
||||
label: Some("camera_bind_group"),
|
||||
label: Some("globaldata bind group"),
|
||||
});
|
||||
|
||||
return Self {
|
||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use bytemuck;
|
||||
use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector3};
|
||||
use galactica_constants;
|
||||
use std::{iter, rc::Rc};
|
||||
use std::{iter, rc::Rc, time::Instant};
|
||||
use wgpu;
|
||||
use winit::{self, dpi::LogicalSize, window::Window};
|
||||
|
||||
|
@ -10,13 +10,13 @@ use crate::{
|
|||
content,
|
||||
globaldata::{GlobalData, GlobalDataContent},
|
||||
pipeline::PipelineBuilder,
|
||||
sprite::ObjectSubSprite,
|
||||
sprite::{ObjectSubSprite, ParticleBuilder},
|
||||
starfield::Starfield,
|
||||
texturearray::TextureArray,
|
||||
vertexbuffer::{
|
||||
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
||||
types::{ObjectInstance, StarfieldInstance, TexturedVertex, UiInstance},
|
||||
VertexBuffer,
|
||||
types::{ObjectInstance, ParticleInstance, StarfieldInstance, TexturedVertex, UiInstance},
|
||||
BufferObject, VertexBuffer,
|
||||
},
|
||||
ObjectSprite, UiSprite, OPENGL_TO_WGPU_MATRIX,
|
||||
};
|
||||
|
@ -39,6 +39,7 @@ pub struct GPUState {
|
|||
|
||||
object_pipeline: wgpu::RenderPipeline,
|
||||
starfield_pipeline: wgpu::RenderPipeline,
|
||||
particle_pipeline: wgpu::RenderPipeline,
|
||||
ui_pipeline: wgpu::RenderPipeline,
|
||||
|
||||
starfield: Starfield,
|
||||
|
@ -51,6 +52,12 @@ struct VertexBuffers {
|
|||
object: Rc<VertexBuffer>,
|
||||
starfield: Rc<VertexBuffer>,
|
||||
ui: Rc<VertexBuffer>,
|
||||
|
||||
/// The index of the next particle slot we'll write to.
|
||||
/// This must cycle to 0 whenever it exceeds the size
|
||||
/// of the particle instance array.
|
||||
particle_array_head: u64,
|
||||
particle: Rc<VertexBuffer>,
|
||||
}
|
||||
|
||||
impl GPUState {
|
||||
|
@ -120,7 +127,7 @@ impl GPUState {
|
|||
|
||||
let vertex_buffers = VertexBuffers {
|
||||
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
|
||||
"objecte",
|
||||
"object",
|
||||
&device,
|
||||
Some(SPRITE_VERTICES),
|
||||
Some(SPRITE_INDICES),
|
||||
|
@ -142,6 +149,15 @@ impl GPUState {
|
|||
Some(SPRITE_INDICES),
|
||||
galactica_constants::UI_SPRITE_INSTANCE_LIMIT,
|
||||
)),
|
||||
|
||||
particle_array_head: 0,
|
||||
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
|
||||
"particle",
|
||||
&device,
|
||||
Some(SPRITE_VERTICES),
|
||||
Some(SPRITE_INDICES),
|
||||
galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT,
|
||||
)),
|
||||
};
|
||||
|
||||
// Load uniforms
|
||||
|
@ -191,6 +207,18 @@ impl GPUState {
|
|||
.set_bind_group_layouts(bind_group_layouts)
|
||||
.build();
|
||||
|
||||
let particle_pipeline = PipelineBuilder::new("particle", &device)
|
||||
.set_shader(include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/shaders/",
|
||||
"particle.wgsl"
|
||||
)))
|
||||
.set_format(config.format)
|
||||
.set_triangle(true)
|
||||
.set_vertex_buffer(&vertex_buffers.particle)
|
||||
.set_bind_group_layouts(bind_group_layouts)
|
||||
.build();
|
||||
|
||||
let mut starfield = Starfield::new();
|
||||
starfield.regenerate();
|
||||
|
||||
|
@ -207,6 +235,7 @@ impl GPUState {
|
|||
object_pipeline,
|
||||
starfield_pipeline,
|
||||
ui_pipeline,
|
||||
particle_pipeline,
|
||||
|
||||
starfield,
|
||||
texture_array,
|
||||
|
@ -500,8 +529,12 @@ impl GPUState {
|
|||
&mut self,
|
||||
camera_pos: Point2<f32>,
|
||||
camera_zoom: f32,
|
||||
|
||||
// TODO: clean this up, pass one struct
|
||||
object_sprites: &Vec<ObjectSprite>,
|
||||
ui_sprites: &Vec<UiSprite>,
|
||||
new_particles: &mut Vec<ParticleBuilder>,
|
||||
start_instant: Instant,
|
||||
) -> Result<(), wgpu::SurfaceError> {
|
||||
let output = self.surface.get_current_texture()?;
|
||||
let view = output
|
||||
|
@ -535,6 +568,9 @@ impl GPUState {
|
|||
timestamp_writes: None,
|
||||
});
|
||||
|
||||
// TODO: handle overflow
|
||||
let time_now = start_instant.elapsed().as_secs_f32();
|
||||
|
||||
// Update global values
|
||||
self.queue.write_buffer(
|
||||
&self.global_data.buffer,
|
||||
|
@ -554,9 +590,31 @@ impl GPUState {
|
|||
galactica_constants::STARFIELD_SIZE_MIN,
|
||||
galactica_constants::STARFIELD_SIZE_MAX,
|
||||
],
|
||||
current_time: [time_now, 0.0],
|
||||
}]),
|
||||
);
|
||||
|
||||
for i in new_particles.iter() {
|
||||
let texture = self.texture_array.get_texture(i.texture);
|
||||
self.queue.write_buffer(
|
||||
&self.vertex_buffers.particle.instances,
|
||||
ParticleInstance::SIZE * self.vertex_buffers.particle_array_head,
|
||||
bytemuck::cast_slice(&[ParticleInstance {
|
||||
position: [i.pos.x, i.pos.y, 1.0],
|
||||
size: i.size,
|
||||
texture_index: texture.index,
|
||||
expires: time_now + i.lifetime,
|
||||
}]),
|
||||
);
|
||||
self.vertex_buffers.particle_array_head += 1;
|
||||
if self.vertex_buffers.particle_array_head
|
||||
== galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT
|
||||
{
|
||||
self.vertex_buffers.particle_array_head = 0;
|
||||
}
|
||||
}
|
||||
new_particles.clear();
|
||||
|
||||
// Create sprite instances
|
||||
let (n_object, n_ui) =
|
||||
self.update_sprite_instances(camera_zoom, camera_pos, object_sprites, ui_sprites);
|
||||
|
@ -580,6 +638,15 @@ impl GPUState {
|
|||
render_pass.set_pipeline(&self.object_pipeline);
|
||||
render_pass.draw_indexed(0..SPRITE_INDICES.len() as u32, 0, 0..n_object as _);
|
||||
|
||||
// Particle pipeline
|
||||
self.vertex_buffers.particle.set_in_pass(&mut render_pass);
|
||||
render_pass.set_pipeline(&self.particle_pipeline);
|
||||
render_pass.draw_indexed(
|
||||
0..SPRITE_INDICES.len() as u32,
|
||||
0,
|
||||
0..galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT as _,
|
||||
);
|
||||
|
||||
// Ui pipeline
|
||||
self.vertex_buffers.ui.set_in_pass(&mut render_pass);
|
||||
render_pass.set_pipeline(&self.ui_pipeline);
|
||||
|
|
|
@ -17,7 +17,7 @@ mod vertexbuffer;
|
|||
|
||||
use galactica_content as content;
|
||||
pub use gpustate::GPUState;
|
||||
pub use sprite::{AnchoredUiPosition, ObjectSprite, ObjectSubSprite, UiSprite};
|
||||
pub use sprite::{AnchoredUiPosition, ObjectSprite, ObjectSubSprite, ParticleBuilder, UiSprite};
|
||||
|
||||
use cgmath::Matrix4;
|
||||
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
use crate::content;
|
||||
use cgmath::{Deg, Point2, Point3};
|
||||
|
||||
/// Instructions to create a new particle
|
||||
pub struct ParticleBuilder {
|
||||
/// The texture to use for this particle
|
||||
pub texture: content::TextureHandle,
|
||||
|
||||
// TODO: rotation, velocity
|
||||
/// This object's center, in world coordinates.
|
||||
pub pos: Point3<f32>,
|
||||
|
||||
/// This particle's lifetime, in seconds
|
||||
pub lifetime: f32,
|
||||
|
||||
/// The size of this sprite,
|
||||
/// given as height in world units.
|
||||
pub size: f32,
|
||||
}
|
||||
|
||||
/// The location of a UI element, in one of a few
|
||||
/// possible coordinate systems.
|
||||
///
|
||||
|
|
|
@ -195,3 +195,60 @@ impl BufferObject for UiInstance {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct ParticleInstance {
|
||||
/// World position of this particle
|
||||
pub position: [f32; 3],
|
||||
|
||||
// TODO: docs: particle frames must all have the same size
|
||||
// TODO: is transparency trimmed? That's not ideal for animated particles!
|
||||
/// The height of this particle, in world units
|
||||
pub size: f32,
|
||||
|
||||
// TODO: rotation, velocity vector
|
||||
// TODO: animated sprites
|
||||
// TODO: texture aspect ratio
|
||||
/// The time, in seconds, at which this particle expires.
|
||||
/// Time is kept by a variable in the global uniform.
|
||||
pub expires: f32,
|
||||
|
||||
/// What texture to use for this particle
|
||||
pub texture_index: u32,
|
||||
}
|
||||
|
||||
impl BufferObject for ParticleInstance {
|
||||
fn layout() -> wgpu::VertexBufferLayout<'static> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: Self::SIZE,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
attributes: &[
|
||||
// Position
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
// Size
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
// Expires
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
},
|
||||
// Texture
|
||||
wgpu::VertexAttribute {
|
||||
offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Uint32,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Rad, Vector2};
|
||||
use cgmath::{Deg, EuclideanSpace, InnerSpace, Matrix2, Point2, Point3, Rad, Vector2};
|
||||
use crossbeam::channel::Receiver;
|
||||
use nalgebra::vector;
|
||||
use rand::Rng;
|
||||
|
@ -12,7 +12,7 @@ use std::{collections::HashMap, f32::consts::PI};
|
|||
use crate::{objects, objects::ProjectileWorldObject, util, wrapper::Wrapper, ShipPhysicsHandle};
|
||||
use galactica_content as content;
|
||||
use galactica_gameobject as object;
|
||||
use galactica_render::ObjectSprite;
|
||||
use galactica_render::{ObjectSprite, ParticleBuilder};
|
||||
|
||||
/// Keeps track of all objects in the world that we can interact with.
|
||||
/// Also wraps our physics engine
|
||||
|
@ -92,7 +92,7 @@ impl<'a> World {
|
|||
.linvel(vector![vel.x, vel.y])
|
||||
.build();
|
||||
|
||||
let collider = ColliderBuilder::ball(5.0)
|
||||
let collider = ColliderBuilder::ball(1.0)
|
||||
.sensor(true)
|
||||
.active_events(ActiveEvents::COLLISION_EVENTS)
|
||||
.build();
|
||||
|
@ -169,7 +169,7 @@ impl<'a> World {
|
|||
}
|
||||
|
||||
/// Step this physics system by `t` seconds
|
||||
pub fn step(&mut self, t: f32, ct: &content::Content) {
|
||||
pub fn step(&mut self, t: f32, ct: &content::Content, particles: &mut Vec<ParticleBuilder>) {
|
||||
// Run ship updates
|
||||
// TODO: maybe reorganize projectile creation?
|
||||
let mut projectiles = Vec::new();
|
||||
|
@ -214,6 +214,18 @@ impl<'a> World {
|
|||
if let Some(s) = self.ships.get_mut(b) {
|
||||
let hit = s.ship.handle_projectile_collision(ct, &p.projectile);
|
||||
if hit {
|
||||
let r = self.get_rigid_body(p.rigid_body);
|
||||
let pos = util::rigidbody_position(r);
|
||||
particles.push(ParticleBuilder {
|
||||
texture: ct.get_texture_handle("particle::blaster"),
|
||||
pos: Point3 {
|
||||
x: pos.x,
|
||||
y: pos.y,
|
||||
z: 1.0, // TODO:remove z coordinate
|
||||
},
|
||||
lifetime: 0.1,
|
||||
size: 10.0,
|
||||
});
|
||||
self.remove_projectile(*a);
|
||||
}
|
||||
}
|
||||
|
|
32
src/game.rs
32
src/game.rs
|
@ -1,4 +1,4 @@
|
|||
use cgmath::Point2;
|
||||
use cgmath::{Point2, Point3};
|
||||
use content::SystemHandle;
|
||||
use std::time::Instant;
|
||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||
|
@ -7,7 +7,7 @@ use crate::camera::Camera;
|
|||
use crate::{content, inputstatus::InputStatus};
|
||||
use galactica_constants;
|
||||
use galactica_gameobject as object;
|
||||
use galactica_render::{ObjectSprite, UiSprite};
|
||||
use galactica_render::{ObjectSprite, ParticleBuilder, UiSprite};
|
||||
use galactica_shipbehavior::{behavior, ShipBehavior};
|
||||
use galactica_ui as ui;
|
||||
use galactica_world::{util, ShipPhysicsHandle, World};
|
||||
|
@ -21,10 +21,14 @@ pub struct Game {
|
|||
paused: bool,
|
||||
pub time_scale: f32,
|
||||
|
||||
physics: World,
|
||||
world: World,
|
||||
shipbehaviors: Vec<Box<dyn ShipBehavior>>,
|
||||
playerbehavior: behavior::Player,
|
||||
content: content::Content,
|
||||
pub start_instant: Instant,
|
||||
|
||||
// TODO: clean this up
|
||||
pub new_particles: Vec<ParticleBuilder>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
|
@ -85,6 +89,7 @@ impl Game {
|
|||
last_update: Instant::now(),
|
||||
input: InputStatus::new(),
|
||||
player: h1,
|
||||
start_instant: Instant::now(),
|
||||
|
||||
camera: Camera {
|
||||
pos: (0.0, 0.0).into(),
|
||||
|
@ -95,10 +100,11 @@ impl Game {
|
|||
|
||||
paused: false,
|
||||
time_scale: 1.0,
|
||||
physics,
|
||||
world: physics,
|
||||
shipbehaviors,
|
||||
content: ct,
|
||||
playerbehavior: behavior::Player::new(h1),
|
||||
new_particles: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,19 +137,19 @@ impl Game {
|
|||
self.playerbehavior.key_right = self.input.key_right;
|
||||
self.playerbehavior.key_left = self.input.key_left;
|
||||
self.playerbehavior
|
||||
.update_controls(&mut self.physics, &self.content);
|
||||
.update_controls(&mut self.world, &self.content);
|
||||
|
||||
self.shipbehaviors.retain_mut(|b| {
|
||||
// Remove shipbehaviors of destroyed ships
|
||||
if self.physics.get_ship_mut(&b.get_handle()).is_none() {
|
||||
if self.world.get_ship_mut(&b.get_handle()).is_none() {
|
||||
false
|
||||
} else {
|
||||
b.update_controls(&mut self.physics, &self.content);
|
||||
b.update_controls(&mut self.world, &self.content);
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
self.physics.step(t, &self.content);
|
||||
self.world.step(t, &self.content, &mut self.new_particles);
|
||||
|
||||
if self.input.v_scroll != 0.0 {
|
||||
self.camera.zoom = (self.camera.zoom + self.input.v_scroll)
|
||||
|
@ -153,11 +159,11 @@ impl Game {
|
|||
|
||||
// TODO: Camera physics
|
||||
let r = self
|
||||
.physics
|
||||
.world
|
||||
.get_ship_mut(&self.player)
|
||||
.unwrap()
|
||||
.physics_handle;
|
||||
let r = self.physics.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
|
||||
let r = self.world.get_rigid_body(r.0); // TODO: r.0 shouldn't be public
|
||||
let ship_pos = util::rigidbody_position(r);
|
||||
self.camera.pos = ship_pos;
|
||||
self.last_update = Instant::now();
|
||||
|
@ -167,7 +173,7 @@ impl Game {
|
|||
let mut sprites: Vec<ObjectSprite> = Vec::new();
|
||||
|
||||
sprites.append(&mut self.system.get_sprites());
|
||||
sprites.extend(self.physics.get_ship_sprites(&self.content));
|
||||
sprites.extend(self.world.get_ship_sprites(&self.content));
|
||||
|
||||
// Make sure sprites are drawn in the correct order
|
||||
// (note the reversed a, b in the comparator)
|
||||
|
@ -177,7 +183,7 @@ impl Game {
|
|||
sprites.sort_by(|a, b| b.pos.z.total_cmp(&a.pos.z));
|
||||
|
||||
// Don't waste time sorting these, they should always be on top.
|
||||
sprites.extend(self.physics.get_projectile_sprites());
|
||||
sprites.extend(self.world.get_projectile_sprites());
|
||||
|
||||
return sprites;
|
||||
}
|
||||
|
@ -186,7 +192,7 @@ impl Game {
|
|||
return ui::build_radar(
|
||||
&self.content,
|
||||
&self.player,
|
||||
&self.physics,
|
||||
&self.world,
|
||||
&self.system,
|
||||
self.camera.zoom,
|
||||
self.camera.aspect,
|
||||
|
|
|
@ -37,6 +37,11 @@ fn main() -> Result<()> {
|
|||
game.camera.zoom,
|
||||
&game.get_object_sprites(),
|
||||
&game.get_ui_sprites(),
|
||||
// TODO: clean this up, single game data struct
|
||||
// Game in another crate?
|
||||
// Shipbehavior needs game state too...
|
||||
&mut game.new_particles,
|
||||
game.start_instant,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(wgpu::SurfaceError::Lost) => gpu.resize(),
|
||||
|
|
Loading…
Reference in New Issue