I just wrote an obj loader for my game last week, obj files are text files that 
describes a 3d model. To stress test it I tried the common large minecraft 
model rungholt, this is a text file with over 9 million lines. Blender took 2 
minutes to load the file and windows preview gave up. I load it in 1-2 seconds 
by inlining the strtofloat procedures like this:
    
    
    proc fromString*(m:ObjModels,s:string,materials:string)=
      #  This has to be the format:
      #  no textures or normals.
      #  Blender export settings:
      #  Z forward, Y up,
      #  only theese checked: Apply modifiers, Write Materials, Triangulate 
Faces, Obj as obj groups
      #
      #  mtllib watermill2.mtl
      #  g watermill
      #  v 0.285000 0.715365 -0.413016
      #  v 0.285000 0.774984 -0.052391
      #  usemtl woodDark
      #  s 1
      #  f 1 2 3
      #  f 4 2 1
      
      
      # Material string:
      #
      #[
    
    # Blender v2.78 (sub 0) OBJ File: ''
    # www.blender.org
    mtllib test.mtl
    o sail_front_1
    v -5.776396 31.717396 32.006035
    v -5.776396 24.370396 32.006035
    v 5.776396 24.370396 32.006035
    v 5.776396 31.717396 32.006035
    g sail_front_1_sail_front_1_textile
    usemtl textile
    s 1
    f 1 2 3
    f 3 4 1
    f 5 1 6
    f 7 1 4
    f 4 8 7
    
    # Blender MTL File: 'None'
    # Material Count: 7
    
    newmtl Material
    Ns 96.078431
    Ka 1.000000 1.000000 1.000000
    Kd 0.640000 0.640000 0.640000
    Ks 0.500000 0.500000 0.500000
    Ke 0.000000 0.000000 0.000000
    Ni 1.000000
    d 1.000000
    illum 2
    
    newmtl _defaultMat
    Ns 96.078431
    Ka 0.000000 0.000000 0.000000
    Kd 1.000000 1.000000 1.000000
    Ks 0.330000 0.330000 0.330000
    Ke 0.000000 0.000000 0.000000
      ]#
      
      m.faces.clear
      m.verts.clear
      m.material.clear
      m.materials.clear
      m.matnr.clear
      
      var currentmat = ""
      
      var lastmat:uint32
      
      for i in materials.splitLines:
        if i.startswith("newmtl"):
          var mat:ObjMaterial
          mat.name = i.sfrom("newmtl ")
          m.material.add mat
          lastmat = m.material.len-1
        elif i.startswith("Kd"):
          var c = i.split()
          let col = color(parsefloat(c[1]), parsefloat(c[2]), parsefloat(c[3]), 
1.0)
          m.material[lastmat].color = col
          
          m.materials.add col.pack()
          m.matnr[m.material[lastmat].name] = int16(m.materials.len - 1)
      
      var onlyverts = false
      var first = true
      
      var currentMatnr:int16 = 0
      var currentGroupnr:uint32 = 0
      
      var i = 0
      let slen = s.len-1
      while i < slen:
        case s[i]
        of 'u': # usemtl
          if s.startswithat("usemtl",i):
            var matname = ""
            i += "usemtl ".len
            
            while s[i] notin {' ',chr(13),chr(10)}:
              matname.add s[i]
              inc i
            currentMatnr = m.matnr[matname]
        of 'g': # g group_0_4634441
          while s[i] notin {' ',chr(13),chr(10)}:
            inc i
        of 'o': # o object_0_4634441
          while s[i] notin {' ',chr(13),chr(10)}:
            inc i
        of 'v': # Only positions and colors, normals are calculated in the 
shader
          # Inlined for speed reasons, no string allocations etc, just scan 
directly
          # into float32
          
          if s[i+1] == ' ':
            inc i
            inc i
    
    #          var c = i.split({' '})   # Slow allocations here..
    #          m.verts.add parsefloat(c[1].strip())
    #          m.verts.add parsefloat(c[2].strip())
    #          m.verts.add parsefloat(c[3].strip())
    #
    #       Optimized version of the above below
            
            #v -5.776396 31.717396 32.006035
            
            # This has to be the format, since we have control over this and 
know this
            # we can just use a simple string to float inline and do not have to
            # take care of all special cases etc, but more importantly we never 
have to
            # allocate any temps.
            
            
            var neg = false
            const zero = ord('0')
            const digits = {'0'..'9'}
            var number = 0f
            
            if (s[i] == '-'):
              neg = true
              inc i
            
            while s[i] in digits:
              number = ( number * 10f ) + ( ord(s[i]) - zero )
              inc i
            
            if (s[i] == '.'):
              var f = 0f
              var n = 0f
              
              inc i
              
              while s[i] in digits:
                f = ( f * 10f ) + (ord(s[i]) - zero)
                inc i
                n = n + 1
              
              number += f / pow(10f, n)
            
            if neg:
              number = -number
            
            
            m.verts.add number   #  X
            
            if s[i] == ' ':
              inc i
              neg = false
              number = 0f
              if (s[i] == '-'):
                neg = true
                inc i
              while s[i] in digits:
                number = ( number * 10f ) + ( ord(s[i]) - zero )
                inc i
              if (s[i] == '.'):
                var f = 0f
                var n = 0f
                inc i
                while s[i] in digits:
                  f = ( f * 10f ) + (ord(s[i]) - zero)
                  inc i
                  n = n + 1
                number += f / pow(10f, n)
              if neg:
                number = -number
              m.verts.add number     # Y
              
              if s[i] == ' ':
                inc i
                neg = false
                number = 0f
                if (s[i] == '-'):
                  neg = true
                  inc i
                while s[i] in digits:
                  number = ( number * 10f ) + ( ord(s[i]) - zero )
                  inc i
                if (s[i] == '.'):
                  var f = 0f
                  var n = 0f
                  inc i
                  while s[i] in digits:
                    f = ( f * 10f ) + (ord(s[i]) - zero)
                    inc i
                    n = n + 1
                  number += f / pow(10f, n)
                if neg:
                  number = -number
                m.verts.add number # Z
        
        of 'f':
          #f 2 1 3
          
          inc i
          
          var vno = 0'i32
          while s[i] notin {'0'..'9'}:
            inc i
          while s[i] in {'0'..'9'}:
            vno = 10'i32 * vno + ord(s[i]).int32 - 48i32
            inc i
          
          var vno2 = 0'i32
          while s[i] notin {' '}:
            inc i
          while s[i] notin {'0'..'9'}:
            inc i
          while s[i] in {'0'..'9'}:
            vno2 = 10'i32 * vno2 + ord(s[i]).int32 - 48i32
            inc i
          
          var vno3 = 0'i32
          while s[i] notin {' '}: # Skip 2/2/1 format
            inc i
          while s[i] notin {'0'..'9'}:
            inc i
          while s[i] in {'0'..'9'}:
            vno3 = 10'i32 * vno3 + ord(s[i]).int32 - 48i32
            inc i
          
          var f:ObjFace
          f.vert = (vno-1)*3
          f.material = currentMatnr.int8
          m.faces.add f.pack()
          
          f.vert = (vno2-1)*3
          f.material = currentMatnr.int8
          m.faces.add f.pack()
          
          f.vert = (vno3-1)*3
          f.material = currentMatnr.int8
          m.faces.add f.pack()
        
        else:
          discard
        
        # Skip rest of line
        while s[i] notin {chr(13),chr(10)} and i < slen:
          inc i
        while s[i] in {chr(13),chr(10)} and i < slen:
          inc i
    
    
    
    
    Run

StrToFloat is slow in every language, but If you know the source format you can 
just do an extremely fast inline version.

300k lines should never have to take more than a second to parse. (I'm doing 9 
million in a second with the code above)

Reply via email to