Posts Tagged ‘freetype’

h1

super article update time and WOG

October 5, 2008

thundercats yay snerf snerf snerf snerf o-lion-o!

no, no, no, NO, NO!  no.  NO.  no…

😐

thundercats FAIL.

but they’re funny, so you can’t really hate them.  bawwww conk.

But the reason I’m posting is that I just updated the FreeType article with better code boxes plus proper vertical placement, a big thing I missed the first time like a total idiot!  The FreeType font article is already updated, will fix the code boxes on the others next.

WOG, that is to say, WORLD OF FUCKING AWESOME GOO, or for the uninitiated, World of Goo.  And it is COMING OUT on OCTOBER 13.  Yayuh.  If you pre-ordered, guess what, they send you a download link on the 6th.  Which means awesome times come sooner.

Advertisements
h1

FreeType + OpenGL revisited

October 4, 2008

Well, when I tried to display text with different vertical extents like lower case ‘g’, it obviously didn’t work, because it simply assumed all characters were the same height.  So, I’ve got new stuff involving metrics for you.  I don’t really have any need for all of them, but here’s an image I ripped off from who knows where, that shows you all the metrics involved in placement.  Search around, I’m sure you’ll find the source.

FreeType Placement Metrics

FreeType Placement Metrics

The image.  The additional metrics I’ve used are height and y-bearing.  It’s pretty simple.  In my original code I had a scale of (1,-1,1) to turn it rightside up.  Then I positioned it at -y-font->pt, which was essentially adding the font height to y, in negative scale.  But since in many fonts, not all glyphs are the same height, you must consiter each glyph.  So I added another metric: float hoss[num_glyphs], height offsets.  This was calculated by subtracting the height from the y-bearing, giving me a negative value.  Then I changed -y-font->pt to -y-(font->pt-font->hoss[*ch]), which gave me the height offset calculated for that glyph.

And the actual code to calculate this was VERY simple.  Given that you’ve just loaded your glyph, (as my article did,) you access face->glyph.  So our old code was:

tf->wids[ch]=(float)(face->glyph->advance.x>>6);

tf->qvws[ch]=bitmap.width;
tf->qvhs[ch]=bitmap.rows;

tf->qtws[ch]=(float)bitmap.width/(float)width;
tf->qths[ch]=(float)bitmap.rows/(float)height;

Yeah.  And then you’ve got the new code, which has the extra metric:

tf->wids[ch]=(float)(face->glyph->advance.x>>6);
tf->hoss[ch]=(float)((face->glyph->metrics.horiBearingY-face->glyph->metrics.height)>>6);

tf->qvws[ch]=bitmap.width;
tf->qvhs[ch]=bitmap.rows;

tf->qtws[ch]=(float)bitmap.width/(float)width;
tf->qths[ch]=(float)bitmap.rows/(float)height;

Now we have the old transform code:

glTranslatef(x,-y-font->pt,0);

Versus the new transform code, with correct height placement:

glTranslatef(x,-y-(font->pt-font->hoss[*ch]),0);

Hope this helps you.  I’ll probably update the article to include this stuff eventually.


				
h1

possible new pages and game menu sphere trouble.

September 17, 2008

I’m having mega trouble with this sphere business.  I have been trying to create my own version of the menus in Metroid Prime 2.  If you played that, you know these menus are totally sweet, and consist of a hub in the middle with spokes coming out as menu items.  You rotate this hub in 3D, and the front most option is what is selected.  I’m making a more solid version with cylindrical spokes (easy) and a sphere in the middle (hard.)  You’re probably thinking, “why doesn’t this idiot just use latitude/longitude for his sphere?”  Because it’s not as efficient as a geodesic sphere.

Now, by my understanding, the best method for triangular tessellation of a sphere is simple.  Basically, we have several arrays of longitudinal looking great circles, each one with a different pole pair.  The intersection of three great circles then is a triangle on the surface of the sphere.  Problem is, I have no idea how to store and calculate the intersections of these great circles, even though I have a perfect understanding of the geometric concept.

Fig. 1

Fig. 1

Fig. 1 shows a great circle.  That green plane intersects the center of the white sphere forming the red great circle.  So, it’s not that hard of a concept.

But do we store the plane’s information, or the circle’s?  It makes sense to me we should store the plane’s information with an orientation matrix (or other orientation representation) for the plane.  So that’s out of the way.

Now, in Fig. 2, we see what a great circle array looks like.  This is what I want each pole pair to have.

Fig. 2

Fig. 2

This is actually an image of the longitudinal lines on a lat/lon method sphere, such as a planet.  These are actually, like I said before, a great gircle array.  Now imagine a set of these, each with one pole originating from the vertex on a tetrahedron.  This is a perfectly triangle tesselated sphere.  I am really having trouble with the geometry math on this though, and would appreciate any help.  If I get this working, I will do a page in the blog pages for it.

Speaking of pages, I am thinking of creating a blog page about text rendering in OpenGL.  This will include instruction of how to load freetype fonts and how to display texture fonts.  I will discuss the converting of the alpha-luminance bitmaps given by freetype in to power of two 8 or 4 bit alpha textures, and how to make and load pure bitmap fonts.  Then displaying the textures with a simple function.  I’ll also cover metrics.  But only if it’s requested.  Or if I feel like it.

h1

dynamic text affectors

September 14, 2008

Still working on fancy text rendering.  I have my basic rendering down, but also have a counter in place to iterate text effects.  Then, each string has an initial offset (added to the main counter) to keep strings from all being at the same point in their effects.  On top of that, there’s a per character counter within the draw loop as well, that allows you to offset the effect point for each character to create things like traveling waves in text.  I’m thinking of writing an affector object with a primary counter callback, a per-char counter callback, a string transform callback, and a per-char transform callback.

So, I’m basically just working on this for now.  The menus will be done as soon as I have the font system standardized, I guess you could call it.  I’ll be leaving it as is for now to work on the hub.  I’ll show both together depending on how important the fancy text becomes in the menus.
Peace.

h1

more freedom in font rendering / cool menus

September 13, 2008

I’m rewriting the font and text code to do the following:

  • No more display lists; render on-the-fly.
  • Instead store glyph metrics to use for draw functions.
  • Add some drawing effects, as enabled by this new on-the-fly freedom.

I’ll post some screenshots of this when I have (a) fancy draw modes for fonts and (b) the menu in place.

The menu itself is quite impressive looking.  It’s not finished yet, but did you ever play Metroid Prime Echoes (the second one)?  Well, it’s a lot like those hub menus.  I don’t care if it’s not original, what are they going to do, sue me?  That’s not intellectual property.  Those things are hella coolz0rz.  I can’t beleive I said that.  KTHXBYE!

h1

freetype render screenshot

September 12, 2008

I kinda forgot a screenshot in that last post, so I’m adding one here.  See the previous post for a harrowing account of what was done, and the source code.  This is basically a screenshot of the font renderer I just got working.

h1

TRIUMPH!

September 12, 2008

So, news flash, I win at everything.  Or actually, just font rendering.  BUT I WON!  I implemented the FreeType library to render luminance-alpha format pixels to power of 2 textures, then drew them as textured quads from display lists.  My sources for technique are OGLFT and the excelent NeHeGL tutorial on the matter.  If you want to see how I did it, I have added my code below.

font.h

#ifndef FONT_SYSTEM_H
#define FONT_SYSTEM_H

#include <stdlib.h>
#include <stdio.h>

struct rat_texture_font;
struct rat_glyph_font;

typedef struct rat_texture_font rat_texture_font;
typedef struct rat_glyph_font rat_glyph_font;

int rat_start_font_system();
void rat_stop_font_system();

rat_glyph_font *rat_glyph_font_load(char *filename,int pt);
rat_texture_font *rat_texture_font_load(rat_glyph_font *font);

void rat_glyph_font_destroy(rat_glyph_font *font);
void rat_texture_font_destroy(rat_texture_font *font);

void rat_texture_font_render_text(rat_texture_font *font,float x,float y,char *text);

#endif

cfont.c

#include <stdlib.h>
#include <stdio.h>
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
#include <GL/gl.h>

static FT_Library ftlib;

typedef struct rat_texture_font
{
	float pt;
	unsigned int *textures;
	unsigned int list_base;
} rat_texture_font;

typedef struct rat_glyph_font
{
	float pt;
	FT_Face face;
} rat_glyph_font;

int rat_start_font_system()
{
	return !(FT_Init_FreeType(&ftlib));
}

void rat_stop_font_system()
{
	FT_Done_FreeType(ftlib);
}

rat_glyph_font *rat_glyph_font_load(char *filename,int pt);
rat_texture_font *rat_texture_font_load(rat_glyph_font *font);

void rat_glyph_font_destroy(rat_glyph_font *font);
void rat_texture_font_destroy(rat_texture_font *font);

rat_glyph_font *rat_glyph_font_load(char *filename,int pt)
{
	rat_glyph_font *font=(rat_glyph_font *)malloc(sizeof(rat_glyph_font));

	printf("Loading font from file \"%s\" at ptsize %i...",filename,pt);

	// load the font from the file
	if (FT_New_Face(ftlib,filename,0,&(font->face)))
	{
		printf("failed load!\n");
		free((void *)font);
		return NULL;
	}

	// freetype measures fonts in 64ths of pixels, which
	// I will never understand.  6 left bit shift multiplies
	// the pt size by 64.
	FT_Set_Char_Size(font->face,pt<<6,pt<<6,96,96);
	font->pt=pt;

	printf("done.\n");
	return font;
}

void rat_glyph_font_destroy(rat_glyph_font *font)
{
	printf("Destroying glyph font...");
	FT_Done_Face(font->face);
	free((void *)font);
	printf("done.\n");
}

inline static unsigned int _pow2(unsigned int i)
{
	register unsigned int p2;
	for (p2=1; p2<i; p2<<=1);
	return p2;
}

static int make_glyph_texture(rat_glyph_font *gf,rat_texture_font *tf,unsigned char ch)
{
	register unsigned int i,j;
	FT_Face face=gf->face;
	unsigned int *textures=tf->textures;
	unsigned int list_base=tf->list_base;

	if (FT_Load_Glyph(face,FT_Get_Char_Index(face,ch),FT_LOAD_DEFAULT))
		return 0;

    FT_Glyph glyph;
    if (FT_Get_Glyph(face->glyph,&glyph))
		return 0;

	FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1);
    FT_BitmapGlyph bitmap_glyph=(FT_BitmapGlyph)glyph;

	FT_Bitmap bitmap=bitmap_glyph->bitmap;

	unsigned int width=_pow2(bitmap.width);
	unsigned int height=_pow2(bitmap.rows);

	GLubyte* expanded_data=(GLubyte *)malloc(sizeof(GLubyte)*2*width*height);

	for (j=0; j<height;j++)
	{
		for (i=0; i<width; i++)
		{
			expanded_data[2*(i+j*width)]=
			expanded_data[2*(i+j*width)+1]=
				(i>=bitmap.width||j>=bitmap.rows)?
				0:bitmap.buffer[i+bitmap.width*j];
		}
	}

    glBindTexture(GL_TEXTURE_2D,textures[ch]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D,0,GL_ALPHA,width,height,
		0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,expanded_data);

    free((void *)expanded_data);

	glNewList(list_base+ch,GL_COMPILE);
		glBindTexture(GL_TEXTURE_2D,textures[ch]);

		glTranslatef(bitmap_glyph->left,0,0);

		glPushMatrix();
			glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

			float texx=(float)bitmap.width/(float)width;
			float texy=(float)bitmap.rows/(float)height;

			glBegin(GL_QUADS);
				glTexCoord2f(0,0); glVertex2f(0,bitmap.rows);
				glTexCoord2f(0,texy); glVertex2f(0,0);
				glTexCoord2f(texx,texy); glVertex2f(bitmap.width,0);
				glTexCoord2f(texx,0); glVertex2f(bitmap.width,bitmap.rows);
			glEnd();
		glPopMatrix();
		glTranslatef(face->glyph->advance.x>>6,0,0);
	glEndList();

	return 1;
}

rat_texture_font *rat_texture_font_load(rat_glyph_font *font)
{
	register unsigned char i;
	rat_texture_font *tf=(rat_texture_font *)malloc(sizeof(rat_texture_font));

	tf->pt=font->pt;

	// prepare the OpenGL textures / display lists
	tf->list_base=glGenLists(255);
	tf->textures=(unsigned int *)malloc(sizeof(unsigned int)*255);
	glGenTextures(255,tf->textures);

	for (i=0;i<255;i++)
	{
		if (!make_glyph_texture(font,tf,i))
		{
			glDeleteLists(tf->list_base,255);
			glDeleteTextures(255,tf->textures);
			free((void *)tf->textures);
			free((void *)tf);
			return NULL;
		}
	}

	return tf;
}

void rat_texture_font_destroy(rat_texture_font *font)
{
	glDeleteLists(font->list_base,255);
	glDeleteTextures(255,font->textures);
	free((void *)font->textures);
	free((void *)font);
}

void rat_texture_font_render_text(rat_texture_font *font,float x,float y,char *text)
{
	const char *ch;
	register unsigned int i;

	glPushAttrib(GL_LIST_BIT|GL_CURRENT_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
		glMatrixMode(GL_MODELVIEW);
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glDisable(GL_DEPTH_TEST);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

		glListBase(font->list_base);

		glPushMatrix();
			glScalef(1,-1,1);
			glTranslatef(x,-y,0);
			glCallLists(strlen(text),GL_UNSIGNED_BYTE,text);
		glPopMatrix();
	glPopAttrib();
}