This adds .mtl loading and support for relative vertex numbers and other weird stuff. It has been able to handle everything I have thrown at it:
CreateMeshLoader(LoadMeshObj,"obj") Function LoadMeshObj:TEntity(path$) Local stream:TStream Local position:TBank=New TBank Local normal:TBank=New TBank Local texcoords:TBank=New TBank Local line:String[] Local indices:String[] Local sumindices:Int Local x#,y#,z#,s$,a,b,c,na,nb,nc Local nx#,ny#,nz#,tu#,tv#,v,m# Local currentsmoothgroup Local face:TObjFace,vert:TObjVert,face2:TObjFace Local n,avg#,n2 Local currentgroup Local currentmaterialfile$ Local materiallist:TObjMTL[] Local groupmaterial:TObjMTL[1024] stream=ReadFile(path) If Not stream Return Local mesh:TMesh=CreateMesh() While Not stream.Eof() s$=Trim(stream.ReadLine()) If s="" Continue If Left(s,1)="#" Continue line=s.split("") If line.length=0 Continue Select Lower(line[0]) Case "mtllib" If line.length>1 materiallist=ParseMTLLib(ExtractDir(path)+"/"+line[1]) EndIf Case "usemtl" currentgroup:+1 If line.length>1 For Local objmtl:TObjMTL=EachIn materiallist If objmtl.name.tolower()=line[1].tolower() groupmaterial[currentgroup]=objmtl Exit EndIf Next EndIf Case "v" If line.length<3 Return AppendFloat position,Float(line[1]) AppendFloat position,Float(line[2]) AppendFloat position,Float(line[3]) Case "vt" If line.length<2 Return AppendFloat texcoords,Float(line[1]) AppendFloat texcoords,-Float(line[2]) Case "vn" If line.length<3 Return nx=Float(line[1]) ny=Float(line[2]) nz=Float(line[3]) m=Sqr(nx*nx+ny*ny+nz*nz) nx:/m ny:/m nz:/m AppendFloat normal,nx AppendFloat normal,ny AppendFloat normal,nz Case "s" If line.length>1 currentsmoothgroup=Int(line[1]) EndIf Case "g" currentgroup:+1 Case "f" Local indice[line.length-1] Local verts:TObjVert[indice.length] face=New TObjFace face.verts=verts face.group=currentgroup face.smoothgroup=currentsmoothgroup For n=0 To indice.length-1 indices=line[n+1].split("/") tu=0 tv=0 nx=0 ny=0 nz=0 vert=New TObjVert v=Int(indices[0])-1 If v<0 v=position.size()/12+v+1 x#=position.PeekFloat(v*12+0) y#=position.PeekFloat(v*12+4) z#=position.PeekFloat(v*12+8) If indices.length>1 v=Int(indices[1])-1 If v<0 v=texcoords.size()/8+v+1 tu#=texcoords.PeekFloat(v*8+0) tv#=texcoords.PeekFloat(v*8+4) If indices.length>2 v=Int(indices[2])-1 If v<0 v=normal.size()/12+v+1 nx#=normal.PeekFloat(v*12+0) ny#=normal.PeekFloat(v*12+4) nz#=normal.PeekFloat(v*12+8) vert.normalsdefined=True EndIf EndIf vert.x=x vert.y=y vert.z=z vert.nx=nx vert.ny=ny vert.nz=nz vert.u=tu vert.v=tv face.verts[n]=vert Next 'For Local n=2 To indice.length-1 ' surf.addtriangle indice[n],indice[0],indice[n-1] ' sumindices:+3 'Next EndSelect Wend 'Calculate face normals Local norm:TVec3 For face=EachIn TObjFace.list If face.verts.length<3 Continue 'TriangleNormal(face.verts[0].x,face.verts[0].y,face.verts[0].z,face.verts[1].x,face.verts[1].y,face.verts[1].z,face.verts[2].x,face.verts[2].y,face.verts[2].z) norm=TPlane.FromTriangle(vec3(face.verts[0].x,face.verts[0].y,face.verts[0].z),vec3(face.verts[1].x,face.verts[1].y,face.verts[1].z),vec3(face.verts[2].x,face.verts[2].y,face.verts[2].z)).normal() face.nx=-norm.x face.ny=-norm.y face.nz=-norm.z For n=0 To face.verts.length-1 If Not face.verts[n].normalsdefined ' use hard edges for verts with undefined normal face.verts[n].nx=face.nx face.verts[n].ny=face.ny face.verts[n].nz=face.nz EndIf Next Next For face=EachIn TObjFace.list If face.smoothgroup For n=0 To face.verts.length-1 nx#=face.nx ny#=face.ny nz#=face.nz avg#=1.0 For face2=EachIn TObjFace.list' this part is highly inefficient, but don't give a fuck If face=face2 Continue If face.group<>face2.group Continue If face.smoothgroup<>face2.smoothgroup Continue For n2=0 To face2.verts.length-1 If Abs(face.verts[n].x-face2.verts[n2].x)>0.01 Continue If Abs(face.verts[n].y-face2.verts[n2].y)>0.01 Continue If Abs(face.verts[n].z-face2.verts[n2].z)>0.01 Continue nx:+face2.nx ny:+face2.ny nz:+face2.nz avg:+1 Exit 'n2=face2.verts.length-1 Next Next face.verts[n].nx=nx/avg face.verts[n].ny=ny/avg face.verts[n].nz=nz/avg Next EndIf Next Local badvertices=0 'Add triangles Local submesh:TSubmesh For Local g=1 To currentgroup submesh=Null For face=EachIn TObjFace.list If face.group<>g Continue 'Local s$="" If Not submesh submesh=CreateSubmesh(mesh) If groupmaterial[ g ] submesh.paint groupmaterial[ g ].material 'Notify groupmaterial[ g ].material.name EndIf EndIf For n=0 To face.verts.length-1 face.verts[n].index=submesh.surface.FindVertex(face.verts[n].x,face.verts[n].y,face.verts[n].z,face.verts[n].nx,face.verts[n].ny,face.verts[n].nz,face.verts[n].u,face.verts[n].v) 's:+n+", " If n>1 a=face.verts[n].index b=face.verts[n-1].index c=face.verts[0].index If (VertsMatchU(submesh.surface,a,b) And VertsMatchU(submesh.surface,a,c) And VertsMatchU(submesh.surface,b,c))=True Or (VertsMatchV(submesh.surface,a,b) And VertsMatchV(submesh.surface,a,c) And VertsMatchV(submesh.surface,b,c))=True ' Print "WARNING: Possible mesh error (identical texcoords in triangle)" badvertices:+1 EndIf If a=b Or b=c Or a=c mesh.destroy() Return ' Notify "MESH ERROR" EndIf submesh.surface.addtriangle a,c,b sumindices:+3 EndIf Next Next If submesh 'If submesh.surface.counttriangles()>1024 submesh.surface.drawinstanced=0 EndIf Next mesh.update() mesh.lock() TObjFace.list=New TList 'Print badvertices+" bad triangles." Return mesh Function VertsMatchU%(surf:TSurface,a,b,e#=0.000001) If Abs(surf.vertexu(a)-surf.vertexu(b))>e Return False Return True EndFunction Function VertsMatchV%(surf:TSurface,a,b,e#=0.000001) If Abs(surf.vertexv(a)-surf.vertexv(b))>e Return False Return True EndFunction Function AppendFloat(bank:TBank,value#) Local size size=bank.size() bank.resize size+4 bank.PokeFloat size,value EndFunction EndFunction Private Type TObjVert Field x#,y#,z# Field nx#,ny#,nz# Field u#,v# Field index Field normalsdefined EndType Type TObjFace Global list:TList=New TList Method New() list.addfirst(Self) EndMethod Field nx#,ny#,nz# Field verts:TObjVert[] Field smoothgroup Field group EndType Type TObjMTL Field name:String 'Field Brush:TBrush Field Texture:TTexture Field material:TMaterial EndType Function ParseMTLLib:TObjMTL[](Path:String) Local matStream:TStream = ReadStream(Path) Local dir:String = ExtractDir(Path) + "/" If dir = "/" Then dir = "" If Not matStream Then Return Null Local MatLib:TObjMtl[0] Local CMI:Int = -1 While Not Eof(matStream) Local Line:String = ReadLine(MatStream) If Line[0..7] = "newmtl " Then MatLib = MatLib[..Matlib.Length + 1] CMI = MatLib.Length-1 MatLib[CMI] = New TObjMtl MatLib[CMI].Name = Line[7..].Trim() 'MatLib[CMI].Brush = CreateBrush() 'BrushFX MatLib[CMI].Brush,4+16 'DebugLog("Matname : " + Matlib[CMI].Name) EndIf 'Colours If Line[0..3] = "Kd " Then Local Data:String = Line[3..].Trim()+" " Local F:Float[3] For Local I:Int = 0 To 2 'Print "Before : " + Data Local FL:Int = Data.Find(" ") If I < 2 Then f[I] = Float(Data[..FL]) Else f[i] = Float(Data) EndIf Data = Data[FL+1..] 'Print "After : " + Data Next 'BrushColor(MatLib[CMI].Brush , F[0] * 255 , F[1] * 255 , F[2] * 255) 'DebugLog("MatColor : " + (F[0] * 255) +","+(F[1] * 255)+","+(F[2] * 255)) EndIf If Line[0..2] = "d " Then 'BrushAlpha(MatLib[CMI].Brush , Float(Line[2..])) 'DebugLog("MatAlpha : " + Float(Line[2..]) ) EndIf If Line[0..3] = "Tr " Then 'BrushAlpha(MatLib[CMI].Brush , Float(Line[2..])) 'DebugLog("MatAlpha : " + Float(Line[2..]) ) EndIf If Line[0..7] = "map_Kd " Then 'MatLib[CMI].Texture = LoadTexture(dir+Line[7..].Trim()) 'Notify dir+Line[7..].Trim() 'MatLib[CMI].Material = LoadMaterial(dir+Line[7..].Trim()) 'If Not MatLib[CMI].Material MatLib[CMI].Material = LoadMaterial(Line[7..].Trim()) 'If Not MatLib[CMI].Material MatLib[CMI].Material = LoadMaterial( StripAll( Line[7..].Trim()) ) 'If MatLib[CMI].Texture <> Null BrushTexture(MatLib[CMI].Brush , MatLib[CMI].Texture) 'DebugLog("MatTexture : " + Line[7..].Trim() ) EndIf Wend Return MatLib End Function Public