Page 1 of 1

wavefront OBJ files Loader

Posted: Wed May 03, 2017 7:09 am
by applePi
a basic OBJ reader will read coordinates of vertex, normal, textures and indices. it does not read the associated material files sometimes associated with OBJ files
test it with any Obj file you have or with these collection of OBJ files
Righ click to be able to click on the button to read another OBJ file
http://s000.tinyupload.com/?file_id=990 ... 9989198972
Or
http://www.mediafire.com/file/hgcn6rdso ... _Loader.7z

try to load hairball.obj from
http://graphics.williams.edu/data/meshes.xml
it is a gigantic mesh (200 MB) with Triangles: 2,880,000 and Vertices: 1,441,098 but the program will read a part of the mesh to be able to view acceptable hairball in 15 seconds on my computer

the first demo use Lines (#PB_Mesh_LineList) so it will show the polygons such as Quads, look the woman.obj inside OBJ_Loader.7z package
EDITED:: added remove the double spaces in some OBJ files.

Code: Select all

;DisableDebugger
Global CameraSpeed.f = 0.02

Declare ParseString(st.s)
Declare MakeMesh()
Declare ChangeMesh()

Global Dim MeshData.PB_MeshVertex(0) ; for centering the mesh only

Define.f KeyX, KeyY, MouseX, MouseY
Quit.b = #False
#but = 10

Global.s path
Define.f x, y, z, nx, ny, nz, u, v
Global vert.l
Global tri.l, i.l, IndicesPerLine.l
Global st.s
Global.f NormX, NormY, NormZ
Global IndicesPerLine
Global vertices = 0
tri = 0
vert = 0
Global maxVertices = 300000
Global maxFaces = 500000
Global textured = 0
Global normalized = 0
IndicesPerLine = 0


Global Dim x.f(maxVertices) ;vertex coordinates
Global Dim y.f(maxVertices)
Global Dim z.f(maxVertices) 
Global Dim nx.f(maxVertices)
Global Dim ny.f(maxVertices)
Global Dim nz.f(maxVertices)
Global Dim u.f(maxVertices)
Global Dim v.f(maxVertices)
Global Dim t.l(100)
Global Dim f.s(50)
Global Dim vt(50)
Global Dim vn(50)

ExamineDesktops()
OpenWindow(3, 0, 0, DesktopWidth(0), DesktopHeight(0), "OBJ  Loader ,  R_Click Show mouse cursor ..... W: Wire/Solid. ...Mouse+Arrow keys: Camera. .. .Space: stop rotation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

;Initialize environment
InitEngine3D()
InitSprite()
InitMouse()

OpenWindowedScreen(WindowID(3), 50, 0, DesktopWidth(0), DesktopHeight(0), 0, 0, 0)
ButtonGadget(#but, 0, 10, 50, 60, "Open File")
InitKeyboard()

Add3DArchive(".", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Textures", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Scripts",#PB_3DArchive_FileSystem)
Parse3DScripts()

path = OpenFileRequester("Choose file to load", "", "OBJ-Mesh (*.obj)|*.obj", 0, 0)
Add3DArchive(GetPathPart(path), #PB_3DArchive_FileSystem)
If path = "":End:EndIf
;OpenFile(1, GetFilePart(path))
OpenFile(1, path)
;Debug GetPathPart(path)
MakeMesh() ; read OBJ file lines
  
rot = 1
ExamineKeyboard()

Repeat
  If MouseButton(#PB_MouseButton_Right)
    flag = 1: ReleaseMouse(1)
  EndIf
  
  If flag = 0
  If ExamineMouse()
      MouseX = -MouseDeltaX() * 0.1 * 0.5
      MouseY = -MouseDeltaY() * 0.1 * 0.5
  EndIf
  EndIf

  Repeat
    Event = WindowEvent()
  
  Select event
        Case #PB_Event_Gadget
          Select EventGadget()
                      
            Case #but
              FreeEntity(0)
              FreeMesh(0)
              FreeTexture(0)
              
              For i=0 To maxVertices
              x(i)=0:y(i)=0:z(i)=0:nx(i)=0:ny(i)=0:nz(i)=0:u(i)=0:v(i)=0
              Next
              For i=0 To 50:f(i)="":Next
              tri = 0: vert = 0: textured = 0: normalized = 0: vertices = 0: indicesPerLine = 0
              path = OpenFileRequester("Choose file to load", "", "OBJ-Mesh (*.obj)|*.obj", 0, 0)
              Add3DArchive(GetPathPart(path), #PB_3DArchive_FileSystem)
              If path = "":End:EndIf

              ;OpenFile(1, GetFilePart(path))
              OpenFile(1, path)
              SetActiveGadget(-1)
              flag = 0: ReleaseMouse(0)
              Dim MeshData.PB_MeshVertex(0)
              MakeMesh()
                                          
          EndSelect
          
      EndSelect
      Until event = 0

      If ExamineKeyboard()
        
        If KeyboardPushed(#PB_Key_Left)
          KeyX = -CameraSpeed
        ElseIf KeyboardPushed(#PB_Key_Right)
          KeyX = CameraSpeed
        Else
          KeyX = 0
        EndIf
        
        If KeyboardPushed(#PB_Key_Up)
          KeyY = -CameraSpeed
        ElseIf KeyboardPushed(#PB_Key_Down)
          KeyY = CameraSpeed
        Else
          KeyY = 0
        EndIf
        
        If KeyboardReleased(#PB_Key_W)
          wire ! 1
          If wire
            MaterialShadingMode(0, #PB_Material_Wireframe)
          Else
            MaterialShadingMode(0, #PB_Material_Solid) 
          EndIf  
        EndIf
        
        If KeyboardReleased(#PB_Key_S)
          SaveMesh(0, "test.mesh")
        EndIf
                
        If KeyboardReleased(#PB_Key_Space)
          rot ! 1
        EndIf
               
          
      EndIf
      
      RotateEntity(0, 0, 0.5*rot, 0, #PB_Relative)
      
      RotateCamera(0, MouseY, MouseX, 0, #PB_Relative)
      MoveCamera  (0, KeyX, 0, KeyY)
           
   RenderWorld()
   FlipBuffers()
   
   
   If KeyboardReleased(#PB_Key_Escape)
     Quit = #True
   EndIf
  Until Quit = #True Or Event = #PB_Event_CloseWindow


Procedure ParseString(st.s)
  Protected.f ax,ay,az,bx,by,bz
  Dim f.s(50)
  Dim vt(50)
  st = RemoveString(st, "f")
  st = Trim(st)
  k = CountString(st, " ") + 1 
  
  For i=1 To k+1
    f(i) = StringField(st, i, " ")
  Next
  
  For i=1 To k+1
    t(i) = Val(StringField(f(i), 1, "/"))
    vt(i) = Val(StringField(f(i), 2, "/"))
  Next

  ;vn1 = Val(StringField(f(1), 3, "/"))
  ;vn2 = Val(StringField(f(2), 3, "/"))
  ;vn3 = Val(StringField(f(3), 3, "/"))
  
   ax= x(t(2))-x(t(1))
   ay= y(t(2))-y(t(1))
   az= z(t(2))-z(t(1))
   bx= x(t(3))-x(t(1))
   by= y(t(3))-y(t(1))
   bz= z(t(3))-z(t(1))
   NormX = (ay*bz)-(az*by)
   NormY = (az*bx)-(ax*bz)
   NormZ = (ax*by)-(ay*bx)
  For i=1 To k-1
    ;draw one line
    MeshVertexPosition(x(t(i)),y(t(i)),z(t(i)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i)), v(vt(i)))
    MeshVertexPosition(x(t(i+1)),y(t(i+1)),z(t(i+1)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i+1)), v(vt(i+1)))
   
  Next
    ;draw last line in the Polygon
    MeshVertexPosition(x(t(k)),y(t(k)),z(t(k)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(k)), v(vt(k)))
    MeshVertexPosition(x(t(1)),y(t(1)),z(t(1))) ; it close to vertex 1
    MeshVertexNormal(NormX,NormY,NormZ)
    
       
  For n=0 To 100
    t(n)=0
  Next
  
EndProcedure

Procedure MakeMesh()


CreateMesh(0, #PB_Mesh_LineList, #PB_Mesh_Static )

While Not Eof(1)
  
  ln$ = ReadString(1)
  ;ln$ = ReplaceString(ln$, "  ", " ")
  While FindString(ln$, "  ") > 0
   ln$ = ReplaceString(ln$, "  ", " ")
  Wend
  If Left(ln$, 2) = "vt"
    textured = 1: normalized = 1
    vert + 1
    u(vert) = ValF(StringField(ln$, 2, " "))
    v(vert) = ValF(StringField(ln$, 3, " "))
    
  EndIf
  If vert >= maxVertices:Break:EndIf 
Wend 
vert = 0

FileSeek(1,0)

While Not Eof(1)
  
  ln$ = ReadString(1)
  ;ln$ = ReplaceString(ln$, "  ", " ")
  ;ln$ = ReplaceString(ln$, "  ", " ")
  While FindString(ln$, "  ") > 0
   ln$ = ReplaceString(ln$, "  ", " ")
  Wend
  t+1
  If Left(ln$, 2) = "v "
    vertices = vertices+1
    ;ReDim x(vertices): ReDim y(vertices): ReDim z(vertices)
    x(vertices) = ValF(StringField(ln$, 2, " "))
    y(vertices) = ValF(StringField(ln$, 3, " "))
    z(vertices) = ValF(StringField(ln$, 4, " "))
    ;MeshVertexPosition(x(vertices), y(vertices), z(vertices))
  ElseIf Left(ln$, 2) = "vn"
    normalized = 1
    vert + 1
    ;If vert = maxVert:Break:EndIf
    nx(vert) = ValF(StringField(ln$, 2, " "))
    ny(vert) = ValF(StringField(ln$, 3, " "))
    nz(vert) = ValF(StringField(ln$, 4, " "))
  
  EndIf    
 If vertices >= maxVertices:Break:EndIf   
Wend


FileSeek(1,0)
tri = 0: i =0
Global.f ax,ay,az,bx,by,bz,sq,rcent 

While Not Eof(1)
  
  ln$ = ReadString(1)
  ;ln$ = ReplaceString(ln$, "  ", " ")
  ;ln$ = ReplaceString(ln$, "  ", " ")
  While FindString(ln$, "  ") > 0
   ln$ = ReplaceString(ln$, "  ", " ")
  Wend
  If Left(ln$, 1) = "f"
    st.s = ln$
    
   ParseString(st.s)

   tri + 1
    
  EndIf
  If tri >= maxFaces:Break:EndIf   
Wend

CloseFile(1)       
    
    ;NormalizeMesh(0) 
    FinishMesh(#True)
    ;NormalizeMesh(0) ; gives error
    GetMeshData(0,0, MeshData(), #PB_Mesh_Vertex, 0, MeshVertexCount(0)-1)
    ChangeMesh()
    
    UpdateMeshBoundingBox(0)

    If textured
      CreateMaterial(0, LoadTexture(0, "ground_diffuse.png"))
      MaterialCullingMode(0, #PB_Material_NoCulling)
      CreateEntity(0, MeshID(0), MaterialID(0))
    Else
      CreateMaterial(0, LoadTexture(0, "White.jpg"))
      MaterialCullingMode(0, #PB_Material_NoCulling)
      CreateEntity(0, MeshID(0), MaterialID(0))
    EndIf
    
    CreateCamera(0, 0, 0, 100, 100)
    
    MeshRad.f = MeshRadius(0)
    ScaleEntity(0, 1.244/MeshRad, 1.24/MeshRad, 1.24/MeshRad) 
    MoveCamera(0, 0, 2, 3, #PB_Absolute)
    CameraLookAt(0, 0,0,0)
    
    CreateLight(0, RGB(200,150,100), 10, 2000, 300)
    AmbientColor(RGB(90, 90, 60))
  
  
EndProcedure

Procedure ChangeMesh()
  ; by Comtois  
  Protected.f minx, miny, minz, maxx, maxy, maxz
  Protected.f corx, cory, corz
  minx =  999999
  miny =  999999
  minz =  999999
  maxx = -999999
  maxy = -999999
  maxz = -999999
  
  For i=0 To ArraySize(MeshData())
    If MeshData(i)\x > maxx
      maxx = MeshData(i)\x
    EndIf
    If MeshData(i)\x < minx
      minx = MeshData(i)\x
    EndIf    
    If MeshData(i)\y > maxy
      maxy = MeshData(i)\y
    EndIf
    If MeshData(i)\y < miny
      miny = MeshData(i)\y
    EndIf   
    If MeshData(i)\z > maxz
      maxz = MeshData(i)\z
    EndIf
    If MeshData(i)\z < minz
      minz = MeshData(i)\z
    EndIf       
  Next  
  corx = (-maxx - minx) /2.0
  cory = (-maxy - miny) /2.0
  corz = (-maxz - minz) /2.0

  For i=0 To ArraySize(MeshData())
    MeshData(i)\x + corx
    MeshData(i)\y + cory
    MeshData(i)\z + corz     
  Next  
  SetMeshData(0,0, MeshData(), #PB_Mesh_Vertex, 0, MeshVertexCount(0)-1)
EndProcedure

the second demo use triangles (#PB_Mesh_TriangleList) and will triangulate the possible polygons, and if they have texture coordinates then will apply a default texture.
regarding the submeshes , the second code below will read the lines begins with "g " and consider it a submesh, a showcase only demonstrated if you load spider.obj: texturing the eyes and the belly.
EDITED: to remove the extra spaces from some files

Code: Select all

;DisableDebugger
Global CameraSpeed.f = 0.02

Declare ParseString(st.s)
Declare MakeMesh()
Declare ChangeMesh()

Global Dim MeshData.PB_MeshVertex(0) ; for centering the mesh only

Define.f KeyX, KeyY, MouseX, MouseY
Quit.b = #False
#but = 10 ; button

Global.s path
Define.f x, y, z, nx, ny, nz, u, v
Global vert.l
Global tri.l, i.l, IndicesPerLine.l
Global st.s
Global.f NormX, NormY, NormZ
Global IndicesPerLine
Global vertices = 0
tri = 0
vert = 0
Global maxVertices = 300000
Global maxFaces = 500000
Global textured = 0
Global normalized = 0
Global pp
IndicesPerLine = 0


Global Dim x.f(maxVertices) ;vertex coordinates
Global Dim y.f(maxVertices)
Global Dim z.f(maxVertices) 
Global Dim nx.f(maxVertices)
Global Dim ny.f(maxVertices)
Global Dim nz.f(maxVertices)
Global Dim u.f(maxVertices)
Global Dim v.f(maxVertices)
Global Dim t.l(100)
Global Dim f.s(50)
Global Dim vt(50)
Global Dim vn(50)

ExamineDesktops()
OpenWindow(3, 0, 0, DesktopWidth(0), DesktopHeight(0), "OBJ  Loader ,  R_Click Show mouse cursor ..... W: Wire/Solid. ...Mouse+Arrow keys: Camera. .. .Space: stop rotation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

;Initialize environment
InitEngine3D()
InitSprite()
InitMouse()

OpenWindowedScreen(WindowID(3), 50, 0, DesktopWidth(0), DesktopHeight(0), 0, 0, 0)
ButtonGadget(#but, 0, 10, 50, 60, "Open File")
InitKeyboard()

Add3DArchive(".", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Textures", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Scripts",#PB_3DArchive_FileSystem)
Parse3DScripts()

path = OpenFileRequester("Choose file to load", "", "OBJ-Mesh (*.obj)|*.obj", 0, 0)
Add3DArchive(GetPathPart(path), #PB_3DArchive_FileSystem)
If path = "":End:EndIf
;OpenFile(1, GetFilePart(path))
OpenFile(1, path)

MakeMesh() ; read OBJ file lines
  
rot = 1
ExamineKeyboard()

Repeat
  If MouseButton(#PB_MouseButton_Right)
    flag = 1: ReleaseMouse(1)
  EndIf
  
  If flag = 0
  If ExamineMouse()
      MouseX = -MouseDeltaX() * 0.1 * 0.5
      MouseY = -MouseDeltaY() * 0.1 * 0.5
  EndIf
  EndIf

  Repeat
    Event = WindowEvent()
  
  Select event
        Case #PB_Event_Gadget
          Select EventGadget()
                      
            Case #but
              FreeEntity(0)
              FreeMesh(0)
              FreeTexture(0)
              
              For i=0 To maxVertices
              x(i)=0:y(i)=0:z(i)=0:nx(i)=0:ny(i)=0:nz(i)=0:u(i)=0:v(i)=0
            Next
            For i=0 To 50:f(i)="":Next
              tri = 0: vert = 0: textured = 0: normalized = 0: vertices = 0: indicesPerLine = 0
              path = OpenFileRequester("Choose file to load", "", "OBJ-Mesh (*.obj)|*.obj", 0, 0)
              If path = "":End:EndIf

              ;OpenFile(1, GetFilePart(path))
              OpenFile(1, path)
              SetActiveGadget(-1)
              flag = 0: ReleaseMouse(0) : wire = 0
              Dim MeshData.PB_MeshVertex(0)
              MakeMesh()
                                          
          EndSelect
          
      EndSelect
      Until event = 0

      If ExamineKeyboard()
        
        If KeyboardPushed(#PB_Key_Left)
          KeyX = -CameraSpeed
        ElseIf KeyboardPushed(#PB_Key_Right)
          KeyX = CameraSpeed
        Else
          KeyX = 0
        EndIf
        
        If KeyboardPushed(#PB_Key_Up)
          KeyY = -CameraSpeed
        ElseIf KeyboardPushed(#PB_Key_Down)
          KeyY = CameraSpeed
        Else
          KeyY = 0
        EndIf
        
        If KeyboardReleased(#PB_Key_W)
          wire ! 1
          If wire
            MaterialShadingMode(0, #PB_Material_Wireframe)
          Else
            MaterialShadingMode(0, #PB_Material_Solid) 
          EndIf  
        EndIf
        
        If KeyboardReleased(#PB_Key_S)
          SaveMesh(0, "spider3.mesh")
        EndIf
                
        If KeyboardReleased(#PB_Key_Space)
          rot ! 1
        EndIf
               
          
      EndIf
      
      RotateEntity(0, 0, 0.5*rot, 0, #PB_Relative)
      
      RotateCamera(0, MouseY, MouseX, 0, #PB_Relative)
      MoveCamera  (0, KeyX, 0, KeyY)
           
   RenderWorld()
   FlipBuffers()
   
   
   If KeyboardReleased(#PB_Key_Escape)
     Quit = #True
   EndIf
  Until Quit = #True Or Event = #PB_Event_CloseWindow


Procedure ParseString(st.s)
  Protected.f ax,ay,az,bx,by,bz
  st = RemoveString(st, "f")
  st = Trim(st)
  k = CountString(st, " ") + 1 
  
  For i=1 To k+1
    f(i) = StringField(st, i, " ")
  Next
  
  For i=1 To k+1
    t(i) = Val(StringField(f(i), 1, "/"))
    vt(i) = Val(StringField(f(i), 2, "/"))
    vn(i) = Val(StringField(f(i), 3, "/"))
  Next
  
  If (textured And Not normalized) Or Not normalized
   i=1
   ;just simple normalizarion 
   ax= x(t(2))-x(t(1))
   ay= y(t(2))-y(t(1))
   az= z(t(2))-z(t(1))
   bx= x(t(3))-x(t(1))
   by= y(t(3))-y(t(1))
   bz= z(t(3))-z(t(1))
   NormX = (ay*bz)-(az*by)
   NormY = (az*bx)-(ax*bz)
   NormZ = (ax*by)-(ay*bx)
   ;triangulation of the polygon
   ;the first triangle of the polygon
    MeshVertexPosition(x(t(i)),y(t(i)),z(t(i)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i)), v(vt(i)))
        
    MeshVertexPosition(x(t(i+1)),y(t(i+1)),z(t(i+1)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i+1)), v(vt(i+1)))
    
    MeshVertexPosition(x(t(i+2)),y(t(i+2)),z(t(i+2)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i+2)), v(vt(i+2)))
    
  ;remaining triangles of the the polygon 
  i=i+2
  While i < k
    
    MeshVertexPosition(x(t(i)),y(t(i)),z(t(i)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i)), v(vt(i)))
    
    MeshVertexPosition(x(t(i+1)),y(t(i+1)),z(t(i+1)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(i+1)), v(vt(i+1)))
    
    MeshVertexPosition(x(t(1)),y(t(1)),z(t(1)))
    MeshVertexNormal(NormX,NormY,NormZ)
    MeshVertexTextureCoordinate(u(vt(1)), v(vt(1)))
    i+1
   
  Wend
  
ElseIf textured Or normalized

   ;triangulation of the polygon
   ;first triangle
    i=1
    MeshVertexPosition(x(t(i)),y(t(i)),z(t(i)))
    MeshVertexNormal(nx(vn(i)), ny(vn(i)), nz(vn(i)))
    MeshVertexTextureCoordinate(u(vt(i)), v(vt(i)))
        
    MeshVertexPosition(x(t(i+1)),y(t(i+1)),z(t(i+1)))
    MeshVertexNormal(nx(vn(i+1)), ny(vn(i+1)), nz(vn(i+1)))
    MeshVertexTextureCoordinate(u(vt(i+1)), v(vt(i+1)))
    
    MeshVertexPosition(x(t(i+2)),y(t(i+2)),z(t(i+2)))
    MeshVertexNormal(nx(vn(i+2)), ny(vn(i+2)), nz(vn(i+2)))
    MeshVertexTextureCoordinate(u(vt(i+2)), v(vt(i+2)))
    
  ;remaining triangles  
  i=i+2
  While i < k
    
    MeshVertexPosition(x(t(i)),y(t(i)),z(t(i)))
    MeshVertexNormal(nx(vn(i)), ny(vn(i)), nz(vn(i)))
    MeshVertexTextureCoordinate(u(vt(i)), v(vt(i)))
    
    MeshVertexPosition(x(t(i+1)),y(t(i+1)),z(t(i+1)))
    MeshVertexNormal(nx(vn(i+1)), ny(vn(i+1)), nz(vn(i+1)))
    MeshVertexTextureCoordinate(u(vt(i+1)), v(vt(i+1)))
    
    MeshVertexPosition(x(t(1)),y(t(1)),z(t(1)))
    MeshVertexNormal(nx(vn(1)), ny(vn(1)), nz(vn(1)))
    MeshVertexTextureCoordinate(u(vt(1)), v(vt(1)))
    i+1
   
  Wend

EndIf 

  For n=0 To 100
    t(n)=0
  Next
  
EndProcedure

Procedure MakeMesh()


CreateMesh(0, #PB_Mesh_TriangleList, #PB_Mesh_Dynamic )

While Not Eof(1)
  
  ln$ = ReadString(1)
  ;ln$ = ReplaceString(ln$, "  ", " ")
  While FindString(ln$, "  ") > 0
    ln$ = ReplaceString(ln$, "  ", " ")
  Wend
  If Left(ln$, 2) = "vt"
    textured = 1
    vert + 1
    submeshFlag=0
    u(vert) = ValF(StringField(ln$, 2, " "))
    v(vert) = ValF(StringField(ln$, 3, " "))
    
  EndIf
  If vert >= maxVertices:Break:EndIf 
Wend 
vert = 0

FileSeek(1,0)

While Not Eof(1)
  
  ln$ = ReadString(1)
  While FindString(ln$, "  ") > 0
   ln$ = ReplaceString(ln$, "  ", " ")
  Wend
  ;ln$ = ReplaceString(ln$, "  ", " ")
  t+1
  If Left(ln$, 2) = "v "
    vertices = vertices+1
    submeshFlag=0
    ;ReDim x(vertices): ReDim y(vertices): ReDim z(vertices)
    x(vertices) = ValF(StringField(ln$, 2, " "))
    y(vertices) = ValF(StringField(ln$, 3, " "))
    z(vertices) = ValF(StringField(ln$, 4, " "))
    
  ElseIf Left(ln$, 2) = "vn"
    normalized = 1
    vert + 1
    nx(vert) = ValF(StringField(ln$, 2, " "))
    ny(vert) = ValF(StringField(ln$, 3, " "))
    nz(vert) = ValF(StringField(ln$, 4, " "))
        
  EndIf
  
 If vertices >= maxVertices:Break:EndIf   
Wend


FileSeek(1,0)
tri = 0: i =0

While Not Eof(1)
  
  ln$ = ReadString(1)
  While FindString(ln$, "  ") > 0
   ln$ = ReplaceString(ln$, "  ", " ")
  Wend
  ;ln$ = ReplaceString(ln$, "  ", " ")
  If Left(ln$, 2) = "g ":
    AddSubMesh(#PB_Mesh_TriangleList)
  EndIf
  
  If Left(ln$, 1) = "f"
    st.s = ln$
    
   ParseString(st.s)
;Debug tri
   tri + 1
    
  EndIf
  If tri >= maxFaces:Break:EndIf ; to break loading very big meshes like hairball.obj
Wend

CloseFile(1)       
    
FinishMesh(#True)
;NormalizeMesh(0) ; gives error
subMeshes = SubMeshCount(0)

GetMeshData(0,0, MeshData(), #PB_Mesh_Vertex, 0, MeshVertexCount(0)-1)
ChangeMesh()

UpdateMeshBoundingBox(0)
CreateMaterial(1, LoadTexture(1, "RustySteel.jpg"))
MaterialCullingMode(1, #PB_Material_NoCulling)
DisableMaterialLighting(1, 1)

CreateMaterial(0, LoadTexture(0, "ground_diffuse.png"))
    
    If textured
      CreateMaterial(0, LoadTexture(0, "ground_diffuse.png"))
      ;CreateMaterial(0, LoadTexture(0, "SpiderTex.jpg"))
      MaterialCullingMode(0, #PB_Material_NoCulling)
      CreateEntity(0, MeshID(0), MaterialID(0));#PB_Material_None);MaterialID(17)) ;
    Else
      CreateMaterial(0, LoadTexture(0, "White.jpg"))
      SetMaterialColor(0, #PB_Material_SpecularColor, RGB(255,0,0))
      MaterialCullingMode(0, #PB_Material_NoCulling)
      CreateEntity(0, MeshID(0), MaterialID(0))
    EndIf
    
    If subMeshes = 19 ; only for the spider mesh as a showcase for submeshes texturing
      CreateMaterial(2, LoadTexture(2, "Geebee2.bmp"));"Lensflare5.jpg"));"engineflare1.jpg"))
      MaterialCullingMode(2, #PB_Material_NoCulling)
      DisableMaterialLighting(2, 1)
      CreateEntity(0, MeshID(0), MaterialID(0))
      SetEntityMaterial(0, MaterialID(1),0) ; belly
      SetEntityMaterial(0, MaterialID(0),1)
      SetEntityMaterial(0, MaterialID(2),17) ; spider eye
      SetEntityMaterial(0, MaterialID(2),18) ; spider eye
    EndIf
    
    
   
    CreateCamera(0, 0, 0, 100, 100)
    
    MeshRad.f = MeshRadius(0)
    ScaleEntity(0, 1.244/MeshRad, 1.24/MeshRad, 1.24/MeshRad) 
    MoveCamera(0, 0, 2, 3, #PB_Absolute)
    CameraLookAt(0, 0,0,0)
    
    CreateLight(0, RGB(200,150,100), 10, 2000, 300)
    AmbientColor(RGB(90, 90, 60))
  
  
  EndProcedure
  
  Procedure ChangeMesh()
  ; by Comtois  
  Protected.f minx, miny, minz, maxx, maxy, maxz
  Protected.f corx, cory, corz
  minx =  999999
  miny =  999999
  minz =  999999
  maxx = -999999
  maxy = -999999
  maxz = -999999
  
  For i=0 To ArraySize(MeshData())
    If MeshData(i)\x > maxx
      maxx = MeshData(i)\x
    EndIf
    If MeshData(i)\x < minx
      minx = MeshData(i)\x
    EndIf    
    If MeshData(i)\y > maxy
      maxy = MeshData(i)\y
    EndIf
    If MeshData(i)\y < miny
      miny = MeshData(i)\y
    EndIf   
    If MeshData(i)\z > maxz
      maxz = MeshData(i)\z
    EndIf
    If MeshData(i)\z < minz
      minz = MeshData(i)\z
    EndIf       
  Next  
  corx = (-maxx - minx) /2.0
  cory = (-maxy - miny) /2.0
  corz = (-maxz - minz) /2.0

  For i=0 To ArraySize(MeshData())
    MeshData(i)\x + corx
    MeshData(i)\y + cory
    MeshData(i)\z + corz     
  Next  
  SetMeshData(0,0, MeshData(), #PB_Mesh_Vertex, 0, MeshVertexCount(0)-1)
EndProcedure

press 'S' to save a test.mesh suitable for PB Ogre

some tools:
1-the best models viewer (for OBJ and others) is the small and free view3dscene from https://castle-engine.sourceforge.io/view3dscene.php
2- to convert OBJ or 3DS or other models to Ogre Mesh use
OgreASSIMPConverte from http://www.purebasic.fr/english/viewtop ... 22#p401729

Re: wavefront OBJ files Loader

Posted: Fri May 05, 2017 2:50 pm
by djes
It works remarkably well with several big objects I have ! Thank you, could be useful. :)

Re: wavefront OBJ files Loader

Posted: Sun May 07, 2017 8:47 pm
by Kwai chang caine
Cool works fine, like usually 8)
After several obj in your zip, I have try the head, very nice :D

With "LoadObjMesh_Tri.pb" i have also try the "cornell-box" and "CornellBox-Empty-CO.obj" and i have an error at this line :|

Code: Select all

  ;just simple normalizarion 
   ax= x(t(2))-x(t(1))
And also several others "CornellBox-Glossy.obj"

I have W10 X64 PB v5.60(x86)

Thanks again for your perpetual splendid works 8)

Re: wavefront OBJ files Loader

Posted: Mon May 08, 2017 10:46 am
by applePi
Thank you djes and Kwai chang caine
Thanks for reporting a bug , i have opened the CornellBox-Empty-CO.obj file with notepad and it seems the error is because the indexes have negative values such as f -4 -3 -2 -1
usualy and in most cases it is positive. -1 means the last point, it is a relative indexes. can't insert this idea in my spaghetti steel of coding, it is may be very simple change but i don't get it. will look at this later.
CornellBox-Glossy + CornellBox-Sphere.obj + CornellBox-Water.obj works for me (look the first code marked EDITED, the CornellBox-Water.obj in interesting. the others does not work, if they have negative indices.

here is another obj files examples, i have tested it all (the minicooper.obj takes 5 seconds to load):
http://people.sc.fsu.edu/~jburkardt/data/obj/obj.html
http://wikisend.com/download/242146/obj_files.7z

in fact i suggest PureBasic to support OBJ files, will be great, since it is simpler than other 3D files and it is a text files with OBJ extension. and it does not have animation so it is easier to implement, it is available in all and every thing ie natural and classic

Re: wavefront OBJ files Loader

Posted: Mon May 08, 2017 11:13 am
by djes
I totally agree, on my last work I've abandonned pb for 3D, because of the lack of object import possibilities, even if it was the first tool I've tested. Conversion tools are a mess, and with really big objects, most are unstable. As it was on point clouds, a medium sized scan has 20 million points... Anyway, your simple code permits it, proving that PB could do the stuff. I've tested some other tools, and what I need is really something handling a lot of low level stuff.

Re: wavefront OBJ files Loader

Posted: Wed May 10, 2017 8:23 am
by applePi
in purebasic when we add Submesh (any time) the indices to vertices begins with index zero and up . in some OBJ files it is like this
f -4 -3 -2 -1
with -1 refer to the last created vertex, and -4 refer to the first created vertex in the newest submesh.
to translate something like f -4 -3 -2 -1 to purebasic Ogre we first find the smallest number (the biggest in negative direction) and then adding its absolute value to all negative numbers so then we turn it to usual PB indice going from 0 and up
here is the vertices data and the indices in CornellBox-Empty-CO.obj in the package http://graphics.williams.edu/data/meshe ... ll-box.zip
this is the line of thought to follow when we want to add support to negative indices.
it is useful in designing delicate shapes, but not in big meshes (will be too many submeshes)
Image

Code: Select all

Procedure.l Min (Array a.l(1))
  ;http://rosettacode.org/wiki/Greatest_element_of_a_list#PureBasic
  ;example: Debug Min(a())
   Protected last, i, ret.f
 
   ret = a(0)   
   last = ArraySize(a())
   For i = 1 To last
      If ret > a(i) ; for Max change > to <
         ret = a(i)
      EndIf
   Next
 
   ProcedureReturn ret
 EndProcedure
 

IncludeFile #PB_Compiler_Home + "examples/3d/Screen3DRequester.pb"


If InitEngine3D()
  
  Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Textures", #PB_3DArchive_FileSystem)
  Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/fonts", #PB_3DArchive_FileSystem)
  Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Scripts", #PB_3DArchive_FileSystem)
  Parse3DScripts()
  
  InitSprite()
  InitKeyboard()
  InitMouse()
  
  If Screen3DRequester()
    
    ;- Material
    CreateMaterial(0, LoadTexture(0, "White.jpg"))
    DisableMaterialLighting(0, #True)
    
   ;****************************************************************** 
    Define.f x,y,z 
    Dim indx.l(3)
 
   CreateMesh(4, #PB_Mesh_LineStrip, #PB_Mesh_Static)
   Restore vertex
   For i=1 To 6
    AddSubMesh(#PB_Mesh_LineStrip)
    Read.f x: Read.f y: Read.f z
    MeshVertexPosition(x, y, z) ; have indx 0
    Read.f x: Read.f y: Read.f z
    MeshVertexPosition(x, y, z);1
    Read.f x: Read.f y: Read.f z
    MeshVertexPosition(x, y, z) ;2
    Read.f x: Read.f y: Read.f z
    MeshVertexPosition(x, y, z)  ;3
    
    Read.l indx(0): Read.l indx(1): Read.l indx(2): Read.l indx(3)
    min = Min(indx())
    indx(0)-min: indx(1)-min: indx(2)-min: indx(3)-min
    MeshIndex(indx(0)): MeshIndex(indx(1)):MeshIndex(indx(2)):MeshIndex(indx(3)):MeshIndex(indx(0))
    
   Next
  
    FinishMesh(#False)
    
    SetMeshMaterial(4, MaterialID(0))
    Box = CreateNode(#PB_Any, 0, 0, 0)
    AttachNodeObject(Box, MeshID(4))
    MoveNode(Box, 0,0,0)
    
    ;-Camera
    CreateCamera(0, 0, 0, 100, 100)
    MoveCamera(0, 0, 3, 4, #PB_Absolute)
    CameraFOV(0, 60)
    CameraLookAt(0, 0,  0,  0)
    CameraBackColor(0, RGB(0, 0, 40))
    
    ;-Light
    CreateLight(0, RGB(255,255,255), -10, 60, 10)
    AmbientColor(RGB(90, 90, 90))
    
    Repeat
      Screen3DEvents()
      
      ExamineKeyboard()
      
      RotateNode(Box, 0, -1, 0, #PB_Relative) 
      RenderWorld()
      
      FlipBuffers()
    Until KeyboardPushed(#PB_Key_Escape) Or Quit = 1
  EndIf
  
Else
  MessageRequester("Error", "The 3D Engine can't be initialized", 0)
EndIf

End

DataSection
vertex:  
Data.f  -1.01,  0.00,   0.99
Data.f  1.00,  0.00,   0.99
Data.f  1.00,  0.00,  -1.04
Data.f  -0.99,  0.00,  -1.04
Data.l -4, -3, -2, -1 ; relative indexes

Data.f  -1.02,  1.99,   0.99  
Data.f  -1.02,  1.99,  -1.04
Data.f   1.00,  1.99,  -1.04
Data.f   1.00,  1.99,   0.99
Data.l -4, -3, -2, -1

Data.f  -0.99,  0.00,  -1.04 
Data.f   1.00,  0.00,  -1.04
Data.f   1.00,  1.99,  -1.04
Data.f  -1.02,  1.99,  -1.04
Data.l -4, -3, -2, -1

Data.f   1.00,  0.00,  -1.04   
Data.f   1.00,  0.00,   0.99
Data.f   1.00,  1.99,   0.99
Data.f   1.00,  1.99,  -1.04
Data.l -4, -3, -2, -1

Data.f  -1.01,  0.00,   0.99
Data.f  -0.99,  0.00,  -1.04
Data.f  -1.02,  1.99,  -1.04
Data.f  -1.02,  1.99,   0.99
Data.l -4, -3, -2, -1

Data.f   -0.24,  1.98,   0.16
Data.f   -0.24,  1.98,  -0.22
Data.f    0.23,  1.98,  -0.22
Data.f    0.23,  1.98,   0.16
Data.l -4, -3, -2, -1

EndDataSection

Re: wavefront OBJ files Loader

Posted: Fri Jun 09, 2017 10:41 pm
by Pureabc
This is a very nice OBJ viewer, I like it.

Any chance of adding support to STL format?

Thank you.

Re: wavefront OBJ files Loader

Posted: Sat Jun 10, 2017 11:12 am
by applePi
Thank you Pureabc
i have made the Obj Viewer project for the only purpose to view the hairball.obj in http://graphics.williams.edu/data/meshes.xml which have size 236 MBytes and Vertices: 1,441,098 Triangles: 2,880,000 since the available viewers trying to load all the mesh and since it is very big mesh it crashed. but my OBJ viewer loads only part of the very big mesh so it does not crash my computer. the OBJ files is relatively simple to parse.
while stl have more complex structure and it is a text like files and also have a binary mode, it is too much difficult task for me now and can't do it.
thanks for your interest
regards

Re: wavefront OBJ files Loader

Posted: Sun Jul 16, 2017 11:14 am
by DK_PETER
Normally I do all my conversions to Ogre3D using third party software.
It's a nice piece of code, though.
Thank you.