  'OBJINTERP.INC
  '=============


  indexbase 1

  'STRUCTURES
  '==========

  type Vertex3d
    float x,y,z
  end type

  type    Vertex4d
    float x,y,z,w
  end type
  '

  'GLOBAL VARIABLES
  '================
  string ObjFilename  ' loaded file
  string ObjPath      ' directory pathname of file
  sys    li           ' LISTS INDEX
  string status       ' info about file

  float  mat[4096]    ' MATERIAL RECORDS (256 stride 16)
  sys    glist[1000]  ' GL COMPILED LISTS
  sys    mlist[1000]  ' MATERIAL LISTS
  string matname[256] ' MATERIAL NAMES
  float  smin         ' min scalar size (incl negatives)
  float  smax         ' max scalar size
  int    snew         ' NEW-OBJECT FLAG

  ================
  class ObjBuilder
  ================
  '
  string er           ' info about errors 
  sys    minmaxf      ' flag for smin and smax
  string d            ' BUFFER FOR DATA TEXT
  sys    i            ' SOURCE STRING INDEX
  sys    p            ' SOURCE STRING POINTER
  '
  macro ReadValB(v)
  =================
  'READ NUMBERS DIRECTLY FROM STL STRING
  skiplspace d,i
  v=valat p+i
  StepWord d,i 'READY FOR NEXT WORD
  if minmaxf then
    if v<smin then smin=v
    if v>smax then smax=v
  end if
  end macro

  sub ReadVertex3D(vertex3d*ve)
  =============================
  readvalB ve.x
  readvalB ve.y
  'CHECK FOR OPTIONAL VALUE
  skiplspace d,i
  if ascb>32 then
    readvalB ve.z
  else
    ve.z=0.0
  end if
  'ITR OPTIONAL VERTEX COLORS
  end sub		
  '
  sub ReadVertex4D(vertex4d*ve)
  =============================
  readvalB ve.x
  readvalB ve.y
  readvalB ve.z
  'CHECK FOR OPTIONAL VERTEX
  skiplspace d,i
  if ascb>32 then
    readvalB ve.w
  else
    ve.w=1.0
  end if
  'ITR OPTIONAL VERTEX COLORS
  end sub
  '
  sub SurfaceNormal(vertex4d*n, sys*pm)
  =====================================
  'see OpenGlSceneFrame.inc
  'assumes triangle points are defined anticlockwise
  vector u,v
  vector p1 at pm[1]
  vector p2 at pm[2]
  vector p3 at pm[3]
  VectorDiff(u,p1,p2)
  VectorDiff(v,p1,p3)
  n.x=(u.y*v.z)-(u.z*v.y)
  n.y=(u.z*v.x)-(u.x*v.z)
  n.z=(u.x*v.y)-(u.y*v.x)
  end sub
  '
  function ExtractPath(string f) as string
  ========================================
  sys i=len f
  do
    if i<=0 then exit do
    select asc f,i
    case "/" , "\" , ":" : exit do
    end select
    i--
  end do
  return left f,i
  end function


  sys    imgn         'TEXTURE
  sys    imgbase      'OFFSET FOR IMGN
  '
  sys    mni          'MATERIAL NAME INDEX
  sys    mti          'MATERIAL ARRAY OFFSET
  '
  float  c
  '
  '
  macro RenderVertex
  ==================
  if imgn>imgbase then
    @f=pu[k] : if @f then glTexCoord2fv f
  end if
  @f=pn[k] : if @f then glNormal3fv   f
  @f=pm[k] : if @f then glVertex3fv   f
  end macro
  '
  macro ReadFace()
  ================
  j=1
  pm[1]=0 : pu[1]=0 : pn[1]=0 'CLEAR 1ST VERTEX POINTERS 
  vj=1
  sg=0
  do
    skiplspace d,i
    select ascb
    case 0  to 31 : exit do  'END OF LINE
    case 45
      sg=1 : i++             'MAKE VERTEX INDEX RELATIVE
      continue do
    case 46,48 to 57 :       'NUMBERS
      vn=valat p+i
      if sg then
        vn=vim[vj]-vn+1      'RELATIVE TO ABSOLUTE INDEX
        sg=0       
      end if
      select vj
      case 1
      if vn<=cm then pm[j]=@vtm[vn]
      case 2
      if vn<=cu then pu[j]=@vtu[vn]
      case 3
      if vn<=cn then pn[j]=@vtn[vn]
      end select
      vi=0
      sg=0
    end select
    stepword d,i
    @b=p+i
    if b=47 then '/'
      i++
      vj++
    else
      j++
      pm[j]=0 : pu[j]=0 : pn[j]=0 'CLEAR VERTEX POINTERS
      vj=1
    end if
  end do
  j-=1
  end macro
  '
  macro RenderFace()
  ==================
  ReadFace
  select j
  case 3  :
    glBegin GL_TRIANGLES
    if cn=0 then
      SurfaceNormal vfn,pm
      @f=@vfn : glNormal3fv f
    end if
    for k=1 to 3
      RenderVertex
    next
    glEnd
  case 4  :
    glBegin GL_QUADS
    if cn=0 then
      SurfaceNormal vfn,pm
      @f=@vfn : glNormal3fv f
   end if
    for k=1 to 4
      RenderVertex
    next
    glEnd
  case else :
    'ITR NOT SUPPORTED
  end select
  end macro
  '
  macro ignore()
  ==============
  :
  end macro
  '
  macro CountData()
  =================
  i=1
  le=len d
  cm=0 : cu=0 : cn=0 : cp=0 : cf=0
  do
    StepWord d,i
    if i>=le then exit do 'NO MORE WORDS
    select ascb
    case "v"
      @b=p+sttw+1
      select b
      case "#"  : 'SKIP COMMENT
      case 9,32 : cm++
      case "t"  : cu++
      case "n"  : cn++
      case "p"  : cp++
      end select
    case "f" : cf++ 'COLLECT OR PASS TO OPENGL
    end select
    NextLine d,i
  end do
  end macro
  '
  macro ReadMaterial()
  ====================
  float rr,bb,gg,aa
  sys   q,nf
  nf=0
  do
    stepword  MtlLib, a1
    q=strptr(MtlLib)-1
    @b=q+sttw+1 'second char
    select ascb
    case 0 'END OF FILE
      exit do
    case "n" 'NEWMTL
      if nf then
        a1=sttw
        exit do 'NEXT MATERIAL DEFINITION
      end if
      mni++ 'Matname index
      matname[mni]=GetWord MtlLib,a1
      mti+=16 'material record offset
      'SET DEFAULT MATERIAL
      mat[mti]={
      .15, 0.15, 0.15, 1.0, 'ambient
      0.7, 0.70, 0.70, 1.0, 'diffuse
      0.1, 0.10, 0.10, 1.0, 'specular  
      0.                    'shininess
      }
      nf=1
    case "K"
      stepword MtlLib,a1 : rr=valat q+sttw 
      stepword MtlLib,a1 : gg=valat q+sttw 
      stepword MtlLib,a1 : bb=valat q+sttw
      skiplspace MtlLib,a1
      if ascb>32
        stepword MtlLib,a1 : aa=valat q+sttw
      else
        aa=1.0
      end if
      q=mti
      select b 'SECOND CHAR
      case "a" :'q+=0 'AMBIENT
      case "d" : q+=4 'DIFFUSE
      case "s" : q+=8 'SPECULAR
      end select
      mat[q]={rr,gg,bb,aa}
    case "N"
      stepword MtlLib,a1 : aa=valat q+sttw 
      q=mti
      select b 'SECOND CHAR
      case "s" : q+=12
      case "i" : q+=13
      end select
      mat[q]=aa
    case "d","T" 'Tr
      stepword MtlLib,a1 : aa=valat q+sttw 
      q=mti+3  : mat[q]=aa
      q=mti+7  : mat[q]=aa
      q=mti+11 : mat[q]=aa
    case "m"
      'map_
      wr=ObjPath + getword MtlLib,a1
      static sys res,wi,ht
      res=1024
      imgn++ '21...
      LoadTexture wr,imgn,res,wi,ht
      q=mti+14 : mat[q]=imgn
    case "i"
      'illum
    case "b"
      'bump
    case "d"
      'disp
      'decal
    end select
    NextLine MtlLib,a1
  end do
  mat[mti+12]*=mat[mti+13] 'scaler Ni
  '
  'INTERPRET MATERIAL AND TEXTURE SPEC
  'Ka r g b
  'Kd r g b
  'Ks r g b
  'Ns 1.0
  'Ni 1.5
  'd / Tr 0.6
  'map_Kd imagefilename map_bump
  'map_ 
  '
  'illum n
  'illum modes
  '0  Color on and Ambient off
  '1  Color on and Ambient on
  '2  Highlight on
  '3  Reflection on and Ray trace on
  '4  Transparency: Glass on, Reflection: Ray trace on
  '5  Reflection: Fresnel on and Ray trace on
  '6  Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
  '7  Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
  '8  Reflection on and Ray trace off
  '9  Transparency: Glass on, Reflection: Ray trace off
  '10 Casts shadows onto invisible surfaces
  '
  'bump
  'disp
  'decal
  '
  end macro
  '
  '
  macro ReadMaterials()
  =====================
  sys a1=1
  do
    ReadMaterial
    if ascb=0 then exit do
  end do
  end macro
  '
  macro ReadLines()
  =================
  i=1
  smin=0 : smax=0
  int vi
  '
  do
    StepWord d,i
    if i>=le then exit do 'NO MORE WORDS 
    select ascb
    case "v"
      @b=p+sttw+1
      select b
      case "#"  : ignore   'SKIP COMMENT
      case 9,32 : minmaxf=1 : 
                : vim[1]++ : ReadVertex4D vtm[vim[1]]
                : minmaxf=0
      case "t"  : vim[2]++ : ReadVertex3D vtu[vim[2]]
      case "n"  : vim[3]++ : ReadVertex4D vtn[vim[3]]
      case "p"  : vim[4]++ : ReadVertex4D vtp[vim[4]]
      end select
    case "f" : RenderFace : 'COLLECT OR PASS TO OPENGL
    case "m" :
      i=sttw
      if GetWord(d,i)="mtllib" then
        ''STOP GL LIST
        'glEndlist
        'READ WHOLE LINE TO GET PATHNAME
        skiplspace d,i
        sttw=i
        endline d,i
        wr=ObjPath + unquote rtrim mid d,sttw,i-sttw
        getfile wr,MtlLib
        if not MtlLib
          er+=" cannot read mtl file: " wr
        else
          ReadMaterials() 'TO STORE IN MATERIAL RECORDS
        end if
        ''RESTART GL LIST
        'glist[li]=CompileList glist[li]
      end if
    case "u" : 'usemtl
      i=sttw
      if GetWord(d,i)="usemtl" then
        UseMtl=unquote getword d,i
        for j=1 to mni
          if UseMtl=MatName[j] then
            'CREATE ANOTHER GL LIST
            glEndlist
            li++
            glist[li]=CompileList 'glist[li]
            mlist[li]=j*16 'MAT SELECTOR
            exit for
          end if 
        next
      end if
    case "o" : 'OBJECTS
      objectA=MayGetWord d,i
    case "g" : 'GROUPS
      groupA=MayGetWord d,i
    end select
    NextLine d,i
  end do
  end macro 'ReadLines
  '
  macro FreeBuffers()
  ===================
  if @vtm then FreeMemory @vtm
  if @vtu then FreeMemory @vtu
  if @vtn then FreeMemory @vtn
  if @vtp then FreeMemory @vtp
  end macro
  '
  macro NewBuffers()
  ==================
  FreeBuffers
  @vtm = GetMemory cm*sizeof(Vertex4D)
  @vtu = GetMemory cu*sizeof(Vertex3D)
  @vtn = GetMemory cn*sizeof(Vertex4D)
  @vtp = GetMemory cp*sizeof(Vertex4D)
  end macro
  '


  function CompileObj(string sd) as sys
  ====================================
  '
  sys    j,k         'INDEXES AND ITERATORS
  sys    e,m,n,q     'GENERIC
  sys    sg          'NEG SIGN FLAG FOR RELATIVE VERTEX INDEX
  sys    vj          'VERTEX ARRAY SELECTOR
  sys    vn          'VERTEX INDEX
  float  *f          'FOR GL DATA POINTER
  float  v           'GENERIC
  byte   *b          'SOURCE STRING BYTE READER
  sys    pm[5]       'POINTER TO VERTEX
  sys    pn[5]       'POINTER TO NORMAL
  sys    pp[5]       'POINTER TO PARAMETRIC VECTOR / ITR
  sys    pu[5]       'POINTER TO TEX COORD
  sys    vim[3]      'MAX VERTEX COUNT FOR MAINS,TEXTURES,NORMALS
  sys cf,cm,cu,cn,cp 'COUNTERS
  '
  string wr
  string MtlLib      'MATERIAL LIBRARY TEXT
  string UseMtl      'NAME OF MATERIAL TO USE
  string ObjectA     'NAME OF CURRENT OBJECT
  string GroupA      'NAME OF CURRENT GROUP
  '
  d=sd 'GLOBAL STRING

  sys    t1,t2       'TIMERS
  '
  t1=GetTickCount
  '
  le=len d
  Objpath=ExtractPath ObjFileName
  '
  imgbase=20
  imgn=imgbase
  e=mti+15
  for j=16 to e
    mat[j]=0
  next
  mti=0
  mni=0

  'clear previous lists
  for j=1 to li
    glDeleteLists glist[j],1
  next

  li=1
  glist[li] = CompileList 'glist[li] 'MAYBE EMPTY

  Vertex4d vfn       'DEFAULT FACE NORMAL
  Vertex4d *vtm      'FOR GLVERTEX
  Vertex3d *vtu      'FOR GLTEXCOORD
  Vertex4d *vtn      'FOR GLNORMAL
  Vertex4d *vtp      'FOR PARAMETRIC VECTOR
  '
  if not le then
    er="Problem accessing file " ObjFileName
    jmp fwd done
  end if
  p=strptr(d)-1
  @b=p 'SOURCE BYTES
  CountData
  string sp="  " ' or tab
  if not cf then
    jmp fwd done
  end if
  NewBuffers
  ReadLines
  FreeBuffers
  done:
  =====
  glEndList
  t2=GetTickCount
  if er
    status=er
    er=""
  else
    status= _
    "Faces:" sp cf sp _
    "Vert:"  sp cm sp _
    "Tex:"   sp cu sp _
    "Norm:"  sp cn sp _
    "Lists:"   sp li sp _
    "Range:" sp str(smax-smin,4) sp _
    "mSec:"  sp str(t2-t1) sp _
     ""
  endif
  'mbox status
  d=""
  snew=1
  return li 'COMPILED LISTS
  end function

  end class 'objbuilder


  function BuildObj() as sys
  ==========================
  string s
  ObjBuilder b
  GetFile ObjFileName,s
  function=b.CompileObj(s)
  end function


