pub struct SceneRenderer { /* private fields */ }Expand description
Unified renderer over the CPU and GPU paths. See the crate docs.
Implementations§
Source§impl SceneRenderer
impl SceneRenderer
Sourcepub fn new<W>(window: Arc<W>, size: (u32, u32), opts: &RenderOptions) -> Self
pub fn new<W>(window: Arc<W>, size: (u32, u32), opts: &RenderOptions) -> Self
Build a renderer for window — any raw-window-handle
provider (winit, SDL, GLFW, …) in an Arc. size is the
window’s initial physical framebuffer size in pixels; thereafter
the host reports changes via Self::resize. Passing the size
explicitly keeps the facade decoupled from any one windowing
library’s size API.
Selects the GPU backend when opts.want_gpu and WGPU
initialises; otherwise the CPU backend. Never fails — a
missing/incompatible GPU silently yields the CPU path (the
message is logged to stderr).
Sourcepub fn adapter_info(&self) -> Option<&str>
pub fn adapter_info(&self) -> Option<&str>
The GPU adapter description when on the GPU backend, else
None.
Sourcepub fn set_sky_panorama(&mut self, rgba: &[u8], w: u32, h: u32)
pub fn set_sky_panorama(&mut self, rgba: &[u8], w: u32, h: u32)
Upload an equirectangular sky panorama (RGBA8, w×h) for the
GPU marcher’s sky sampling. No-op on the CPU backend, which
samples the Sky passed in each FrameParams instead.
Sourcepub fn resize(&mut self, width: u32, height: u32)
pub fn resize(&mut self, width: u32, height: u32)
Follow a window resize. CPU resizes its framebuffer lazily, so this only matters to the GPU swapchain — but it’s safe to call for both.
Sourcepub fn render(
&mut self,
scene: &mut Scene,
camera: &Camera,
frame: &FrameParams<'_>,
)
pub fn render( &mut self, scene: &mut Scene, camera: &Camera, frame: &FrameParams<'_>, )
Composite scene from camera with frame params into the
backend’s frame buffer — without presenting. The CPU backend
fills sky + runs the opticast compositor into an owned buffer;
the GPU backend uploads/refreshes the scene, runs the compute
marcher + sprite pass, and acquires (but does not present) the
swapchain frame.
Finish the frame with exactly one of present
(no overlay) or paint_egui (UI overlay).
Calling render again without finishing drops the pending frame.
Sourcepub fn draw_lines(&mut self, camera: &Camera, lines: &[Line3])
pub fn draw_lines(&mut self, camera: &Camera, lines: &[Line3])
Draw world-space Line3 segments over the frame
render composited, using that frame’s camera +
projection + depth buffer. Call after render
and before present /
paint_egui — the lines land in the
framebuffer, so a subsequent paint_egui still draws its panels
on top.
camera must be the one the last frame rendered with (the
projection is taken from that frame). Depth-tested segments
(Line3::depth_test) are occluded by nearer rendered geometry;
always-on-top segments ignore depth. See Line3 for colour /
width / blend semantics.
Sourcepub fn upload_image(&mut self, rgba: &[u8], width: u32, height: u32) -> ImageId
pub fn upload_image(&mut self, rgba: &[u8], width: u32, height: u32) -> ImageId
Upload (or replace) an RGBA8 image and return a stable ImageId
to reference it in draw_images. rgba is
row-major, width * height * 4 bytes, straight (un-premultiplied)
alpha. The texture is retained until drop_image,
so the per-frame draw call stays cheap. Sampling is
nearest-neighbour (pixel-art friendly — no blurring).
Returns ImageId(0) for malformed input (wrong byte count or a
zero dimension); such an id draws nothing.
Sourcepub fn drop_image(&mut self, id: ImageId)
pub fn drop_image(&mut self, id: ImageId)
Release a texture uploaded with upload_image.
The id must not be reused afterwards (a later upload_image may
hand the slot back out under a fresh id).
Sourcepub fn draw_images(&mut self, camera: &Camera, images: &[ImageSprite])
pub fn draw_images(&mut self, camera: &Camera, images: &[ImageSprite])
Draw 2D ImageSprites over the frame render
composited — flat textured quads placed in world space, using that
frame’s camera + projection + depth buffer. Same contract as
draw_lines: call after render
and before present / paint_egui.
UVs are perspective-correct (no affine warp on an obliquely-viewed
quad). Depth-tested sprites are occluded by nearer rendered
geometry (with a bias to avoid z-fighting on a coincident face);
the texture’s straight alpha + the ImageSprite::tint composite
over the scene. camera must be the one the last frame rendered.
Sourcepub fn project_point(
&self,
camera: &Camera,
world: [f32; 3],
) -> Option<(f32, f32)>
pub fn project_point( &self, camera: &Camera, world: [f32; 3], ) -> Option<(f32, f32)>
Project a world point to window pixel coordinates (x, y) under
the projection the last frame rendered with — the backend-correct
world → screen inverse of view_ray. None
before the first frame or for a point at/behind the camera near
plane.
Both backends honour their own projection (CPU setcamera
hx/hy/hz, GPU vertical-FOV pinhole), so hosts never reconstruct
it themselves. The returned (x, y) may fall outside [0, w) × [0, h) for points off-screen but in front of the camera.
Sourcepub fn pick_image(
&self,
camera: &Camera,
x: f64,
y: f64,
sprites: &[ImageSprite],
) -> Option<ImagePickHit>
pub fn pick_image( &self, camera: &Camera, x: f64, y: f64, sprites: &[ImageSprite], ) -> Option<ImagePickHit>
Screen→sprite pick: the nearest ImageSprite hit under window
pixel (x, y), resolving which texel was clicked. sprites is the
same list passed to draw_images (image
sprites are immediate-mode, so the caller owns the set). None for
a miss.
The ray is intersected with each quad’s plane and mapped to its
uv / source texel. A texel whose alpha is below the sprite’s
ImageSprite::alpha_cutoff (and any fully-transparent texel) is
see-through — the pick passes through it to a sprite behind.
For depth_test sprites the hit is
rejected when nearer scene geometry occludes that pixel (shares the
depth convention + bias of pick; on the GPU backend
the occlusion test costs a click-time depth readback).
Sourcepub fn set_flip_x(&mut self, flip: bool)
pub fn set_flip_x(&mut self, flip: bool)
Mirror the rendered 3D scene horizontally before display. The flip is
applied before any egui overlay, so the UI stays upright while the
viewport un-mirrors — a fix for the engine’s left-handed render.
Supported on both backends (CPU reverses the framebuffer rows; GPU
mirrors the scene blit + line/image overlays). Picking/projection are
unchanged, so a host that flips must mirror its cursor X (width - x)
for ray casts.
Sourcepub fn present(&mut self)
pub fn present(&mut self)
Present the frame render composited, with no UI
overlay. Pairs with render; use paint_egui
instead to overlay an egui UI before presenting.
Sourcepub fn set_sprites(&mut self, set: &SpriteSet) -> Vec<SpriteModelId>
pub fn set_sprites(&mut self, set: &SpriteSet) -> Vec<SpriteModelId>
Register sprite models + instances. The CPU backend builds a per-instance draw list; the GPU backend builds an instanced model registry. Call once at setup (or again to replace).
Sourcepub fn refresh_sprite_model(&mut self, model: SpriteModelId, kv6: &Kv6)
pub fn refresh_sprite_model(&mut self, model: SpriteModelId, kv6: &Kv6)
Re-register one sprite model’s geometry after you’ve edited its
content (a carve or recolour of its kv6). model is the
SpriteModelId handed back by set_sprites;
kv6 is the model’s new geometry — the caller owns the source
of truth (e.g. a dense carve grid the surface-only kv6 can’t
represent) and supplies the refreshed mesh here.
This is a backend-agnostic content refresh, not a GPU upload:
the renderer brings its stored model up to date however its active
backend needs to. The instance set is left untouched (an edit never
moves or adds an instance), so on the GPU backend only that one
model’s voxel data is re-uploaded — through a slack-backed
suballocator, one model’s bytes rather than the whole registry —
while the CPU backend swaps the cached kv6 into each instance of
the model. Use set_sprites to add/remove
models or change the instance set.
Sourcepub fn add_sprite_instance(
&mut self,
model: SpriteModelId,
pos: [f32; 3],
) -> SpriteInstanceId
pub fn add_sprite_instance( &mut self, model: SpriteModelId, pos: [f32; 3], ) -> SpriteInstanceId
Add one sprite instance of an already-registered model at world
pos, incrementally — the cheap streaming-spawn path that both
backends now share (GPU: append to the instance buffer, growing by
powers of two; CPU: push one pre-posed Sprite). Returns a
stable SpriteInstanceId for later removal.
model must be a SpriteModelId from the current
set_sprites (a model registered there, even
with zero initial instances). Dynamic instances live after the
static set + any KFA limbs, so register those first.
Sourcepub fn remove_sprite_instance(&mut self, id: SpriteInstanceId) -> bool
pub fn remove_sprite_instance(&mut self, id: SpriteInstanceId) -> bool
Remove a dynamic sprite instance added by
add_sprite_instance. O(1) on both
backends (swap-remove); other dynamic handles stay valid. Returns
false if the handle is stale / already removed.
Sourcepub fn dynamic_sprite_count(&self) -> usize
pub fn dynamic_sprite_count(&self) -> usize
Number of live dynamic sprite instances (those added via
add_sprite_instance).
Sourcepub fn set_kfa_sprites(&mut self, kfas: &mut [KfaSprite])
pub fn set_kfa_sprites(&mut self, kfas: &mut [KfaSprite])
Register animated KFA sprites (one or more bone hierarchies).
The GPU backend uploads each limb’s kv6 as an instanced model
once (appended to the sprite registry) and seeds the limb
instances at their current pose; the CPU backend caches the
posed limbs for drawing. Call once at setup, after
set_sprites, then drive motion per frame
with update_kfa_poses.
Limbs are posed from the sprites’ current
kfaval (advance
animsprite first
if using a baked curve), so kfas is taken &mut.
Sourcepub fn update_kfa_poses(&mut self, kfas: &mut [KfaSprite])
pub fn update_kfa_poses(&mut self, kfas: &mut [KfaSprite])
Re-pose the registered KFA sprites from their current
kfaval[]. Call each frame after advancing the animation
(kfa.animsprite(dt_ms) or poking kfaval[]). The GPU backend
takes the cheap transform-only update (no model-volume
re-upload); the CPU backend re-solves limb transforms for the
next render. Must follow a
set_kfa_sprites with the same sprites.
Sourcepub fn carve_active_sprite(&mut self) -> u32
pub fn carve_active_sprite(&mut self) -> u32
Carve the next z-layer off the SpriteSet::carve_model and
re-upload (the demo’s G hotkey + GPU.12 copy-on-modify). GPU
only; a no-op on the CPU backend. Returns the voxels removed.
Sourcepub fn request_capture(&mut self)
pub fn request_capture(&mut self)
Request that the next render capture its
framebuffer for take_capture. CPU only
(the GPU swapchain isn’t read back) — a no-op on GPU.
Sourcepub fn take_capture(&mut self) -> Option<(Vec<u32>, u32, u32)>
pub fn take_capture(&mut self) -> Option<(Vec<u32>, u32, u32)>
Take the most recently captured frame as packed 0x00RRGGBB
pixels + dimensions, or None if no capture is ready / GPU.
Sourcepub fn pick_depth(&self, x: u32, y: u32) -> Option<f32>
pub fn pick_depth(&self, x: u32, y: u32) -> Option<f32>
Screen→world picking input: the world-space hit distance t at
window pixel (x, y) from the last rendered frame, or None
for out-of-bounds pixels and sky / no-hit. The host reconstructs
the world hit point as cam.pos + t * normalize(ray_dir), where
ray_dir is the same per-pixel ray the frame was rendered with
(see the backend’s projection).
t is the distance to the nearest scene-grid surface
(terrain + grids); sprites do not occlude it (the sprite pass
reads depth read-only), so a cursor sprite under the pointer is
transparent to the pick.
Cost: the CPU backend reads its in-memory z-buffer (free); the
GPU backend stages the depth buffer and blocks on a device poll
(cheap at click time — do not call every frame). The GPU path
only has depth when the last frame drew sprites (write_depth).
Sourcepub fn pixel_ray(&self, camera: &Camera, x: f64, y: f64) -> Option<[f64; 3]>
pub fn pixel_ray(&self, camera: &Camera, x: f64, y: f64) -> Option<[f64; 3]>
World-space view-ray direction (un-normalised) for window pixel
(x, y), under the projection the last frame rendered with.
The backends differ (CPU setcamera vs GPU vertical-FOV
pinhole), so this hides which one is active. None before the
first frame. Intersect it with a plane for tile picking, or feed
it to Self::pick for a voxel.
Sourcepub fn view_ray(&self, camera: &Camera, x: f64, y: f64) -> Option<Ray>
pub fn view_ray(&self, camera: &Camera, x: f64, y: f64) -> Option<Ray>
Canonical screen→world unproject: the full view Ray
(camera.pos origin + unit direction) for window pixel
(x, y), under whichever projection the last frame used. The
one entry point both backends honour — hosts never reconstruct
the projection. None before the first frame or for a
degenerate ray.
Compose with roxlap_scene::Scene::raycast for depth-free
picking that’s identical on CPU and GPU:
renderer.view_ray(cam, x, y).and_then(|r| scene.raycast(r.origin, r.dir, max)).
Sourcepub fn pick(
&self,
scene: &Scene,
camera: &Camera,
x: u32,
y: u32,
) -> Option<PickHit>
pub fn pick( &self, scene: &Scene, camera: &Camera, x: u32, y: u32, ) -> Option<PickHit>
One-call screen→world voxel pick: unproject pixel (x, y) with
the active backend’s projection, read the last frame’s depth
there, reconstruct the world hit, and resolve it to the owning
grid + grid-local voxel via Scene::resolve_voxel. None on
sky / no-hit, or when no grid claims the surface.
scene and camera must be the ones the last frame rendered;
the projection (size + FOV / hx,hy,hz) is taken from that
frame. Cheap on CPU (in-memory z-buffer); on GPU it stages the
depth buffer (a click-time device poll — not per frame).
Auto Trait Implementations§
impl !RefUnwindSafe for SceneRenderer
impl !Sync for SceneRenderer
impl !UnwindSafe for SceneRenderer
impl Freeze for SceneRenderer
impl Send for SceneRenderer
impl Unpin for SceneRenderer
impl UnsafeUnpin for SceneRenderer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more