Shared, cross-platform LP64 modern-C++ runtime for native ports of Milo-engine games. It is the deliverable engine that the decomp repos link against to produce playable native builds:
- dc3-decomp — Dance Central 3 (Xbox 360, MSVC-PPC). Mature native port.
- rb3 — Rock Band 3 (Wii, MWCC). First bring-up target.
- rb3-xenon — Rock Band 3 (Xbox 360, MSVC-PPC). In progress.
The engine is game-agnostic and SDK-agnostic. It never includes per-game
headers (hamobj/, band3/, dance/, meta_*) or per-platform SDK headers
(xdk/, revolution/). It owns the parts that are the same for every port:
WebGPU graphics, miniaudio/FFmpeg audio, input, file I/O, the host-STL shim
layer, POSIX implementations of the os/ interfaces, and the CMake scaffolding.
Canonical plan:
rb3/docs/native/NATIVE_PORT_ROADMAP.mdWhat moves here, in what shape:rb3/docs/native/NATIVE_PORT_INVENTORY.md
Every native source file belongs to exactly one layer. Be explicit about which:
| Layer | Lives in | Compiles under | Role |
|---|---|---|---|
| Matched fork | <decomp>/src/system/** |
MWCC (RB3) / MSVC-PPC (DC3) | Asm-match verification. Off the native link line. |
| Engine runtime | milo-native-engine/src/** |
Clang LP64 only | The deliverable. Linked by every decomp's native build. |
| Per-decomp glue | <decomp>/native/src/** |
Clang LP64 only | Compat shims (mwcc_compat.h / msvc_compat.h), SDK shims (rvl_shims.cpp / xdk_shims.cpp), link glue, game-specific stubs. |
The matched fork and the engine runtime are separate copies that drift
independently. They share header interfaces (e.g. os/ThreadCall.h) but
compile separate .cpp files; they are never merged.
The engine is a sibling repo, not a submodule. Each decomp's top-level
CMakeLists.txt pulls it in by path with a soft SHA pin (mismatch warns,
never fails) and assembles its own executable:
set(MILO_ENGINE_PATH "${CMAKE_SOURCE_DIR}/../milo-native-engine" CACHE PATH "")
set(MILO_ENGINE_PIN "<commit-sha>" CACHE STRING "")
# ... compare HEAD vs pin, message(WARNING) on mismatch ...
add_subdirectory(${MILO_ENGINE_PATH} ${CMAKE_BINARY_DIR}/milo-engine)
add_executable(rb3-native ${RB3_NATIVE_GLUE_SOURCES} ${RB3_MATCHED_SOURCES})
target_link_libraries(rb3-native PRIVATE milo-engine)
target_compile_definitions(rb3-native PRIVATE HX_NATIVE=1)There is no MILO_NATIVE_GAME=DC3|RB3 switch inside the engine. The engine
knows nothing about either game; each decomp links it plus its own sources.
cmake -B build
cmake --build build # produces build/libmilo-engine.aThe engine targets Clang LP64 C++17. Standalone configure autodetects
clang/clang++. Useful options:
| Option | Default | Effect |
|---|---|---|
MILO_ENGINE_BUILD_TOOLS |
OFF |
Build milo-viewer, milo2gltf, render-test (Phase 0.2). |
MILO_ENGINE_BUILD_TESTS |
ON standalone |
Engine-only convergence test suite (Phase 0.2). |
MILO_ENGINE_BUILD_GFX |
ON |
Build the WebGPU gfx + *_Wgpu platform backends (and pull Dawn/glfw/imgui). RB3 sets this OFF — its 2010-era rndobj can't yet compile the DC3-wired GPU layer (see roadmap Phase 2). With a consumer-injected MILO_ENGINE_DECOMP_PLATFORM_EXCLUDE basename list, the consumer can also drop individual platform TUs whose RB3-header shape doesn't match. |
MILO_BUILD_WEB |
OFF |
Compile the Emscripten/web platform layer into libmilo-engine.a (WebAssets fetcher, File/CDReader/GpuDevice/AudioDevice/ImGuiBackend web variants, audio-worklet.js deploy step) and expose milo_engine_apply_web_target_options(<tgt>) for consumers. Only takes effect under emcmake cmake (EMSCRIPTEN toolchain). |
MILO_WEB_ASYNC |
ON |
When MILO_BUILD_WEB=ON, enables JSPI (non-blocking on-demand fetches via emscripten_sleep()) and defines MILO_WEB_ASYNCIFY=1. Requires Chrome 137+ / Firefox 139+. Set OFF to fall back to blocking synchronous XHR. |
MILO_ENGINE_ENABLE_ASAN |
OFF |
AddressSanitizer. |
MILO_ENGINE_LP64_AUDIT |
OFF |
Pointer-truncation warnings. |
The web layer is shared between RB3 and DC3; per-consumer identity is injected via two compile definitions on the consumer's executable target:
MILO_WEB_CANVAS_SELECTOR(string literal, default"#milo-canvas") — the DOM selector the canvas surface, GpuDevice resize logic, and ImGui input callbacks attach to. DC3 sets"#dc3-canvas", RB3 sets"#rb3-canvas".MILO_WEB_AUDIO_NS(identifier, defaultmilo) — prefix for the JSwindow._<ns>Audioglobal state, the user-facingwindow.<ns>CaptureAudio()debug helpers, the capture filename<ns>_web_capture.wav, and the C exports<ns>_start_capture / <ns>_download_capture / <ns>_dump_sab / <ns>_audio_stats. Token-pasted, so pass an unquoted identifier:-DMILO_WEB_AUDIO_NS=dc3not'"dc3"'. The AudioWorklet processor name (milo-audio-processor) is fixed engine-wide.
Example consumer wiring:
add_executable(my-app main_web.cpp ...)
target_link_libraries(my-app PRIVATE milo-engine)
milo_engine_apply_web_target_options(my-app) # base flag set
target_link_options(my-app PRIVATE # extend with tick export
-sEXPORTED_FUNCTIONS=["_main","_myAppTick"]
-sJSPI_EXPORTS=["_main","_myAppTick"])
target_compile_definitions(my-app PRIVATE
MILO_WEB_CANVAS_SELECTOR="#my-canvas"
MILO_WEB_AUDIO_NS=myapp)milo-native-engine/
├── CMakeLists.txt engine-only; per-game source lists live in each decomp
├── cmake/ helper modules
├── include/ vendored single-headers (cgltf, stb, httplib, ...)
├── third_party/ vendored libs (miniaudio.h) — no ncnn
├── src/
│ ├── gfx/ WebGPU device, pipelines, post-processing, shadow
│ ├── audio/ miniaudio device + web adapter
│ ├── platform/ file I/O, input, pthread threading, renderer glue
│ │ └── web/assets/ audio-worklet.js + missing_stubs.js (deployed by helper)
│ ├── char/ engine character helpers (CharTwistSolver)
│ ├── stl/ host-STL shim layer (the STL ABI seam)
│ ├── system/ NEW clean-LP64 impls of os/ interfaces
│ ├── export/ glTF/material/texture exporters (tool target)
│ ├── tools/ CLI tools (opt-in)
│ ├── viewer/ standalone milo viewer (opt-in)
│ └── render_test/ programmatic render test (opt-in)
├── tests/ engine-only convergence test subset
└── docs/ engine docs + session notes (mirrors decomp layout)
Subsystem trees are empty in Phase 0.1 and populated by the Phase 0.2
extraction from dc3-decomp/native/.
Phase 0 — COMPLETE. The engine is extracted, consumed by both decomps, and gated by two convergence test suites. Substeps closed:
- 0.1 — engine repo skeleton; valid near-empty
libmilo-engine.a. - 0.2 — engine-clean code (gfx/audio/char/clean-platform) extracted out of
dc3-decomp/native/; DC3 switched to consume this repo via a consumer-injected context model; engine-only tests moved in asmilo-engine-tests. - 0.2a —
Rnd_Wgpugraduated into the engine behind a game-agnosticGameRenderHookinterface (src/platform/); DC3 suppliesHamRenderHook, RB3 will supplyBandRenderHook. - 0.2b —
Memory_Native+ThreadCall_Nativegraduated with POSIX-only impls (pthread +sem_t); they no longer#include "xdk/XAPILIB.h". - 0.3 —
rb3/native/stood up; headlessrb3-dtaparses 138 real RB3 songs from arkhelper-extracted assets (milestone (a)). - 0.4 — per-repo CI workflows (engine + dc3 + rb3) for native build + test.
Phase 1 — rb3-native milestone (b): COMPLETE. rb3-native injects RB3's
MWCC matched-fork context and links the engine built GFX-off, runs to a clean
exit, and dumps .milo scene-tree headers (object names + class types) — 60/60
sampled milos, 0 crashes. This added the MILO_ENGINE_BUILD_GFX option +
MILO_ENGINE_DECOMP_PLATFORM_EXCLUDE seam: RB3's older rndobj can't compile the
DC3-wired WebGPU layer (engine Part_Wgpu needs RndParticleSys::NumTilesAcross;
WgpuRnd : NgRnd vs RB3's older Rnd), so RB3 links the engine's non-GPU half.
A full object-graph load and RB3 rendering are the next steps — see the roadmap's
Critical path to a booting RB3.
The engine now owns gfx / audio / char / clean-platform = 50 TUs (was 47;
+Memory_Native, +ThreadCall_Native, +Rnd_Wgpu/GameRenderHook), plus the
GameRenderHook interface in src/platform/.
Deferred per-decomp glue — matched-fork interface headers that still pull
SDK/game couplings; they graduate once HX_NATIVE-gated:
PlatformMgr_Native (xdk/XSOCIAL), RenderState_Native (xdk/D3D9),
Skeleton_Native (Kinect), HttpServer+DebugPanel
(telemetry/DC3_HTTP_SERVER), MeshFilter (DC3 hardcoded Kinect mesh skip
list).
Convergence gates:
- DC3's
milo-tests(linkslibmilo-engine.a): 371/371 pass. - In-engine
milo-engine-tests: 191/195 as of the 2026-05-27 session (1 intentional skip). The 4 failures are allRndCamProjectionTest(perspective/orthographic projection-matrix checks) and are pre-existing / not introduced by recent engine changes — suspected clang-22 float-codegen drift; see roadmap Known issues (K1). They were 195/195 under the earlier toolchain.
Published at https://github.com/freeqaz/milo-native-engine (public). Both
decomps pin engine 54b9fa011e0a90c087ed2c6cc0b167729c87caec. See the roadmap's
Status Log for the live
phase tracker.