Memory and iOS 7

@epicurus101 Hers another example of setting the mesh texture using the sprite sheet I created above. You can tap on a character or slide your finger around the characters to show the area in the mesh box. If you want to key in a letter, tap the upper left corner to show the parameters area. Tap the txt area to show the keyboard and key in characters. I only show the last character typed in the mesh area.

displayMode(FULLSCREEN)
supportedOrientations(PORTRAIT_ANY)

function setup()

    fontSize(50)
    img=image(700,700)
    setContext(img)
    background(0, 217, 255, 255)
    fill(255)
    s=32
    for a=0,12 do
        for b=0,9 do
            text(string.char(s),b*70+35,a*70+35)
            s=s+1
        end
    end
    setContext()
    m=mesh()
    m.texture=img
    m:addRect(WIDTH/2,HEIGHT-100,70,70)
    parameter.text("txt"," ",getTxt)
end

function draw()
    background(0)
    sprite(img,WIDTH/2,400)
    m:draw()
end

function getTxt()
    ln=string.len(txt)
    if ln>0 then
        v=string.byte(string.sub(txt,ln,ln))-32
        vx=(v%10)/10
        vy=(v//10)/10
        m:setRectTex(1,vx,vy,.1,.1)    
    end
end

function touched(t)
    if t.state==BEGAN or t.state==MOVING then
        tx=(t.x-70)/700
        ty=(t.y-90)/700
        m:setRectTex(1,tx,ty,.1,.1)        
    end
end

@dave1707 Thanks. I haven’t really experimented with taking advantage of the unicode codes in that way.

@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.


Here’s an example main.lua that show’s how it’s used.

-- TextureAtlas
Utils = {}

Utils.vardump = function(value, depth, key)
    local linePrefix = ""
    local spaces = ""
  
    if key ~= nil then linePrefix = "["..key.."] = " end
  
    if depth == nil then depth = 0
    else
        depth = depth + 1
        for i=1, depth do spaces = spaces .. " " end
    end
  
    if type(value) == 'table' then
        mTable = getmetatable(value)
        if mTable == nil then
            print(spaces ..linePrefix.."(table) ")
        else
            print(spaces .."(metatable) ")
            value = mTable
        end        
    
        for tableKey, tableValue in pairs(value) do Utils.vardump(tableValue, depth, tableKey) end
    elseif type(value)	== 'function' or type(value) == 'thread' or type(value)	== 'userdata' or value == nil then
        print(spaces..tostring(value))
    else
        print(spaces..linePrefix.."("..type(value)..") "..tostring(value))  
    end
end

-- Use this function to perform your initial setup
function setup()
    print("Texture atlas!")
    
    print("Start = "..ElapsedTime)
    
---[[
    atlas = TextureAtlas({padding=1,smooth=true})
    
    -- Load in a selection of images and add them to the atlas.
    
    atlas:add(readImage("Dropbox:aa_MainBackground"),{w=768,h=1024})
    atlas:add(readImage("Dropbox:aa_flag_russia"))
    atlas:add(readImage("Dropbox:aa_flag_russia"),{w=300,h=300})
    ai = atlas:add(readImage("Cargo Bot:Cargo Bot Title"))
    atlas:add(readImage("Cargo Bot:Cargo Bot Title"))
    atlas:add(readImage("Cargo Bot:Cargo Bot Title"))
    atlas:add(readImage("Dropbox:010back"))
    atlas:add(readImage("Small World:Court"))
    atlas:add(readImage("Small World:Church"))
    atlas:add(readImage("Small World:Base Large"))
    atlas:add(readImage("Planet Cute:Character Boy"))
    atlas:add(readImage("Planet Cute:Character Horn Girl"))
    atlas:add(readImage("Planet Cute:Character Pink Girl"))
    atlas:add(readImage("Planet Cute:Character Pink Girl"))
    atlas:add(readImage("Planet Cute:Character Pink Girl"),{padding=20})
    atlas:add(readImage("Planet Cute:Character Pink Girl"),{padding=20})
    atlas:add(readImage("Planet Cute:Character Pink Girl"),{padding=20})
    atlas:add(readImage("Planet Cute:Character Pink Girl"),{padding=20})
    atlas:add(readImage("Planet Cute:Character Pink Girl"))
    atlas:add(readImage("Planet Cute:Character Pink Girl"))
    
    atlas:lock()
    
    print("End = "..ElapsedTime)

    img = atlas:getAssetImage(ai)
    print("Img = "..img.width..","..img.height)
--]]
    
    m = mesh()
    --m.texture = "Planet Cute:Grass Block"

--[[
    -- anti clockwise from bottom left
    m.vertices = {vec3(0,0,0),vec3(300,0,0),vec3(300,300,0)}
    --m.texCoords = {vec2(0,0),vec2(1,0),vec2(1,1)}

    print(m)
    Utils.vardump(m.vertices)

    m:setColors(255,255,255,255)            

    print("Colors before "..#m.colors)
    Utils.vardump(m.colors)

    -- m.colors = {color(0,255,0),color(0,0,255),color(255,255,0)}
    -- Access individual elements
    b = m:buffer("color");    b[1] = color(0,0,0)
    b = m:buffer("position"); b[1] = vec3(10,10,0)
    
    Utils.vardump(b)
    Utils.vardump(m.vertices)
--]]


    print(m:addRect(200,200,400,400))
    b = m:buffer("color");    
    b[1] = color(255,0,0)
    b[2] = color(0,255,0)
    b[3] = color(0,0,255)
    b[4] = color(0,255,255)
    b[5] = color(255,0,255)
    b[6] = color(255,255,0)

    
    Utils.vardump(m.vertices)

    print(m:addRect(350,550,200,150))
    
    Utils.vardump(m.vertices)
        
        
    print("Done")
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(132, 64, 64)


---[[
    -- Dump the atlas
    local w,h = atlas.width * 0.333, atlas.height * 0.333
    spriteMode(CORNER)
    sprite(atlas.image,0,0,w,h)

    -- Render elements as a mesh
    noSmooth()
    -- sprite(img,20,HEIGHT-(img.height+10))
--]]    
    
   -- m:draw()
    
end

Again this code is WIP so feel free to replace the dropbox links with assets of your own - also I found that a tiny (like 1 pixel) bit of padding is required to stop “texture bleed” when using the mesh coordinates (this is a common issue with the bilinear filtering).

One of Codea’s biggest strengths is being able to load in images as PDF files - that means you could, store ONE version of all your assets as PDF files, then at run time render them to the correct size for your device and store them in an atlas and not have to worry about messing about with @2x or @4x versions of everything for all different device types.