question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Reading a glTF file

See original GitHub issue

Hi, i have written a glTF exporter for my custom model format using this library and it was really simple to do as the library did most of the work for me, for example writing the joints and weights just took about 25 lines total as all i had to do was create the 2 buffers, create an accessor model for each, add the 2 attributes(JOINTS_0 and WEIGHTS_0) and create a buffer view model which was extremely easy due to the ‘helper’ classes like BufferStructureBuilder

Now i am not sure about importing a glTF file and extracting data from it, are there any helper methods that make it simpler? For example if i wanted to read the weights of a mesh primitive, this is what i do right now:

        GltfReaderV2 reader = new GltfReaderV2();
        GlTF gltf = reader.read(new ByteArrayInputStream(Files.readAllBytes(filePath)));
        Mesh mesh = gltf.getMeshes().get(0); // the gltf file only has 1 mesh
        MeshPrimitive primitive = mesh.getPrimitives().get(0); // the mesh only has 1 mesh primitive(for now)
        int weightsIndex = primitive.getAttributes().get("WEIGHTS_0");
        Accessor weightsAccessor = gltf.getAccessors().get(weightsIndex);
        BufferView weightsBufferView = gltf.getBufferViews().get(weightsAccessor.getBufferView());
        Buffer gltfBuffer = gltf.getBuffers().get(weightsBufferView.getBuffer());
        String base64 = gltfBuffer.getUri().substring(gltfBuffer.getUri().indexOf("base64") + "base64, ".length());
        byte[] bytes = Base64.getDecoder().decode(base64.getBytes(StandardCharsets.UTF_8));
        int byteOffset = weightsBufferView.getByteOffset();
        int byteLength = weightsBufferView.getByteLength();
        //TODO create float buffer and start reading weights

This isn’t very tedious to do, but im still curious if there are perhaps simpler ways. Any advice is appreciated.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
javaglcommented, Nov 26, 2021

Also, im assuming that the GltfModel interface has been updated since late 2018 as i don’t have the getMeshModels() method in mine,

Yes, sorry, I had quickly created this example while being on the model-creation branch. In the released version, the line should be

MeshModel meshModel = gltfModel.getNodeModels().get(0).getMeshModels().get(0);

The packages in the releases are only preview releases of the standalone gltf-browser application. (This hasn’t been updated in a while). The latest “official” release is version 2.0.0 in Maven Central.

1reaction
javaglcommented, Nov 26, 2021

Some background: When I started creating that library, glTF 1.0 was published, and the update for the next version was in progress. Unfortunately, the changes from glTF 1.0 to glTF 2.0 turned out to be significant. I naively tried to find some sort of “abstraction” for changes, in form of the ...Model classes. In hindsight, much of this could have been much simpler and cleaner if I had just dropped the glTF 1.0 support. However, here we are. I’m trying to make the best out of this.


When you mention the BufferStructureBuilder, then I have to emphasize the JavaDoc comment from the current state of this class:

This class is only intended for internal use!

But I’m aware of the demand for such a “helper” class: Most of glTF is somewhat trivial to build (for simple gemetry models, at least). But the buffer-bufferView-accessor structures are the most tricky part, even for “simple” models. Therefore, I started extending that functionality, with the goal to be able to “easily” create glTF models. The progress is slow (too many other things to do), but I actually made a bit of progress recently. This is done in the https://github.com/javagl/JglTF/tree/model-creation branch.

The goal of this “refactoring” is to offer more convenient classes for building models (and there will be some changes in the BufferStructureBuilder and related classes). An example of what this could look like can be found at https://github.com/javagl/JglTF/blob/model-creation/jgltf-model-builder/src/test/java/de/javagl/jgltf/model/creation/example/GltfModelCreationExample.java#L97 . Feedback and suggestions for improvements are welcome - but I’m aware that until now, only a tiny fraction of glTF is covered with these classes, and there are some things that I’ll have to address before making these changes “official”.


Coming back to your actual question:

There are some degrees of freedom, depending on your exact goals. But as I mentioned above, there are two levels of abstraction:

  • The classes in the de.javagl.jgltf.impl.v2 package. These are low-level classes, auto-generated from the JSON schema. And that’s what you are using in the code that you showed.
  • The classes in the de.javagl.jgltf.model package. These are “convenience classes”, representing the glTF asset in a form that is supposed to be easier to use, when you don’t need to manipulate things on the level of the JSON input

And I think that the code that you showed is tedious (at least, more tedious than it should be). There are many assumptions in the code. This refers to the special case like having only 1 mesh primitive, but also to the fact that the buffer has a data-uri and so on. And when the //TODO create float buffer and start reading weights is to be addressed, things can become tricky and clumsy very quickly: You’ll have to check the component type, number of elements, and think about sparse accessors and such…

The model classes, and particularly the AccessorModel/AccessorData classes, should make that a bit easier. Here is an example that loads the SimpleSkin sample model, and prints the WEIGHTS_0 data:

package de.javagl.jgltf.example;

import java.net.URI;

import de.javagl.jgltf.model.AccessorFloatData;
import de.javagl.jgltf.model.AccessorModel;
import de.javagl.jgltf.model.GltfModel;
import de.javagl.jgltf.model.MeshModel;
import de.javagl.jgltf.model.MeshPrimitiveModel;
import de.javagl.jgltf.model.io.GltfModelReader;

public class GltfModelExample
{
    public static void main(String[] args) throws Exception
    {
        String modelName = "SimpleSkin";
        String attributeName = "WEIGHTS_0";
        
        String flavor = "Embedded";
        String extensionWithoutDot = "gltf";
        URI uri = new URI("https://raw.githubusercontent.com/KhronosGroup/"
            + "glTF-Sample-Models/master/2.0/" + modelName + "/glTF-" + flavor
            + "/" + modelName + "." + extensionWithoutDot);
        GltfModelReader gltfModelReader = new GltfModelReader();

        System.out.println("Reading from " + uri);
        GltfModel gltfModel = gltfModelReader.read(uri);

        MeshModel meshModel = gltfModel.getMeshModels().get(0);
        MeshPrimitiveModel meshPrimitiveModel =
            meshModel.getMeshPrimitiveModels().get(0);
        
        AccessorModel accessorModel =
            meshPrimitiveModel.getAttributes().get(attributeName);
        
        // This cast is safe, because we know the type. Otherwise, check 
        // that accessorModel.getComponentDataType() == float.class
        AccessorFloatData data =
            (AccessorFloatData) accessorModel.getAccessorData();
        
        int numElements = data.getNumElements();
        int numComponents = data.getNumComponentsPerElement();
        for (int e = 0; e < numElements; e++)
        {
            System.out.println("Element " + e);
            for (int c = 0; c < numComponents; c++)
            {
                float f = data.get(e, c);
                System.out.println("  component " + c + ": " + f);
            }
        }
    }

}

When you change the modelName to SimpleSparseAccessor, and the attributeName to POSITION, then it will print the positions, with the sparse substitution already applied - just as one example where these model classes can make things a bit simpler…

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to manually read GLTF files
In this article, we're going to go over how to read the vertex position data from a single mesh from both a .gltf...
Read more >
Everything You Need to Know About glTF Files
You can use a glTF viewer to examine the 3D object encoded in the file, and in many cases, debug errors. There are...
Read more >
Working with glTF Files — PyVista 0.37.0 documentation
Import a glTF file directly into a PyVista plotting scene. ... You can also directly read in gltf files and extract the underlying...
Read more >
rgl2gltf: Read and Write '.gltf' and '.glb' Files
This package provides read and write functions to work with it. The glTF file is the JSON part of a glTF representation of...
Read more >
Reading glTF, best approach to using the .bin file
gltf is a JSON file, it comes with one or more .bin files accompanying it. The content of the .bin is effectively the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found