Pixel font [how do I tint a mesh?]

In main I did:

function setup()
    letters=PixelFont(100,300, "caa")
    print(string.format(letters1))
end

function draw()
    background(40, 40, 50)
    letters:draw()
end

And it only printed “Dropbox:ascii27” once

Run this and see what prints. It should print Dropbox:ascii97 Dropbox:ascii98 Dropbox:ascii99 .

function setup()
    letters=PixelFont(100,300, "abc")
end

function draw()
    background(40, 40, 50)
    letters:draw()
end

PixelFont = class()

function PixelFont:init(x,y,letters)
    self.m=mesh()
    self.pos=vec2(x,y)
    self.letters=letters
    for z=1,#self.letters do
        c=string.sub(self.letters,z,z)
        v=string.byte(c)
        letters1=string.format("Dropbox:ascii%d",v)
        print(letters1)   -- print what letters1 is
        img=readImage(letters1)
        self.m.texture=img
        self.m:addRect(self.pos.x,self.pos.y,img.width,img.height)
        self.pos.x=self.pos.x+img.width
    end
end

function PixelFont:draw()
    self.m:draw()
end

Ok now it printed what you said but I still don’t understand why it draws 3 "c"s instead of an “a” a “b” and a “c”.

@Ignatz @yojimbo2000 Is there a way to change the texture image. It looks like only one image is being used for the texture instead of 3. I’m overlooking something right now.

EDIT: Apparently different rects can’t have different images in the same mesh. All of the rects get the last image assigned.

OK, something isn’t working the way I think it should. I’m getting the same thing. The texture image isn’t changing to the different images.

@GameCoder I’m thinking that what needs to be done is each letter image needs to be put into a single larger image. Then that larger image is used for the mesh texture.

Can we try it the way we were doing it before first. I might not be able to create a image big enough (with the app I’m using to create the textures).

Try it which way. What code above are you referring to.

Yes @dave1707
This code:

PixelFont = class()

function PixelFont:init(x,y,letters)
    self.m=mesh()
    self.pos=vec2(x,y)
    self.letters=letters
    for z=1,#self.letters do
        c=string.sub(self.letters,z,z)
        v=string.byte(c)
        letters1=string.format("Dropbox:ascii%d",v)
        print(letters1)   -- print what letters1 is
        img=readImage(letters1)
        self.m.texture=img
        self.m:addRect(self.pos.x,self.pos.y,img.width,img.height)
        self.pos.x=self.pos.x+img.width
    end
end

function PixelFont:draw()
    self.m:draw()
end

function setup()
    letters=PixelFont(100,300, "abc")
end

function draw()
    background(40, 40, 50)
    letters:draw()
end

Try this and tell me what happens.

function setup()
    letters=PixelFont(30,400, "abc")
end

function draw()
    background(40, 40, 50)
    letters:draw()
end

PixelFont = class()

function PixelFont:init(x,y,letters)
    self.pos=vec2(x,y)
    self.letters=letters
    self.mTab={}
    for z=1,#self.letters do
        c=string.sub(self.letters,z,z)
        v=string.byte(c)
        letters1=string.format("Dropbox:ascii%d",v)
        img=readImage(letters1)
        self.mTab[z]=mesh()
        self.mTab[z].texture=img
        self.mTab[z]:addRect(self.pos.x+z*img.width,self.pos.y,img.width,img.height)
    end
end

function PixelFont:draw()
    for a,b in pairs(self.mTab) do
        b:draw()
    end
end

It… WORKED! Thank you! :slight_smile:

You can only have one image per mesh.

The way both of you are doing this, is to read the image for each letter, every time you use it. So if you have a string with the letter “A” three times, you’ll have three copies of “A” in memory.

Why not read all the letters into a table in setup, then the class only needs to store the letters themselves, and can look up the images in the table when it draws?

One image per mesh, I found that out the hard way. You can addrects and have different colors for each rect, but not images. As for reading the letters, I don’t know exactly what his whole program is doing. I was just trying to get it to work. He can optimize it if he wants to.

ahem https://codea.io/talk/discussion/comment/69015/#Comment_69015

How would I tint the mesh? I want to do this so I can make the letters different colors (because I made the images I used grey) I also added more variables to “PixelFont:initi” (“colorR, colorB, ColorG, colorA”). By default they will be tinted black (if you put in nothing after “letters”), but if you only put in numbers for the variables “colorR, colorB, ColorG” the variable “colorA” by default will be 100% (255).

Here’s the code I want to add but wherever I put it it doesn’t tint the text:

if colorR or colorG or colorB == nil then
        tint(0, 0, 0, 255)
elseif colorA == nil then
        tint(colorR, colorG, colorB, 255)
else
        tint(colorR, colorG, colorB, colorA)
    end

If the letters are rects on the same mesh (with a single texture for the entire glyph set) as I recommended on the previous page use setRectColor:

https://codea.io/reference/Shaders.html#mesh.setRectColor

There’s not a lot of advantage to using meshes over sprites if you’re going to have an individual mesh for each onscreen object, you might as well use sprite. The performance gain with meshes comes from batching lots of objects into one mesh.

I doubt there’s any performance problem with a handful of images like this, so spriting is simpler

I have made what I consider to be a very good pixel text engine. The way it works is it uses the character data provided to draw some characters into mesh. The mesh is then drawn into the canvas.

PixelFont = class()

function PixelFont:init()
    self.data = {
    ["A"] = "0111111000000101000001010000010101111110",
    ["B"] = "0111111101000101010001010100010100111010",
    ["C"] = "0011111001000001010000010100000100100010",
    ["D"] = "0111111101000001010000010100000100111110",
    ["E"] = "0111111101000101010001010100010101000001",
    ["F"] = "0111111100000101000001010000010100000001",
    ["G"] = "0011111001000001010000010100010100111101",
    ["H"] = "0111111100000100000001000000010001111111",
    ["I"] = "0100000101111111010000010000000000000000",
    ["J"] = "0010000001000000010000000100000000111111",
    ["K"] = "0111111100000100000001000000101001110001",
    ["L"] = "0111111101000000010000000100000001000000",
    ["M"] = "0111111100000010000001000000001001111111",
    ["N"] = "0111111100000010000001000000100001111111",
    ["O"] = "0011111001000001010000010100000100111110",
    ["P"] = "0111111100000101000001010000010100000010",
    ["Q"] = "0011111001000001010000010010000101011110",
    ["R"] = "0111111100000101000001010000010101111010",
    ["S"] = "0010001001000101010001010100010100111001",
    ["T"] = "0000000100000001011111110000000100000001",
    ["U"] = "0011111101000000010000000100000000111111",
    ["V"] = "0000111100110000010000000011000000001111",
    ["W"] = "0111111100100000000100000010000001111111",
    ["X"] = "0111000100001010000001000000101001110001",
    ["Y"] = "0000000100000010011111000000001000000001",
    ["Z"] = "0110000101010001010010010100010101000011",
    ["a"] = "0010000001010100010101000101010001111000",
    ["b"] = "0111111101001000010001000100010000111000",
    ["c"] = "0011100001000100010001000100010000101000",
    ["d"] = "0011100001000100010001000100100001111111",
    ["e"] = "0011100001010100010101000101010001011000",
    ["f"] = "0000010001111110000001010000010100000000",
    ["g"] = "1001100010100100101001001010010001111100",
    ["h"] = "0111111100001000000001000000010001111000",
    ["i"] = "0111110100000000000000000000000000000000",
    ["j"] = "0100000010000000100000001000000001111101",
    ["k"] = "0111111100010000001010000100010000000000",
    ["l"] = "0011111101000000000000000000000000000000",
    ["m"] = "0111110000000100000110000000010001111000",
    ["n"] = "0111110000000100000001000000010001111000",
    ["o"] = "0011100001000100010001000100010000111000",
    ["p"] = "1111110000101000001001000010010000011000",
    ["q"] = "0001100000100100001001000010100011111100",
    ["r"] = "0111110000001000000001000000010000001000",
    ["s"] = "0100100001010100010101000101010000100100",
    ["t"] = "0000010000111111010001000000000000000000",
    ["u"] = "0011110001000000010000000100000001111100",
    ["v"] = "0001110000100000010000000010000000011100",
    ["w"] = "0011110001000000011100000100000001111100",
    ["x"] = "0100010000101000000100000010100001000100",
    ["y"] = "1001110010100000101000001010000001111100",
    ["z"] = "0100010001100100010101000100110001000100",
    ["0"] = "0011111001010001010010010100010100111110",
    ["1"] = "0100000001000010011111110100000001000000",
    ["2"] = "0110001001010001010010010100100101100110",
    ["3"] = "0010001001000001010010010100100100110110",
    ["4"] = "0001100000010100000100100001000101111111",
    ["5"] = "0010011101000101010001010100010100111001",
    ["6"] = "0011110001001010010010010100100100110000",
    ["7"] = "0000001100000001011100010000100100000111",
    ["8"] = "0011011001001001010010010100100100110110",
    ["9"] = "0000011001001001010010010010100100011110",
    ["!"] = "0101111100000000000000000000000000000000",
    ["?"] = "0000001000000001010100010000100100000110",
    ["#"] = "0001010001111111000101000111111100010100",
    ["%"] = "0100001100110000000010000000011001100001",
    ["&"] = "0011000001001010010111010011001001001000",
    ["$"] = "0010010000101010011010110010101000010010",
    ["@"] = "0011111001000001010111010101000100011110",
    ["*"] = "0000010100000010000000100000010100000000",
    ['"'] = "0000001100000000000000110000000000000000",
    ["'"] = "0000001100000000000000000000000000000000",
    ["("] = "0001110000100010010000010100000100000000",
    [")"] = "0100000101000001001000100001110000000000",
    ["+"] = "0001000000010000011111000001000000010000",
    [","] = "1110000000000000000000000000000000000000",
    ["-"] = "0001000000010000000100000001000000010000",
    ["/"] = "0100000000110000000010000000011000000001",
    ["."] = "1100000000000000000000000000000000000000",
    [":"] = "0110011000000000000000000000000000000000",
    [";"] = "1110011000000000000000000000000000000000",
    ["<"] = "0000100000010100001000100100000100000000",
    [">"] = "0100000100100010000101000000100000000000",
    ["="] = "0010010000100100001001000010010000100100",
    ["\\\"] = "0000000100000110000010000011000001000000",
    ["["] = "0111111101000001010000010000000000000000",
    ["]"] = "0100000101000001011111110000000000000000",
    ["^"] = "0000010000000010000000010000001000000100",
    ["_"] = "1000000010000000100000001000000010000000",
    ["`"] = "0000001000000001000000000000000000000000",
    ["{"] = "0000100000110110010000010100000100000000",
    ["}"] = "0100000101000001001101100000100000000000",
    ["|"] = "0111011100000000000000000000000000000000",
    ["~"] = "0000001000000001000000100000000100000000"
    }
end

function PixelFont:text(tex, xp, yp, size, col, centering)
    local t = tostring(tex)
    local pic = mesh()
    local char = ""
    local x = 0
    local y = 0
    local xScroll = 0
    local charLen = 0
    local curCharData = ""
    local curRect = nil
    for a = 1, #t do
        char = string.sub(t, a, a)
        if char == " " then
            xScroll = xScroll + 4
        else
            curCharData = self.data[char]
            for i = 1, 40 do
                x = math.floor((i - 1) / 8)
                y = (i - 1) % 8
                if tonumber(string.sub(curCharData, i, i)) == 1 then
                    curRect = pic:addRect(x + xScroll, y, 1, 1)
                    pic:setRectColor(curRect, col)
                    charLen = x + 1
                end
            end
            xScroll = xScroll + charLen + 1
        end
    end
    xScroll = xScroll - 2 
    pushMatrix()
    translate((xScroll * size * (centering - 1) / 2) + xp, yp - size * 4)
    scale(size)
    pic:draw()
    popMatrix()
end

You can use this by copying the code, and pasting it into a new tab in your project. To show that this is pretty fast, use this code in the main tab of the project:

-- Pixel Font

-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
    t = PixelFont()
end

-- This function gets called once every frame
function draw()
    background(199, 199, 199, 255)
    t:text(os.clock(), WIDTH / 2, HEIGHT / 2, 10, color(0, 0, 0), 0)
end

In order to use this, you will need to make an instance of the PixelFont class. Then, you can call the function of that font class called text. To use this, simply put in these inputs:
myPixelFontInstance:text(text you want to render, X position, y position, size (size of 1 = 16 pixels tall), color of text (color object), centering (-1 is centered to right, 0 is centered in middle, and 1 is centered on left))
Note that the text will be centered in the middle on the Y axis. Also, centering gives you a lot more control than with the standard text function. I made this engine use the Minecraft font (with 2 minor changes). Let me know if you want to know how to change it.

@Valgo nice work! B)

You asked whether anyone had any suggestions, I have a couple: how about adding support for \ \\r line breaks (for printing multi- line text)?

As your code shows, it performs very well, though apart from split second timers, most text doesn’t need to be reparsed each frame. So one suggestion might be that you could store the pic mesh as self.pic, and put the last 6 lines of the :text method into a separate :draw method, so you’d just call :text when the text changes, and :draw every frame.