Vlog 02: FbxLoader Fixes: Mixamo models now loading in jME!
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.
One particularly interesting finding is that, using the old animation system, copying animations between AnimControls 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.)
This 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.
Mixamo 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.
Additionally, 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.
The 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.
model = assetManager.loadModel("Models/Swat/Swat.fbx");
//TangentBinormalGenerator.generate(model, true);
model.depthFirstTraversal(new SceneGraphVisitorAdapter() {
@Override
public void visit(Geometry geom) {
Mesh mesh = geom.getMesh();
if (mesh.getBuffer(VertexBuffer.Type.Tangent) == null) {
// generate tangents for normal mapping and skinned meshes
MikkTSpaceOffline.generate(mesh);
}
}
});
Spatial animAsset = assetManager.loadModel("Models/Swat/Firing Rifle-noskin.fbx");
copyAnimation(animAsset.getControl(AnimControl.class), model.getControl(AnimControl.class), "Firing Rifle");
AnimMigrationUtils.migrate(model); // migrate from old animation system
FbxModelProcessor.centerRootJoint(model);
// model.setLocalScale(0.01f); // no longer needed !!!
rootNode.attachChild(model);
public static void copyAnimation(AnimControl source, AnimControl target, String newName) {
for (String animName : source.getAnimationNames()) {
Animation anim = source.getAnim(animName);
anim.setName(newName);
target.addAnim(anim);
}
}
For 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.
Additionally, 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.
String[] extensions = { "3ds", "3mf", "blend", "bvh", "dae", "fbx", "glb", "gltf",
"lwo", "meshxml", "mesh.xml", "obj", "ply", "stl" };
assetManager.registerLoader(LwjglAssetLoader.class, extensions);
int ppFlags = Assimp.aiProcess_CalcTangentSpace
| Assimp.aiProcess_JoinIdenticalVertices
| Assimp.aiProcess_Triangulate
| Assimp.aiProcess_GenNormals
| Assimp.aiProcess_ValidateDataStructure
// | Assimp.aiProcess_RemoveRedundantMaterials
| Assimp.aiProcess_SortByPType;
TextureLoader textureLoader = new TextureLoader(PathEdit.LastComponent); // ???
LwjglAssetKey key = new LwjglAssetKey("Models/Swat/Swat.fbx", textureLoader, ppFlags);
key.setVerboseLogging(true);
model = assetManager.loadModel(key);
rootNode.attachChild(model);
/*
Models/Swat
| Soldier_Body_diffuse.png
| Soldier_Body_normal.png
| Soldier_Body_specular.png
| Soldier_head_diffuse.png
| Soldier_head_normal.png
| Soldier_head_specular.png
| Swat.fbx
*/
adi.barda:
@capdevon is it possible to get your source code targeting the new animation system? i would like to try it if it’s OK
@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.
Discussion in the ATmosphere