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.

file: Media:3DObjLoaderV2.lua <syntaxhighlight lang="lua"> --[[ 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 </source>