Quill to GLTF Bug

Hello!

I am trying to get quill VR animations into a GLTF format. My pipeline right now is quill → FBX → Maya → Sketchfab → GLTF.

Everything seems to be working smoothly with the animations while inside Quill, Maya, and Sketchfab. However, when I export my animations to GLTF, then the animations start glitching out in terms of scale and location in space. it’s really tripping me up because everything works so smooth on every other platform in my pipeline. I have included a link to a video of what is happening when the models are being exported. If anyone has any good solutions for this, as well as exporting a scene with multiple models from a 3D program into threejs that would helpful as well. I am struggling to get a scene with multiple different animated models to all export properly into threejs and be in the correct position. I have included the model that is giving me trouble, as well as a video showing what happens when I put it in babylon sandbox.

house4.zip (3.0 MB)

The glTF file has bad animation data here — if it plays correctly in Sketchfab but exports incorrectly, then I’d have to assume that’s Sketchfab’s bug and should be reported there. If the model works correctly at the FBX stage (are you bringing it to Maya to use Sketchfab’s importer, or to do manual fixes in Maya?) and you can share the FBX, there may be some simpler conversion workflows.

The model works correctly at the FBX stage. I am currently just exporting selections from maya for each animated model in the scene, and bringing those into sketchfab. Exporting to GLTF is difficult from Maya, and my blender gltf exporter isn’t exporting things correctly unfortunately. I have attached the FBX, are you aware of any simpler conversion workflows?
house3.fbx (3.5 MB)

I’m not too familiar with Quill, but I guess the intention for this animation is to cycle through each of the meshes, one frame at a time? The animation data included seems incorrect to me, and I’m not sure how to easily fix that within tools like Blender or Maya. If you know the naming pattern of the meshes (House_# in this case), it might be easier just to script a new animation into the file. For example:

const fs = require('fs');
const path = require('path');
const { NodeIO, FileUtils } = require('@gltf-transform/core');

const FPS = 10;
const INPUT_PATH = 'scene.gltf';
const OUTPUT_PATH = 'scene_step.glb';
const MESH_PATTERN = /House_\d+$/;

const io = new NodeIO(fs, path);
const doc = io.read(INPUT_PATH);
const root = doc.getRoot();

// Remove original animation data.
root.listAnimations().forEach((anim) => {
    anim.listChannels().forEach((channel) => channel.dispose());
    anim.listSamplers().forEach((sampler) => sampler.dispose());
    anim.dispose();
});
root.listAccessors().forEach((accessor) => {
    if (accessor.listParents().length === 1) {
        accessor.dispose();
    }
});

// Collect house nodes.
const houseNodes = root.listNodes()
    .filter((node) => node.getName().match(MESH_PATTERN));

// Create animation cycling visibility of each mesh.
const anim = doc.createAnimation();
const animBuffer = root.listBuffers()[0]
    .setURI(FileUtils.basename(OUTPUT_PATH) + '.bin');
houseNodes.forEach((node, i) => {
    // Create keyframe tracks that show each mesh for a single frame.
    let inputArray;
    let outputArray;
    if (i === 0) {
        inputArray = [i / FPS, (i + 1) / FPS];
        outputArray = [1,1,1, 0,0,0];
    } else if (i === houseNodes.length - 1) {
        inputArray = [(i - 1) / FPS, i / FPS];
        outputArray = [0,0,0, 1,1,1];
    } else {
        inputArray = [(i - 1) / FPS, i / FPS, (i + 1) / FPS];
        outputArray = [0,0,0, 1,1,1, 0,0,0];
    }

    // Construct glTF animation.
    const input = doc.createAccessor()
        .setArray(new Float32Array(inputArray))
        .setBuffer(animBuffer);
    const output = doc.createAccessor()
        .setArray(new Float32Array(outputArray))
        .setBuffer(animBuffer)
        .setType('VEC3');
    const sampler = doc.createAnimationSampler(node.getName())
        .setInterpolation('STEP')
        .setInput(input)
        .setOutput(output);
    const channel = doc.createAnimationChannel(node.getName())
        .setTargetNode(node)
        .setTargetPath('scale')
        .setSampler(sampler);
    anim.addSampler(sampler).addChannel(channel);
});

io.write(OUTPUT_PATH, doc);

The result works correctly, using your original .gltf file as input:

scene_step.glb.zip (3.3 MB)

3 Likes

Oh, one other thing. Technically these vertex colors are in the wrong colorspace, I’m pretty sure you want it to look more saturated like this?

If that’s the case, you can fix it with a small change to the last line of the script above. Or if it looks how you want in your app, feel free to skip this, it just may look different in official viewers. :slight_smile:


// Fix vertex colors, then write.
const { colorspace } = require('@gltf-transform/lib');
doc.transform(colorspace({inputEncoding: 'sRGB'}))
    .then(() => io.write(OUTPUT_PATH, doc));

2 Likes

ok awesome, I did need the vertex colors fixed, so that line is perfect, but i can also just change the color space in quill. I will try this and get back to you