void FontRenderer::RenderText(unsigned int x, unsigned int y, const char* pText) { bool IsCullEnabled = glIsEnabled(GL_CULL_FACE); glDisable(GL_CULL_FACE); assert(strlen(pText) < MAX_STRING_LEN); wchar_t text[MAX_STRING_LEN] = { 0 }; int len = mbstowcs(text, pText, strlen(pText)); assert(len > 0); vertex_buffer_clear( m_pTextBuffer ); TextureGlyph* pGlyph = texture_font_get_glyph( m_pFont, text[0] ); assert(pGlyph); Pen pen = {(float)x, (float)y}; texture_glyph_add_to_vertex_buffer( pGlyph, m_pTextBuffer, &markup, &pen, 0 ); for( size_t i=1; i<wcslen(text); ++i ) { pGlyph = texture_font_get_glyph( m_pFont, text[i] ); assert(pGlyph); int kerning = texture_glyph_get_kerning( pGlyph, text[i-1] ); texture_glyph_add_to_vertex_buffer( pGlyph, m_pTextBuffer, &markup, &pen, kerning ); } glActiveTexture(GL_TEXTURE0); glBindTexture( GL_TEXTURE_2D, m_pManager->atlas->texid ); m_fontShader.Enable(); int viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); float xScale = 2.0f / (float)viewport[2]; float yScale = 2.0f / (float)viewport[3]; GLfloat Trans[] = { xScale, 0.0f, 0.0f, -1.0f, 0.0f, yScale, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; m_fontShader.SetTransformation(Trans); vertex_buffer_render( m_pTextBuffer, GL_TRIANGLES, "vtc" ); if (IsCullEnabled) { glEnable(GL_CULL_FACE); } }
// ------------------------------------------------------------------- init --- void init( void ) { atlas = texture_atlas_new( 512, 512, 1 ); font = texture_font_new_from_file( atlas, 32, "../media/fonts/Vera.ttf" ); texture_glyph_t *glyph; // Generate the glyp at 512 points, compute distance field and scale it // back to 32 points // Just load another glyph if you want to see difference (draw render a '@') glyph = load_glyph( "../media/fonts/Vera.ttf", "@", 512, 64, 0.1); vector_push_back( font->glyphs, &glyph ); texture_atlas_upload( atlas ); glyph = texture_font_get_glyph( font, "@"); GLuint indices[6] = {0,1,2, 0,2,3}; vertex_t vertices[4] = { { -.5,-.5,0, glyph->s0,glyph->t1, 0,0,0,1 }, { -.5, .5,0, glyph->s0,glyph->t0, 0,0,0,1 }, { .5, .5,0, glyph->s1,glyph->t0, 0,0,0,1 }, { .5,-.5,0, glyph->s1,glyph->t1, 0,0,0,1 } }; buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" ); vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); program = shader_load( "../media/shaders/distance-field.vert", "../media/shaders/distance-field-2.frag" ); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); }
// --------------------------------------------------------------- add_text --- void add_text( vertex_buffer_t * buffer, texture_font_t * font, wchar_t * text, vec4 * color, vec2 * pen ) { size_t i; float r = color->red, g = color->green, b = color->blue, a = color->alpha; for( i=0; i<wcslen(text); ++i ) { texture_glyph_t *glyph = texture_font_get_glyph( font, text[i] ); if( glyph != NULL ) { int kerning = 0; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text[i-1] ); } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); int x1 = (int)( x0 + glyph->width ); int y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint indices[] = {0,1,2,0,2,3}; vertex_t vertices[] = { { x0,y0,0, s0,t0, r,g,b,a }, { x0,y1,0, s0,t1, r,g,b,a }, { x1,y1,0, s1,t1, r,g,b,a }, { x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); pen->x += glyph->advance_x; } } }
// ----------------------------------------------------- console_add_glyph --- void console_add_glyph( console_t *self, wchar_t current, wchar_t previous, markup_t *markup ) { texture_glyph_t *glyph = texture_font_get_glyph( markup->font, current ); if( previous != L'\0' ) { self->pen.x += texture_glyph_get_kerning( glyph, previous ); } float r = markup->foreground_color.r; float g = markup->foreground_color.g; float b = markup->foreground_color.b; float a = markup->foreground_color.a; int x0 = self->pen.x + glyph->offset_x; int y0 = self->pen.y + glyph->offset_y; int x1 = x0 + glyph->width; int y1 = y0 - glyph->height; float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint indices[] = {0,1,2, 0,2,3}; vertex_t vertices[] = { { x0,y0,0, s0,t0, r,g,b,a }, { x0,y1,0, s0,t1, r,g,b,a }, { x1,y1,0, s1,t1, r,g,b,a }, { x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back( self->buffer, vertices, 4, indices, 6 ); self->pen.x += glyph->advance_x; self->pen.y += glyph->advance_y; }
Vector2 Render::GetTextSize(const Font& font, const std::string& text) { float totalW = 0; float totalH = 0; float cx = 0; float cy = 0; unsigned long len = text.size(); for (unsigned long i = 0; i < len; i++) { char l = text[i]; texture_glyph_t* glyph = texture_font_get_glyph(font.FontHandle, (wchar_t)text[i]); if (glyph == null) { TL_ASSERT(false); return Vector2::Zero; } if (i > 0) { cx += texture_glyph_get_kerning(glyph, text[i - 1]); } if (l == '\n') { cy += font.FontHandle->height; cx = 0; continue; } cx += glyph->advance_x; if (cx > totalW) totalW = cx; if (cy + font.FontHandle->height > totalH) totalH = cy + font.FontHandle->height; } return Vector2(totalW, totalH); }
void opengl_font_size( const wchar_t* text, mFloat* x, mFloat* y) { opengl_font_t* font = (opengl_font_t*)current_font->data; if(x) { size_t i, len = wcslen(text); int last_offset; mFloat totx = (mFloat)0.0; for(i = 0 ; i < len ; i++) { texture_glyph_t *glyph = texture_font_get_glyph(font->font, text[i]); if(glyph == NULL) continue; totx += glyph->advance_x / window_zoom;// - texture_glyph_get_kerning(glyph, text[i]) / window_zoom; last_offset = glyph->offset_x; } *x = totx - last_offset; } if(y) *y = (int)font->font->height / window_zoom; }
void add_text( vertex_buffer_t * buffer, texture_font_t * font, char* fileBuffer, int bufferSizeBytes, const char* text, vec2 * pen, int currentSize ) { vec2 startPos = *pen; size_t length = strlen(text); Color vertColor = Color::White; for(size_t i=0; i < length; ++i) { if(text[i] == '\n') { pen->x = startPos.x; pen->y -= currentSize; continue; } if(text[i] == '<') { if(i + 1 < length && text[i + 1] == '#') { vertColor = ParseColor(text, i); i += 11; } } float r = vertColor.r; float g = vertColor.g; float b = vertColor.b; float a = vertColor.a; texture_glyph_t *glyph = texture_font_get_glyph( font, fileBuffer, bufferSizeBytes, text[i] ); if( glyph != NULL ) { float kerning = 0; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text[i-1] ); } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); int x1 = (int)( x0 + glyph->width ); int y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint indices[6] = {0,1,2, 0,2,3}; vertex_t vertices[4] = { {(float)x0, (float)y0, 1.0f, s0, t0, r, g, b, a}, {(float)x0, (float)y1, 1.0f, s0, t1, r, g, b, a}, {(float)x1, (float)y1, 1.0f, s1, t1, r, g, b, a}, {(float)x1, (float)y0, 1.0f, s1, t0, r, g, b, a} }; vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); pen->x += glyph->advance_x; } } }
TextureGlyph* TextureFont::GetGlyph(wchar_t charcode) { texture_glyph_t* glypht = texture_font_get_glyph(static_cast<texture_font_t*>(self_), charcode); TextureGlyph*& glyphp = glyphs_[static_cast<void*>(glypht)]; if(!glyphp) glyphp = new TextureGlyph(static_cast<void*>(glypht)); return glyphp; }
Vector2 Render::GetTextSize(char letter) { texture_glyph_t* glyph = texture_font_get_glyph(this->DefaultFont->FontHandle, (wchar_t)letter); if (glyph == null) { TL_ASSERT(false); return Vector2::Zero; } return Vector2((float)glyph->width, (float)glyph->height); }
/** * @text * @floatarray [offsetx,offsety,width,height,s0,t0,s1,t1] * @start optional * @end optional */ METHOD_BEGIN(glyphs, info) { HandleScope scope; Font* font = internalPtr<Font>(info, CLASS_Font); if(font == 0) { return; } // text v8::Local<v8::String> tmp = info[0]->ToString(); int length = tmp->Length(); uint16_t uchars[length + 1]; tmp->Write(uchars); // Float32Array ClassBase* ptr = internalArg<ClassBase>(info[1], CLASS_Float32Array); if(ptr == 0) { ThrowException(String::New("Font.measure Float32Array not found")); return; } ByteBuffer buf; ptr->getUnderlying(&buf); // start and end int start = 0; int end = length; if(info.Length() >= 3) { start = info[2]->Uint32Value(); } if(info.Length() >= 4) { end = info[3]->Uint32Value(); } // setup float empty[8] = { 0,0,0,0,0,0,0,0 }; int starti = 0; for(int i = start; i < end; i++) { texture_glyph_t *glyph = texture_font_get_glyph(font->font, uchars[i] ); if(glyph != NULL) { float values[8] = { 0,0,0,0,glyph->s0,glyph->t0,glyph->s1,glyph->t1 }; values[0] = glyph->offset_x; values[1] = glyph->offset_y; values[2] = glyph->width; values[3] = glyph->height; buf.set_value<float>(starti, values, 8); } else { buf.set_value<float>(starti, empty, 8); } starti += 8; } }
/** * @text * @floatarray [advance,kerning...] * @start optional * @end optional */ METHOD_BEGIN(measure, info) { HandleScope scope; Font* font = internalPtr<Font>(info, CLASS_Font); if(font == 0) { return; } // text v8::Local<v8::String> tmp = info[0]->ToString(); int length = tmp->Length(); uint16_t uchars[length + 1]; tmp->Write(uchars); // Float32Array ClassBase* ptr = internalArg<ClassBase>(info[1], CLASS_Float32Array); if(ptr == 0) { ThrowException(String::New("Font.measure Float32Array not found")); return; } ByteBuffer buf; ptr->getUnderlying(&buf); // start and end int start = 0; int end = length; if(info.Length() >= 3) { start = info[2]->Uint32Value(); } if(info.Length() >= 4) { end = info[3]->Uint32Value(); } // setup for(int i = start; i < end; i++) { texture_glyph_t *glyph = texture_font_get_glyph(font->font, uchars[i] ); if(glyph != NULL) { if(i > 0) { float values[2] = { glyph->advance_x, texture_glyph_get_kerning(glyph, uchars[i - 1]) }; buf.set_value<float>((i - start) * 2, values, 2); } else { float values[2] = { glyph->advance_x, 0 }; buf.set_value<float>((i - start) * 2, values, 2); } } else { float values[2] = { 0, 0 }; buf.set_value<float>((i - start) * 2, values, 2); } } }
float OLabel::getCharWidth(const char& c) { float width = 0; const maths::vec2& scale = m_Font->GetScale(); ftgl::texture_font_t* ftFont = m_Font->GetFTFont(); ftgl::texture_glyph_t* glyph = texture_font_get_glyph(ftFont, c); if (glyph != nullptr) { float gwidth = glyph->advance_x / scale.x; width += gwidth; } return width; }
// ---------------------------------------------------------------- display --- void display( void ) { glClearColor(1.0,1.0,1.0,1.0); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, atlas->id); glEnable( GL_TEXTURE_2D ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); GLuint handle = glGetUniformLocation( program, "texture" ); glUniform1i( handle, 0); texture_glyph_t * glyph = texture_font_get_glyph( font, L'@'); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; int width = 512; int height = 512; if( glyph->width > glyph->height ) height = glyph->height * width/(float)glyph->width; else width = glyph->width * height/(float)glyph->height; int x = 0 - width/2; int y = 0 - height/2; glPushMatrix(); glTranslatef(256,256,0); glRotatef(angle, 0,0,1); float s = .025+.975*(1+cos(angle/100.0))/2.; glScalef(s,s,s); glBegin(GL_QUADS); glTexCoord2f( s0, t1 ); glVertex2f( x, y ); glTexCoord2f( s0, t0 ); glVertex2f( x, y+height ); glTexCoord2f( s1, t0 ); glVertex2f( x+width, y+height ); glTexCoord2f( s1, t1 ); glVertex2f( x+width, y ); glEnd(); glPopMatrix(); glutSwapBuffers( ); }
// ---------------------------------------------------------------- display --- void display( GLFWwindow* window ) { glClearColor(1.0,1.0,1.0,1.0); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, atlas->id); glEnable( GL_TEXTURE_2D ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); texture_glyph_t * glyph = texture_font_get_glyph( font, "@"); int width = 512; int height = 512; float glyph_height = glyph->height * width/(float)glyph->width; float glyph_width = glyph->width * height/(float)glyph->height; int x = -glyph_width/2 + 512/2.; int y = -glyph_height/2 + 512/2.; float s = .025+.975*(1+cos(angle/100.0))/2.; vec4 color = {{1.0, 1.0, 1.0, 1.0 }}; mat4_set_identity( &model ); mat4_scale( &model, width * s, width * s, 1 ); mat4_rotate( &model, angle, 0, 0, 1 ); mat4_translate( &model, 256, 256, 0 ); glUseProgram( program ); { glUniform1i( glGetUniformLocation( program, "u_texture" ), 0); glUniform4f( glGetUniformLocation( program, "u_color" ), color.r, color.g, color.b, color.a); glUniformMatrix4fv( glGetUniformLocation( program, "u_model" ), 1, 0, model.data); glUniformMatrix4fv( glGetUniformLocation( program, "u_view" ), 1, 0, view.data); glUniformMatrix4fv( glGetUniformLocation( program, "u_projection" ), 1, 0, projection.data); vertex_buffer_render( buffer, GL_TRIANGLES ); } glfwSwapBuffers( window ); }
void FontBufferCache::Add(const std::string &text, const Point3 &position, const GameColor &color, texture_font_t *font) { HashedString hash(text.c_str()); vertex_buffer_t *buffer = 0; if(Contains(text)) { buffer = m_vertexBufferMap.find(hash.getHashValue())->second; vertex_buffer_clear(buffer); } else { buffer = vertex_buffer_new("vertex:3f,tex_coord:2f,color:4f"); m_vertexBufferMap[hash.getHashValue()] = buffer; } F32 r = color.GetX(), g = color.GetY(), b = color.GetZ(), a = color.GetW(); F32 x = 0.0f; U32 size = (U32)text.size(); for(U32 i = 0; i < size; ++i) { texture_glyph_t *glyph = texture_font_get_glyph(font, text[i]); if(glyph != NULL) { I32 kerning = 0; if(i > 0) { kerning = texture_glyph_get_kerning(glyph, text[i - 1]); } x += (float)kerning; F32 x0 = (F32)(position.GetX() + x + glyph->offset_x); F32 y0 = (F32)(position.GetY() + glyph->offset_y); F32 x1 = (F32)(x0 + glyph->width); F32 y1 = (F32)(y0 - glyph->height); F32 s0 = glyph->s0; F32 t0 = glyph->t0; F32 s1 = glyph->s1; F32 t1 = glyph->t1; GLuint indices[6] = {0, 1, 2, 0, 2, 3}; vertex_t vertices[4] = { { x0, y0, 0.0f, s0, t0, r, g, b, a }, { x0, y1, 0.0f, s0, t1, r, g, b, a }, { x1, y1, 0.0f, s1, t1, r, g, b, a }, { x1, y0, 0.0f, s1, t0, r, g, b, a } }; vertex_buffer_push_back(buffer, vertices, 4, indices, 6); x += glyph->advance_x; } } }
// --------------------------------------------------------------- add_text --- vec4 add_text( vertex_buffer_t * buffer, texture_font_t * font, char *text, vec4 * color, vec2 * pen ) { vec4 bbox = {{0,0,0,0}}; size_t i; float r = color->red, g = color->green, b = color->blue, a = color->alpha; for( i = 0; i < strlen(text); ++i ) { texture_glyph_t *glyph = texture_font_get_glyph( font, text + i ); if( glyph != NULL ) { float kerning = 0.0f; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text + i - 1 ); } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); int x1 = (int)( x0 + glyph->width ); int y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint indices[6] = {0,1,2, 0,2,3}; vertex_t vertices[4] = { { x0,y0,0, s0,t0, r,g,b,a }, { x0,y1,0, s0,t1, r,g,b,a }, { x1,y1,0, s1,t1, r,g,b,a }, { x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); pen->x += glyph->advance_x; if (x0 < bbox.x) bbox.x = x0; if (y1 < bbox.y) bbox.y = y1; if ((x1 - bbox.x) > bbox.width) bbox.width = x1-bbox.x; if ((y0 - bbox.y) > bbox.height) bbox.height = y0-bbox.y; } } return bbox; }
// --------------------------------------------------------------- add_text --- vec4 add_text( vertex_buffer_t * buffer, texture_font_t * font, const char *text, vec4 * color, vec2 * pen ) { auto bbox = vec4{0,0,0,0}; size_t i; auto r = color->red, g = color->green, b = color->blue, a = color->alpha; for( i = 0; i < strlen(text); ++i ) { glfwSetTime(total_time); auto glyph = texture_font_get_glyph( font, text + i ); total_time += glfwGetTime(); if( glyph ) { float kerning = 0.0f; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text + i - 1 ); } pen->x += kerning; auto x0 = (float)( pen->x + glyph->offset_x ); auto y0 = (float)( pen->y + glyph->offset_y ); auto x1 = (float)( x0 + glyph->width ); auto y1 = (float)( y0 - glyph->height ); auto s0 = glyph->s0; auto t0 = glyph->t0; auto s1 = glyph->s1; auto t1 = glyph->t1; GLuint indices[6] = {0,1,2, 0,2,3}; vertex vertices[4] = { { x0,y0,0, s0,t0, r,g,b,a }, { x0,y1,0, s0,t1, r,g,b,a }, { x1,y1,0, s1,t1, r,g,b,a }, { x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); pen->x += glyph->advance_x; if (x0 < bbox.x) bbox.x = x0; if (y1 < bbox.y) bbox.y = y1; if ((x1 - bbox.x) > bbox.width) bbox.width = x1-bbox.x; if ((y0 - bbox.y) > bbox.height) bbox.height = y0-bbox.y; } } return bbox; }
float OLabel::getWordWidth(const std::string& word) { float width = 0; const maths::vec2& scale = m_Font->GetScale(); ftgl::texture_font_t* ftFont = m_Font->GetFTFont(); for (uint i = 0; i < word.length(); i++) { char c = word[i]; ftgl::texture_glyph_t* glyph = texture_font_get_glyph(ftFont, c); if (glyph != nullptr) { if (i > 0) { float kerning = texture_glyph_get_kerning(glyph, m_Text[i - 1]); width += kerning / scale.x; } float gwidth = glyph->advance_x / scale.x; width += gwidth; } } return width; }
// --------------------------------------------------------------- add_text --- void add_text( vertex_buffer_t * buffer, texture_font_t * font, char * text, vec4 * color, vec2 * pen ) { size_t i; float r = color->red, g = color->green, b = color->blue, a = color->alpha; for( i = 0; i < strlen(text); ++i ) { texture_glyph_t *glyph = texture_font_get_glyph( font, text + i ); if( glyph != NULL ) { float kerning = 0.0f; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text + i - 1 ); } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); int x1 = (int)( x0 + glyph->width ); int y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint index = buffer->vertices->size; GLuint indices[] = {index, index+1, index+2, index, index+2, index+3}; vertex_t vertices[] = { { x0,y0,0, s0,t0, r,g,b,a, 0,1 }, { x0,y1,0, s0,t1, r,g,b,a, 0,1 }, { x1,y1,0, s1,t1, r,g,b,a, 0,1 }, { x1,y0,0, s1,t0, r,g,b,a, 0,1 } }; vertex_buffer_push_back_indices( buffer, indices, 6 ); vertex_buffer_push_back_vertices( buffer, vertices, 4 ); pen->x += glyph->advance_x; } } }
// --------------------------------------------------------------- add_text --- void add_text( vertex_buffer_t * buffer, texture_font_t * font, char *text, vec2 pen, vec4 fg_color_1, vec4 fg_color_2 ) { size_t i; for( i = 0; i < strlen(text); ++i ) { texture_glyph_t *glyph = texture_font_get_glyph( font, text + i ); float kerning = 0.0f; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text + i - 1 ); } pen.x += kerning; /* Actual glyph */ float x0 = ( pen.x + glyph->offset_x ); float y0 = (int)( pen.y + glyph->offset_y ); float x1 = ( x0 + glyph->width ); float y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint index = buffer->vertices->size; GLuint indices[] = {index, index+1, index+2, index, index+2, index+3}; vertex_t vertices[] = { { (int)x0,y0,0, s0,t0, fg_color_1 }, { (int)x0,y1,0, s0,t1, fg_color_2 }, { (int)x1,y1,0, s1,t1, fg_color_2 }, { (int)x1,y0,0, s1,t0, fg_color_1 } }; vertex_buffer_push_back_indices( buffer, indices, 6 ); vertex_buffer_push_back_vertices( buffer, vertices, 4 ); pen.x += glyph->advance_x; } }
// ------------------------------------------------------- texture_font_new --- texture_font_t * texture_font_new( texture_atlas_t * atlas, const char * filename, const float size) { assert( filename ); assert( size ); texture_font_t *self = (texture_font_t *) malloc( sizeof(texture_font_t) ); if( self == NULL) { fprintf( stderr, "line %d: No more memory for allocating data\n", __LINE__ ); exit( EXIT_FAILURE ); } self->glyphs = vector_new( sizeof(texture_glyph_t *) ); self->atlas = atlas; self->height = 0; self->ascender = 0; self->descender = 0; self->filename = strdup( filename ); self->size = size; self->outline_type = 0; self->outline_thickness = 0.0; self->hinting = 1; self->filtering = 1; // FT_LCD_FILTER_LIGHT is (0x00, 0x55, 0x56, 0x55, 0x00) // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10) self->lcd_weights[0] = 0x10; self->lcd_weights[1] = 0x40; self->lcd_weights[2] = 0x70; self->lcd_weights[3] = 0x40; self->lcd_weights[4] = 0x10; /* Get font metrics at high resolution */ FT_Library library; FT_Face face; if( !texture_font_load_face( &library, self->filename, self->size*100, &face ) ) { return self; } // 64 * 64 because of 26.6 encoding AND the transform matrix used // in texture_font_load_face (hres = 64) self->underline_position = face->underline_position / (float)(64.0f*64.0f) * self->size; self->underline_position = round( self->underline_position ); if( self->underline_position > -2 ) { self->underline_position = -2.0; } self->underline_thickness = face->underline_thickness / (float)(64.0f*64.0f) * self->size; self->underline_thickness = round( self->underline_thickness ); if( self->underline_thickness < 1 ) { self->underline_thickness = 1.0; } FT_Size_Metrics metrics = face->size->metrics; self->ascender = (metrics.ascender >> 6) / 100.0; self->descender = (metrics.descender >> 6) / 100.0; self->height = (metrics.height >> 6) / 100.0; self->linegap = self->height - self->ascender + self->descender; FT_Done_Face( face ); FT_Done_FreeType( library ); /* -1 is a special glyph */ texture_font_get_glyph( self, -1 ); return self; }
// --------------------------------------------------------------- add_text --- void add_text( vertex_buffer_t * buffer, vec2 * pen, ... ) { markup_t *markup; char *text; va_list args; va_start ( args, pen ); do { markup = va_arg( args, markup_t * ); if( markup == NULL ) { break; } text = va_arg( args, char * ); size_t i; texture_font_t * font = markup->font; float r = markup->foreground_color.red; float g = markup->foreground_color.green; float b = markup->foreground_color.blue; float a = markup->foreground_color.alpha; for( i = 0; i < strlen(text); ++i ) { texture_glyph_t *glyph = texture_font_get_glyph( font, text + i ); if( glyph != NULL ) { float kerning = 0.0f; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text + i - 1 ); } pen->x += kerning; /* Actual glyph */ float x0 = ( pen->x + glyph->offset_x ); float y0 = (int)( pen->y + glyph->offset_y ); float x1 = ( x0 + glyph->width ); float y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint index = buffer->vertices->size; GLuint indices[] = {index, index+1, index+2, index, index+2, index+3 }; vertex_t vertices[] = { { (int)x0,y0,0, s0,t0, r,g,b,a }, { (int)x0,y1,0, s0,t1, r,g,b,a }, { (int)x1,y1,0, s1,t1, r,g,b,a }, { (int)x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back_indices( buffer, indices, 6 ); vertex_buffer_push_back_vertices( buffer, vertices, 4 ); pen->x += glyph->advance_x; } } } while( markup != 0 ); va_end ( args ); }
// ---------------------------------------------------------------------------- void text_buffer_add_wchar( text_buffer_t * self, vec2 * pen, markup_t * markup, wchar_t current, wchar_t previous ) { size_t vcount = 0; size_t icount = 0; vertex_buffer_t * buffer = self->buffer; texture_font_t * font = markup->font; float gamma = markup->gamma; // Maximum number of vertices is 20 (= 5x2 triangles) per glyph: // - 2 triangles for background // - 2 triangles for overline // - 2 triangles for underline // - 2 triangles for strikethrough // - 2 triangles for glyph glyph_vertex_t vertices[4*5]; GLuint indices[6*5]; texture_glyph_t *glyph; texture_glyph_t *black; float kerning = 0; if( current == L'\n' ) { pen->x = self->origin.x; pen->y += self->line_descender; self->line_descender = 0; self->line_ascender = 0; self->line_start = vector_size( self->buffer->items ); return; } if( markup->font->ascender > self->line_ascender ) { float y = pen->y; pen->y -= (markup->font->ascender - self->line_ascender); text_buffer_move_last_line( self, (int)(y-pen->y) ); self->line_ascender = markup->font->ascender; } if( markup->font->descender < self->line_descender ) { self->line_descender = markup->font->descender; } glyph = texture_font_get_glyph( font, current ); black = texture_font_get_glyph( font, -1 ); if( glyph == NULL ) { return; } if( previous && markup->font->kerning ) { kerning = texture_glyph_get_kerning( glyph, previous ); } pen->x += kerning; // Background if( markup->background_color.alpha > 0 ) { float r = markup->background_color.r; float g = markup->background_color.g; float b = markup->background_color.b; float a = markup->background_color.a; float x0 = ( pen->x -kerning ); float y0 = (int)( pen->y + font->descender ); float x1 = ( x0 + glyph->advance_x ); float y1 = (int)( y0 + font->height + font->linegap ); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount+0], (int)x0,y0,0, s0,t0, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+1], (int)x0,y1,0, s0,t1, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+2], (int)x1,y1,0, s1,t1, r,g,b,a, x1-((int)x1), gamma ); SET_GLYPH_VERTEX(vertices[vcount+3], (int)x1,y0,0, s1,t0, r,g,b,a, x1-((int)x1), gamma ); indices[icount + 0] = vcount+0; indices[icount + 1] = vcount+1; indices[icount + 2] = vcount+2; indices[icount + 3] = vcount+0; indices[icount + 4] = vcount+2; indices[icount + 5] = vcount+3; vcount += 4; icount += 6; } // Underline if( markup->underline ) { float r = markup->underline_color.r; float g = markup->underline_color.g; float b = markup->underline_color.b; float a = markup->underline_color.a; float x0 = ( pen->x - kerning ); float y0 = (int)( pen->y + font->underline_position ); float x1 = ( x0 + glyph->advance_x ); float y1 = (int)( y0 + font->underline_thickness ); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount+0], (int)x0,y0,0, s0,t0, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+1], (int)x0,y1,0, s0,t1, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+2], (int)x1,y1,0, s1,t1, r,g,b,a, x1-((int)x1), gamma ); SET_GLYPH_VERTEX(vertices[vcount+3], (int)x1,y0,0, s1,t0, r,g,b,a, x1-((int)x1), gamma ); indices[icount + 0] = vcount+0; indices[icount + 1] = vcount+1; indices[icount + 2] = vcount+2; indices[icount + 3] = vcount+0; indices[icount + 4] = vcount+2; indices[icount + 5] = vcount+3; vcount += 4; icount += 6; } // Overline if( markup->overline ) { float r = markup->overline_color.r; float g = markup->overline_color.g; float b = markup->overline_color.b; float a = markup->overline_color.a; float x0 = ( pen->x -kerning ); float y0 = (int)( pen->y + (int)font->ascender ); float x1 = ( x0 + glyph->advance_x ); float y1 = (int)( y0 + (int)font->underline_thickness ); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount+0], (int)x0,y0,0, s0,t0, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+1], (int)x0,y1,0, s0,t1, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+2], (int)x1,y1,0, s1,t1, r,g,b,a, x1-((int)x1), gamma ); SET_GLYPH_VERTEX(vertices[vcount+3], (int)x1,y0,0, s1,t0, r,g,b,a, x1-((int)x1), gamma ); indices[icount + 0] = vcount+0; indices[icount + 1] = vcount+1; indices[icount + 2] = vcount+2; indices[icount + 3] = vcount+0; indices[icount + 4] = vcount+2; indices[icount + 5] = vcount+3; vcount += 4; icount += 6; } /* Strikethrough */ if( markup->strikethrough ) { float r = markup->strikethrough_color.r; float g = markup->strikethrough_color.g; float b = markup->strikethrough_color.b; float a = markup->strikethrough_color.a; float x0 = ( pen->x -kerning ); float y0 = (int)( pen->y + (int)font->ascender*.33); float x1 = ( x0 + glyph->advance_x ); float y1 = (int)( y0 + (int)font->underline_thickness ); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount+0], (int)x0,y0,0, s0,t0, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+1], (int)x0,y1,0, s0,t1, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+2], (int)x1,y1,0, s1,t1, r,g,b,a, x1-((int)x1), gamma ); SET_GLYPH_VERTEX(vertices[vcount+3], (int)x1,y0,0, s1,t0, r,g,b,a, x1-((int)x1), gamma ); indices[icount + 0] = vcount+0; indices[icount + 1] = vcount+1; indices[icount + 2] = vcount+2; indices[icount + 3] = vcount+0; indices[icount + 4] = vcount+2; indices[icount + 5] = vcount+3; vcount += 4; icount += 6; } { // Actual glyph float r = markup->foreground_color.red; float g = markup->foreground_color.green; float b = markup->foreground_color.blue; float a = markup->foreground_color.alpha; float x0 = ( pen->x + glyph->offset_x ); float y0 = (int)( pen->y + glyph->offset_y ); float x1 = ( x0 + glyph->width ); float y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; SET_GLYPH_VERTEX(vertices[vcount+0], (int)x0,y0,0, s0,t0, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+1], (int)x0,y1,0, s0,t1, r,g,b,a, x0-((int)x0), gamma ); SET_GLYPH_VERTEX(vertices[vcount+2], (int)x1,y1,0, s1,t1, r,g,b,a, x1-((int)x1), gamma ); SET_GLYPH_VERTEX(vertices[vcount+3], (int)x1,y0,0, s1,t0, r,g,b,a, x1-((int)x1), gamma ); indices[icount + 0] = vcount+0; indices[icount + 1] = vcount+1; indices[icount + 2] = vcount+2; indices[icount + 3] = vcount+0; indices[icount + 4] = vcount+2; indices[icount + 5] = vcount+3; vcount += 4; icount += 6; vertex_buffer_push_back( buffer, vertices, vcount, indices, icount ); pen->x += glyph->advance_x * (1.0 + markup->spacing); } }
// ------------------------------------------------------------------- main --- int main( int argc, char **argv ) { size_t i, j; GLFWwindow* window; glfwSetErrorCallback( error_callback ); if (!glfwInit( )) { exit( EXIT_FAILURE ); } glfwWindowHint( GLFW_VISIBLE, GL_TRUE ); glfwWindowHint( GLFW_RESIZABLE, GL_FALSE ); window = glfwCreateWindow( 1, 1, argv[0], NULL, NULL ); if (!window) { glfwTerminate( ); exit( EXIT_FAILURE ); } glfwMakeContextCurrent( window ); glfwSwapInterval( 1 ); glfwSetFramebufferSizeCallback( window, reshape ); glfwSetWindowRefreshCallback( window, display ); glfwSetKeyCallback( window, keyboard ); #ifndef __APPLE__ glewExperimental = GL_TRUE; GLenum err = glewInit(); if (GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ fprintf( stderr, "Error: %s\n", glewGetErrorString(err) ); exit( EXIT_FAILURE ); } fprintf( stderr, "Using GLEW %s\n", glewGetString(GLEW_VERSION) ); #endif texture_atlas_t * atlas = texture_atlas_new( 512, 512, 3 ); texture_font_t *fonts[20]; for ( i=0; i< 20; ++i ) { fonts[i] = texture_font_new_from_file(atlas, 12+i, font_filename), texture_font_load_glyphs(fonts[i], text, direction, language, script ); } typedef struct { float x,y,z, u,v, r,g,b,a, shift, gamma; } vertex_t; vbuffer = vertex_buffer_new( "vertex:3f,tex_coord:2f," "color:4f,ashift:1f,agamma:1f" ); /* Create a buffer for harfbuzz to use */ hb_buffer_t *buffer = hb_buffer_create(); for (i=0; i < 20; ++i) { hb_buffer_set_direction( buffer, direction ); hb_buffer_set_script( buffer, script ); hb_buffer_set_language( buffer, hb_language_from_string(language, strlen(language)) ); hb_buffer_add_utf8( buffer, text, strlen(text), 0, strlen(text) ); hb_shape( fonts[i]->hb_ft_font, buffer, NULL, 0 ); unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); texture_font_load_glyphs( fonts[i], text, direction, language, script ); float gamma = 1.0; float shift = 0.0; float x = 0; float y = 600 - i * (10+i) - 15; float width = 0.0; float hres = fonts[i]->hres; for (j = 0; j < glyph_count; ++j) { int codepoint = glyph_info[j].codepoint; float x_advance = glyph_pos[j].x_advance/(float)(hres*64); float x_offset = glyph_pos[j].x_offset/(float)(hres*64); texture_glyph_t *glyph = texture_font_get_glyph(fonts[i], codepoint); if( i < (glyph_count-1) ) width += x_advance + x_offset; else width += glyph->offset_x + glyph->width; } x = 800 - width - 10 ; for (j = 0; j < glyph_count; ++j) { int codepoint = glyph_info[j].codepoint; // because of vhinting trick we need the extra 64 (hres) float x_advance = glyph_pos[j].x_advance/(float)(hres*64); float x_offset = glyph_pos[j].x_offset/(float)(hres*64); float y_advance = glyph_pos[j].y_advance/(float)(64); float y_offset = glyph_pos[j].y_offset/(float)(64); texture_glyph_t *glyph = texture_font_get_glyph(fonts[i], codepoint); float r = 0.0; float g = 0.0; float b = 0.0; float a = 1.0; float x0 = x + x_offset + glyph->offset_x; float x1 = x0 + glyph->width; float y0 = floor(y + y_offset + glyph->offset_y); float y1 = floor(y0 - glyph->height); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; vertex_t vertices[4] = { {x0,y0,0, s0,t0, r,g,b,a, shift, gamma}, {x0,y1,0, s0,t1, r,g,b,a, shift, gamma}, {x1,y1,0, s1,t1, r,g,b,a, shift, gamma}, {x1,y0,0, s1,t0, r,g,b,a, shift, gamma} }; GLushort indices[6] = { 0,1,2, 0,2,3 }; vertex_buffer_push_back( vbuffer, vertices, 4, indices, 6 ); x += x_advance; y += y_advance; } /* clean up the buffer, but don't kill it just yet */ hb_buffer_reset(buffer); } glClearColor(1,1,1,1); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glBindTexture( GL_TEXTURE_2D, atlas->id ); texture_atlas_upload( atlas ); vertex_buffer_upload( vbuffer ); shader = shader_load("shaders/text.vert", "shaders/text.frag"); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); glfwSetWindowSize( window, 800, 600 ); glfwShowWindow( window ); while(!glfwWindowShouldClose( window )) { display( window ); glfwPollEvents( ); } glfwDestroyWindow( window ); glfwTerminate( ); return 0; }
// ----------------------------------------------------------- build_buffer --- void build_buffer( void ) { vec2 pen; size_t i; texture_font_t *font; texture_glyph_t *glyph; vec4 white = {{1.0, 1.0, 1.0, 1.0}}; vec4 none = {{1.0, 1.0, 1.0, 0.0}}; markup_t markup = { .family = "Arial", .size = 10.0, .bold = 0, .italic = 0, .rise = 0.0, .spacing = 0.0, .gamma = 2.2, .foreground_color = white, .background_color = none, .underline = 0, .underline_color = white, .overline = 0, .overline_color = white, .strikethrough = 0, .strikethrough_color = white, .font = 0, }; vertex_buffer_clear( buffer ); texture_atlas_clear( atlas ); if( p_family == VERA) { font = texture_font_new( atlas, "fonts/Vera.ttf", p_size ); } else if( p_family == VERA_MONO) { font = texture_font_new( atlas, "fonts/VeraMono.ttf", p_size ); } else if( p_family == GEORGIA) { font = texture_font_new( atlas, "fonts/Georgia.ttf", p_size ); } else if( p_family == TIMES ) { font = texture_font_new( atlas, "fonts/Times.ttf", p_size ); } else if( p_family == TAHOMA ) { font = texture_font_new( atlas, "fonts/Tahoma.ttf", p_size ); } else if( p_family == ARIAL ) { font = texture_font_new( atlas, "fonts/Arial.ttf", p_size ); } else if( p_family == VERDANA ) { font = texture_font_new( atlas, "fonts/Verdana.ttf", p_size ); } else { fprintf( stderr, "Error : Unknown family type\n" ); return; } font->hinting = p_hinting; font->filtering = 1; float norm = 1.0/(p_primary + 2*p_secondary + 2*p_tertiary); font->lcd_weights[0] = (unsigned char)(p_tertiary*norm*256); font->lcd_weights[1] = (unsigned char)(p_secondary*norm*256); font->lcd_weights[2] = (unsigned char)(p_primary*norm*256); font->lcd_weights[3] = (unsigned char)(p_secondary*norm*256); font->lcd_weights[4] = (unsigned char)(p_tertiary*norm*256); texture_font_load_glyphs( font, L" !\"#$%&'()*+,-./0123456789:;<=>?" L"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" L"`abcdefghijklmnopqrstuvwxyz{|}~" ); pen.x = 10; pen.y = 600 - font->height - 10; glyph = texture_font_get_glyph( font, text[0] ); add_glyph( glyph, buffer, &markup, &pen, 0 ); for( i=1; i<wcslen(text); ++i ) { if( text[i] == '\n' ) { pen.x = 10; pen.y -= font->height; // + 0.01*(size - (int)size)*font->height; } else { glyph = texture_font_get_glyph( font, text[i] ); float kerning = 0.0; if( p_kerning ) { kerning = texture_glyph_get_kerning( glyph, text[i-1] ); } add_glyph( glyph, buffer, &markup, &pen, kerning ); } } texture_font_delete (font ); } // ---------------------------------------------------------------- display --- void display(void) { if( p_invert ) { glClearColor( 0, 0, 0, 1 ); } else { glClearColor( 1, 1, 1, 1 ); } glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, atlas->id ); if( !p_lcd_filtering ) { glEnable( GL_COLOR_MATERIAL ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); if( p_invert ) { glColor4f(1,1,1,1); } else { glColor4f(0,0,0,1); } } else { glEnable( GL_COLOR_MATERIAL ); glBlendFunc( GL_CONSTANT_COLOR_EXT, GL_ONE_MINUS_SRC_COLOR ); glEnable( GL_BLEND ); glColor3f( 1,1,1 ); if( p_invert ) { glBlendColor( 1, 1, 1, 1 ); } else { glBlendColor( 0, 0, 0, 1 ); } } if( !p_lcd_filtering ) { vertex_buffer_render( buffer, GL_TRIANGLES, "vt" ); } else { glUseProgram( program ); glUniform1i( texture_location, 0 ); glUniform1f( gamma_location, p_gamma ); float norm = 1.0/(p_primary+2*p_secondary+2*p_tertiary); glUniform1f( primary_location, p_primary*norm ); glUniform1f( secondary_location, p_secondary*norm ); glUniform1f( tertiary_location, p_tertiary*norm ); glUniform2f( pixel_location, 1.0/atlas->width, 1.0/atlas->height ); vertex_buffer_render( buffer, GL_TRIANGLES, "vtc" ); glUseProgram( 0 ); } TwDraw( ); glutSwapBuffers( ); } // ---------------------------------------------------------------- reshape --- void reshape( int width, int height ) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, width, 0, height, -1, 1); glMatrixMode(GL_MODELVIEW); TwWindowSize( width, height ); }
void modelEngine::add_text(texture_font_t * font, wchar_t * text, vec4 * color, vec2 * pen, GLuint vertexBuffer, GLfloat Zoffset) { size_t i; float r = color->red, g = color->green, b = color->blue, a = color->alpha; for( i=0; i<wcslen(text); ++i ) { texture_glyph_t *glyph = texture_font_get_glyph( font, text[i] ); if( glyph != NULL ) { int kerning = 0; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text[i-1] ); } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); int x1 = (int)( x0 + glyph->width ); int y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLfloat indices[6] = {0,1,2, 0,2,3}; GLfloat vertexCoordinates[] = { x0,y0,Zoffset, x0,y1,Zoffset, x1,y1,Zoffset, x0,y0,Zoffset, x1,y1,Zoffset, x1,y0,Zoffset, }; for(int i = 0; i < 18; i++) { vertexCoordinates[i] = vertexCoordinates[i] / 1000; } GLfloat textureCoordinates[] = { s0,t0, s0,t1, s1,t1, s0,t0, s1,t1, s1,t0, }; GLfloat colors[] = { r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a, r, g, b, a, }; GLfloat vertices[] = { x0,y0,0, s0,t0, r, g, b, a, x0,y1,0, s0,t1, r, g, b, a, x1,y1,0, s1,t1, r, g, b, a, x0,y0,0, s0,t0, r, g, b, a, x1,y1,0, s1,t1, r, g, b, a, x1,y0,0, s1,t0, r, g, b, a }; //vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); pen->x += glyph->advance_x; // Store vertices to buffer // Generate buffer GLuint verticeBuffer = 0; GLuint textureBuffer = 0; GLuint colorBuffer = 0; // Generate vertex buffer glGenBuffers(1, &verticeBuffer); // Bind buffer glBindBuffer(GL_ARRAY_BUFFER, verticeBuffer); // Create buffer data glBufferData(GL_ARRAY_BUFFER, 216, &vertexCoordinates, GL_STATIC_DRAW); // Unbind buffer glBindBuffer(GL_ARRAY_BUFFER, 0); // Generate texture buffer glGenBuffers(1, &textureBuffer); // Bind buffer glBindBuffer(GL_ARRAY_BUFFER, textureBuffer); // Create buffer data glBufferData(GL_ARRAY_BUFFER, 48, &textureCoordinates, GL_STATIC_DRAW); // Unbind buffer glBindBuffer(GL_ARRAY_BUFFER, 0); // Generate color buffer glGenBuffers(1, &colorBuffer); // Bind buffer glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); // Create buffer data glBufferData(GL_ARRAY_BUFFER, 48, &colors, GL_STATIC_DRAW); // Unbind buffer glBindBuffer(GL_ARRAY_BUFFER, 0); // Enabling color array makes font dissapear //glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState( GL_VERTEX_ARRAY ); //glDisable(GL_TEXTURE_2D); //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); // Start with a clear screen glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Draw on screen // Bind texture glBindTexture(GL_TEXTURE_2D, atlas->id); // Bind buffers // Bind vertex buffer glBindBuffer(GL_ARRAY_BUFFER, verticeBuffer); glVertexPointer(3, GL_FLOAT, 0, NULL); // Bind texture buffer glBindBuffer(GL_ARRAY_BUFFER, textureBuffer); glTexCoordPointer(2, GL_FLOAT, 0, NULL); // Bind color buffer glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); glColorPointer(4, GL_FLOAT, 0, NULL); glPushMatrix(); // Move model glTranslatef(0.0f,0.0f,20.0f); // Rotate model glRotatef(0.0f, 1.0, 0.0, 0.0); glRotatef(0.0f, 0.0, 1.0, 0.0); glRotatef(0.0f, 0.0, 0.0, 1.0); // Scale model glScalef(15.0f,15.0f,15.0f); glColor4f(0.0f, 0.6f, 1.0f, 0.5f); glDrawArrays(GL_TRIANGLES, 0, 6); // Display eglSwapBuffers(state.display, state.surface); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); //glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glPopMatrix(); } } }
// -------------------------------------------------------- console_render --- void console_render( console_t *self ) { int viewport[4]; glGetIntegerv( GL_VIEWPORT, viewport ); size_t i, index; self->pen.x = 0; self->pen.y = viewport[3]; vertex_buffer_clear( console->buffer ); int cursor_x = self->pen.x; int cursor_y = self->pen.y; markup_t markup; // console_t buffer markup = self->markup[MARKUP_FAINT]; self->pen.y -= markup.font->height; for( i=0; i<self->lines->size; ++i ) { wchar_t *text = * (wchar_t **) vector_get( self->lines, i ) ; if( wcslen(text) > 0 ) { console_add_glyph( console, text[0], L'\0', &markup ); for( index=1; index < wcslen(text)-1; ++index ) { console_add_glyph( console, text[index], text[index-1], &markup ); } } self->pen.y -= markup.font->height - markup.font->linegap; self->pen.x = 0; cursor_x = self->pen.x; cursor_y = self->pen.y; } // Prompt markup = self->markup[MARKUP_BOLD]; if( wcslen( self->prompt ) > 0 ) { console_add_glyph( console, self->prompt[0], L'\0', &markup ); for( index=1; index < wcslen(self->prompt); ++index ) { console_add_glyph( console, self->prompt[index], self->prompt[index-1], &markup ); } } cursor_x = (int) self->pen.x; // Input markup = self->markup[MARKUP_NORMAL]; if( wcslen(self->input) > 0 ) { console_add_glyph( console, self->input[0], L'\0', &markup ); if( self->cursor > 0) { cursor_x = (int) self->pen.x; } for( index=1; index < wcslen(self->input); ++index ) { console_add_glyph( console, self->input[index], self->input[index-1], &markup ); if( index < self->cursor ) { cursor_x = (int) self->pen.x; } } } // Cursor (we use the black character (-1) as texture ) texture_glyph_t *glyph = texture_font_get_glyph( markup.font, -1 ); float r = markup.foreground_color.r; float g = markup.foreground_color.g; float b = markup.foreground_color.b; float a = markup.foreground_color.a; int x0 = cursor_x+1; int y0 = cursor_y + markup.font->descender; int x1 = cursor_x+2; int y1 = y0 + markup.font->height - markup.font->linegap; float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLuint indices[] = {0,1,2, 0,2,3}; vertex_t vertices[] = { { x0,y0,0, s0,t0, r,g,b,a }, { x0,y1,0, s0,t1, r,g,b,a }, { x1,y1,0, s1,t1, r,g,b,a }, { x1,y0,0, s1,t0, r,g,b,a } }; vertex_buffer_push_back( self->buffer, vertices, 4, indices, 6 ); glEnable( GL_TEXTURE_2D ); glUseProgram( shader ); { glUniform1i( glGetUniformLocation( shader, "texture" ), 0 ); glUniformMatrix4fv( glGetUniformLocation( shader, "model" ), 1, 0, model.data); glUniformMatrix4fv( glGetUniformLocation( shader, "view" ), 1, 0, view.data); glUniformMatrix4fv( glGetUniformLocation( shader, "projection" ), 1, 0, projection.data); vertex_buffer_render( console->buffer, GL_TRIANGLES ); } }
// Text functions // Create text string with specified font // Begin at [0,0] // Store data to modelText[modelIndex] int modelEngine::createText(int modelIndex, texture_font_t * font, wchar_t * text, vec4 * color, vec2 * pen, vec3 * offset, vec2 * scale) { size_t i; vec2 startPen = *pen; //Zoffset = Zoffset * 400; // Make room to store vertices and texture coordinates for 50 characters GLfloat vertices[3600]; int verticesIndex = 0; GLfloat texCoordinates[2400]; int texIndex = 0; // Return -1 if model index is invalid if(modelIndex == -1) { return -1; } float r = color->red, g = color->green, b = color->blue, a = color->alpha; for( i=0; i<wcslen(text); ++i ) { // Check for special character wchar_t currentChar = text[i]; wchar_t* currentCharPointer = text + i; //wchar_t newLineChar = L"\n"; //if((text + i) == L"\n") int result = wcsncmp(currentCharPointer, L"\n", 1); if(result == 0) { // New line found pen->x = startPen.x; pen->y = startPen.y - font->height; startPen.y = pen->y; } texture_glyph_t *glyph = texture_font_get_glyph( font, text[i] ); if( glyph != NULL ) { int kerning = 0; if( i > 0) { kerning = texture_glyph_get_kerning( glyph, text[i-1] ); } pen->x += kerning; int x0 = (int)( pen->x + glyph->offset_x ); int y0 = (int)( pen->y + glyph->offset_y ); int x1 = (int)( x0 + glyph->width ); int y1 = (int)( y0 - glyph->height ); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; GLfloat vertexCoordinates[] = { x0,y0,0, x0,y1,0, x1,y1,0, x0,y0,0, x1,y1,0, x1,y0,0, }; for(int i = 0; i < 18; i++) { vertexCoordinates[i] = vertexCoordinates[i] / 400; } GLfloat textureCoordinates[] = { s0,t0, s0,t1, s1,t1, s0,t0, s1,t1, s1,t0, }; // Copy vertex data memcpy (vertices + verticesIndex, vertexCoordinates, 18 * sizeof(GLfloat)); // Increase pointer verticesIndex += 18; // Copy texture data memcpy (texCoordinates + texIndex, textureCoordinates, 12 * sizeof(GLfloat)); // Increase pointer texIndex += 12; // Increase pen position pen->x += glyph->advance_x; } } // Scale all data in X and Y direction for(int i = 0; i < verticesIndex; i+=3) { vertices[i] = vertices[i] * scale->x; vertices[i+1] = vertices[i+1] * scale->y; } // Store data to buffers // First, orphan GL buffers // Check if text has been enabled if(modelText[modelIndex].textReady != 0) { glDeleteBuffers(1, &modelText[modelIndex].vertexBuffer); glDeleteBuffers(1, &modelText[modelIndex].texBuffer); //orphanArrayBuffer(modelText[modelIndex].vertexBuffer, modelText[modelIndex].vertexBufferSize); //orphanArrayBuffer(modelText[modelIndex].texBuffer, modelText[modelIndex].texBufferSize); } // Generate vertex buffer glGenBuffers(1, &modelText[modelIndex].vertexBuffer); // Bind buffer glBindBuffer(GL_ARRAY_BUFFER, modelText[modelIndex].vertexBuffer); // Create buffer data glBufferData(GL_ARRAY_BUFFER, verticesIndex * sizeof(GLfloat), &vertices, GL_STATIC_DRAW); checkGLError(); // Unbind buffer glBindBuffer(GL_ARRAY_BUFFER, 0); // Store number of vertices modelText[modelIndex].vertexBufferSize = verticesIndex; // Generate texture buffer glGenBuffers(1, &modelText[modelIndex].texBuffer); // Bind buffer glBindBuffer(GL_ARRAY_BUFFER, modelText[modelIndex].texBuffer); // Create buffer data glBufferData(GL_ARRAY_BUFFER, texIndex * sizeof(GLfloat), &texCoordinates, GL_STATIC_DRAW); checkGLError(); // Unbind buffer glBindBuffer(GL_ARRAY_BUFFER, 0); // Store number of vertices modelText[modelIndex].texBufferSize = texIndex; // Store number of characters modelText[modelIndex].characters = wcslen(text); // Store font color modelText[modelIndex].fontColor.r = color->r; modelText[modelIndex].fontColor.g = color->g; modelText[modelIndex].fontColor.b = color->b; modelText[modelIndex].fontColor.a = color->a; // Store font offset on model modelText[modelIndex].offset = *offset; modelText[modelIndex].textReady = 1; return 0; }
// ------------------------------------------------------------------- main --- int main( int argc, char **argv ) { int width = 600; int height = 600; GLFWwindow* window; glfwSetErrorCallback( error_callback ); if (!glfwInit( )) { exit( EXIT_FAILURE ); } glfwWindowHint( GLFW_VISIBLE, GL_TRUE ); glfwWindowHint( GLFW_RESIZABLE, GL_FALSE ); window = glfwCreateWindow( 1, 1, "Freetype OpenGL", NULL, NULL ); if (!window) { glfwTerminate( ); exit( EXIT_FAILURE ); } glfwMakeContextCurrent( window ); glfwSwapInterval( 1 ); glfwSetFramebufferSizeCallback( window, reshape ); glfwSetWindowRefreshCallback( window, display ); glfwSetKeyCallback( window, keyboard ); #ifndef __APPLE__ glewExperimental = GL_TRUE; GLenum err = glewInit(); if (GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ fprintf( stderr, "Error: %s\n", glewGetErrorString(err) ); exit( EXIT_FAILURE ); } fprintf( stderr, "Using GLEW %s\n", glewGetString(GLEW_VERSION) ); #endif vec4 blue = {{0,0,1,1}}; vec4 black = {{0,0,0,1}}; texture_atlas_t * atlas = texture_atlas_new( 512, 512, 1); texture_font_t * big = texture_font_new_from_file( atlas, 400, "fonts/Vera.ttf"); texture_font_t * small = texture_font_new_from_file( atlas, 18, "fonts/Vera.ttf"); texture_font_t * title = texture_font_new_from_file( atlas, 32, "fonts/Vera.ttf"); text_buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" ); line_buffer = vertex_buffer_new( "vertex:3f,color:4f" ); point_buffer = vertex_buffer_new( "vertex:3f,color:4f" ); vec2 pen, origin; texture_glyph_t *glyph = texture_font_get_glyph( big, L'g' ); origin.x = width/2 - glyph->offset_x - glyph->width/2; origin.y = height/2 - glyph->offset_y + glyph->height/2; add_text( text_buffer, big, L"g", &black, &origin ); // title pen.x = 50; pen.y = 560; add_text( text_buffer, title, L"Glyph metrics", &black, &pen ); point_t vertices[] = { // Baseline {0.1*width, origin.y, 0, black}, {0.9*width, origin.y, 0, black}, // Top line {0.1*width, origin.y + glyph->offset_y, 0, black}, {0.9*width, origin.y + glyph->offset_y, 0, black}, // Bottom line {0.1*width, origin.y + glyph->offset_y - glyph->height, 0, black}, {0.9*width, origin.y + glyph->offset_y - glyph->height, 0, black}, // Left line at origin {width/2-glyph->offset_x-glyph->width/2, 0.1*height, 0, black}, {width/2-glyph->offset_x-glyph->width/2, 0.9*height, 0, black}, // Left line {width/2 - glyph->width/2, .3*height, 0, black}, {width/2 - glyph->width/2, .9*height, 0, black}, // Right line {width/2 + glyph->width/2, .3*height, 0, black}, {width/2 + glyph->width/2, .9*height, 0, black}, // Right line at origin {width/2-glyph->offset_x-glyph->width/2+glyph->advance_x, 0.1*height, 0, black}, {width/2-glyph->offset_x-glyph->width/2+glyph->advance_x, 0.7*height, 0, black}, // Width {width/2 - glyph->width/2, 0.8*height, 0, blue}, {width/2 + glyph->width/2, 0.8*height, 0, blue}, // Advance_x {width/2-glyph->width/2-glyph->offset_x, 0.2*height, 0, blue}, {width/2-glyph->width/2-glyph->offset_x+glyph->advance_x, 0.2*height, 0, blue}, // Offset_x {width/2-glyph->width/2-glyph->offset_x, 0.85*height, 0, blue}, {width/2-glyph->width/2, 0.85*height, 0, blue}, // Height {0.3*width/2, origin.y + glyph->offset_y - glyph->height, 0, blue}, {0.3*width/2, origin.y + glyph->offset_y, 0, blue}, // Offset y {0.8*width, origin.y + glyph->offset_y, 0, blue}, {0.8*width, origin.y , 0, blue}, }; GLuint indices [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12, 13,14,15,16,17,18,19,20,21,22,23,24,25}; vertex_buffer_push_back( line_buffer, vertices, 26, indices, 26 ); pen.x = width/2 - 48; pen.y = .2*height - 18; add_text( text_buffer, small, L"advance_x", &blue, &pen ); pen.x = width/2 - 20; pen.y = .8*height + 3; add_text( text_buffer, small, L"width", &blue, &pen ); pen.x = width/2 - glyph->width/2 + 5; pen.y = .85*height-8; add_text( text_buffer, small, L"offset_x", &blue, &pen ); pen.x = 0.2*width/2-30; pen.y = origin.y + glyph->offset_y - glyph->height/2; add_text( text_buffer, small, L"height", &blue, &pen ); pen.x = 0.8*width+3; pen.y = origin.y + glyph->offset_y/2 -6; add_text( text_buffer, small, L"offset_y", &blue, &pen ); pen.x = width/2 - glyph->offset_x - glyph->width/2 - 58; pen.y = height/2 - glyph->offset_y + glyph->height/2 - 20; add_text( text_buffer, small, L"Origin", &black, &pen ); GLuint i = 0; point_t p; p.color = black; // Origin point p.x = width/2 - glyph->offset_x - glyph->width/2; p.y = height/2 - glyph->offset_y + glyph->height/2; vertex_buffer_push_back( point_buffer, &p, 1, &i, 1 ); // Advance point p.x = width/2 - glyph->offset_x - glyph->width/2 + glyph->advance_x; p.y = height/2 - glyph->offset_y + glyph->height/2; vertex_buffer_push_back( point_buffer, &p, 1, &i, 1 ); text_shader = shader_load( "shaders/v3f-t2f-c4f.vert", "shaders/v3f-t2f-c4f.frag" ); shader = shader_load( "shaders/v3f-c4f.vert", "shaders/v3f-c4f.frag" ); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); glfwSetWindowSize( window, width, height ); glfwShowWindow( window ); while(!glfwWindowShouldClose( window )) { display( window ); glfwPollEvents( ); } glfwDestroyWindow( window ); glfwTerminate( ); return 0; }
// ------------------------------------------------------------------- main --- int main( int argc, char **argv ) { int width = 600; int height = 600; glutInit( &argc, argv ); glutInitWindowSize( width, height ); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); glutCreateWindow( "Freetype OpenGL" ); glutReshapeFunc( reshape ); glutDisplayFunc( display ); glutKeyboardFunc( keyboard ); // GLenum err = glewInit(); // if (GLEW_OK != err) // { // /* Problem: glewInit failed, something is seriously wrong. */ // fprintf( stderr, "Error: %s\n", glewGetErrorString(err) ); // exit( EXIT_FAILURE ); // } // fprintf( stderr, "Using GLEW %s\n", glewGetString(GLEW_VERSION) ); vec4 blue = {{0,0,1,1}}; vec4 black = {{0,0,0,1}}; texture_atlas_t * atlas = texture_atlas_new( 512, 512, 1); texture_font_t * big = texture_font_new( atlas, "fonts/Vera.ttf", 400); texture_font_t * small = texture_font_new( atlas, "fonts/Vera.ttf", 18); texture_font_t * title = texture_font_new( atlas, "fonts/Vera.ttf", 32); text_buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" ); line_buffer = vertex_buffer_new( "vertex:3f,color:4f" ); point_buffer = vertex_buffer_new( "vertex:3f,color:4f" ); vec2 pen, origin; texture_glyph_t *glyph = texture_font_get_glyph( big, L'g' ); origin.x = width/2 - glyph->offset_x - glyph->width/2; origin.y = height/2 - glyph->offset_y + glyph->height/2; add_text( text_buffer, big, L"g", &black, &origin ); // title pen.x = 50; pen.y = 560; add_text( text_buffer, title, L"Glyph metrics", &black, &pen ); point_t vertices[] = { // Baseline {0.1*width, origin.y, 0, black}, {0.9*width, origin.y, 0, black}, // Top line {0.1*width, origin.y + glyph->offset_y, 0, black}, {0.9*width, origin.y + glyph->offset_y, 0, black}, // Bottom line {0.1*width, origin.y + glyph->offset_y - glyph->height, 0, black}, {0.9*width, origin.y + glyph->offset_y - glyph->height, 0, black}, // Left line at origin {width/2-glyph->offset_x-glyph->width/2, 0.1*height, 0, black}, {width/2-glyph->offset_x-glyph->width/2, 0.9*height, 0, black}, // Left line {width/2 - glyph->width/2, .3*height, 0, black}, {width/2 - glyph->width/2, .9*height, 0, black}, // Right line {width/2 + glyph->width/2, .3*height, 0, black}, {width/2 + glyph->width/2, .9*height, 0, black}, // Right line at origin {width/2-glyph->offset_x-glyph->width/2+glyph->advance_x, 0.1*height, 0, black}, {width/2-glyph->offset_x-glyph->width/2+glyph->advance_x, 0.7*height, 0, black}, // Width {width/2 - glyph->width/2, 0.8*height, 0, blue}, {width/2 + glyph->width/2, 0.8*height, 0, blue}, // Advance_x {width/2-glyph->width/2-glyph->offset_x, 0.2*height, 0, blue}, {width/2-glyph->width/2-glyph->offset_x+glyph->advance_x, 0.2*height, 0, blue}, // Offset_x {width/2-glyph->width/2-glyph->offset_x, 0.85*height, 0, blue}, {width/2-glyph->width/2, 0.85*height, 0, blue}, // Height {0.3*width/2, origin.y + glyph->offset_y - glyph->height, 0, blue}, {0.3*width/2, origin.y + glyph->offset_y, 0, blue}, // Offset y {0.8*width, origin.y + glyph->offset_y, 0, blue}, {0.8*width, origin.y , 0, blue}, }; GLuint indices [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12, 13,14,15,16,17,18,19,20,21,22,23,24,25}; vertex_buffer_push_back( line_buffer, vertices, 26, indices, 26 ); pen.x = width/2 - 48; pen.y = .2*height - 18; add_text( text_buffer, small, L"advance_x", &blue, &pen ); pen.x = width/2 - 20; pen.y = .8*height + 3; add_text( text_buffer, small, L"width", &blue, &pen ); pen.x = width/2 - glyph->width/2 + 5; pen.y = .85*height-8; add_text( text_buffer, small, L"offset_x", &blue, &pen ); pen.x = 0.2*width/2-30; pen.y = origin.y + glyph->offset_y - glyph->height/2; add_text( text_buffer, small, L"height", &blue, &pen ); pen.x = 0.8*width+3; pen.y = origin.y + glyph->offset_y/2 -6; add_text( text_buffer, small, L"offset_y", &blue, &pen ); pen.x = width/2 - glyph->offset_x - glyph->width/2 - 58; pen.y = height/2 - glyph->offset_y + glyph->height/2 - 20; add_text( text_buffer, small, L"Origin", &black, &pen ); GLuint i = 0; point_t p; p.color = black; // Origin point p.x = width/2 - glyph->offset_x - glyph->width/2; p.y = height/2 - glyph->offset_y + glyph->height/2; vertex_buffer_push_back( point_buffer, &p, 1, &i, 1 ); // Advance point p.x = width/2 - glyph->offset_x - glyph->width/2 + glyph->advance_x; p.y = height/2 - glyph->offset_y + glyph->height/2; vertex_buffer_push_back( point_buffer, &p, 1, &i, 1 ); text_shader = shader_load( "shaders/v3f-t2f-c4f.vert", "shaders/v3f-t2f-c4f.frag" ); shader = shader_load( "shaders/v3f-c4f.vert", "shaders/v3f-c4f.frag" ); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); glutMainLoop( ); return 0; }