Wavefront (OBJ) Loader
From GiderosMobile
Here you will find various resources to help you create 3D games and apps in Gideros Studio.
note: you may have to provide your own assets (fonts, gfx, …).
Wavefront (OBJ) Loader
Import your Wavefront OBJ models in Gideros.
--[[
Load meshes from a wavefront .obj file
Usage:
sprite = loadObj(path, file) : load the file located at path/file
Returned sprite has a few specific attributes:
- objs: table referencing all objects within the loaded file
]]
local function Split(str, delim, maxNb)
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
if maxNb == nil or maxNb < 1 then
maxNb = 0 -- no limit
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gfind(str, pat) do
nb += 1
result[nb] = part
lastPos = pos
if nb == maxNb then break end
end
-- Handle the last field
if nb ~= maxNb then
result[nb + 1] = string.sub(str, lastPos)
end
return result
end
local function parsemtl(mtls, path, file, texture_replacements, texture_folder, texture)
local mtl = { texturew = 0, textureh = 0 }
for line in io.lines(path.."/"..file) do
fld = Split(line," ",6)
for i = 1,#fld, 1 do
fld[i] = string.gsub(fld[i], "\r", "")
end
if (fld[2] ~= nil) then
fld[2] = string.gsub(fld[2], "\r", "")
end
if fld[1] == "newmtl" then
--print("DM",fld[2])
mtl = {texturew = 0, textureh = 0}
mtls[fld[2]] = mtl
elseif fld[1] == "Kd" then
mtl.kd = {fld[2],fld[3],fld[4],1.0}
elseif fld[1] == "map_Kd" then
table.remove(fld, 1)
local f = table.concat(fld," ")
--print("Texture:.. ["..path.."/"..f.."]")
if texture_replacements then
for i = 1, #texture_replacements do
if texture_replacements[i][1] == f then
f = texture_replacements[i][2] -- .. " "
end
end
end
if texture then
mtl.texture = texture
--print("Applying existing texture to material.")
elseif texture_folder then
mtl.texture = Texture.new(texture_folder.."/"..f, true,{ wrap = TextureBase.REPEAT })
else
mtl.texture = Texture.new(path.."/"..f, true,{ wrap = TextureBase.REPEAT })
end
mtl.texturew = mtl.texture:getWidth()
mtl.textureh = mtl.texture:getHeight()
elseif fld[1] == "map_Bump" then
table.remove(fld, 1)
local f = table.concat(fld," ")
--print("Texture:.. ["..path.."/"..f.."]")
mtl.normalMap = Texture.new(path.."/"..f, true,{ wrap = TextureBase.REPEAT })
mtl.normalMapW = mtl.normalMap:getWidth()
mtl.normalMapH = mtl.normalMap:getHeight()
end
end
end
function string.starts(String, Start)
return string.sub(String, 1, string.len(Start)) == Start
end
function string.ends(String, End)
return End == '' or string.sub(String, -string.len(End)) == End
end
function loadObj(path, file, texture_replacements, texture_folder, texture)
local spr = Sprite.new()
v = {}
imap = nil
vt = {}
vn = {}
mtls = {}
mtl = nil
spr.min = {1000000, 1000000, 1000000}
spr.max = {-1000000,-1000000,-1000000}
spr.objs = {}
oname = nil
local function buildObject()
local m = nil
if (imap ~= nil) then
for _, vm in pairs(imap.vngmap) do
local nx, ny, nz = 0, 0, 0
for _, vn in ipairs(vm) do
nx = nx+imap.lvn[vn+1]
ny = ny+imap.lvn[vn+2]
nz = nz+imap.lvn[vn+3]
end
local nl = math.sqrt(nx*nx+ny*ny+nz*nz)
nx = nx/nl
ny = ny/nl
nz = nz/nl
for _, vn in ipairs(vm) do
imap.lvn[vn+1] = nx
imap.lvn[vn+2] = ny
imap.lvn[vn+3] = nz
end
end
m = Mesh.new(true)
m:setVertexArray(imap.lv)
m:setIndexArray(imap.i)
--m:setColorArray(c)
if (mtl.texture ~= nil) then
m:setTextureCoordinateArray(imap.lvt)
m:setTexture(mtl.texture)
m.hasTexture = true
end
if (mtl.normalMap ~= nil) then
m:setTexture(mtl.normalMap, 1)
m.hasNormalMap = true
end
if mtl.kd then
m:setColorTransform(mtl.kd[1],mtl.kd[2],mtl.kd[3],mtl.kd[4])
end
if #imap.lvn > 0 then
m.hasNormals = true
m:setGenericArray(3, Shader.DFLOAT, 3,#imap.lvn/3, imap.lvn)
end
local sobj = spr.objs[oname]
if sobj == nil then
sobj = Sprite.new()
spr:addChild(sobj)
spr.objs[oname] = sobj
sobj.min = {1000000, 1000000, 1000000}
sobj.max = {-1000000,-1000000,-1000000}
end
sobj:addChild(m)
local minx, miny, minz = 100000, 100000, 100000
local maxx, maxy, maxz = -100000,-100000,-100000
for i = 1,#imap.lv-2, 3 do
local x, y, z = imap.lv[i],imap.lv[i+1],imap.lv[i+2]
minx = math.min(minx, x)
miny = math.min(miny, y)
minz = math.min(minz, z)
maxx = math.max(maxx, x)
maxy = math.max(maxy, y)
maxz = math.max(maxz, z)
end
m.min = {minx, miny, minz}
m.max = {maxx, maxy, maxz}
m.center = {(m.max[1]+m.min[1])/2,(m.max[2]+m.min[2])/2,(m.max[3]+m.min[3])/2}
sobj.min = {math.min(sobj.min[1],m.min[1]),math.min(sobj.min[2],m.min[2]),math.min(sobj.min[3],m.min[3])}
sobj.max = {math.max(sobj.max[1],m.max[1]),math.max(sobj.max[2],m.max[2]),math.max(sobj.max[3],m.max[3])}
spr.min = {math.min(spr.min[1],m.min[1]),math.min(spr.min[2],m.min[2]),math.min(spr.min[3],m.min[3])}
spr.max = {math.max(spr.max[1],m.max[1]),math.max(spr.max[2],m.max[2]),math.max(spr.max[3],m.max[3])}
m.name = oname
--[[print(oname, m.min[1],m.min[2],m.min[3],m.max[1],m.max[2],m.max[3])
if string.starts(oname,"light") then
lightPosX, lightPosY, lightPosZ = m.center[1],m.center[2],m.center[3]
lightRef = m
end]]
imap = nil
end
return m
end
for line in io.lines(path.."/"..file) do
fld = Split(line," ")
for i = 1,#fld, 1 do
fld[i] = string.gsub(fld[i], "\r", "")
end
if fld[1] == "v" then
table.insert(v, tonumber(fld[2]))
table.insert(v, tonumber(fld[3]))
table.insert(v, tonumber(fld[4]))
elseif fld[1] == "vn" then
table.insert(vn, tonumber(fld[2]))
table.insert(vn, tonumber(fld[3]))
table.insert(vn, tonumber(fld[4]))
elseif fld[1] == "vt" then
table.insert(vt, tonumber(fld[2]))
table.insert(vt, tonumber(fld[3]))
elseif fld[1] == "f" then
if imap == nil then
imap = {}
imap.alloc = function(self, ifld, facenm)
local ifl = Split(ifld,"/",3)
local iv = tonumber(ifl[1])
if (iv<0) then iv = (#v/3+1+iv) end
iv = iv-1
local it = tonumber(ifl[2])
if (it == nil) then
it = -1
else
if (it<0) then it = (#vt / 2) + it + 1 end
it -= 1
end
local inm = tonumber(ifl[3])
if (inm == nil) then
inm = -1
else
if (inm<0) then
inm = (#vn / 3) + inm + 1
end
inm -= 1
end
if inm == -1 then inm = facenm.code end
local ms = iv..":"..it..":"..inm
if self.vmap[ms] == nil then
ni = self.ni + 1
self.ni = ni
table.insert(facenm.lvi,#self.lv)
table.insert(self.lv, v[iv*3+1])
table.insert(self.lv, v[iv*3+2])
table.insert(self.lv, v[iv*3+3])
if it >= 0 then
table.insert(self.lvt, vt[it*2+1]*mtl.texturew)
table.insert(self.lvt, vt[it*2+2]*mtl.textureh)
end
if inm >= 0 then
table.insert(self.lvn, vn[inm*3+1])
table.insert(self.lvn, vn[inm*3+2])
table.insert(self.lvn, vn[inm*3+3])
else
local vngmap = self.vngmap[iv] or { }
self.vngmap[iv] = vngmap
table.insert(vngmap,#self.lvn)
table.insert(facenm.lvni,#self.lvn)
table.insert(self.lvn, 0)
table.insert(self.lvn, 0)
table.insert(self.lvn, 0)
end
self.vmap[ms] = ni
end
return self.vmap[ms]
end
imap.i = {}
imap.ni = 0
imap.lv = {}
imap.lvt = {}
imap.lvn = {}
imap.vmap = {}
imap.vngmap = {}
imap.gnorm = -2
end
local itab = {}
local normtab = { code = imap.gnorm, lvi = {}, lvni = {} }
for ii = 2,#fld, 1 do
if (fld[ii] ~= nil) and (fld[ii] ~= "") then
table.insert(itab, imap:alloc(fld[ii],normtab))
end
end
imap.gnorm = imap.gnorm-1
if (#itab >= 3) then
if #normtab.lvni > 0 then -- Gen normals
local ux = imap.lv[normtab.lvi[2]+1]-imap.lv[normtab.lvi[1]+1]
local uy = imap.lv[normtab.lvi[2]+2]-imap.lv[normtab.lvi[1]+2]
local uz = imap.lv[normtab.lvi[2]+3]-imap.lv[normtab.lvi[1]+3]
local vx = imap.lv[normtab.lvi[3]+1]-imap.lv[normtab.lvi[1]+1]
local vy = imap.lv[normtab.lvi[3]+2]-imap.lv[normtab.lvi[1]+2]
local vz = imap.lv[normtab.lvi[3]+3]-imap.lv[normtab.lvi[1]+3]
local nx = uy*vz-uz*vy
local ny = uz*vx-ux*vz
local nz = ux*vy-uy*vx
local nl = math.sqrt(nx * nx + ny * ny + nz * nz)
nx = nx/nl
ny = ny/nl
nz = nz/nl
for _, vni in ipairs(normtab.lvni) do
imap.lvn[vni+1] = nx
imap.lvn[vni+2] = ny
imap.lvn[vni+3] = nz
end
end
for ii = 3,#itab, 1 do
table.insert(imap.i, itab[1])
table.insert(imap.i, itab[ii-1])
table.insert(imap.i, itab[ii])
end
end
elseif fld[1] == "o" or fld[1] == "g" then
buildObject()
--print(line)
oname = fld[2]
elseif fld[1] == "mtllib" then
table.remove(fld, 1)
parsemtl(mtls, path, table.concat(fld," "), texture_replacements, texture_folder, texture)
elseif fld[1] == "usemtl" then
buildObject()
mtl = mtls[fld[2]]
end
end
--spr:setColorTransform(1.0, 0, 0, 1.0)
buildObject() --If any in progress
spr.center = {(spr.max[1]+spr.min[1])/2,(spr.max[2]+spr.min[2])/2,(spr.max[3]+spr.min[3])/2}
return spr
end