JavaScript bindings to use FFL, the Wii U Mii renderer decompilation, in Three.js.
- Rendering
- Full rendering of Mii head models from the FFL decomp by aboood40091.
- Accurate shaders to reproduce lighting from Wii U, Miitomo, Switch, 3DS, Wii
- Including a port of the Wii U shader for THREE.WebGPURenderer.
- The shaders work exclusively in sRGB. If you don't know what this means and want to opt out, see this post from Don McCurdy.
- Linear color support from FFL, enabled with
ffl.module._FFLSetLinearGammaMode(1). Useful for built-in Three.js materials. - Misc: Multiple expressions, texture mipmaps, bounding box, basic icon creation, headwear coordinates/hair variants
- Data
- 3DS/Wii U Mii Data (
FFLStoreData) - Mii Studio data (raw "codes" or obfuscated URL "data")
- Wii Mii Data (
RFLCharData,RFLStoreData) - All data is verified by FFL.
- (Cannot currently edit/export data.)
- 3DS/Wii U Mii Data (
- Compatibility
- Implemented in JSDoc annotated and fully typed JavaScript calling into FFL in WASM.
- Just ESM import
ffl.jsand materials. In dist/ there are.d.tsdefinitions and browser non-module versions. - Base library is 140 KB minified.
- Just ESM import
- Tested from Three.js r144 up to r183 (latest as of writing), WebGL 1/2 and WebGPU.
- For WebGPU, use
FFLShaderNodeMaterial. For r152 and later, opt out of sRGB by following the link above.
- For WebGPU, use
- Implemented in JSDoc annotated and fully typed JavaScript calling into FFL in WASM.
There are currently two demos within examples: demo-basic.html and demo-minimal.html, both of which just show spinning Mii heads.
The main library is in ffl.js, and the materials are included in materials/, but built-in Three.js materials such as THREE.MeshStandardMaterial should work too.
To get started, I recommend looking at the demo code in examples/. In order to run them, you need to download the resource file AFLResHigh_2_3.dat (See the FFL-Testing README (search "resource file") for details), or use your own and change content property of meta#ffl-js-resource-fetch-path.
For more help, you can either examine the ffl.js source, or, generate documentation with TypeDoc, install it and run: typedoc ffl.js
This package isn't on npmjs.com, so install it from the repo instead: npm install https://github.com/ariankordi/FFL.js#v2.2.0 (replacing v2.2.0 with whatever is latest)
For the browser, you have to use <script type="module">, as well as adding import maps.
Include the following on your page.
<!-- Import maps. This correlates "import" statements
with the actual links for where to get them.
This example is using esm.sh, which acts most like
importing modules from npm.
-->
<script type="importmap">
{
"imports": {
"three": "https://esm.sh/[email protected]",
"three/": "https://esm.sh/[email protected]/",
"FFL.js": "https://esm.sh/*gh/ariankordi/[email protected]",
"FFL.js/": "https://esm.sh/*gh/ariankordi/[email protected]/"
}
}
</script>
<!-- The * before gh is to make sure that the import
uses our version of Three.js, and not theirs.-->
<!-- This is your JS code. It can be in a file too. -->
<script type="module">
import * as THREE from 'three'; // Include Three.js.
// Imports from FFL.js. More may be added as needed.
import { FFL, CharModel, FFLCharModelDescDefault, ModelIcon } from 'FFL.js';
import FFLShaderMaterial from 'FFL.js/materials/FFLShaderMaterial.js';
// NOTE:
// - In the browser, when not using esm.sh, ffl-emscripten.cjs needs
// to be included in a <script> tag since it's not a proper ES module.
// - For Node.js, you need to use examples/ffl-emscripten-single-file.cjs.
import ModuleFFL from 'FFL.js/ffl-emscripten.cjs';
// The example below renders a simple icon.
(async function () {
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(300, 300);
document.body.append(renderer.domElement);
// NOTE: You need to get AFLResHigh_2_3.dat from somewhere.
const ffl = await FFL.initWithResource(fetch('../AFLResHigh_2_3.dat'),
// If not using a CDN like esm.sh, then pass just "ModuleFFL" to CharModel directly.
ModuleFFL({locateFile: () => 'https://esm.sh/gh/ariankordi/[email protected]/ffl-emscripten.wasm'}));
/** Mii data from NNID: JasmineChlora */
const data = Uint8Array.fromHex('000d142a303f434b717a7b84939ba6b2bbbec5cbc9d0e2ea010d15252b3250535960736f726870757f8289a0a7aeb1');
const model = new CharModel(ffl, data, FFLCharModelDescDefault,
FFLShaderMaterial, renderer);
const scene = new THREE.Scene();
scene.add(model.meshes);
renderer.render(scene, /* camera */ ModelIcon.getCamera());
})();
</script>There are builds that don't need ES modules/"import" available in dist/, for example: dist/ffl.browser.js.
Shaders are available in global namespace (window.FFLShaderMaterial), but FFL.js itself is dropped in the FFLjs namespace. Example: await FFLjs.FFL.initWithResource(fetch('../AFLResHigh_2_3.dat'), ModuleFFL)
To actually use this, you'll need to use a UMD build of Three.js. They stopped supporting UMD, so the last one is r160: https://unpkg.com/[email protected]/build/three.min.js
You can adapt the previous example to fit all of these, but this is left as an exercise to the reader :)
This library depends on FFL built for Emscripten (in WASM). NOTE that this is already included in the repository as of writing, but you may either need to rebuild it, or I may remove it at some point.
Click to reveal build instructions.
-
You will need to make sure emsdk is installed and you can build binaries with Emscripten. This is mostly left as an exercise to the reader, but don't forget to activate your emsdk environment before continuing.
-
Pull FFL and its dependencies. For simplicity, you can actually just pull FFL-Testing (active branch) which has the dependencies.
git clone -b renderer-server-prototype --recursive https://github.com/ariankordi/FFL-Testing
- Recurse into
fflwithinFFL-Testing.
cd FFL-Testing/ffl
- Build using CMake with Emscripten.
Note that the argument to -DCMAKE_TOOLCHAIN_FILE= has to be within your emsdk directory, so please change that in the command below:
cmake -S . -B build-em -DCMAKE_TOOLCHAIN_FILE=/path/to/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DFFL_WITH_RIO=../rio/ -DCMAKE_BUILD_TYPE=Release
# Remember to find and fill in emsdk path: ^^^^^^^^^
cmake --build build
- Notable build options:
- Use
-DFFL_NO_NINTEXUTILS=ONfor a smaller build if you will not be using resource (.dat) files for Wii U. - To build JS only you can use
-DFFL_BUILD_WASM=OFF, which may be more convenient/compatible at 2x the size of the wasm binary. - If you want to pass other options to emcc, use
-DCMAKE_EXE_LINKER_FLAGS="-s SINGLE_FILE=1", for example.
- Use
- If that worked, find and copy the library.
- It should be sitting in
build-em(or whatever folder you chose) as:ffl-emscripten.js,ffl-emscripten.wasm
- Finally, in order to use the library, you'll need an FFL resource such as
FFLResHigh.dat,AFLResHigh_2_3.dat, etc.
- See the FFL-Testing README (search "resource file") to know how to acquire this.
If you run into issues with dependencies here, see the FFL-Testing repo.
The library is using eslint, so I recommend linting if you ever want to contribute back. Install dev dependencies with npm install -D then use npm run-script lint.
Additionally use npm run-script check-types to validate types, and npm run-script prepare to make .d.ts definitions and .browser.js builds.
-
I would love to have more demos/examples.
- Body model rendering and accurate scaling
- Editing Mii data (CharInfo)
- Export models using Three.js GLTFExporter
- Render a ton of icons + make random Mii data
- TBD: Headwear, linear gamma
-
Add an option to switch color space (
FFLSetLinearGammaMode), needs to be kept track of per-CharModel. -
Create unit tests with good coverage.
- Can be split into non-rendering, WebGL 1.0, and WebGPU.
- There can be tests for each branch/expected feature, and material class.
- Tests for matching renders - icon images or model exports would be fantastic.
This project was only meant to be a simple port to use FFL in Three.js, like an "adapter". The goals were to be light and reuse as much code from FFL as possible. This would avoid a tedious rewrite and keep to one C++ codebase.
Since then I've realized that this approach has flaws, including how obtuse it can be to deal with a native library and structs, with WASM and its heap, not being able to extend its internals as easily, etc.
My focus has instead been on making a successor that handles Mii data thoroughly as well as being better for rendering and even more flexible. I've written about some of my plans in a post on my blog.
- aboood40091/AboodXD for the FFL decompilation and port to RIO.
- mrdoob for Three.js.
- Nintendo for making FFL.