/ let-engine / examples / client / circle.rs
circle.rs
  1  //! Simple circle scene.
  2  //!
  3  //! # Controls
  4  //! - Scroll: increase / decrease number of sides
  5  
  6  use glam::Vec2;
  7  use let_engine::prelude::*;
  8  
  9  use gpu::{VulkanTypes, model::ModelId};
 10  
 11  // Limit of corners
 12  const MAX_SIDES: usize = 1000;
 13  
 14  fn main() {
 15      // Log messages
 16      simple_logger::SimpleLogger::new()
 17          .with_level(log::LevelFilter::Debug)
 18          .init()
 19          .unwrap();
 20  
 21      // First you make a builder containing the description of the window.
 22      let window_builder = WindowBuilder::new()
 23          .inner_size(uvec2(1280, 720))
 24          .title(env!("CARGO_CRATE_NAME"));
 25  
 26      // Now we run the engine
 27      let_engine::start(
 28          EngineSettings::default()
 29              .window(window_builder)
 30              .gpu(gpu::GpuSettings {
 31                  present_mode: gpu::PresentMode::Fifo,
 32                  ..Default::default()
 33              }),
 34          Game::new,
 35      )
 36      .unwrap();
 37  }
 38  
 39  /// Makes a game struct containing
 40  struct Game {
 41      model: ModelId<Vec2>,
 42      sides: u32,
 43  }
 44  
 45  impl Game {
 46      /// Constructor for this scene.
 47      pub fn new(context: EngineContext) -> Result<Self, ()> {
 48          {
 49              let root_view = context.scene.root_view_mut();
 50  
 51              // next we set the view of the game scene zoomed out and not stretchy.
 52              root_view.transform = Transform::with_size(Vec3::splat(2.0));
 53              root_view.scaling = CameraScaling::Circle;
 54          }
 55  
 56          // First we get the root layer where the scene will be simulated on.
 57          let root_layer = context.scene.root_layer();
 58  
 59          // Create a "circle" model with a default amount of sides.
 60          let sides = 15;
 61          let mut circle_model = circle!(sides, BufferAccess::Pinned(PreferOperation::Write));
 62  
 63          // Raise maximum vertices and indices for growable model
 64  
 65          // 1 vertex per side including `+ 1` for the center vertex
 66          circle_model.set_max_vertices(MAX_SIDES + 1);
 67          // Each side is 1 vertex, so 3 corners.
 68          circle_model.set_max_indices(MAX_SIDES * 3);
 69  
 70          // Load circle model to the GPU.
 71          let circle_model = context.gpu.load_model(&circle_model).unwrap();
 72  
 73          let default_material = context
 74              .gpu
 75              .load_material::<Vec2>(&Material::new_default())
 76              .unwrap();
 77  
 78          let color_buffer = context
 79              .gpu
 80              .load_buffer(&Buffer::from_data(
 81                  buffer::BufferUsage::Uniform,
 82                  BufferAccess::Fixed,
 83                  Color::from_rgb(1.0, 0.3, 0.5),
 84              ))
 85              .unwrap();
 86  
 87          let circle_appearance = AppearanceBuilder::<VulkanTypes, Vec2>::default()
 88              .model(circle_model)
 89              .material(default_material)
 90              .descriptors(&[
 91                  (Location::new(0, 0), Descriptor::Mvp),
 92                  (Location::new(1, 0), Descriptor::buffer(color_buffer)),
 93              ])
 94              .build(&context.gpu)
 95              .unwrap();
 96  
 97          // Makes the circle in the middle.
 98          let circle = ObjectBuilder::with_appearance(circle_appearance);
 99  
100          // Initializes the object to the layer
101          context.scene.add_object(root_layer.id(), circle).unwrap();
102  
103          Ok(Self {
104              model: circle_model,
105              sides,
106          })
107      }
108  }
109  
110  /// Implement the Game trait into the Game struct.
111  impl let_engine::Game for Game {
112      // Exit when the X button on the window is pressed.
113      fn window(&mut self, context: EngineContext, event: WindowEvent) -> Result<(), ()> {
114          match event {
115              WindowEvent::CloseRequested => context.exit(),
116              WindowEvent::MouseWheel(ScrollDelta::LineDelta(delta)) => {
117                  // Add or subtract side depending on the delta of the scroll
118                  if delta.y > 0.0 {
119                      if self.sides < MAX_SIDES as u32 {
120                          self.sides += 1;
121                          log::info!("(+) Corners: {}", self.sides);
122                      }
123                  } else if self.sides > 2 {
124                      self.sides -= 1;
125                      log::info!("(-) Corners: {}", self.sides);
126                  }
127  
128                  // Generate new circle model and write it to the GPU
129                  let new_model = circle!(self.sides);
130                  // Index model from backend index implementation directly
131                  context.gpu[self.model].write_model(&new_model).unwrap();
132              }
133              _ => (),
134          }
135          Ok(())
136      }
137  
138      // Exit when the escape key is pressed.
139      fn input(&mut self, context: EngineContext, event: InputEvent) -> Result<(), ()> {
140          if let InputEvent::KeyboardInput { input } = event
141              && let ElementState::Pressed = input.state
142              && let Key::Named(NamedKey::Escape) = input.key
143          {
144              context.exit();
145          }
146          Ok(())
147      }
148  }