#ifndef __GLMESH_BI__
#define __GLMESH_BI__

#include once "fbSPE.bi"
#include once "windows.bi"
#include once "fbgfx.bi"
#include once "GL/gl.bi"
#include once "GL/glut.bi"

const TEXTURE_PATH = "demo/Texture/"
const GL_PATH      = "demo/GL_MEDIA/"

type GLMesh
  declare constructor
  declare destructor
  declare sub Release
  declare function Load(filename as string, ScaleX as SPEFLOAT=1.0,ScaleY as SPEFLOAT=1.0,ScaleZ as SPEFLOAT=1.0) as SPEBOOL
  declare sub      Render(pMat as SPEFLOAT ptr)

  as SPEVERTEX ptr pVertex
  as integer   ptr pIndex
  as integer       nVertex
  as integer       nIndex
end type

constructor GLMesh()
  ' nothing ATM
end constructor

destructor GLMesh()
  Release()
end destructor

sub GLMesh.Release()
  if (pVertex) then
    deallocate pVertex
    pVertex = 0
  end if
  if(pIndex) then 
    deallocate pIndex
    pIndex = 0
  end if
end sub

function GLMesh.Load(filename as string, ScaleX as SPEFLOAT,ScaleY as SPEFLOAT,ScaleZ as SPEFLOAT) as SPEBOOL
  dim as integer hFile = FreeFile()
  filename = GL_PATH & filename
  if Open(filename,for binary,access read,as #hFile) then
    MessageBox(0, "error: file [" & filename & "] not found !", "GLMesh.Load()", MB_ICONERROR)
    end
  end if
  
  if (pVertex<>0) or (pIndex<>0) then
    Release()
    MessageBox(0, "warning: mesh is in use !", "GLMesh.Load()",  MB_ICONWARNING)
  end if

  
  get #hFile,,nVertex
  dim as ubyte ptr p = callocate(sizeof(SPEVERTEX)*nVertex)
  get #hFile,,*p,sizeof(SPEVERTEX)*nVertex
  pVertex=cptr(SPEVERTEX ptr,p)

  get #hFile,,nIndex

  p = callocate(sizeof(integer)*nIndex)
  get #hFile,,*p,sizeof(integer)*nIndex
 
  pIndex=cptr(integer ptr,p)

  close #hFile

  for i as integer = 0 to nVertex-1
    pVertex[i].p.x*=ScaleX
    pVertex[i].p.y*=ScaleY
    pVertex[i].p.z*=ScaleZ
  next
  return 1

end function

sub GLMesh.Render(pMat as SPEFLOAT ptr)
  glEnableClientState(GL_VERTEX_ARRAY)
  glEnableClientState(GL_NORMAL_ARRAY)
  glVertexPointer(3, GL_FLOAT, sizeof(SPEVERTEX), pVertex)
  glNormalPointer(   GL_FLOAT, sizeof(SPEVERTEX), @pVertex[0].n)
  glPushMatrix()
    glMultMatrixf(pMat)
    glDrawElements GL_TRIANGLES, nIndex, GL_UNSIGNED_INT, pIndex
  glPopMatrix()
  glDisableClientState(GL_VERTEX_ARRAY)
  glDisableClientState(GL_NORMAL_ARRAY)
end sub



sub FluidRender(pFluid as SPEFLUID ptr)
  dim as SPEMESH   ptr pMesh = FluidGetMesh(pFluid)
  dim as SPEVERTEX ptr pVertex = MeshGetVertexPointer(pMesh)
  glEnableClientState(GL_VERTEX_ARRAY)
  glEnableClientState(GL_NORMAL_ARRAY)
  glVertexPointer(3, GL_FLOAT, sizeof(SPEVERTEX),@pVertex->p)
  glNormalPointer(   GL_FLOAT, sizeof(SPEVERTEX),@pVertex->n)
  'glPushMatrix()
  'glLoadIdentity()
  glDrawArrays(GL_TRIANGLES, 0, MeshGetTriangleCount(pMesh)*3)
  'glPopMatrix()
  glDisableClientState(GL_VERTEX_ARRAY)
  glDisableClientState(GL_NORMAL_ARRAY)
end sub



const GROUND_SIZE   = 500.0
const GROUND_OFFSET = 0.0     ' -0.001 polygon offsetting doesn't work well
const GROUND_SCALE  = 1.0/8.0   

const SKY_SIZE   = 1000.0
const SKY_SCALE  = 1.0/16.0 ' sky texture scale (1/size)
const SKY_HEIGHT = 1.0      ' sky height above viewpoint 

dim shared as integer UseFullscreen = 0
dim shared as integer UseShadow     = 1
dim shared as integer UseGroundTex  = 1
dim shared as integer UseGround     = 1
dim shared as integer UseFog        = 1
dim shared as integer UseSkyTex     = 1
dim shared as integer UseSky        = 1
dim shared as integer GroundTex     = 0
dim shared as integer SkyTex        = 0
dim shared as SPEFLOAT FogStart      = 500
dim shared as SPEFLOAT FogEnd        = 1000
dim shared as SPEFLOAT FogDensity    = 0.01

dim shared as SPEVECTOR FogColor    = SPEVECTOR(.5,.5,.5,1)
dim shared as SPEVECTOR SkyColor    = SPEVECTOR(.2,.8,.2,1)
dim shared as SPEVECTOR GroundColor = SPEVECTOR(.6,.3,.1,1)

dim shared as SPEVECTOR LightPos    = SPEVECTOR(0,10,0, 0)

dim shared as SPEFLOAT  SkyOffset     = 0
dim shared as SPEVECTOR Camera = SPEVECTOR(0,5,10)
dim shared as SPEVECTOR   Target = SPEVECTOR(0,2, 0)

function CreateTexture(file     as string   , _
                       REAPEAT  as integer=1, _
                       MIPMAP   as integer=1, _
                       MODULATE as integer=0) as integer
  if ScreenPtr()=0 then
     MessageBox(0,"error: no active OpenGL !","CreateTexture()",MB_ICONERROR)
     beep:sleep:end
  end if
  dim as integer W,H
  dim as integer hFile = FreeFile()
  if open(file, FOR BINARY, ACCESS READ, AS #hFile) then
    MessageBox(0, "error: file [" & file & "] not found !", "CreateTexture()", MB_ICONERROR)
    beep:sleep:end
  end if
  get #hFile,19,w
  get #hFile,  ,h
  close #hFile

  dim as byte ptr pImage = ImageCreate(w,h)
  if bload(file,pImage) then
    MessageBox(0, "error: can't read [" & file & "] !", "CreateTexture()", MB_ICONERROR)
    beep:sleep:end
  end if
  
  dim as integer id
  glGenTextures(1,@id)
  glBindTexture(GL_TEXTURE_2D, id)
  glTexImage2D (GL_TEXTURE_2D, 0, 3, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE,pImage+32)
  
  if (REAPEAT<>0) then
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
  end if

  if (MIPMAP<>0) then
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w,h, GL_BGRA, GL_UNSIGNED_BYTE,pImage+32)
  else
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
  end if

  if (MODULATE<>0) then
    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
  else
    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
  end if

  ImageDestroy pImage
  
  return id
end function

sub InitOpenGL(w as integer,h as integer)
  dim as integer flag = FB.GFX_OPENGL
  if UseFullscreen then flag or=FB.GFX_FULLSCREEN
  ScreenRes w,h,32,,flag
  chdir exepath
  SkyTex    = CreateTexture(TEXTURE_PATH & "sky.bmp")
  GroundTex = CreateTexture(TEXTURE_PATH & "ground.bmp")
  glClearDepth(1.0)
  glEnable(GL_DEPTH_TEST)
  glDepthFunc(GL_LEQUAL)
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
  glViewPort 0,0,w,h
  glMatrixMode GL_PROJECTION
  glLoadIdentity
  gluPerspective 45.0,w/h,0.1,1000.0
  glMatrixMode GL_MODELVIEW
  glLoadIdentity

  glLightfv (GL_LIGHT0, GL_AMBIENT ,SPEVECTOR( 0.8, 0.8, 0.8, 1.0 ))
  glLightfv (GL_LIGHT0, GL_DIFFUSE ,SPEVECTOR( 1.0, 1.0, 1.0, 1.0 ))
  glLightfv (GL_LIGHT0, GL_SPECULAR,SPEVECTOR( 1.0, 1.0, 1.0, 1.0 ))
  'glColor3f (1.0, 1.0, 1.0)
  glEnable(GL_LIGHT0)
  glEnable(GL_LIGHTING)
end sub


sub DrawGround()
  glDisable   (GL_LIGHTING)
  glShadeModel(GL_FLAT)
  glEnable    (GL_DEPTH_TEST)
  glDepthFunc (GL_LESS)
  ' glDepthRange (1,1)

  if (UseGroundTex) then
    glEnable (GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D,GroundTex)
    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
  else
    glDisable (GL_TEXTURE_2D)
    glColor4fv(GroundColor)
  end if

  if UseFog then
    glEnable (GL_FOG)
    glFogi   (GL_FOG_MODE   , GL_EXP ) 'GL_EXP2
    glFogfv  (GL_FOG_COLOR  , FogColor)
    glFogf   (GL_FOG_DENSITY, FogDensity)
    glHint   (GL_FOG_HINT   , GL_NICEST) 
    glFogf   (GL_FOG_START  , FogStart)
    glFogf   (GL_FOG_END    , FogEnd)
  end if
  glBegin (GL_QUADS)
    glNormal3f (0,1,0)
    glTexCoord2f (-ground_size*ground_scale, -ground_size*ground_scale)
    glVertex3f   (-ground_size,ground_offset,-ground_size)
    glTexCoord2f ( ground_size*ground_scale, -ground_size*ground_scale)
    glVertex3f   ( ground_size,ground_offset,-ground_size)
    glTexCoord2f ( ground_size*ground_scale,  ground_size*ground_scale)
    glVertex3f   ( ground_size,ground_offset, ground_size)
    glTexCoord2f (-ground_size*ground_scale,  ground_size*ground_scale)
    glVertex3f   (-ground_size,ground_offset, ground_size)
  glEnd()

  glDisable(GL_FOG)
end sub

sub DrawSky ()
  ' make sure sky depth is as far back as possible
  glDisable (GL_LIGHTING)
  glShadeModel (GL_FLAT)
  glEnable     (GL_DEPTH_TEST)
  glDepthFunc  (GL_LEQUAL)
  glDepthRange (1,1)
    
  if (UseSkyTex) then
    glEnable (GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D,SkyTex)
    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
  else 
    glDisable (GL_TEXTURE_2D)
    glColor4fv(SkyColor)
  end if


  dim as SPEFLOAT x = SKY_SIZE*SKY_SCALE
  dim as SPEFLOAT y = Camera.y + SKY_HEIGHT

  glBegin (GL_QUADS)
   glNormal3f (0,0,-1)
   glTexCoord2f (-x+SkyOffset,-x+SkyOffset)
   glVertex3f   (-SKY_SIZE+Camera.x,y,-SKY_SIZE+Camera.z)
   glTexCoord2f (-x+SkyOffset,x+SkyOffset)
   glVertex3f   (-SKY_SIZE+Camera.x,y, SKY_SIZE+Camera.z)
   glTexCoord2f ( x+SkyOffset,x+SkyOffset)
   glVertex3f   ( SKY_SIZE+Camera.x,y, SKY_SIZE+Camera.z)
   glTexCoord2f ( x+SkyOffset,-x+SkyOffset)
   glVertex3f   ( SKY_SIZE+Camera.x,y,-SKY_SIZE+Camera.z)
  glEnd()

  SkyOffset = SkyOffset + 0.0001
  if (SkyOffset > 1) then
    SkyOffset = SkyOffset - 1.0
  end if
  'glClear(GL_DEPTH_BUFFER_BIT)  
  
  glDepthFunc (GL_LESS)
  glDepthRange (0,1)
  'glEnable(GL_LIGHTING)
  'glDisable(GL_FOG) 
end sub

sub BeginFrame
  glClearColor(FogColor.x,FogColor.y,FogColor.z,1.0)
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
  glLoadIdentity
  gluLookAt(Camera.x,Camera.y,Camera.z, _
            Target.x,Target.y,Target.z, _
            0,1,0)
  glLightfv(GL_LIGHT0, GL_POSITION,LightPos)
  if UseFog    then 
    glEnable(GL_FOG)
  else
    glDisable(GL_FOG)
  end if
  if UseSky    then DrawSky()
  if UseGround then DrawGround()
  glEnable     (GL_LIGHTING)
  glEnable     (GL_LIGHT0)
  glDisable    (GL_TEXTURE_2D)
  glShadeModel (GL_SMOOTH)
  glEnable     (GL_DEPTH_TEST)
  glDepthFunc  (GL_LESS)
end sub

sub EndFrame
  flip
end sub

#endif ' __GLMESH_BI__
