BitmapText rendering issue with jme-cardboard

Hello,

I’m programming my first project with jMonkey engine 3.1 alpha 1. It’s a planetarium, or night sky simulator, for android with Google Cardboard VR support.

I used the jme-cardboard plugin to handle the rendering on android, using the CardBoardHarnessFragment. I had some troubles with the camera rotating in the inverted Y direction (pitch) and the display being distorted, which I successfully solved by modifying the source code of jme-cardboard.

I was then able to show some stars in the sky, and run the application on my Samsung Galaxy S5 phone.

Next step is adding an user interface. I cannot use the guiNode for displaying, the interface with jme-cardboard, so I decided to use Lemur to display the interface inside the rootNode, so the interface appears like a floating panel in the 3D world.

I tried to show a window (container), containing a label. It works well on my computer (Ubuntu Linux x64), but some glitches appear instead of the fonts when I try to run my project on my android device (Samsung Galaxy S5) using CardboardHarnessFragment from jme-cardboard. It works fine with the standard AndroidHarnessFragment.

I tried to troubleshoot the problem and found out that the issue is caused by BitmapText that does not correctly render when using CardboardHarnessFragment. I made a test application to demonstrate this particular issue:

package mygame;

import com.jme3.app.FlyCamAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.font.Rectangle;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

public class Main extends SimpleApplication {

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

    @Override
     public void simpleInitApp() {
        // Create a bitmap text and add it to root node
        BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
        BitmapText txt = new BitmapText(fnt, false);
        txt.setBox(new Rectangle(0, 0, 6, 3));
        txt.setLocalTranslation(0, 0, -14);
        txt.setQueueBucket(Bucket.Transparent);
        txt.setSize(1.0f);
        txt.setText("Hello world!");
        rootNode.attachChild(txt);

        // Create a green box and add it to root node
        Box boxMesh = new Box(1f,1f,1f);
        Geometry boxGeo = new Geometry("Green Box", boxMesh);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Green);
        boxGeo.setMaterial(mat); 
        boxGeo.setLocalTranslation(0, 0, -15);
        rootNode.attachChild(boxGeo);

        // Set up camera
        flyCam.setEnabled(false);
           stateManager.detach(stateManager.getState(FlyCamAppState.class));
        cam.setLocation(Vector3f.ZERO);
        cam.lookAt(boxGeo.getWorldTranslation(), Vector3f.UNIT_Y);
    }
}

When running this code on my PC, I get this:

With CardboardHarnessFragment, I get this:

With AndroidHarnessFragment, I get this:

There is clearly a problem with CardboardHarnessFragment that alters the displaying of the BitmapText but I cannot find what. Maybe is it related to the multiple viewports in CardBoardHarnessFragment?

My knowledge of the internals of JME is very basic since this is my first project, but I’m higly motivated to fix this bug that prevents me from moving forward.

Does anybody there have some clues about what is happening and how to prevent it?

BTW, thanks to all the developers involved in JME, for building this amazing piece of software and distributing it under an open source license!

BitmapText is essentially a really complicated way to display bits of texture on a bunch of batched quads. So ultimately, that’s all a bitmap text is: a mesh with some texture coordinates.

So I’m not really sure what could go wrong here.

Typical things that might cause the texture note to show up would be things like the texture not loading properly or blend modes or face culling getting screwed up or something. You could try loading the bitmap texture onto the green box to see if it shows up there.

Other than that, I’m not sure. That would be how I’d go about it, though. Create a quad and start doing things to it until it fails in a similar way.

Given that the shapes of the letter quads seem to be there, I’m going to guess that the font loaded correctly but that might be something to check also. Try to get some metrics from it and make sure it has valid data and stuff.

Thank you for your quick answer!

I tried to apply the texture from the BitmapFont to another box and it seems that you are right: the problem is with the texture.

Here is my new code:

 public class Main extends SimpleApplication {

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

    @Override
    public void simpleInitApp() {
        // Create a bitmap text and add it to root node
        BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
        BitmapText txt = new BitmapText(fnt, false);
        txt.setBox(new Rectangle(0, 0, 6, 3));
        txt.setLocalTranslation(0, 0, -14);
        txt.setQueueBucket(Bucket.Transparent);
        txt.setSize(1.0f);
        txt.setText("Hello world!");
        rootNode.attachChild(txt);
        
        
        // Create a green box and add it to root node
        Box boxMesh = new Box(1f,1f,1f);
        Geometry boxGeo = new Geometry("Green Box", boxMesh);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Green);
        boxGeo.setMaterial(mat); 
        boxGeo.setLocalTranslation(0, 0, -15);
        rootNode.attachChild(boxGeo);
        
        // Create a box with the font texture and add it to the root node
        Geometry boxGeo2 = new Geometry("Text Box", boxMesh);
        boxGeo2.setMaterial(txt.getFont().getPage(0)); 
        boxGeo2.setLocalTranslation(2, 2, -15);
        rootNode.attachChild(boxGeo2);

        // Set up camera
        flyCam.setEnabled(false);
        stateManager.detach(stateManager.getState(FlyCamAppState.class));
        cam.setLocation(Vector3f.ZERO);
        cam.lookAt(boxGeo.getWorldTranslation(), Vector3f.UNIT_Y);
    }
}

OK on PC:

Not OK on CardboardHarnessFragment:

I will try to make some fixes to the BitmapFont texture.

It seems that there’s a problem with all the textures, not specifically the BitmapFonts.

I tried to add another box with a random png texture found on the web, and it appears with the same glitches than the text when runned in CardboardHarnessFragment. Still ok on PC.

I tried to use the latest jme-cardboard release and my modified version: both have the same glitches with textures. Which is somewhat a good news: I did not break it with my modifications :sweat_smile:

I should try to run the jme-cardboard examples to see if they have an issue with textures as well.

I should also try with another device than my Samsung Galaxy S5 because it’s maybe related to the device… Which would be strange because the AsteroidBeltTravelVR app, by the author of jme-cardboard, displays textures correctly on my device. Unfortunately the source code for this app is not available and I cannot know how it’s done.

FYI here is my latest test code:

public class Main extends SimpleApplication {

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

    @Override
    public void simpleInitApp() {
        // Create a bitmap text and add it to root node
        BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
        BitmapText txt = new BitmapText(fnt, false);
        txt.setBox(new Rectangle(0, 0, 6, 3));
        txt.setLocalTranslation(0, 0, -14);
        txt.setQueueBucket(Bucket.Transparent);
        txt.setSize(1.0f);
        txt.setText("Hello world!");
        rootNode.attachChild(txt);
        
        
        // Create a green box and add it to root node
        Box boxMesh = new Box(1f,1f,1f);
        Geometry boxGeo = new Geometry("Green Box", boxMesh);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Green);
        boxGeo.setMaterial(mat); 
        boxGeo.setLocalTranslation(0, 0, -15);
        rootNode.attachChild(boxGeo);
        
        // Create a box with the font texture and add it to the root node
        Geometry boxGeo2 = new Geometry("Text Box", boxMesh);
        boxGeo2.setMaterial(txt.getFont().getPage(0)); 
        boxGeo2.setLocalTranslation(2, 2, -15);
        rootNode.attachChild(boxGeo2);
        
        // Create a box with a texture and add it to the root node
        Geometry boxGeo3 = new Geometry("Texture Box", boxMesh);
        Material texMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        texMat.setTexture("ColorMap", assetManager.loadTexture("Textures/TestTexture.png"));
        boxGeo3.setMaterial(texMat); 
        boxGeo3.setLocalTranslation(-2, -2, -15);
        rootNode.attachChild(boxGeo3);

        // Set up camera
        flyCam.setEnabled(false);
        stateManager.detach(stateManager.getState(FlyCamAppState.class));
        cam.setLocation(Vector3f.ZERO);
        cam.lookAt(boxGeo.getWorldTranslation(), Vector3f.UNIT_Y);
    }
}