Skip to content

Instantly share code, notes, and snippets.

@JMV38
Created November 25, 2012 19:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JMV38/4144835 to your computer and use it in GitHub Desktop.
Save JMV38/4144835 to your computer and use it in GitHub Desktop.
Convert tab
Convert = class()
-- converting a 3d object into a color image is done in steps:
--    1/ convert each element of object in an image
--    2/ assemble the image elements in one big image
-- the reading is the same in inverse order
function Convert:init()
end
function Convert:saveMesh(ms,name,debug)
    local obj = {}
    obj.texture = ms.texture
    obj.vertices = ms.vertices
    obj.texCoords = ms.texCoords
    obj.colors = ms.colors
    local img,size = self:pushObject00(obj,debug)
    if debug then print("mesh size = "..math.floor(size*100).."% of max size") end
    self:saveImage(name, img)
    return size
end
function Convert:loadMesh(name,debug)
    local img = self:readImage(name)
    local obj = self:popObject00(img,debug)
    local ms = mesh()
    ms.texture = obj.texture
    ms.vertices = obj.vertices
    ms.texCoords = obj.texCoords
    ms.colors = obj.colors
    return ms
end
-- local functions to be faster
local insert = table.insert
local floor = math.floor
function Convert:saveImage(name,img)
    local w,h = img.width, img.height
    local x,y,r,g,b,a
    local out = image(w,h*2)
    for x=1,w do for y=1,h do
        r,g,b,a = img:get(x,y)
        out:set(x,y*2-1,r,g,0,255)
        out:set(x,y*2  ,b,a,0,255)
    end end
    saveImage("Documents:"..name,out)
end
function Convert:readImage(name)
    local img = readImage("Documents:"..name)
    local w,h = img.width, img.height
    local x,y,r,g,b,a,_
    local out = image(w,h/2)
    for x=1,w do for y=1,h/2 do
        r,g,_,_ = img:get(x,y*2-1)
        b,a,_,_ = img:get(x,y*2)
        out:set(x,y,r,g,b,a)
    end end
    return out
end
function debugPrint(debug,txt,t)
    if debug then 
        local str = ""
        if type(t)=="table" then str ="{"..tostring(t[1])..",... }"
        elseif t~=nil then str = tostring(t) 
        end
        print(txt..str) 
    end
end
function Convert:popObject00(img,debug)
    local n
    local t,out = {},{}
    -- read verification information
    n = 1
    t,n = self:getTable(n,img)
    self:popToObject(t,out,debug)
    t,n = self:getTable(n,img)
    self:popToObject(t,out,debug)
    if out.id~="JMV38-3d-object" 
    then error("this image is not a 3d object saved to an image") 
    elseif out.saveVersion ~= 0
    then error("the object version ("..out.saveVersion..") is not compatible with this reader")
    end
    -- get other elements
    t,n = self:getTable(n,img)
    while t do
        self:popToObject(t,out,debug)
        t,n = self:getTable(n,img)
    end
    return out,n
end
function Convert:pushObject00(obj,debug)
    local input = {}
    input.id = "JMV38-3d-object"
    input.saveVersion = 0
    -- put everything in a table
    local t ={}
    self:pushToTable("id",input,t)
    debugPrint(debug,"pushed : obj.".."id".." = ",input.id)
    self:pushToTable("saveVersion",input,t)
    debugPrint(debug,"pushed : obj.".."saveVersion".." = ",input.saveVersion)
    local i,v
    for i,v in pairs(obj) do
        self:pushToTable(i,obj,t)
        debugPrint(debug,"pushed : obj."..i.." = ",v)
    end
    -- 1 more pixel at 0 to indicate it is finished (and avoid image overflow)
    insert(t, self:intToColor(0) )
    -- convert to an image and return it
    return self:tableToImage(t)
end
function Convert:tableToImage(t)
    -- write table in an image
    local n = #t
    local h = floor(n/2048)+1
    local sizefraction = (h+1)/2048
    local txt
    if sizefraction>1 then 
        txt = "Sorry, your mesh size once converted is: "..floor(sizefraction*100)
        txt = txt.."% of maximum size, too big for this saving tool."
        error(txt)
    end
    local w
    if n>2048 then w=2048 else w=n end
    local img = image(w,h)
    local x,y,i
    for i=1,n do 
        y = floor((i-1)/2048)
        x = i-1 - y*2048
        img:set(x+1,y+1,t[i])
     --   if self:type(t[i])~="color" then 
     --   error(i.." : "..tostring(t[i]).." : "..self:type(t[i])) end
    end
    return img,sizefraction
end
function  Convert:xy(i)
    local x,y
        y = floor((i-1)/2048)
        x = i-1 - y*2048
    return x+1,y+1
end
function Convert:getTable(n,img)
    -- write some of the colors into a table
    local w,h = img.width, img.height
    local x,y,i
    local t = {}
    nmax = self:colorToInt( color(img:get(self:xy(n))) )
    if nmax==0 then t=nil
    else
        for i=0,nmax do
            t[i+1] = color(img:get( self:xy(i+n) ))
        end
    end
    return t,(n+nmax+1)
end
-- **********************************************
function Convert:supported(typ)
    local ans = false
    if     typ=="string" then ans = true
    elseif typ=="number" then ans = true
    elseif typ=="image"  then ans = true
    elseif typ=="{color}"then ans = true
    elseif typ=="{vec3}" then ans = true
    elseif typ=="{vec2}" then ans = true
    elseif typ=="matrix" then ans = true
    end
    return ans
end
function Convert:pushToTable(tag,obj,out)
    local x = obj[tag]
    local typ = self:type(x)
  if self:supported(typ) then
    local n = #out
    self:pushString(tag,out)
    insert(out, self:stringToColor("=") )
    self:pushString(typ,out)
    insert(out, self:stringToColor("=") )
    if     typ=="string" then self:pushString(x,out)
    elseif typ=="number" then self:pushNumber(x,out)
    elseif typ=="image"  then self:pushImage(x,out)
    elseif typ=="{color}"then self:pushColors(x,out)
    elseif typ=="{vec3}" then self:pushVec3(x,out)
    elseif typ=="{vec2}" then self:pushVec2(x,out)
    elseif typ=="matrix" then self:pushMatrix(x,out)
    end
    local n1 = #out - n 
    insert(out, n+1, self:intToColor(n1) )
  else
    print("the type of '"..tag.."' is not supported for saving: "..typ)
  end
    return out
end
function Convert:pushNumber(x,out)
    insert(out,self:floatToColor(x))
end
function Convert:pushMatrix(m,out)
    local i
    for i=1,16 do
        insert(out,self:floatToColor(m[i]))
    end
end
function Convert:pushVec2(t,out)
    local i,v
    for i,v in ipairs(t) do
        -- this is a texCoord, so it is transformed in (1-y)
        insert(out,self:floatToColor(v.x))
        insert(out,self:floatToColor(1.0-v.y))
    end
end
function Convert:pushVec3(t,out)
    local i,v
    for i,v in ipairs(t) do
        insert(out,self:floatToColor(v.x))
        insert(out,self:floatToColor(v.y))
        insert(out,self:floatToColor(v.z))
    end
end
function Convert:pushColors(t,out)
    local i,v,r,g,b,a
    for i,v in ipairs(t) do
        -- the color table is read from a mesh: it is normalized into [0.0;1.0]
        r,g,b,a = v.r, v.g, v.b, v.a
        r,g,b,a = floor(r*255),floor(g*255),floor(b*255),floor(a*255)
        v = color(r,g,b,a)
        insert(out,v)
    end
end
function Convert:pushImage(img,out)
    local w,h,x,y
    w,h = img.width, img.height
    insert(out,self:intToColor(w))  
    insert(out,self:intToColor(h))
    for x=1,w do
        for y=1,h do
            insert(out,color(img:get(x,y)))
        end
    end
end
function Convert:pushString(txt,out)
    local t1,t2
    if txt:len()>4 then
        t1 = txt:sub(1,4)
        t2 = txt:sub(5)
    else
        t1 = txt
    end
    insert(out,self:stringToColor(t1))
    if t2 then self:pushString(t2,out) end
end
    
-- **********************************************
function Convert:getString(t,n)
    local p = n+1
    while self:colorToString(t[p]) ~= "=" do p = p + 1 end
    local res = {}
    local i
--    for i=1,(p-n-1) do insert(res,t[n+i]) end
    for i=1,(p-n-1) do insert(res, self:colorToString(t[n+i]) ) end
    return table.concat(res,""),p
end
function Convert:popToObject(t,obj,debug)
    local nmax = self:colorToInt( t[1] ) + 1
    local n = 1
    local tag,typ
    tag,n = self:getString(t,n)
    typ,n = self:getString(t,n)
    local x,i
    local out
    if     typ=="string"  then out,n = self:popString(n,nmax,t)
    elseif typ=="number"  then out,n = self:popNumber(n,nmax,t)
    elseif typ=="image"   then out,n = self:popImage(n,nmax,t)
    elseif typ=="{color}" then out,n = self:popColors(n,nmax,t)
    elseif typ=="{vec3}"  then out,n = self:popVec3(n,nmax,t)
    elseif typ=="{vec2}"  then out,n = self:popVec2(n,nmax,t)
    elseif typ=="matrix"  then out,n = self:popMatrix(n,nmax,t)
    end
    obj[tag]=out
    debugPrint(debug,"pop : obj."..tag.." = ",out)
    return tag,n
end
function Convert:popNumber(n,nmax,t)
    local out 
        out = self:colorToFloat( t[1+n] )
    return out,n+1+1
end
function Convert:popMatrix(n,nmax,t)
    local i
    local out = matrix()
    for i=1,16 do 
        out[i] = self:colorToFloat( t[i+n] )
    end
    return out,n+16+1
end
function Convert:popVec2(n,nmax,t)
    local i=1
    local out = {}
    while i<(nmax-n) do 
        local v = vec2()
        v.x = self:colorToFloat( t[i+n] )
        v.y = self:colorToFloat( t[i+n+1] )
        out[(i+1)/2] = v
        i = i + 2
    end
    return out,n+i
end
function Convert:popVec3(n,nmax,t)
    local i=1
    local out = {}
    while i<(nmax-n) do 
        local v = vec3()
        v.x = self:colorToFloat( t[i+n] )
        v.y = self:colorToFloat( t[i+n+1] )
        v.z = self:colorToFloat( t[i+n+2] )
        out[(i+2)/3] = v
        i = i + 3
    end
    return out,n+i
end
function Convert:popColors(n,nmax,t)
    local i
    local out = {}
    for i=1,nmax-n do out[i] = t[i+n] end
    return out,n+nmax+1
end
function Convert:popImage(n,nmax,t)
    local w,h,x,y
    w = self:colorToInt(t[n+1])
    h = self:colorToInt(t[n+2])
    local out = image(w,h)
    local i = n+3
    for x=1,w do
        for y=1,h do
            out:set(x,y,t[i])
            i = i + 1
        end
    end
    return out,n+i
end
function Convert:popString(n,nmax,t)
    local i
    local out = {}
    for i=1,nmax-n do out[i] = self:colorToString(t[i+n]) end
    out = table.concat(out,"")
    return out,nmax+1
end
-- **********************************************
-- basic function to convert to and from colors
local abs = math.abs
local log10 = math.log10
local pow = math.pow
-- type(v):"nil", "number", "string", "boolean", "table", "function", "thread", "userdata".
function Convert:type(x)
    local txt = type(x)
    function typeCompare(x,ref)
    -- nb: returns an error if x or ref have no metatable!
    local i = getmetatable(x).__index
    local j = getmetatable(ref).__index
    return (i==j)
    end
    if txt == "userdata" then
        if     typeCompare(x,vec2())     then txt ="vec2" 
        elseif typeCompare(x,vec3())     then txt ="vec3" 
        elseif typeCompare(x,color())    then txt ="color" 
        elseif typeCompare(x,image(1,1)) then txt ="image" 
        elseif typeCompare(x,matrix())   then txt ="matrix" 
        end
    end
    if txt == "table" then
        txt = "{"..self:type(x[1]).."}"
    end
    return txt
end
function Convert:intToColor(dat)
    -- abs value coded on 4 octets uint (r,g,b,a)
    local r,g,b,a = 0,0,0,0
    local x
    -- chech no sign
    x = abs(dat)
    if x~=dat then error("only uint supported : "..dat) end
    -- convert in octet basis
    r = x % 256
    x = floor(x/256)
    g = x % 256
    x = floor(x/256)
    b = x % 256
    x = floor(x/256)
    a = x
    if a>255 then error("this integer is too big to save : "..dat) end
    return color(r,g,b,a)
end
function Convert:colorToInt(c)
    local r,g,b,a = c.r, c.g, c.b, c.a
    local i = r +256*(g + 256*(b +256*a))
    return i
end
function Convert:floatToColor(dat)
    -- abs value coded on 3 octets (r,g,b)
    -- a is coding the power of 10, from -64 to +63
    -- the sign is coded by 1 bit (+128) in a
    -- accuracy limit: about less than 10/(256*256*256) relative error
    local r,g,b,a = 0,0,0,0
    local x,sgn,p,c
    -- remove the sign and save it
    x = abs(dat)
    if x==dat then sgn = 0 else sgn = 128 end
    -- get the exponent
    if x==0 then p=0 else
        p = log10(x)
        p = floor(p) 
    end
    if (p<-64) or (63<p) then -- overflow
        -- in case of overflow clip the data
        if p<-64 then p=-64 else p=63 end
        r,g,b,a =255,255,255,127
    else
        -- convert to int
        x =  x / pow(10,p)/10 -- number x is now : 0.abcdefg with a from 1 to 9
        x = floor( x *255 * 256 * 256 )
        -- convert to octet basis
        c = self:intToColor(x)
        r,g,b,a = c.r, c.g, c.b, c.a
        if a>0 then error("bug: a should be 0") end
    end
    -- a saves sign and exponent
    a = (p + 64) + sgn
    return color(r,g,b,a)
end
function Convert:colorToFloat(c)
    local r,g,b,a = c.r, c.g, c.b, c.a
    local sgn
    if a >127 then sgn =-1 ; a=a-128 else sgn =1 end
    local p = a - 64
    local x = ((r/256 + g)/256 +b)/255 * pow(10,p) * 10 * sgn
    return x
end
function Convert:stringToColor(t)
    local r,g,b,a = 0,0,0,0
    local l = t:len()
    if l>0 then r = t:byte(1) end
    if l>1 then g = t:byte(2) end
    if l>2 then b = t:byte(3) end
    if l>3 then a = t:byte(4) end
    if l>4 then error("the text: "..t.." is too long (4 chars max).") end
    return color(r,g,b,a)
end
function Convert:colorToString(c)
    local r,g,b,a = c.r, c.g, c.b, c.a
    local txt = ""
    if a>0     then txt = string.char(r,g,b,a)
    elseif b>0 then txt = string.char(r,g,b)
    elseif g>0 then txt = string.char(r,g)
    elseif r>0 then txt = string.char(r)
    end
    return txt
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment