2024-01-10 17:53:27 -08:00
|
|
|
use anyhow::Result;
|
|
|
|
use galactica_content::Content;
|
2024-01-10 18:53:19 -08:00
|
|
|
use galactica_util::constants::{
|
2024-01-10 22:44:22 -08:00
|
|
|
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT,
|
2024-01-10 17:53:27 -08:00
|
|
|
};
|
2024-01-10 18:53:19 -08:00
|
|
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
2024-01-10 17:53:27 -08:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
datastructs::{RenderState, VertexBuffers},
|
|
|
|
globaluniform::GlobalUniform,
|
|
|
|
pipeline::PipelineBuilder,
|
|
|
|
shaderprocessor::preprocess_shader,
|
|
|
|
starfield::Starfield,
|
|
|
|
texturearray::TextureArray,
|
|
|
|
ui::UiManager,
|
|
|
|
vertexbuffer::{
|
|
|
|
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
|
|
|
types::{
|
|
|
|
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
|
|
|
|
UiInstance,
|
|
|
|
},
|
|
|
|
VertexBuffer,
|
|
|
|
},
|
|
|
|
GPUState,
|
|
|
|
};
|
|
|
|
|
|
|
|
impl GPUState {
|
|
|
|
/// Make a new GPUState that draws on `window`
|
|
|
|
pub async fn new(window: winit::window::Window, ct: &Content) -> Result<Self> {
|
|
|
|
let window_size = window.inner_size();
|
|
|
|
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
|
|
|
|
|
|
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
|
|
backends: wgpu::Backends::all(),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
|
|
|
|
let surface = unsafe { instance.create_surface(&window) }.unwrap();
|
|
|
|
|
|
|
|
// Basic setup
|
|
|
|
let device;
|
|
|
|
let queue;
|
|
|
|
let config;
|
|
|
|
|
|
|
|
{
|
|
|
|
let adapter = instance
|
|
|
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
|
|
|
power_preference: wgpu::PowerPreference::default(),
|
|
|
|
compatible_surface: Some(&surface),
|
|
|
|
force_fallback_adapter: false,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
(device, queue) = adapter
|
|
|
|
.request_device(
|
|
|
|
&wgpu::DeviceDescriptor {
|
|
|
|
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
|
|
|
|
limits: wgpu::Limits::default(),
|
|
|
|
label: Some("gpu device"),
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Assume sRGB
|
|
|
|
let surface_caps = surface.get_capabilities(&adapter);
|
|
|
|
let surface_format = surface_caps
|
|
|
|
.formats
|
|
|
|
.iter()
|
|
|
|
.copied()
|
|
|
|
.filter(|f| f.is_srgb())
|
|
|
|
.filter(|f| f.has_stencil_aspect())
|
|
|
|
.next()
|
|
|
|
.unwrap_or(surface_caps.formats[0]);
|
|
|
|
|
|
|
|
config = wgpu::SurfaceConfiguration {
|
|
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
|
|
format: surface_format,
|
|
|
|
width: window_size.width,
|
|
|
|
height: window_size.height,
|
|
|
|
present_mode: surface_caps.present_modes[0],
|
|
|
|
alpha_mode: surface_caps.alpha_modes[0],
|
|
|
|
view_formats: vec![],
|
|
|
|
};
|
|
|
|
|
|
|
|
surface.configure(&device, &config);
|
|
|
|
}
|
|
|
|
|
|
|
|
let vertex_buffers = VertexBuffers {
|
|
|
|
object_counter: 0,
|
|
|
|
ui_counter: 0,
|
|
|
|
particle_counter: 0,
|
|
|
|
radialbar_counter: 0,
|
|
|
|
|
|
|
|
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
|
|
|
|
"object",
|
|
|
|
&device,
|
|
|
|
Some(SPRITE_VERTICES),
|
|
|
|
Some(SPRITE_INDICES),
|
2024-01-10 18:53:19 -08:00
|
|
|
OBJECT_SPRITE_INSTANCE_LIMIT,
|
2024-01-10 17:53:27 -08:00
|
|
|
)),
|
|
|
|
|
|
|
|
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
|
|
|
|
"starfield",
|
|
|
|
&device,
|
|
|
|
Some(SPRITE_VERTICES),
|
|
|
|
Some(SPRITE_INDICES),
|
2024-01-10 22:44:22 -08:00
|
|
|
ct.get_config().starfield_instance_limit,
|
2024-01-10 17:53:27 -08:00
|
|
|
)),
|
|
|
|
|
|
|
|
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
|
|
|
"ui",
|
|
|
|
&device,
|
|
|
|
Some(SPRITE_VERTICES),
|
|
|
|
Some(SPRITE_INDICES),
|
2024-01-10 18:53:19 -08:00
|
|
|
UI_SPRITE_INSTANCE_LIMIT,
|
2024-01-10 17:53:27 -08:00
|
|
|
)),
|
|
|
|
|
|
|
|
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
|
|
|
|
"particle",
|
|
|
|
&device,
|
|
|
|
Some(SPRITE_VERTICES),
|
|
|
|
Some(SPRITE_INDICES),
|
2024-01-10 18:53:19 -08:00
|
|
|
PARTICLE_SPRITE_INSTANCE_LIMIT,
|
2024-01-10 17:53:27 -08:00
|
|
|
)),
|
|
|
|
|
|
|
|
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
|
|
|
|
"radial bar",
|
|
|
|
&device,
|
|
|
|
Some(SPRITE_VERTICES),
|
|
|
|
Some(SPRITE_INDICES),
|
|
|
|
10,
|
|
|
|
)),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Load uniforms
|
|
|
|
let global_uniform = GlobalUniform::new(&device);
|
|
|
|
let texture_array = TextureArray::new(&device, &queue, ct)?;
|
|
|
|
|
|
|
|
// Make sure these match the indices in each shader
|
|
|
|
let bind_group_layouts = &[
|
|
|
|
&texture_array.bind_group_layout,
|
|
|
|
&global_uniform.bind_group_layout,
|
|
|
|
];
|
|
|
|
|
|
|
|
// Text renderer
|
|
|
|
let mut text_atlas = TextAtlas::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb);
|
2024-01-10 22:44:22 -08:00
|
|
|
let mut text_font_system = FontSystem::new_with_locale_and_db(
|
|
|
|
"en-US".to_string(),
|
|
|
|
glyphon::fontdb::Database::new(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let conf = ct.get_config();
|
|
|
|
for font in &conf.font_files {
|
|
|
|
text_font_system.db_mut().load_font_file(font)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: nice error if no family with this name is found
|
|
|
|
text_font_system
|
|
|
|
.db_mut()
|
|
|
|
.set_sans_serif_family(conf.font_sans.clone());
|
|
|
|
text_font_system
|
|
|
|
.db_mut()
|
|
|
|
.set_serif_family(conf.font_serif.clone());
|
|
|
|
text_font_system
|
|
|
|
.db_mut()
|
|
|
|
.set_monospace_family(conf.font_mono.clone());
|
|
|
|
//text_font_system
|
|
|
|
// .db_mut()
|
|
|
|
// .set_cursive_family(conf.font_cursive.clone());
|
|
|
|
//text_font_system
|
|
|
|
// .db_mut()
|
|
|
|
// .set_fantasy_family(conf.font_fantasy.clone());
|
2024-01-10 17:53:27 -08:00
|
|
|
|
|
|
|
let text_cache = SwashCache::new();
|
|
|
|
let text_renderer = TextRenderer::new(
|
|
|
|
&mut text_atlas,
|
|
|
|
&device,
|
|
|
|
wgpu::MultisampleState::default(),
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Create render pipelines
|
|
|
|
let object_pipeline = PipelineBuilder::new("object", &device)
|
|
|
|
.set_shader(&preprocess_shader(
|
|
|
|
&include_str!(concat!(
|
|
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
|
|
"/shaders/",
|
|
|
|
"object.wgsl"
|
|
|
|
)),
|
|
|
|
&global_uniform,
|
|
|
|
1,
|
|
|
|
))
|
|
|
|
.set_format(config.format)
|
|
|
|
.set_triangle(true)
|
|
|
|
.set_vertex_buffer(&vertex_buffers.object)
|
|
|
|
.set_bind_group_layouts(bind_group_layouts)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
|
|
|
.set_shader(&preprocess_shader(
|
|
|
|
&include_str!(concat!(
|
|
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
|
|
"/shaders/",
|
|
|
|
"starfield.wgsl"
|
|
|
|
)),
|
|
|
|
&global_uniform,
|
|
|
|
1,
|
|
|
|
))
|
|
|
|
.set_format(config.format)
|
|
|
|
.set_triangle(true)
|
|
|
|
.set_vertex_buffer(&vertex_buffers.starfield)
|
|
|
|
.set_bind_group_layouts(bind_group_layouts)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let ui_pipeline = PipelineBuilder::new("ui", &device)
|
|
|
|
.set_shader(&preprocess_shader(
|
|
|
|
&include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/", "ui.wgsl")),
|
|
|
|
&global_uniform,
|
|
|
|
1,
|
|
|
|
))
|
|
|
|
.set_format(config.format)
|
|
|
|
.set_triangle(true)
|
|
|
|
.set_vertex_buffer(&vertex_buffers.ui)
|
|
|
|
.set_bind_group_layouts(bind_group_layouts)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let particle_pipeline = PipelineBuilder::new("particle", &device)
|
|
|
|
.set_shader(&preprocess_shader(
|
|
|
|
&include_str!(concat!(
|
|
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
|
|
"/shaders/",
|
|
|
|
"particle.wgsl"
|
|
|
|
)),
|
|
|
|
&global_uniform,
|
|
|
|
1,
|
|
|
|
))
|
|
|
|
.set_format(config.format)
|
|
|
|
.set_triangle(true)
|
|
|
|
.set_vertex_buffer(&vertex_buffers.particle)
|
|
|
|
.set_bind_group_layouts(bind_group_layouts)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let radialbar_pipeline = PipelineBuilder::new("radialbar", &device)
|
|
|
|
.set_shader(&preprocess_shader(
|
|
|
|
&include_str!(concat!(
|
|
|
|
env!("CARGO_MANIFEST_DIR"),
|
|
|
|
"/shaders/",
|
|
|
|
"radialbar.wgsl"
|
|
|
|
)),
|
|
|
|
&global_uniform,
|
|
|
|
1,
|
|
|
|
))
|
|
|
|
.set_format(config.format)
|
|
|
|
.set_triangle(true)
|
|
|
|
.set_vertex_buffer(&vertex_buffers.radialbar)
|
|
|
|
.set_bind_group_layouts(bind_group_layouts)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let mut starfield = Starfield::new();
|
2024-01-10 22:44:22 -08:00
|
|
|
starfield.regenerate(ct);
|
2024-01-10 17:53:27 -08:00
|
|
|
|
2024-01-10 18:53:19 -08:00
|
|
|
let mut state = RenderState {
|
|
|
|
queue,
|
2024-01-10 17:53:27 -08:00
|
|
|
|
2024-01-10 18:53:19 -08:00
|
|
|
window,
|
|
|
|
window_size,
|
|
|
|
window_aspect,
|
|
|
|
global_uniform,
|
2024-01-10 17:53:27 -08:00
|
|
|
|
2024-01-10 18:53:19 -08:00
|
|
|
vertex_buffers,
|
2024-01-10 17:53:27 -08:00
|
|
|
|
2024-01-10 18:53:19 -08:00
|
|
|
text_atlas,
|
|
|
|
text_cache,
|
|
|
|
text_font_system,
|
|
|
|
text_renderer,
|
|
|
|
};
|
2024-01-10 17:53:27 -08:00
|
|
|
|
2024-01-10 18:53:19 -08:00
|
|
|
return Ok(Self {
|
|
|
|
ui: UiManager::new(&mut state),
|
2024-01-10 17:53:27 -08:00
|
|
|
device,
|
|
|
|
config,
|
|
|
|
surface,
|
|
|
|
starfield,
|
|
|
|
texture_array,
|
|
|
|
object_pipeline,
|
|
|
|
starfield_pipeline,
|
|
|
|
ui_pipeline,
|
|
|
|
particle_pipeline,
|
|
|
|
radialbar_pipeline,
|
2024-01-10 18:53:19 -08:00
|
|
|
|
|
|
|
state,
|
2024-01-10 17:53:27 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|