diff --git a/Cargo.toml b/Cargo.toml
index aea0de4..eda13ca 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,9 @@ parallel = ["legion/parallel"]
legion = { git = "https://github.com/grzi/legion", branch = "feat/uuid_as_optional", default-features = false, features = ["codegen"]}
# window & rendering
-miniquad = "0.3.0-alpha.26"
+winit = "0.24.0"
+wgpu = "0.7.0"
+futures = "0.3"
# maths
ultraviolet = "0.7"
@@ -19,8 +21,15 @@ ultraviolet = "0.7"
# serialization
serde = { version = "1.0.124", features = ["derive"] }
toml = "0.5.8"
+bytemuck = { version = "1.4", features = [ "derive" ] }
image = {version = "0.23", default-features = false, features = ["png"]}
# logging
log = { version = "0.4.14", features = ["serde"] }
-fern = { version = "0.6.0", features = ["colored"] }
\ No newline at end of file
+fern = { version = "0.6.0", features = ["colored"] }
+
+[build-dependencies]
+anyhow = "1.0"
+fs_extra = "1.1"
+glob = "0.3"
+shaderc = "0.7"
\ No newline at end of file
diff --git a/README.md b/README.md
index 3875966..8ab49ea 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-Scion is a minimalist, **easy** to use, modulable game engine built on top of legion and miniquad.
+Scion is a minimalist, **easy** to use, modulable game engine built on top of legion, wgpu and winit.
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..bf5096e
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,81 @@
+use anyhow::*;
+use glob::glob;
+use std::fs::{read_to_string, write};
+use std::path::PathBuf;
+
+struct ShaderData {
+ src: String,
+ src_path: PathBuf,
+ spv_path: PathBuf,
+ kind: shaderc::ShaderKind,
+}
+
+impl ShaderData {
+ pub fn load(src_path: PathBuf) -> Result {
+ let extension = src_path
+ .extension()
+ .context("File has no extension")?
+ .to_str()
+ .context("Extension cannot be converted to &str")?;
+ let kind = match extension {
+ "vert" => shaderc::ShaderKind::Vertex,
+ "frag" => shaderc::ShaderKind::Fragment,
+ "comp" => shaderc::ShaderKind::Compute,
+ _ => bail!("Unsupported shader: {}", src_path.display()),
+ };
+
+ let src = read_to_string(src_path.clone())?;
+ let spv_path = src_path.with_extension(format!("{}.spv", extension));
+
+ Ok(Self {
+ src,
+ src_path,
+ spv_path,
+ kind,
+ })
+ }
+}
+
+fn main() -> Result<()> {
+ // Collect all shaders recursively within /src/
+ let mut shader_paths = [
+ glob("./src/**/*.vert")?,
+ glob("./src/**/*.frag")?,
+ glob("./src/**/*.comp")?,
+ ];
+
+ // This could be parallelized
+ let shaders = shader_paths
+ .iter_mut()
+ .flatten()
+ .map(|glob_result| ShaderData::load(glob_result?))
+ .collect::>>()
+ .into_iter()
+ .collect::>>()?;
+
+ let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
+
+ // This can't be parallelized. The [shaderc::Compiler] is not
+ // thread safe. Also, it creates a lot of resources. You could
+ // spawn multiple processes to handle this, but it would probably
+ // be better just to only compile shaders that have been changed
+ // recently.
+ for shader in shaders {
+ // This tells cargo to rerun this script if something in /src/ changes.
+ println!(
+ "cargo:rerun-if-changed={}",
+ shader.src_path.as_os_str().to_str().unwrap()
+ );
+
+ let compiled = compiler.compile_into_spirv(
+ &shader.src,
+ shader.kind,
+ &shader.src_path.to_str().unwrap(),
+ "main",
+ None,
+ )?;
+ write(shader.spv_path, compiled.as_binary_u8())?;
+ }
+
+ Ok(())
+}
diff --git a/examples/hello-world/main.rs b/examples/hello-world/main.rs
index 35f57f6..3893f67 100644
--- a/examples/hello-world/main.rs
+++ b/examples/hello-world/main.rs
@@ -1,38 +1,47 @@
use scion::application::Scion;
+use scion::game_layer::{GameLayer, SimpleGameLayer};
use scion::legion::{system, Resources, World};
-use scion::utils::time::Time;
-use scion::game_layer::{SimpleGameLayer, GameLayer};
-
+use scion::renderer::bidimensional::material::{Material2D, Texture2D};
+use scion::renderer::bidimensional::transform::{Position2D, Transform2D};
use scion::renderer::bidimensional::triangle::Triangle;
-use scion::renderer::bidimensional::material::{Material2D};
use scion::renderer::color::Color;
-use scion::renderer::bidimensional::transform::{Transform2D, Position2D};
+use scion::utils::time::Time;
+use std::path::Path;
fn triangle() -> Triangle {
Triangle {
vertices: [
Position2D { x: -0.5, y: -0.5 },
Position2D { x: 0.5, y: -0.5 },
- Position2D { x: 0., y: 0.5 }
+ Position2D { x: 0., y: 0.5 },
],
uvs: None,
}
}
#[system(for_each)]
-fn color(#[state] timer: &mut f32, #[resource] time: &Time, material: &mut Material2D, transform: &mut Transform2D) {
+fn color(
+ #[state] timer: &mut f32,
+ #[resource] time: &Time,
+ material: &mut Material2D,
+ transform: &mut Transform2D,
+) {
*timer += time.delta_duration().as_secs_f32();
if *timer > 0.01 {
*timer = 0.;
match material {
Material2D::Color(color) => {
- let new_red = if color.red() < 255 { color.red() + 1 } else { 0 };
+ let new_red = if color.red() < 255 {
+ color.red() + 1
+ } else {
+ 0
+ };
color.replace(Color::new_rgb(new_red, color.green(), color.blue()));
}
_ => {}
}
+ transform.append_angle(0.1);
}
- transform.append_angle(0.1);
}
#[derive(Default)]
@@ -40,12 +49,17 @@ struct Layer;
impl SimpleGameLayer for Layer {
fn on_start(&mut self, world: &mut World, _resource: &mut Resources) {
- let triangle1 =
- (triangle(),
- Material2D::Color(Color::new(0, 47, 110, 1.0)),
- Transform2D::new(Position2D { x: 0.0, y: 0.0 }, 0.5, 0.)
- );
- world.extend(vec![triangle1]);
+ let triangle1 = (
+ triangle(),
+ Material2D::Texture(Texture2D::from_png(Path::new("Yo"))),
+ Transform2D::new(Position2D { x: -1.0, y: 0.0 }, 0.5, 0.),
+ );
+ let triangle2 = (
+ triangle(),
+ Material2D::Texture(Texture2D::from_png(Path::new("Yo"))),
+ Transform2D::new(Position2D { x: 1.0, y: 0.0 }, 0.5, 0.),
+ );
+ world.extend(vec![triangle1, triangle2]);
}
}
@@ -54,4 +68,4 @@ fn main() {
.with_system(color_system(0.))
.with_game_layer(GameLayer::weak::())
.run();
-}
\ No newline at end of file
+}
diff --git a/src/application.rs b/src/application.rs
index 1996743..04f3313 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -1,42 +1,29 @@
-use legion::{Resources, Schedule, World};
use legion::systems::{Builder, ParallelRunnable, Runnable};
+use legion::{Resources, Schedule, World};
use log::info;
-use miniquad::{conf, Context, EventHandlerFree, UserData};
use crate::config::scion_config::{ScionConfig, ScionConfigReader};
use crate::utils::time::Time;
-use crate::utils::window::WindowDimensions;
-use crate::renderer::{RendererType, ScionRenderer};
+use crate::renderer::RendererType;
+
+use crate::game_layer::{GameLayer, GameLayerType, LayerAction};
+use winit::event::{Event, WindowEvent};
+use winit::event_loop::{ControlFlow, EventLoop};
+use winit::window::{Window, WindowBuilder};
-use crate::game_layer::{GameLayer, SimpleGameLayer, GameLayerType, LayerAction};
+use crate::renderer::renderer_state::RendererState;
/// `Scion` is the entry point of any application made with Scion engine.
pub struct Scion {
+ #[allow(dead_code)]
config: ScionConfig,
world: World,
resources: Resources,
schedule: Schedule,
game_layers: Vec>,
- context: Option,
- renderer: Box,
-}
-
-impl EventHandlerFree for Scion {
- fn update(&mut self) {
- self.next_frame();
- }
-
- fn draw(&mut self) {
- self.renderer.draw(
- self.context.as_mut().expect("Miniquad context is mandatory"),
- &mut self.world, &mut self.resources)
- }
-
- fn resize_event(&mut self, w: f32, h: f32) {
- self.resources
- .get_mut::().expect("Missing Screen Dimension Resource. Did something deleted it ?").set(w, h);
- }
+ window: Option,
+ renderer: Option,
}
impl Scion {
@@ -58,38 +45,105 @@ impl Scion {
ScionBuilder::new(app_config)
}
- fn setup(mut self, context: Context) -> Self {
- let screen_size = context.screen_size();
- self.context = Some(context);
+ pub fn run(mut self, event_loop: EventLoop<()>) {
+ event_loop.run(move |event, _, control_flow| {
+ *control_flow = ControlFlow::Poll;
+ match event {
+ Event::WindowEvent {
+ ref event,
+ window_id,
+ } if window_id
+ == self
+ .window
+ .as_ref()
+ .expect("A window is mandatory to run this game !")
+ .id() =>
+ {
+ match event {
+ WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
+ WindowEvent::Resized(physical_size) => {
+ self.renderer
+ .as_mut()
+ .expect("A renderer is mandatory to run this game !")
+ .resize(*physical_size);
+ }
+ WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
+ self.renderer
+ .as_mut()
+ .expect("A renderer is mandatory to run this game !")
+ .resize(**new_inner_size);
+ }
+ _ => {}
+ }
+ }
+ Event::MainEventsCleared => {
+ self.window.as_ref().unwrap().request_redraw();
+ }
+ Event::RedrawRequested(_) => {
+ self.renderer
+ .as_mut()
+ .expect("A renderer is mandatory to run this game !")
+ .update(&mut self.world);
+ match self
+ .renderer
+ .as_mut()
+ .expect("A renderer is mandatory to run this game !")
+ .render(&mut self.world, &mut self.resources)
+ {
+ Ok(_) => {}
+ Err(e) => log::error!("{:?}", e),
+ }
+ }
+ _ => (),
+ }
+ self.next_frame();
+ });
+ }
+
+ fn setup(&mut self) {
+ //let screen_size = context.screen_size();
self.resources.insert(Time::default());
- self.resources.insert(WindowDimensions::new(screen_size));
- self.apply_layers_action(LayerAction::START);
- self
+ // self.resources.insert(WindowDimensions::new(screen_size));
+ self.apply_layers_action(LayerAction::Start);
}
fn next_frame(&mut self) {
- self.apply_layers_action(LayerAction::UPDATE);
- self.resources.get_mut::