Bug/Mistake using blender camera and Picture

Hello guys,
I could go ahead with my work (fixed camera engine with pre-rendered hd and pixelation effect).
I don’t know why but I have a strange behavior without any apparent reason.
In pratical…
I load the blender scene (.blend file directly,because with ogre scene i will loose some camera data).

Suppose this scene is made by 4 rooms, so there are

  • base building
  • 4 cameras (named: Camera_REGION_NAME)
  • 4 region (named: Region_REGION_NAME)

So for example there’s the Camera/Region_Dining_Room, Camera/Region_Bathroom etc… so a camera for each area of the scene.

On the simple update of my scene, I will calculate the collisions with te floor, and when the Ray touch a floor region different by the previous, there’s a check if is there a relative blender camera for this region, and a picture (named Render_REGION_NAME.png) to put as a background (scene is invisibile, only depth is wrote), jme3 camera is updated with last blender camera used.

Here some pieces of code of simpleUpdate

[java]

if (!lastRegionName.equals(currentRegionName)) {
//System.out.println("BEFORE Camera changed, \nFRN: " + lastRegionName);
lastRegionName = currentRegionName;
//System.out.println("AFTER Camera changed, \nFRN: " + currentRegionName + "\nLRN: " + lastRegionName);
try {
cameraNode = prepareHDBackground(currentRegionName, sceneNode, cameraNode, tpf);
viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Opaque, new PreHDComparator(“Background”));
} catch (Exception e) {
System.out.println("EXCP: " + e.toString());
// Nothing to do
}

        //objNode.setLocalTranslation(cam.getLocation());
    }
 
   
    cam.copyFrom(cameraNode.getCamera());

[/java]

and the method for update background picture and camera:

[java]
public CameraNode prepareHDBackground(String name, Node sceneNode, CameraNode camNode, float tpf) {

    if (camNode != null) {
        camNode.detachAllChildren();
        Quaternion rot3 = camNode.getLocalRotation();
        camNode.setLocalRotation(new Quaternion(rot3.getZ(), -rot3.getW(), -rot3.getX(), rot3.getY()));
    }
    camNode = (CameraNode) sceneNode.getChild("Camera_" + name);
    Quaternion rot3 = camNode.getLocalRotation();
    camNode.setLocalRotation(new Quaternion(-rot3.getZ(), rot3.getW(), rot3.getX(), -rot3.getY()));


    Quad q = new Quad(1, 1);
   
    Geometry background = new Geometry("Background", q);
    Material mat = new Material(assetManager, "MatDefs/Background.j3md");
    mat.setTexture("Texture", (Texture2D) getAssetManager().loadTexture("Textures/Renders/" + name + ".png"));
    mat.getAdditionalRenderState().setDepthTest(false);
    background.setMaterial(mat);
    background.setQueueBucket(RenderQueue.Bucket.Opaque);
    
    System.out.println("Textures/Renders/" + name + ".png");

   
    rootNode.attachChild(background);

    

    return camNode;

}

[/java]

Ok until here is ok, but in some cases (I don’t know why) the background is not shown, except if i keep the relative camera in a certain range position… this has not sense… for example in this screen I will try to explain

This is a screen of a bedroom from blender.
The floor is the region, and there’s a camera, in pratical when the player walks on the bedroom floor, the bedroom camera is activated, and a bacground picture (Bedroom.png) is shown as background. Normally is working, but if i try to move the camera from position 1 to position 2 for example (but is sufficient even in the center of the room or just move it a bit on the right), I will see only the player and black background (because scene color is not wrote, only depth). This event for other cameras and only for some camera positions. I tried to debug, it seems no error, is like background is loaded without any problem, but it seems like background is not loaded… I don’t know how to di for understand this problem. Do you know whether there are some bugs with blender cameras, or if (I hope) there’s a specific reason for this behavior?

Thank you very much!

I forgot to say that in the prerendering method i always deattach the background node from rootnode before attacching the new background (in last test i was trying to remove this instruction for some debugs)

try some recursive method to print out all nodes geometrys ect, and comparte with the should be case, maybee something is missing.

I tried a little test, just to be sure about the problem root.
I tried to leave the same background image for each camera (so the quad background loading in prerendering method is hardcoded on the same background), and for some cameras (almost all) is working fine, for some others cameras (for example the bedroom camera in particular positions) I will see black background. If i move camera in other positions it works fine…
It seems there’s a correlation between camera position and background loading, but is very strange, because i see that camera is looking in the right position, so it doesn’t seems that there are problem loading the camera… problem is only for background loading.
I got this problem even in the old version, using a different viewport for background, so it doesn’t seems a problem for background…
I’m not able to find any reason for this problem… no errors, non logs… very strange

how are your background made? custom meshes by any chance with no proper boundning volume?

@Empire Phoenix said: try some recursive method to print out all nodes geometrys ect, and comparte with the should be case, maybee something is missing.
Thank you for your reply, I will try it soon, even if I can't understand how it can affect this behavior, because if there was a geometry hierarchy problem, I should get an exception loading the camera or the background. If you see, in the pre-rendering method and the end there's a

[java]System.out.println(“Textures/Renders/” + name + “.png”);[/java]
and the path is right… even when background is not loaded… if a move a bit the camera everything ok… same path, same data… but right behavior… very very strange :expressionless:

@Empire Phoenix said: how are your background made? custom meshes by any chance with no proper boundning volume?

Ok, for example for bedroom, when I find the right camera angolation (for example Camera_Bedroom) I make a blender HD rendering and saving the image in asset Textures/Renders/Bedroom.png.
In JME3 i will load the entire scene (without materials and textures) and keep it invisible, only depth is wrote (in this way if i walk behind a table, player is hidden by this and it seems the background part of table is covering him).
So when player walks the bedroom floor (a plane called “Region_Bedroom”) automatically Camera_Bedroom is used and Bedroom.png loaded

I tried even with a simple picture instead textured Quad… same behavior…

If you temporarily stop writing depth on the scene (like you don’t write color) does it fix the issue?

I’m having trouble understanding exactly how your scene is setup. I think I get it but the parts of the code I’m curious to look at aren’t posted or I’m misunderstanding. You might need to put together a simple test case to illustrate the issue so that we can see it in one view and maybe spot something you haven’t.

@pspeed said: If you temporarily stop writing depth on the scene (like you don't write color) does it fix the issue?

I’m having trouble understanding exactly how your scene is setup. I think I get it but the parts of the code I’m curious to look at aren’t posted or I’m misunderstanding. You might need to put together a simple test case to illustrate the issue so that we can see it in one view and maybe spot something you haven’t.


I tried this, but no differences…
But I tried another stuff… instead of using a quad for background I tried to put a semitransparent (to see the player) picture, attached directly to rootNode, and in this way is working… so yes… it seems related to quad/geometry, maybe for geometry comparation… even if I can’t see correlation with camera position… bah…

Maybe could be a problem of my comparator (even if I don’t think because I had same issue with viewport for backgrounds so without comaprator)

[java]
public class PreHDComparator extends OpaqueComparator {

private String backgroundPrefix;

public PreHDComparator(String backgroundPrefix) {
    super();
    this.backgroundPrefix = backgroundPrefix;
}

@Override
public int compare(Geometry geometry1, Geometry geometry2) {

    if (geometry1.getName().startsWith(backgroundPrefix)) {          
        return -1;
    } else if (geometry2.getName().startsWith(backgroundPrefix)) {           
        return 1;
    } else {
        return 0;
    }

}

}
[/java]

Thank you again for all support

Just an aside:
geometry1.getName().startsWith(backgroundPrefix)

…tagging the geometry with some UserData (setUserData/getUserData) will be way more efficient than doing string comparisons.

Or if your background has a specific Material instance not shared by the rest of the scene then a simple geometry1.getMaterial() == backgroundMaterial would be the fastest of all.

@pspeed said: Just an aside: geometry1.getName().startsWith(backgroundPrefix)

…tagging the geometry with some UserData (setUserData/getUserData) will be way more efficient than doing string comparisons.

Or if your background has a specific Material instance not shared by the rest of the scene then a simple geometry1.getMaterial() == backgroundMaterial would be the fastest of all.

I want to try to play with comparators… because it seems the only reason is that with some particular camera angles , the viewport is not able to put the background quad geometry at the right rendering queue position (so in front of all others geometries except player and some others objects).

Now I’m trying in this way…

this is the code called every time I change region
[java]
if (camNode != null) {
camNode.detachAllChildren();
Quaternion rot3 = camNode.getLocalRotation();
camNode.setLocalRotation(new Quaternion(rot3.getZ(), -rot3.getW(), -rot3.getX(), rot3.getY()));
}
// camNode = camerasMap.get(“Camera_” + name); // (CameraNode) sceneNode.getChild(“Camera_” + name);
camNode = (CameraNode) sceneNode.getChild(“Camera_” + name);
Quaternion rot3 = camNode.getLocalRotation();
camNode.setLocalRotation(new Quaternion(-rot3.getZ(), rot3.getW(), rot3.getX(), -rot3.getY()));
rootNode.detachChildNamed(“Background”);

    Picture pic = new Picture("Background");
    pic.setUserData("ID", -165840);
    pic.setImage(assetManager, "Textures/Renders/" + name + ".png", true);
    pic.setWidth(1280);
    pic.setHeight(720);
    pic.setPosition(0, 0);
    Material mat = pic.getMaterial().clone();

    mat.setColor("Color", new ColorRGBA(1, 1, 1, 0.9f)); // Red with 50% transparency
    mat.getAdditionalRenderState().setDepthTest(false);
    mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

    pic.setMaterial(mat);
    pic.setQueueBucket(RenderQueue.Bucket.Opaque);

    rootNode.attachChild(pic);
    viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Opaque, new PreHDComparator(-165840));

[/java]

And this is my comparator

[java]
public int compare(Geometry geometry1, Geometry geometry2) {

    if (geometry1.getUserData("ID") != null && ((Integer) (geometry1.getUserData("ID"))) == backgroundID) {
        System.out.println(geometry1.getName());
        System.out.println(geometry2.getName());
        return -1;
    } else if (geometry2.getUserData("ID") != null && ((Integer) (geometry2.getUserData("ID"))) == backgroundID) {
        System.out.println(geometry2.getName());
        System.out.println(geometry1.getName());
        return 1;
    } else {
        return super.compare(geometry2, geometry1);
    }

}

[/java]

First of all in the compateTo last else I’ve to call super.compare(geometry2, geometry1), I don’t know why if I use super.compare(geometry1, geometry2) it seems depth is not write (for example if I walk behind a table it seems always that I’m in front of the table…)
And in any case with pictures in this way is not working… with quads yes… but with quads there’s the strange problem of some quads not rendered for some cameras position…
Could be maybe some strange mistakes that I did in the blender scenes?

Just a couple notes, not really solutions or anything…

  1. the null checks seem unnecessary since you will just use == anyway.

  2. There is no real reason to use Picture in this case as it’s just a quad with a texture. Might as well just use a Quad yourself.

  3. depth sorting is generally done using the near point on the bounding shape. Oddly shaped things will sometimes sort in front of other things… but there is no perfect way to sort things, really. It’s provably impossible in the most trivial cases.

…but if you are always sorting the background as above and it’s always accurate then it should matter. The background will always be drawn first. I assume if you cullhint always the rest of your scene then the quads are displayed correctly?

1 Like
@pspeed said: ...but if you are always sorting the background as above and it's always accurate then it should matter. The background will always be drawn first. I assume if you cullhint always the rest of your scene then the quads are displayed correctly?

Is there another way to force the background just behind the player and in front on of rest of scene? Because actually I have same compex geometries inside the scene (probably after the rendering I will delete from 3D scene a good part of them), and so as I understood the geometry comparator can not work properly sometimes.
So is there another way to do it? I searched but anything found…

Thanks again!

The cullhint thing was just a test to see if it is even a sorting issue.

I’m still not sure I understand how your scene is constructed. Like, why isn’t it background then scene, then player?

Before I did in this way, behind the background, then scene, then player, keeping the scene invisble and only depth wrote… but I continued to see a black scene (infact in this topic http://hub.jmonkeyengine.org/forum/topic/screen-smoothing-retro-games-mode/page/2/ you said me to try with geometry comparator) but infact if is possibile to keep

------ BACKGROUND --------------


---------------------------------
---------------------------------
---------- SCENE ----------------
------ (ONLY DEPTH)--------------
---------------------------------
---------------------------------
---------------------------------


          |      |
            |  |
             ||
---------- CAMERA ---------------

In this way would be perfect, I don’t have to worry about geometris sorting and I can avoid the problem (and probably even gain a bit of performance)… but I don’t know why scene is not invisbile but black…

I’m trying in this way now…

Loading picture in sky bucket
render above scene (tried with no depth writing and color writing to test)
render player

I see only player and black scene… I don’t know what is happening… are there some examples of 2.5D with JME3?
Maybe I’m totally wrong using this system for my issue

@Ray1184 said: Before I did in this way, behind the background, then scene, then player, keeping the scene invisble and only depth wrote... but I continued to see a black scene (infact in this topic http://hub.jmonkeyengine.org/forum/topic/screen-smoothing-retro-games-mode/page/2/ you said me to try with geometry comparator) but infact if is possibile to keep
------ BACKGROUND --------------


---------------------------------
---------------------------------
---------- SCENE ----------------
------ (ONLY DEPTH)--------------
---------------------------------
---------------------------------
---------------------------------


          |      |
            |  |
             ||
---------- CAMERA ---------------

In this way would be perfect, I don’t have to worry about geometris sorting and I can avoid the problem (and probably even gain a bit of performance)… but I don’t know why scene is not invisbile but black…

Are those separate viewports?

How are the materials setup (actual code)?

@pspeed said: Are those separate viewports?

How are the materials setup (actual code)?

Is only one viewport (and I need to do everything in one viewport, because after I’ve to apply the pixelation filter)

I will post the entire scene code, in this way you can have a complete overview of situation

In actual code (because is the “almost” working, except some camera angolations) I use the custom geometry comparator

The init scene
[java]
@Override
public void simpleInitApp() {

    // Init
    initKeys();
    assetManager.registerLocator("/", FileLocator.class);


    // Scene        
    backgroundMap = new HashMap<String, Geometry>();
    BlenderKey roomKey = new BlenderKey("Models/SHart_House_Interior_New/SHart_House_Interior_New.blend");
    roomKey.setFixUpAxis(false);
    Material hide = assetManager.loadMaterial("Materials/Unshaded.j3m");
    hide.getAdditionalRenderState().setColorWrite(false);
    hide.getAdditionalRenderState().setDepthWrite(true);
    sceneNode = new Node("SceneNode");
    Spatial sceneSpatial = (Spatial) assetManager.loadModel(roomKey);
    sceneSpatial.setMaterial(hide);       
    sceneNode.attachChild(sceneSpatial);
    rootNode.attachChild(sceneNode);


    // Light
    AmbientLight al = new AmbientLight();
    al.setColor(ColorRGBA.White.mult(1.2f));
    rootNode.addLight(al);

    // Player and animation
    Spatial playerSpat = (Spatial) assetManager.loadModel("Models/TP/TP.mesh.j3o");
    playerSpat.rotate(0, 0, FastMath.DEG_TO_RAD * 90);
    playerNode = new Node("Player");
    playerNode.attachChild(playerSpat);
    playerNode.setLocalTranslation(0.5f, 0.5f, 0.02f);
    playerNode.scale(0.5f);
    AnimControl control = playerSpat.getControl(AnimControl.class);
    control.addListener(this);
    aiming = false;
    try {
        channel = control.createChannel();
        channel.setAnim("my_animation");
        channel.setTime(0);
        channel.setSpeed(0);

    } catch (Exception e) {
        e.printStackTrace();
    }
    rootNode.attachChild(playerNode);
	
    // Filter
    PixelationFilter filter = new PixelationFilter();
    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
    fpp.addFilter(filter);
    fpp2 = new FilterPostProcessor(assetManager);
    OldFilmFilter ofp = new OldFilmFilter(new ColorRGBA(200, 190, 150, 255), 0.01f, 0.15f, 200f, 0.9f);
    fpp2.addFilter(ofp);

    viewPort.addProcessor(fpp); 

	// Prepare first background + camera for first location
    cameraNode = prepareHDBackground("Sala_2", sceneNode, cameraNode, 0);
 
}

[/java]

The update

[java]
@Override
public void simpleUpdate(float tpf) {

    if (currentRegionName == null || currentRegionName.equals("")) {
        currentRegionName = lastRegionName;
    }
    if (!lastRegionName.equals(currentRegionName)) {           
        lastRegionName = currentRegionName;          
        try {
            cameraNode = prepareHDBackground(currentRegionName, sceneNode, cameraNode, tpf);

            // Probably is enough to call it only one time
            viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Opaque, new PreHDComparator(-165840));
        } catch (Exception e) {
            System.out.println("EXCP: " + e.toString());
            // Nothing to do
        }

       
    }       

    cam.copyFrom(cameraNode.getCamera());       

	// Collision check for player and ray to the floor for region detection
    Quaternion rot = playerNode.getLocalRotation();
    Vector3f direction = new Vector3f();
    rot.mult(Vector3f.UNIT_Y.negate(), direction);
    Ray rayFront = new Ray(new Vector3f(playerNode.getLocalTranslation().x, playerNode.getLocalTranslation().y, playerNode.getLocalTranslation().z + 0.0f), direction.normalize());
    Ray rayBack = new Ray(playerNode.getLocalTranslation(), direction.negate().normalize());
    Ray rayDown = new Ray(playerNode.getLocalTranslation(), Vector3f.UNIT_Z.negate());

    checkCollision(rayFront, "Front", frontWalk, tpf, 0.5f, true);
    checkCollision(rayBack, "Back", backWalk, tpf, 0.5f, true);
    checkCollision(rayDown, "Down", 0, tpf, 0.5f, true);
    playerNode.rotate(0.0f, 0.0f, rotation * tpf);

}

[/java]

Raycasting simple collision detection
[java]
private void checkCollision(Ray ray, String name, float walkVar, float tpf, float treshold, boolean move) {
CollisionResults results = new CollisionResults();
sceneNode.collideWith(ray, results);
Quaternion rot = playerNode.getLocalRotation();
Vector3f movement = new Vector3f();
rot.mult(new Vector3f(0, walkVar * tpf, 0.0f), movement);
if (results.size() > 0) {
CollisionResult closest = results.getClosestCollision();
if (name.equals(“Down”)) {
// System.out.println(closest.getGeometry().getName());
currentRegionName = closest.getGeometry().getName().substring(“Region_”.length(), closest.getGeometry().getName().length() - 1);
} else if (closest.getDistance() < treshold) {
if (move) {
playerNode.move(Vector3f.ZERO);
} else {
playerNode.move(movement.negate());
}
} else {
if (move && !name.endsWith(“REP”)) {
playerNode.move(movement);
}
}
} else {
if (move && !name.endsWith(“REP”)) {
playerNode.move(movement);
}
}
}
[/java]

The background and camera loader
[java]
public CameraNode prepareHDBackground(String name, Node sceneNode, CameraNode camNode, float tpf) {

	// Fix blender camera rotation
    if (camNode != null) {
        camNode.detachAllChildren();
        Quaternion rot3 = camNode.getLocalRotation();
        camNode.setLocalRotation(new Quaternion(rot3.getZ(), -rot3.getW(), -rot3.getX(), rot3.getY()));
    }    
    camNode = (CameraNode) sceneNode.getChild("Camera_" + name);
    Quaternion rot3 = camNode.getLocalRotation();
    camNode.setLocalRotation(new Quaternion(-rot3.getZ(), rot3.getW(), rot3.getX(), -rot3.getY()));
	
	// Deattach previous background
    rootNode.detachChildNamed("Background");

    Quad q = new Quad(1, 1);

    Geometry background = new Geometry("Background", q);
    background.setUserData("ID", -165840);
    Material mat = new Material(assetManager, "MatDefs/Background.j3md");
    mat.setTexture("Texture", (Texture2D) getAssetManager().loadTexture("Textures/Renders/" + name + ".png"));
    mat.getAdditionalRenderState().setDepthTest(false);
    background.setMaterial(mat);
    background.setQueueBucket(RenderQueue.Bucket.Opaque);
    rootNode.attachChild(background);
    return camNode;

}

[/java]

The comparator
[java]
/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package it.coc.test;

import com.jme3.renderer.queue.OpaqueComparator;
import com.jme3.scene.Geometry;

/**
*

  • @author Nick
    */
    public class PreHDComparator extends OpaqueComparator {

    private int backgroundID;

    public PreHDComparator(int backgroundID) {
    super();
    this.backgroundID = backgroundID;
    }

    @Override
    public int compare(Geometry geometry1, Geometry geometry2) {

     if (geometry1.getUserData("ID") != null &amp;&amp; ((Integer) (geometry1.getUserData("ID"))) == backgroundID) {
        
         return -1;
     } else if (geometry2.getUserData("ID") != null &amp;&amp; ((Integer) (geometry2.getUserData("ID"))) == backgroundID) {
      
         return 1;
     } else {
         return super.compare(geometry2, geometry1);
     }
    

    }
    }

[/java]

That’s all… If it was not too big I will upload also the blender scene :smiley:

But at this point if is possibile and if like you said the comparator can makes problem with complex geometries, I would to do everything putting the quad in background and drawing all scene with only depth writing… I tried but every time different problems… I don’t know if there’s a specific pattern to follow

It occurs to me that with everything in the opaque bucket… your comparator might be backwards. The opaque bucket draws front to back so you may want to reverse how order is defined if you want the background to be behind.

I think currently as it’s defined, the background always gets drawn first.