Fonts

    I will use simple sprite fonts. It easy to implement. I want to use TTF fonts, but I need to learn how to draw them.

    Sprite fonts is good when you need beautiful colorful characters.

    I don't want to create font generator, easy to add methods and add characters by hands.

    Add structure for character

    
    struct slGUIFontGlyph
    {
    	char32_t m_symbol = U' ';
    	slVec4f m_UV;
    	slRect m_rect;
    	int32_t m_underhang = 0;
    	int32_t m_overhang = 0;
    	uint32_t m_width = 0;
    	uint32_t m_height = 0;
    	uint32_t m_textureSlot = 0;
    };
    

    Class for font

    
    class slGUIFont
    {
    	uint32_t m_textureNum = 0;
    	slArray<slTexture*> m_textures;
    
    	slArray<slGUIFontGlyph*> m_glyphs;
    
    	slPoint m_maxSize;
    
    	slGUIFontGlyph* m_glyphMap[0x32000];
    public:
    	slGUIFont();
    	~slGUIFont();
    
    	void AddTexture(slTexture*);
    	void AddGlyph(const slGUIFontGlyph&);
    	void AddGlyph(char32_t ch, const slVec2f& leftTop, const slPoint& charSz, uint32_t texture, const slPoint& texSz)
    	{
    		slGUIFontGlyph glyph;
    		glyph.m_symbol = ch;
    		glyph.m_width = charSz.x;
    		glyph.m_height = charSz.y;
    		glyph.m_textureSlot = texture;
    		glyph.m_UV.x = slMath::coordToUV(leftTop.x, (float)texSz.x);
    		glyph.m_UV.y = slMath::coordToUV(leftTop.y, (float)texSz.y);
    		glyph.m_UV.z = slMath::coordToUV(leftTop.x + (float)charSz.x, (float)texSz.x);
    		glyph.m_UV.w = slMath::coordToUV(leftTop.y + (float)charSz.y, (float)texSz.y);
    		AddGlyph(glyph);
    	}
    
    	const slPoint& GetMaxSize() { return m_maxSize; }
    	slGUIFontGlyph** GetGlyphMap() { return m_glyphMap; }
    	slTexture* GetTexture(uint32_t i) { return m_textures.m_data[i]; }
    
    	int32_t m_characterSpacing = 1;
    	int32_t m_lineSpacing = 3;
    	int32_t m_spaceSize = 4;
    	int32_t m_tabSize = 16;
    };
    

    All glyphs stored in m_glyphs. m_glyphMap will have addresses. 0x32000 is current maximum unicode code, everything above is like reserved, not printable

    Implementation

    
    slGUIFont::slGUIFont() 
    {
    	slGUIFontGlyph g;
    	g.m_symbol = 0;
    	AddGlyph(g);
    
    	for (uint32_t i = 0; i < 0x32000; ++i)
    	{
    		m_glyphMap[i] = m_glyphs.m_data[0];
    	}
    }
    
    slGUIFont::~slGUIFont()
    {
    	for (size_t i = 0; i < m_glyphs.m_size; ++i)
    	{
    		slDestroy(m_glyphs.m_data[i]);
    	}
    }
    
    void slGUIFont::AddTexture(slTexture* t)
    {
    	SL_ASSERT_ST(t);
    	m_textures.push_back(t);
    }
    
    void slGUIFont::AddGlyph(const slGUIFontGlyph& g)
    {
    	// create new
    	slGUIFontGlyph* newG = slCreate<slGUIFontGlyph>();
    	// copy data
    	*newG = g;
    	// save it
    	m_glyphs.push_back(newG);
    	// save address
    	m_glyphMap[g.m_symbol] = newG;
    
    	if ((int32_t)g.m_height > m_maxSize.y) m_maxSize.y = g.m_height;
    	if ((int32_t)g.m_width > m_maxSize.x) m_maxSize.x = g.m_width;
    }
    
    

    Text drawing

    GS must draw text. Without buffer creation.

    You can create text like mesh object, but it not good if you want to draw a lot of text.

    I want to draw not just text, but only one glyph. Because for text editor I need to draw character by character. But for things like text on button I need to give whole text. So add in GS two methods:

    
    virtual void DrawGUICharacter(char32_t, slGUIFont*, const slVec2f& position, const slColor&) = 0;
    virtual void DrawGUIText(const char32_t* text, uint32_t textSz, const slVec2f& position,
    	slGUIDrawTextCallback*) = 0;
    

    In DrawGUIText I need to use callback to set the font and color. What if we want to write japanese and other language in one text line?

    
    void slGSD3D11::DrawGUICharacter(char32_t ch, slGUIFont* font, const slVec2f& position, const slColor& color)
    {
    	SL_ASSERT_ST(font);
    	SL_ASSERT_ST(ch < 0x32000);
    
    	slGUIFontGlyph* g = font->GetGlyphMap()[ch];
    
    	slRect rct;
    	rct.left = (int32_t)position.x;
    	rct.top = (int32_t)position.y;
    
    	rct.right = rct.left + g->m_width;
    	rct.bottom = rct.top + g->m_height;
    
    	drawGUIRectangle(rct, color, color, (slGSD3D11Texture*)font->GetTexture(g->m_textureSlot), 
    		&g->m_UV);
    }
    

    Draw something in app. Add glyphs.

    
    slImage* myFontImage = slFramework::SummonImage(slFramework::GetPathA("../data/fonts/font.png").c_str());
    slTextureInfo ti;
    slTexture* myFontTexture = app.m_gs->SummonTexture(myFontImage, ti);
    slGUIFont* myFont = slFramework::SummonFont();
    myFont->AddTexture(myFontTexture);
    slGUIFontGlyph glyph;
    glyph.m_symbol = U'A';
    glyph.m_height = 13;
    glyph.m_width = 11;
    glyph.m_textureSlot = 0;
    glyph.m_UV.x = slMath::coordToUV(0, 256);
    glyph.m_UV.y = slMath::coordToUV(0, 256);
    glyph.m_UV.z = slMath::coordToUV(10, 256);
    glyph.m_UV.w = slMath::coordToUV(12, 256);
    myFont->AddGlyph(glyph);
    myFont->AddGlyph(U'B', slVec4f(10, 0, 18, 12), slPoint(256, 256), slPoint(9, 13), 0);
    myFont->AddGlyph(U'C', slVec2f(19, 0), slPoint(9, 13), 0, slPoint(256, 256));
    

    Draw

    
    app.m_gs->DrawGUICharacter(U'A', myFont, slVec2f(10.f, 10.f), ColorWhite);
    app.m_gs->DrawGUICharacter(U'B', myFont, slVec2f(20.f, 10.f), ColorLime);
    app.m_gs->DrawGUICharacter(U'C', myFont, slVec2f(30.f, 10.f), ColorCornsilk);
    

    Now DrawGUIText

    
    void slGSD3D11::DrawGUIText(const char32_t* text, uint32_t textSz, const slVec2f& _position,
    	slGUIDrawTextCallback* cb)
    {
    	SL_ASSERT_ST(cb);
    	SL_ASSERT_ST(text);
    
    	slVec2f position = _position;
    
    	for (uint32_t i = 0; i < textSz; ++i)
    	{
    		slGUIFont* font = cb->OnFont(text[i]);
    		slColor* color = cb->OnColor(text[i]);
    
    		slGUIFontGlyph* g = font->GetGlyphMap()[text[i]];
    
    		slRect rct;
    		rct.left = (int32_t)position.x;
    		rct.top = (int32_t)position.y;
    
    		rct.right = rct.left + g->m_width;
    		rct.bottom = rct.top + g->m_height;
    
    		drawGUIRectangle(rct, *color, *color, (slGSD3D11Texture*)font->GetTexture(g->m_textureSlot),
    			&g->m_UV);
    
    		position.x += g->m_width + g->m_overhang + g->m_underhang + font->m_characterSpacing;
    
    		switch (text[i])
    		{
    		case U' ':
    			position.x += font->m_spaceSize;
    			break;
    		case U'\t':
    			position.x += font->m_tabSize;
    			break;
    		case U'\n':
    			position.y += font->m_lineSpacing + font->GetMaxSize().y;
    			position.x = _position.x;
    			break;
    		}
    	}
    }
    

    Callback

    
    class slGUIDrawTextCallback : public slUserData
    {
    public:
    	slGUIDrawTextCallback() {}
    	virtual ~slGUIDrawTextCallback() {}
    
    	virtual slGUIFont* OnFont(char32_t) = 0;
    	virtual slColor* OnColor(char32_t) = 0;
    };
    

    Draw

    
    
    class GUIDrawTextCallback : public slGUIDrawTextCallback
    {
    	slGUIFont* m_font = 0;
    	slColor m_colorWhite = ColorWhite;
    	slColor m_colorRed = ColorRed;
    public:
    	GUIDrawTextCallback(slGUIFont* f):m_font(f) {}
    	virtual ~GUIDrawTextCallback() {}
    	virtual slGUIFont* OnFont(char32_t c) final
    	{
    		return m_font;
    	}
    	virtual slColor* OnColor(char32_t c) final
    	{
    		switch (c)
    		{
    		case U'A':
    		case U'E':
    		case U'O':
    		case U'U':
    		case U'Y':
    		case U'I':
    			return &m_colorRed;
    		}
    
    		return &m_colorWhite;
    	}
    };
    ...
    app.m_gs->DrawGUIText(U"Hello world!\nNew LINE\nABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_)(abcdefghijklmnop<>", 79, slVec2f(0.f, 20.f), &textDrawCallback);
    

    Other things

    
    float slMath::coordToUV(float value, float textureSz)
    {
    	return value * (1.f / textureSz);
    }
    

    Download