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 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 towmmd-material.wmmd-render- the backend-agnostic render interface. It depends onglamandserdeand 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-renderhas zero GPU dependencies and zero runtime dependencies. GPU layout types likeCameraUniformlive in the wgpu crate. The abstract interface never references runtime commands.wmmd-runtimenever loads files. The host calls the asset crate and pushes anAddModelcommand carrying an already-parsed asset. The engine has no opinion about your filesystem.wmmd-render-wgpunever owns the swapchain. The host acquires the frame, builds a render target, callsrender(), and presents. That single rule is what lets an AR or headless host exist at all.wmmd-render-wgpuis egui-free, andwmmd-host-winitis surface-only. The GPU context lives in the render crate, so a host without winit can still create a device.RenderRequestis the single interface point. Callers assemble camera, shadow center, lighting, debug flags, and frame, then hand it over.wmmd-clockandwmmd-audioare 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.