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
|