Code archives/3D Graphics - Mesh/.obj loader for minib3d
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| This is one of some loaders I'm working on besides other things on minib3d. This is a wavefront .obj loader. Currently I have disabled some material settings (ALpha eg) and also the mesh is loaded with Brushflag 16 (doublesided rendering) I hope you find it useful. Please inform me about bugs you find. | |||||
SuperStrict
Framework klepto.minib3d
Graphics3D 800 , 600 , 0 , 2
Local Cam:TCamera = CreateCamera()
CameraClsColor Cam , 0 , 0 , 255
PositionEntity Cam,0,0,-10
Local Light:TLight = CreateLight(1)
Local File:String = RequestFile(".Obj file auswählen","obj files:obj",,CurrentDir())
Local Test:TMesh = LoadObj(File)
Local Wire:Byte = False
While Not KeyHit(KEY_ESCAPE)
TurnEntity Test , .03 , 0 , .03
PositionEntity Light , EntityX(Cam) , EntityY(Cam) , EntityZ(Cam)
PointEntity Light,Test
Wireframe(Wire)
If KeyHit(Key_W) Then Wire = Not Wire
If KeyDown(KEY_UP) Then MoveEntity cam,0,0,1.0*TGlobal.GetDelta()'RY:+0.5
If KeyDown(KEY_DOWN) Then MoveEntity cam,0,0,-1.0*TGlobal.GetDelta()
If KeyDown(KEY_RIGHT) Then MoveEntity cam,1.0*TGlobal.GetDelta(),0,0
If KeyDown(KEY_LEFT) Then MoveEntity cam,-1.0*TGlobal.GetDelta(),0,0
RenderWorld
UpdateWorld
Flip 0
Wend
Function LoadObj:TMesh(url:String)
Local Stream:TStream = ReadStream(url)
If Not Stream Then Return CreateCube()
Local matlibs:TMap = CreateMap()
Local VertexP:TObjVertex[60000]
Local VertexN:TObjNormal[60000]
Local VertexT:TObjTexCoord[60000]
Local Faces:TFaceData[60000]
Local gname:String = ""
Local snumber:Int = -1
Local curmtl:String = ""
Local Readface:Byte = True
Local dir:String = ExtractDir(url) + "/"
If dir = "/" Then dir = ""
'Print dir
Local VC:Int = 0
Local VN:Int = 0
Local VT:Int = 0
Local FC:Int = 0
Local SC:Int = 0
DebugLog "File " + url + " found !!!"
Local Mesh:TMesh = CreateMesh()
Local Surface:TSurface '= CreateSurface(Mesh)
While Not Eof(Stream)
Local Line:String = ReadLine(Stream).Trim()
If Line <> "" Then
If Line[0] = Asc("#") Then
DebugLog(".Obj Comment : " + Line)
Else
'DebugLog("Line : " + Line[0..2] + "-!")
If Line[0..2].tolower() = "v " Then
VertexP[VC] = New TObjVertex
VertexP[VC].GetValues(Line[2..])
VC:+1
EndIf
If Line[0..3].toLower() = "vn " Then
VertexN[VN] = New TObjNormal
VertexN[VN].GetValues(Line[3..])
VN:+1
EndIf
If Line[0..3].toLower() = "vt " Then
VertexT[VT] = New TObjTexCoord
VertexT[VT].GetValues(Line[3..])
VT:+1
EndIf
If Line[0..2].toLower() = "g " Then GName = Line[3..].tolower()
If Line[0..2].toLower() = "s " Then
snumber = Int(Line[3..])
Surface = CreateSurface(Mesh)
' EntityFX Mesh,16
SC:+1
EndIf
'If Line[0..7].toLower() = "usemtl " Then curmtl = Line[7..].tolower()
If Line[0..7].toLower() = "mtllib " Then
Local L:TObjMtl[] = ParseMTLLib(dir+Line[7..])
For Local Obj:TObjMtl = EachIn L
MapInsert(Matlibs , Obj.Name , Obj)
Next
EndIf
If Line[0..7] = "usemtl " Then
Local Obj:TObjMtl = TObjMtl(MapValueForKey(MatLibs , Line[7..].Trim() ) )
Print Line[7..]
If Obj <> Null Then
If Not surface Then Surface = CreateSurface(Mesh)
PaintSurface(Surface , Obj.Brush)
Print "Surface Painted with " + Obj.name
EndIf
EndIf
If Line[0..2].tolower() = "f " Then
'Print its
If Surface = Null Then Surface = CreateSurface(Mesh)
If Surface Then
Local V:TFaceData[] = ParseFaces(Line[2..])
For Local i2:Int = 2 To V.Length - 1
Local V0:Int = AddVertex(Surface , VertexP[V[0].T[0]].X , VertexP[V[0].T[0]].Y ,- VertexP[V[0].T[0]].Z)',VertexT[V[0].T[1]].U,VertexT[V[0].T[1]].V)
Local V1:Int = AddVertex(Surface , VertexP[V[i2-1].T[0]].X , VertexP[V[i2-1].T[0]].Y ,- VertexP[V[i2-1].T[0]].Z)',VertexT[V[1].T[1]].U,VertexT[V[1].T[1]].V)
Local V2:Int = AddVertex(Surface , VertexP[V[i2].T[0]].X , VertexP[V[i2].T[0]].Y ,- VertexP[V[i2].T[0]].Z)',VertexT[V[2].T[1]].U,VertexT[V[2].T[1]].V)
If VertexN[0] <> Null
VertexNormal Surface , V0 , VertexN[V[0].T[2]].NX , VertexN[V[0].T[2]].NY , VertexN[V[0].T[2]].NZ
VertexNormal Surface , V1 , VertexN[V[i2-1].T[2]].NX , VertexN[V[i2-1].T[2]].NY , VertexN[V[i2-1].T[2]].NZ
VertexNormal Surface , V2 , VertexN[V[i2].T[2]].NX , VertexN[V[i2].T[2]].NY , VertexN[V[i2].T[2]].NZ
EndIf
If VertexT[0] <> Null
VertexTexCoords Surface , V0 , VertexT[V[0].T[1]].U ,1- VertexT[V[0].T[1]].V
VertexTexCoords Surface , V1 , VertexT[V[i2-1].T[1]].U ,1- VertexT[V[i2-1].T[1]].V
VertexTexCoords Surface , V2 , VertexT[V[i2].T[1]].U , 1 - VertexT[V[i2].T[1]].V
EndIf
AddTriangle Surface , V0 , V2 , V1
Next
FC:+1
EndIf
EndIf
EndIf
EndIf
Wend
DebugLog "VertexCount : " + VC
DebugLog "NormalsCount : " + VN
DebugLog "TexCoordsCount : " + VT
DebugLog "Faces : " + FC
DebugLog "Surfs : " + SC
DebugLog "surfs real : " + CountSurfaces(Mesh)
For Local V:TObjMtl = EachIn MatLibs.Values()
Print V.Name
Next
CloseStream(Stream)
'FlipMesh Mesh
UpdateNormals(Mesh)
Return Mesh
End Function
Type TFaceData
Field T:Int[3]
Field its:Int
Method GetValues:String(Data:String)
'Print Data
Local F:Int[3]
For Local I:Int = 0 To 2
'Print "Before : " + Data
Local FL:Int = Data.Find("/")
If I < 2 Then
T[I] = Int(Data[..FL])-1
Data = Data[FL+1..]
Else
T[i] = Int(Data[..Data.Find(" ")])-1
EndIf
'Print "After : " + Data
Next
'Print Data
Return Data[Data.Find(" ")..]
End Method
End Type
Function ParseFaces:TFaceData[](Data:String)
Local Data1:String[] = Data.Split(" ")
Local S:Int = 0
If Data1[0] = "" Then S = 1
Local FData:TFaceData[Data1.Length-S]
For Local I:Int = S To Data1.Length - 1
FData[I-S] = New TFaceData
Local D2:String[] = Data1[I].Split("/")
'DebugLog "~q"+D2[1]+"~q"
FData[I-S].T[0] = Int(D2[0])-1
FData[I-S].T[1] = Int(D2[1])-1
FData[I-S].T[2] = Int(D2[2])-1
Next
Return FData
End Function
Type TObjNormal
Field NX# , NY# , NZ#
Method GetValues(Data:String)
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
NX = F[0]
NY = F[1]
NZ = F[2]
'DebugLog ("X:"+X+" Y:"+Y + " Z:"+Z)
End Method
End Type
Type TObjTexCoord
Field U# , v#
Method GetValues(Data:String)
'DebugLog "OrigUV : " + Data
Local F:Float[2]
For Local I:Int = 0 To 1
'Print "Before : " + Data
Local FL:Int = Data.Find(" ")
If I < 1 Then
f[I] = Float(Data[..FL])
Else
f[i] = Float(Data)
EndIf
Data = Data[FL+1..]
'Print "After : " + Data
Next
u = F[0]
v = F[1]
'DebugLog ("X:"+U+" Y:"+V)
End Method
End Type
Type TObjVertex
Field X# , Y# , Z#
'Field NX# , NY# , NZ#
'Field u# , v#
Method GetValues(Data:String)
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
X = F[0]
Y = F[1]
Z = F[2]
'DebugLog ("X:"+X+" Y:"+Y + " Z:"+Z)
End Method
End Type
Type TObjMTL
Field name:String
Field Brush:TBrush
Field Texture:TTexture
End Type
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(),4)
If MatLib[CMI].Texture <> Null BrushTexture(MatLib[CMI].Brush , MatLib[CMI].Texture)
DebugLog("MatTexture : " + Line[7..].Trim() )
EndIf
Wend
Return MatLib
End Function |
Comments
| ||
| Love it, thanks! Works with Lightwave9 and 3DCoat. One small problem tho in Lightwave 9, it seems it does not always export texture vertex numbers, leaving a "f 41//32..." strange, but it drops out with an undefined array error. Easy fix, just check for negative vertex numbers. I assigned to vertex 0, maybe not the best but it handles fine for what I need. THE PROBLEM: some OBJ files use negative vertex numbers to move back up the vertex list. Also found an error: If Line[0..2].toLower() = "g " Then GName = Line[2..].toLower() Another error with "s" but I've also taken the liberty to create a surface cache to consolidate the surfaces, since Lightwave will separate the surfaces. add this before the While not EOF stream: Local surfaceCache:Int[] = New Int[255] then |
Code Archives Forum