Resident ready Static companion
kari_mikumao Lab

Systems

WMMD architecture: a hostable MMD runtime

The engine is split into layered Rust crates with hard dependency rules, so the same code can drive an editor, an AR host, or an offline renderer.

  • wmmd
  • architecture
  • rust
  • mmd

WMMD is a MikuMikuDance runtime and rendering backend I am writing in Rust.

The whole design constraint is one word: hostable. Today it drives a desktop editor. But I want the same codebase embedded in VJ tools, AR and VR runtimes, game engines, mocap pipelines, and offline renderers.

That only works if no layer secretly assumes it owns the window, the GPU, or the clock. Easy to say. Very easy to violate by accident, usually while fixing something else.

The Layering

The workspace is a set of crates arranged so dependencies point one way:

  • wmmd-clock - the transport clock, with zero external dependencies at all.
  • wmmd-runtime - the CPU-only simulation engine: scene data, skeletons, physics backends, the IK solver, and the frame update loop.
  • wmmd-asset - PMX/PMD parsing, VMD animation loading, texture analysis, and path resolution. Material work is delegated to wmmd-material.
  • wmmd-render - the backend-agnostic render interface. It depends on glam and serde and nothing else.
  • wmmd-render-wgpu - the wgpu implementation of that interface, owning every GPU pass.
  • wmmd-host-winit - a desktop surface, and only a surface.
  • wmmd-session, wmmd-ui-egui, wmmd-audio, wmmd-export, wmmd-editor, wmmd-desktop, wmmd-tools - host-neutral session state, panels, audio, offline export, and the applications on top.

The Invariants Are the Architecture

The dependency graph is enforced by a written list of invariants. Violating one is a bug, not a style question.

The ones that shape everything else:

  • wmmd-render has zero GPU dependencies and zero runtime dependencies. GPU layout types like CameraUniform live in the wgpu crate. The abstract interface never references runtime commands.
  • wmmd-runtime never loads files. The host calls the asset crate and pushes an AddModel command carrying an already-parsed asset. The engine has no opinion about your filesystem.
  • wmmd-render-wgpu never owns the swapchain. The host acquires the frame, builds a render target, calls render(), and presents. That single rule is what lets an AR or headless host exist at all.
  • wmmd-render-wgpu is egui-free, and wmmd-host-winit is surface-only. The GPU context lives in the render crate, so a host without winit can still create a device.
  • RenderRequest is the single interface point. Callers assemble camera, shadow center, lighting, debug flags, and frame, then hand it over.
  • wmmd-clock and wmmd-audio are standalone. Neither knows anything about scenes or rendering. The host wires them to the runtime.

Why I Bother

Every one of those rules is me refusing a shortcut that would have been easier in a desktop-only engine. Letting the renderer grab the swapchain is one line and saves real work today.

The payoff comes later. Adding a new surface - a Vision Pro host, a VJ plugin, an export job - becomes a matter of driving the interfaces that already exist, instead of forking the renderer.

The engine is model-agnostic by the same kind of rule. Fixes are made at the system level and never branch on a particular MMD model. If a bug reproduces with one file, the fix has to hold for the whole space of valid PMX and VMD inputs.