Class GltfModel

java.lang.Object
limn.render3d.gltf.GltfModel

public final class GltfModel extends Object
A parsed glTF 2.0 asset as neutral CPU data: a node hierarchy, meshes (as MeshData), metallic-roughness materials, and encoded texture images. GltfLoader produces it without touching the GPU or decoding images, so it is fully headless-testable. toScene3D() then uploads everything (via Graphics3D/Images) and builds a retained Scene3D; call it inside a render frame, with a backend running.

Getting to a scene is two costs, and only one of them needs the frame. Decoding the embedded PNG/JPEG textures is plain CPU work on bytes this object already holds (tens of milliseconds for a single 2048² base-colour map), while the GPU uploads need the render thread with the context current. decodeTextures() is the first half on its own, decodeTexturesAsync() runs it on the worker pool, and toScene3D(DecodedTextures) uploads what it produced. Use those three when the model is loaded while a window is live; toScene3D() does both halves in the frame and is for setup code that can afford it.

  • Method Details

    • meshes

      public List<GltfModel.MeshDef> meshes()
      Meshes, indexed as the glTF file numbers them.
    • materials

      public List<GltfModel.MaterialDef> materials()
      Materials, indexed as the glTF file numbers them.
    • textures

      public List<GltfModel.TextureDef> textures()
      Textures, each pairing an image with a sampler.
    • samplers

      public List<GltfModel.SamplerDef> samplers()
      Samplers: filtering and wrap modes referenced by textures.
    • images

      public List<GltfModel.ImageDef> images()
      The images the textures sample, still in their file encoding.
    • nodes

      public List<GltfModel.NodeDef> nodes()
      Every node in the file, flat; the hierarchy is in each node's child indices.
    • rootNodes

      public int[] rootNodes()
      Indices into nodes() of the scene's roots, where traversal starts.
    • primitiveCount

      public int primitiveCount()
      Total primitives across all meshes (a mesh may have several).
    • decodeTextures

      public GltfModel.DecodedTextures decodeTextures()
      Decodes every texture image some material references, on the calling thread, and returns them for toScene3D(DecodedTextures) to upload. Pure CPU work on bytes this model already holds (no GPU, no frame, no UI thread), so it is safe on a worker, which is where a model loaded while a window is live should do it.

      Textures no material references are skipped, exactly as toScene3D() skips them: glTF files commonly carry normal/ORM/emissive maps this renderer does not sample yet, and decoding one costs the same as decoding a map that gets drawn. An image two textures share is decoded once and shared between them.

      Returns:
      the decoded images, tied to this model
      Throws:
      IllegalStateException - if no backend is running (there is no image decoder installed)
    • decodeTexturesAsync

      public Work<GltfModel.DecodedTextures> decodeTexturesAsync()
      Decodes the referenced texture images on the Ui worker pool, delivering them to onSuccess on the UI thread: the asynchronous form of decodeTextures().
      
       decoding = model.decodeTexturesAsync()
                       .onSuccess(decoded -> this.pending = decoded)  // upload in the next frame
                       .deliverIf(viewport::isShowing)
                       .start();
       

      Returned unstarted: register handlers, then call start(). A job cancelled while it runs stops between images. No disposer is registered and none is needed: the result is decoded pixels and nothing else.

      What it produces still has to be uploaded, and that half cannot leave the render thread: pass it to toScene3D(DecodedTextures) from inside a frame.

      Throws:
      IllegalStateException - if no backend is running (there is no worker pool to use)
    • toScene3D

      public Scene3D toScene3D()
      Decodes the textures and uploads everything, then instantiates the node hierarchy as a retained Scene3D: decodeTextures() followed by toScene3D(DecodedTextures), for setup code that can afford both in one frame. Lights and camera are the caller's to set on the scene.

      Must run inside a render frame with a backend installed, and the texture decode it does first runs there too: a model with large base-colour maps stalls that frame for tens of milliseconds per map. Split the two when that matters.

      The returned scene owns the uploaded GPU resources: release them with Scene3D.dispose() once it is no longer drawn (each call to this method uploads a fresh copy).

    • toScene3D

      public Scene3D toScene3D(GltfModel.DecodedTextures decoded)
      Uploads decoded and the meshes, and instantiates the node hierarchy as a retained Scene3D: the GPU half of toScene3D(), for a caller that has already decoded the textures elsewhere.

      Must run inside a render frame with a backend installed; there is no asynchronous form, because every call it makes needs the GL context current on the render thread. What can be moved off the frame is the decode, and decodeTexturesAsync() is where that lives.

      The returned scene owns the uploaded GPU resources: release them with Scene3D.dispose() once it is no longer drawn (each call uploads a fresh copy, so one decoded may build several scenes and each disposes independently).

      Parameters:
      decoded - textures from decodeTextures() or decodeTexturesAsync() on this model
      Throws:
      IllegalArgumentException - if decoded came from a different model, whose texture numbering would silently mismatch this one's