Gltf not animated properly

Hello again.

Can you take a look on those videos. In blender animation is OK, but when i export it to glb animation is not properly.
What can i do with that.

Please see the Loading 3D Models • Troubleshooting section in the docs:

  1. if the model works in one or more of the 3D viewers but not in your code, you will need to share enough information about your code for someone to debug and reproduce the issue.
  2. If the model does not work correctly in any 3D viewer, please report a bug to the blender addon, including an example .blend so that they can debug and reproduce the issue.

In either case, you’ll need to provide more information for others to help.

same animation recorded from glb file from 3D viewer for Windows - played correctly

something seems to be wrong in GLTFLoader

Below my code

{
var animationCanvas = function (where, hero, type) {

            var clock, mixer, camera, scene, renderer, model, GLBFile;
    
            clock = new THREE.Clock();
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(25, 300 / 300, 0.01, 10);
            camera.position.set(-0.25, 0, 0.5);
    
            renderer = new THREE.WebGLRenderer({
                antialias: true,
                alpha: true,
            });
    
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(300, 300);
            renderer.shadowMap.enabled = true;


            renderer.outputEncoding = THREE.sRGBEncoding;

            container = document.createElement('div');
            container.classList.add(`animation_canvas`);
            where.innerHTML = null;
            where.append(container);
            container.appendChild(renderer.domElement);
    
            window.addEventListener('resize', () => {
    
                renderer.setSize(300, 300);
                camera.aspect = 300 / 300;
                camera.updateProjectionMatrix();
    
            });
    
            var controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = false; // an animation loop is required when either damping or auto-rotation are enabled
            controls.dampingFactor = 10.5;
            controls.screenSpacePanning = false;
    
            controls.maxPolarAngle = Math.PI / 2;
            controls.target.set(0, 0.03, 0);
    
            if (existsFile('./animations/' + hero + '_new.glb')) {
                GLBFile = './animations/' + hero + '_new.glb';
                controls.maxDistance = 0.15;
                controls.minDistance = 0.15;
            } else {
                GLBFile = './animations/' + hero + '.glb';
                controls.maxDistance = 0.25;
                controls.minDistance = 0.25;
            };
    
            var loader = new THREE.GLTFLoader();
            loader.load(GLBFile, async (gltf) => {
    
                    model = gltf.scene;
                    gltf.scene.traverse(function (node) {
                        if (node.isMesh) {
                            node.castShadow = true;
                            node.receiveShadow = true;
                            node.material.metalness = 0; // undo this change if you apply an env map
                        }
    
                    });
    
                    var myArray = [];
                    var debut, idle, reply, sp;
                    var attack, daze, die, hurt, moving, skill_1, skill_2, skill_3, victory;
                    var escape, goal, lie, scared_1, scared_2, scared_3;
    
                    var action;
    
                    if (gltf.animations.filter(e => e.name == `debut`).length > 0) {
                        debut = true;
                        myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('debut')])
                    };
                    if (debut) {
                        if (gltf.animations.filter(e => e.name == `idle`).length > 0) {
                            idle = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('idle')])
                        };
                        if (gltf.animations.filter(e => e.name == `reply`).length > 0) {
                            reply = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('reply')])
                        };
                        if (gltf.animations.filter(e => e.name == `sp`).length > 0) {
                            sp = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('sp')])
                        };
                    } else {
                        if (gltf.animations.filter(e => e.name == `idle`).length > 0) {
                            idle = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('idle')])
                        };
                        if (gltf.animations.filter(e => e.name == `attack`).length > 0) {
                            attack = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('attack')])
                        };
                        if (gltf.animations.filter(e => e.name == `daze`).length > 0) {
                            daze = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('daze')])
                        };
                        if (gltf.animations.filter(e => e.name == `die`).length > 0) {
                            die = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('die')])
                        };
                        if (gltf.animations.filter(e => e.name == `hurt`).length > 0) {
                            hurt = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('hurt')])
                        };
                        if (gltf.animations.filter(e => e.name == `moving`).length > 0) {
                            moving = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('moving')])
                        };
                        if (gltf.animations.filter(e => e.name == `skill_1`).length > 0) {
                            skill_1 = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('skill_1')])
                        };
                        if (gltf.animations.filter(e => e.name == `skill_2`).length > 0) {
                            skill_2 = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('skill_2')])
                        };
                        if (gltf.animations.filter(e => e.name == `skill_3`).length > 0) {
                            skill_3 = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('skill_3')])
                        };
                        if (gltf.animations.filter(e => e.name == `victory`).length > 0) {
                            victory = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('victory')])
                        };
                        if (gltf.animations.filter(e => e.name == `escape`).length > 0) {
                            escape = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('escape')])
                        };
                        if (gltf.animations.filter(e => e.name == `goal`).length > 0) {
                            goal = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('goal')])
                        };
                        if (gltf.animations.filter(e => e.name == `lie`).length > 0) {
                            lie = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('lie')])
                        };
                        if (gltf.animations.filter(e => e.name == `scared_1`).length > 0) {
                            scared_1 = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('scared_1')])
                        };
                        if (gltf.animations.filter(e => e.name == `scared_2`).length > 0) {
                            scared_2 = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('scared_2')])
                        };
                        if (gltf.animations.filter(e => e.name == `scared_3`).length > 0) {
                            scared_3 = true;
                            myArray.push(gltf.animations[gltf.animations.map(e => e.name).indexOf('scared_3')])
                        };
                    };
    
                    mixer = new THREE.AnimationMixer(gltf.scene);
    
                    if (debut) {
                        action = mixer.clipAction(myArray[myArray.map(e => e.name).indexOf('debut')])
                        action.setLoop(THREE.LoopOnce);
                        action.play();
                        mixer.addEventListener('finished', ( /*event*/ ) => {
                            action = mixer.clipAction(myArray[myArray.map(e => e.name).indexOf('idle')]);
                            action.play();
                        });
    
                    } else {
                        action = mixer.clipAction(myArray[myArray.map(e => e.name).indexOf('idle')]);
                        action.play();
    
                        var set = null
    
                        function onClick(event) {
    
                            if (!set) {
                                set = true;
                                event.preventDefault();
                                var randomItem = myArray.filter(e => e.name !== `idle`)[Math.floor(Math.random() * (myArray.length - 1))];

                                action = mixer.clipAction(randomItem);
    
                                action.setLoop(THREE.LoopOnce);
                                action.play();
                                mixer.addEventListener('finished', ( /*event*/ ) => {
                                    action.stop()
                                    set = null;
                                    return;
                                })
                            }
                        }
                        document.querySelector(`canvas`).addEventListener('click', onClick, false);
                    }
    
                    scene.add(model);
                    animate();
    
                },
                function (xhr) {
    
                    console.log((xhr.loaded / xhr.total * 100) + '% loaded');
    
                },
                function (error) {
    
                    console.error('An error happened', error);
    
                });
    
            // lights
    
            var hemiLight = new THREE.HemisphereLight(0xffffff, 0x666666);
            hemiLight.position.set(0, 1, 0);
            scene.add(hemiLight);
    
            var dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
            dirLight.position.set(0, 1, 1);
            dirLight.castShadow = true;
            dirLight.shadow.camera.top = 0.2;
            dirLight.shadow.camera.bottom = -0.2;
            dirLight.shadow.camera.left = -0.2;
            dirLight.shadow.camera.right = 0.2;
            dirLight.shadow.camera.near = 0.1;
            dirLight.shadow.camera.far = 2;
            dirLight.shadow.mapSize.set(300, 300);
            scene.add(dirLight);
    
            var material = new THREE.ShadowMaterial();
            material.opacity = 0.4;

            var mesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(0, 0),  material );
            mesh.rotation.x = -Math.PI / 2;
            mesh.receiveShadow = true;
            mesh.castShadow = true;
            scene.add( mesh );
    
            function animate() {
    
                requestAnimationFrame(animate);
                var delta = clock.getDelta();
                if (mixer) mixer.update(delta);
                render();
                controls.update();
            }
    
            function render() {
                renderer.render(scene, camera);
            }


        };

}

Hm, there’s quite a bit of animation-related stuff happening in that code. :slight_smile: Am I correct in understanding that your model has a lot of animations, and they’re meant to be played one at a time? And, what version of three.js do you have installed?

Just to check that it works in some three.js renderer, does gltf-viewer.donmccurdy.com/ work for you?

Three.js varsion latest

“and they’re meant to be played one at a time?”
yup
primary animation playimg in loop, but when you click on canvas animation will change, and after finish go back to primary animation

OMG the object showing for a sec and then hundreds of bugs
glTF 2.0 validation report.pdf (387.6 KB)

I’m not sure what you mean here — the validation report aside, is the model appearing and animating correctly? It would be much easier for others to debug this if you’re able to share the model, or the .blend, or a subset of the model that reproduces the problem.

i think i found the problem.
So it looks like that.
When i export to glt only one animation, then that animation is perfect, no matter whitch one i export.
But when i tryed to export couple animation, then no one playig good.
But idk what to do, how to fix it

We can’t help you without some way to reproduce the problem. A video demonstrates that there is a problem but rarely gives clues into why it is happening. There are many demos on the three.js site showing models with animation working correctly, and the reason yours is broken will have to do with the model (which we can’t see) and the code (which we can’t run without a model).

Please confirm whether your model appears correctly in https://gltf-viewer.donmccurdy.com/ and/or https://sandbox.babylonjs.com/.

mecha_trojan.glb (771.6 KB)
here is my glb file
it works properly on babylon
but on https://gltf-viewer.donmccurdy.com/ dont work properlymecha_trojan.blend (4.0 MB)
alos blender file

The issue I see on https://gltf-viewer.donmccurdy.com/ is that the model is huge, and won’t let you zoom out far enough to see the whole thing:

The animation and textures appear to be intact, though. When I open the .blend file, I don’t see any textures included there (that’s fine, maybe they’re not embedded in the file) but I also notice that the armature is scaled by 0.01. If you fix that (change the Armature’s scale to 1, 1, 1) it appears at the right scale in the three.js viewer.

What result do you get when exporting with the Armature scaled to 1 first, when opening it on gltf-viewer.donmccurdy.com/?

More specifically, try changing these values to 1, not 0.010:

In Scale 1

Warning Message Pointer
NODE_SKINNED_MESH_NON_ROOT Node with a skinned mesh is not root. Parent transforms will not affect a skinned mesh. /nodes/17

I will try more tomorow, i have to go sleep :stuck_out_tongue: it’s almost moring in here

Keep in mind that I cannot guess what you are seeing. It is time consuming to keep asking “what result do you get”, and your .blend file does not include textures so I cannot reproduce any of this. Please fully describe what you are seeing — an isolated validation warning doesn’t mean much.

Hi @donmccurdy I believe I am having a similar issue where exporting multiple animations causes corruption of the animations. I have no idea how the information from each animation is bleeding into other animations but I was hoping you might have some thoughts.

Edit (I was able to make a change to the exporter to solve the issue):