Created
November 25, 2012 19:11
-
-
Save JMV38/4144835 to your computer and use it in GitHub Desktop.
Convert tab
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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