[SOLVED] SkyControl giving nullpointer when loaded from saved j3o file

Hi @sgold , please can you assist.
I have started building in the SkyControl into my upcoming scene editor but I am now faced with an issue. In my scene editor I add the SkyControl to the scene and then save the scene as a .j3o file.
As soon as I load the the scene from file again it will hit the render update methods.
At that stage I get the following exception:

SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.NullPointerException
	at com.jme3.renderer.opengl.GLRenderer.clearVertexAttribs(GLRenderer.java:2911)
	at com.jme3.renderer.opengl.GLRenderer.renderMeshDefault(GLRenderer.java:3245)
	at com.jme3.renderer.opengl.GLRenderer.renderMesh(GLRenderer.java:3276)
	at com.jme3.material.logic.DefaultTechniqueDefLogic.renderMeshFromGeometry(DefaultTechniqueDefLogic.java:72)
	at com.jme3.material.logic.DefaultTechniqueDefLogic.render(DefaultTechniqueDefLogic.java:97)
	at com.jme3.material.Technique.render(Technique.java:167)
	at com.jme3.material.Material.render(Material.java:1052)
	at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:651)
	at com.jme3.renderer.queue.RenderQueue.renderGeometryList(RenderQueue.java:273)
	at com.jme3.renderer.queue.RenderQueue.renderQueue(RenderQueue.java:315)
	at com.jme3.renderer.RenderManager.renderViewPortQueues(RenderManager.java:936)
	at com.jme3.renderer.RenderManager.flushQueue(RenderManager.java:823)
	at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1184)
	at com.jme3.renderer.RenderManager.render(RenderManager.java:1248)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:278)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:160)
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:201)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:242)
	at java.base/java.lang.Thread.run(Thread.java:829)

Here is my setup:
OS: Linux Mint
Java: JDK 11
SkyControl: 1.0.3
Heart: 8.0.0

Running on jMonkeyEngine 3.5.2-stable
 * Branch: HEAD
 * Git Hash: 8ab3d24
 * Build Date: 2022-04-21
4 Likes

Thanks for reaching out to me.

It’s a curious place to crash:

It seems unlikely that context is null in line 2911, since it wasn’t null in line 2907. That implies boundAttribs[idx] is null. (Already I take the enhanced NPE messages of JDK 17 for granted.) Perhaps some idx value appears twice in oldList[]?

GLRenderer isn’t a part of the Engine I know much about. While I can’t rule it out, I struggle to imagine how the crash is connected with SkyControl.

In order to make further progress understanding the crash, I think I’ll need a simple test case I can run, or at least a copy of the .j3o file.

Yes sure I will provide a .j3o file asap.
At first I thought it was something in the editor but no it was not. I created a simpleapplication with a few boxes in it and then just a normal setup of SkyControl.
And whala it happend again even with the simple setup.

1 Like

Okay here is the test class.
Please just change the file location for your own file structure.
You also need to run the class twice.
The first run will create the file and the second run will load it.


import com.jme3.app.SimpleApplication;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.export.binary.BinaryImporter;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import jme3utilities.sky.SkyControl;
import jme3utilities.sky.StarsOption;
import jme3utilities.sky.Updater;

/**
 *
 * @author nicki
 */
public class TestSkyControl extends SimpleApplication {

    private DirectionalLight directionalLight;
    private AmbientLight ambientLight;
    private Node sceneNode;
    private File file = new File("/home/ndebruyn/Personal/GameDevelopment/scenes/sky.j3o");

    public static void main(String[] args) {

        TestSkyControl app = new TestSkyControl();
        app.start();

    }

    @Override
    public void simpleInitApp() {
        loadScene();
        loadCamera();

    }

    private void loadScene() {

        if (file.exists()) {
            //Load existing scene
            Spatial loaded = openSpatial(file);
            sceneNode = (Node) loaded;
            rootNode.attachChild(sceneNode);
            System.out.println("LOADED SCENE FROM FILE");

        } else {
            //Create a new scene
            sceneNode = new Node("Scene");
            rootNode.attachChild(sceneNode);

            Spatial box = addBox(sceneNode, 10, 0.1f, 10);
            addColor(box, ColorRGBA.Orange, 0);

            box = addBox(sceneNode, 0.2f, 5f, 0.2f);
            addColor(box, ColorRGBA.Cyan, 1);

            directionalLight = new DirectionalLight(new Vector3f(1, 1, 1), ColorRGBA.White);
            sceneNode.addLight(directionalLight);

            ambientLight = new AmbientLight(ColorRGBA.LightGray);
            sceneNode.addLight(ambientLight);

            loadSky();

            try {
                saveSpatial(sceneNode, file);
            } catch (Exception ex) {
                Logger.getLogger(TestSkyControl.class.getName()).log(Level.SEVERE, null, ex);
            }

            System.out.println("CREATED SCENE AND SAVE TO FILE");
        }

    }

    private void loadSky() {
        //Add the sky control stuff
        SkyControl skyControl = new SkyControl(assetManager, cam, 0.8f, StarsOption.Cube, true);
        sceneNode.addControl(skyControl);
        skyControl.setCloudiness(0.2f);
        skyControl.setCloudsYOffset(0.4f);
        skyControl.setCloudsRate(10);
        skyControl.getSunAndStars().setHour(13f);
        skyControl.setEnabled(true);

        Updater updater = skyControl.getUpdater();
        updater.setAmbientLight(ambientLight);
        updater.setMainLight(directionalLight);

    }

    private void loadCamera() {
        cam.setLocation(new Vector3f(-10, 4, 10));
        cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
        flyCam.setMoveSpeed(10);
    }

    private Spatial addBox(Node parent, float xExtend, float yExtend, float zExtend) {
        Box box = new Box(xExtend, yExtend, zExtend);
        Geometry geometry = new Geometry("box", box);
        parent.attachChild(geometry);
        return geometry;
    }

    private Material addColor(Spatial spatial, ColorRGBA colorRGBA, int type) {
        Material material = null;

        if (type == 1) {
            material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
            material.setBoolean("UseMaterialColors", true);
            material.setColor("Ambient", colorRGBA);
            material.setColor("Diffuse", colorRGBA);

        } else if (type == 2) {
            material = new Material(assetManager, "Common/MatDefs/Light/PBRLighting.j3md");
            material.setColor("BaseColor", colorRGBA);
            material.setFloat("Metallic", 0f);
            material.setFloat("Roughness", 0.5f);

            spatial.setMaterial(material);

        } else {
            material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            material.setColor("Color", colorRGBA);

        }

        spatial.setMaterial(material);

        return material;
    }

    private void saveSpatial(Spatial spatial, File file) throws Exception {
        BinaryExporter exporter = BinaryExporter.getInstance();
        if (file != null && !file.getName().endsWith(".j3o")) {
            throw new Exception("Invalid file extension, must be .j3o");
        }

        try {
            exporter.save(spatial, file);
            System.out.println("Spatial " + file.getName() + " successfully saved! Location: " + file.getPath());

        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    /**
     * Opens a .j3o on the file system
     *
     * @param file
     * @return
     */
    private Spatial openSpatial(File file) {
        if (file == null || !file.exists()) {
            return null;
        }

        BinaryImporter importer = BinaryImporter.getInstance();
        importer.setAssetManager(assetManager);
        try {
            Spatial spatial = (Spatial) importer.load(file);
            System.out.println("Found spatial: " + spatial.getName());

            return spatial;

        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

}
1 Like

Okay, I’ve reproduced the NPE crash.

Edit: As expected, boundAttribs[idx] is null at the point of failure. However, the null is not caused by duplicate indices. Here are the values at the start of the final invocation of clearVertexAttribs():

oldLen = 1
oldList[0] = 1
boundAttribs[1] = null

According to the javadoc for boundAttribs, null array elements are legal:

I’ve filed a new issue at GitHub to get this fixed, hopefully in v3.6 .

3 Likes

Yeah you are the man.
Thanks for taking the time to investigate this issue.
I think this fix will benefit the engine in the future.

2 Likes

By the way, I think it is a good opportunity to test your editor with JME 3.6.0-beta2 and let us know if you had an issue. :slightly_smiling_face:

1 Like

Okay I will do that. It would be great if we can get that issues sorted for the 3.6 release.
Just give me some time, maybe later today I will try 3.6 in my editor. I have some chores first…
Such as cleaning the pool and painting a room. Hehe

2 Likes

I have picked up something but I don’t know if it was logged for 3.6.0.

It seems like this does not work anymore.

material.getAdditionalRenderState().setLineWidth(5);

In order for this work, the app must be in “compatibility” profile, which is no longer the default as of v3.6. The easiest workaround would be something like:

        AppSettings s = new AppSettings(true);
        s.setRenderer(AppSettings.LWJGL_OPENGL2);
        app.setSettings(s);
1 Like

Please see the below issue for why it does not work and a workaround

1 Like

@ndebruyn: Sorry I accidentally edited your post instead of replying to it. My bad!

1 Like

Shouting: “Hey, world… stop removing features from OpenGL that we like to use!!!”

I don’t think it will help but I thought I would try. :slight_smile:

Haha, thanks for the suggestion but I am okay.
I found a different solution by making use of the CartoonEdgeProcessor.
It work very well in my case where I used it for highlighting the selected object.
I don’t really know of any other solution, UNLESS someone with super glsl skills can show the way.
Anyways, here is a little scene I slapped together for demonstration purposes.

1 Like

The NPE in GLRenderer.clearVertexAttribs() issue is solved and will be included in the next beta release.

Thanks @sgold for submitting the fix. :slightly_smiling_face:

3 Likes