Native plugin backing the MMD bullet physics in Unity MMD Tools (UMT). It wraps Bullet 2.75 behind a small, flat Cdecl ABI that the managed UMT.MMDBulletPhysics P/Invoke wrapper calls into.
| Platform | ABI | Output |
|---|---|---|
| Windows | x64 | UMTNativePlugin.dll |
| Windows (static) | x64 | UMTNativePlugin.lib (-DUMT_STATIC=ON) |
| Android | arm64-v8a | libUMTNativePlugin.so |
| Web | WebAssembly | libUMTNativePlugin.a |
The build is standalone and does not require Unity. Optionally, it can also copy the finished binary into a UMT Unity package's Plugins/ folder via a CMake POST_BUILDstep whenUMT_PACKAGE_DIR` is set (see Output).
UMTNativePlugin/
├── CMakeLists.txt CMake project (Bullet subdirs + plugin target)
├── Source/
│ ├── API.h extern "C" exported API surface
│ ├── NativeContext.h opaque context + blittable struct/enum layouts
│ └── NativeContext.cpp Bullet-backed implementation
├── Toolchains/
│ └── Android-aarch64.cmake NDK toolchain wrapper (auto-selected)
├── ThirdParty/
│ └── bullet-2.75/ Optional local Bullet checkout : NOT committed
├── LICENSE MIT (this project's own source)
├── .gitignore
└── README.md
The struct layouts in
Source/NativeContext.h(RigidBodySimulationData,NativeJointData) and the enums (RigidBodyShape,RigidBodyMode,JointType) are byte-for-byte layout-critical and must stay in sync with their managed counterparts inMMDBulletPhysics.cs/MMDRigidBody.cs. Do not reorder fields on one side without the other : it corrupts the marshalled data.
- CMake ≥ 3.16
- A C++17 compiler
- Windows: Visual Studio 2022+ (MSVC14.3+)
- Android: an Android NDK (r27 recommended) and Ninja
- Web: an Emscripten SDK (3.1.x recommended) and Ninja
- Network access on first configure (to download Bullet) or a local checkout for offline builds
A CMake build directory is single-toolchain : use a separate build directory per target.
cmake -S . -B build -G "Visual Studio 18 2026" -A x64
cmake --build build --config Release --target UMTNativePluginHosts that link the plugin directly through their own build system (e.g. Unreal Engine via UBT) consume it as a static library instead of loading a .dll at runtime. -DUMT_STATIC=ON builds UMTNativePlugin.lib (defining UMT_NATIVE_STATIC, which drops __declspec(dllexport) so the archive exposes plain extern "C" symbols). Use a separate build directory so it does not clobber the shared-library build/:
cmake -S . -B build-static -DUMT_STATIC=ON
cmake --build build-static --config Release --target UMTNativePluginAdd -DBulletRoot=/path/to/bullet-2.75 (e.g. an existing build/_deps/bullet275-src) to reuse a local Bullet checkout and skip the download. A POST_BUILD step merges the three static Bullet archives into UMTNativePlugin.lib (via lib.exe /OUT: on MSVC), so the host links one self-contained library and needs no knowledge of Bullet:
build-static/bundled/UMTNativePlugin.lib— the merged, self-contained archive (link this)Source/API.h+Source/NativeContext.h— the ABI surface (include these)
build-static/Release/UMTNativePlugin.libis the unmerged target output (NativeContext only) — linkbundled/UMTNativePlugin.libinstead.
The library compiles against the dynamic CRT (/MD, MSVCRT), matching an Unreal editor build. In the Unreal MMD Tools plugin it is staged as Plugins/UnrealMMDTools/External/UMTNativePlugin/lib/Win64/UMTNativePlugin.lib (headers under .../include) and linked via PublicAdditionalLibraries in the editor module's Build.cs.
Requires Ninja and an Android NDK. CMakeLists.txt activates the Android toolchain
cmake -S . -B build-android-arm64 -G Ninja \
-DANDROID_NDK="/path/to/android-ndk" \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-android-arm64 --config ReleaseANDROID_NDK can be any standalone NDK install (e.g. from Android Studio's SDK Manager, the sdkmanager CLI, a manual download or a Unity-bundled NDK <...>/Editor/Data/PlaybackEngines/AndroidPlayer/NDK ).
Requires Ninja and an Emscripten SDK, and builds through the Emscripten toolchain file. The Unity-bundled Emscripten is recommended (<...>/Editor/Data/PlaybackEngines/WebGLSupport/BuildTools/Emscripten) since it matches the Editor's WebGL toolchain but it ships no .emscripten config and is not on PATH, so set up a generated config, a writable cache, and PATH before configuring.
$em = "$<...>/Editor/Data/PlaybackEngines/WebGLSupport/BuildTools/Emscripten"
$env:EMSDK_PYTHON = "$em/python/python.exe"
$env:EM_CONFIG = "$env:TEMP/umt-emscripten.config"
$env:EM_CACHE = "$env:TEMP/umt-em-cache"
Set-Content $env:EM_CONFIG @("LLVM_ROOT = '$em/llvm'", "BINARYEN_ROOT = '$em/binaryen'", "NODE_JS = '$em/node/node.exe'", "EMSCRIPTEN_ROOT = '$em/emscripten'", "COMPILER_ENGINE = NODE_JS", "JS_ENGINES = [NODE_JS]")
$env:PATH = "$em/emscripten;$em/node;$env:PATH"
cmake -S . -B build-web -G Ninja `
-DCMAKE_TOOLCHAIN_FILE="$em/emscripten/cmake/Modules/Platform/Emscripten.cmake" `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_INSTALL_PREFIX="build-web/_install"
cmake --build build-web-DCMAKE_INSTALL_PREFIX is required: the Emscripten toolchain otherwise derives the install prefix from em-config CACHE, whose Windows .bat wrapper echoes batch lines into the captured output and corrupts CMakeCache.txt. Passing the prefix ourselves skips that probe.
target_link_libraries does not fold the Bullet archives into libUMTNativePlugin.a, so a POST_BUILD step bundles them into one self-contained archive for Unity to link.
Offline / air-gapped builds. Point at a local Bullet 2.75 checkout to skip the download entirely:
cmake -S . -B build -DBulletRoot=/path/to/bullet-2.75By default the build is standalone: the binary lands in the build tree (e.g. build/Release/UMTNativePlugin.dll) with no Unity coupling.
To also drop the binary into a UMT Unity package so Unity can hot-reload it, set UMT_PACKAGE_DIR to that package folder; a POST_BUILD step then copies it into the package's Plugins/ folder:
cmake -S . -B build -G "Visual Studio 18 2026" -A x64 \
-DUMT_PACKAGE_DIR=/path/to/com.candidumgames.unitymmdtools- Windows :
<UMT_PACKAGE_DIR>/Plugins/Windows/x64/UMTNativePlugin.dll - Android :
<UMT_PACKAGE_DIR>/Plugins/Android/arm64-v8a/libUMTNativePlugin.so - Web :
<UMT_PACKAGE_DIR>/Plugins/Web/libUMTNativePlugin.a
Override the exact destination instead with -DUnityPluginOutput=<path>.
If you change the managed marshalling structs, rebuild before entering Play mode.
| Option | Default | Purpose |
|---|---|---|
UMT_STATIC |
OFF |
Build a static library (.lib/.a) with plain extern "C" symbols for hosts that link it directly (e.g. Unreal Engine), instead of a shared library |
BulletRoot |
(empty → auto-download) | Local Bullet 2.75 root; set to skip the download |
BULLET_2_75_URL |
Google Code archive zip | Bullet 2.75 archive URL |
BULLET_2_75_SHA256 |
fe2e369…61fe8f |
Pinned archive checksum |
UMT_PACKAGE_DIR |
(empty → no copy) | UMT package to copy the binary into; empty skips copy |
UnityPluginOutput |
${UMT_PACKAGE_DIR}/Plugins/<platform>/<abi> |
Exact copy destination (overrides the above) |
ANDROID_NDK |
(unset) | Android NDK root; set to cross-compile for Android |
ANDROID_ABI |
arm64-v8a |
Android target ABI |
ANDROID_PLATFORM |
android-24 |
Android minimum API level |
ANDROID_STL |
c++_static |
Android C++ runtime |
The full surface is documented in Source/API.h.
MMDBulletPhysicsCreate/MMDBulletPhysicsDestroy: context lifecycleMMDBulletPhysicsSetConfig: override ground-plane normal/constant, MMD→Unity unit scale, and scaled convex margin (defaults reproduce the historical hardcoded values)MMDBulletPhysicsReset: deterministic reset from a seedMMDBulletPhysicsBuildRigidBodies/MMDBulletPhysicsBuildJoints: world constructionMMDBulletPhysicsBuildGround/MMDBulletPhysicsSetGroundCollisionEnabled: ground planeMMDBulletPhysicsSetRigidBodyTransforms/MMDBulletPhysicsGetRigidBodyMotionTransforms: transform syncMMDBulletPhysicsShiftRigidBodyPosition: world-space position shiftMMDBulletPhysicsStepSimulation: advance with fixed sub-steps
This project's own source (Source/, CMakeLists.txt, Toolchains/) is licensed under the MIT License.
Bullet is licensed under its own terms (zlib).