4.2 KiB
4.2 KiB
Quickstart
Get a window with a rotating cube on screen in ~30 lines. The full version with comments is
in the simple example (2D quad, unlit) and
cube (3D cube, lit).
1. Add the dependency
# 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.29andwinit 0.30; match these versions to avoid type mismatches.
2. Implement AppHandler
Three mandatory methods (setup, update, render) and an optional event hook.
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
fn main() -> Result<(), Box<dyn std::error::Error>> {
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) |
.with_shadows() |
Opt-in shadow mapping (see Shadows) |
.with_hdr(ToneMapper::Aces) |
Opt-in HDR + tone mapping (see HDR) |
4. Run it
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 — entities, transforms, custom geometries
- Materials & textures — diffuse textures, unlit mode
- Lights — point/spot lights, ambient
- Camera & input — the full input API
- GPU-driven — culling, LOD, debugging the GPU path