scenix 1.5.0

Facade crate for the scenix Renderer APIs.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# scenix

> Modular Rust-native 3D scenes for native and WASM apps.

[![CI](https://github.com/AarambhDevHub/scenix/actions/workflows/ci.yml/badge.svg)](https://github.com/AarambhDevHub/scenix/actions)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)

scenix `1.5.0` is the current stable release. It adds cross-platform controls,
touch/gamepad/pointer-lock input, selection and transform primitives, reusable
gizmos, inspector snapshots, optional egui rendering, and on-demand GPU picking.
CPU authoring stays lightweight by default while GPU, browser, and UI paths
remain opt-in.

## Install

Most applications start with the facade crate:

```toml
[dependencies]
scenix = "1"
```

Enable optional systems only when needed:

```toml
[dependencies]
scenix = { version = "1", features = ["loader"] }
scenix = { version = "1", features = ["renderer", "post"] }
scenix = { version = "1", features = ["animato"] }
scenix = { version = "1", features = ["wasm"] }
scenix = { version = "1", features = ["interaction"] }
scenix = { version = "1", features = ["editor", "egui"] }
```

Focused crates can be used directly:

```toml
[dependencies]
scenix-math = "1"
scenix-core = "1"
scenix-input = "1"
scenix-scene = "1"
scenix-camera = "1"
scenix-mesh = "1"
scenix-material = "1"
scenix-light = "1"
scenix-texture = "1"
scenix-loader = "1"
scenix-renderer = "1"
scenix-post = "1"
scenix-raycaster = "1"
scenix-helpers = "1"
scenix-animato = "1"
scenix-wasm = "1"
```

For `no_std` CPU authoring:

```toml
[dependencies]
scenix-math = { version = "1", default-features = false, features = ["libm"] }
scenix-core = { version = "1", default-features = false }
scenix-input = { version = "1", default-features = false }
scenix-scene = { version = "1", default-features = false }
scenix-camera = { version = "1", default-features = false }
scenix-mesh = { version = "1", default-features = false }
scenix-material = { version = "1", default-features = false }
scenix-light = { version = "1", default-features = false }
scenix-texture = { version = "1", default-features = false }
scenix-raycaster = { version = "1", default-features = false }
scenix-helpers = { version = "1", default-features = false }
```

`scenix-loader`, `scenix-renderer`, `scenix-post`, `scenix-animato`, and
`scenix-wasm` are optional `std` paths. `scenix-animato` continues to target
Animato `1.7.0`.

## Feature Flags

| Feature | Default | Description |
| --- | --- | --- |
| `std` | yes | Standard-library support for CPU crates. |
| `scene`, `camera`, `mesh`, `material`, `light`, `texture` | yes | CPU authoring crates. |
| `raycaster`, `helpers` | yes | BVH picking and debug line helper data. |
| `interaction` | no | Convenience bundle for scene, camera, raycaster, and helpers. |
| `editor` | no | Inspector snapshots and editor-facing selection/transform data. |
| `egui` | no | Read-only egui inspector adapter; the host owns its window/render loop. |
| `loader` | no | Asset packages, asset manager, glTF/GLB extension metadata, OBJ/MTL, STL, image, KTX2, HDR/EXR loading, and exporters. |
| `renderer` | no | `wgpu` renderer with surface/headless targets. |
| `post` | no | Full-screen post-processing stack; use with `renderer`. |
| `animato` | no | Animato bridge: procedural tween/spring tracks **and** the clip-based Animation Runtime (clips, actions, mixer, loop modes, crossfade, additive blending, markers/events, light/morph targets, retargeting, CPU skinning). Targets Animato `1.7.0`. |
| `wasm` | no | Browser canvas wrapper with WebGPU first, WebGL2 full fallback, WebGL1 reduced fallback, and generated demo scene. |
| `serde` | no | Serialization support where the focused crate supports it. |

## Quick Start

### Controls and interaction

```rust
use scenix::{
    ArcballController, InputState, PerspectiveCamera, PointerButton, Vec2, Vec3,
    ViewportMetrics,
};

let mut input = InputState::new(ViewportMetrics::new(Vec2::new(1280.0, 720.0), 2.0));
input.on_pointer_down(PointerButton::Left);
input.on_pointer_move(Vec2::new(24.0, 12.0));
input.on_scroll(-0.25);

let mut controls = ArcballController::new(Vec3::ZERO, 5.0);
let mut camera = PerspectiveCamera::default();
controls.update_from_input(&input, 1.0 / 60.0);
controls.apply_to_perspective(&mut camera);
input.end_frame();
```

`InputState` also accepts fixed-capacity touch contacts, two-finger gestures,
four standard gamepads, and pointer-lock relative motion without allocating in
the event/control hot path.

### Scene authoring and animation

```rust
use std::collections::BTreeMap;
use scenix::{
    CameraId, CameraStores, Geometry, MaterialId, MeshId, NodeAnimationTarget, NodeAnimator,
    PbrMaterial, PerspectiveCamera, SceneGraph, SceneNode, ScenixAnimationDriver, Vec3,
    Vec3Track, box_geometry,
};

# fn run() -> Result<(), scenix::ValidationError> {
let mesh_id = MeshId::new(1);
let material_id = MaterialId::new(1);

let mut meshes = BTreeMap::<MeshId, Geometry>::new();
meshes.insert(mesh_id, box_geometry(1.0, 1.0, 1.0, 1, 1, 1));

let mut scene = SceneGraph::new();
let cube = scene.add(SceneNode::mesh("cube", mesh_id, material_id));

let mut driver = ScenixAnimationDriver::new();
driver.add_node(NodeAnimator::new(
    cube,
    NodeAnimationTarget::Translation(Vec3Track::tween(
        Vec3::ZERO,
        Vec3::new(1.0, 0.0, 0.0),
        0.5,
    )),
));

let camera_id = CameraId::new(1);
let mut perspective = BTreeMap::from([(
    camera_id,
    PerspectiveCamera::new(60.0, 1.0, 0.1, 100.0)
        .position(Vec3::new(0.0, 0.0, 4.0))
        .target(Vec3::ZERO),
)]);
let mut orthographic = BTreeMap::new();
let mut cameras = CameraStores {
    perspective: &mut perspective,
    orthographic: &mut orthographic,
};
let mut materials = BTreeMap::from([(material_id, PbrMaterial::new())]);
let mut skeletons = Vec::new();

driver.tick(0.5, &mut scene, &mut cameras, &mut materials, &mut skeletons)?;
scene.update_world_transforms();
# Ok(())
# }
```

### Headless Rendering

```rust
use scenix::{Renderer, RendererConfig, PerspectiveCamera, Vec3};

# async fn run(scene: &scenix::SceneGraph) -> Result<(), scenix::ScenixError> {
let mut renderer = Renderer::headless(RendererConfig::new(256, 256)).await?;
let camera = PerspectiveCamera::new(60.0, 1.0, 0.1, 100.0)
    .position(Vec3::new(0.0, 0.0, 5.0))
    .target(Vec3::ZERO);
let stats = renderer.render(scene, &camera)?;
assert!(stats.frame_index > 0);
# Ok(())
# }
```

### Loading Assets

```rust
use scenix::{AssetManager, RendererAssetExt};

# async fn run() -> Result<(), scenix::ScenixError> {
let mut manager = AssetManager::new();
let package = manager.load_file("scene.glb")?;

let mut renderer = scenix::Renderer::headless(scenix::RendererConfig::new(512, 512)).await?;
let uploaded = renderer.register_asset_package(&package)?;
println!("meshes={}, textures={}", uploaded.meshes, uploaded.textures);
# Ok(())
# }
```

### Animation Runtime

The v1.4.0 `AnimationMixer` plays clip-based animation on top of Animato:

```rust
use scenix::{
    AnimationClip, AnimationMixer, ClipChannel, ClipTrack, KeyframeInterpolation,
    KeyframeVec3, LoopMode, NodeProperty, PropertyBinding, SceneGraph, SceneNode, Vec3,
};

let mut scene = SceneGraph::new();
let node = scene.add(SceneNode::new("mover"));

let clip = AnimationClip::empty("move").with_channel(ClipChannel {
    binding: PropertyBinding::Node { node_id: node, property: NodeProperty::Translation },
    track: ClipTrack::Vec3(KeyframeVec3::new(
        vec![0.0, 1.0],
        vec![Vec3::ZERO, Vec3::new(5.0, 0.0, 0.0)],
        KeyframeInterpolation::Linear,
    )),
});

let mut mixer = AnimationMixer::new();
let clip_index = mixer.add_clip(clip);
let action = mixer.add_action(clip_index);
mixer.action_mut(action).unwrap().set_loop_mode(LoopMode::REPEAT);
mixer.action_mut(action).unwrap().play(0.0);

// each frame:
// mixer.tick(dt, &mut scene, &mut cameras, &mut materials,
//            &mut lights, &mut skeletons, &mut morphs)
```

### Raycasting

```rust
use std::collections::BTreeMap;
use scenix::{
    Geometry, MaterialId, MeshId, PerspectiveCamera, Raycaster, SceneGraph, SceneNode,
    Vec2, Vec3, box_geometry,
};

let mesh_id = MeshId::new(1);
let material_id = MaterialId::new(1);
let meshes = BTreeMap::<MeshId, Geometry>::from([(
    mesh_id,
    box_geometry(1.0, 1.0, 1.0, 1, 1, 1),
)]);

let mut scene = SceneGraph::new();
scene.add(SceneNode::mesh("cube", mesh_id, material_id));
scene.update_world_transforms();

let camera = PerspectiveCamera::new(60.0, 1.0, 0.1, 100.0)
    .position(Vec3::new(0.0, 0.0, 4.0))
    .target(Vec3::ZERO);
let ray = Raycaster::from_camera_ndc(&camera, Vec2::ZERO);

let mut raycaster = Raycaster::new();
raycaster.build_bvh(&scene, &meshes).unwrap();
assert!(raycaster.cast_ray(ray, &scene, &meshes).is_some());
```

## Workspace

| Crate | Role |
| --- | --- |
| `scenix` | Facade crate with stable v1 feature flags. |
| `scenix-math` | `no_std` vectors, matrices, quaternions, transforms, rays, and bounds. |
| `scenix-core` | IDs, colors, errors, and shared traits. |
| `scenix-input` | Pointer, keyboard, touch, gesture, gamepad, pointer-lock, and viewport state. |
| `scenix-scene` | Scene graph plus editor metadata, layers, snapping, and selection state. |
| `scenix-camera` | Cameras plus orbit, fly, arcball, trackball, map, first-person, and pointer-lock controls. |
| `scenix-mesh` | Geometry buffers, primitives, instancing, batching, and morph targets. |
| `scenix-material` | GPU-free material descriptions and pipeline keys. |
| `scenix-light` | Lights, shadow settings, and light probes. |
| `scenix-texture` | CPU textures, samplers, atlases, video updates, and mipmaps. |
| `scenix-loader` | Optional CPU asset packages, asset manager, importers, exporters, diagnostics, and cache. |
| `scenix-renderer` | Optional `wgpu` renderer with on-demand ID/normal/depth editor picking. |
| `scenix-post` | Optional `wgpu` post-processing effects. |
| `scenix-raycaster` | BVH ray picking, marquee selection, drag planes, and transform interactions. |
| `scenix-helpers` | Debug lines, reusable gizmos, selection visuals, snap grids, and optional egui. |
| `scenix-animato` | Optional Animato bridge. |
| `scenix-wasm` | Optional browser canvas wrapper with WebGPU and WebGL paths. |

## Examples

The facade crate registers the example set from [ARCHITECTURE.md](./ARCHITECTURE.md):

```sh
cargo run -p scenix --example hello_cube --features renderer
cargo run -p scenix --example pbr_sphere --features renderer
cargo run -p scenix --example physical_material --features renderer
cargo run -p scenix --example toon_shading --features renderer
cargo run -p scenix --example gltf_scene --features "loader renderer"
cargo run -p scenix --example asset_pipeline --features "loader renderer"
cargo run -p scenix --example asset_manager --features loader
cargo run -p scenix --example export_scene --features loader
cargo run -p scenix --example animation_import --features loader
cargo run -p scenix --example compressed_assets --features loader
cargo run -p scenix --example shadow_demo --features renderer
cargo run -p scenix --example raycasting
cargo run -p scenix --example post_processing --features "renderer post"
cargo run -p scenix --example instanced_mesh
cargo run -p scenix --example animato_integration --features animato
cargo run -p scenix --example animation_runtime --features "animato scene"
cargo run -p scenix --example animation_mixer --features "animato scene material light"
cargo run -p scenix --example skeleton_skinning --features "mesh animato"
cargo run -p scenix --example animation_events --features "animato scene light"
cargo run -p scenix --example orbit_camera
cargo run -p scenix --example lod_demo
cargo run -p scenix --example morph_targets
cargo run -p scenix --example fog_demo
cargo run -p scenix --example helpers_demo
cargo run -p scenix --example controls_showcase
cargo run -p scenix --example selection_and_drag
cargo run -p scenix --example transform_gizmo
cargo run -p scenix --example editor_inspector --features egui
cargo run -p scenix --example renderer_picking --features renderer
cargo run -p scenix --example sprite_particles
cargo run -p scenix --example environment_map --features renderer
cargo run -p scenix --example render_target_capture --features renderer
```

The browser example lives in `examples/wasm_viewer`:

```sh
rustup target add wasm32-unknown-unknown
cargo check --manifest-path examples/wasm_viewer/Cargo.toml --target wasm32-unknown-unknown
```

## Website

The static website is a standalone Leptos CSR app in `website/`. It is intentionally outside the main workspace so website dependencies do not affect normal library users.

```sh
cd website
trunk serve
trunk build --release --public-url /scenix/
```

GitHub Pages deployment is handled by `.github/workflows/pages.yml`, which builds `website/dist` with Trunk and deploys it at `/scenix/`. The demo uses `scenix-wasm`: it tries WebGPU where safe, falls back to a WebGL2 renderer with the same generated scene/material/light controls when WebGPU is unavailable, uses reduced WebGL1 only as a last GPU path, and only uses the Canvas2D preview when both GPU paths are unavailable.

## Development Checks

```sh
cargo fmt --check
cargo clippy --workspace --all-features -- -D warnings
cargo test --workspace
cargo test --workspace --all-features
cargo test -p scenix-math -p scenix-core -p scenix-input -p scenix-scene -p scenix-camera -p scenix-mesh -p scenix-material -p scenix-light -p scenix-texture -p scenix-raycaster -p scenix-helpers -p scenix-animato --no-default-features
cargo test -p scenix-loader --all-features
cargo test -p scenix-raycaster -p scenix-helpers --all-features
cargo test -p scenix-animato --all-features
cargo test -p scenix --test scenix_v15 --all-features
cargo check -p scenix --no-default-features --features interaction
cargo check -p scenix --no-default-features --features editor
cargo check -p scenix --no-default-features --features egui
cargo check -p scenix-wasm --target wasm32-unknown-unknown --all-features
cargo check --manifest-path examples/wasm_viewer/Cargo.toml --target wasm32-unknown-unknown
SCENIX_RUN_GPU_TESTS=1 WGPU_BACKEND=vulkan cargo test -p scenix-renderer -p scenix-post --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps
cargo bench --workspace --no-run
cargo llvm-cov --workspace --all-features
```

## Documentation

- [Developer docs]./docs/README.md
- [Architecture]./ARCHITECTURE.md
- [Roadmap]./ROADMAP.md
- [Changelog]./CHANGELOG.md
- [Getting started]./docs/getting-started.md
- [Installation]./docs/installation.md
- [Quick start]./docs/quick-start.md
- [Concepts]./docs/concepts/README.md
- [Guides]./docs/guides/create-your-first-scene.md
- [API reference]./docs/api/facade-crate.md
- [Examples]./docs/examples/README.md
- [Recipes]./docs/recipes/README.md
- [Performance]./docs/performance/README.md
- [Deployment]./docs/deployment/README.md
- [Migration]./docs/migration/from-0.9-to-1.0.md
- [Reference]./docs/reference/feature-matrix.md
- [v1.5.0 release notes]./.github/release-notes/1.5.0.md
- [v1.4.0 release notes]./.github/release-notes/1.4.0.md
- [v1.3.0 release notes]./.github/release-notes/v1.3.0.md

## Known Limitations

- The renderer now uploads material textures, light data, environment descriptors, and render targets through real GPU resources. Advanced physical shading is a pragmatic realtime path, not an offline film renderer.
- Loader APIs produce CPU-side scenix data; `RendererAssetExt` is a convenience bridge that still uploads into renderer-owned GPU resources explicitly.
- Draco and meshopt compressed glTF assets currently produce explicit diagnostics unless preprocessed by an external converter.
- WebGL2 is the full browser fallback for the generated renderer scene when WebGPU is unavailable. WebGL1 remains a reduced last-resort fallback for older browsers.
- The website demo does not vendor large model assets.
- GPU tests require a Vulkan-capable device or Mesa lavapipe.

## License

Licensed under either of:

- Apache License, Version 2.0
- MIT license