aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:24 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:24 +0100
commit09a777d42d6301028cd3a8967ff6a82e703c8800 (patch)
tree79ac93287a15e8b0f90e1ac5d6981fa6eaeb6229 /examples
parent6367a9ba5a549b62f01da61fb50323877b9f52ff (diff)
refactor(all): remove levels and render
Diffstat (limited to 'examples')
-rw-r--r--examples/render-quad/Cargo.toml7
-rw-r--r--examples/render-quad/src/data/shader.frag11
-rw-r--r--examples/render-quad/src/data/shader.vert16
-rw-r--r--examples/render-quad/src/draw_pass.rs335
-rw-r--r--examples/render-quad/src/level.rs79
-rw-r--r--examples/render-quad/src/main.rs252
-rw-r--r--examples/render-quad/src/system.rs73
7 files changed, 486 insertions, 287 deletions
diff --git a/examples/render-quad/Cargo.toml b/examples/render-quad/Cargo.toml
index 99cf38e..7e7f57c 100644
--- a/examples/render-quad/Cargo.toml
+++ b/examples/render-quad/Cargo.toml
@@ -6,15 +6,10 @@ edition = "2018"
[dependencies]
stockton-skeleton = { path = "../../stockton-skeleton", features = ["vulkan"] }
-stockton-input = { path = "../../stockton-input" }
-stockton-input-codegen = { path = "../../stockton-input-codegen" }
-stockton-levels = { path = "../../stockton-levels" }
-stockton-contrib = { path = "../../stockton-contrib", features = ["delta_time", "flycam"] }
-stockton-render = { path = "../../stockton-render" }
winit = "^0.21"
log = "0.4.0"
simplelog = "^0.10"
image = "0.23.2"
-egui = "^0.12"
legion = { version = "^0.3" }
anyhow = "1.0.40"
+gfx-hal = "^0.8.0"
diff --git a/examples/render-quad/src/data/shader.frag b/examples/render-quad/src/data/shader.frag
new file mode 100644
index 0000000..929073f
--- /dev/null
+++ b/examples/render-quad/src/data/shader.frag
@@ -0,0 +1,11 @@
+#version 450
+
+layout (location = 0) in vec2 frag_pos;
+layout (location = 1) in vec3 frag_color;
+
+layout (location = 0) out vec4 color;
+
+void main()
+{
+ color = vec4(frag_color, 1.0);
+} \ No newline at end of file
diff --git a/examples/render-quad/src/data/shader.vert b/examples/render-quad/src/data/shader.vert
new file mode 100644
index 0000000..5ca3090
--- /dev/null
+++ b/examples/render-quad/src/data/shader.vert
@@ -0,0 +1,16 @@
+#version 450
+
+layout (location = 0) in vec2 position;
+layout (location = 1) in vec3 color;
+
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+layout (location = 1) out vec3 frag_color;
+
+
+void main()
+{
+ gl_Position = vec4(position, 0.5, 1.0);
+ frag_color = color;
+} \ No newline at end of file
diff --git a/examples/render-quad/src/draw_pass.rs b/examples/render-quad/src/draw_pass.rs
new file mode 100644
index 0000000..9b32fcb
--- /dev/null
+++ b/examples/render-quad/src/draw_pass.rs
@@ -0,0 +1,335 @@
+//! Minimal code for drawing any level, based on traits from stockton-levels
+
+use anyhow::{Context, Result};
+use hal::{
+ buffer::SubRange,
+ command::{ClearColor, ClearValue, RenderAttachmentInfo, SubpassContents},
+ format::Format,
+ image::Layout,
+ pass::Attachment,
+ pso::{
+ BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, DepthStencilDesc, Face, Factor,
+ FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, Rasterizer, State,
+ VertexInputRate,
+ },
+};
+use legion::{Entity, IntoQuery};
+use std::{
+ array::IntoIter,
+ iter::{empty, once},
+};
+use stockton_skeleton::{
+ buffers::draw::DrawBuffers,
+ builders::{
+ AttachmentSpec, CompletePipeline, PipelineSpecBuilder, RenderpassSpec, ShaderDesc,
+ ShaderKind, VertexBufferSpec, VertexPrimitiveAssemblerSpec,
+ },
+ draw_passes::util::TargetSpecificResources,
+ mem::{DataPool, StagingPool},
+ queue_negotiator::QueueFamilyNegotiator,
+ types::*,
+ DrawPass, IntoDrawPass, PassPosition, RenderingContext, Session,
+};
+
+use crate::ExampleState;
+
+/// The vertices that go to the shader (XY + RGB)
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+struct Vertex(pub Vector2, pub Vector3);
+
+/// An example draw pass
+pub struct ExampleDrawPass<'a> {
+ /// Index and vertex buffer pair
+ draw_buffers: DrawBuffers<'a, Vertex, DataPool, StagingPool>,
+
+ /// Resources that depend on the surface. This is seperate so that we can deal with surface changes more easily.
+ surface_resources: SurfaceDependentResources,
+
+ /// Entity we get our state from
+ state_ent: Entity,
+}
+
+/// Config for our draw pass. This is turned into our drawpass using [`IntoDrawPass`]
+pub struct ExampleDrawPassConfig {
+ pub state_ent: Entity,
+}
+
+impl<'a, P: PassPosition> DrawPass<P> for ExampleDrawPass<'a> {
+ /// Called every frame to queue actual drawing.
+ fn queue_draw(
+ &mut self,
+ session: &Session,
+ img_view: &ImageViewT,
+ cmd_buffer: &mut CommandBufferT,
+ ) -> anyhow::Result<()> {
+ // Commit any changes to our vertex buffers
+ // We queue this first so that it's executed before any draw commands
+ self.draw_buffers
+ .vertex_buffer
+ .record_commit_cmds(cmd_buffer)?;
+ self.draw_buffers
+ .index_buffer
+ .record_commit_cmds(cmd_buffer)?;
+
+ // Get framebuffer
+ let fb = self.surface_resources.framebuffers.get_next();
+
+ // Get state
+ let (state,) = <(&ExampleState,)>::query().get(&session.world, self.state_ent)?;
+
+ // Begin render pass & bind everything needed
+ unsafe {
+ cmd_buffer.begin_render_pass(
+ &self.surface_resources.pipeline.renderpass,
+ fb,
+ self.surface_resources.pipeline.render_area,
+ vec![RenderAttachmentInfo {
+ image_view: img_view,
+ clear_value: ClearValue {
+ color: ClearColor {
+ float32: [0.0, 0.0, 0.0, 1.0],
+ },
+ },
+ }]
+ .into_iter(),
+ SubpassContents::Inline,
+ );
+ cmd_buffer.bind_graphics_pipeline(&self.surface_resources.pipeline.pipeline);
+
+ // Bind buffers
+ cmd_buffer.bind_vertex_buffers(
+ 0,
+ once((
+ self.draw_buffers.vertex_buffer.get_buffer(),
+ SubRange {
+ offset: 0,
+ size: None,
+ },
+ )),
+ );
+ cmd_buffer.bind_index_buffer(
+ self.draw_buffers.index_buffer.get_buffer(),
+ SubRange {
+ offset: 0,
+ size: None,
+ },
+ hal::IndexType::U16,
+ );
+ }
+
+ // Draw an example
+ self.draw_buffers.index_buffer[0] = (0, 1, 2);
+ self.draw_buffers.vertex_buffer[0] = Vertex(Vector2::new(0.5, 0.5), state.color());
+ self.draw_buffers.vertex_buffer[1] = Vertex(Vector2::new(0.0, -0.5), state.color());
+ self.draw_buffers.vertex_buffer[2] = Vertex(Vector2::new(-0.5, 0.5), state.color());
+
+ unsafe {
+ cmd_buffer.draw_indexed(0..3, 0, 0..1);
+ }
+
+ // Remember to clean up afterwards!
+ unsafe {
+ cmd_buffer.end_render_pass();
+ }
+
+ Ok(())
+ }
+
+ /// Destroy all our vulkan objects
+ fn deactivate(self, context: &mut RenderingContext) -> Result<()> {
+ self.draw_buffers.deactivate(context);
+ self.surface_resources.deactivate(context)?;
+
+ Ok(())
+ }
+
+ /// Deal with a surface change
+ fn handle_surface_change(
+ mut self,
+ _session: &Session,
+ context: &mut RenderingContext,
+ ) -> Result<Self> {
+ // We need to make sure there's never an invalid value for self.surface_resources,
+ // and that we deactivate everything in case of an error (since we'll be dropped in that case).
+ let new_resources = match SurfaceDependentResources::new::<P>(context) {
+ Ok(x) => x,
+ Err(e) => {
+ <Self as DrawPass<P>>::deactivate(self, context)?;
+
+ return Err(e);
+ }
+ };
+
+ let old_resources = self.surface_resources;
+ self.surface_resources = new_resources;
+
+ match old_resources.deactivate(context) {
+ Ok(_) => Ok(self),
+ Err(e) => {
+ <Self as DrawPass<P>>::deactivate(self, context)?;
+ Err(e)
+ }
+ }
+ }
+}
+
+impl<'a, P: PassPosition> IntoDrawPass<ExampleDrawPass<'a>, P> for ExampleDrawPassConfig {
+ /// Create our example draw pass
+ fn init(
+ self,
+ _session: &mut Session,
+ context: &mut RenderingContext,
+ ) -> Result<ExampleDrawPass<'a>> {
+ let surface_resources = SurfaceDependentResources::new::<P>(context)?;
+ let draw_buffers =
+ match DrawBuffers::from_context(context).context("Error creating draw buffers") {
+ Ok(x) => x,
+ Err(e) => {
+ surface_resources.deactivate(context)?;
+ return Err(e);
+ }
+ };
+
+ Ok(ExampleDrawPass {
+ draw_buffers,
+ surface_resources,
+ state_ent: self.state_ent,
+ })
+ }
+
+ fn find_aux_queues(
+ _adapter: &Adapter,
+ _queue_negotiator: &mut QueueFamilyNegotiator,
+ ) -> Result<()> {
+ // We don't need any queues, but we'd need code to find their families here if we did.
+ Ok(())
+ }
+}
+
+/// Used to store resources which depend on the surface, for convenience in handle_surface_change
+struct SurfaceDependentResources {
+ pub pipeline: CompletePipeline,
+ pub framebuffers: TargetSpecificResources<FramebufferT>,
+}
+
+impl SurfaceDependentResources {
+ pub fn new<P: PassPosition>(context: &mut RenderingContext) -> Result<Self> {
+ let (pipeline, framebuffers) = {
+ // Our graphics pipeline
+ // Vulkan has a lot of config, so this is basically always going to be a big builder block
+ let pipeline_spec = PipelineSpecBuilder::default()
+ .rasterizer(Rasterizer {
+ polygon_mode: PolygonMode::Fill,
+ cull_face: Face::NONE,
+ front_face: FrontFace::CounterClockwise,
+ depth_clamping: false,
+ depth_bias: None,
+ conservative: true,
+ line_width: State::Static(1.0),
+ })
+ .depth_stencil(DepthStencilDesc {
+ depth: None,
+ depth_bounds: false,
+ stencil: None,
+ })
+ .blender(BlendDesc {
+ logic_op: Some(LogicOp::Copy),
+ targets: vec![ColorBlendDesc {
+ mask: ColorMask::ALL,
+ blend: Some(BlendState {
+ color: BlendOp::Add {
+ src: Factor::SrcAlpha,
+ dst: Factor::OneMinusSrcAlpha,
+ },
+ alpha: BlendOp::Add {
+ src: Factor::SrcAlpha,
+ dst: Factor::OneMinusSrcAlpha,
+ },
+ }),
+ }],
+ })
+ .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers(
+ InputAssemblerDesc::new(Primitive::TriangleList),
+ vec![VertexBufferSpec {
+ attributes: vec![Format::Rg32Sfloat, Format::Rgb32Sfloat],
+ rate: VertexInputRate::Vertex,
+ }],
+ ))
+ .shader_vertex(ShaderDesc {
+ source: include_str!("./data/shader.vert").to_string(),
+ entry: "main".to_string(),
+ kind: ShaderKind::Vertex,
+ })
+ .shader_fragment(ShaderDesc {
+ source: include_str!("./data/shader.frag").to_string(),
+ entry: "main".to_string(),
+ kind: ShaderKind::Fragment,
+ })
+ .renderpass(RenderpassSpec {
+ colors: vec![AttachmentSpec {
+ attachment: Attachment {
+ format: Some(context.properties().color_format),
+ samples: 1,
+ // Here we use PassPosition to get the proper operations
+ // Since, for example, the last pass needs to finish in present mode.
+ ops: P::attachment_ops(),
+ stencil_ops: P::attachment_ops(),
+ layouts: P::layout_as_range(),
+ },
+ // This is the layout we want to deal with in our `queue_draw` function.
+ // It's almost certainly `Layout::ColorAttachmentOptimal`
+ used_layout: Layout::ColorAttachmentOptimal,
+ }],
+ depth: None,
+ inputs: vec![],
+ resolves: vec![],
+ preserves: vec![],
+ })
+ .build()
+ .context("Error building pipeline")?;
+
+ // Lock our device to actually build it
+ // Try to lock the device for as little time as possible
+ let mut device = context.lock_device()?;
+
+ let pipeline = pipeline_spec
+ .build(&mut device, context.properties().extent, empty())
+ .context("Error building pipeline")?;
+
+ // Our framebuffers just have the swapchain framebuffer attachment
+ // TargetSpecificResources makes sure we use a different one each frame.
+ let fat = context.properties().swapchain_framebuffer_attachment();
+ let framebuffers = TargetSpecificResources::new(
+ || unsafe {
+ Ok(device.create_framebuffer(
+ &pipeline.renderpass,
+ IntoIter::new([fat.clone()]),
+ context.properties().extent,
+ )?)
+ },
+ context.properties().image_count as usize,
+ )?;
+
+ (pipeline, framebuffers)
+ };
+
+ Ok(Self {
+ pipeline,
+ framebuffers,
+ })
+ }
+
+ pub fn deactivate(self, context: &mut RenderingContext) -> Result<()> {
+ unsafe {
+ let mut device = context.lock_device()?;
+ for fb in self.framebuffers.dissolve() {
+ device.destroy_framebuffer(fb);
+ }
+
+ self.pipeline.deactivate(&mut device);
+ }
+
+ Ok(())
+ }
+}
diff --git a/examples/render-quad/src/level.rs b/examples/render-quad/src/level.rs
deleted file mode 100644
index 74d1cc3..0000000
--- a/examples/render-quad/src/level.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-use stockton_levels::parts::{
- data::{FaceRef, Geometry, TextureRef},
- HasFaces, HasTextures, HasVisData, IsFace, IsTexture,
-};
-use stockton_skeleton::components::{CameraSettings, Transform};
-
-pub struct DemoLevel {
- pub faces: Box<[Face]>,
- pub textures: Box<[Texture]>,
-}
-
-impl DemoLevel {
- fn face_idx(&self, search: &Face) -> FaceRef {
- for (idx, face) in self.faces.iter().enumerate() {
- if face == search {
- return idx as u32;
- }
- }
- panic!("face not in level")
- }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub struct Face {
- pub geometry: Geometry,
- pub texture_idx: TextureRef,
-}
-
-impl HasFaces for DemoLevel {
- type Face = Face;
-
- fn get_face(&self, index: FaceRef) -> Option<&Self::Face> {
- self.faces.get(index as usize)
- }
-}
-
-impl IsFace<DemoLevel> for Face {
- fn index(&self, container: &DemoLevel) -> stockton_levels::parts::data::FaceRef {
- container.face_idx(self)
- }
-
- fn geometry(&self, _container: &DemoLevel) -> Geometry {
- self.geometry.clone()
- }
-
- fn texture_idx(&self, _container: &DemoLevel) -> TextureRef {
- self.texture_idx
- }
-}
-
-pub struct Texture {
- pub name: String,
-}
-
-impl HasTextures for DemoLevel {
- type Texture = Texture;
-
- fn get_texture(&self, idx: TextureRef) -> Option<&Self::Texture> {
- self.textures.get(idx as usize)
- }
-}
-
-impl IsTexture for Texture {
- fn name(&self) -> &str {
- &self.name
- }
-}
-
-impl<'a> HasVisData<'a> for DemoLevel {
- type Faces = std::ops::Range<FaceRef>;
-
- fn get_visible(
- &'a self,
- _transform: &Transform,
- _settings: &CameraSettings,
- ) -> Self::Faces {
- 0..self.faces.len() as u32
- }
-}
diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs
index c5d52a9..1955b51 100644
--- a/examples/render-quad/src/main.rs
+++ b/examples/render-quad/src/main.rs
@@ -1,82 +1,29 @@
//! Renders ./example.bsp geometry: (), texture_idx: () geometry: (), texture_idx: ()
-#[macro_use]
-extern crate stockton_input_codegen;
+extern crate gfx_hal as hal;
#[macro_use]
extern crate legion;
-use std::{
- collections::BTreeMap,
- path::Path,
- sync::{Arc, RwLock},
-};
-
-use stockton_contrib::{delta_time::*, flycam::*};
-use stockton_input::{Axis, InputManager, Mouse};
-use stockton_levels::{
- parts::{data::{Geometry, Vertex}, FsResolver},
- types::Rgba,
-};
-use stockton_render::{
- level::{LevelDrawPass, LevelDrawPassConfig},
- ui::UiDrawPass,
- window::{process_window_events_system, UiState, WindowEvent, WindowFlow},
-};
-use stockton_skeleton::{
- draw_passes::ConsDrawPass, error::full_error_display, Renderer,
- Session, types::*,
- components::{CameraSettings, Transform},
-};
-
use anyhow::{Context, Result};
-use egui::{containers::CentralPanel, Frame};
use log::warn;
+use stockton_skeleton::{error::full_error_display, Renderer, Session};
use winit::{
- event::Event,
+ event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
};
-mod level;
-use level::*;
-
-type Dp<'a> = ConsDrawPass<LevelDrawPass<'a, DemoLevel>, UiDrawPass<'a>>;
+mod draw_pass;
+mod system;
+use draw_pass::*;
+use system::*;
-#[derive(InputManager, Default, Clone, Debug)]
-struct MovementInputs {
- #[axis]
- x: Axis,
-
- #[axis]
- y: Axis,
-
- #[axis]
- z: Axis,
-}
-
-impl FlycamInput for MovementInputs {
- fn get_x_axis(&self) -> &Axis {
- &self.x
- }
- fn get_y_axis(&self) -> &Axis {
- &self.y
- }
- fn get_z_axis(&self) -> &Axis {
- &self.z
- }
-}
-
-#[system]
-fn hello_world(#[resource] ui: &mut UiState) {
- CentralPanel::default()
- .frame(Frame::none())
- .show(ui.ctx(), |ui| {
- ui.heading("Hello, World!");
- });
-}
+/// Alias for our drawpass
+type Dp<'a> = ExampleDrawPass<'a>;
fn main() {
+ // Wrap for full error display
if let Err(err) = try_main() {
eprintln!("{}", full_error_display(err));
}
@@ -101,165 +48,66 @@ fn try_main() -> Result<()> {
.build(&event_loop)
.context("Error creating window")?;
+ // Grab cursor
+ window.set_cursor_visible(false);
if window.set_cursor_grab(true).is_err() {
warn!("warning: cursor not grabbed");
}
- window.set_cursor_visible(false);
-
- // TODO: Parse the map file
- let map = Arc::new(RwLock::new(DemoLevel {
- faces: vec![Face {
- geometry: Geometry::Vertices(
- Vertex {
- position: Vector3::new(-128.0, 128.0, 128.0),
- tex: Vector2::new(0.0, 0.0),
- color: Rgba::from_slice(&[0, 0, 0, 1]),
- },
- Vertex {
- position: Vector3::new(-128.0, -128.0, 128.0),
- tex: Vector2::new(0.0, 1.0),
- color: Rgba::from_slice(&[0, 0, 0, 1]),
- },
- Vertex {
- position: Vector3::new(128.0, 128.0, 128.0),
- tex: Vector2::new(1.0, 0.0),
- color: Rgba::from_slice(&[0, 0, 0, 1]),
- },
- ),
- texture_idx: 0,
- }]
- .into_boxed_slice(),
- textures: vec![Texture {
- name: "example_texture".to_string(),
- }]
- .into_boxed_slice(),
- }));
-
- // Create the UI State
- let ui = UiState::default();
-
- // Create the input manager
- let manager = {
- use stockton_input::InputMutation::*;
- use MovementInputsFields::*;
-
- let mut actions = BTreeMap::new();
- actions.insert(17, (Z, PositiveAxis)); // W
- actions.insert(30, (X, NegativeAxis)); // A
- actions.insert(31, (Z, NegativeAxis)); // S
- actions.insert(32, (X, PositiveAxis)); // D
- actions.insert(29, (Y, NegativeAxis)); // Ctrl
- actions.insert(57, (Y, PositiveAxis)); // Space
-
- MovementInputsManager::new(actions)
- };
-
- // Load everything into the session
+ // Our game world
let mut session = Session::new(move |schedule| {
- schedule
- .add_system(update_deltatime_system())
- .add_system(process_window_events_system::<MovementInputsManager>())
- .flush()
- .add_system(hello_world_system())
- .add_system(flycam_move_system::<MovementInputsManager>());
+ schedule.add_system(mutate_state_system());
});
- session.resources.insert(map.clone());
- session.resources.insert(manager);
- session.resources.insert(Timing::default());
- session.resources.insert(Mouse::default());
- session.resources.insert(ui);
-
- // Add our player entity
- let player = session.world.push((
- Transform {
- position: Vector3::new(0.0, 0.0, 0.0),
- rotation: Vector3::new(0.0, 0.0, 0.0),
- },
- CameraSettings {
- far: 1024.0,
- fov: 90.0,
- near: 0.1,
- },
- FlycamControlled::new(512.0, 400.0),
- ));
+ // An entity to keep track of our state
+ let state_ent = session.world.push((ExampleState::default(),));
// Create the renderer
- let renderer = Renderer::<Dp<'static>>::new(
- &window,
- &mut session,
- (
- LevelDrawPassConfig {
- active_camera: player,
- tex_resolver: FsResolver::new(Path::new("./examples/render-quad/textures"), map),
- },
- (),
- ),
- )?;
-
- let new_control_flow = Arc::new(RwLock::new(ControlFlow::Poll));
- let (window_flow, tx) = WindowFlow::new(new_control_flow.clone());
- session.resources.insert(window_flow);
-
- // Populate the initial UI state
- {
- let ui = &mut session.resources.get_mut::<UiState>().unwrap();
- ui.populate_initial_state(&renderer);
- }
+ let renderer =
+ Renderer::<Dp<'static>>::new(&window, &mut session, ExampleDrawPassConfig { state_ent })?;
+ // We'll be moving it in/out of here, so we need an Option for safety.
let mut renderer = Some(renderer);
// Done loading - This is our main loop.
// It just communicates events to the session and continuously ticks
- event_loop.run(move |event, _, flow| {
- match event {
- Event::MainEventsCleared => {
- window.request_redraw();
- }
- Event::RedrawRequested(_) => {
- session.do_update();
- let r = renderer.take().unwrap();
- match r.render(&session) {
- Ok(r) => {
- renderer = Some(r);
- }
- Err(e) => {
- println!("Error drawing: {}", full_error_display(e));
-
- // TODO: Not really sound
- *(new_control_flow.write().unwrap()) = ControlFlow::Exit;
- }
+ event_loop.run(move |event, _, flow| match event {
+ Event::MainEventsCleared => {
+ window.request_redraw();
+ }
+ Event::RedrawRequested(_) => {
+ session.do_update();
+
+ // Render
+ let r = renderer.take().unwrap();
+ match r.render(&session) {
+ Ok(r) => {
+ renderer = Some(r);
}
- }
- _ => {
- if let Some(we) = WindowEvent::from(&event) {
- tx.send(we).unwrap();
+ Err(e) => {
+ println!("Error drawing: {}", full_error_display(e));
- if let WindowEvent::SizeChanged(_, _) = we {
- let r = renderer.take().unwrap();
- match r.recreate_surface(&session) {
- Ok(r) => {
- renderer = Some(r);
- }
- Err(e) => {
- println!("Error resizing: {}", full_error_display(e));
-
- // TODO: Not really sound
- *(new_control_flow.write().unwrap()) = ControlFlow::Exit;
- }
- }
- }
+ *flow = ControlFlow::Exit;
}
}
}
+ Event::WindowEvent {
+ window_id: _,
+ event: WindowEvent::Resized(_),
+ } => {
+ // (Attempt) resize
+ let r = renderer.take().unwrap();
+ match r.recreate_surface(&session) {
+ Ok(r) => {
+ renderer = Some(r);
+ }
+ Err(e) => {
+ println!("Error resizing: {}", full_error_display(e));
- // Update the control flow if the session has requested it.
- {
- let new_control_flow = new_control_flow.read().unwrap();
- if *new_control_flow != *flow {
- *flow = *new_control_flow;
+ *flow = ControlFlow::Exit;
+ }
}
- };
+ }
+ _ => (),
});
}
diff --git a/examples/render-quad/src/system.rs b/examples/render-quad/src/system.rs
new file mode 100644
index 0000000..3342774
--- /dev/null
+++ b/examples/render-quad/src/system.rs
@@ -0,0 +1,73 @@
+//! An example system that just alternates the colours of our triangle
+
+use stockton_skeleton::types::Vector3;
+
+/// RGB Channels
+#[derive(Debug, Clone, Copy)]
+enum ColorChannel {
+ Red,
+ Green,
+ Blue,
+}
+
+/// A component for our entity.
+#[derive(Debug, Clone, Copy)]
+pub struct ExampleState {
+ channel: ColorChannel,
+ falling: bool,
+ col_val: Vector3,
+}
+
+impl ExampleState {
+ pub fn color(&self) -> Vector3 {
+ self.col_val
+ }
+}
+
+impl Default for ExampleState {
+ fn default() -> Self {
+ Self {
+ channel: ColorChannel::Red,
+ falling: true,
+ col_val: Vector3::new(1.0, 1.0, 1.0),
+ }
+ }
+}
+
+/// The speed at which we change colour
+const TRANSITION_SPEED: f32 = 0.1;
+
+/// Keep changing the colour of any ExampleStates in our world.
+#[system(for_each)]
+pub fn mutate_state(state: &mut ExampleState) {
+ // Which value we're changing
+ let val = match state.channel {
+ ColorChannel::Red => &mut state.col_val.x,
+ ColorChannel::Green => &mut state.col_val.y,
+ ColorChannel::Blue => &mut state.col_val.z,
+ };
+
+ if state.falling {
+ *val -= TRANSITION_SPEED;
+
+ // Fall, then rise
+ if *val <= 0.0 {
+ *val = 0.0;
+ state.falling = false;
+ }
+ } else {
+ *val += TRANSITION_SPEED;
+
+ if *val >= 1.0 {
+ *val = 1.0;
+
+ // Rather than going back to falling, go to the next channel
+ state.falling = true;
+ state.channel = match state.channel {
+ ColorChannel::Red => ColorChannel::Green,
+ ColorChannel::Green => ColorChannel::Blue,
+ ColorChannel::Blue => ColorChannel::Red,
+ }
+ }
+ }
+}