roast2d 0.3.4

Roast2D is a homebrew 2D game engine
Documentation
# Repository Guidelines

## Project Structure & Module Organization
The workspace centers on the `roast2d` crate in `src/lib.rs`, which re-exports the engine implementation from `crates/roast2d_internal`. Supporting crates live under `crates/`, including `roast2d_macro` for proc-macros and `roast2d_dylib` for optional dynamic linking. Runtime examples sit in `examples/` (e.g., `breakout.rs` and the `hotreload` member), while shared assets reside in `assets/`. Benchmarks live under `benches/`, and maintenance scripts are collected in `tools/`.

### Rendering Architecture (updated)
- Core rendering lives under `crates/roast2d_internal/src/renderer/` and is grouped by domain:
  - `render2d/` — 2D pipelines (`default.rs`, `line.rs`, `circle.rs`), batching (`render.rs`), and the 2D runner (`runner.rs`).
  - `render3d/` — 3D mesh draw types (`draw3d.rs`) and the 3D runner (`runner.rs`).
  - `post/` — post-process pipelines (`screen.rs`, `retro.rs`).
  - `driver.rs``RenderDriver`, which orchestrates a frame: clears the offscreen once, records 3D, then 2D world/UI, then runs post.
  - `traits.rs`, `resource.rs` — shared shaders traits and texture cache/upload helpers.

- The high-level engine state for presentation is in `crates/roast2d_internal/src/render.rs`.

### Public imports used by examples
- 3D draws: `use roast2d::renderer::shader3d::draw3d::Draw3D;`
- 2D default shaders (for custom shaders/examples):
  `use roast2d::renderer::shader2d::default::DefaultShader;`
- Shader traits: `use roast2d::renderer::traits::{SpriteShader, SpritePipeline};`
- Post-process: `use roast2d::renderer::post::RetroShader;`

## Build, Test, and Development Commands
- `cargo build --workspace` compiles all crates against the pinned stable toolchain in `rust-toolchain.toml`.
- `cargo run --example breakout` launches the canonical gameplay demo; swap `breakout` with any file in `examples/`.
- `tools/check.sh` runs `cargo fmt`, the full workspace test suite, doc tests, and a warning-free `cargo clippy`.
- For rapid iteration on features, `cargo test --workspace --all-targets` and `cargo clippy --workspace --all-targets --all-features -- --deny warnings` should stay green.

## Coding Style & Naming Conventions
Rust code follows `cargo fmt` defaults (4-space indent, trailing commas, snake_case modules). Types and traits use PascalCase, functions snake_case, and constants SCREAMING_SNAKE_CASE. Keep public APIs documented with `///` comments mirroring existing modules. Prefer explicit module files within `crates/roast2d_internal/src/` and gate optional features behind `cfg` flags consistent with `Cargo.toml`.

## Testing Guidelines
Unit tests typically live beside implementations under `#[cfg(test)]` modules (see `crates/roast2d_internal/src/sat.rs`). Add focused cases alongside new systems, and mirror example-driven scenarios when adding rendering behaviors. Run `cargo test --workspace --doc` to catch documentation regressions. Performance-sensitive changes should update or extend the Criterion benchmark at `benches/sat_collision.rs` via `cargo bench --bench sat_collision`.

## Commit & Pull Request Guidelines
Recent history favors concise, imperative subjects (e.g., “Simplify draw_line interface”). Group related work per commit and include rationale in the body when behavior shifts. Before opening a PR, ensure `tools/check.sh` passes, explain user-facing impact, and link any tracking issues. Visual or rendering tweaks benefit from attaching screenshots or short clips so reviewers can validate results quickly.
## Transform Model & 2D sizing (important)
- Transform now contains only `pos: Vec3`, `scale: Vec2`, and `rotation: Quat`.
  - `with_angle(f32)` remains and sets `rotation = Quat::from_rotation_z(angle)`.
  - `matrix()` composes translation, rotation, and XY scale (Z scale is 1.0 by default).
- Size is not stored on Transform. For 2D quads:
  - Base size is on the `Sprite` (`sprite.size`) or derived from `Sprite.src` when set.
  - A `Draw` carries `quad_size` internally; batching uses `quad_size * transform.scale`.
- Bounds helpers:
  - Use `transform.bounds_with_anchor_and_size(anchor, size)` where `size` is the final quad size in pixels (after any per-sprite/base-size decisions) and before scale; the method applies `transform.scale` externally if you pass a scaled size.

## 2D Drawing API changes
- `draw_rect` now requires an explicit size: `draw_rect(color: Color, size: Vec2, anchor: Option<Vec2>, transform: Transform)`.
  - Pass the desired pixel size in `size`; Transform controls position/rotation/scale only.
- Nine-slice helper (`utils/nine_slice.rs`) takes an explicit `size: Vec2` and sets `sprite.size` for each drawn segment.
- UI helpers set sprite sizes explicitly before draw when a specific pixel size is desired.

## Render flow & clear/load
- Offscreen is cleared once per frame at the top of `RenderDriver::record_frame`.
- 2D and 3D passes use `LoadOp::Load` for color (depth clears in 3D); post clears the swapchain to black to avoid trails under transparency.

## Quick checks
- `cargo check --workspace` and `cargo check --examples` should be clean.
- `cargo run --example breakout` validates mixed 2D + UI; `cargo run --example cube` validates 3D path.

## Egui Integration & Usage
- Egui support is behind the `egui` feature:
  - Enable at the top level with `--features egui` (this forwards to `roast2d_internal/egui`).
  - The prelude re-exports the `egui` crate: `use roast2d::prelude::*;` then call `egui::...` directly.
- Runtime integration:
  - The engine owns a single `egui::Context` and an `EguiSystem` wired into `WGPUPlatform` using the existing `wgpu::Device/Queue` and swapchain.
  - Egui is rendered after the 3D + 2D + post pipeline, directly on the swapchain color view, so it is not affected by post-process shaders (e.g. `RetroShader`).
  - Input events go first through `egui_winit::State::on_window_event`; only unconsumed events are forwarded to `Engine::input`.
- Immediate-style API from game code:
  - Call `Engine::egui` each frame where you want to draw UI:
    ```rust
    g.egui(|ctx, g| {
        egui::Window::new("Debug").show(ctx, |ui| {
            ui.label(format!("time: {:.2}", g.time));
            ui.label(format!("view_size: {:?}", g.view_size()));
        });
    });
    ```
  - `egui` callbacks are *per frame*; the engine clears the internal list after each frame, so you must re-register in `Scene::update`/`Scene::draw`.
- Coordinate system and sizing:
  - Egui uses winit’s logical coordinates (origin at top-left, Y down) and `egui_winit` handles `RawInput`/`screen_rect`.
  - `ScreenDescriptor.size_in_pixels` for the egui renderer is derived from the current swapchain configuration (`SurfaceConfiguration.width/height`), and `pixels_per_point` comes from `FullOutput::pixels_per_point`.
  - Roast2d world/UI 2D rendering still uses the logical view size + cameras described above; the egui overlay is visually independent of `ScaleMode`.