[SOLVED] GLTF / GLB animated model

Hello,

I’ve written a little example for importing GLTF/GLB models within JMonkey.

Thanks to @sgold and @pspeed i’ve been able to load a GLTF 2.0 model correctely (with PBR material) that display fine.

However, the imported model is animated and i’m not able to animate it within JMonkey. When i list all the controls attached to the loaded model, i found a SkinningControl with an Armature, but i don’t know what to do with them…

Here is my code:

import com.jme3.anim.SkinningControl;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.environment.EnvironmentCamera;
import com.jme3.environment.LightProbeFactory;
import com.jme3.input.ChaseCamera;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.LightProbe;
import com.jme3.light.SpotLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;

public class GLBAnimatedModel extends SimpleApplication {

	private int frame = 0;
	
	private SkinningControl sc;
	
	@Override
	public void simpleInitApp() {
		DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(0,0,0).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);

        AmbientLight al = new AmbientLight();
        al.setColor(ColorRGBA.White.mult(1.3f));
        rootNode.addLight(al);
		
        SpotLight sl = new SpotLight(new Vector3f(0.0f, 0.0f, 0.0f), Vector3f.UNIT_Y);
        sl.setColor(ColorRGBA.White.mult(1.3f));
        rootNode.addLight(sl);
        
        // Load the GLB model
        assetManager.registerLocator("data", FileLocator.class);
        Spatial animatedModel = assetManager.loadModel("model/HoverRocket.glb");

        Quaternion rotation = new Quaternion();
        rotation.fromAngles(-FastMath.HALF_PI, 0.0f, 0.0f);
        animatedModel.setLocalRotation(rotation);
        
        rootNode.attachChild(animatedModel);

        // PBR - Environment camera initialization
        final EnvironmentCamera envCam = new EnvironmentCamera(256, new Vector3f(0.0f, 0.0f, 0.0f));
        stateManager.attach(envCam);
        
        sc = animatedModel.getControl(SkinningControl.class);

        flyCam.setEnabled(false);
        ChaseCamera chaseCam = new ChaseCamera(cam, animatedModel, inputManager);
        
        this.getViewPort().setBackgroundColor(ColorRGBA.LightGray);
	}
	
	@Override
	public void simpleUpdate(float tpf) {
		
		frame++;
		
		// PBR - LightProbe initialization
		// Rendering need to be effective before adding LightProbe
		if (frame == 2) {
			final LightProbe probe = LightProbeFactory.makeProbe(stateManager.getState(EnvironmentCamera.class), rootNode);
	        
	        probe.getArea().setRadius(100);
	        rootNode.addLight(probe);
		}
	}
	
	/**
	 * The main method.
	 * @param args the main method arguments
	 */
	public static void main(String[] args) {
		GLBAnimatedModel app = new GLBAnimatedModel();
		app.start();
	}
}

Can you please help me to deal with the animations ?

I can precise that the used model (HoverRocket.glb) has been downloaded from SketchFab at https://sketchfab.com/3d-models/hover-bike-the-rocket-8b2e5bfca78e41c791b4e5b5e8c04512 using the SketchFab export to GLB (with 1K textures).

Thanlks a lot.

1 Like

If it were me, I’d open the file in Blender to see if it even has animations.

There have been several times that for some reason the preview in sketchfab has more stuff than the model you download. I don’t know why.

An animated model will have an AnimComposer control. If not then it doesn’t have animations or for some reason they weren’t loaded correctly.

1 Like

Hello,

Thanks for the answer. The model is displaying well and is animated within Blender 4.1.

The check using Kronos tools give me this errors

 {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/0/output"
            },
            {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/1/output"
            },
            {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/2/output"
            },
            {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/3/output"
            },
            {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/4/output"
            },
            {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/5/output"
            },
            {
                "code": "ANIMATION_SAMPLER_ACCESSOR_WITH_BYTESTRIDE",
                "message": "bufferView.byteStride must not be defined for buffer views used by animation sampler accessors.",
                "severity": 0,
                "pointer": "/animations/0/samplers/6/output"
            },
            {
                "code": "NODE_EMPTY",
                "message": "Empty node encountered.",
                "severity": 2,
                "pointer": "/nodes/5"
            },
            {
                "code": "UNUSED_OBJECT",
                "message": "This object may be unused.",
                "severity": 2,
                "pointer": "/skins/0"
            }

According the returned errors, animations seems to be integrated within the GLB file (and even with these errors, the model is well animated within Blender)

1 Like

The standard glTF loader included in jme3-plugins still has a few issues. In the case of “hover-bike-the-rocket.glb”, the loader generates a serious warning:

Apr 22, 2024 9:44:38 AM com.jme3.scene.plugins.gltf.GltfLoader setupControls
WARNING: No skinned Spatial for joint "Root_00" -- will skin the models root node!

This particular model loads better using the MonkeyWrench library. When loaded using MonkeyWrench v0.6.0, the model has an AnimComposer containing a single animation named “Armature|Idle”.

2 Likes

Thanks a lot.

I wasn’t aware of Monkey wrench.

I will use it for now

1 Like

I’m glad I could help.
Now there’s an issue at GitHub: "No skinned Spatial for joint" in GltfLoader.setupControls() · Issue #2247 · jMonkeyEngine/jmonkeyengine · GitHub

2 Likes