Added render scenes, cleanup
parent
14bac2d378
commit
1472296766
|
@ -4,7 +4,7 @@ use anyhow::{bail, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use galactica_content::{Content, SystemHandle};
|
use galactica_content::{Content, SystemHandle};
|
||||||
use galactica_playeragent::{PlayerAgent, PlayerStatus};
|
use galactica_playeragent::{PlayerAgent, PlayerStatus};
|
||||||
use galactica_render::RenderInput;
|
use galactica_render::{RenderInput, RenderScenes};
|
||||||
use galactica_system::{
|
use galactica_system::{
|
||||||
data::ShipState,
|
data::ShipState,
|
||||||
phys::{PhysImage, PhysSimShipHandle},
|
phys::{PhysImage, PhysSimShipHandle},
|
||||||
|
@ -120,7 +120,11 @@ fn try_main() -> Result<()> {
|
||||||
|
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
let mut gpu = pollster::block_on(galactica_render::GPUState::new(window, &content))?;
|
let mut gpu = pollster::block_on(galactica_render::GPUState::new(
|
||||||
|
window,
|
||||||
|
&content,
|
||||||
|
RenderScenes::System,
|
||||||
|
))?;
|
||||||
gpu.init(&content);
|
gpu.init(&content);
|
||||||
|
|
||||||
// TODO: don't clone content
|
// TODO: don't clone content
|
||||||
|
@ -134,6 +138,7 @@ fn try_main() -> Result<()> {
|
||||||
|
|
||||||
let mut phys_img = PhysImage::new();
|
let mut phys_img = PhysImage::new();
|
||||||
let mut last_run = Instant::now();
|
let mut last_run = Instant::now();
|
||||||
|
let mut was_landed = false;
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
match event {
|
match event {
|
||||||
|
@ -174,9 +179,21 @@ fn try_main() -> Result<()> {
|
||||||
ShipState::Landing { .. }
|
ShipState::Landing { .. }
|
||||||
| ShipState::UnLanding { .. }
|
| ShipState::UnLanding { .. }
|
||||||
| ShipState::Collapsing { .. }
|
| ShipState::Collapsing { .. }
|
||||||
| ShipState::Flying { .. } => Some(*o.rigidbody.translation()),
|
| ShipState::Flying { .. } => {
|
||||||
|
if was_landed {
|
||||||
|
was_landed = false;
|
||||||
|
gpu.set_scene(&content, RenderScenes::System);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(*o.rigidbody.translation())
|
||||||
|
}
|
||||||
|
|
||||||
ShipState::Landed { target } => {
|
ShipState::Landed { target } => {
|
||||||
|
if !was_landed {
|
||||||
|
was_landed = true;
|
||||||
|
gpu.set_scene(&content, RenderScenes::Landed);
|
||||||
|
}
|
||||||
|
|
||||||
let b = content.get_system_object(*target);
|
let b = content.get_system_object(*target);
|
||||||
Some(Vector2::new(b.pos.x, b.pos.y))
|
Some(Vector2::new(b.pos.x, b.pos.y))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,48 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use bytemuck;
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
|
use log::debug;
|
||||||
|
use wgpu;
|
||||||
|
use winit;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::GlobalUniform, pipeline::PipelineBuilder, shaderprocessor::preprocess_shader,
|
globaluniform::{GlobalDataContent, GlobalUniform},
|
||||||
starfield::Starfield, texturearray::TextureArray, ui::UiManager, GPUState, RenderState,
|
pipeline::PipelineBuilder,
|
||||||
VertexBuffers,
|
renderscene::{LandedScene, RenderScene, SystemScene},
|
||||||
|
shaderprocessor::preprocess_shader,
|
||||||
|
starfield::Starfield,
|
||||||
|
texturearray::TextureArray,
|
||||||
|
ui::{
|
||||||
|
scenes::{UiFlyingScene, UiLandedScene},
|
||||||
|
UiManager, UiScenes,
|
||||||
|
},
|
||||||
|
RenderInput, RenderScenes, RenderState, VertexBuffers,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A high-level GPU wrapper. Reads game state (via RenderInput), produces pretty pictures.
|
||||||
|
pub struct GPUState {
|
||||||
|
pub(crate) device: wgpu::Device,
|
||||||
|
pub(crate) config: wgpu::SurfaceConfiguration,
|
||||||
|
pub(crate) surface: wgpu::Surface,
|
||||||
|
pub(crate) object_pipeline: wgpu::RenderPipeline,
|
||||||
|
pub(crate) starfield_pipeline: wgpu::RenderPipeline,
|
||||||
|
pub(crate) ui_pipeline: wgpu::RenderPipeline,
|
||||||
|
pub(crate) radialbar_pipeline: wgpu::RenderPipeline,
|
||||||
|
pub(crate) starfield: Starfield,
|
||||||
|
pub(crate) texture_array: TextureArray,
|
||||||
|
pub(crate) state: RenderState,
|
||||||
|
pub(crate) ui: UiManager,
|
||||||
|
pub(crate) scene: RenderScenes,
|
||||||
|
}
|
||||||
|
|
||||||
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: winit::window::Window, ct: &Content) -> Result<Self> {
|
pub async fn new(
|
||||||
|
window: winit::window::Window,
|
||||||
|
ct: &Content,
|
||||||
|
scene: RenderScenes,
|
||||||
|
) -> Result<Self> {
|
||||||
let window_size = window.inner_size();
|
let window_size = window.inner_size();
|
||||||
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
||||||
|
|
||||||
|
@ -37,18 +69,18 @@ impl GPUState {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(device, queue) = adapter
|
(device, queue) = adapter
|
||||||
.request_device(
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
// TODO: remove nonuniform sampled textures
|
// TODO: remove nonuniform sampled textures
|
||||||
features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
// We may need limits if we compile for wasm
|
// We may need limits if we compile for wasm
|
||||||
limits: wgpu::Limits::default(),
|
limits: wgpu::Limits::default(),
|
||||||
label: Some("gpu device"),
|
label: Some("gpu device"),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Assume sRGB
|
// Assume sRGB
|
||||||
let surface_caps = surface.get_capabilities(&adapter);
|
let surface_caps = surface.get_capabilities(&adapter);
|
||||||
|
@ -189,14 +221,11 @@ impl GPUState {
|
||||||
|
|
||||||
let mut state = RenderState {
|
let mut state = RenderState {
|
||||||
queue,
|
queue,
|
||||||
|
|
||||||
window,
|
window,
|
||||||
window_size,
|
window_size,
|
||||||
window_aspect,
|
window_aspect,
|
||||||
global_uniform,
|
global_uniform,
|
||||||
|
|
||||||
vertex_buffers,
|
vertex_buffers,
|
||||||
|
|
||||||
text_atlas,
|
text_atlas,
|
||||||
text_cache,
|
text_cache,
|
||||||
text_font_system,
|
text_font_system,
|
||||||
|
@ -214,8 +243,90 @@ impl GPUState {
|
||||||
starfield_pipeline,
|
starfield_pipeline,
|
||||||
ui_pipeline,
|
ui_pipeline,
|
||||||
radialbar_pipeline,
|
radialbar_pipeline,
|
||||||
|
scene,
|
||||||
state,
|
state,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GPUState {
|
||||||
|
/// Get the window we are attached to
|
||||||
|
pub fn window(&self) -> &winit::window::Window {
|
||||||
|
&self.state.window
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the current scene
|
||||||
|
pub fn set_scene(&mut self, ct: &Content, scene: RenderScenes) {
|
||||||
|
debug!("switching to {:?}", scene);
|
||||||
|
|
||||||
|
match scene {
|
||||||
|
RenderScenes::Landed => self
|
||||||
|
.ui
|
||||||
|
.set_scene(UiScenes::Landed(UiLandedScene::new(ct, &mut self.state))),
|
||||||
|
RenderScenes::System => self
|
||||||
|
.ui
|
||||||
|
.set_scene(UiScenes::Flying(UiFlyingScene::new(ct, &mut self.state))),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update window size.
|
||||||
|
/// This should be called whenever our window is resized.
|
||||||
|
pub fn resize(&mut self, ct: &Content) {
|
||||||
|
let new_size = self.state.window.inner_size();
|
||||||
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
|
self.state.window_size = new_size;
|
||||||
|
self.state.window_aspect = new_size.width as f32 / new_size.height as f32;
|
||||||
|
self.config.width = new_size.width;
|
||||||
|
self.config.height = new_size.height;
|
||||||
|
self.surface.configure(&self.device, &self.config);
|
||||||
|
}
|
||||||
|
self.starfield.update_buffer(ct, &mut self.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the rendering engine
|
||||||
|
pub fn init(&mut self, ct: &Content) {
|
||||||
|
// Update global values
|
||||||
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.global_uniform.atlas_buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&[self.texture_array.texture_atlas]),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.starfield.update_buffer(ct, &mut self.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Main render function. Draws sprites on a window.
|
||||||
|
pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> {
|
||||||
|
// Update global values
|
||||||
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.global_uniform.data_buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&[GlobalDataContent {
|
||||||
|
camera_position_x: input.camera_pos.x,
|
||||||
|
camera_position_y: input.camera_pos.y,
|
||||||
|
camera_zoom: input.camera_zoom,
|
||||||
|
camera_zoom_min: input.ct.get_config().zoom_min,
|
||||||
|
camera_zoom_max: input.ct.get_config().zoom_max,
|
||||||
|
window_size_w: self.state.window_size.width as f32,
|
||||||
|
window_size_h: self.state.window_size.height as f32,
|
||||||
|
window_scale: self.state.window.scale_factor() as f32,
|
||||||
|
window_aspect: self.state.window_aspect,
|
||||||
|
starfield_sprite: input.ct.get_config().starfield_texture.into(),
|
||||||
|
starfield_tile_size: input.ct.get_config().starfield_size,
|
||||||
|
starfield_size_min: input.ct.get_config().starfield_min_size,
|
||||||
|
starfield_size_max: input.ct.get_config().starfield_max_size,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.state.frame_reset();
|
||||||
|
|
||||||
|
match self.scene {
|
||||||
|
RenderScenes::System => SystemScene::render(self, &input).unwrap(),
|
||||||
|
RenderScenes::Landed => LandedScene::render(self, &input).unwrap(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,64 +0,0 @@
|
||||||
use bytemuck;
|
|
||||||
use galactica_content::Content;
|
|
||||||
use wgpu;
|
|
||||||
use winit;
|
|
||||||
|
|
||||||
use crate::{starfield::Starfield, texturearray::TextureArray, ui::UiManager, RenderState};
|
|
||||||
|
|
||||||
/// GPUState is very big, so its methods have been split
|
|
||||||
/// among the following files.
|
|
||||||
mod new;
|
|
||||||
mod phys;
|
|
||||||
mod render;
|
|
||||||
|
|
||||||
/// A high-level GPU wrapper. Reads game state (via RenderInput),
|
|
||||||
/// produces pretty pictures.
|
|
||||||
pub struct GPUState {
|
|
||||||
device: wgpu::Device,
|
|
||||||
config: wgpu::SurfaceConfiguration,
|
|
||||||
surface: wgpu::Surface,
|
|
||||||
|
|
||||||
object_pipeline: wgpu::RenderPipeline,
|
|
||||||
starfield_pipeline: wgpu::RenderPipeline,
|
|
||||||
ui_pipeline: wgpu::RenderPipeline,
|
|
||||||
radialbar_pipeline: wgpu::RenderPipeline,
|
|
||||||
|
|
||||||
starfield: Starfield,
|
|
||||||
texture_array: TextureArray,
|
|
||||||
|
|
||||||
state: RenderState,
|
|
||||||
ui: UiManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GPUState {
|
|
||||||
/// Get the window we are attached to
|
|
||||||
pub fn window(&self) -> &winit::window::Window {
|
|
||||||
&self.state.window
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update window size.
|
|
||||||
/// This should be called whenever our window is resized.
|
|
||||||
pub fn resize(&mut self, ct: &Content) {
|
|
||||||
let new_size = self.state.window.inner_size();
|
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
|
||||||
self.state.window_size = new_size;
|
|
||||||
self.state.window_aspect = new_size.width as f32 / new_size.height as f32;
|
|
||||||
self.config.width = new_size.width;
|
|
||||||
self.config.height = new_size.height;
|
|
||||||
self.surface.configure(&self.device, &self.config);
|
|
||||||
}
|
|
||||||
self.starfield.update_buffer(ct, &mut self.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the rendering engine
|
|
||||||
pub fn init(&mut self, ct: &Content) {
|
|
||||||
// Update global values
|
|
||||||
self.state.queue.write_buffer(
|
|
||||||
&self.state.global_uniform.atlas_buffer,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&[self.texture_array.texture_atlas]),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.starfield.update_buffer(ct, &mut self.state);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,303 +0,0 @@
|
||||||
use anyhow::Result;
|
|
||||||
use bytemuck;
|
|
||||||
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
|
||||||
use glyphon::Resolution;
|
|
||||||
use nalgebra::Point2;
|
|
||||||
use std::iter;
|
|
||||||
use wgpu;
|
|
||||||
|
|
||||||
use crate::{globaluniform::GlobalDataContent, vertexbuffer::consts::SPRITE_INDICES, RenderInput};
|
|
||||||
|
|
||||||
impl<'a> super::GPUState {
|
|
||||||
/// Render routines while player is flying
|
|
||||||
fn render_flying(&'a mut self, input: &RenderInput) -> Result<()> {
|
|
||||||
let output = self.surface.get_current_texture()?;
|
|
||||||
let view = output.texture.create_view(&Default::default());
|
|
||||||
|
|
||||||
let mut encoder = self
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("render encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("render pass"),
|
|
||||||
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
||||||
r: 0.0,
|
|
||||||
g: 0.0,
|
|
||||||
b: 0.0,
|
|
||||||
a: 1.0,
|
|
||||||
}),
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
occlusion_query_set: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create sprite instances
|
|
||||||
|
|
||||||
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
|
||||||
// Used to skip off-screen sprites.
|
|
||||||
let clip_ne = Point2::new(-self.state.window_aspect, 1.0) * input.camera_zoom;
|
|
||||||
let clip_sw = Point2::new(self.state.window_aspect, -1.0) * input.camera_zoom;
|
|
||||||
|
|
||||||
// Order matters, it determines what is drawn on top.
|
|
||||||
// The order inside ships and projectiles doesn't matter,
|
|
||||||
// but ships should always be under projectiles.
|
|
||||||
self.phys_push_system(&input, (clip_ne, clip_sw));
|
|
||||||
self.phys_push_ships(&input, (clip_ne, clip_sw));
|
|
||||||
self.phys_push_projectiles(&input, (clip_ne, clip_sw));
|
|
||||||
self.phys_push_effects(&input, (clip_ne, clip_sw));
|
|
||||||
self.ui.draw(&input, &mut self.state);
|
|
||||||
|
|
||||||
// These should match the indices in each shader,
|
|
||||||
// and should each have a corresponding bind group layout.
|
|
||||||
render_pass.set_bind_group(0, &self.texture_array.bind_group, &[]);
|
|
||||||
render_pass.set_bind_group(1, &self.state.global_uniform.bind_group, &[]);
|
|
||||||
|
|
||||||
// Starfield pipeline
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_starfield()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.starfield_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_starfield_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sprite pipeline
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_object()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.object_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_object_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ui pipeline
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_ui()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.ui_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_ui_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Radial progress bars
|
|
||||||
// TODO: do we need to do this every time?
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_radialbar()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.radialbar_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_radialbar_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let textareas = self.ui.get_textareas(input, &self.state);
|
|
||||||
self.state
|
|
||||||
.text_renderer
|
|
||||||
.prepare(
|
|
||||||
&self.device,
|
|
||||||
&self.state.queue,
|
|
||||||
&mut self.state.text_font_system,
|
|
||||||
&mut self.state.text_atlas,
|
|
||||||
Resolution {
|
|
||||||
width: self.state.window_size.width,
|
|
||||||
height: self.state.window_size.height,
|
|
||||||
},
|
|
||||||
textareas,
|
|
||||||
&mut self.state.text_cache,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.state
|
|
||||||
.text_renderer
|
|
||||||
.render(&self.state.text_atlas, &mut render_pass)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// begin_render_pass borrows encoder mutably,
|
|
||||||
// so we need to drop it before calling finish.
|
|
||||||
drop(render_pass);
|
|
||||||
|
|
||||||
self.state.queue.submit(iter::once(encoder.finish()));
|
|
||||||
output.present();
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render routines while player is landed
|
|
||||||
fn render_landed(&'a mut self, input: &RenderInput) -> Result<()> {
|
|
||||||
let output = self.surface.get_current_texture()?;
|
|
||||||
let view = output.texture.create_view(&Default::default());
|
|
||||||
|
|
||||||
let mut encoder = self
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("render encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("render pass"),
|
|
||||||
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
||||||
r: 0.0,
|
|
||||||
g: 0.0,
|
|
||||||
b: 0.0,
|
|
||||||
a: 1.0,
|
|
||||||
}),
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
occlusion_query_set: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create sprite instances
|
|
||||||
self.ui.draw(&input, &mut self.state);
|
|
||||||
|
|
||||||
// These should match the indices in each shader,
|
|
||||||
// and should each have a corresponding bind group layout.
|
|
||||||
render_pass.set_bind_group(0, &self.texture_array.bind_group, &[]);
|
|
||||||
render_pass.set_bind_group(1, &self.state.global_uniform.bind_group, &[]);
|
|
||||||
|
|
||||||
// Starfield pipeline
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_starfield()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.starfield_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_starfield_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ui pipeline
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_ui()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.ui_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_ui_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Radial progress bars
|
|
||||||
self.state
|
|
||||||
.vertex_buffers
|
|
||||||
.get_radialbar()
|
|
||||||
.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.radialbar_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.state.get_radialbar_counter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let textareas = self.ui.get_textareas(input, &self.state);
|
|
||||||
self.state
|
|
||||||
.text_renderer
|
|
||||||
.prepare(
|
|
||||||
&self.device,
|
|
||||||
&self.state.queue,
|
|
||||||
&mut self.state.text_font_system,
|
|
||||||
&mut self.state.text_atlas,
|
|
||||||
Resolution {
|
|
||||||
width: self.state.window_size.width,
|
|
||||||
height: self.state.window_size.height,
|
|
||||||
},
|
|
||||||
textareas,
|
|
||||||
&mut self.state.text_cache,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.state
|
|
||||||
.text_renderer
|
|
||||||
.render(&self.state.text_atlas, &mut render_pass)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// begin_render_pass borrows encoder mutably,
|
|
||||||
// so we need to drop it before calling finish.
|
|
||||||
drop(render_pass);
|
|
||||||
|
|
||||||
self.state.queue.submit(iter::once(encoder.finish()));
|
|
||||||
output.present();
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Main render function. Draws sprites on a window.
|
|
||||||
pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> {
|
|
||||||
// Update global values
|
|
||||||
self.state.queue.write_buffer(
|
|
||||||
&self.state.global_uniform.data_buffer,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&[GlobalDataContent {
|
|
||||||
camera_position_x: input.camera_pos.x,
|
|
||||||
camera_position_y: input.camera_pos.y,
|
|
||||||
camera_zoom: input.camera_zoom,
|
|
||||||
camera_zoom_min: input.ct.get_config().zoom_min,
|
|
||||||
camera_zoom_max: input.ct.get_config().zoom_max,
|
|
||||||
window_size_w: self.state.window_size.width as f32,
|
|
||||||
window_size_h: self.state.window_size.height as f32,
|
|
||||||
window_scale: self.state.window.scale_factor() as f32,
|
|
||||||
window_aspect: self.state.window_aspect,
|
|
||||||
starfield_sprite: input.ct.get_config().starfield_texture.into(),
|
|
||||||
starfield_tile_size: input.ct.get_config().starfield_size,
|
|
||||||
starfield_size_min: input.ct.get_config().starfield_min_size,
|
|
||||||
starfield_size_max: input.ct.get_config().starfield_max_size,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.state.frame_reset();
|
|
||||||
self.ui.update_state(&input, &mut self.state);
|
|
||||||
|
|
||||||
match input
|
|
||||||
.phys_img
|
|
||||||
.get_ship(&PhysSimShipHandle(input.player.ship.unwrap()))
|
|
||||||
.unwrap()
|
|
||||||
.ship
|
|
||||||
.get_data()
|
|
||||||
.get_state()
|
|
||||||
{
|
|
||||||
ShipState::Collapsing
|
|
||||||
| ShipState::Dead
|
|
||||||
| ShipState::Landing { .. }
|
|
||||||
| ShipState::UnLanding { .. }
|
|
||||||
| ShipState::Flying { .. } => {
|
|
||||||
self.render_flying(&input).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
ShipState::Landed { .. } => {
|
|
||||||
self.render_landed(&input).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ mod gpustate;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod positionanchor;
|
mod positionanchor;
|
||||||
mod renderinput;
|
mod renderinput;
|
||||||
|
mod renderscene;
|
||||||
mod renderstate;
|
mod renderstate;
|
||||||
mod shaderprocessor;
|
mod shaderprocessor;
|
||||||
mod starfield;
|
mod starfield;
|
||||||
|
@ -19,11 +20,11 @@ mod texturearray;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod vertexbuffer;
|
mod vertexbuffer;
|
||||||
|
|
||||||
use renderstate::*;
|
|
||||||
|
|
||||||
pub use gpustate::GPUState;
|
pub use gpustate::GPUState;
|
||||||
pub use positionanchor::PositionAnchor;
|
pub use positionanchor::PositionAnchor;
|
||||||
pub use renderinput::RenderInput;
|
pub use renderinput::RenderInput;
|
||||||
|
pub use renderscene::RenderScenes;
|
||||||
|
use renderstate::*;
|
||||||
|
|
||||||
use nalgebra::Matrix4;
|
use nalgebra::Matrix4;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use glyphon::Resolution;
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use super::RenderScene;
|
||||||
|
use crate::vertexbuffer::consts::SPRITE_INDICES;
|
||||||
|
|
||||||
|
pub struct LandedScene {}
|
||||||
|
|
||||||
|
impl RenderScene for LandedScene {
|
||||||
|
fn render(g: &mut crate::GPUState, input: &crate::RenderInput) -> Result<()> {
|
||||||
|
let output = g.surface.get_current_texture()?;
|
||||||
|
let view = output.texture.create_view(&Default::default());
|
||||||
|
|
||||||
|
let mut encoder = g
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("render encoder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("render pass"),
|
||||||
|
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create sprite instances
|
||||||
|
g.ui.draw(&input, &mut g.state);
|
||||||
|
|
||||||
|
// These should match the indices in each shader,
|
||||||
|
// and should each have a corresponding bind group layout.
|
||||||
|
render_pass.set_bind_group(0, &g.texture_array.bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(1, &g.state.global_uniform.bind_group, &[]);
|
||||||
|
|
||||||
|
// Starfield pipeline
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_starfield()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.starfield_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_starfield_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ui pipeline
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_ui()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.ui_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_ui_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Radial progress bars
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_radialbar()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.radialbar_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_radialbar_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let textareas = g.ui.get_textareas(input, &g.state);
|
||||||
|
g.state
|
||||||
|
.text_renderer
|
||||||
|
.prepare(
|
||||||
|
&g.device,
|
||||||
|
&g.state.queue,
|
||||||
|
&mut g.state.text_font_system,
|
||||||
|
&mut g.state.text_atlas,
|
||||||
|
Resolution {
|
||||||
|
width: g.state.window_size.width,
|
||||||
|
height: g.state.window_size.height,
|
||||||
|
},
|
||||||
|
textareas,
|
||||||
|
&mut g.state.text_cache,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
g.state
|
||||||
|
.text_renderer
|
||||||
|
.render(&g.state.text_atlas, &mut render_pass)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// begin_render_pass borrows encoder mutably,
|
||||||
|
// so we need to drop it before calling finish.
|
||||||
|
drop(render_pass);
|
||||||
|
|
||||||
|
g.state.queue.submit(iter::once(encoder.finish()));
|
||||||
|
output.present();
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
mod landed;
|
||||||
|
mod system;
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
pub use landed::LandedScene;
|
||||||
|
pub use system::SystemScene;
|
||||||
|
|
||||||
|
use crate::{GPUState, RenderInput};
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub trait RenderScene {
|
||||||
|
fn render(g: &mut GPUState, input: &RenderInput) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What render routine to run
|
||||||
|
pub enum RenderScenes {
|
||||||
|
/// Draw the system we're in
|
||||||
|
System,
|
||||||
|
|
||||||
|
/// Draw the landed UI
|
||||||
|
Landed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for RenderScenes {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Landed => write!(f, "RenderScenes::Landed"),
|
||||||
|
Self::System => write!(f, "RenderScenes::System"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
mod phys;
|
||||||
|
mod system;
|
||||||
|
|
||||||
|
pub use system::SystemScene;
|
|
@ -1,5 +1,3 @@
|
||||||
//! GPUState routines for drawing objects in a system
|
|
||||||
|
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use galactica_system::data::ShipState;
|
use galactica_system::data::ShipState;
|
||||||
use galactica_util::to_radians;
|
use galactica_util::to_radians;
|
||||||
|
@ -9,9 +7,11 @@ use crate::{
|
||||||
globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderInput,
|
globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl GPUState {
|
use super::SystemScene;
|
||||||
pub(super) fn phys_push_ships(
|
|
||||||
&mut self,
|
impl SystemScene {
|
||||||
|
pub(super) fn push_ships(
|
||||||
|
g: &mut GPUState,
|
||||||
input: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
|
@ -65,10 +65,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = g.state.get_object_counter();
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
g.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&g.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: ship_pos.x,
|
xpos: ship_pos.x,
|
||||||
|
@ -84,7 +84,7 @@ impl GPUState {
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
let anim_state = s.ship.get_anim_state();
|
let anim_state = s.ship.get_anim_state();
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
g.state.push_object_buffer(ObjectInstance {
|
||||||
texture_index: anim_state.texture_index(),
|
texture_index: anim_state.texture_index(),
|
||||||
texture_fade: anim_state.fade,
|
texture_fade: anim_state.fade,
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
|
@ -101,9 +101,9 @@ impl GPUState {
|
||||||
is_flying
|
is_flying
|
||||||
} {
|
} {
|
||||||
for (engine_point, anim) in s.ship.iter_engine_anim() {
|
for (engine_point, anim) in s.ship.iter_engine_anim() {
|
||||||
self.state.queue.write_buffer(
|
g.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&g.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * self.state.get_object_counter() as u64,
|
ObjectData::SIZE * g.state.get_object_counter() as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
// Note that we adjust the y-coordinate for half-height,
|
// Note that we adjust the y-coordinate for half-height,
|
||||||
// not the x-coordinate, even though our ships point east
|
// not the x-coordinate, even though our ships point east
|
||||||
|
@ -124,10 +124,10 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
let anim_state = anim.get_texture_idx();
|
let anim_state = anim.get_texture_idx();
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
g.state.push_object_buffer(ObjectInstance {
|
||||||
texture_index: anim_state.texture_index(),
|
texture_index: anim_state.texture_index(),
|
||||||
texture_fade: anim_state.fade,
|
texture_fade: anim_state.fade,
|
||||||
object_index: self.state.get_object_counter() as u32,
|
object_index: g.state.get_object_counter() as u32,
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -135,8 +135,8 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn phys_push_projectiles(
|
pub(super) fn push_projectiles(
|
||||||
&mut self,
|
g: &mut GPUState,
|
||||||
input: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
|
@ -169,10 +169,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = g.state.get_object_counter();
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
g.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&g.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: proj_pos.x,
|
xpos: proj_pos.x,
|
||||||
|
@ -187,7 +187,7 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
let anim_state = p.projectile.get_anim_state();
|
let anim_state = p.projectile.get_anim_state();
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
g.state.push_object_buffer(ObjectInstance {
|
||||||
texture_index: anim_state.texture_index(),
|
texture_index: anim_state.texture_index(),
|
||||||
texture_fade: anim_state.fade,
|
texture_fade: anim_state.fade,
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
|
@ -196,8 +196,8 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn phys_push_system(
|
pub(super) fn push_system(
|
||||||
&mut self,
|
g: &mut GPUState,
|
||||||
input: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
|
@ -224,10 +224,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = g.state.get_object_counter();
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
g.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&g.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: o.pos.x,
|
xpos: o.pos.x,
|
||||||
|
@ -245,7 +245,7 @@ impl GPUState {
|
||||||
let texture_a = sprite.get_first_frame(); // ANIMATE
|
let texture_a = sprite.get_first_frame(); // ANIMATE
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
g.state.push_object_buffer(ObjectInstance {
|
||||||
texture_index: [texture_a, texture_a],
|
texture_index: [texture_a, texture_a],
|
||||||
texture_fade: 1.0,
|
texture_fade: 1.0,
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
|
@ -254,8 +254,8 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn phys_push_effects(
|
pub(super) fn push_effects(
|
||||||
&mut self,
|
g: &mut GPUState,
|
||||||
input: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
|
@ -292,10 +292,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.state.get_object_counter();
|
let idx = g.state.get_object_counter();
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.state.queue.write_buffer(
|
g.state.queue.write_buffer(
|
||||||
&self.state.global_uniform.object_buffer,
|
&g.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: pos.x,
|
xpos: pos.x,
|
||||||
|
@ -310,7 +310,7 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
let anim_state = p.effect.anim.get_texture_idx();
|
let anim_state = p.effect.anim.get_texture_idx();
|
||||||
self.state.push_object_buffer(ObjectInstance {
|
g.state.push_object_buffer(ObjectInstance {
|
||||||
texture_index: anim_state.texture_index(),
|
texture_index: anim_state.texture_index(),
|
||||||
texture_fade: anim_state.fade,
|
texture_fade: anim_state.fade,
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
|
@ -0,0 +1,144 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use glyphon::Resolution;
|
||||||
|
use nalgebra::Point2;
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use super::super::RenderScene;
|
||||||
|
use crate::vertexbuffer::consts::SPRITE_INDICES;
|
||||||
|
|
||||||
|
pub struct SystemScene {}
|
||||||
|
|
||||||
|
impl RenderScene for SystemScene {
|
||||||
|
fn render(g: &mut crate::GPUState, input: &crate::RenderInput) -> Result<()> {
|
||||||
|
let output = g.surface.get_current_texture()?;
|
||||||
|
let view = output.texture.create_view(&Default::default());
|
||||||
|
|
||||||
|
let mut encoder = g
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("render encoder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("render pass"),
|
||||||
|
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create sprite instances
|
||||||
|
|
||||||
|
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
||||||
|
// Used to skip off-screen sprites.
|
||||||
|
let clip_ne = Point2::new(-g.state.window_aspect, 1.0) * input.camera_zoom;
|
||||||
|
let clip_sw = Point2::new(g.state.window_aspect, -1.0) * input.camera_zoom;
|
||||||
|
|
||||||
|
// Order matters, it determines what is drawn on top.
|
||||||
|
// The order inside ships and projectiles doesn't matter,
|
||||||
|
// but ships should always be under projectiles.
|
||||||
|
Self::push_system(g, &input, (clip_ne, clip_sw));
|
||||||
|
Self::push_ships(g, &input, (clip_ne, clip_sw));
|
||||||
|
Self::push_projectiles(g, &input, (clip_ne, clip_sw));
|
||||||
|
Self::push_effects(g, &input, (clip_ne, clip_sw));
|
||||||
|
g.ui.draw(&input, &mut g.state);
|
||||||
|
|
||||||
|
// These should match the indices in each shader,
|
||||||
|
// and should each have a corresponding bind group layout.
|
||||||
|
render_pass.set_bind_group(0, &g.texture_array.bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(1, &g.state.global_uniform.bind_group, &[]);
|
||||||
|
|
||||||
|
// Starfield pipeline
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_starfield()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.starfield_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_starfield_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sprite pipeline
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_object()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.object_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_object_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ui pipeline
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_ui()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.ui_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_ui_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Radial progress bars
|
||||||
|
// TODO: do we need to do this every time?
|
||||||
|
g.state
|
||||||
|
.vertex_buffers
|
||||||
|
.get_radialbar()
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&g.radialbar_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..g.state.get_radialbar_counter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let textareas = g.ui.get_textareas(input, &g.state);
|
||||||
|
g.state
|
||||||
|
.text_renderer
|
||||||
|
.prepare(
|
||||||
|
&g.device,
|
||||||
|
&g.state.queue,
|
||||||
|
&mut g.state.text_font_system,
|
||||||
|
&mut g.state.text_atlas,
|
||||||
|
Resolution {
|
||||||
|
width: g.state.window_size.width,
|
||||||
|
height: g.state.window_size.height,
|
||||||
|
},
|
||||||
|
textareas,
|
||||||
|
&mut g.state.text_cache,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
g.state
|
||||||
|
.text_renderer
|
||||||
|
.render(&g.state.text_atlas, &mut render_pass)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// begin_render_pass borrows encoder mutably,
|
||||||
|
// so we need to drop it before calling finish.
|
||||||
|
drop(render_pass);
|
||||||
|
|
||||||
|
g.state.queue.submit(iter::once(encoder.finish()));
|
||||||
|
output.present();
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use galactica_system::{data::ShipState, phys::PhysSimShipHandle};
|
|
||||||
use glyphon::TextArea;
|
use glyphon::TextArea;
|
||||||
use log::info;
|
use log::debug;
|
||||||
|
|
||||||
use super::scenes::{FlyingScene, LandedScene, OutfitterScene};
|
use super::scenes::{UiFlyingScene, UiLandedScene, UiOutfitterScene};
|
||||||
use crate::{RenderInput, RenderState};
|
use crate::{RenderInput, RenderState};
|
||||||
|
|
||||||
/// Output from a ui scene step
|
/// Output from a ui scene step
|
||||||
|
@ -25,6 +24,7 @@ where
|
||||||
/// Handles clicks, keys, etc.
|
/// Handles clicks, keys, etc.
|
||||||
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult;
|
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult;
|
||||||
|
|
||||||
|
/// Add all textareas in this scene to `v`
|
||||||
fn get_textareas(
|
fn get_textareas(
|
||||||
&'this self,
|
&'this self,
|
||||||
v: &mut Vec<TextArea<'this>>,
|
v: &mut Vec<TextArea<'this>>,
|
||||||
|
@ -33,10 +33,10 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) enum UiScenes {
|
pub(crate) enum UiScenes {
|
||||||
Landed(LandedScene),
|
Landed(UiLandedScene),
|
||||||
Flying(FlyingScene),
|
Flying(UiFlyingScene),
|
||||||
Outfitter(OutfitterScene),
|
Outfitter(UiOutfitterScene),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for UiScenes {
|
impl Debug for UiScenes {
|
||||||
|
@ -49,6 +49,7 @@ impl Debug for UiScenes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
impl UiScenes {
|
impl UiScenes {
|
||||||
fn is_flying(&self) -> bool {
|
fn is_flying(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -71,6 +72,7 @@ impl UiScenes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
impl<'a> UiScene<'a> for UiScenes {
|
impl<'a> UiScene<'a> for UiScenes {
|
||||||
fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
|
@ -110,45 +112,23 @@ pub struct UiManager {
|
||||||
impl UiManager {
|
impl UiManager {
|
||||||
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
current_scene: UiScenes::Flying(FlyingScene::new(ct, state)),
|
current_scene: UiScenes::Flying(UiFlyingScene::new(ct, state)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this.
|
/// Change the current scene
|
||||||
pub fn update_state(&mut self, input: &RenderInput, state: &mut RenderState) {
|
pub fn set_scene(&mut self, scene: UiScenes) {
|
||||||
let ship_handle = input.player.ship.unwrap();
|
debug!("switching to {:?}", scene);
|
||||||
let ship = &input
|
self.current_scene = scene;
|
||||||
.phys_img
|
|
||||||
.get_ship(&PhysSimShipHandle(ship_handle))
|
|
||||||
.unwrap()
|
|
||||||
.ship;
|
|
||||||
|
|
||||||
match ship.get_data().get_state() {
|
|
||||||
ShipState::Collapsing
|
|
||||||
| ShipState::Dead
|
|
||||||
| ShipState::Flying { .. }
|
|
||||||
| ShipState::Landing { .. }
|
|
||||||
| ShipState::UnLanding { .. } => {
|
|
||||||
if !self.current_scene.is_flying() {
|
|
||||||
self.current_scene = UiScenes::Flying(FlyingScene::new(input.ct, state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShipState::Landed { .. } => {
|
|
||||||
if !self.current_scene.is_landed() && !self.current_scene.is_outfitter() {
|
|
||||||
self.current_scene = UiScenes::Landed(LandedScene::new(input.ct, state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw all ui elements
|
/// Draw all ui elements
|
||||||
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
loop {
|
loop {
|
||||||
let r = self.current_scene.step(input, state);
|
let r = self.current_scene.step(input, state);
|
||||||
if let Some(new_state) = r.new_scene {
|
if let Some(new_scene) = r.new_scene {
|
||||||
info!("switching to {:?}", new_state);
|
debug!("{:?} changed scene", self.current_scene);
|
||||||
self.current_scene = new_state;
|
self.set_scene(new_scene)
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod manager;
|
mod manager;
|
||||||
mod scenes;
|
pub(crate) mod scenes;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub use manager::UiManager;
|
pub use manager::UiManager;
|
||||||
|
pub(crate) use manager::UiScenes;
|
||||||
|
|
|
@ -3,4 +3,4 @@ mod radar;
|
||||||
mod scene;
|
mod scene;
|
||||||
mod status;
|
mod status;
|
||||||
|
|
||||||
pub use scene::FlyingScene;
|
pub use scene::UiFlyingScene;
|
||||||
|
|
|
@ -7,13 +7,13 @@ use crate::{
|
||||||
RenderInput, RenderState,
|
RenderInput, RenderState,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct FlyingScene {
|
pub struct UiFlyingScene {
|
||||||
radar: Radar,
|
radar: Radar,
|
||||||
status: Status,
|
status: Status,
|
||||||
fps: FpsIndicator,
|
fps: FpsIndicator,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlyingScene {
|
impl UiFlyingScene {
|
||||||
pub fn new(_ct: &Content, state: &mut RenderState) -> Self {
|
pub fn new(_ct: &Content, state: &mut RenderState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
radar: Radar::new(),
|
radar: Radar::new(),
|
||||||
|
@ -23,7 +23,7 @@ impl FlyingScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'this> UiScene<'this> for FlyingScene {
|
impl<'this> UiScene<'this> for UiFlyingScene {
|
||||||
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
||||||
self.fps.update(input, state);
|
self.fps.update(input, state);
|
||||||
return UiSceneStepResult { new_scene: None };
|
return UiSceneStepResult { new_scene: None };
|
||||||
|
|
|
@ -11,9 +11,9 @@ use crate::{
|
||||||
RenderInput, RenderState,
|
RenderInput, RenderState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::OutfitterScene;
|
use super::UiOutfitterScene;
|
||||||
|
|
||||||
pub struct LandedScene {
|
pub struct UiLandedScene {
|
||||||
// UI elements
|
// UI elements
|
||||||
description: UiTextArea,
|
description: UiTextArea,
|
||||||
title: UiTextArea,
|
title: UiTextArea,
|
||||||
|
@ -30,7 +30,7 @@ pub struct LandedScene {
|
||||||
leftclick_down: bool,
|
leftclick_down: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LandedScene {
|
impl UiLandedScene {
|
||||||
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
||||||
let frame = UiSprite::from(ct, &ct.get_ui().landed_frame);
|
let frame = UiSprite::from(ct, &ct.get_ui().landed_frame);
|
||||||
let button = UiSprite::from(ct, &ct.get_ui().landed_button);
|
let button = UiSprite::from(ct, &ct.get_ui().landed_button);
|
||||||
|
@ -103,7 +103,7 @@ impl LandedScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'this> UiScene<'this> for LandedScene {
|
impl<'this> UiScene<'this> for UiLandedScene {
|
||||||
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
||||||
let frame_rect = Some(self.frame.get_rect(input));
|
let frame_rect = Some(self.frame.get_rect(input));
|
||||||
self.button.step(input, state, frame_rect);
|
self.button.step(input, state, frame_rect);
|
||||||
|
@ -114,7 +114,7 @@ impl<'this> UiScene<'this> for LandedScene {
|
||||||
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
||||||
self.leftclick_down = true;
|
self.leftclick_down = true;
|
||||||
if self.button.contains_mouse(input, state, frame_rect) {
|
if self.button.contains_mouse(input, state, frame_rect) {
|
||||||
new_scene = Some(UiScenes::Outfitter(OutfitterScene::new(input.ct, state)));
|
new_scene = Some(UiScenes::Outfitter(UiOutfitterScene::new(input.ct, state)));
|
||||||
}
|
}
|
||||||
} else if !input.player.input.pressed_leftclick() {
|
} else if !input.player.input.pressed_leftclick() {
|
||||||
self.leftclick_down = false;
|
self.leftclick_down = false;
|
||||||
|
|
|
@ -2,6 +2,6 @@ mod flying;
|
||||||
mod landed;
|
mod landed;
|
||||||
mod outfitter;
|
mod outfitter;
|
||||||
|
|
||||||
pub use flying::FlyingScene;
|
pub use flying::UiFlyingScene;
|
||||||
pub use landed::LandedScene;
|
pub use landed::UiLandedScene;
|
||||||
pub use outfitter::OutfitterScene;
|
pub use outfitter::UiOutfitterScene;
|
||||||
|
|
|
@ -11,9 +11,9 @@ use crate::{
|
||||||
RenderInput, RenderState,
|
RenderInput, RenderState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::LandedScene;
|
use super::UiLandedScene;
|
||||||
|
|
||||||
pub struct OutfitterScene {
|
pub struct UiOutfitterScene {
|
||||||
// UI elements
|
// UI elements
|
||||||
description: UiTextArea,
|
description: UiTextArea,
|
||||||
landscape: UiSprite,
|
landscape: UiSprite,
|
||||||
|
@ -28,7 +28,7 @@ pub struct OutfitterScene {
|
||||||
leftclick_down: bool,
|
leftclick_down: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutfitterScene {
|
impl UiOutfitterScene {
|
||||||
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
pub fn new(ct: &Content, state: &mut RenderState) -> Self {
|
||||||
let button = UiSprite::new(
|
let button = UiSprite::new(
|
||||||
ct,
|
ct,
|
||||||
|
@ -88,7 +88,7 @@ impl OutfitterScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'this> UiScene<'this> for OutfitterScene {
|
impl<'this> UiScene<'this> for UiOutfitterScene {
|
||||||
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
fn step(&mut self, input: &RenderInput, state: &mut RenderState) -> UiSceneStepResult {
|
||||||
self.button.step(input, state, None);
|
self.button.step(input, state, None);
|
||||||
self.landscape.step(input, state, None);
|
self.landscape.step(input, state, None);
|
||||||
|
@ -97,7 +97,7 @@ impl<'this> UiScene<'this> for OutfitterScene {
|
||||||
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
if input.player.input.pressed_leftclick() && !self.leftclick_down {
|
||||||
self.leftclick_down = true;
|
self.leftclick_down = true;
|
||||||
if self.button.contains_mouse(input, state, None) {
|
if self.button.contains_mouse(input, state, None) {
|
||||||
new_scene = Some(UiScenes::Landed(LandedScene::new(input.ct, state)));
|
new_scene = Some(UiScenes::Landed(UiLandedScene::new(input.ct, state)));
|
||||||
}
|
}
|
||||||
} else if !input.player.input.pressed_leftclick() {
|
} else if !input.player.input.pressed_leftclick() {
|
||||||
self.leftclick_down = false;
|
self.leftclick_down = false;
|
||||||
|
|
Loading…
Reference in New Issue