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 (630 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