105 lines
3.7 KiB
Markdown
105 lines
3.7 KiB
Markdown
# pbio: simple pixel buffer and I/O system
|
||
|
||
Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer (winit + wgpu) und Maus/Tastatureingaben.
|
||
|
||
pbio nimmt einen Bytearray, interpretiert diesen als 8-Bit-Pixelbuffer und rendert ihn auf ein Fenster. Optional mit Letterbox für ein festes Seitenverhältnis.
|
||
|
||
## Integration
|
||
|
||
pbio ist nicht auf crates.io veröffentlicht. Das Crate kann direkt aus dem Git-Repository eingebunden werden:
|
||
|
||
```toml
|
||
[dependencies]
|
||
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git" }
|
||
```
|
||
|
||
pbio braucht keine System-Libraries zum Bauen — winit lädt X11/Wayland zur Laufzeit per dlopen. Zur Laufzeit reichen aktuelle Grafiktreiber mit Vulkan-, Metal- oder DX12-Unterstützung für wgpu.
|
||
|
||
## Verwendung
|
||
|
||
```rust
|
||
use pbio::{Event, Key, Platform, PlatformConfig};
|
||
|
||
fn main() {
|
||
let mut p = Platform::new(PlatformConfig {
|
||
title: "Demo".into(),
|
||
window_size: (1024, 768),
|
||
framebuffer_size: (320, 240),
|
||
..Default::default()
|
||
});
|
||
|
||
while !p.should_close() {
|
||
p.poll_events(None);
|
||
|
||
// drain_events() borrowt &mut self → close erst nach dem Loop setzen.
|
||
let mut close = false;
|
||
for ev in p.drain_events() {
|
||
if let Event::Key { key: Key::Escape, pressed: true, .. } = ev {
|
||
close = true;
|
||
}
|
||
}
|
||
if close { p.request_close(); }
|
||
|
||
let (w, _h) = p.framebuffer_size();
|
||
let fb = p.framebuffer_mut();
|
||
for (i, px) in fb.iter_mut().enumerate() {
|
||
*px = ((i as u32 % w) ^ (i as u32 / w)) as u8;
|
||
}
|
||
|
||
p.present();
|
||
}
|
||
}
|
||
```
|
||
|
||
## API-Überblick
|
||
|
||
- `PlatformConfig` – Titel, Fenster-/Framebuffer-Größe, optionales
|
||
`aspect_ratio` (Letterbox), Palette, VSync, `mouse_visible`, `mouse_capture`.
|
||
Default = 800×600 Fenster, 320×240 Framebuffer, 4:3, `palette::RGB332`.
|
||
- `Platform::new(cfg)` – Fenster + GPU-Pipeline aufsetzen.
|
||
- `poll_events(timeout)` – GLFW-Events einlesen (`None` = blockierend).
|
||
- `drain_events()` – Iterator über `Event` (`Key`, `Char`, `MouseMove`,
|
||
`MouseBtn`, `Resize`, `CloseRequested`).
|
||
- `framebuffer_mut()` – `&mut [u8]` der Länge `w*h`, je Pixel ein Paletten-Index.
|
||
- `palette_mut()` / `palette()` – `&mut [[u8; 3]; 256]` bzw. `&[...]`; Schreibzugriff
|
||
markiert die Palette dirty, der nächste `present()` lädt sie hoch.
|
||
- `present()` – Frame auf das Fenster bringen.
|
||
- `should_close()` / `request_close()`.
|
||
- `set_mouse_visible(bool)` – Cursor anzeigen oder verstecken (freie Bewegung).
|
||
- `set_mouse_capture(bool)` – Cursor einsperren + verstecken (`CursorMode::Disabled`),
|
||
liefert unbegrenzte Bewegungsdeltas. Impliziert `mouse_visible = false`.
|
||
- `mouse_delta() -> (f32, f32)` – akkumulierte Mausbewegung seit dem letzten Aufruf
|
||
in Framebuffer-Pixeln (gleiche Skala wie `MouseMove`), danach genullt. Nützlich
|
||
für FPS-Kamerasteuerung.
|
||
|
||
### Cursor-Modi
|
||
|
||
| `mouse_visible` | `mouse_capture` | GLFW-Modus | Einsatz |
|
||
|---|---|---|---|
|
||
| `true` | `false` | Normal | Standard-UI |
|
||
| `false` | `false` | Hidden | Eigener Cursor im Framebuffer |
|
||
| `*` | `true` | Disabled | FPS-Kamera, unbegrenzte Deltas |
|
||
|
||
```rust
|
||
// FPS-Kamera
|
||
p.set_mouse_capture(true);
|
||
let (dx, dy) = p.mouse_delta();
|
||
yaw += dx * sensitivity;
|
||
pitch += dy * sensitivity;
|
||
```
|
||
|
||
## Paletten
|
||
|
||
`palette::RGB332` ist als Default eingebaut. Eigene Paletten sind ein
|
||
`[[u8; 3]; 256]` (RGB pro Index) und werden über `PlatformConfig::palette`
|
||
übergeben.
|
||
|
||
Zur Laufzeit kann die Palette über `palette_mut()` modifiziert werden — einzelne
|
||
Einträge oder die komplette Tabelle. Der Upload passiert automatisch im nächsten
|
||
`present()`:
|
||
|
||
```rust
|
||
p.palette_mut()[42] = [255, 0, 0]; // Slot 42 → rot
|
||
p.present();
|
||
```
|