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 try_new<W>(
window: Arc<W>,
size: (u32, u32),
opts: &RenderOptions,
) -> Result<Self, RenderError>
pub fn try_new<W>( window: Arc<W>, size: (u32, u32), opts: &RenderOptions, ) -> Result<Self, RenderError>
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.
Builds the backend opts.backend
asks for. Under BackendPreference::PreferGpu a WGPU-init
failure falls back to the CPU backend with a warn through
the log facade (install a logger like env_logger to see
why a machine is software-rendering);
BackendPreference::RequireGpu turns that failure into an
error instead (QE.7b — headless CI / benchmark rigs must not
silently measure a software render).
§Errors
RenderError::CpuSurface — the last-resort softbuffer
context/surface couldn’t bind to window;
RenderError::GpuInit — RequireGpu and WGPU init failed.
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
Infallible Self::try_new: panics if even the CPU
software surface can’t be created — the convenient default for
demos and tools, where “no window surface at all” has no
meaningful recovery. Games that want to show their own error UI
call try_new.
§Panics
When Self::try_new returns RenderError::CpuSurface.
Sourcepub fn supports(&self, feature: Feature) -> bool
pub fn supports(&self, feature: Feature) -> bool
Whether the active backend supports feature (QE.7a) — the
queryable form of the parity table on Feature. Methods on
the “❌” side stay callable and degrade to documented no-ops;
use this to pick a strategy up front (e.g. photo mode: capture
vs “not available on this platform”).
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 is_low_power(&self) -> bool
pub fn is_low_power(&self) -> bool
true when this renderer runs on modest hardware: the CPU
backend, or a GPU adapter that is not a discrete card
(integrated / software / virtual). A hint for hosts to pick a
lighter default render resolution — never consulted internally.
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 set_render_resolution(&mut self, res: RenderResolution)
pub fn set_render_resolution(&mut self, res: RenderResolution)
Set the logical (fixed) render resolution (RP.0). The scene marches at
the resolved logical size and is nearest-upscaled to the window, so the
raycaster’s cost — and thus FPS — stops depending on the window size.
RenderResolution::Native (the default) keeps logical == window
and is byte-identical to pre-RP rendering. Takes effect from the next
render.
Sourcepub fn set_ssaa(&mut self, factor: u8)
pub fn set_ssaa(&mut self, factor: u8)
Set the supersampling factor (RP.1). 1 = off; 2 marches 2×2
samples per logical pixel and box-downfilters back before the upscale,
anti-aliasing the retro grid. Clamped to 1..=4. The marcher then runs
at logical_dims × factor — predictable cost, independent of the window
size. Takes effect from the next render.
Sourcepub fn render_dims(&self) -> (u32, u32)
pub fn render_dims(&self) -> (u32, u32)
The resolution the raycaster actually runs at this frame —
logical_dims × ssaa (RP.1). Reflects the most recent window size,
RenderResolution, and SSAA factor.
Sourcepub fn set_posterize(&mut self, cfg: Option<PosterizeConfig>)
pub fn set_posterize(&mut self, cfg: Option<PosterizeConfig>)
Set the reduced-palette posterize post (RP.2), or None to disable it
(the default — RP.0/RP.1 paths verbatim). Quantization runs at the
logical resolution in the resolve step, after the SSAA downfilter and
before the nearest upscale, with the configured dither. Takes effect
from the next render.
Sourcepub fn logical_dims(&self) -> (u32, u32)
pub fn logical_dims(&self) -> (u32, u32)
The logical (fixed) render-target size resolved against the current
window size, per the active RenderResolution.
Sourcepub fn tick(&mut self, camera: &Camera, dt: f64)
pub fn tick(&mut self, camera: &Camera, dt: f64)
One-call per-frame animation tick (QE.1b): drives every
facade-owned animated collection, replacing the multi-call
protocol hosts previously had to know (and could silently get
wrong — a missed call meant frozen clips or unfaced
billboards). Call once per frame before render:
advance_voxel_clips— auto-playing flipbook + streaming clip players;- every live character — skeleton + clip attachments (the
all-characters sweep of
advance_character); update_billboard_actors— actor direction/state clips + camera facing;face_billboards_to— plain billboard instances.
The fine-grained methods stay public for hosts that need a
custom per-entity dt (slow-motion on one character) or their
own ordering — tick is the “do the right thing” default and
is exactly equivalent to calling them in the order above.
KFA sprites driven via
update_kfa_poses remain a separate
call (they mutate host-owned skeletons).
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,
) -> Option<ImageId>
pub fn upload_image( &mut self, rgba: &[u8], width: u32, height: u32, ) -> Option<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 None for malformed input — a wrong byte count
(!= width·height·4) or a zero dimension — so a bad upload can’t be
confused with the first valid id (ImageId(0)).
Sourcepub fn drop_image(&mut self, id: ImageId)
pub fn drop_image(&mut self, id: ImageId)
Release a texture uploaded with upload_image.
Stale handles (already dropped, or aliasing a reused slot) are a
safe no-op — ids are generational since QE-B6.
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 wait_idle(&mut self)
pub fn wait_idle(&mut self)
Block until the active backend has finished all in-flight work, ready for a clean teardown. On the GPU backend this drains the device queue and releases any acquired-but-unpresented swapchain frame; on the CPU backend it is a no-op (nothing is in flight).
Call this at shutdown before dropping the renderer and its window, so the GPU device/surface tear down with no commands queued and no half-presented frame. Skipping it (or dropping the window first) can leave the driver/compositor showing stale buffers after an exit — the “leftover triangles / flicker” symptom of an unclean shutdown.
Sourcepub fn frame(
&mut self,
scene: &mut Scene,
camera: &Camera,
params: &FrameParams<'_>,
) -> Frame<'_>
pub fn frame( &mut self, scene: &mut Scene, camera: &Camera, params: &FrameParams<'_>, ) -> Frame<'_>
Composite scene and return a Frame guard — the type-state
form of the render → overlays → present/paint_egui protocol
(QE-B6), which the split calls enforce by documentation only.
With the guard, misuse is unrepresentable: overlays can only be
drawn on a live frame (with the camera it was rendered with —
the guard holds it), presenting consumes the guard so a double
present can’t compile, and dropping an unfinished frame
presents it — a bare renderer.frame(..); shows the frame.
renderer
.frame(&mut scene, &camera, ¶ms)
.draw_lines(&gizmo)
.present(); // or .paint_egui(..); or just drop itThe split render / present
calls remain available for hosts that need custom control flow
between the stages.
Sourcepub fn paint_egui(
&mut self,
jobs: &[ClippedPrimitive],
textures: &TexturesDelta,
pixels_per_point: f32,
)
pub fn paint_egui( &mut self, jobs: &[ClippedPrimitive], textures: &TexturesDelta, pixels_per_point: f32, )
Overlay an egui UI on the frame render
composited, then present it (hud feature). The host runs egui
itself (e.g. egui + egui-winit) and passes the tessellated
jobs (egui::Context::tessellate) and the per-frame
textures delta from egui::FullOutput; pixels_per_point is
the UI scale (ctx.pixels_per_point()).
The GPU backend paints via egui-wgpu; the CPU backend
software-rasterises the tessellation into its framebuffer. Use
this instead of present — both finish the
frame.
Sourcepub fn clear_sprites(&mut self)
pub fn clear_sprites(&mut self)
Reset the whole sprite world: every model, dynamic instance, voxel clip, streaming clip, character, billboard and actor is dropped, and every outstanding handle from any of those families goes stale (resolving to safe no-ops — the maps are generational). The explicit scene-switch verb (QE-B6): what the demo host does between scenes, without the “register an empty set” idiom spelling it.
Sourcepub fn set_sprites(&mut self, set: &SpriteSet) -> Vec<SpriteModelId>
pub fn set_sprites(&mut self, set: &SpriteSet) -> Vec<SpriteModelId>
Register sprite models + instances — replacing the whole
sprite world. This is a bulk set, not an append: every
handle family (models, dynamic instances, clips, streaming
clips, characters, billboards, actors) is reset and previously
issued handles go stale (safe no-ops). Call once at setup, or
again to replace everything; to build up incrementally instead,
use add_sprite_model /
add_sprite_instance &c., and to
drop everything use clear_sprites.
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 refresh_sprite_model_with_materials(
&mut self,
model: SpriteModelId,
kv6: &Kv6,
material_map: &[(Rgb, u8)],
)
pub fn refresh_sprite_model_with_materials( &mut self, model: SpriteModelId, kv6: &Kv6, material_map: &[(Rgb, u8)], )
Like refresh_sprite_model but also
re-classifies the refreshed voxels into per-voxel material ids by
colour (TV.3) via material_map — used by the material-aware streaming
clip path so a re-uploaded frame keeps its per-voxel materials. An
empty map matches refresh_sprite_model.
Sourcepub fn add_sprite_instance(
&mut self,
model: SpriteModelId,
pos: [f32; 3],
) -> Option<SpriteInstanceId>
pub fn add_sprite_instance( &mut self, model: SpriteModelId, pos: [f32; 3], ) -> Option<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.
Returns None — spawning nothing — on a stale/removed model
handle (QE.1c; previously a silent sentinel id).
Sourcepub fn add_sprite_instance_posed(
&mut self,
model: SpriteModelId,
xf: DynSpriteTransform,
) -> Option<SpriteInstanceId>
pub fn add_sprite_instance_posed( &mut self, model: SpriteModelId, xf: DynSpriteTransform, ) -> Option<SpriteInstanceId>
Add one sprite instance of an already-registered model,
pre-posed with the orientation in xf — the streaming-spawn path
for objects that appear mid-flight already rotated (so there’s no
one-frame axis-aligned flash before the first
set_sprite_instance_transform).
Otherwise identical to
add_sprite_instance (which is just
this with the identity basis). Returns a stable
SpriteInstanceId.
Returns None — spawning nothing — on a stale/removed model
handle (QE.1c; previously a silent sentinel id). xf’s basis
must be non-singular; a degenerate one makes the instance
silently skip drawing (see DynSpriteTransform).
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 define_material(&mut self, id: u8, mat: Material) -> bool
pub fn define_material(&mut self, id: u8, mat: Material) -> bool
Define a global voxel material (TV stage): the opacity + blend mode that a per-voxel material id resolves to. The renderer owns one 256-entry palette shared by every model and grid.
Id 0 is permanently Material::OPAQUE — the value every voxel
without explicit material data resolves to — and cannot be
redefined; passing id == 0 is a no-op that returns false. Any
other id returns true.
While no translucent material is defined the renderer stays on the
fully-opaque fast path, so this is inert until first called. See
PORTING-TRANSPARENCY.md.
Sourcepub fn material(&self, id: u8) -> Material
pub fn material(&self, id: u8) -> Material
The Material currently at palette id (Material::OPAQUE for
any id never passed to define_material).
Sourcepub fn set_terrain_materials(&mut self, map: &[(Rgb, u8)])
pub fn set_terrain_materials(&mut self, map: &[(Rgb, u8)])
Set the terrain colour→material map (TV.4): pairs of (rgb, material_id) that make matching-colour world (grid) voxels translucent
— glass walls, water pools. The materials themselves are defined via
define_material. An empty map (the default)
keeps all terrain opaque. The CPU backend composites these today; the
GPU backend renders them once the TV.6 device path lands.
Sourcepub fn add_sprite_model(&mut self, kv6: &Kv6) -> SpriteModelId
pub fn add_sprite_model(&mut self, kv6: &Kv6) -> SpriteModelId
Register one new sprite model incrementally from kv6,
without rebuilding the existing model set — the streaming-in
counterpart to add_sprite_instance
for unique generated geometry (procedural asteroids, debris).
Returns a stable SpriteModelId usable immediately with
add_sprite_instance /
add_sprite_instance_posed.
Works before any set_sprites (it establishes
residency on the GPU backend’s first model). The GPU backend
appends one LOD chain to the resident registry (amortised O(model
voxels)); the CPU backend pushes an axis-aligned template.
Sourcepub fn add_sprite_model_with_materials(
&mut self,
kv6: &Kv6,
material_map: &[(Rgb, u8)],
) -> SpriteModelId
pub fn add_sprite_model_with_materials( &mut self, kv6: &Kv6, material_map: &[(Rgb, u8)], ) -> SpriteModelId
Register a mixed-material sprite model (TV.3): material_map pairs
a voxel RGB colour (0xRRGGBB) with a material id (defined via
define_material), so a single model can mix
opaque and translucent voxels — an opaque window frame around glass, a
bottle around a translucent potion. Voxels whose colour isn’t in the
map are opaque (material 0). Like add_sprite_model
otherwise.
The CPU backend composites per-voxel materials today; the GPU backend carries the data and renders per-voxel materials once the TV.3b device path lands (until then it uses the instance’s uniform material).
Sourcepub fn remove_sprite_model(&mut self, id: SpriteModelId) -> bool
pub fn remove_sprite_model(&mut self, id: SpriteModelId) -> bool
Remove a registered sprite model, freeing its voxel data. Returns
false if id is stale / already removed.
The model’s slot is tombstoned in place: its id is never
reused, so every other SpriteModelId stays valid (no remap).
Existing instances of the removed model are not dropped here —
they linger but draw as nothing on the GPU backend (the CPU
backend keeps each instance’s own kv6 clone, so they keep drawing
until removed via
remove_sprite_instance); remove
them when convenient. Call
compact_sprite_models afterwards
to reclaim the GPU buffer holes.
Sourcepub fn compact_sprite_models(&mut self)
pub fn compact_sprite_models(&mut self)
Reclaim the GPU buffer space left by
remove_sprite_model by repacking the
resident registry to its live models only. Model ids are preserved
(no remap). O(live voxel volume) — call it when many models have
been removed, not every frame. No-op on the CPU backend (which
keeps cheap empty placeholders) and when nothing was removed.
Sourcepub fn set_sprite_instance_transform(
&mut self,
id: SpriteInstanceId,
xf: DynSpriteTransform,
)
pub fn set_sprite_instance_transform( &mut self, id: SpriteInstanceId, xf: DynSpriteTransform, )
Update one dynamic instance’s full pose (position + orientation)
for this frame. id is from
add_sprite_instance /
add_sprite_instance_posed. A
stale / removed handle is a no-op.
For many instances per frame prefer
set_sprite_instance_transforms:
the GPU backend flushes all pending pose changes to the device
once per render, so a per-instance call here is
still O(1) device work, but the batch variant avoids re-walking
the slotmap.
Sourcepub fn set_sprite_instance_transforms(
&mut self,
updates: &[(SpriteInstanceId, DynSpriteTransform)],
)
pub fn set_sprite_instance_transforms( &mut self, updates: &[(SpriteInstanceId, DynSpriteTransform)], )
Batch form of
set_sprite_instance_transform
— apply many (instance, pose) updates in one call. Stale handles
in updates are skipped. On the GPU backend this marks the
instance buffer dirty once and uploads the new poses a single time
at the next render, so spinning a whole cluster
of instances per frame is one device upload, not one per instance.
Sourcepub fn set_sprite_instance_material(
&mut self,
id: SpriteInstanceId,
material: u8,
)
pub fn set_sprite_instance_material( &mut self, id: SpriteInstanceId, material: u8, )
Set sprite instance id’s voxel-material id (TV stage) — indexes the
global palette defined via define_material
for this whole instance’s opacity + blend mode. 0 (the default) is
opaque. Stale handles are ignored.
Only the CPU backend composites translucent sprites today; the GPU
backend retains the value for the forthcoming device-side path (see
PORTING-TRANSPARENCY.md).
Sourcepub fn set_sprite_instance_alpha(&mut self, id: SpriteInstanceId, alpha_mul: u8)
pub fn set_sprite_instance_alpha(&mut self, id: SpriteInstanceId, alpha_mul: u8)
Set sprite instance id’s per-instance alpha multiplier (TV stage),
0..=255 (255 = unscaled). Scales the material’s opacity so an
effect can fade out by cheap per-frame updates without re-uploading
its volume. Stale handles are ignored.
Sourcepub fn set_sprite_instance_tint(&mut self, id: SpriteInstanceId, tint: Rgb)
pub fn set_sprite_instance_tint(&mut self, id: SpriteInstanceId, tint: Rgb)
Set sprite instance id’s per-instance RGB tint, packed
0x00RRGGBB: every rendered voxel’s colour is multiplied by it (per
channel), so instances of one model can be recoloured cheaply per frame.
0x00FF_FFFF (white, the default) is a no-op. Works on both backends;
stale handles are ignored. Tint is colour only — for transparency, use a
translucent material with
set_sprite_instance_alpha.
Sourcepub fn set_sprite_instance_shadow_flags(
&mut self,
id: SpriteInstanceId,
shadows: ShadowFlags,
)
pub fn set_sprite_instance_shadow_flags( &mut self, id: SpriteInstanceId, shadows: ShadowFlags, )
Toggle a sprite/clip instance’s shadow participation live
(XS.4 flags, BB.3). ShadowFlags::default (both on) is what
every spawn starts with. The per-instance counterpart to the
template-level Sprite::with_casts_shadow /
with_receives_shadow — e.g. a flat additive glow billboard
that should not cast, or a UI marker that ignores shadows.
Other flag bits are preserved. No-op on a stale id.
QE.7b — takes ShadowFlags instead of the old unreadable
(id, true, false) bool pair; migrate with
ShadowFlags { casts, receives }.
Sourcepub fn set_sprite_instance_lighting(
&mut self,
id: SpriteInstanceId,
mode: BillboardLighting,
)
pub fn set_sprite_instance_lighting( &mut self, id: SpriteInstanceId, mode: BillboardLighting, )
Set a sprite/clip instance’s lighting mode live (BB.2b): how its
shading normal is derived (BillboardLighting). Useful for
camera-facing billboards whose face normal would otherwise track the
camera. Other flag bits are preserved; only affects the dynamic
lighting path. No-op on a stale id.
Sourcepub fn add_voxel_clip(&mut self, clip: &DecodedClip) -> VoxelClipId
pub fn add_voxel_clip(&mut self, clip: &DecodedClip) -> VoxelClipId
Register an animated voxel clip (“GIF/MP4 for voxels”): decode all
its frames and upload the flipbook to the active backend (GPU: one
LOD chain per frame; CPU: a cached dense grid per frame). Returns a
VoxelClipId to spawn instances of it via
add_clip_instance_posed.
Build the DecodedClip from a .rvc via
VoxelClip::decode.
Like add_sprite_model, this works before
any set_sprites; a later set_sprites
drops all registered clips (re-register afterwards).
Sourcepub fn add_voxel_clip_with_materials(
&mut self,
clip: &DecodedClip,
material_map: &[(Rgb, u8)],
) -> VoxelClipId
pub fn add_voxel_clip_with_materials( &mut self, clip: &DecodedClip, material_map: &[(Rgb, u8)], ) -> VoxelClipId
Register a mixed-material animated voxel clip (TV.3): the clip
analogue of
add_sprite_model_with_materials.
material_map pairs a voxel RGB colour (0xRRGGBB) with a material id
(defined via define_material), classifying
every frame’s voxels so an animated clip can mix opaque and translucent
voxels — an opaque torch handle around an additive flame, a spinning
glass orb. Voxels whose colour isn’t in the map stay opaque
(material 0). Like add_voxel_clip otherwise.
Sourcepub fn remove_voxel_clip(&mut self, id: VoxelClipId) -> bool
pub fn remove_voxel_clip(&mut self, id: VoxelClipId) -> bool
Remove a registered clip, freeing its per-frame volumes. Instances
of it linger but draw nothing until removed via
remove_sprite_instance. Returns
false if id is stale / already removed.
Sourcepub fn add_clip_instance_posed(
&mut self,
clip: VoxelClipId,
xf: DynSpriteTransform,
) -> Option<SpriteInstanceId>
pub fn add_clip_instance_posed( &mut self, clip: VoxelClipId, xf: DynSpriteTransform, ) -> Option<SpriteInstanceId>
Spawn an instance of clip clip, posed by xf, starting on frame
0. Returns a SpriteInstanceId — a clip instance is a dynamic
sprite instance, so move it with
set_sprite_instance_transform,
advance its frame with
set_clip_instance_frame, and drop
it with remove_sprite_instance.
Returns None — spawning nothing — on a stale/removed clip
handle (QE.1c; previously a silent sentinel id).
This instance has no playback clock: drive its frame yourself via
set_clip_instance_frame (frame-based
scrubbing). For clock-based control — auto-advance, play/pause, or
set_clip_instance_clock_ms
scrubbing — spawn with
add_clip_instance_playing instead
(the player-control methods no-op on an instance with no player).
Sourcepub fn set_clip_instance_frame(&mut self, id: SpriteInstanceId, frame: u32)
pub fn set_clip_instance_frame(&mut self, id: SpriteInstanceId, frame: u32)
Select which frame a clip instance shows — the per-frame playback
step. Cheap on both backends (GPU: swap the instance’s model id;
CPU: select the cached frame grid), with no volume re-upload. Drive
it from a playback clock via
DecodedClip::frame_at.
No-op on a stale id or a non-clip instance.
Sourcepub fn set_clip_instance_clip(
&mut self,
id: SpriteInstanceId,
clip: VoxelClipId,
) -> bool
pub fn set_clip_instance_clip( &mut self, id: SpriteInstanceId, clip: VoxelClipId, ) -> bool
Retarget a live clip instance onto a different registered clip,
restarting it at frame 0 while keeping its world transform and any
auto-playback clock policy (speed / paused). The per-frame primitive
for directional (“8-way”) billboards and animation-state changes
(idle → walk → attack): far cheaper than remove_sprite_instance +
add_clip_instance_*, reusing the instance’s existing GPU residency
(just a model-id swap, no volume re-upload).
If the instance has a playback clock
(add_clip_instance_playing), its
timeline is retargeted to the new clip (durations + loop mode) and the
clock restarts at 0; the speed and paused state carry over.
Returns false (a safe no-op) on a stale instance id, a stale clip,
or a non-clip instance.
Sourcepub fn add_billboard_instance(
&mut self,
clip: VoxelClipId,
pos: [f32; 3],
mode: BillboardMode,
) -> Option<SpriteInstanceId>
pub fn add_billboard_instance( &mut self, clip: VoxelClipId, pos: [f32; 3], mode: BillboardMode, ) -> Option<SpriteInstanceId>
Spawn a clip instance that auto-orients toward the camera every
face_billboards_to — a Doom/Build-style
billboard. pos is its world position (the clip pivot maps here);
mode chooses cylindrical (the Doom default) or spherical facing.
Drive its animation through the clip player
(advance_voxel_clips) and swap
animations with set_clip_instance_clip.
The instance starts axis-aligned until the first face_billboards_to,
so call that (with the frame’s camera) before render — like
advance_voxel_clips(dt). Returns a stale id on a stale clip (no
billboard recorded).
Sourcepub fn set_billboard_mode(&mut self, id: SpriteInstanceId, mode: BillboardMode)
pub fn set_billboard_mode(&mut self, id: SpriteInstanceId, mode: BillboardMode)
Change a billboard instance’s facing mode. No-op on a non-billboard id.
Sourcepub fn set_billboard_position(&mut self, id: SpriteInstanceId, pos: [f32; 3])
pub fn set_billboard_position(&mut self, id: SpriteInstanceId, pos: [f32; 3])
Move a billboard instance. Its auto-orientation is preserved; the new
position takes effect on the next
face_billboards_to. No-op on a
non-billboard id.
Sourcepub fn face_billboards_to(&mut self, camera: &Camera)
pub fn face_billboards_to(&mut self, camera: &Camera)
Re-orient every billboard instance to face camera — one batched
transform flush (BB.2). Call once per frame before render, after
moving billboards / the camera (the billboard analogue of
advance_voxel_clips). Billboards whose
instance was removed are pruned; a degenerate pose (camera on the
sprite’s vertical axis) is skipped for that frame.
Sourcepub fn add_billboard_actor(
&mut self,
def: BillboardActorDef,
pos: [f32; 3],
facing_yaw: f64,
) -> Option<BillboardActorId>
pub fn add_billboard_actor( &mut self, def: BillboardActorDef, pos: [f32; 3], facing_yaw: f64, ) -> Option<BillboardActorId>
Register a high-level directional billboard actor (BB.4): the
renderer owns one clip instance and, every
update_billboard_actors, picks the
directional clip from the view angle, faces it to the camera, and
advances its state animation. The convenience layer over
add_billboard_instance +
set_clip_instance_clip + the clip
clock for Doom-style monsters.
pos is the actor’s world position; facing_yaw is the world yaw it
faces (radians; the dir picker compares the camera’s bearing to it).
Returns None — spawning nothing — if def has no states / a
state with no dirs, or the initial clip is stale (QE.1c;
previously a silent sentinel id).
Sourcepub fn set_actor_state(&mut self, id: BillboardActorId, state: &str) -> bool
pub fn set_actor_state(&mut self, id: BillboardActorId, state: &str) -> bool
Switch an actor to a named animation state, restarting its clock (the
directional clip is reselected on the next
update_billboard_actors). No-op on a
stale id or an unknown state name.
Sourcepub fn set_actor_transform(
&mut self,
id: BillboardActorId,
pos: [f32; 3],
facing_yaw: f64,
)
pub fn set_actor_transform( &mut self, id: BillboardActorId, pos: [f32; 3], facing_yaw: f64, )
Move/turn an actor. Its orientation + directional clip update on the
next update_billboard_actors. No-op
on a stale id.
Sourcepub fn set_actor_lighting(
&mut self,
id: BillboardActorId,
mode: BillboardLighting,
) -> bool
pub fn set_actor_lighting( &mut self, id: BillboardActorId, mode: BillboardLighting, ) -> bool
Change an actor’s lighting mode at runtime (BB.2b) — the per-actor
counterpart to BillboardActorDef::lighting, routed to its clip
instance via set_sprite_instance_lighting.
Returns false on a stale id.
Sourcepub fn set_actor_tint(&mut self, id: BillboardActorId, tint: Rgb) -> bool
pub fn set_actor_tint(&mut self, id: BillboardActorId, tint: Rgb) -> bool
Tint an actor at runtime — the per-actor counterpart to
set_sprite_instance_tint, routed to
its clip instance. tint is an 0x00RR_GGBB colour multiply
(0x00FF_FFFF = white = no-op). Returns false on a stale id.
Sourcepub fn remove_billboard_actor(&mut self, id: BillboardActorId) -> bool
pub fn remove_billboard_actor(&mut self, id: BillboardActorId) -> bool
Remove an actor and its clip instance. Returns false on a stale id.
Sourcepub fn update_billboard_actors(&mut self, camera: &Camera, dt: f64)
pub fn update_billboard_actors(&mut self, camera: &Camera, dt: f64)
Drive every billboard actor by dt seconds (BB.4): for each, pick the
directional clip from the camera bearing (swapping clips only on
change), advance its state-animation clock, and face it to the camera.
Call once per frame before render (the actor analogue of
advance_voxel_clips +
face_billboards_to). Actors whose
instance was removed are pruned.
Sourcepub fn clip_frame_count(&self, id: VoxelClipId) -> Option<usize>
pub fn clip_frame_count(&self, id: VoxelClipId) -> Option<usize>
Frame count of a registered flipbook clip, or None if id is
stale. (Same as clip_metadata(id)?.frame_count, without the clone.)
Sourcepub fn clip_metadata(&self, id: VoxelClipId) -> Option<ClipMetadata>
pub fn clip_metadata(&self, id: VoxelClipId) -> Option<ClipMetadata>
Inspector metadata (dims / pivot / scale / loop mode / per-frame
durations) of a registered flipbook clip, or None if id is stale
— so an editor needn’t shadow the source DecodedClip.
Sourcepub fn clip_instance_frame(&self, id: SpriteInstanceId) -> Option<u32>
pub fn clip_instance_frame(&self, id: SpriteInstanceId) -> Option<u32>
Which frame a clip instance is currently showing (the timeline
scrubber’s read-back), or None if id isn’t a live clip instance.
Sourcepub fn get_clip_instance_frame(&self, id: SpriteInstanceId) -> Option<u32>
👎Deprecated since 0.22.0: renamed to clip_instance_frame
pub fn get_clip_instance_frame(&self, id: SpriteInstanceId) -> Option<u32>
renamed to clip_instance_frame
QE.7b — renamed: this was the only get_-prefixed method in
the crate. Forwarding shim for one minor release.
Sourcepub fn update_clip_frame(
&mut self,
id: VoxelClipId,
frame: u32,
vf: &VoxelFrame,
) -> bool
pub fn update_clip_frame( &mut self, id: VoxelClipId, frame: u32, vf: &VoxelFrame, ) -> bool
Re-upload a single frame of registered clip id in place — the
editor’s one-voxel paint, O(1 frame) instead of remove_voxel_clip +
add_voxel_clip (which rebuilds all N volumes). vf must fit the
clip’s fixed dims. Returns false on a stale id, an out-of-range
frame, or a frame that fails the clip’s layout (so it can’t corrupt
the flipbook).
Sourcepub fn add_streaming_clip(
&mut self,
clip: &VoxelClip,
) -> Result<StreamingClipId, DecodeError>
pub fn add_streaming_clip( &mut self, clip: &VoxelClip, ) -> Result<StreamingClipId, DecodeError>
Register a streaming voxel clip — O(1-frame) memory (one sprite
model + the compact encoded stream) rather than the N-volume flipbook
add_voxel_clip builds, for huge clips where
N frames are too costly to hold resident. Builds the model from frame
0; advance it with
set_streaming_clip_frame. Spawn
instances with
add_streaming_clip_instance —
note that, unlike a flipbook, all instances of a streaming clip
share its one model and so always show the same (current) frame.
Takes the encoded VoxelClip (not a DecodedClip) — the whole
point is to avoid materialising every frame.
§Errors
DecodeError if the clip’s frame stream is empty or doesn’t begin
with a keyframe.
Sourcepub fn add_streaming_clip_with_materials(
&mut self,
clip: &VoxelClip,
material_map: &[(Rgb, u8)],
) -> Result<StreamingClipId, DecodeError>
pub fn add_streaming_clip_with_materials( &mut self, clip: &VoxelClip, material_map: &[(Rgb, u8)], ) -> Result<StreamingClipId, DecodeError>
Register a mixed-material streaming voxel clip (TV.3): the streaming
analogue of
add_voxel_clip_with_materials.
material_map pairs a voxel RGB colour with a material id (defined via
define_material); it is re-applied on every
per-frame re-upload, so the single streamed model keeps its per-voxel
materials as the clip advances. An empty map is identical to
add_streaming_clip.
§Errors
Sourcepub fn add_streaming_clip_instance(
&mut self,
id: StreamingClipId,
xf: DynSpriteTransform,
) -> Option<StreamingInstanceId>
pub fn add_streaming_clip_instance( &mut self, id: StreamingClipId, xf: DynSpriteTransform, ) -> Option<StreamingInstanceId>
Spawn an instance of streaming clip id, posed by xf. Returns a
SpriteInstanceId — move it with
set_sprite_instance_transform
and drop it with
remove_sprite_instance, like any
dynamic instance. All instances of one streaming clip share its single
model. Returns None — spawning nothing — on a stale/removed
id (QE.1c; previously a silent sentinel handle).
Sourcepub fn set_streaming_instance_transform(
&mut self,
id: StreamingInstanceId,
xf: DynSpriteTransform,
)
pub fn set_streaming_instance_transform( &mut self, id: StreamingInstanceId, xf: DynSpriteTransform, )
Re-pose a streaming-clip instance (world transform). No-op on a stale handle.
Sourcepub fn remove_streaming_instance(&mut self, id: StreamingInstanceId) -> bool
pub fn remove_streaming_instance(&mut self, id: StreamingInstanceId) -> bool
Remove a streaming-clip instance. Returns false if id is stale.
Sourcepub fn set_streaming_clip_frame(&mut self, id: StreamingClipId, frame: u32)
pub fn set_streaming_clip_frame(&mut self, id: StreamingClipId, frame: u32)
Advance a streaming clip to frame: seek the cursor and re-upload its
single model — the per-frame streaming step (one volume re-upload,
vs the flipbook’s cheap model-select). Updates every instance of
the clip at once. Drive it from a clock via
frame_at. No-op on a stale
id; frame is clamped to the last.
Sourcepub fn remove_streaming_clip(&mut self, id: StreamingClipId) -> bool
pub fn remove_streaming_clip(&mut self, id: StreamingClipId) -> bool
Remove a streaming clip: free its model and drop the cursor (the
memory win for huge clips). Instances linger but draw nothing until
removed. Returns false if id is stale / already removed.
Sourcepub fn add_clip_instance_playing(
&mut self,
clip: VoxelClipId,
xf: DynSpriteTransform,
speed: f32,
start_phase_ms: u32,
) -> Option<SpriteInstanceId>
pub fn add_clip_instance_playing( &mut self, clip: VoxelClipId, xf: DynSpriteTransform, speed: f32, start_phase_ms: u32, ) -> Option<SpriteInstanceId>
Spawn a flipbook-clip instance that plays itself: like
add_clip_instance_posed, but the
facade tracks a playback clock so a single
advance_voxel_clips call advances every
such instance — no per-frame frame_at + set_clip_instance_frame
bookkeeping in the host. speed is the playback rate (1.0 =
authored speed, negative = reverse; QE.7b - was Q8, migrate with
speed_q8 as f32 / 256.0); start_phase_ms offsets the clock
(stagger copies of one clip). Returns None — spawning nothing,
registering no player — on a stale/removed clip (QE.1c;
previously a silent sentinel id).
Sourcepub fn play_streaming_clip(
&mut self,
clip: StreamingClipId,
speed: f32,
start_phase_ms: u32,
)
pub fn play_streaming_clip( &mut self, clip: StreamingClipId, speed: f32, start_phase_ms: u32, )
Give a streaming clip (add_streaming_clip)
its own playback clock, advanced by
advance_voxel_clips. A streaming clip’s
frame is per-clip (all its instances share one model), so this is
keyed on the clip, not an instance — register instances separately
with
add_streaming_clip_instance.
No-op on a stale clip.
Control the player (play/pause/scrub) via
set_streaming_clip_paused /
set_streaming_clip_speed /
set_streaming_clip_clock_ms, the
per-clip analogues of the flipbook set_clip_instance_* methods.
speed is the playback rate (1.0 = authored; QE.7b - was Q8).
Sourcepub fn advance_voxel_clips(&mut self, dt: f64)
pub fn advance_voxel_clips(&mut self, dt: f64)
Advance every auto-playing clip (add_clip_instance_playing /
play_streaming_clip) by dt seconds: tick each clock, resolve its
frame via frame_at, and
apply it. Players whose instance / clip was removed are pruned. Call
once per frame.
Sourcepub fn set_clip_instance_paused(&mut self, id: SpriteInstanceId, paused: bool)
pub fn set_clip_instance_paused(&mut self, id: SpriteInstanceId, paused: bool)
Pause / resume the auto-player driving clip instance id (the
editor’s play/pause). No-op if id has no player.
Sourcepub fn is_clip_instance_paused(&self, id: SpriteInstanceId) -> Option<bool>
pub fn is_clip_instance_paused(&self, id: SpriteInstanceId) -> Option<bool>
Whether clip instance id’s auto-player is paused, or None if it
has no player.
Sourcepub fn set_clip_instance_speed(&mut self, id: SpriteInstanceId, speed: f32)
pub fn set_clip_instance_speed(&mut self, id: SpriteInstanceId, speed: f32)
Set the playback speed (1.0 = authored rate, negative =
reverse; QE.7b — was Q8) of clip instance id’s auto-player.
No-op if id has no player.
Sourcepub fn set_clip_instance_clock_ms(
&mut self,
id: SpriteInstanceId,
clock_ms: f64,
)
pub fn set_clip_instance_clock_ms( &mut self, id: SpriteInstanceId, clock_ms: f64, )
Scrub: set clip instance id’s playback clock to clock_ms and
immediately show the matching frame (works while paused). No-op if
id has no player.
Sourcepub fn clip_instance_clock_ms(&self, id: SpriteInstanceId) -> Option<f64>
pub fn clip_instance_clock_ms(&self, id: SpriteInstanceId) -> Option<f64>
Clip instance id’s current playback-clock position (ms), or None
if it has no player — the scrubber’s read-back.
Sourcepub fn set_streaming_clip_paused(&mut self, clip: StreamingClipId, paused: bool)
pub fn set_streaming_clip_paused(&mut self, clip: StreamingClipId, paused: bool)
Pause / resume a streaming clip’s auto-player
(play_streaming_clip). No-op if clip
has no player.
Sourcepub fn is_streaming_clip_paused(&self, clip: StreamingClipId) -> Option<bool>
pub fn is_streaming_clip_paused(&self, clip: StreamingClipId) -> Option<bool>
Whether streaming clip clip’s auto-player is paused, or None if it
has no player.
Sourcepub fn set_streaming_clip_speed(&mut self, clip: StreamingClipId, speed: f32)
pub fn set_streaming_clip_speed(&mut self, clip: StreamingClipId, speed: f32)
Set the playback speed (1.0 = authored rate, negative =
reverse; QE.7b — was Q8) of streaming clip clip’s
auto-player. No-op if clip has no player.
Sourcepub fn set_streaming_clip_clock_ms(
&mut self,
clip: StreamingClipId,
clock_ms: f64,
)
pub fn set_streaming_clip_clock_ms( &mut self, clip: StreamingClipId, clock_ms: f64, )
Scrub a streaming clip: set its auto-player’s clock to clock_ms
and immediately show the matching frame (works while paused). No-op if
clip has no player.
Sourcepub fn streaming_clip_clock_ms(&self, clip: StreamingClipId) -> Option<f64>
pub fn streaming_clip_clock_ms(&self, clip: StreamingClipId) -> Option<f64>
Streaming clip clip’s current playback-clock position (ms), or
None if it has no player — the scrubber’s read-back.
Sourcepub fn add_character(
&mut self,
ch: &Character,
clip: Option<usize>,
) -> CharacterId
pub fn add_character( &mut self, ch: &Character, clip: Option<usize>, ) -> CharacterId
Register an animated character (RKC v3): upload its meshes as sprite
models + its embedded voxel clips as flipbooks, then spawn one
renderer instance per bone attachment — a static mesh sits at
its bone, a clip attachment plays back on its own clock. clip
selects a skeletal animation clip to drive the bones (None =
rest pose). Returns a CharacterId; advance it each frame with
advance_character.
Like clips, this works before any set_sprites;
a later set_sprites drops all registered characters.
Sourcepub fn advance_character(&mut self, id: CharacterId, dt: f64)
pub fn advance_character(&mut self, id: CharacterId, dt: f64)
Advance a character by dt seconds: tick its skeletal animation +
each clip attachment’s clock, then re-pose every attachment
(bone × local_offset) and select each clip’s current frame. No-op on
a stale id.
Sourcepub fn set_character_world_transform(
&mut self,
id: CharacterId,
xf: DynSpriteTransform,
)
pub fn set_character_world_transform( &mut self, id: CharacterId, xf: DynSpriteTransform, )
Move/re-orient a character to a new world transform xf (the root
limb’s world pose) without ticking its animation or clip clocks —
a teleport that holds the current animation frame (e.g. dragging a
paused character in an editor). Re-solves the skeleton from the new
root + re-poses every attachment; clip frames are left as-is. No-op on
a stale id.
Sourcepub fn remove_character(&mut self, id: CharacterId) -> bool
pub fn remove_character(&mut self, id: CharacterId) -> bool
Remove a character, dropping all its attachment instances and
freeing the sprite models + voxel clips it registered. Returns
false if id is stale.
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 a frame be captured for
take_capture — screenshots, photo
modes, golden tests. Works on both backends since QE.7a
(previously a GPU no-op — screenshots were impossible on the
backend most games run). CPU: the next
render copies its composited framebuffer.
GPU: arms take_capture to read back the
most recent frame.
Sourcepub fn take_capture(&mut self) -> Option<(Vec<u32>, u32, u32)>
pub fn take_capture(&mut self) -> Option<(Vec<u32>, u32, u32)>
Take the captured frame as packed 0x00RRGGBB pixels +
dimensions (the logical resolution on GPU —
post-SSAA/posterize, pre-upscale), or None if no capture was
requested / nothing rendered yet. The GPU readback blocks like
pick_depth — a hotkey path, not
per-frame — and returns None on wasm (WebGPU can’t block).
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 scene pass always writes depth (L3.1), so the pick works with or without sprites in the frame.
wasm GPU path (PW.1): one-frame latency. WebGPU has no
blocking readback, so the call submits the readback for
(x, y) and returns the latest completed pick — usually
None on the first call and the value on the next; the value
may correspond to the previously requested pixel. Poll by
calling again next frame. Hosts needing synchronous picks use
the CPU backend (which is synchronous everywhere).
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). On the
wasm GPU path the underlying Self::pick_depth has
one-frame latency — expect None on the first call after a
click and poll again next 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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
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