# Quickstart Get a window with a rotating cube on screen in ~30 lines. The full version with comments is in the [`simple`](../../lib/examples/meshes/simple.rs) example (2D quad, unlit) and [`cube`](../../lib/examples/meshes/cube.rs) (3D cube, lit). ## 1. Add the dependency ```toml # Cargo.toml [dependencies] wsg-lib = { path = "../lib" } glam = "0.29" # Vec3/Quat — re-exported but you need it in your own code winit = "0.30" # KeyCode/MouseButton for the input (only if you use app.input) ``` > The workspace pins `glam 0.29` and `winit 0.30`; match these versions to avoid > type mismatches. ## 2. Implement `AppHandler` Three mandatory methods (`setup`, `update`, `render`) and an optional event hook. ```rust use glam::{Quat, Vec3}; use winit::event::MouseButton; use winit::keyboard::KeyCode; use wsg_lib::camera::CameraController; use wsg_lib::prelude::*; struct MyHandler { camera: CameraController, } impl AppHandler for MyHandler { fn new() -> Self { Self { camera: CameraController::default() } } fn setup(&mut self, app: &mut App) -> Result<(), String> { // A cube (primitive) + the standard material. app.scene.create_mesh("cube_mesh", cube(1.0), Some("cube_mat"))?; app.scene.add_entity("cube", "cube_mesh")?; // A warm directional light + shadows on it. app.scene .add_directional_light(Vec3::new(1.0, 1.2, 1.0).normalize(), [1.0, 0.98, 0.92], 1.5)?; app.scene.set_shadow_caster(Some(0)); Ok(()) } fn update(&mut self, app: &mut App) { // Spin the cube. let mut tf = *app.scene.entity_transform("cube").unwrap(); tf.rotation = Quat::from_rotation_y(self.t) * tf.rotation; app.scene.set_entity_transform("cube", tf); self.t += 0.02; // Camera: orbit (left-drag), zoom (wheel), reset (R), presets (1/2/3). let (dx, dy) = app.input.mouse_delta(); if app.input.mouse_button_held(MouseButton::Left) { self.camera.orbit(dx, dy); } let (_, sy) = app.input.scroll_delta(); self.camera.zoom(sy); if app.input.key_pressed(KeyCode::KeyR) { self.camera.yaw = 0.6; self.camera.pitch = 0.35; self.camera.distance = 6.5; } self.camera.apply_to(app.scene.camera_mut()); } fn render(&mut self, app: &mut App) -> Result<(), String> { // Default implementation: renders the whole scene. Override only for custom passes. app.renderer().render_scene(app.context()) } } ``` ## 3. Build the app ```rust fn main() -> Result<(), Box> { let app = AppBuilder::new() .title("My WSG app") .size(1024, 768) .with_culling(true) // optional: skip off-screen entities .with_shadows() // optional: enable shadow mapping .build() .await?; let mut handler = MyHandler::new(); app.run(&mut handler).await?; Ok(()) } ``` `AppBuilder` methods you will use early: | Method | Purpose | |--------|---------| | `.title(…)` / `.size(w, h)` | Window | | `.with_vsync(false)` / `.with_frame_limit(n)` | Frame pacing (vsync off + 144 fps cap in the `demo`) | | `.with_culling(true)` | Opt-in frustum culling (see [GPU-driven](cameras/gpu-driven.md)) | | `.with_shadows()` | Opt-in shadow mapping (see [Shadows](lights/shadows.md)) | | `.with_hdr(ToneMapper::Aces)` | Opt-in HDR + tone mapping (see [HDR](effects/hdr.md)) | ## 4. Run it ```sh cargo run -p wsg-lib --example cube # the reference "hello world" of the engine ``` Controls (in the `cube`/`demo` examples): **left-drag** orbit, **wheel** zoom, **R** reset camera, **1/2/3** view presets, **H** help overlay, **Esc** quit. ## 5. Where to go next - [Meshes](meshes/meshes.md) — entities, transforms, custom geometries - [Materials & textures](meshes/materials.md) — diffuse textures, unlit mode - [Lights](lights/lights.md) — point/spot lights, ambient - [Camera & input](cameras/camera-input.md) — the full input API - [GPU-driven](cameras/gpu-driven.md) — culling, LOD, debugging the GPU path ## Links - [User README](README.md) · [Meshes](meshes/meshes.md) · [Examples](examples.md) - [Root README](../../README.md) · [FRAME_LOOP](../tech/FRAME_LOOP.md)