gravitron 0.1.2

A GameEngine based on an ECS and Vulkan
Documentation
use ash::vk;
use glam as g;
use vulkan::{config::{EngineConfig, VulkanConfig}, Vulkan};
use winit::{
  application::ApplicationHandler,
  dpi::{LogicalSize, Size},
};

use crate::camera::Camera;
use crate::aetna::Aetna;
use crate::model::{Model, InstanceData};

mod camera;
mod debug;
mod surface;
mod swapchain;
mod queues;
mod pipeline;
mod pools;
mod buffer;
mod model;
mod aetna;
mod light;
mod vulkan;
mod utils;

fn main() -> Result<(), Box<dyn std::error::Error>> {
  let event_loop = winit::event_loop::EventLoop::new().unwrap();
  event_loop
    .run_app(&mut App {
      aetna: None,
      frame: 0,
      start_time: std::time::Instant::now(),
      handle: 0,
      camera: Camera::builder().build(),
    })
    .unwrap();

  Ok(())
}

struct App {
  aetna: Option<Aetna>,
  frame: u64,
  start_time: std::time::Instant,
  handle: usize,
  camera: Camera,
}

impl ApplicationHandler for App {
  fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
    let window_attributes = winit::window::WindowAttributes::default()
      .with_title("Vulkan")
      .with_inner_size(Size::Logical(LogicalSize::new(800.0, 600.0)));

    let window = event_loop.create_window(window_attributes).unwrap();

    let engine_config = EngineConfig::default().set_debug(true).set_debug_log_level(utils::LogLevel::Verbose);
    let mut v = Vulkan::init(VulkanConfig::default().set_engine_config(engine_config), &window).unwrap();
    v.destroy();
    
    let mut aetna = Aetna::init(window).unwrap();

    let mut cube = Model::cube();

    let mut lights = light::LightManager::default();
    lights.add_light(light::DirectionalLight {
      direction: g::Vec3::new(-1.0, 1.0, 0.0),
      illuminance: [10.0, 10.0, 10.0],
    });
    lights.add_light(light::PointLight {
      position: g::Vec3::new(1.5, 0.0, 0.0),
      illuminance: [10.0, 10.0, 10.0],
    });
    lights.add_light(light::PointLight {
      position: g::Vec3::new(1.5, 0.2, 0.0),
      illuminance: [5.0, 5.0, 5.0],
    });
    lights.add_light(light::PointLight {
      position: g::Vec3::new(1.6, -0.2, 0.1),
      illuminance: [5.0, 5.0, 5.0],
    });
    lights.update_buffer(&mut aetna.light_buffer).unwrap();

    self.handle = cube.insert_visibly(InstanceData::new(
      g::Mat4::from_translation(g::Vec3::new(0.5, 0.0, 0.0))
        * g::Mat4::from_scale(g::Vec3::from_array([0.5, 0.01, 0.01])),
      [1.0, 0.5, 0.5],
      0.0,
      1.0
    ));
    self.handle = cube.insert_visibly(InstanceData::new(
      g::Mat4::from_translation(g::Vec3::new(0.0, 0.5, 0.0))
        * g::Mat4::from_scale(g::Vec3::from_array([0.01, 0.5, 0.01])),
      [0.5, 1.0, 0.5],
      0.0,
      1.0
    ));
    self.handle = cube.insert_visibly(InstanceData::new(
      g::Mat4::from_translation(g::Vec3::new(0.0, 0.0, 0.5))
        * g::Mat4::from_scale(g::Vec3::from_array([0.01, 0.01, 0.5])),
      [0.5, 0.5, 1.0],
      0.0,
      1.0
    ));

    let mut ico = Model::sphere(3);
    for i in 0..10 {
      for j in 0..10 {
        self.handle = ico.insert_visibly(InstanceData::new(
          g::Mat4::from_scale(g::Vec3::from_array([0.5, 0.5, 0.5])) * g::Mat4::from_translation(g::Vec3::new(i as f32 - 5.0, j as f32 - 5.0, 10.0)),
          [0.0, 0.0, 0.8],
          i as f32 * 0.1,
          j as f32 * 0.1
        ));
      }
    }

    cube
      .update_vertex_buffer(&mut aetna.allocator, &aetna.device)
      .unwrap();
    cube
      .update_index_buffer(&mut aetna.allocator, &aetna.device)
      .unwrap();
    cube
      .update_instance_buffer(&mut aetna.allocator, &aetna.device)
      .unwrap();

    ico
      .update_vertex_buffer(&mut aetna.allocator, &aetna.device)
      .unwrap();
    ico
      .update_index_buffer(&mut aetna.allocator, &aetna.device)
      .unwrap();
    ico
      .update_instance_buffer(&mut aetna.allocator, &aetna.device)
      .unwrap();

    let models = vec![cube, ico];
    aetna.models = models;

    self.aetna = Some(aetna);
  }

  fn window_event(
    &mut self,
    _event_loop: &winit::event_loop::ActiveEventLoop,
    _window_id: winit::window::WindowId,
    event: winit::event::WindowEvent,
  ) {
    match event {
      winit::event::WindowEvent::CloseRequested => {
        std::mem::drop(self.aetna.take());
      }
      winit::event::WindowEvent::KeyboardInput {
        device_id: _,
        event:
          winit::event::KeyEvent {
            logical_key: key,
            state: winit::event::ElementState::Pressed,
            ..
          },
        is_synthetic: _,
      } => match key.as_ref() {
        winit::keyboard::Key::Character("w") => {
          self.camera.move_forward(0.05);
        }
        winit::keyboard::Key::Character("s") => {
          self.camera.move_backward(0.05);
        }
        winit::keyboard::Key::Character("a") => {
          self.camera.move_left(0.05);
        }
        winit::keyboard::Key::Character("d") => {
          self.camera.move_right(0.05);
        }
        winit::keyboard::Key::Character("q") => {
          self.camera.move_up(0.05);
        }
        winit::keyboard::Key::Character("e") => {
          self.camera.move_down(0.05);
        }
        winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowUp) => {
          self.camera.turn_up(0.05);
        }
        winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowDown) => {
          self.camera.turn_down(0.05);
        }
        winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowLeft) => {
          self.camera.turn_left(0.05);
        }
        winit::keyboard::Key::Named(winit::keyboard::NamedKey::ArrowRight) => {
          self.camera.turn_right(0.05);
        }
        _ => {}
      },
      winit::event::WindowEvent::RedrawRequested => {
        if let Some(aetna) = self.aetna.as_mut() {
          unsafe {
            aetna
              .device
              .wait_for_fences(
                &[aetna.swapchain.may_begin_drawing[aetna.swapchain.current_image]],
                true,
                std::u64::MAX,
              )
              .expect("Unable to wait for fences");

            aetna
              .device
              .reset_fences(&[aetna.swapchain.may_begin_drawing[aetna.swapchain.current_image]])
              .expect("Unable to reset fences");
          }

          self.camera.update_buffer(&mut aetna.uniform_buffer).unwrap();

          for m in &mut aetna.models {
            m.update_instance_buffer(&mut aetna.allocator, &aetna.device)
              .unwrap();
          }

          aetna
            .update_command_buffer(aetna.swapchain.current_image)
            .expect("Unable to update command buffer");

          let (image_index, _) = unsafe {
            aetna
              .swapchain
              .loader
              .acquire_next_image(
                aetna.swapchain.swapchain,
                std::u64::MAX,
                aetna.swapchain.image_available[aetna.swapchain.current_image],
                vk::Fence::null(),
              )
              .expect("Unable to acquire next image")
          };

          let semaphore_available =
            [aetna.swapchain.image_available[aetna.swapchain.current_image]];
          let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT];
          let semaphore_render_finished =
            [aetna.swapchain.render_finished[aetna.swapchain.current_image]];
          let command_buffer = [aetna.command_buffers[aetna.swapchain.current_image]];

          let submit_info = [vk::SubmitInfo::default()
            .wait_semaphores(&semaphore_available)
            .wait_dst_stage_mask(&wait_stages)
            .command_buffers(&command_buffer)
            .signal_semaphores(&semaphore_render_finished)];

          unsafe {
            aetna
              .device
              .queue_submit(
                aetna.queues.graphics,
                &submit_info,
                aetna.swapchain.may_begin_drawing[aetna.swapchain.current_image],
              )
              .expect("Unable to submit queue");
          }

          let swapchains = [aetna.swapchain.swapchain];
          let image_indices = [image_index];
          let present_info = vk::PresentInfoKHR::default()
            .wait_semaphores(&semaphore_render_finished)
            .swapchains(&swapchains)
            .image_indices(&image_indices);
          unsafe {
            aetna
              .swapchain
              .loader
              .queue_present(aetna.queues.graphics, &present_info)
              .expect("Unable to queue present");
          }

          aetna.swapchain.current_image =
            (aetna.swapchain.current_image + 1) % aetna.swapchain.amount_of_images as usize;

          self.frame += 1;
          let elapsed = self.start_time.elapsed();
          if elapsed.as_secs() >= 1 {
            println!("FPS: {}", self.frame);
            self.frame = 0;
            self.start_time = std::time::Instant::now();
          }

          let max_frames = 165;
          let frame_time = std::time::Duration::from_secs(1) / max_frames;
          let elapsed = self.start_time.elapsed();
          if elapsed < frame_time * self.frame as u32 {
            std::thread::sleep(frame_time * self.frame as u32 - elapsed);
          }
        }
      }
      _ => {}
    }
  }

  fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {
    if let Some(aetna) = self.aetna.as_mut() {
      aetna.window.request_redraw();
    }
  }
}