Search code examples
qtanimationqmlqt3d

Qt3D - Import gltf and play animation


I'm currently struggling to play the animation of a gltf-file with Qt3D/QML.

Model I want to use:

https://sketchfab.com/3d-models/plane-cedc8a07370747f7b0d14400cdf2faf9

Code I have so far:

 Entity {
    id: root

    Transform {
        id: planeTransform
        scale: 0.1
    }
    components: [
        planeClip,
        planeTransform,
        planeScene
    ]

    SceneLoader{
        id:planeScene
        source: "qrc:/Modells/3DModelle/plane/scene.gltf"
    }

    AnimationClipLoader{
        id: planeClipLoader
        source: "qrc:/Modells/3DModelle/plane/scene.gltf"
    }
    ClipAnimator{
        id: planeClip
        clip: planeClipLoader
        channelMapper: ChannelMapper {
            mappings: [  ] // Dont know where to get this
        }

        loops: Animation.Infinite
        running: true
    }

}

I use the SceneLoader to check if the gltf can be even loaded (it works, I can see the plane). But I'm struggling to start the animation because I don't know where I get the ChannelMapper from.

When I open the file with 3DViewer on Windows, it plays the Animation correctly, so I suppose it should work without much insider knowledge of the file itself.

Anyone here familiar with playing gltf animations?

Thanks for the help!


Solution

  • To do this you need to traverse the tree of Entity(s) build by SceneLoader and find the appropriate component to initialize ClipAnimator with.

    It's going to look something like this:

    SceneLoader {
        id: planeScene
        source: "qrc:/Modells/3DModelle/plane/scene.gltf"
    
        onStatusChanged: {
            console.log("SceneLoader status=" + status);
            if (status != SceneLoader.Ready)
                return;
    
            // retrieve a list of all the Entity(s) in the scene
            var entityNames = planeScene.entityNames();
            console.log("SceneLoader: entityNames=" + entityNames);
    
            // traverse the Entity tree
            for (var i = 0; i < entityNames.length; ++i) {
                var entityName = entityNames[i];      
                var entityVar = planeScene.entity(entityName);
                console.log("SceneLoader: entity=" + entityName + " components.length=" + entityVar.components.length);
    
                // iterate on the components of this Entity
                for (var j = 0; j < entityVar.components.length; ++j) {
                    var cmp = entityVar.components[j];
                    if (!cmp)
                        continue;
    
                    var cmp_class = cmp.toString();
                    console.log("SceneLoader:  -> " + cmp_class);
    
                    // compare the type of this component to whatever type 
                    // you are looking for. On this silly example, its QPhongMaterial:
                    if (cmp_class.indexOf("QPhongMaterial") >= 0) {
                        // initialize ClipAnimator with this data
                    }                
                }
            }
        }
    }
    

    I'm sharing the generic procedure here as I don't have a way to test this right now and figure out the data type you need to initialize ClipAnimator correctly.