Text2D for xith

Hey all

Since I couldn’t find any way of rendering text in the scenegraph (only the swing overlay) I decided to create a class myself.

I designed it to be easy to use, so it subclasses TransformGroup. The reason for this is that it uses a Billboard, and the Billboard changes the transform of its parent so it’s always facing the observer. That means, to put a text object in an already transformed node, only one line of code is needed, eg:


//myTransform could be a translation etc
TransformGroup tg = new TransformGroup(myTransform);
tg.addChild(new Text2D("Hello World!"));

A screenshot of the class in action can be seen at http://diplomen.dyndns.org/text2d.png

If there is any interest in this, I’ll post the code here for others to use.

Terje

always an interest dude :slight_smile:

Okay, here’s the code =)


/*
 * Copyright (c) 2004 Terje Wiesener <wiesener@samfundet.no>
 * 
 * This code may be freely used and distributed as long as this notice is kept intact.
 * 
 * THE CODE IS SUPPLIED AS-IS, WITH NO GUARANTEE WHATSOEVER.
 * 
 */
package com.agentus.diplom.util.xith3d;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;

import com.xith3d.loaders.texture.TextureLoader;
import com.xith3d.scenegraph.Appearance;
import com.xith3d.scenegraph.Billboard;
import com.xith3d.scenegraph.GeometryArray;
import com.xith3d.scenegraph.Material;
import com.xith3d.scenegraph.Shape3D;
import com.xith3d.scenegraph.Texture;
import com.xith3d.scenegraph.Texture2D;
import com.xith3d.scenegraph.TransformGroup;
import com.xith3d.scenegraph.TransparencyAttributes;

/** Implementation of 2D text for Xith3d
 * 
 * @author Terje Wiesener <wiesener@samfundet.no>
 * 
 */
public class Text2D extends TransformGroup {
      private static final TextureLoader tl;
      private static final Graphics2D g;
      private static final Material mat;
      private static final TransparencyAttributes ta;
      private static final HashMap textures;
      
      static{
            tl = new TextureLoader();
            g = (new BufferedImage(100, 100,
                  BufferedImage.TYPE_INT_ARGB)).createGraphics();
            mat      = new Material();
            mat.setLightingEnable(false);
            ta = new TransparencyAttributes(TransparencyAttributes.BLENDED, 0.0f);
            textures = new HashMap();
      }
      
      public Text2D(String text) {
//            this(text, new Font("SansSerif", Font.BOLD, 20));//default font
            this(text, new Font("Dialog", Font.BOLD, 20));//default font
      }
      
      public Text2D(String text, Font font) {
            super();
            TextInstance ti = new TextInstance(text, font);
            Texture2D stringTex = (Texture2D)textures.get(ti);
            
            if (stringTex == null){//texture is not cached
                  Rectangle2D bounds = font.getStringBounds(text, g.getFontRenderContext());
                  int textWidth = (int) bounds.getWidth();
                  int textHeight = (int) bounds.getHeight();
                  int width = 64 * (int)(Math.ceil((float)textWidth / 64.0f));//make width a multiple of 64
                  int height = 128;
                  
                  BufferedImage bi = new BufferedImage(width, 128,
                              BufferedImage.TYPE_INT_ARGB);
                  Graphics2D g2 = (Graphics2D) bi.createGraphics();
                  g2.setColor(Color.WHITE);
                  g2.setFont(font);
                  g2.drawString(text, (width - textWidth) / 2, textHeight + 1);//draw in the top middle
                  
                  stringTex = (Texture2D) tl.constructTexture(bi, "RGBA",
                              false, Texture.BASE_LEVEL, Texture.BASE_LEVEL_LINEAR,
                              Texture.WRAP, false, TextureLoader.SCALE_DRAW_BEST);
                  
                  textures.put(ti, stringTex);//cache texture for later use
            }
            Appearance app = new Appearance();
            app.setMaterial(mat);
            app.setTexture(stringTex);
            app.setTransparencyAttributes(ta);
            
            
            Billboard billboard = new Billboard(GeometryArray.COORDINATES
                        | GeometryArray.TEXTURE_COORDINATE_2,
                        1.5f * ((float)stringTex.getWidth() / (float) stringTex.getHeight()), 1.5f);
            this.addChild(new Shape3D(billboard, app));
      }
      
      private class TextInstance {
            public Font font;
            public String text;
            
            public TextInstance(String text, Font font){
                  this.text = text;
                  this.font = font;
            }
            
            public int hashCode () 
            { 
                  int result = 17; 
                  result += text.hashCode(); 
                  result += font.hashCode();
                  return result; 
            }
            
            public boolean equals ( Object other ) 
            { 
                  if ( other == null ){ 
                        return false; 
                  } 
                  else if ( other instanceof TextInstance){
                        TextInstance otherText = (TextInstance) other;
                        if (!otherText.text.equals(this.text)) return false;
                        if (!otherText.font.equals(this.font)) return false;
                        return true;
                  }
                  else return false;
            }
      }
}

You’ll probably notice that the default height of the text object is 1.5, this is because I use it to place labels over objects with a radius of 1.0 =)

Terje

this looks useful, I think I might use it myself.

A thought - so it doesn’t get lost in the forum, would you think about putting it up on xith.org or as part of the xith-tk project?

small utilities like this could easly go in the wiki, or as a downloadable package at xith-tk.dev.java.net

Will.

I vote for a downloadable package at xith-tk, and maybe in xith-tk CVS.

Yuri

I have sent a request for joining the xith-tk group, I guess that is needed for adding any files to the file list or cvs?

In my opinion this tool is a little small for justifying a downloadable package, but maybe that’s easier for people. Which package name should be used for something like this? com.xith3d.something?

I registered on the xith.org tiki, but where is a good place to put something like this? In the FAQ maybe?

Terje

I’ve approved the developer role - you should now be able to add files to the download section (and if you’ve sent ur fax to sun, use CVS as well).

package name can be what ever you like, so long as it isn’t “com.xith3d” as that is reserved for the core to avoid confusion.

You’re welcome to use “org.xith3d.anything” or any other name of your choosing.

To create a new page in the Wiki - you just go to http://xith.org/tiki-index.php?page=YourNewPageName

And click “create page”.

It’s just a bit more persistant than these forums where info just gets buried.

However - there needs to be an index of such pages. I’m thinking of creating a Wiki “Xith3D examples and tools” page which can index such handy classes. However, such pages can be linked to from the FAQ & these forums. I’d prefer larger topics to have their own page, and just linked to in the FAQ.

Feel free to use CVS, althogh you don’t have to. If you program like me then you’ll probably create more handy utils in the future which you can add to the kit later on.

Will.

Okay, the file is added to the xith-tk page.

However, I couldn’t create a page in the wiki. I tried going to http://xith.org/tiki-index.php?page=Text2D and the option “create” is not there.

Is my tiki user (orz) lacking some rights?

Terje

Yes. Try now :slight_smile:

Will.