mittens-engine 0.7.0

A Vulkan and OpenXR scene engine with ECS, reactive signals, and Meow Meow scripting
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
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439

# mittens-engine 0.7.0
<img width="1920" height="745" alt="Screenshot_20260303_015535" src="https://github.com/user-attachments/assets/16d9656c-9df3-4a96-89bd-658d222e78d0" />

A hypermedia / web development inspired game engine specially made for social vr, vtubing, visual novels, css UI / spatial layout, and 3D character animation.


### Workspace crates

- `mittens-engine` 0.7.0 at the workspace root: Vulkan/OpenXR rendering, ECS,
  engine component materialization, and the Mittens-specific scripting host.
- [`meow-meow-script`]crates/meow-meow-script/README.md 0.6.0:
  host-neutral syntax, parser, runtime/session evaluator, configurable
  component/API catalogs, and the generic host protocol.
- [`mittens-query`]crates/mittens-query/README.md 0.6.0: CSS/MMQ parsing and
  host-neutral query-tree evaluation.

The dependency graph is acyclic: `mittens-engine` depends on both standalone
crates; neither standalone crate depends on the engine.

`mittens-engine` 0.7 removes the deprecated `ActionComponent`, `ActionSystem`,
and `Action.*` MMS constructors. Deferred animation now uses executable
`Keyframe.at(...) { ... }` blocks that capture live component handles. The
standalone `meow-meow-script` and `mittens-query` crates remain at 0.6.0.

For scripting, see the [MMS language overview](docs/meow_meow/README.md), the
[`meow-meow-script` crate and its Mittens integration](crates/meow-meow-script/README.md),
and the [host API boundary](docs/meow_meow/spec/host-call-api.md).

## Running examples

- Run examples in release mode by default: `cargo run --release --example <name>`.
- Avoid debug example runs unless you specifically need debug-only diagnostics or faster compile iteration.
- Large `.glb` model assets are omitted from the crates.io package. Every example
  calls `mittens_engine::example_support::ensure_model_assets()` before scene
  setup; if `assets/models` contains no `.glb` files, it runs
  `scripts/download-model-assets.sh` to fetch the example model bundle from
  GitHub. Run the script manually to prefetch models. Set
  `MITTENS_MODEL_ASSET_BASE_URL` to override the download source.

## Windowing
+ uses winit to make a window and passes the RawDisplayHandle to renderer to render into the window
+ provides user input events and frame loop

## Universe
+ holds all the layers below,
+ and provides simple API to build component trees and add them to the world

### Universe API (common helpers)

The `engine::Universe` type is a convenience wrapper around `World + SystemWorld + VisualWorld + CommandQueue`.

In addition to `add(...)` and `attach(parent, child)`, it provides a few higher-level helpers for
prefab-style workflows and safe subtree removal:

- `attach_clone(parent, prefab_root) -> Result<ComponentId, String>`
  - Clones the component subtree rooted at `prefab_root` (fresh `ComponentId`s and fresh GUIDs) and attaches it under `parent`.
  - Clone is done via component `encode`/`decode` using `ComponentCodec` (no JSON round-trip).
  - Note: if any components contain references to other components (e.g. action targets stored inside component payloads), those references are currently copied as-is and may need a future fixup pass.

- `remove_child(parent, index) -> Result<ComponentId, &'static str>`
  - Detaches the child immediately and queues deletion of that child subtree via the command queue.
  - Deletion is applied when the command queue is processed (after systems tick), so systems/visuals can cleanly unregister.

- `remove_children(parent) -> Result<Vec<ComponentId>, &'static str>`
  - Detaches all direct children and queues deletion of each child subtree (applied on command processing).

Example (prefab clone):

```rust
use mittens_engine::engine;

let prefab_root: engine::ecs::ComponentId = /* detached prefab subtree root */;
let parent: engine::ecs::ComponentId = /* some TransformComponent in the live scene */;

let instance_root = universe.attach_clone(parent, prefab_root)?;
// GUID is stored on the component record:
let guid = universe.world.get_component_record(instance_root).unwrap().guid;
```

## (component) World
+ stores list of components and topology (parent / child relationship between components)
+ components can have subcomponents
+ specific types of components register with SystemWorld and have methods that also call SystemWorld
+ registration / removal and methods of components that affect SystemWorld go through a CommandQueue and get applied after systems.tick() in the update loop.

## SystemWorld
+ handles the behaviors of components
+ can have one system's method invoked and then defer to one or more other systems
+ can call methods on components (via CommandQueue)
    + calls to component methods are applied after all systems have run their tick() method.

#### RenderableSystem
+ keeps a queue of CPUMesh from RenderableComponent that need to be converted to GpuMesh and uploaded into the GPU.

## VisualWorld
+ stores a snapshot of GpuRenderables
+ and builds cache, sorted by material pipeline, mesh, and texture
  + when ever RenderableSystem or LightSystem (or TransformSystem if involving renderables, lights or cameras) updates. 
  
#### RenderAssets
+ converts `CPUMesh` into `GPUMesh`

## VulkanoRenderer 
+ displays data from VisualWorld through vulkan
+ TODO: make WgpuRenderer for web / webasm

### Render phases (render graph summary)

Rendering is recorded in a single dynamic-rendering scope, but split into explicit phases (a small “render graph”) built by `VisualWorld` and recorded by `VulkanoRenderer`.

Current phase order (high level):

1. **Background** (instanced, no depth write)
2. **Background occluded+lit** (instanced, depth write ON for self-occlusion)
   - Then the renderer clears depth so background never occludes the foreground.
3. **Opaque** (instanced, depth write ON)
4. **Cutout** (instanced, alpha-tested)
5. **Transparent single-layer** (instanced)
6. **Transparent multi-layer** (sorted back-to-front, drawn one-by-one for correct blending)

See [docs/render-phases.md](docs/render-phases.md) for details and the relevant code entry points.

# Components

See the [MMS component guide](docs/how_to/guide/components.md) for the exhaustive component catalog and current scripting support.

## Transforms

Transforms are central in Mittens: most component subtrees are rooted at a `TransformComponent`, and many engine systems interpret the component tree as “a scene graph of nested transforms + things attached under them”.

0. **Brief intro: `TransformComponent`**
  - Stores local TRS (translation / rotation / scale) and a cached `matrix_world`.
  - Local TRS is represented as a *model matrix* (`transform.model`), and the engine propagates it through the component tree to compute `matrix_world` for nested transforms.

1. **How transforms can be nested**
  - A `TransformComponent` can parent other `TransformComponent`s.
  - Nesting is defined by the ECS topology (parent/child relationships in the component tree).

2. **What that means for model vs `matrix_world` propagation**
  - Each transform has a local `model` matrix derived from its TRS.
  - World-space transforms are computed by multiplying ancestor models down the tree:
    - `matrix_world(child) = matrix_world(parent) * model(child)`
  - `TransformSystem` caches `matrix_world` on each `TransformComponent` and uses it as the source of truth for systems that need world-space.
  - Topology changes (Attach/Detach) can require recomputation even if local TRS didn’t change; the engine has a dedicated intent for that (`UpdateTransformWorld`).

3. **Which systems are affected by transforms**
  - `RenderableSystem` / `VisualWorld`: instance model matrices for renderables
  - `CameraSystem`: camera view/projection updates when parent transform changes
  - `LightSystem`: point light world-space position updates
  - `CollisionSystem`: collider world-space updates
  - `SkinnedMeshSystem`: joint world matrices / skinning matrices become dirty
  - `BvhSystem` + `RaycastSystem`: BVH refit and raycast correctness depends on world matrices
  - `OpenXRSystem`: XR devices/cameras often read/write world transforms
  - Editor gizmos: visual alignment + drag application depend on consistent `matrix_world`

4. **Which systems determine / write transform intents**
  - User code: calling `TransformComponent::{set_position,set_rotation_*,set_scale}` queues `UpdateTransform`
  - `InputSystem`: movement/controls update transforms via `UpdateTransform`
  - `OpenXRSystem`: device pose application uses `UpdateTransform`
  - `CollisionResponseSystem`: kinematic collision response integrates motion via `UpdateTransform`
  - `TransformGizmoSystem`: editor gestures call transform setters (which queue `UpdateTransform`)

4.1 **Transform propagation pipelines / transform operators**
  - `TransformSystem::transform_changed(...)` is the core propagation pipeline: it recomputes cached `matrix_world` for a transform subtree and pushes side effects to dependent systems.
  - `TransformFilterComponent` is a “filter-as-node” operator that changes what descendants inherit (e.g. inherit translation+rotation but *not* scale). It’s used heavily for editor/gizmo visuals.
  - For deeper notes/specs:
    - `TransformFilterComponent` motivation: `docs/analysis/gizmo-transform-propagation.md`
    - Gizmo coord spaces (Local/World): `docs/spec/editor-gizmo-coord-spaces.md`
    - Transform update flow and refit/rebuild behavior: `docs/analysis/refresh-transform.md`


# Signals

This engine uses an explicit **drain-point** signal model.

Instead of letting systems mutate everything immediately (and in arbitrary order), code emits **signals** into the per-frame queue, and the engine drains them in a consistent sequence.

Signal types:

- **Events** (`EventSignal`): facts/observations ("parent changed", "drag started", ...).
  - Dispatched to handlers (global handlers and/or scoped handlers rooted at a scope subtree).
  - Event handlers should be *observers*: they typically emit follow-up intents rather than directly performing large mutations.

- **Intents** (`IntentSignal` carrying an `IntentValue`): requests for side effects ("attach", "set transform", "remove subtree", ...).
  - Executed at drain points.
  - Can be scheduled for the future via `at_beat(...)` (timed intents sit in a holding-pen until due).

Execution order (inside `SystemWorld::process_signals`):

1. **Dispatch ready events** to handlers.
   - Any *events emitted by handlers* are **deferred to the next tick**.
   - Any *intents emitted by handlers* are queued for later execution.

2. **Promote timed intents** that have become due at the current beat.

3. **Execute ready intents**.
   - Intents may emit more intents; those will run later in the same tick (after queue draining), up to the `max_signals` cap.
   - Intents may also emit events, but (like handler-emitted events) those are deferred to the next tick.

Implementation detail: intent execution is split into two layers:

- `RxIntentExecutor`: “interpretation” intents that expand into follow-up work.
- `RxMutationExecutor`: low-level canonical mutations (register/update/remove, etc.).

Scoped handler lifecycle: systems can install handlers rooted at a component subtree (e.g. gizmos). When a subtree is removed, any scoped handlers rooted in that deleted subtree are removed automatically.

See the [MMS signal guide](docs/how_to/guide/signals.md) for the architecture, exhaustive signal catalog, and current scripting support.


# Building Widgets (Panels & Tools)

Complex editor UI (like the `inspector_panel`, `paint_panel`, or `world_panel`) follows a data-driven projection pattern.

### 1. State & Reducers
Panels define their own domain-specific state and a pure reducer function to handle events.
- **State**: e.g., `InspectorWorkspaceState` or `PaintState`.
- **Reducer**: `fn reduce_state(old: &State, event: &Event) -> State`.

### 2. Event Adapters
Raw engine events (clicks, drags, signal emissions) are converted into high-level domain events by "adapters" (often scoped signal handlers installed at the widget root).

### 3. Data Renderer System
The `DataRendererSystem` manages the lifecycle of projecting a list of data items into a live component subtree. It ensures that when data changes, the previous visual subtree is cleaned up and a fresh one is attached to the target slot.

### 4. RendererSpec
You define how each item in your data list should be rendered using a `RendererSpec<T>`:

- **RendererSpec::Mms**: Project data into an MMS component factory.
  ```rust
  RendererSpec::Mms {
      asset_path: "assets/components/item.mms",
      export_name: "my_item",
      to_args: |data| vec![Value::String(data.label.clone())],
  }
  ```
- **RendererSpec::Rust**: Build the component tree directly in Rust.
  ```rust
  RendererSpec::Rust {
      render_fn: Box::new(|world, emit, data| {
          let root = world.add_component(...);
          // ... build tree ...
          Ok(root)
      }),
  }
  ```

## Working with MMS Components

For simpler widgets or reusable UI elements, you can define factory functions in `.mms` scripts.

### Calling component methods from animation keyframes

Keyframe blocks run in the live world when the keyframe becomes due, so they can call methods on captured component handles directly.

```javascript
Clock.bpm(60) {}

let cube_t = T.position(0.0, 0.0, 0.0).scale(0.5, 0.5, 0.5) {
    Transition {
        duration_beats(0.85)
        ease_in_out_sine()
        replace_same_target()
    }
    R.cube() {
        C.rgba(0.90, 0.75, 0.30, 1.0)
    }
}

cube_t

Animation.looping() {
    Keyframe.at(0) {
        cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.5, 0.5, 0.5])
    }
    Keyframe.at(1) {
        cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 3.14159 / 2, 0.0], [0.5, 0.5, 0.5])
    }
    Keyframe.at(2) {
        cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 3.14159, 0.0], [0.5, 0.5, 0.5])
    }
    Keyframe.at(3) {
        cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 3.14159 * 1.5, 0.0], [0.5, 0.5, 0.5])
    }
}
```

This is the simplest pattern for authoring animation-driven behavior in MMS: capture a live component handle with `let`, then mutate it from `Keyframe.at(...)` blocks.

### Playing and pausing an animation from MMS

Animation components are also live handles, so you can store them in a variable and call playback methods from signal handlers or other script logic.

```c
let anim = Animation.looping() {
    Keyframe.at(0) { cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.5, 0.5, 0.5]) }
    Keyframe.at(1) { cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 3.14159 / 2, 0.0], [0.5, 0.5, 0.5]) }
    Keyframe.at(2) { cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 3.14159, 0.0], [0.5, 0.5, 0.5]) }
    Keyframe.at(3) { cube_t.update_transform([0.0, 0.0, 0.0], [0.0, 3.14159 * 1.5, 0.0], [0.5, 0.5, 0.5]) }
}

anim

let pause_btn = T.position(-1.2, -1.2, 0.0).scale(0.35, 0.35, 0.35) {
    R.cube() {
        C.rgba(0.25, 0.55, 1.0, 1.0)
        Raycastable.enabled()
    }
}

let play_btn = T.position(1.2, -1.2, 0.0).scale(0.35, 0.35, 0.35) {
    R.cube() {
        C.rgba(0.30, 0.85, 0.45, 1.0)
        Raycastable.enabled()
    }
}

pause_btn
play_btn

on(pause_btn, "Click", fn(event) {
    anim.pause()
})

on(play_btn, "Click", fn(event) {
    anim.play()
})
```

See `examples/component-method-call.mms` for a complete runnable example.

### Reusable Buttons (`button.mms`)
The `assets/components/button.mms` file provides a standard button:

```javascript
import { button } from "assets/components/button.mms"

let my_btn = button("Click Me")
T.position(0, 0, 0) { my_btn }

// Attach signal handlers directly in MMS:
on(my_btn, "Click", fn(e) {
    print("Button was clicked!")
})
```

See `assets/components/` for more reusable primitives.


# REPL / CLI

There is a small stdin-driven REPL (processed on the main thread in `Universe::update()`) for inspecting the component tree.

## Commands

- `help` — print commands
- `ls` — list children of the current working component (or roots at `/`)
- `cd <name|index|guid|path>` — change working component
  - `cd /` goes to root
  - `cd ..` goes to parent
  - `cd /7v1:root/8v1:child` walks by `ComponentId` tokens and names
  - `cd <guid>` supports a global jump by GUID
- `pwd` — print a copy-pastable path for the current working component
- `cat [path]` — pretty-print JSON serialization of the subtree
  - `cat` with no args prints from the current working component
  - `cat /` prints the whole scene (all roots)
- `clear` / `cls` — clear the terminal

## Pipes

Pipes use `|` but they pipe *component objects* (ComponentIds), not strings.

- A trailing `|` prints an `ls`-style summary of the piped components.
  - Example: `cat / |`

### `grep`

`grep <pattern>` filters the piped components by matching against component properties (including `name`, `type`, `guid`, and encoded fields), and prints the full serialized value of any matching property.

- Example: `ls | grep color`
- Example: `cat /6v1:input | grep camera`


# Lifecycle

#### Frame loop:
```rust
// in engine::Universe:

/// Game/update step
  pub fn update(&mut self, _dt_sec: f32, _input: &InputState) {
      // each frame,
      // 1. Process input events (handled inside systems for now).
      // 2. Let systems call methods on components,
      //      for example, to update transforms or renderables, which
      //      will update VisualWorld can update draw_batches and give Renderer a snapshot
      self.systems.tick(&mut self.world, &mut self.visuals, _input);
      
      // Process commands after tick so any commands queued during tick are processed in the same frame
      self.systems.process_commands(&mut self.world, &mut self.visuals, &mut self.command_queue);
  }

  pub fn render(&mut self, renderer: &mut graphics::Renderer) {
      // Ensure VisualWorld contains only GPU-ready instances.
      self.systems
          .prepare_render(&mut self.world, &mut self.visuals, &mut self.render_assets, renderer);
      // TODO: rebuild inspector around component graph instead of entities.
      renderer.render_visual_world(&mut self.visuals)
              .expect("render failed");
  }
```


https://github.com/user-attachments/assets/ce4ac311-1087-4792-bec8-5dd012d848f2



## Profiling (flamegraph)

You can profile binaries (including examples) using `cargo-flamegraph` without adding any dependency to this project.

Prereqs:

- Install the Cargo subcommand: `cargo install cargo-flamegraph`
- Linux: install `perf` (Arch/EndeavourOS: `sudo pacman -S perf`)

Example (profile an optimized release build of an example with debug symbols and frame pointers):

```bash
CARGO_PROFILE_RELEASE_DEBUG=true \
  RUSTFLAGS="-C debuginfo=1 -C force-frame-pointers=yes" \
  cargo flamegraph --release --example vtuber-joints-example
```


# Credits:

Special thanks to [2gd4.me](https://2gd4.me) for designing font_system.png