{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreid7sqvfv5dygaig2m4hfu5zrpwxarduf6sx32k2ugjzajgntto5oa",
    "uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3mjd57puidpi2"
  },
  "path": "/t/vlog-02-fbxloader-fixes-mixamo-models-now-loading-in-jme/49452#post_10",
  "publishedAt": "2026-04-12T18:09:47.000Z",
  "site": "https://hub.jmonkeyengine.org",
  "tags": [
    "@sgold",
    "@capdevon",
    "@adi.barda",
    "@Override"
  ],
  "textContent": "I experimented with downloading an animated 3D model along with a separate file containing only its animation. I was able to successfully load both using the `FbxLoader` and then transfer the animation onto the model—and it works perfectly.\n\nOne particularly interesting finding is that, using the old animation system, copying animations between `AnimControl`s is significantly simpler and faster. After that, everything can be converted to the new animation system using `AnimMigrationUtil`. (On a side note, I also made an additional fix to this class to improve thread safety and eliminate a memory leak.)\n\nThis pipeline removes the need to convert FBX files to glTF via Blender and then to j3o. For rapid prototyping, this is extremely valuable, as it provides full control over all data and enables full automation through scripting—allowing models and animations to be merged with just a few clicks.\n\nMixamo FBX models do not appear to be designed for a PBR pipeline. As a result, converting materials from Lighting.j3md to jME’s PBRLighting can only rely on physical and artistic approximations when translating parameters from the Phong lighting model to PBR.\n\nAdditionally, PBR shading requires models to include tangent buffers in order to correctly compute lighting effects. For this reason, I focused on optimizing the `MikkTGenerator` tool, which, imo, appears to be a performance bottleneck in its current implementation.\n\nThe only remaining task on my side was optimizing the cm-to-meter conversion for FBX files. This has now been resolved in the `FbxLoader`: models are loaded with the correct proportions, preventing issues where, for example, a 1.80 m tall character would otherwise appear as 180 meters tall in jME.\n\n\n            model = assetManager.loadModel(\"Models/Swat/Swat.fbx\");\n            //TangentBinormalGenerator.generate(model, true);\n\n            model.depthFirstTraversal(new SceneGraphVisitorAdapter() {\n                @Override\n                public void visit(Geometry geom) {\n                    Mesh mesh = geom.getMesh();\n                    if (mesh.getBuffer(VertexBuffer.Type.Tangent) == null) {\n                        // generate tangents for normal mapping and skinned meshes\n                        MikkTSpaceOffline.generate(mesh);\n                    }\n                }\n            });\n\n            Spatial animAsset = assetManager.loadModel(\"Models/Swat/Firing Rifle-noskin.fbx\");\n            copyAnimation(animAsset.getControl(AnimControl.class), model.getControl(AnimControl.class), \"Firing Rifle\");\n\n            AnimMigrationUtils.migrate(model); // migrate from old animation system\n            FbxModelProcessor.centerRootJoint(model);\n    //        model.setLocalScale(0.01f); // no longer needed !!!\n            rootNode.attachChild(model);\n\n\n        public static void copyAnimation(AnimControl source, AnimControl target, String newName) {\n            for (String animName : source.getAnimationNames()) {\n                Animation anim = source.getAnim(animName);\n                anim.setName(newName);\n                target.addAnim(anim);\n            }\n        }\n\n\nFor comparison, see the screenshot below obtained with MonkeyWrench, where the model proportions appear significantly larger than expected relative to their real-world scale. I’m not sure whether this is something that can be addressed directly—perhaps `Assimp` exposes additional information regarding the model’s scale factor.\n\nAdditionally, using the same file, `MonkeyWrench` does not seem to resolve textures from the model’s file path. This might be due to a configuration detail that I haven’t fully explored yet (`TextureLoader` ??). @sgold likely has more insight into this aspect.\n\n\n                String[] extensions = { \"3ds\", \"3mf\", \"blend\", \"bvh\", \"dae\", \"fbx\", \"glb\", \"gltf\",\n                        \"lwo\", \"meshxml\", \"mesh.xml\", \"obj\", \"ply\", \"stl\" };\n                assetManager.registerLoader(LwjglAssetLoader.class, extensions);\n\n                int ppFlags = Assimp.aiProcess_CalcTangentSpace\n                        | Assimp.aiProcess_JoinIdenticalVertices\n                        | Assimp.aiProcess_Triangulate\n                        | Assimp.aiProcess_GenNormals\n                        | Assimp.aiProcess_ValidateDataStructure\n    //                    | Assimp.aiProcess_RemoveRedundantMaterials\n                        | Assimp.aiProcess_SortByPType;\n\n                TextureLoader textureLoader = new TextureLoader(PathEdit.LastComponent); // ???\n\n                LwjglAssetKey key = new LwjglAssetKey(\"Models/Swat/Swat.fbx\", textureLoader, ppFlags);\n                key.setVerboseLogging(true);\n                model = assetManager.loadModel(key);\n                rootNode.attachChild(model);\n\n    /*\n    Models/Swat\n    |   Soldier_Body_diffuse.png\n    |   Soldier_Body_normal.png\n    |   Soldier_Body_specular.png\n    |   Soldier_head_diffuse.png\n    |   Soldier_head_normal.png\n    |   Soldier_head_specular.png\n    |   Swat.fbx\n    */\n\n\nadi.barda:\n\n> @capdevon is it possible to get your source code targeting the new animation system? i would like to try it if it’s OK\n\n@adi.barda , unfortunately I’m not able to provide that in the short term. I’m still working on resolving a few issues in the original plugin, so I’d prefer to stabilize things first before sharing anything targeting the new animation system.",
  "title": "Vlog 02: FbxLoader Fixes: Mixamo models now loading in jME!"
}