GLTF Importer fails to load assets via input stream


#1

I’m working on my own converter for jme files to streamline setting assets up specifically for my game. I’ve done this in the past pretty easily using swing’s filechooser and loading and exporting via file streams. I wanted to give gltf format a try since it seems promising and I discovered an issue. Because gltf separates its data into 2 files my usual stream importing is failing!

@Override
    public void simpleInitApp() {
        //use a file stream to load and asset not registered to an asset directory
        File targetAsset = new File(System.getProperty("user.dir")+
                File.separator+"wip"+File.separator+"TestCube.gltf");
        //start a file stream to load this asset
        try(InputStream in = new FileInputStream(targetAsset)){
            AssetKey key = new ModelKey(targetAsset.getPath());
            Spatial spat = (Spatial)assetManager
                    .loadAssetFromStream(new ModelKey(targetAsset.getPath()), in);
            System.out.println("Spatial loaded from "+targetAsset+" with key "+key+" gives us "+spat);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(TestGLTF.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(TestGLTF.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
com.jme3.asset.AssetLoadException: An error occurred loading /mnt/DragonLair/Docs/Dev/JME/JMonkeyProjects/JMEConverter/wip/TestCube.gltf
	at com.jme3.scene.plugins.gltf.GltfLoader.loadFromStream(GltfLoader.java:150)
	at com.jme3.scene.plugins.gltf.GltfLoader.load(GltfLoader.java:78)
	at com.jme3.asset.DesktopAssetManager.loadLocatedAsset(DesktopAssetManager.java:259)
	at com.jme3.asset.DesktopAssetManager.loadAssetFromStream(DesktopAssetManager.java:341)
	at com.mru.converter.TestGLTF.simpleInitApp(TestGLTF.java:44)
	at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:220)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:130)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:211)
	at java.lang.Thread.run(Thread.java:748)
Caused by: com.jme3.asset.AssetNotFoundException: /mnt/DragonLair/Docs/Dev/JME/JMonkeyProjects/JMEConverter/wip/TestCube.bin
	at com.jme3.asset.DesktopAssetManager.loadAsset(DesktopAssetManager.java:370)
	at com.jme3.scene.plugins.gltf.GltfLoader.getBytes(GltfLoader.java:535)
	at com.jme3.scene.plugins.gltf.GltfLoader.readData(GltfLoader.java:513)
	at com.jme3.scene.plugins.gltf.GltfLoader.readBuffer(GltfLoader.java:484)
	at com.jme3.scene.plugins.gltf.GltfLoader$VertexBufferPopulator.populate(GltfLoader.java:1350)
	at com.jme3.scene.plugins.gltf.GltfLoader$VertexBufferPopulator.populate(GltfLoader.java:1318)
	at com.jme3.scene.plugins.gltf.GltfLoader.readAccessorData(GltfLoader.java:465)
	at com.jme3.scene.plugins.gltf.GltfLoader.readMeshPrimitives(GltfLoader.java:356)
	at com.jme3.scene.plugins.gltf.GltfLoader.readNode(GltfLoader.java:217)
	at com.jme3.scene.plugins.gltf.GltfLoader.readChild(GltfLoader.java:266)
	at com.jme3.scene.plugins.gltf.GltfLoader.readScenes(GltfLoader.java:183)
	at com.jme3.scene.plugins.gltf.GltfLoader.loadFromStream(GltfLoader.java:127)
	... 8 more

This isn’t severe for me, the easy workaround is to simply add the root of the files location to the assetmanager and it’ll find the bin no problem, but this definitely seems like a bug to me. The gltf importer should have a way to determine if the asset import is via a stream or assetkey and if it is via a stream attempt to open a new stream for the bin (I’m not sure how feasible this is).

For now I’m gonna keep trying gltf and simply register the root when I open a stream and clear it when I close.


#2

own converter? or maybe now you mean you want specify different asset location? because that is what i see based on your code.

if im correct you want have different asset locations.

myself i do it for development with gradle like:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    if(production){
        runtime project(':Assets')     // production
    } else {
        runtime files("..//SeparateAssetsDir/assets")  // develop
    }
}

then load models same way as in standard JME. (gradle take care of it, you can specify many pull asset locations)

never tried changing logic of assetManager paths.


#3

The code is literally the simplest case to get it to fail. Not at all what I’m using for my app. I have an editor in swing I use for importing models, assigning materials and game attributes (stats and stuff) then exporting all of that to jme file formats to be packaged with my game.

I use the following for my assets folder and it doesn’t require a conditional (compiles and runs fine in netbeans as well as packaged)

project(":assets"){
    apply plugin: "java"
    buildDir = rootProject.file("build/assets")
    sourceSets {
        main {
            resources {
                srcDir '.'
            }
        }
    }
}

I forget where I found that, not mine originally but works perfectly


#4

dont get me wrong, i understand you want verify issue(because it looks like). just wanted provide you simple alternative-way for different .gltf locations where you need NO modifications.(just assetManager.load … like it would be in project, but its not)

and it dont require conditional, i would try something like:

dependencies {
        runtime files("/mnt/DragonLair/Docs/Dev/JME/JMonkeyProjects/JMEConverter") 
}

replacing path with some variables and load “wip/TestCube.gltf” in assetManager.

about issue, someone else should look at it. im not familiar with assetManager modifications.


#5

The asset manager is not really designed for game editors. It’s built for loading resources in a game. That is not going to change. JME is very adamant about not compromising the engine design just to support some random game editor’s needs.

As such, if you want to use the asset manager in your game editor then you have to work around things. The easiest way in this case I think is to register your own asset locator that will let you resolve things relative to whatever current directory you want.

While the asset manager doesn’t outwardly support the type of thing you want to do, it is very ‘pluggable’.

Really any format that depends on other files will have the same issue.


#6

Can you try this, I once was using this in an editor to locate assets at runtime .

/**
 * Runtime asset locator
 * 
 * @author Ali-RS 
 */
public class RuntimeLocator implements AssetLocator{
    
    private String rootPath;

    public RuntimeLocator() {
    }

    @Override
    public void setRootPath(String rootPath) {
        this.rootPath = rootPath;
    }
    
    @Override
    public AssetInfo locate(AssetManager manager, AssetKey key) {

        Path assetPath = Paths.get(rootPath + File.separator + key.toString());
       
        if (Files.exists(assetPath)) {
            try {
                return new StreamAssetInfo(manager, key, Files.newInputStream(assetPath));
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }

    }
}

You need to register it in asset manager
assetManager.registerLocator(rootPath, RuntimeLocator.class);

then you can load your asset as usual with AssetManager.
Have not tried it for gltf/glb, hope it works.


#7

Isn’t that precisely what com.jme3.asset.plugins.FileLocator is for?


#8

Yes, swapping out a new file locator all the time is definitely possible. I was thinking of something that could be configured dynamically.

It’s possible that the asset manager implementations prevent such a thing… there are quite a few questionable design decisions in there.


#9

While I don’t want to argue jme should cater to every designers needs, this clearly comes off as a bug to me. If the asset manager supports loading assets via streams, I would argue that loading assets via a stream should work.


#10

Cool. I look forward to your redesign of the AssetManager that covers this.