Ejemplo n.º 1
0
	// Find a place to pack a bitmap into a larger bitmap
	BinPackNode *BinPack(BMSize BitmapWidth, BMSize BitmapHeight, BinPackNode *ParentNode)
	{
		// This function is copied almost exactly from: http://www.blackpawn.com/texts/lightmaps/
		if(ParentNode->Children[0] != nullptr || ParentNode->Children[1] != nullptr)
		{
			// Try inserting into first child
			BinPackNode *newNode = BinPack(BitmapWidth, BitmapHeight, ParentNode->Children[0].get());
			if(newNode != nullptr)
				return newNode;

			// No room in first child, try inserting into second
			return BinPack(BitmapWidth, BitmapHeight, ParentNode->Children[1].get());
		}
		else
		{
			// Check if there is already a glyph here
			if(ParentNode->Filled)
				return nullptr;

			// Check if the glyph is too big to fit in this node
			if(ParentNode->Rect.z < BitmapWidth || ParentNode->Rect.w < BitmapHeight)
				return nullptr;

			// Check if the glyph fits perfectly in this node
			if(ParentNode->Rect.z == BitmapWidth && ParentNode->Rect.w == BitmapHeight)
			{
				ParentNode->Filled = true;
				return ParentNode;
			}

			// Node is larger than the glyph, so split the node into two
			ParentNode->Children[0].reset(new BinPackNode());
			ParentNode->Children[1].reset(new BinPackNode());

			// Decide which way to split
			int SpaceWidth = ParentNode->Rect.z - BitmapWidth;
			int SpaceHeight = ParentNode->Rect.w - BitmapHeight;

			if(SpaceWidth > SpaceHeight)
			{
				ParentNode->Children[0]->Rect = vec4(ParentNode->Rect.x, ParentNode->Rect.y, BitmapWidth, ParentNode->Rect.w);
				ParentNode->Children[1]->Rect = vec4(ParentNode->Rect.x + BitmapWidth, ParentNode->Rect.y, SpaceWidth, ParentNode->Rect.w);
			}
			else
			{
				ParentNode->Children[0]->Rect = vec4(ParentNode->Rect.x, ParentNode->Rect.y, ParentNode->Rect.z, BitmapHeight);
				ParentNode->Children[1]->Rect = vec4(ParentNode->Rect.x, ParentNode->Rect.y + BitmapHeight, ParentNode->Rect.z, SpaceHeight);
			}

			// Insert into first child
			return BinPack(BitmapWidth, BitmapHeight, ParentNode->Children[0].get());
		}
	}
Ejemplo n.º 2
0
bool Font::CreateAtlas()
{
    unsigned int totalPixels = 0;
    for (FontSymbols::const_iterator it = this->symbols.cbegin(), end = this->symbols.cend(); it != end; ++it)
    {
        std::shared_ptr<FontSymbol> glyph = it->second;
        totalPixels += glyph->GetPixelCount();
    }

    unsigned int textureWidth   = 1;
    unsigned int textureHeight  = 1;
    while (textureWidth * textureHeight < totalPixels)
    {
        if (textureWidth < textureHeight)
        {
            textureWidth *= 2;
        }
        else
        {
            textureHeight *= 2;
        }

        if (textureWidth > MaxTextureDimension || textureHeight > MaxTextureDimension)
        {
            LogError("Font atlas became too large.");
            return false;
        }
    }

    std::vector<std::shared_ptr<FontSymbol>> sortedSymbols;
    for (FontSymbols::const_iterator it = this->symbols.cbegin(), end = this->symbols.cend(); it != end; ++it)
    {
        sortedSymbols.push_back(it->second);
    }
    std::sort(sortedSymbols.begin(), sortedSymbols.end(),
        [](std::shared_ptr<FontSymbol> &glyph1, std::shared_ptr<FontSymbol> &glyph2) -> bool {
            return glyph1->GetPixelCount() > glyph2->GetPixelCount();
        }
    );
    
    GlyphTreeNode::GetAllocator().Init((this->symbols.size() + 1) * 2);
    if (!BinPack(textureWidth, textureHeight))
    {
        // TODO: We shouldn't fail like this because binary packing the glyphs might fail if for example
        // texture is not big enough to fit all glyphs because of too many unused gaps. In such case we should
        // continue looping and increase textureWidth & textureHeight until BinPack succeeds.
        GlyphTreeNode::GetAllocator().Release();
        LogError("Font::BinPack has failed.");
        return false;
    }
    GlyphTreeNode::GetAllocator().Release();

    unsigned char *textureBuffer = new unsigned char[textureWidth * textureHeight];

    for (FontSymbols::const_iterator it = this->symbols.cbegin(), end = this->symbols.cend(); it != end; ++it)
    {
        std::shared_ptr<FontSymbol> symbol = it->second;
        symbol->RenderToBuffer(textureBuffer, textureWidth, textureHeight);
        symbol->GlyphLoaded();
    }
    
    glGenTextures(1, &this->glTexture);
    glBindTexture(GL_TEXTURE_2D, this->glTexture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, textureWidth, textureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, textureBuffer);

    GL_CHECK_ERROR();

    delete[] textureBuffer;

    this->atlas = std::shared_ptr<FontSymbol>(TG_NEW FontSymbol);
    this->atlas->SetXY(0, 0);
    this->atlas->SetSize(textureWidth, textureHeight);
    this->atlas->InitTextureCoords(textureWidth, textureHeight);

    return true;
}