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