Embed gltf plugin in standalone exe

I’m trying to embed the gltf plugin into a standalone exe and haven’t been able to get it to work. I’ve found two other topics with the same issue, and the general solution appears to be “Don’t; convert the gltf files to BAM instead”. This isn’t for a game, I’m working on a simple 3d viewer, so I need to be able to display arbitrary gltf files.

In theory I could add a gltf2bam step, but afaik gltf2bam.exe is just a thin wrapper around the Python script. I need this to be a standalone exe, and I think I’d just run into the same problem if I tried to freeze gltf2bam as well.

The script itself works when running from Python, the issue is just with the exe. The error is the same I’ve seen it the other questions:

:loader(error): Extension of file ../../Fox.glb is unrecognized; cannot load.
Currently known scene file types are:
  Bam                             .bam

Is there a way to force the exe to include and use the gltf plugin?

The script itself contains import gltf, requirements.txt includes panda3d-gltf, and setup.py includes ‘gltf’ under “packages”, “include_modules”, and “plugins”. From adding debug statements to the freeze code, it looks like it is detecting gltf as a requirement, but it’s not including it in the build directory. I don’t see a gltf.dll or similar in the build folder, and the exe itself does not work.

It’s not recommended to do this from what I understand, but we did discuss this in another thread already. Link is to a comment I made pretty far into the discussion. Building a Distributable with GLTF Support - #11 by Simulan

It’s not recommended, but I don’t believe I have any alternative.

The linked post appears to be talking about using gltf2bam as part of the build system. I need to be able to load gltf files after the build is completed.

I believe you’ll need to manually register the glTF file loader with LoaderFileTypeRegistry. This is done automatically by ShowBase via entry points if the pkg_resources module is available (code here). However, I believe neither entry points nor pkg_resources is available to frozen applications (at least, not easily). Fortunately, you should be able to manually register the appropriate loader for glTF. This would look something like the following (untested):

from gltf._loader import GltfLoader
from panda3d.core import LoaderFileTypeRegistry

registry = LoaderFileTypeRegistry.getGlobalPtr()

You can also set the loader-support-entry-points PRC variable to false/0 to disable auto-registering loaders so you can test that this works without having to build the application.

The loader doesn’t seem to register properly. If I do it with the class, e.g.

    registry = LoaderFileTypeRegistry.getGlobalPtr()

The output is of the debug statements is:

Bam                             .bam
Python loader                   .glTF

It seems to only be picking up the gltf extension and not glb. If I try to load a glb through python, it works. If I load a gltf, I get:

:loader(error): unable to load <class 'gltf.loader.GltfLoader'>
:loader(error): Python loader file type (.glTF) does not support loading.

If I try using the pkg_resources code, I get the opposite: gltf works, glb does not.

Loading a glb files gives the error:

Bam                             .bam
Python loader                   .glb

:loader(error): unable to load EntryPoint.parse('glb = gltf.loader:GltfLoader')
:loader(error): Python loader file type (.glb) does not support loading.

This is done via:

    registry = LoaderFileTypeRegistry.getGlobalPtr()
    registry.register_deferred_type(pkg_resources.EntryPoint.parse('glb = gltf.loader:GltfLoader'))

I’ve never done anything with entry points. I’m guessing a class can’t be used as a drop-in replacement, and I wouldn’t be surprised if just calling EntryPoint.parse isn’t sufficient either.

Ah, yeah, looks like PythonLoaderFileType (created by register_deferred_type()) only works with an entrypoint and not what the entrypoint loads. Luckily, this is Python, so we can just fake it with some duck-typing:

rom gltf._loader import GltfLoader
from panda3d.core import LoaderFileTypeRegistry

class LoaderEntryPoint:
    def __init__(self, name, loader):
        self.name = name
        self.loader = loader

    def load(self):
        return self.loader

registry = LoaderFileTypeRegistry.getGlobalPtr()
registry.register_deferred_type(LoaderEntryPoint('gltf', Gltf Loader))
registry.register_deferred_type(LoaderEntryPoint('glb', Gltf Loader))

Again, this is untested, but hopefully it gets you closer.

Works perfectly! Thanks for the assistance.