GLTF to J3O Texture Filtering Issue

if you refer to:

imo it have nothing to do with this topic and could not find “add a prob light”(only that in most cases there is lack of probe)

here we talk about texture filtering, where in yours one as i see it was some wrong texture (or stretched texture) SDK bug. And it was already adressed on Github based on comments.

Split SDK and Engine(do not treat it the same), im not even using SDK myself, but i use JME using Intellij. SDK is nice tool, tho its not essential really.

If you feel its related, then just load .gltf directly without SDK at all.

2 Likes

It works fine in my case, not sure what is going wrong in OP’s case.

Also, I do not use SDK in my case. (note that SDK does not use J3MExporter)

I have this in my gltf file:

    "samplers" : [
        {
            "magFilter" : 9729,
            "minFilter" : 9987
        }
    ],

I then convert it to j3o with external material files (exported as j3m file) by JmeConvert after that load the j3o in JME and filters loads correctly for me.

MinFilter:Trilinear
MagFilter:Bilinear

It would be nice if someone else could also test it. (Not with SDK)

@JhonKkk can you check if you are using the same JME version (3.6.1) both for exporting and importing?

2 Likes

hmm, i thought OP have it for both SDK and JMEC because:

While when I convert to j3o… (even using Jmec for conversion gives the same result):

and JMEC as i noticed do use J3MExporter

Could be he is using an older version of JME with JMEC.

Edit:
Yes JMEC uses J3MExporter.

2 Likes

good point, as i notice in gradle of JMEC:

ext.jmeVersion = “3.3.2-stable”

well… 3.3.2 is from * on Apr 26, 2020 lol - ofc need build JMEC ourself it seems and be updating it before just using JMEC

This might be case if OP were converting models using JMEC, but not using SDK but just opening in SDK. (opening / converting is different thing here ofc)

Because then SDK convert should be fine if using new SDK versions.

@JhonKkk You convert into j3o using only JMEC or also SDK->.gltf->second click->convert ?

1 Like

Here is a simple test case:

You can use with your own gltf model.

public class TestMimMapMatParams extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        Spatial model = assetManager.loadModel(new GltfModelKey("Models/scene.gltf"));
        debugMaterial(model);

        assetManager.clearCache();

        model = BinaryExporter.saveAndLoad(assetManager, model);

        debugMaterial(model);
    }

    private void debugMaterial(Spatial model) {
        model.depthFirstTraversal(new SceneGraphVisitorAdapter(){
            @Override
            public void visit(Geometry geom) {
                Material mat = geom.getMaterial();
                MatParamTexture baseColorMap = mat.getTextureParam("BaseColorMap");
                if (baseColorMap != null) {
                    Texture texture = baseColorMap.getTextureValue();
                    System.out.println("---------------------------------------");
                    System.out.println("Mat Name:" + mat.getName());
                    System.out.println("MinFilter:" + texture.getMinFilter());
                    System.out.println("MagFilter:" + texture.getMagFilter());
                }
            }
        });
    }

    public static void main(String[] args) {
        new TestMimMapMatParams().start();
    }
}
---------------------------------------
Mat Name:Material_25
MinFilter:Trilinear
MagFilter:Bilinear
---------------------------------------
Mat Name:Material_25
MinFilter:Trilinear
MagFilter:Bilinear

Yet another test explicitly using J3MExporter:

public class TestMimMapMatParams extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        Spatial model = assetManager.loadModel(new GltfModelKey("Models/scene.gltf"));

        debugMaterial(model);

        exportMaterials(model);
        assetManager.clearCache();

        model = BinaryExporter.saveAndLoad(assetManager, model);

        debugMaterial(model);
    }

    private void exportMaterials(Spatial model) {
        J3MExporter j3MExporter = new J3MExporter();

        model.depthFirstTraversal(new SceneGraphVisitorAdapter() {
            @Override
            public void visit(Geometry geom) {
                try {
                    File f = new File(new File("."), "src/main/resources/Materials/"+ geom.getMaterial().getName()+".j3m");
                    //System.out.println(f.getCanonicalFile().getAbsolutePath());
                    j3MExporter.save(geom.getMaterial(), f.getCanonicalFile());
                    Material material = assetManager.loadMaterial("Materials/"+ geom.getMaterial().getName()+".j3m");
                    geom.setMaterial(material);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    private void debugMaterial(Spatial model) {
        model.depthFirstTraversal(new SceneGraphVisitorAdapter(){
            @Override
            public void visit(Geometry geom) {
                Material mat = geom.getMaterial();
                MatParamTexture baseColorMap = mat.getTextureParam("BaseColorMap");
                if (baseColorMap != null) {
                    Texture texture = baseColorMap.getTextureValue();
                    System.out.println("---------------------------------------");
                    System.out.println("Mat Name:" + mat.getName());
                    System.out.println("MinFilter:" + texture.getMinFilter());
                    System.out.println("MagFilter:" + texture.getMagFilter());
                }
            }
        });
    }

    public static void main(String[] args) {
        new TestMimMapMatParams().start();
    }
}
---------------------------------------
Mat Name:Material_25
MinFilter:Trilinear
MagFilter:Bilinear
---------------------------------------
Mat Name:Material_25
MinFilter:Trilinear
MagFilter:Bilinear
Material Material_25 : Common/MatDefs/Light/PBRLighting.j3md {

    MaterialParameters {
      EmissivePower : 3.0
      BackfaceShadows : false
      Roughness : 1.0
      EmissiveIntensity : 2.0
      EmissiveMap : WrapRepeat_S WrapRepeat_T "Models/textures/Material_25_diffuse.png"
      UseSpecGloss : true
      Glossiness : 0.0
      BaseColor : 0.5 0.5 0.5 1.0
      ParallaxHeight : 0.05
      Metallic : 1.0
      NormalType : -1.0
      Emissive : 1.0 1.0 1.0 1.0
      BaseColorMap : WrapRepeat_S WrapRepeat_T "Models/textures/Material_25_diffuse.png"
      Specular : 0.0 0.0 0.0 1.0
    }

    AdditionalRenderState {
      PointSprite On
      FaceCull Off
    }
}

This describes my situation I think. Up until recently (when I finally started using my own editor to convert from gltf > j3o) i always noticed sparkly artefacts all over my models, and thought that was normal and was just JME rendering things at lower quality for some reason. But since I started converting in my editor I realize the models look much better

Here’s a cow model that only has the weird artefacts when I loaded it with SDK 3.6.1, compared to how it looks when I load the model and save its material with J3MExporter in my editor (which also uses jme 3.6.1):

(Sorry if I’m late to the conversation and this isn’t helpful anymore, but I thought it could still be worth showing what results I get with the SDK vs custom importing with both on the latest 3.6.1 version of jme. I ensured both use identical .j3m material files as well, but the rendering difference still persists)

4 Likes

I have tried both solutions.

I did not get separate .j3m files generated when using jmec, so the texture filtering was still incorrect nearest neighbor filtering.
I think this issue has existed since 2019 (because my post above is from 2019), however, once I follow these steps:
SDK → gltf → j3o, then open the j3o, manually click “create .j3m” for each geometry, that particular Geometry will have correct texture filtering effects, but I have to manually click “Create .j3m” for every Geometry.
I think the J3MExporter that @Ali_RS mentioned does something similar (generating separate .j3m for the j3o), but I was using an older version of JMEC, so no .j3m was generated.
Although .j3o + separate .j3m works fine, generating only .j3o results in anomalous texture filtering, so this is a GLTFtoJ3O issue that has persisted (at least since 2019).

Yes, it seems like neither the SDK nor the old JMEC generate standalone .j3m files, so the question is: if standalone .j3m files are not generated, can’t j3o get the correct texture filtering? So this should be a j3o issue.

Please note the step I mentioned in 2019 that standalone j3o must manually click “create .j3m” or use something like J3MExporter code to get correct texture filtering results. So I think this is a GLTFtoJ3O (non-separate j3m) issue.

1 Like

JMEC can generate j3m. You just have to write a script to tag the materials you want to separate out.

1 Like

Yes, I just discovered this feature; but I wonder if anyone knows how to fix the gltf to j3o (without separate .j3m) bug? Sorry I’m not familiar with j3o…

What I mean is that even without separating the .j3m, the rendering result of .j3o should remain consistent with that of .j3o + .j3m, right? But I’m not familiar with j3o conversion, I hope experts who are knowledgeable about this can help fix this issue.

I haven’t looked at the read/write methods of the relevant object types but it sounds like a case where the code doing the writing does not agree on the same defaults as the code doing the reading.

ie: writing code thinks trilinear is the default so doesn’t write that value out. reading code thinks something else is the default and so sets that on read.

Just a guess. I haven’t looked at the code even a little.

1 Like

I’m also not familiar with this part of the code, I haven’t even looked at it. But I’m certain this issue already existed back in 2019 (the first time I used the SDK to convert gltf->j3o). So I don’t know if anyone truly understands how to fix this part… :smiling_face_with_tear:

Hmm, the first test case I provided above is an example of embedded material and works fine for me.

1 Like

I’m not sure if you mean this test code? I tested this code locally, here is my code:

public class TestMimMapMatParams extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        Spatial model = assetManager.loadModel(new GltfModelKey("Models/cannon/scene.gltf"));
        debugMaterial(model);

        assetManager.clearCache();

//        model = BinaryExporter.saveAndLoad(assetManager, model);
        
        Node scene = (Node) assetManager.loadModel("Scenes/SceneLightProbe.j3o");
        scene.attachChild(model);
        
        rootNode.attachChild(scene);

        debugMaterial(model);
        
        cam.setFrustumPerspective(45.0f, cam.getWidth() * 1.0f / cam.getHeight(), 0.01f, 100.0f);
        flyCam.setEnabled(false);
        model.setLocalScale(5);
        ChaseCameraAppState chaseCameraAppState = new ChaseCameraAppState();
        chaseCameraAppState.setTarget(model);
        chaseCameraAppState.setMinDistance(0.1f);
        chaseCameraAppState.setDefaultDistance(1);
        getStateManager().attach(chaseCameraAppState);
    }

    private void debugMaterial(Spatial model) {
        model.depthFirstTraversal(new SceneGraphVisitorAdapter(){
            @Override
            public void visit(Geometry geom) {
                Material mat = geom.getMaterial();
                MatParamTexture baseColorMap = mat.getTextureParam("BaseColorMap");
                if (baseColorMap != null) {
                    Texture texture = baseColorMap.getTextureValue();
                    System.out.println("---------------------------------------");
                    System.out.println("Mat Name:" + mat.getName());
                    System.out.println("MinFilter:" + texture.getMinFilter());
                    System.out.println("MagFilter:" + texture.getMagFilter());
                }
            }
        });
    }

    public static void main(String[] args) {
        new TestMimMapMatParams().start();
    }
}

The rendering result is correct texture filtering (MinFilter:Trilinear, MagFilter:Bilinear):

Then uncomment the line “model = BinaryExporter.saveAndLoad(assetManager, model);” :

public class TestMimMapMatParams extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        Spatial model = assetManager.loadModel(new GltfModelKey("Models/cannon/scene.gltf"));
        debugMaterial(model);

        assetManager.clearCache();

        model = BinaryExporter.saveAndLoad(assetManager, model);
        
        Node scene = (Node) assetManager.loadModel("Scenes/SceneLightProbe.j3o");
        scene.attachChild(model);
        
        rootNode.attachChild(scene);

        debugMaterial(model);
        
        cam.setFrustumPerspective(45.0f, cam.getWidth() * 1.0f / cam.getHeight(), 0.01f, 100.0f);
        flyCam.setEnabled(false);
        model.setLocalScale(5);
        ChaseCameraAppState chaseCameraAppState = new ChaseCameraAppState();
        chaseCameraAppState.setTarget(model);
        chaseCameraAppState.setMinDistance(0.1f);
        chaseCameraAppState.setDefaultDistance(1);
        getStateManager().attach(chaseCameraAppState);
    }

    private void debugMaterial(Spatial model) {
        model.depthFirstTraversal(new SceneGraphVisitorAdapter(){
            @Override
            public void visit(Geometry geom) {
                Material mat = geom.getMaterial();
                MatParamTexture baseColorMap = mat.getTextureParam("BaseColorMap");
                if (baseColorMap != null) {
                    Texture texture = baseColorMap.getTextureValue();
                    System.out.println("---------------------------------------");
                    System.out.println("Mat Name:" + mat.getName());
                    System.out.println("MinFilter:" + texture.getMinFilter());
                    System.out.println("MagFilter:" + texture.getMagFilter());
                }
            }
        });
    }

    public static void main(String[] args) {
        new TestMimMapMatParams().start();
    }
}

The rendering result is incorrect texture filtering (console prints MinFilter:Trilinear, MagFilter:Bilinear), it looks like nearest sampling at this point:


Test model here is TestModel, lightprobe you can use the built-in one or any hdr image.

Clearly, the correct result is the first image. The line of code “model = BinaryExporter.saveAndLoad(assetManager, model);” caused the incorrect texture filtering result.

So the filtering settings are correct but just rendering is incorrect. Yes?

Yes,
From the console printed information, the texture filtering is indeed MinFilter:Trilinear + MagFilter:Bilinear, but I don’t know why the rendering result is nearest sampling. Unless I create a separate .j3m file, then the result is correct…

3 Likes

At least thanks to this information we are closer to find the real issue.

its not like the linked files do not create the issue, because:

/**
 * Saves a Material to a j3m file with proper formatting.
 *
 * usage is :
 * <pre>
 *     J3MExporter exporter = new J3MExporter();
 *     exporter.save(material, myFile);
 *     //or
 *     exporter.save(material, myOutputStream);
 * </pre>
 *
 * @author tsr
 * @author nehon (documentation and safety check)
 */
public class J3MExporter implements JmeExporter {

and:

            rootCapsule.clear();
            object.write(this);
            rootCapsule.writeToStream(out);

this might mean that it can save not only to file but also as j3o capsule.

Just still need find difference while file work differently than j3o saved one.

1 Like