@epicurus101 here’s a class that I cobbled together from references online that show you how to create a texture atlas from inside Codea.
It’s still WIP but it’ll get you started…
What I normally do is work with all my assets as normal and then add them to a single generated tpage when the app runs - this could be done once and stored locally or just generated each time, it’s pretty fast…
--[[
-- ------------------------------------------------------
-- TextureAtlas - a class to maange a single image as a sprite sheet
-- uses a quick method to add images - also want to support save / load of
-- both image & table (if possible)
This is a very simple binary tree based bin packing algorithm that is initialized
with a fixed width and height and will fit each block into the first node where
it fits and then split that node into 2 parts (down and right) to track the
remaining whitespace.
Best results occur when the input blocks are sorted by height, or even better
when sorted by max(width,height).
-- Code examples
-- http://www.codeproject.com/Articles/210979/Fast-optimizing-rectangle-packing-algorithm-for-bu
-- http://codeincomplete.com/posts/2011/5/7/bin_packing/
--]]
TextureAtlas = class()
local cindex,colors = 1, {
color(255,0,0,128),color(0,255,0,128),color(0,0,255,128),color(255,255,0,128),
color(255,0,255,128), color(0,255,255,128),color(255,255,255,128), color(64,64,64,128)
}
-- ----------------------------------------------------------------
function TextureAtlas:init(params)
params = params or {}
w = params.width or 2048
h = params.height or 2048
self.defaultPadding = params.padding or 0
self.defaultSmoothing = params.smooth or false
self.width,self.height = w,h
self.image = image(w,h)
self.assets = {}
self.locked = false
self.root = {x=0, y=0, w=w, h=h, used=false, down=nil, right=nil}
-- Debug - fill the atlas with a solid colour
---[[
setContext(self.image)
fill(128,128,128,255)
rectMode(CORNER)
noSmooth()
rect(0,0,self.width,self.height)
setContext()
--]]
end
-- ----------------------------------------------------------------
-- Add an image to the atlas - returns the index in the asset table or nil
local function findNode(root,w,h)
if root.used then
local found = findNode(root.right,w,h)
if not found then found = findNode(root.down,w,h) end
return found
else
if (root.w >= w) and (root.h >= h) then return root end
end
return nil
end
local function splitNode(node,w,h)
node.used = true
node.down = { x=node.x, y=node.y+h, w=node.w, h=node.h-h, used=false, down=nil, right=nil }
node.right = { x=node.x+w, y=node.y, w=node.w-w, h=h, used=false, down=nil, right=nil }
return node
end
function TextureAtlas:add(img,opts)
if self.locked then return nil end
opts = opts or {}
local w = opts.w or img.width
local h = opts.h or img.height
local name = opts.name or "asset"
local padding = opts.padding or self.defaultPadding
local smoothing = opts.smooth or self.defaultSmoothing
local pw,ph = w + padding * 2, h + padding * 2
-- work out a space
local node = findNode(self.root,pw,ph)
local asset = nil
if node then
-- print("Node",node.x,node.y,w,h,padding)
node = splitNode(node,pw,ph)
local tx,ty = node.x + padding,node.y + padding
print("Node ("..tx..","..ty..","..w..","..h..")")
-- create the asset entry
asset = { name=name,padding=padding,tx=tx,ty=ty,tw=w,th=h,
u = tx/self.width,v=ty/self.height,w=w/self.width,h=h/self.height}
-- print("Asset "..asset.u..","..asset.v..","..asset.w..","..asset.h)
-- copy the image
setContext(self.image); if smoothing then smooth() else noSmooth() end
fill(colors[cindex]); cindex = cindex + 1; if cindex == 9 then cindex = 1 end; rect(tx,ty,w,h)
spriteMode(CORNER); sprite(img,tx,ty,w,h); setContext()
else
print("ERROR "..name.."("..w..","..h..") not allocated")
end
local index = #self.assets+1
self.assets[index] = asset
return index
end
-- ----------------------------------------------------------------
-- Add a list of images to the atlas, returns a table of internal asset indices
function TextureAtlas:addList(list)
local result = {}
for i=1,#list do result[#result+1] = self:add(list[i]) end
return result
end
-- ----------------------------------------------------------------
-- Lock the atlas (clean up the tree structure and prevent any further additions)
function TextureAtlas:lock()
self.locked = true
self.root = nil
collectgarbage("collect")
collectgarbage("collect")
end
-- ----------------------------------------------------------------
-- Return the entry for the given asset
-- { name, tx,ty,tw,th (int), u,v,w,h (float)
function TextureAtlas:getIndexByName(n)
for i=1,#self.assets do if (self.assets[i].name == n) then return i end end
return nil
end
function TextureAtlas:getAsset(a) return self.assets[a] end
function TextureAtlas:getAssetByName(n) return self:getAsset(self:getIndexByName(n)) end
-- ----------------------------------------------------------------
-- Return one of the assets as a new sub image
function TextureAtlas:getAssetImage(a)
local asset = self.assets[a]
return self.image:copy(asset.tx,asset.ty,asset.tw,asset.th)
end
-- ----------------------------------------------------------------
-- Return the asset data ready to add to a mesh
-- ----------------------------------------------------------------
-- Return an asset as a mesh
function TextureAtlas:getMesh(a,x,y)
local m = mesh()
return m
end
-- ----------------------------------------------------------------
-- Save / load to local storage
function TextureAtlas:save(name) alert("TODO : TextureAtlas:save") end
function TextureAtlas:load(name) alert("TODO : TextureAtlas:load") end
-- End of file...
Here’s an example main.lua that show’s how it’s used.