nVsionary, Inc. libGDX extension code - nvisionaryinc/libgdx-extensions GitHub Wiki

Create Blender 3D Font for libGDX import

Blender (2.62)

  • Create new Project -> 'File' -> 'New'
  • Remove everything -> hit 'a' twice; hit 'x'; hit 'Enter'
  • Switch to top view/Orthogonal -> with the mouse pointer in the 3D view hit numpad '7'; hit numpad '5'
  • Add text -> 'Add' -> 'Text'
  • Change to edit mode -> hit 'Tab'
  • Remove the exiting text with 'Backspace' and then write all letters and special characters you need in a single line. The last character placed will be the 'reference character which should be the underscore -> '_'. This character will be used to lower edge alignment of all characters as well as for the size of the space character.
  • Go back to object mode -> hit 'Tab'
  • In the 'properties window' on the right you can now change the font by selecting the little 'F' ('Object Data'). Scrolling down will show a 'Font' tab.
  • Import any font you want by clicking on the little folder symbol in the 'Regular' line. In the file dialog browse to the font file and select it. Press 'Open Font' in the top-right corner.
  • You can apply geometry modifications like extruding and adding bevel in the 'Geometry' tab right above the 'Font' tab.
  • Make sure the mesh is selected -> Select the mesh by right clicking it; has an orange outline on it when selected
  • Convert the text to a Mesh -> hit 'Alt' - 'c'; select 'Mesh from Curve/Meta/Surf/Text'
  • Got to edit mode -> hit 'Tab'
  • Switch to Face selection -> hit 'Ctrl' - 'Tab'; select 'Face'
  • Switch to wireframe view -> hit 'z'
  • You can use the mouse wheel to zoom in and out
  • You can use 'Shift' and the middle mouse button to move left/right, up/down
  • Go to box selection -> hit 'b'
  • Draw a box around one character; confirm with left mouse button click; the character should now be orange; all others black
  • Separate the selection/character from the rest of the text -> hit 'p'; select 'Selection'
  • Repeat for all other characters. Make sure that the 'reference character' is the last one you change. This way it ends up being the first one in the export file.
  • Switch back to object mode -> hit 'Tab'
  • When done delete the empty text meshes. Should be the first ones in the list. On the top right of the display there is a 'scene' view ('outliner') which should have a tree view with all text objects inside. You can select each of them and confirm selection (orange outline) in the 3D view. The empty meshes should not light anything and can be deleted with right click -> select 'Delete'
  • In the 'scene' view ('outliner') tree rename all texts as follows by double left clicking the name, exchanging the number for the representing character. E.g. if the selected character is 'A' and its name is 'Text.001', rename from 'Text.001' into 'Text.A'. As far as I could see capitalization and all special characters worked.
  • Repeat for all other text meshes. This name will be used to extract the character on the libGDX importer and is very important for correct functionality.
  • Save your work
  • Export to Wavefront -> 'File' -> 'Export' -> 'Wavefront (.obj)'
  • On the left side, make sure the following things are selected:
  • 'Apply Modifiers'
  • 'Include Edges'
  • 'Include Normals' (For Lighting to work correctly)
  • 'Triangulate Faces' (libGDX only deals with triangles not quads)
  • 'Objects as OBJ Objects'
  • 'Keep Vertex Order'
  • 'Y Forward' (Found that this seems to be the right export setting for the correct behavior when rendered)
  • 'Z Up'
  • Choose a name on the top and save by clicking 'Export OBJ'
  • Done

libGDX

  • Use the 'FontObjLoader' and 'FontUtil' classes. This should be done during an early application initialization step or by showing a loading screen since this may take a moment.
Mesh text;

try {
    FileHandle objFile = null;
    objFile = Gdx.files.internal ("yusuke_all_single_characters_line.obj");
    final InputStream in = objFile.read ();
    Map <Character, Mesh> font = FontObjLoader.loadObj (in);
    in.close ();
    FontUtil.setFontMap (font);
} catch (Exception ex) {
    Gdx.app.log (TAG, ex.getMessage ());
}

text = FontUtil.createMesh ("Hello world!");

The above code will most likely not work for an Android device. The obj file processing takes way too long, over a minute on my Samsung Galaxy Tab. Therefore I'd recommend using the following for one time initialization or to deploy a preprocessed object file right away.

    try {
        FileHandle objFile = null;
        Map <Character, Mesh> font = null;
        Map <Character, float[]> fontV = null;
        objFile = Gdx.files.external ("fontmeshes");
        InputStream is = null;
        try {
            is = objFile.read ();
        } catch (Exception e) {
            FileHandle inFile = Gdx.files.internal ("yusuke_all_single_characters.obj");
            final InputStream in = inFile.read ();
            font = FontObjLoader.loadObj (in);
            in.close ();
            
            final OutputStream os = objFile.write (false);
            final ObjectOutput oo = new ObjectOutputStream (os);
            fontV = new HashMap <Character, float[]> ();
            Iterator <Character> it = font.keySet ().iterator ();
            while (it.hasNext ()) {
                Character c = it.next ();
                Mesh m = font.get (c);
                int vertLen = m.getNumVertices () * 6;
                float [] vertices = new float [vertLen];
                m.getVertices (vertices);
                fontV.put (c, vertices);
            }
            oo.writeObject ((HashMap<Character, float[]>) fontV);
            oo.flush ();
            os.close ();
            font = null;
            fontV = null;
            
            is = objFile.read ();
        }
        
        ObjectInputStream oi = new ObjectInputStream (is);
        fontV = (HashMap <Character, float[]>) oi.readObject ();
        is.close ();
        
        font = new HashMap <Character, Mesh> ();
        List <VertexAttribute> attributes = new ArrayList <VertexAttribute> ();
        attributes.add (new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE));
        attributes.add (new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE));
        Iterator <Character> it = fontV.keySet ().iterator ();
        while (it.hasNext ()) {
            Character c = it.next ();
            float [] vertices = fontV.get (c);
            Mesh m = new Mesh (true, vertices.length / 2, 0, attributes.toArray (new VertexAttribute [attributes.size ()]));
            m.setVertices (vertices);
            font.put (c, m);
        }
        FontUtil.setFontMap (font);
    } catch (Exception ex) {
        Gdx.app.log (TAG, ex.getMessage ());
    }
    text = FontUtil.createMesh ("Crazy stuff happening!");
⚠️ **GitHub.com Fallback** ⚠️