/'===============================================================================================
 geometry.exe main.cpp
 Copyright (c), Firelight Technologies Pty, Ltd 2005-2011.

 Example to show occlusion
==============================================================================================='/
#include "windows.bi"
#include "crt.bi"
#include "fmodex/fmodex.bi"
#include "fmodex/fmod_errors.bi"

#include "gl/glut.bi"
#ifndef GLUT_BITMAP_HELVETICA_18
#Define GLUT_BITMAP_HELVETICA_18    Cast(Any Ptr, &h0008)
#endif

sub ERRCHECK(byval result as FMOD_RESULT)
    if (result <> FMOD_OK) then
        printf(!"FMOD error! (%d) %s\n", result, FMOD_ErrorString(result))
        end -1
end if
end sub

dim shared as integer INTERFACE_UPDATETIME = 17 ' milliseconds

' window size
dim shared as integer width_ = 500
dim shared as integer height = 500

' mouse control
dim shared as bool doRotate = false
dim shared as integer xMouse = 0
dim shared as integer yMouse = 0

' listener orientation
dim shared as float xRotation = 0.0f
dim shared as float yRotation = -90.0f

' listener position
dim shared as float xListenerPos = 30.0f
dim shared as float yListenerPos = 1.3f
dim shared as float zListenerPos = 0.4f

' keyboard control
dim shared as bool moveForward = false
dim shared as bool moveBackward = false
dim shared as bool moveLeft = false
dim shared as bool moveRight = false
dim shared as bool moveUp = false
dim shared as bool moveDown = false

const as float PI = 3.14159265f

dim shared as float accumulatedTime = 0.0f

dim shared as GLuint texture

' sounds placement
type Obj
	as float xPos
	as float yPos
	as float zPos
	as float intensity
	as integer sound
	as FMOD_CHANNEL ptr channel
end type

const as integer NUM_OBJECTS = 7
dim shared as Obj objects(NUM_OBJECTS-1) = _
{ _
	type(  -11.0f,    1.0f,    0.0f,    1.0f,    0,    0 ), _
	type(   12.0f,    2.0f,    0.0f,    1.0f,    1,    0), _
	type(   45.0f,    1.0f,    0.0f,    1.0f,    3,    0), _
	type(  -30.0f,    1.0f,   21.0f,    1.0f,    2,    0), _
	type(  -30.0f,    1.0f,  -21.0f,    1.0f,    3,    0), _
	type(   12.0f,    1.0f,  -27.0f,    1.0f,    0,    0), _
	type(    4.0f,    1.0f,   16.0f,    1.0f,    0,    0) _
}

' geometry structures for loading and drawing
type Polygon
	as integer numVertices
	as integer indicesOffset
	as float directOcclusion
	as float reverbOcclusion
	as FMOD_VECTOR normal
end type

type Mesh_
	as integer numVertices
	as FMOD_VECTOR ptr vertices
	as float ptr texcoords0
	as float ptr texcoords1
	as integer numPolygons
	as Polygon ptr polygons
	as integer numIndices
	as integer ptr indices
	as FMOD_GEOMETRY ptr geometry
end type


dim shared as mesh_ walls
dim shared as mesh_ rotatingMesh
dim shared as mesh_ doorList(4-1)

' fmod sounds structures
dim shared as FMOD_SYSTEM    ptr fmodSystem = 0
dim shared as FMOD_SOUND     ptr sounds(4-1) = { 0, 0, 0, 0}
dim shared as FMOD_GEOMETRY 	ptr geometry = 0

sub initGeometry(byval szFileName as zstring ptr, byref mesh as mesh_)
	dim as FMOD_RESULT result
	dim as FILE ptr file = fopen(szFileName, "rb")
	if (file) then
		' read vertices
		fread(@mesh.numVertices, sizeof(mesh.numVertices), 1, file)
		mesh.vertices = calloc(mesh.numVertices,sizeof(FMOD_VECTOR))
		mesh.texcoords0 = calloc(mesh.numVertices, sizeof(float))
		mesh.texcoords1 = calloc(mesh.numVertices, sizeof(float))
		fread(mesh.vertices, sizeof (float) * 3, mesh.numVertices, file)	
		fread(mesh.texcoords0, sizeof (float), mesh.numVertices, file)	
		fread(mesh.texcoords1, sizeof (float), mesh.numVertices, file)	
		fread(@mesh.numIndices, sizeof (mesh.numIndices), 1, file)
		mesh.indices = calloc(mesh.numIndices,sizeof(integer))
		fread(mesh.indices, sizeof (integer), mesh.numIndices, file)	
		
		fread(@mesh.numPolygons, sizeof (mesh.numPolygons), 1, file)
		mesh.polygons = calloc(mesh.numPolygons,sizeof(Polygon))
		' read polygons
		for poly as integer = 0 to mesh.numPolygons-1
			dim as Polygon ptr vpolygon = @mesh.polygons[poly]


			fread(@vpolygon->numVertices, sizeof(vpolygon->numVertices), 1, file)
			fread(@vpolygon->indicesOffset, sizeof (vpolygon->indicesOffset), 1, file)
			fread(@vpolygon->directOcclusion, sizeof (vpolygon->directOcclusion), 1, file)
			fread(@vpolygon->reverbOcclusion, sizeof (vpolygon->reverbOcclusion), 1, file)

			dim as integer ptr indices = @mesh.indices[vpolygon->indicesOffset]			

			' calculate polygon normal
			dim as float xN = 0.0f
			dim as float yN = 0.0f
			dim as float zN = 0.0f
			' todo: return an error if a polygon has less then 3 vertices.
			for vertex as integer= 0 to vpolygon->numVertices - 2-1
				dim as float xA = mesh.vertices[indices[vertex + 1]].x -mesh.vertices[indices[0]].x
				dim as float yA = mesh.vertices[indices[vertex + 1]].y -mesh.vertices[indices[0]].y
				dim as float zA = mesh.vertices[indices[vertex + 1]].z -mesh.vertices[indices[0]].z
				dim as float xB = mesh.vertices[indices[vertex + 2]].x -mesh.vertices[indices[0]].x
				dim as float yB = mesh.vertices[indices[vertex + 2]].y -mesh.vertices[indices[0]].y
				dim as float zB = mesh.vertices[indices[vertex + 2]].z -mesh.vertices[indices[0]].z
				' cross product
				xN += yA * zB - zA * yB
				yN += zA * xB - xA * zB
				zN += xA * yB - yA * xB
			next	
			dim as float fMagnidued = sqr(xN * xN + yN * yN + zN * zN)
			if (fMagnidued > 0.0f) then' a tollerance here might be called for
				xN /= fMagnidued
				yN /= fMagnidued
				zN /= fMagnidued
			end if
			vpolygon->normal.x = xN
			vpolygon->normal.y = yN
			vpolygon->normal.z = zN
		next
		fclose(file)
	end if
	result = FMOD_SYSTEM_createGeometry(fmodSystem,mesh.numPolygons, mesh.numIndices, @mesh.geometry)
	ERRCHECK(result)

	for  poly as integer = 0 to mesh.numPolygons-1
		dim as Polygon ptr vpolygon = @mesh.polygons[poly]
		dim as FMOD_VECTOR vertices(16-1)
        dim as integer i

		for i = 0 to vpolygon->numVertices-1
			vertices(i) = mesh.vertices[mesh.indices[vpolygon->indicesOffset + i]]
        next

        dim as integer polygonIndex = 0
		result = FMOD_Geometry_AddPolygon(mesh.geometry,vpolygon->directOcclusion, vpolygon->reverbOcclusion, false, _ ' single sided
                                           vpolygon->numVertices, _
			                               @vertices(0), _
			                              @polygonIndex)
		ERRCHECK(result)
	next
end sub

sub freeGeometry(byref mesh as mesh_)
    FMOD_Geometry_Release(mesh.geometry)

    free(mesh.vertices)
	free ( mesh.texcoords0)
	free ( mesh.texcoords1)
	free(mesh.polygons)
	free(mesh.indices)
end sub

sub drawGeometry(byref mesh as mesh_)
	dim as FMOD_RESULT result

	dim as FMOD_VECTOR position
	result = FMOD_Geometry_GetPosition(mesh.geometry,@position)
	ERRCHECK(result)

	glPushMatrix()
	' create matrix and set gl transformation for geometry
	glTranslatef(position.x, position.y, position.z)	
dim as 	FMOD_VECTOR forward
	dim as FMOD_VECTOR up
	result = FMOD_Geometry_GetRotation(mesh.geometry,@forward, @up)
	ERRCHECK(result)
	dim as float matrix(16-1) = _
	{ _
		up.y * forward.z - up.z * forward.y,		up.x,		forward.x,		0.0f, _
		up.z * forward.x - up.x * forward.z,		up.y,		forward.y,		0.0f, _
		up.x * forward.y - up.y * forward.x,		up.z,		forward.z,		0.0f, _
		0.0f,										0.0f,		0.0f,			1.0f _
	}
	glMultMatrixf(@matrix(0))

	' draw all polygons in object
	glEnable(GL_LIGHTING)	
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
	for poly as integer = 0 to mesh.numPolygons-1
		dim as Polygon ptr vpolygon = @mesh.polygons[poly]
		if (vpolygon->directOcclusion = 0.0f) then
			continue for ' don't draw because it is an open door way
        end if

		glBegin(GL_TRIANGLE_FAN)
		glNormal3fv(@vpolygon->normal.x)

		for i as integer = 0 to vpolygon->numVertices-1
			dim as integer index = mesh.indices[vpolygon->indicesOffset + i]
			glTexCoord2f(mesh.texcoords0[index], mesh.texcoords1[index])
			glVertex3fv(@mesh.vertices[index].x)
		next
		glEnd()
	next
	glPopMatrix()
end sub

sub initObjects()
	dim as FMOD_RESULT result
    dim as integer i

	for i = 0 to NUM_OBJECTS-1
		' play object sounds
		dim as FMOD_VECTOR position = type( objects(i).xPos, objects(i).yPos, objects(i).zPos )
		dim as FMOD_VECTOR vel = type(  0.0f,  0.0f, 0.0f )

		result = FMOD_System_PlaySound(fmodSystem,FMOD_CHANNEL_FREE, sounds(objects(i).sound), true, @objects(i).channel)
		ERRCHECK(result)
		result = FMOD_Channel_Set3DAttributes(objects(i).channel, @position, @vel)
		ERRCHECK(result)
		result = FMOD_Channel_Set3DSpread(objects(i).channel, 40.0f)
		ERRCHECK(result)
		result = FMOD_Channel_SetPaused(objects(i).channel, false)
		ERRCHECK(result)
	next
end sub

sub updateObjectSoundPos(byval obj as Obj ptr)
	dim as FMOD_RESULT result
	dim as FMOD_VECTOR position = type( obj->xPos, obj->yPos, obj->zPos )
	dim as FMOD_VECTOR oldPosition
	FMOD_Channel_Get3DAttributes(obj->channel, @oldPosition, 0)

	dim as FMOD_VECTOR vel
	vel.x = (position.x - oldPosition.x) *  (1000.0f / INTERFACE_UPDATETIME)
	vel.y = (position.y - oldPosition.y) *  (1000.0f / INTERFACE_UPDATETIME)
	vel.z = (position.z - oldPosition.z) *  (1000.0f / INTERFACE_UPDATETIME)
	result = FMOD_Channel_Set3DAttributes(obj->channel, @position, @vel)
	ERRCHECK(result)
end sub

sub mouseFunc cdecl(byval button as integer, byval state as integer, byval x as integer, byval y as integer)
select case button 
	case GLUT_LEFT_BUTTON:
		if (state = GLUT_DOWN) then
			doRotate = true
			xMouse = x
			yMouse = y
		ElseIf (state = GLUT_UP) then
			doRotate = false
		end if
end select
end sub

sub motionFunc cdecl(byval  x as integer, byval y as integer)
	dim as integer dx = x - xMouse
	dim as integer dy = y - yMouse

	' view rotation about y-axis
	yRotation += cast(float,dx * 0.5f)
	if (yRotation > 180.0f) then
		yRotation -= 360.0f
	ElseIf (yRotation < -180.0f) then
		yRotation += 360.0f
end if

	' view rotation about x-axis
	const as float xExtent = 88.0f
	xRotation += cast(float,dy * 0.5f)
	if (xRotation > xExtent) then
		xRotation = xExtent
	ElseIf (xRotation < -xExtent) then
		xRotation = -xExtent
end if

	xMouse = x
	yMouse = y
end sub

sub doGeometryMovement()
	dim as FMOD_RESULT result

	' example of moving individual polygon vertices
	dim as integer xGeometryWarpPos = -30
	dim as integer zGeometryWarpPos = -21
	dim as integer dx = xListenerPos - xGeometryWarpPos
	dim as integer dz = zListenerPos - zGeometryWarpPos
	if (dx * dx + dz * dz < 30 * 30) then
		if (sin(accumulatedTime * 1.0f) > 0.0f) then
			static as FMOD_VECTOR lastOffset = type( 0.0f, 0.0f, 0.0f )
			dim as FMOD_VECTOR offset = type( sin(accumulatedTime * 2.0f), 0.0f, cos(accumulatedTime * 2.0f) )
			for poly as integer = 0 to walls.numPolygons-1
				dim as Polygon ptr vpolygon = @walls.polygons[poly]
				for i as integer = 0 to vpolygon->numVertices-1
					dim as FMOD_VECTOR ptr vertex = @walls.vertices[walls.indices[vpolygon->indicesOffset + i]]

					dx = vertex->x - xGeometryWarpPos
					dz = vertex->z - zGeometryWarpPos
					if (dx * dx + dz * dz > 90) then
						continue for
end if
					vertex->x -= lastOffset.x
					vertex->y -= lastOffset.y
					vertex->z -= lastOffset.z

					vertex->x += offset.x
					vertex->y += offset.y
					vertex->z += offset.z
					result = FMOD_Geometry_SetPolygonVertex(walls.geometry, poly, i, vertex)
					ERRCHECK(result)
				next
			next
			lastOffset = offset
		end if
	end if

	' example of rotation and a geometry object
	dim as FMOD_VECTOR up = type( 0.0f, 1.0f, 0.0f )
	dim as FMOD_VECTOR forward = type( sin(accumulatedTime * 0.5f), 0.0f, cos(accumulatedTime * 0.5f) )
	result = FMOD_Geometry_SetRotation(rotatingMesh.geometry, @forward, @up)
	ERRCHECK(result)
	dim as FMOD_VECTOR position
	position.x = 12.0f
	position.y = sin(accumulatedTime) * 0.4f + 0.1f
	position.z = 0.0f
	result = FMOD_Geometry_SetPosition(rotatingMesh.geometry, @position)
	ERRCHECK(result)

	' example of moving doors
	' door 1
	position.x = 3.25f
	position.y = (sin(accumulatedTime)) * 2.0f + 1.0f
	if (position.y < 0.0f) then
		position.y = 0
end if
	if (position.y > 2.0f) then
		position.y = 2.0f
end if
	position.z = 11.5f
	result = FMOD_Geometry_SetPosition(doorList(0).geometry, @position)
	ERRCHECK(result)

	' door 2
	position.x = 0.75f
	position.y = (sin(accumulatedTime)) * 2.0f + 1.0f
	if (position.y < 0.0f) then
		position.y = 0
end if
	if (position.y > 2.0f) then
		position.y = 2.0f
end if
	position.z = 14.75f
	result = FMOD_Geometry_SetPosition(doorList(1).geometry, @position)
	ERRCHECK(result)

	' door 3
	position.x = 8.25f
	position.y = (sin(accumulatedTime)) * 2.0f + 1.0f
	if (position.y < 0.0f) then
		position.y = 0
end if
	if (position.y > 2.0f) then
		position.y = 2.0f
end if
	position.z = 14.75f
	result = FMOD_Geometry_SetPosition(doorList(2).geometry, @position)
	ERRCHECK(result)

	' door 4
	position.x = 33.0f
	position.y = sin(accumulatedTime) * 2.0f + 1.0f
	if (position.y < 0.0f) then
		position.y = 0
end if
	if (position.y > 2.0f) then
		position.y = 2.0f
end if
	position.z = -0.75f
	result = FMOD_Geometry_SetPosition(doorList(3).geometry, @position)
	ERRCHECK(result)
end sub

sub  doSoundMovement()
	objects(0).zPos = 10.0f * sin(accumulatedTime)
	updateObjectSoundPos(@objects(0))
	objects(5).zPos = -22 + 8.0f * sin(accumulatedTime)
	updateObjectSoundPos(@objects(5))
end sub

sub doListenerMovement()
	' Update user movement
	const as float MOVEMENT_SPEED = 0.1f
	dim as float forward = 0.0f

	if (moveForward) then
		forward += MOVEMENT_SPEED
end if
	if (moveBackward) then
		forward -= MOVEMENT_SPEED
end if

	dim as float right_ = 0.0f
	if (moveLeft) then
		right_ -= MOVEMENT_SPEED
end if
	if (moveRight) then
		right_ += MOVEMENT_SPEED	
end if

	dim as float up = 0.0f
    if (moveUp) then
        up += MOVEMENT_SPEED
end if
    if (moveDown) then
        up -= MOVEMENT_SPEED
	end if

	dim as float xRight = cos(yRotation * (PI / 180.0f))
	dim as float yRight = 0.0f
	dim as float zRight = sin(yRotation * (PI / 180.0f))

	xListenerPos += xRight * right_
	yListenerPos += yRight * right_
	zListenerPos += zRight * right_

	dim as float xForward = sin(yRotation * (PI / 180.0f)) * cos(xRotation  * (PI / 180.0f))
	dim as float yForward = -sin(xRotation  * (PI / 180.0f))
	dim as float zForward = -cos(yRotation * (PI / 180.0f)) * cos(xRotation  * (PI / 180.0f))

	xListenerPos += xForward * forward
	yListenerPos += yForward * forward
	zListenerPos += zForward * forward

	yListenerPos += up

	if (yListenerPos < 1.0f) then
		yListenerPos = 1.0f
    end if

	' cross product
	dim as float xUp = yRight * zForward - zRight * yForward
	dim as float yUp = zRight * xForward - xRight * zForward
	dim as float zUp = xRight * yForward - yRight * xForward

	' Update listener
	scope
		dim as FMOD_VECTOR listenerVector
		listenerVector.x = xListenerPos
		listenerVector.y = yListenerPos
		listenerVector.z = zListenerPos

		static as FMOD_VECTOR lastpos = type( 0.0f, 0.0f, 0.0f )
		static as bool bFirst = true
		dim as FMOD_VECTOR forward
		dim as FMOD_VECTOR up
		dim as FMOD_VECTOR vel

		forward.x = xForward
		forward.y = yForward
		forward.z = zForward
		up.x = xUp
		up.y = yUp
		up.z = zUp

		' ********* NOTE ******* READ NEXT COMMENT!!!!!
		' vel = how far we moved last FRAME (m/f), then time compensate it to SECONDS (m/s).
		vel.x = (listenerVector.x - lastpos.x) * (1000.0f / INTERFACE_UPDATETIME)
		vel.y = (listenerVector.y - lastpos.y) * (1000.0f / INTERFACE_UPDATETIME)
		vel.z = (listenerVector.z - lastpos.z) * (1000.0f / INTERFACE_UPDATETIME)
		if (bFirst) then
			bFirst = false
			vel.x = 0
			vel.y = 0
			vel.z = 0
		end if

		static as FMOD_VECTOR lastVel = type( 0.0f, 0.0f, 0.0f )

		' store pos for next time
		lastpos = listenerVector
		lastVel = vel

		dim as FMOD_RESULT result = FMOD_System_Set3DListenerAttributes(fmodSystem,0, @listenerVector, @vel, @forward, @up)
		ERRCHECK(result)
	end scope
end sub

sub timerFunc cdecl(byval  nValue as integer)
	dim as FMOD_RESULT result

	doGeometryMovement()

	doSoundMovement()
	doListenerMovement()
	
    result = FMOD_System_Update(fmodSystem)
	ERRCHECK(result)

	accumulatedTime += INTERFACE_UPDATETIME / 1000.0f

	glutPostRedisplay()
	glutTimerFunc(INTERFACE_UPDATETIME, @timerFunc, 0)

    if (0) then
        dim as float dsp, stream, geom, update, total
        dim as integer chansplaying

        FMOD_System_GetCPUUsage(fmodSystem, @dsp, @stream, @geom, @update, @total)
        FMOD_System_GetChannelsPlaying(fmodSystem, @chansplaying)

        printf(!"chans %d : cpu : dsp = %.02f stream = %.02f geometry = %.02f update = %.02f total = %.02f\n", chansplaying, dsp, stream, geometry, update, total)
    end if
end sub

sub  keyboardFunc  cdecl(byval key as ubyte, byval x as integer, byval y as integer)
select case key
	case asc("w"):
		moveForward = true
	case asc("s"):
		moveBackward = true
	case asc("a"):
		moveLeft = true
	case asc("d"):
		moveRight = true
	case asc(" "):
		moveUp = true
	case asc("c"):
		moveDown = true
	end select
end sub

sub keyboardUpFunc cdecl(byval key as ubyte, byval x as integer, byval y as integer)
select case key
	case asc("w"):
		moveForward = false
	case asc("s"):
		moveBackward = false
	case asc("a"):
		moveLeft = false
	case asc("d"):
		moveRight = false
	case asc(" "):
		moveUp = false
	case asc("c"):
		moveDown = false
	end select
end sub

sub outputText(byval x as integer, byval y as integer, byval textstring as zstring ptr)
	dim as integer lenstring, i
	
	glRasterPos2f(x, y)
	lenstring = strlen(textstring)
	for i = 0 to lenstring-1
dim character as integer = textstring[i]
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, character)
	next
end sub

sub display cdecl()
	' update view	
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective( _
		60.0,	_							' fov
		width_ / height,	_	' aspect
		0.1,	_							' near
		500.0	_							' far
		)


	glRotatef(xRotation, 1.0f, 0.0f, 0.0f)
	glRotatef(yRotation, 0.0f, 1.0f, 0.0f)
	glTranslatef(-xListenerPos, -yListenerPos, -zListenerPos)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()

	' clear
	glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
	glClearColor(0.4, 0.6f, 1.0f, 0.0f)

	glEnable(GL_TEXTURE_2D)
	glBindTexture( GL_TEXTURE_2D, texture )

	' draw geometry
	drawGeometry(walls)
	drawGeometry(rotatingMesh)
	drawGeometry(doorList(0))
	drawGeometry(doorList(1))
	drawGeometry(doorList(2))
	drawGeometry(doorList(3))


	
	glDisable(GL_TEXTURE_2D)


	' draw sound objects
	dim as integer obj
	for obj = 0 to NUM_OBJECTS-1
		dim as float directOcclusion = 1.0f
		dim as float reverbOcclusion = 1.0f

		' set colour baced on direct occlusion
FMOD_Channel_Get3DOcclusion(		objects(obj).channel, @directOcclusion, @reverbOcclusion)
		dim as float intensity = 1.0f - directOcclusion
				
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
		glPushMatrix()
		glTranslatef(objects(obj).xPos, objects(obj).yPos, objects(obj).zPos)

		glPushAttrib(GL_LIGHTING_BIT)
			
		intensity *= 0.75f
		dim as float color_(4-1) = { intensity, intensity, 0.0f, 0.0f }
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, @color_(0))
		intensity *= 0.5f
		dim as float ambient(4) = { intensity, intensity, 0.0f, 0.0f }
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, @ambient(0))
		
		glRotatef(accumulatedTime * 200.0f, 0.0f, 1.0f, 0.0f)
		glutSolidTorus(0.15f, 0.6f, 8, 16)
		glPopAttrib()
		glPopMatrix()
	next

	' finish
	glutSwapBuffers()
end sub

sub reshapeFunc cdecl(byval w as integer, byval h as integer)
	width_ = w
	height = h
	glViewport(0, 0, w, h)
end sub

function loadTexture(byval  filename as zstring ptr) as GLuint
    dim as GLuint texture
    dim as integer width_
	dim as integer height
    dim as ubyte ptr data_
    dim as FILE ptr file

    ' open texture data
    file = fopen( filename, "rb" )
    if ( file = NULL ) then
		return 0
end if

    width_ = 128
    height = 128
    data_ = malloc(width_ * height * 3)

    fread(data_, width_ * height * 3, 1, file )
    fclose(file)

    glGenTextures(1, @texture)
    glBindTexture(GL_TEXTURE_2D, texture)

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST )
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR )
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)

    gluBuild2DMipmaps( _
		GL_TEXTURE_2D, _
		3, _
		width_, _
		height, _
        GL_RGB, _
		GL_UNSIGNED_BYTE, _
		data_)

    free(data_)

    return texture
end function


sub init()
    dim as FMOD_RESULT      result
    dim as bool             listenerflag = true
    dim as FMOD_VECTOR      listenerpos  = type( 0.0f, 0.0f, 0.0f )
    dim as uinteger     version
    dim as FMOD_SPEAKERMODE speakermode
    dim as FMOD_CAPS        caps
    dim as zstring * 256             devicename
    dim as integer              numdrivers

    printf(!"==================================================================\n")
    printf(!"Geometry example.  Copyright (c) Firelight Technologies 2004-2011.\n")
    printf(!"==================================================================\n\n")

    /'
        Create a System object and initialize.
    '/
    result = FMOD_System_Create(@fmodSystem)
    ERRCHECK(result)
    
    result = FMOD_System_GetVersion             (fmodSystem, @version)
    ERRCHECK(result)

    if (version < FMOD_VERSION) then
        printf(!"Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION)
        end -1
    end if

    result = FMOD_System_GetNumDrivers(fmodSystem, @numdrivers)
    ERRCHECK(result)

    if (numdrivers = 0) then
        result = FMOD_System_SetOutput(fmodSystem, FMOD_OUTPUTTYPE_NOSOUND)
        ERRCHECK(result)
    else
            result = FMOD_System_GetDriverCaps(fmodSystem, 0, @caps, 0, @speakermode)
        ERRCHECK(result)

        result = FMOD_System_SetSpeakerMode(fmodSystem, speakermode)       /' Set the user selected speaker mode. '/
        ERRCHECK(result)

        if (caps and FMOD_CAPS_HARDWARE_EMULATED) then            /' The user has the 'Acceleration' slider set to off!  This is really bad for latency!. '/
                                                           /' You might want to warn the user about this. '/
            result = FMOD_System_SetDSPBufferSize(fmodSystem, 1024, 10)
            ERRCHECK(result)
        end if

        result = FMOD_System_GetDriverInfo(fmodSystem, 0, @devicename, 256, 0)
        ERRCHECK(result)

        if (strstr(devicename, "SigmaTel")) then  /' Sigmatel sound devices crackle for some reason if the format is PCM 16bit.  PCM floating point output seems to solve it. '/
            result = FMOD_System_SetSoftwareFormat(fmodSystem, 48000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,0, FMOD_DSP_RESAMPLER_LINEAR)
            ERRCHECK(result)
        end if
    end if

    result = FMOD_System_Init(fmodSystem, 100, FMOD_INIT_3D_RIGHTHANDED or FMOD_INIT_OCCLUSION_LOWPASS or FMOD_INIT_HRTF_LOWPASS or FMOD_INIT_GEOMETRY_USECLOSEST, 0)
    if (result = FMOD_ERR_OUTPUT_CREATEBUFFER) then        /' Ok, the speaker mode selected isn't supported by this soundcard.  Switch it back to stereo... '/
        result = FMOD_System_SetSpeakerMode(fmodSystem, FMOD_SPEAKERMODE_STEREO)
        ERRCHECK(result)
                
        result = FMOD_System_Init(fmodSystem, 100, FMOD_INIT_3D_RIGHTHANDED or FMOD_INIT_OCCLUSION_LOWPASS or FMOD_INIT_HRTF_LOWPASS or FMOD_INIT_GEOMETRY_USECLOSEST, 0)/' ... and re-init. '/
        ERRCHECK(result)
    end if
  
    ' Load sounds
    result = FMOD_System_CreateSound(fmodSystem, "media/drumloop.wav", FMOD_SOFTWARE or FMOD_3D, 0, @sounds(0))
    ERRCHECK(result)
    result = FMOD_Sound_Set3DMinMaxDistance(sounds(0), 1.0f, 10000.0f)
    ERRCHECK(result)
    result = FMOD_Sound_SetMode(sounds(0), FMOD_LOOP_NORMAL)
    ERRCHECK(result)

    result = FMOD_System_CreateSound(fmodSystem, "media/wave.mp3", FMOD_SOFTWARE or FMOD_CREATECOMPRESSEDSAMPLE or  FMOD_3D, 0, @sounds(1))
    ERRCHECK(result)
    result = FMOD_Sound_Set3DMinMaxDistance(sounds(1), 1.0f, 10000.0f)
    ERRCHECK(result)
    result = FMOD_Sound_SetMode(sounds(1), FMOD_LOOP_NORMAL)
    ERRCHECK(result)

    result = FMOD_System_CreateSound(fmodSystem, "media/jaguar.wav", FMOD_SOFTWARE or FMOD_3D, 0, @sounds(2))
    ERRCHECK(result)
    result = FMOD_Sound_Set3DMinMaxDistance(sounds(2), 1.0f, 10000.0f)
    ERRCHECK(result)
    result = FMOD_Sound_SetMode(sounds(2), FMOD_LOOP_NORMAL)
    ERRCHECK(result)

    result = FMOD_System_CreateSound(fmodSystem, "media/swish.wav", FMOD_SOFTWARE or FMOD_3D, 0, @sounds(3))
    ERRCHECK(result)
    result = FMOD_Sound_Set3DMinMaxDistance(sounds(3), 1.0f, 10000.0f)
    ERRCHECK(result)
    result = FMOD_Sound_SetMode(sounds(3), FMOD_LOOP_NORMAL)
    ERRCHECK(result)

		
	initObjects()

	result = FMOD_System_SetGeometrySettings(fmodSystem, 200.0f)
    ERRCHECK(result)

	' load objects
	initGeometry("media/walls.bin", walls)
	initGeometry("media/center.bin", rotatingMesh)
	initGeometry("media/door.bin", doorList(0))
	initGeometry("media/door.bin", doorList(1))
	initGeometry("media/door.bin", doorList(2))
	initGeometry("media/door.bin", doorList(3))

	' place doors in desired orientations
	dim as FMOD_VECTOR up      = type( 0.0f, 1.0f, 0.0f )
	dim as FMOD_VECTOR forward = type( 1.0f, 0.0f, 0.0f )
	result = FMOD_Geometry_SetRotation(doorList(1).geometry, @forward, @up)
    ERRCHECK(result)
	result = FMOD_Geometry_SetRotation(doorList(2).geometry, @forward, @up)
    ERRCHECK(result)
	result = FMOD_Geometry_SetRotation(doorList(3).geometry, @forward, @up)
    ERRCHECK(result)

	texture = loadTexture("media/texture.img")		
	' setup lighting
	dim as GLfloat lightDiffuse(0 to ...) = {1.0, 1.0, 1.0, 1.0} 
	dim as GLfloat lightPosition(0 to ...) = {300.0, 1000.0, 400.0, 0.0}
	dim as GLfloat lightAmbiant(0 to ...) = {1.25, 1.25, 1.25, 1.0}
	glLightfv(GL_LIGHT0, GL_DIFFUSE, @lightDiffuse(0))
	glLightfv(GL_LIGHT0, GL_POSITION, @lightPosition(0))
	glLightfv(GL_LIGHT0, GL_AMBIENT, @lightAmbiant(0))	
	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0f)
	glEnable(GL_LIGHT0)
	glEnable(GL_LIGHTING)	
	
	glEnable(GL_DEPTH_TEST)

	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
end sub

sub release()
    glDeleteTextures( 1, @texture ) 

	freeGeometry(walls)
	freeGeometry(rotatingMesh)
	freeGeometry(doorList(0))
	freeGeometry(doorList(1))
	freeGeometry(doorList(2))
	freeGeometry(doorList(3))

    FMOD_Sound_Release(sounds(0))
    FMOD_Sound_Release(sounds(1))
    FMOD_Sound_Release(sounds(2))
    FMOD_Sound_Release(sounds(3))

    FMOD_System_Release(fmodSystem)
end sub


' program start

dim argc as integer = __FB_ARGC__
dim argv as zstring ptr ptr = __FB_ARGV__ 
	glutInit(argc, cast(byte ptr,argv))

	glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH)
	glutInitWindowSize(width_, height)
	glutCreateWindow("FMOD Geometry example.")
	glutDisplayFunc(@display)	
	glutReshapeFunc(@reshapeFunc)
	glutMouseFunc(@mouseFunc)
	glutMotionFunc(@motionFunc)
	glutKeyboardFunc(@keyboardFunc)
	glutKeyboardUpFunc(@keyboardUpFunc)
	glutTimerFunc(INTERFACE_UPDATETIME, @timerFunc, 0)

	init()
	glutMainLoop()
release()	
