mesh atlas

(putting in beta for now, but will open when 1.3 is out)

So I’m planning to implement the atlas trick and wanted to bounce ideas/questions.

I’m thinking the atlas has a single mesh whose texture is an image with all the drawings that this atlas knows how to render. Then addRect/setRect creates the mesh rect. So something like:

  • Atlas:map( name, drawingFunc, width, height) adds a new drawing to the texture. drawingFunc draws a single image which will be rendered to the texture with dimensions (width,height)

  • Atlas:addRect(name, x, y, width, height) adds a new rect to the mesh. Return the index of the rect. width, height are optional

  • Atlas:setRect( index, x, w, width, height) moves an existing rect around

  • Atlas:draw() renders the mesh onto the screen

Does this make sense?

A couple of questions

  • @Simon said there’s a limit on the size of mesh textures. Does this mean that my atlas should have multiple meshes to get around that? If so, then Atlas:draw() will need to draw multiple meshes. And addRect will need to do some house keeping to map from each mesh index to the atlas index.

  • If so, is it worth adding my background image (768x1024) to the atlas? In which case it will take a whole mesh - I might as well just draw the background image outside the atlas no?

  • Edit: the way I’m planning to construct the texture is with setContext. Is that the best way? One problems seems to be that setContext actually limits drawing to the dimensions of the screen (which surprised me). That is I can’t draw onto an image at position say 769x1025 with setContext

Interesting project!

I’ve been operating under the assumption that a single mesh:draw() call and a single sprite() call are about the same speed, or will be. I have not actually checked out that supposition.

Suggest adding rotation to the addRect/setRect calls, simply because it’s supported.

Also suggest a function to replace the texture in a given rect with another named region of the atlas.

Extra points for automatically per-draw handling a series of atlas textures for animation. You could either add them as a set, or add them and then take a table of the names to iterate thru.

TLL: Be on the lookout for free graphics data that could be for a sprite pack for mesh stuff - I’d like to see something with some animation cells for a platformer. I know it’s going to be too late for this beta, but that will be in demand.

Bortels, good point on rotation. Will also need to handle z-order if I want to use my atlas as the single draw() call I make in my program.

Didn’t understand this - would you mind clarifying?

“Extra points for automatically per-draw handling a series of atlas textures for animation. You could either add them as a set, or add them and then take a table of the names to iterate thru.”

Yeah, I confused myself when I wrote it.

What I’d like is to be able to say:

a=atlas()
gemanim = {}
for i=1,6 do
   a:map("gem"..i, sprite("Tyrian Remastered:Gem Shine "..i), 19, 27)
   gemanim.insert("gem"..i)
end
a:addRect("gem", 0, 0, 19, 27)
a:setAnim(gemanim)

The above is all yours up to the last line; we make an atlas “a”, and add 6 images (from the Tyrian Remastered spritepack) to it as “gem1” thru “gem6”. I save those names in a table, add the rect to draw, then tell the atlas “draw the following animation” - idea being each time I call a:draw, it draws the next frame in the gem1 thru gem6 animation, without me having to keep track manually or set graphics.

I think a lot of games are going to revolve around moving the mesh to scroll, and changing which chunk of an atlas texture each rect maps to, to do things like turn switches on/off, open doors, and do character animation.

here’s a start: http://ruilov.posterous.com/atlas-01

using the most stupid packing algorithm for now

and how to use:

function setup()
    noSmooth()
    atlas = Atlas()
    
    -- map a few test functions
    atlas:map("test1",draw1,200,100)
    atlas:map("test2",draw2,200,100)
    atlas:map("test3",draw3,101,171)
    
    -- draw1. Note that x and y coords are in CENTER mode
    test1Idx = atlas:addRect("test1",100,50)
    -- update the position of test 1 to make sure it updates the internal data structures
    atlas:setRect(test1Idx,150,50)
    -- draw2. Passing z-coord of -1 to draw under the first one
    test2Idx = atlas:addRect("test2",200,50,nil,nil,nil,-1)
    -- passing a width, height and rotation angle, just like in mesh:addRect
    atlas:addRect("test2",400,400,50,50,math.rad(45))
    test3x = 100
    test3Idx = atlas:addRect("test3",test3x,400)
end

function draw()
    background(0,0,0)
    atlas:draw()
    -- move test 3 one pixel over to the right
    test3x = test3x + 1
    -- also test passing a new width to make sure that works
    atlas:setRect(test3Idx,test3x,400,300-test3x)
    
    -- set test2 on top or below test1 as a test that setRect is working
    if ElapsedTime > 1 and ElapsedTime < 2 then 
        atlas:setRect(test2Idx,200,50,nil,nil,nil,1) 
    end
    -- some more z order tests
    if ElapsedTime > 2 and ElapsedTime < 3 then 
        atlas:setRect(test1Idx,150,50,nil,nil,nil,2)
    end
    if ElapsedTime > 3 then atlas:setRect(test2Idx,200,50,nil,nil,nil,3) end
end

function draw1()
    stroke(9, 255, 0, 255)
    strokeWidth(2)
    fill(0,0,255,255)
    rectMode(CORNER)
    rect(0,0,200,100)
end

function draw2()
    stroke(255, 255, 255, 255)
    strokeWidth(2)
    fill(255, 0, 176, 255)
    rectMode(CORNER)
    rect(0,0,200,100)
end

function draw3()
    spriteMode(CORNER)
    sprite("Planet Cute:Character Pink Girl",0,0,101,171)
end

hm just realized that if I really want to handle z-order well across multiple meshes, it’s going to get messy. Maybe best to just not handle z-order at all, and let the user create multiple atlases and draw them in the desired order.

That, or allow the user to assign z-order either before or after. I like the concept of setting up the object, and all you need to do during draw is “a:draw()” and it takes care of everything.

the problem is that mesh1 might have a sprite at z=0 and another at z=2 and then mesh2 have a sprite at z = 1. In that case I’ll have to move the sprites around from one texture to another based on the z order…and I could, but well I’m lazy.

If the only reason for multiple meshes is to handle images larger than the maximum texture size, the user doesn’t care how you do them internally… but, yeah, management of them would be messy.

But I’d not let that prevent you from doing it… I’d rather see z-level handled well instead of support for gimungous images. If someone wants to have a really big image, they can do it themselves.

The more I think about it, the uglier it gets. :slight_smile:

@ruilov in general I would keep big images (such as background) out of an atlas, except for special circumstances. What you want in the atlas are the small textures, items that are drawn constantly and repeatedly. So characters, power ups, icons, enemies, logos, buttons, particles and so on.

A background is generally drawn once and accounts for a constant state-change overhead, so it’s really not worth filling up your atlas with it.

Bortels, I might. Working on something else and I want to integrate this simple atlas version in it to see how it goes first. The reason for multiple meshes is just that the aggregated size of all sprites you want to draw might be larger than the screen. Not necessarily because the images are large (though I agree that’s probably rare)

Edit: actually the version I posted does handle z-order just fine as long as all sprites fit into one mesh. The packing algorithm is stupid so if you add too many sprites it will wastefully create a new mesh, but improving the packing algorithm to something a little less stupid is easy.

Simon, ok that makes sense, thanks.