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