use crate::ecs::world::World;
use crate::state::{RenderResources, State};
use freecs::dynamic::ResourceHostExt;
pub trait Plugin {
fn build(&self, app: &mut App);
}
pub trait PluginGroup {
fn build(&self, app: &mut App);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Stage {
First,
Update,
LateUpdate,
}
impl Stage {
const ALL: [Stage; 3] = [Stage::First, Stage::Update, Stage::LateUpdate];
fn name(self) -> &'static str {
match self {
Stage::First => "first",
Stage::Update => "update",
Stage::LateUpdate => "late_update",
}
}
}
type RenderGraphConfigFn = Box<
dyn FnMut(
&mut crate::render::wgpu::rendergraph::RenderGraph<
crate::render::wgpu::render_configs::RenderInputs,
>,
&wgpu::Device,
wgpu::TextureFormat,
RenderResources,
),
>;
type PreRenderFn = Box<dyn FnMut(&mut crate::render::wgpu::WgpuRenderer, &mut World)>;
type UpdateRenderGraphFn = Box<
dyn FnMut(
&mut crate::render::wgpu::rendergraph::RenderGraph<
crate::render::wgpu::render_configs::RenderInputs,
>,
&World,
),
>;
pub struct App {
pub world: World,
pub startup: freecs::Schedule<World>,
pub stages: freecs::Stages<World>,
windowed: bool,
rendered: bool,
render_graph_config: Vec<RenderGraphConfigFn>,
pre_render: Vec<PreRenderFn>,
update_render_graph: Vec<UpdateRenderGraphFn>,
}
impl Default for App {
fn default() -> Self {
Self::new()
}
}
impl App {
pub fn new() -> Self {
let mut stages = freecs::Stages::new();
for stage in Stage::ALL {
stages.add_stage(stage.name());
}
Self {
world: World::default(),
startup: freecs::Schedule::new(),
stages,
windowed: false,
rendered: false,
render_graph_config: Vec::new(),
pre_render: Vec::new(),
update_render_graph: Vec::new(),
}
}
pub fn add_plugin(mut self, plugin: impl Plugin) -> Self {
plugin.build(&mut self);
self
}
pub fn add_plugins(mut self, group: impl PluginGroup) -> Self {
group.build(&mut self);
self
}
pub fn add_system(
&mut self,
stage: Stage,
name: &'static str,
system: impl FnMut(&mut World) + Send + 'static,
) -> &mut Self {
self.stages.stage_mut(stage.name()).push(name, system);
self
}
pub fn add_startup_system(
&mut self,
name: &'static str,
system: impl FnMut(&mut World) + Send + 'static,
) -> &mut Self {
self.startup.push(name, system);
self
}
pub fn insert_resource<T: Send + Sync + 'static>(&mut self, value: T) -> &mut Self {
self.world.ecs.insert_resource(value);
self
}
pub fn add_render_graph_config(
&mut self,
hook: impl FnMut(
&mut crate::render::wgpu::rendergraph::RenderGraph<
crate::render::wgpu::render_configs::RenderInputs,
>,
&wgpu::Device,
wgpu::TextureFormat,
RenderResources,
) + 'static,
) -> &mut Self {
self.render_graph_config.push(Box::new(hook));
self
}
pub fn on_pre_render(
&mut self,
hook: impl FnMut(&mut crate::render::wgpu::WgpuRenderer, &mut World) + 'static,
) -> &mut Self {
self.pre_render.push(Box::new(hook));
self
}
pub fn on_update_render_graph(
&mut self,
hook: impl FnMut(
&mut crate::render::wgpu::rendergraph::RenderGraph<
crate::render::wgpu::render_configs::RenderInputs,
>,
&World,
) + 'static,
) -> &mut Self {
self.update_render_graph.push(Box::new(hook));
self
}
pub(crate) fn set_windowed(&mut self) {
self.windowed = true;
}
pub(crate) fn set_rendered(&mut self) {
self.rendered = true;
}
pub fn run(mut self) -> Result<(), Box<dyn std::error::Error>> {
if !self.windowed && !self.rendered {
let mut world = self.world;
self.startup.run(&mut world);
self.stages.run(&mut world);
return Ok(());
}
self.world.resources.window.start_hidden = !self.windowed;
self.world.resources.window.create_renderer = self.rendered;
let (world, state) = self.into_parts();
crate::run::launch_with_world(state, world)
}
pub fn into_parts(self) -> (World, impl State + 'static) {
let App {
world,
startup,
stages,
render_graph_config,
pre_render,
update_render_graph,
..
} = self;
(
world,
StageState {
startup,
stages,
render_graph_config,
pre_render,
update_render_graph,
},
)
}
}
pub fn game_scope<T: Send + Sync + 'static, R>(
world: &mut World,
body: impl FnOnce(&mut T, &mut World) -> R,
) -> R {
world.resource_scope(|world, value| body(value, world))
}
struct StageState {
startup: freecs::Schedule<World>,
stages: freecs::Stages<World>,
render_graph_config: Vec<RenderGraphConfigFn>,
pre_render: Vec<PreRenderFn>,
update_render_graph: Vec<UpdateRenderGraphFn>,
}
impl State for StageState {
fn initialize(&mut self, world: &mut World) {
self.startup.run(world);
}
fn run_systems(&mut self, world: &mut World) {
self.stages.run(world);
}
fn configure_render_graph(
&mut self,
graph: &mut crate::render::wgpu::rendergraph::RenderGraph<
crate::render::wgpu::render_configs::RenderInputs,
>,
device: &wgpu::Device,
format: wgpu::TextureFormat,
resources: RenderResources,
) {
for hook in &mut self.render_graph_config {
hook(graph, device, format, resources);
}
}
fn pre_render(&mut self, renderer: &mut crate::render::wgpu::WgpuRenderer, world: &mut World) {
for hook in &mut self.pre_render {
hook(renderer, world);
}
}
fn update_render_graph(
&mut self,
graph: &mut crate::render::wgpu::rendergraph::RenderGraph<
crate::render::wgpu::render_configs::RenderInputs,
>,
world: &World,
) {
for hook in &mut self.update_render_graph {
hook(graph, world);
}
}
}
pub struct WindowPlugin {
pub title: String,
pub size: Option<(u32, u32)>,
}
impl Default for WindowPlugin {
fn default() -> Self {
Self {
title: "Nightshade".to_string(),
size: None,
}
}
}
impl Plugin for WindowPlugin {
fn build(&self, app: &mut App) {
app.world.resources.window.title = self.title.clone();
if self.size.is_some() {
app.world.resources.window.initial_size = self.size;
}
app.set_windowed();
}
}
pub struct RenderPlugin;
impl Plugin for RenderPlugin {
fn build(&self, app: &mut App) {
app.set_rendered();
}
}
pub struct UiPlugin;
impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.world.resources.user_interface.enabled = true;
app.world.resources.retained_ui.enabled = true;
}
}
#[cfg(feature = "physics")]
pub struct PhysicsPlugin;
#[cfg(feature = "physics")]
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app.world.resources.physics.enabled = true;
}
}
pub struct DefaultPlugins;
impl PluginGroup for DefaultPlugins {
fn build(&self, app: &mut App) {
WindowPlugin::default().build(app);
RenderPlugin.build(app);
UiPlugin.build(app);
}
}