Mixed axis in standalone viewport?

This might be me or not. At that point I’m not sure, hence this post.



I’m running into an issue in my game where light from an independent viewport “leaks” into the main viewport. So, I tried to find the cause by making a test run in an empty program and I discovered something odd.



The sample program is simple.



On start-up I make a simple box that receives light from two directions (left and camera-facing). The first light direction is settable, the other is static. On the screen, once the program is loaded, you can clearly see the box with two faces lit.



When you press I, that makes an independent viewport what will reuse the makeBox(lightDirection); and put that box into a node that is attached to this viewport (the viewport has a dedicated camera). Now, that new box has one different side lit. top and camera-facing instead of left and camera-facing.



I’m at a loss to explain or understand why this happens. Anyone wants to have a go at it?



NOTE: Replace the texture with your favorite one as the one in the program below won’t be found on your side.



Here’s the sample program:

[java]

package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.light.DirectionalLight;

import com.jme3.light.PointLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.renderer.Camera;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.ViewPort;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.shape.Box;

import com.jme3.texture.Texture;



/**

  • test
  • @author MadJack

    */

    public class Main extends SimpleApplication {



    Geometry box;

    boolean viewOff = true;

    PointLight dl;

    Geometry geo;



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    flyCam.setMoveSpeed(20);

    registerKeys();

    cam.setLocation(new Vector3f(-1.9600048f, 1.8629576f, 10.093461f));

    // make a box with light in UNIT_X direction

    box = makeBox(Vector3f.UNIT_X);

    // attach box

    rootNode.attachChild(box);

    // make sure light and box are updated

    rootNode.updateGeometricState();

    }



    private void makeView(Vector3f lightDirection) {

    if (viewOff) {

    // make new camera

    Camera viewCam = new Camera(165, 165);

    viewCam.setFrustumPerspective(90.0f, 1f, 1f, 75f);

    viewCam.setLocation(new Vector3f(-1.9600048f, 1.8629576f, 3.093461f));

    System.out.println(viewCam.getUp());

    viewCam.setViewPort(1.46f, 3.46f, 1.76f, 3.76f);



    // make a pointlight at 20 on Y axis

    dl = new PointLight();

    dl.setPosition(new Vector3f(0, 20, 0));

    dl.setColor(ColorRGBA.randomColor());



    // make a new box with light according to lightDirection

    geo = makeBox(lightDirection);

    // add the light to the geometry

    geo.addLight(dl);



    // turn camera to look at origin

    viewCam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);

    System.out.println(viewCam.getUp());



    // make sure geometry’s light is updated

    geo.updateGeometricState();



    Node n = new Node(“viewNode”);

    n.attachChild(geo);

    n.updateGeometricState();



    // rootNode.attachChild(geo);

    // rootNode.updateGeometricState();



    ViewPort vp = renderManager.createMainView(“view”, viewCam);

    vp.attachScene(n);

    // vp.attachScene(rootNode);

    vp.setClearEnabled(true);

    vp.setEnabled(true);

    vp.setBackgroundColor(ColorRGBA.DarkGray);

    viewOff = false;

    } else {

    viewOff = true;

    renderManager.removeMainView(“view”);

    }



    }



    private Geometry makeBox(Vector3f lightDirection) {

    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    Geometry geom = new Geometry(“Box”, b);

    geom.updateModelBound();



    DirectionalLight dl1 = new DirectionalLight();

    DirectionalLight dl2 = new DirectionalLight();



    dl1.setDirection(lightDirection);

    dl2.setDirection(Vector3f.UNIT_Z.negate());



    dl1.setColor(ColorRGBA.Yellow);

    dl2.setColor(ColorRGBA.Cyan);



    Material mat = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);

    Texture tex = assetManager.loadTexture(“Textures/ClassA.png”);

    mat.setTexture(“ColorRamp”, tex);

    geom.addLight(dl1);

    geom.addLight(dl2);

    geom.setMaterial(mat);



    return geom;

    }



    private void switchViewColor() {

    dl.setColor(ColorRGBA.randomColor());

    geo.updateGeometricState();

    }



    private void registerKeys() {

    inputManager.addMapping(“ViewOnOff”, new KeyTrigger(KeyInput.KEY_I));

    inputManager.addMapping(“SwitchColor”, new KeyTrigger(KeyInput.KEY_O));



    inputManager.addListener(buttonListener, “ViewOnOff”, “SwitchColor”);

    }

    private ActionListener buttonListener = new ActionListener() {



    public void onAction(String name, boolean isPressed, float tpf) {

    if (name.equals(“ViewOnOff”) && !isPressed) {

    makeView(Vector3f.UNIT_Y.negate());

    } else if (name.equals(“SwitchColor”) && !isPressed) {

    switchViewColor();

    }

    }

    };



    @Override

    public void simpleUpdate(float tpf) {

    //TODO: add update code

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }

    }

    [/java]



    If you want, you can comment/uncomment the following lines and see the cube in the main window “flips” as the view pops up as the viewport use the rootNode instead of new node.



    [java]

    Node n = new Node(“viewNode”);

    n.attachChild(geo);

    n.updateGeometricState();



    // rootNode.attachChild(geo);

    // rootNode.updateGeometricState();

    [/java]



    [java]

    vp.attachScene(n);

    // vp.attachScene(rootNode);

    [/java]



    EDIT: Sorry for the mistake, the commented lines were inverted. Fixed now.

Just tried something else.



If I make a clone() or deepClone() of rootNode, I always get the dreaded:



[java]

Make sure scene graph state was not changed after

rootNode.updateGeometricState() call.

Problem spatial name: Root Node

[/java]

So nothing? :frowning:

You confused yourself and me with this code :slight_smile:



First of all, when I uncomment the lines you mentioned, I get the following:

[java]java.lang.IllegalStateException: Scene graph is not properly updated for rendering.

Make sure scene graph state was not changed after

rootNode.updateGeometricState() call.

Problem spatial name: viewNode[/java]

Maybe you posted the wrong code?

In any case, because “n” is empty (see below) I just removed that statement.



Here’s what happens when “I” is pressed:

You create a box, attach it to a new node “n”.

You then detach it from that node implicitly since you’re adding it to your root node on the next statement, because you detached it from “n”, it will need to have its geometric state updated which is not done in the following statements, the exception I mentioned above will happen and is to be expected.

Since the new node is empty, it is not displayed. Instead both the new and old cubes are displayed in both viewports. Because the cubes are rendered in order, the last cube that was added, is rendered in both viewports, thus you see the cube you added with the left side unshaded.

Here’s an image to explain it better…



That’s what it looks like when I start a standalone viewport (when I press I). The boxes are the same (from the code above) so the colored sides should be “pointing” in the same direction. Is that assumption correct? IMO, those boxes should be the exact same thing. So in the picture, the greenish side on the right-side box is on the left side of the cube, but on the box in the viewport, it’s up, why?



The cube in the main view is attached to the rootNode, but the one in the standalone viewport is attached to the viewport’s node (n).



Picture:





Now, if you switch the comments like so:



Change the following from:



[java]

Node n = new Node(“viewNode”);

n.attachChild(geo);

n.updateGeometricState();



// rootNode.attachChild(geo);

// rootNode.updateGeometricState();

[/java]



to



[java]

// Node n = new Node(“viewNode”);

// n.attachChild(geo);

// n.updateGeometricState();



rootNode.attachChild(geo);

rootNode.updateGeometricState();

[/java]



And



[java]

vp.attachScene(n);

// vp.attachScene(rootNode);

[/java]



to



[java]

// vp.attachScene(n);

vp.attachScene(rootNode);

[/java]



Run the program and hit I. What will happen is the cube will “flip” in the main view (right side) as the viewport appears. Again, I’d like to understand why it does that.



Hopefully it’s clearer now.

You’re rendering the root node in both viewports

Momoko_Fan said:
Instead both the new and old cubes are displayed in both viewports. Because the cubes are rendered in order, the last cube that was added, is rendered in both viewports, thus you see the cube you added with the left side unshaded.
Momoko_Fan said:
You're rendering the root node in both viewports


How is that? I don't attach the rootNode to the viewport, I specifically use:

[java]
Node n = new Node("viewNode");
n.attachChild(geo);
n.updateGeometricState();
[/java]

I do attach the rootNode in the second example after switching the comments as I explained previously, which makes the cube flip and I do understand that by having the viewport appear, I change the "state" of the rootNode, but that doesn't explain why the cube is lit on the wrong side after being flipped.

The light comes from dl.setPosition(new Vector3f(0, 20, 0)); but it's the TOP side that is lit. Even if I use a DirectionalLight and set dl.setDirection(New Vector3f(0, 1, 0)); it's STILL the top side that is lit in the viewport.

That is the problem I'm trying to figure out. Why is the wrong side lit?

From what I see in the code, everything is working correctly.



The cube created in simpleInitApp is effected by two directional lights, one of them pointing to 1,0,0 and has yellow color, the other pointing at 0,0,-1 and has cyan color.

Result: the cube is lit from the left and the front.



The cube created in makeView is effected by two directional lights and one point light. One pointing to 0,-1,0 with yellow color, the other pointing at 0,0,-1 and has cyan color. The point light is located at 0,20,0 so it essentially acts as a directional light pointing at 0,-1,0 with random color (which is why it changes each time when you press I).

Result: the cube is lit from the top (with a bit of variation due to randomness) and from the front.



Note: In case you didn’t notice, but makeBox() takes a light argument, and you are passing different values to it, this makes the cube be lit from different directions.