Setting up a 2D viewport with per-pixel coordinates

I am working on a 2D project now, and I really need to be able to position my objects in 2D space, but with screen pixels as space units. Much akin to the GUI node. Only the problem is that the GUI node has troubles with the Z order and is in a specific gui bucket.

My current code is, as I looked in the sources and tried to invent something:

public void init2dViewport() {
    cam2d = new Camera(settings.getWidth(), settings.getHeight());
    cam.setFrustumPerspective(45f, (float) cam2d.getWidth() / cam2d.getHeight(), 1f, 1000f);
    cam.setLocation(new Vector3f(0, 0, 0));
    cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
    cam.setParallelProjection(true);
    vp2d = getRenderManager().createMainView("2d", cam2d);
    vp2d.setClearFlags(true, true, true);
    node2d = new Node("2d root");
    vp2d.attachScene(node2d);
}

and I call this method from simpleInitApp. However, it causes this error:

SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call. 
Make sure you do not modify the scene from another thread!
Problem spatial name: 2d root
	at com.jme3.scene.Spatial.checkCulling(Spatial.java:358)
	at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:719)
	at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:712)
	at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1086)
	at com.jme3.renderer.RenderManager.render(RenderManager.java:1145)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:253)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:193)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232)
	at java.lang.Thread.run(Thread.java:745)

I understand what it means, but I don’t get why is this happening. And also, I am not sure that the code I crafted would give me the desired view, I feel like something is missing.

Could you please, tell me, how do I create such a Node that would work all like the rootNode, but with the coordinate system of and without the problems of the GUI node?

If you create your own viewport then you have to manage your own root node for that viewport. This means calling updateLogicalState and updateGeometricState at the appropriate times. Search the forum for ViewportState or ViewportAppState and you will probably find a few hits talking about this.

Could you elaborate on what the issues are with the gui bucket? Are you trying to draw 3D objects in 2D? For 2D objects, the gui bucket shouldn’t be an issue. (For 3D objects, it inexplicably flattens Z after sorting so the z buffer is all messed up for all but the simplest 3D objects… I don’t know why.)

Note: setting perspective values makes no sense if you are going to turn around and turn off perspective projection in your camera. So you will need to manually set proper frustum values based on what you want to see.

Alright, got it about the manual updates, thanks! I just did not pick them up from the SimpleApplication class where from I took all the other cues. Now it runs.

So, yes, my aim is to be able to freely display “3D” objects in that 2D projection, positioning and scaling them according to the pixel screen coordinates. One thing that I definitely care for is the Z-order. As you correctly mention, the gui node hinders the use of it. I am not sure if it gives any other benifit, but that is unacceptable, any more or less complex models or animations are doomed to look nasty.

So, here’s what I’ve got after correcting my code as I understood your answer:

public void init2dViewport() {
    cam2d = new Camera(settings.getWidth(), settings.getHeight());
    cam2d.setFrustum(1f, 1000f, -settings.getWidth()/2, settings.getWidth()/2, -settings.getHeight(), settings.getHeight());
    cam2d.setLocation(new Vector3f(0, 0, 0));
    cam2d.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
    cam2d.setParallelProjection(true);
    vp2d = getRenderManager().createMainView("2d", cam2d);
    vp2d.setClearFlags(true, true, true);
    node2d = new Node("2d root");
    vp2d.attachScene(node2d);
    vp2d.setBackgroundColor(ColorRGBA.Green);

    Box b = new Box(100, 100, 100);
    Geometry g = new Geometry("Box", b);
    Material mat = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Red);
    g.setLocalTranslation(0, 0, -500);
    g.setMaterial(mat);
    node2d.attachChild(g);
}

In my understanding, now I should see the red square 100x100 pixels in dimensions, on the green background. However, this is not true. I only see the green background. Am I setting any values incorrectly?

PS: at this point setting the parallel projection to true or false does not make any difference.

This is probably giving you something nasty.

If you are at 0,0,0 and asked to look at 0,0,0… what do you think the direction should be?

You are probably off setting the orientation directly anyway. Why get complicated math involved?

That looked a bit strange to me too, but I took it from LegacyApplication – the main 3D camera is set up like this. I figured that these lines either have no effect, or position the camera at 0, 0, 0 and somehow make it look in the -Z direction. At least that’s how the main camera in SimpleApplication ends up being after these commands. However, if I remove those two lines or any one of them, nothing changes… still – the green background without the box… what else could be the problem?

Well, after some more experiments, I’ve found out that doing all the same thing but with the default camera and the default viewport with the default rootNode works just fine.

The problem turns out to be with the viewport I am creating – Idk for what reason does not it render its scene… looks like viewports do not require any additional handling, as I gather from the SDK examples/tests.

Yeah, we don’t have enough info to say… we can’t see where you are initializing things or where you are updating your own root nodes, etc… Could be lots of stuff.

At least now you have something working to go from, though.

Whenever I see “2D” my monkey sense tingles. What do you ACTUALLY want to do?

I also wonder. Why not do an isometric game or top down or side view? That is pretty much 2D, yeah I know not really maybe more a 2.5 D. I mean what’s the point of using a 3D engine to make pure 2D game? But yeah I just wonder. there must be a reason I dont see yet.

Because it’s easier to make a 2D game with a 3D engine than to make a 3D game with a 2D engine… :slight_smile:

1 Like

Usually main use-case for such actions is - to use shaders. Personally I did not find any shader-based 2d engine in java when I needed one.

Another use-cacse is to display some 2d stuff while everything else is genuine 3d. Minimaps and dynamic in-world map views for example.

Unfortunately I did not create my own viewport for 2d stuff so can’t be helpful here.

A 2D game, indeed. And Prog is absolutely right: shaders. If I am to program in Java and I want to use OpenGL enabled with shaders for the game, and I’ve been using JMonkeyEngine a lot, so why not just do 2D with it? OpenGL is perfectly suitable for it, as well as the engine with all its facilities readily available like the scenegraph and the resource system and all that.

As for games – consider puzzles, for example. These do not really often need more than just 2D.

And when you are saying “why make a 2D game with a 3D engine”, I would remind you that if you do anything with graphics that is accelerated on GPU, then really you don’t have options except for using OpenGL (or DirectX for those MS folks). Or are you implying that I am to abandon the graphical acceleration for any 2D game? I don’t see a point in this.

Yeah, JME is fine for 2.5 D games like yours. You really don’t even have to create a separate camera or viewport or anything. You could just use the main viewport and adjust the frustum of the camera… presuming you figure out how to get that right.

I’ve done it before… I just don’t remember where or if I still have the code. I ended up doing a full 3D view setup so that the camera was the right distance for unit = pixel scaling. I actually ended up wanting the foreshortening of perspective.

However, I will mention that at one point in my experimenting I had my geometry inverted because of how I setup the camera. So you might consider turning on back faces and front faces on your test cube or whatever… just in case.

LibGDX does it:

ahem… it’s just that shaders are not first class citizens for libGDX :slight_smile: