/'===============================================================================================
 3D Demo Example
 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 "fmodex/fmod_event.bi"
#include "fmodex/fmod_event_net.bi"

#include "gl/glut.bi"
#include "SOIL.bi"

#ifndef GLUT_BITMAP_HELVETICA_18
#Define GLUT_BITMAP_HELVETICA_18    Cast(Any Ptr, &h0008)
#endif


#define GL_CLAMP_TO_EDGE &H812F

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

dim shared as bool fullscreen = false

' 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 = -4.0f
dim shared as float yListenerPos = 1.3f
dim shared as float zListenerPos = 0.0f

' 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
dim shared as bool moveFast       = false
dim shared as bool ambientVolUp   = false
dim shared as bool ambientVolDown = false
dim shared as bool masterVolUp    = false
dim shared as bool masterVolDown  = false

const as float PI = 3.14159265f
dim shared as GLenum  rendermode = GL_FILL
dim shared as float accumulatedTime = 0.0f

' textures
dim shared as GLuint texture
dim shared as GLuint skyboxTexture(6-1)

' sounds placement
type Obj
	as float xPos
	as float yPos
	as float zPos
	as float intensity
	as integer sound
    as FMOD_Event ptr event
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 structers 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_EventSystem     ptr fmodEventSystem    = 0
dim shared as FMOD_EventProject    ptr fmodEventProject   = 0
dim shared as FMOD_EventGroup      ptr fmodEventGroup     = 0
dim shared as FMOD_EventParameter  ptr fmodEventParameter = 0
dim shared as FMOD_System          ptr fmodSystem         = 0
dim shared as FMOD_Geometry	      ptr geometry           = 0
dim shared as FMOD_DSP             ptr global_lowpass     = 0

dim shared as float   ambientVolume   = 0.2f
dim shared as float   masterVolume


sub initGeometry(byval szFileName as zstring ptr, byref mesh as Mesh_, byval alter as BOOL = false)
	dim as FMOD_RESULT result = any

	dim as FILE ptr file = fopen(szFileName, "rb")
	if (file) then
		' read vertices
		fread(@mesh.numVertices, sizeof (mesh.numVertices), 1, file)	
		mesh.vertices = CAllocate(mesh.numVertices,sizeof(FMOD_VECTOR))
		mesh.texcoords0 = CAllocate(mesh.numVertices, sizeof(float))
		mesh.texcoords1 = CAllocate(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 = CAllocate(mesh.numIndices,sizeof(integer))
		fread(mesh.indices, sizeof (integer), mesh.numIndices, file)	

		
		fread(@mesh.numPolygons, sizeof (mesh.numPolygons), 1, file)
		mesh.polygons = CAllocate (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 = sqrt(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)

    /'
        Tell FMOD about the geometry
    '/
	for poly as integer = 0 to mesh.numPolygons-1
		dim as Polygon ptr vpolygon = @mesh.polygons[poly]
		dim as FMOD_VECTOR vertices(16-1) = any
		for i as integer = 0 to vpolygon->numVertices-1
			vertices(i) = mesh.vertices[mesh.indices[vpolygon->indicesOffset + i]]
  next
		dim as integer polygonIndex = 0
        
        if (alter andalso vpolygon->directOcclusion = 0.85f) then
     '       vpolygon->directOcclusion = 0.95f
        end if

		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)

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


sub inWater()
    dim as FMOD_RESULT result = any
    dim as FMOD_REVERB_PROPERTIES reverbprops = any
    static as bool in_water = false

    if (xListenerPos > -14.75f andalso xListenerPos < -7.6f _
        andalso zListenerPos > -10.85f andalso zListenerPos < -3.75f _
        andalso yListenerPos < 5.0f) then
        /'
            Use opengl fog to make it look like we are in water
        '/
        if (in_water = 0) then
    	    glEnable(GL_FOG)

            result = FMOD_EventSystem_GetReverbPreset(fmodEventSystem,"UnderWater", @reverbprops,0)
            ERRCHECK(result)
            result = FMOD_EventSystem_setReverbProperties(fmodEventSystem, @reverbprops)
            ERRCHECK(result)
            result = FMOD_DSP_SetBypass(global_lowpass, false)
            ERRCHECK(result)

            in_water = true
        end if
    else
        /'
            Disable fog (water)
        '/
        if (in_water) then
            glDisable(GL_FOG)

            result = FMOD_EventSystem_getReverbPreset(fmodEventSystem, "StdReverb", @reverbprops,0)
            ERRCHECK(result)
            result = FMOD_EventSystem_setReverbProperties(fmodEventSystem, @reverbprops)
            ERRCHECK(result)
            if (global_lowpass) then
                result = FMOD_Dsp_SetBypass(global_lowpass, true)
                ERRCHECK(result)
            end if

            in_water = false
        end if
    end if
end sub


sub drawSkyBox()
    glPushMatrix()
        glTranslatef(xListenerPos, 0.0f, yListenerPos)
        glDisable(GL_LIGHTING)
        /'
            Walls
        '/
        glBindTexture(GL_TEXTURE_2D, skyboxTexture(0))
        glBegin(GL_QUADS)
            glTexCoord2f(1.0f, 1.0f)
            glVertex3f(-150.0f, -150.0f, -150.0f)
            glTexCoord2f(1.0f, 0.0f)
            glVertex3f(-150.0f, 150.0f, -150.0f)
            glTexCoord2f(0.0f, 0.0f)
            glVertex3f(150.0f, 150.0f, -150.0f)
            glTexCoord2f(0.0f, 1.0f)
            glVertex3f(150.0f, -150.0f, -150.0f)
        glEnd()

        glBindTexture(GL_TEXTURE_2D, skyboxTexture(1))
        glBegin(GL_QUADS)
            glTexCoord2f(1.0f, 1.0f)
            glVertex3f(150.0f, -150.0f, -150.0f)
            glTexCoord2f(1.0f, 0.0f)
            glVertex3f(150.0f, 150.0f,-150.0f)
            glTexCoord2f(0.0f, 0.0f)
            glVertex3f(150.0f, 150.0f, 150.0f)
            glTexCoord2f(0.0f, 1.0f)
            glVertex3f(150.0f, -150.0f, 150.0f)
        glEnd()

        glBindTexture(GL_TEXTURE_2D, skyboxTexture(2))
        glBegin(GL_QUADS)
            glTexCoord2f(0.0f, 1.0f)
            glVertex3f(-150.0f, -150.0f, 150.0f)
            glTexCoord2f(0.0f, 0.0f)
            glVertex3f(-150.0f, 150.0f, 150.0f)
            glTexCoord2f(1.0f, 0.0f)
            glVertex3f(150.0f, 150.0f, 150.0f)
            glTexCoord2f(1.0f, 1.0f)
            glVertex3f(150.0f, -150.0f, 150.0f)
        glEnd()

        glBindTexture(GL_TEXTURE_2D, skyboxTexture(3))
        glBegin(GL_QUADS)
            glTexCoord2f(0.0f, 1.0f)
            glVertex3f(-150.0f, -150.0f, -150.0f)
            glTexCoord2f(0.0f, 0.0f)
            glVertex3f(-150.0f, 150.0f, -150.0f)
            glTexCoord2f(1.0f, 0.0f)
            glVertex3f(-150.0f, 150.0f, 150.0f)
            glTexCoord2f(1.0f, 1.0f)
            glVertex3f(-150.0f, -150.0f, 150.0f)
        glEnd()

        /'
            Top
        '/
        glBindTexture(GL_TEXTURE_2D, skyboxTexture(4))
        glBegin(GL_QUADS)
            glTexCoord2f(1.0f, 0.0f)
            glVertex3f(-150.0f, 150.0f, -150.0f)
            glTexCoord2f(1.0f, 1.0f)
            glVertex3f(150.0f, 150.0f, -150.0f)
            glTexCoord2f(0.0f, 1.0f)
            glVertex3f(150.0f, 150.0f, 150.0f)
            glTexCoord2f(0.0f, 0.0f)
            glVertex3f(-150.0f, 150.0f, 150.0f)
        glEnd()

        /'
            Bottom
        '/
        glBindTexture(GL_TEXTURE_2D, skyboxTexture(5))
        glBegin(GL_QUADS)
            glTexCoord2f(0.0f, 1.0f)
            glVertex3f(-150.0f, -150.0f, -150.0f)
            glTexCoord2f(0.0f, 0.0f)
            glVertex3f(150.0f, -150.0f, -150.0f)
            glTexCoord2f(1.0f, 0.0f)
            glVertex3f(150.0f, -150.0f, 150.0f)
            glTexCoord2f(1.0f, 1.0f)
            glVertex3f(-150.0f, -150.0f, 150.0f)
        glEnd()

        glEnable(GL_LIGHTING)
    glPopMatrix()
end sub

sub drawWaterRoom()
    glBlendFunc( GL_SRC_ALPHA, GL_ONE )
    glEnable(GL_BLEND)

    glPushMatrix()
        glColor4f(0.0f,0.1f,0.8f,1.0f)
        glDisable(GL_LIGHTING)

        glBegin(GL_QUADS)
            glVertex3f(-14.72f, 4.0f, -10.85f)
            glVertex3f(-7.68f, 4.0f, -10.85f)
            glVertex3f(-7.68f, 4.0f, -3.85f)
            glVertex3f(-14.72f, 4.0f, -3.85f)
        glEnd()

        ' Door
        glBegin(GL_QUADS)
            glVertex3f(-7.6f, 0.0f, -6.3f)
            glVertex3f(-7.6f, 0.0f, -8.35f)
            glVertex3f(-7.6f, 2.0f, -8.35f)
            glVertex3f(-7.6f, 2.0f, -6.3f)
        glEnd()

        ' Door
        glBegin(GL_QUADS)
            glVertex3f(-12.25, 0.0f, -3.75f)
            glVertex3f(-10.17, 0.0f, -3.75f)
            glVertex3f(-10.17, 2.0f, -3.75f)
            glVertex3f(-12.25, 2.0f, -3.75f)
        glEnd()

        glEnable(GL_LIGHTING)
    glPopMatrix()

    glDisable(GL_BLEND)
end sub

sub drawGeometry(byref mesh as Mesh_)
	dim as FMOD_RESULT result = any

	dim as FMOD_VECTOR position = any
	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 = any
	dim as FMOD_VECTOR up = any
	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, rendermode)
	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 = any

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(0).event)
    ERRCHECK(result)

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(1).event)
    ERRCHECK(result)

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(2).event)
    ERRCHECK(result)

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(3).event)
    ERRCHECK(result)

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(4).event)
    ERRCHECK(result)

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(5).event)
    ERRCHECK(result)

    result = FMOD_EventGroup_GetEvent(fmodEventGroup, "3DSoundEmit", FMOD_EVENT_DEFAULT, @objects(6).event)
    ERRCHECK(result)

	for i as integer = 0 to NUM_OBJECTS-1
		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 )

        if (objects(i).event) then
		    result = FMOD_Event_Set3DAttributes(objects(i).event, @position, @vel,0)
		    ERRCHECK(result)
            result = FMOD_Event_Start(objects(i).event)
            ERRCHECK(result)
        end if
	next
end sub


sub updateObjectSoundPos(byval obj as Obj ptr)
    dim as FMOD_RESULT result = any

    if (obj->event) then
	    dim as FMOD_VECTOR position = type( obj->xPos, obj->yPos, obj->zPos )
	    dim as FMOD_VECTOR oldPos = any
     	FMOD_Event_Get3DAttributes(obj->event, @oldPos, 0,0)

	    dim as FMOD_VECTOR vel = any
	    vel.x = (position.x - oldPos.x) *  (1000.0f / INTERFACE_UPDATETIME)
	    vel.y = (position.y - oldPos.y) *  (1000.0f / INTERFACE_UPDATETIME)
	    vel.z = (position.z - oldPos.z) *  (1000.0f / INTERFACE_UPDATETIME)
    	result = FMOD_Event_Set3DAttributes(obj->event, @position, @vel,0)
'    	ERRCHECK(result)
    end if
end sub


sub mouseFunc(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
		else
		if (state = GLUT_UP) then
			doRotate = false
		end if
end if
end select
end sub

sub motionFunc cdecl(byval x as integer, byval y as integer)
	dim as integer test = 0

	dim as integer dx = x - xMouse
	dim as integer dy = y - yMouse

	' view rotation about y-axis
	yRotation += 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 += 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 = any
    dim as bool        doorMoving(4-1) = {true, true, true ,true}

	' 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 = any
	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)
	drawGeometry(rotatingMesh)


	' 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
        doorMoving(0) = false
    end if
	if (position.y > 2.0f) then
		position.y = 2.0f
        doorMoving(0) = false
    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
        doorMoving(1) = false
    end if
	if (position.y > 2.0f) then
		position.y = 2.0f
        doorMoving(1) = false
    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
        doorMoving(2) = false
    end if
	if (position.y > 2.0f) then
		position.y = 2.0f
        doorMoving(2) = false
    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
        doorMoving(3) = false
    end if
	if (position.y > 2.0f) then
		position.y = 2.0f
        doorMoving(3) = false
    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*0.3)
	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 * (iif(moveFast,2.0f,1.0f)))
    end if
	if (moveBackward) then
		forward -= (MOVEMENT_SPEED * (iif(moveFast,2.0f,1.0f)))
    end if

	dim as float right_ = 0.0f
	if (moveLeft) then
		right_ -= (MOVEMENT_SPEED * (iif(moveFast,2.0f,1.0f)))
    end if
	if (moveRight) then
		right_ += (MOVEMENT_SPEED * (iif(moveFast,2.0f,1.0f)))	
    end if
	
	dim as float up = 0.0f
	if (moveUp) then
		up += (MOVEMENT_SPEED * (iif(moveFast,2.0f,1.0f)))
    end if
	if (moveDown) then
		up -= (MOVEMENT_SPEED * (iif(moveFast,2.0f,1.0f)))
    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 = any
		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 = any
		dim as FMOD_VECTOR up = any
		dim as FMOD_VECTOR vel = any

		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

		if (lastVel.x <> 0.0f orelse lastVel.y <> 0.0f orelse lastVel.z <> 0.0f) then
			if (vel.x = 0.0f andalso vel.y = 0.0f andalso vel.z = 0.0f) then
				dim as integer test = 0
			end if
		end if
		lastVel = vel

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

sub doUpdateVolume()
    if (ambientVolUp) then
        ambientVolume += 0.025
        if (ambientVolume > 1.0f) then
            ambientVolume = 1.0f
        end if
    ElseIf (ambientVolDown) then
        ambientVolume -= 0.025
        if (ambientVolume < 0.0f) then
            ambientVolume = 0.0f
        end if
    end if
end sub

sub timerFunc cdecl(byval nValue as integer)
    static as bool firsttime = true
	dim as FMOD_RESULT result = any

	doGeometryMovement()

	doSoundMovement()
	doListenerMovement()
    doUpdateVolume()
	
    result = FMOD_NetEventSystem_Update()
	ERRCHECK(result)
    result = FMOD_EventSystem_update(fmodEventSystem)
	ERRCHECK(result)

	accumulatedTime += INTERFACE_UPDATETIME / 1000.0f

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

#if 0
    if (firsttime) then
        SetWindowPos( _
          GetForegroundWindow(), _   ' handle to window
          HWND_TOPMOST, _            ' placement-order handle
          0, _                       ' horizontal position
          0, _                       ' vertical position
          width_, _                   ' width
          height, _                  ' height
          SWP_NOMOVE _
        ) _
        firsttime = false
    }
#endif
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
    case asc("z") :
        ambientVolDown = true
    case asc("Z") :
        ambientVolUp = true
    case asc("v"):
        masterVolDown = true
    case asc("V"):
        masterVolUp = true

    case asc("p"):
            dim as float value = any

            FMOD_EventParameter_GetValue(fmodEventParameter, @value)

            FMOD_EventParameter_SetValue(fmodEventParameter, value - 20.0f)

    case asc("P"):
            dim as float value = any

            FMOD_EventParameter_getValue(fmodEventParameter, @value)

            FMOD_EventParameter_setValue(fmodEventParameter, value + 20.0f)

    case asc("f"):
        moveFast = 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
    case asc("z") :
        ambientVolDown = false
    case asc("Z") :
        ambientVolUp = false
    case asc("v"):
        masterVolDown = false
    case asc("V"):
        masterVolUp = false
    case asc("f"):
        moveFast = false
    case 27:
        end(1)
end select
end sub

sub specialKeyFunc cdecl(byval key as integer, byval x as integer, byval y as integer)
  /'  select case key


    end select '/

    key = key
end sub

sub specialKeyUpFunc cdecl(byval key as integer, byval x as integer, byval y as integer)
    select case key
    case GLUT_KEY_F2:
        if(fullscreen) then
            glutPositionWindow(20,40)
            glutReshapeWindow(500,500)
            fullscreen = false
        else
            glutFullScreen()
            fullscreen = true
        end if
    case GLUT_KEY_F3:
        rendermode = (iif(rendermode = GL_LINE,GL_FILL,GL_LINE))
    case GLUT_KEY_F8:
        FMOD_Geometry_SetActive(rotatingMesh.geometry, false)
        FMOD_Geometry_SetActive(walls.geometry, false)
    case GLUT_KEY_F9:
        FMOD_Geometry_SetActive(rotatingMesh.geometry, true)
        FMOD_Geometry_SetActive(walls.geometry, true)
    end select
end sub

sub display cdecl()
    /'
        3D RENDERING
    '/
	
	' 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))


    ' draw skybox
    drawSkyBox()

	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_GetOcclusion(objects(obj_).channel, @directOcclusion, @reverbOcclusion)
		dim as float intensity = 1.0f ' - directOcclusion
				
		glPolygonMode(GL_FRONT_AND_BACK, rendermode)
		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-1) = { 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)
        scope
            dim as FMOD_VECTOR soundorientation = any
            dim as float rad = (accumulatedTime * 200.0f)
            
            rad *= 3.14159f
            rad /= 180.0f

            soundorientation.x = sinf(rad)
            soundorientation.y = 0
            soundorientation.z = cosf(rad)

            FMOD_Event_set3DAttributes(objects(obj_).event, 0, 0, @soundorientation)
        end scope

		glutSolidTorus(0.15f, 0.6f, 8, 16)
		glPopAttrib()
		glPopMatrix()
	next

    /'
        Draw blue transparent blue quads to entry to water room
    '/
    drawWaterRoom()

    /'
        Do water effects if we are in the water room
    '/  
    inWater()


    /'
        2D RENDERING
    '/
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluOrtho2D(0.0f, width_, 0.0f, height)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()

	glDisable(GL_LIGHTING)
	glDisable(GL_NORMALIZE)
    glDisable(GL_DEPTH_TEST)


    glEnable(GL_DEPTH_TEST)
	glEnable(GL_LIGHTING)
	glEnable(GL_LIGHT0)
	glEnable(GL_NORMALIZE)
	glShadeModel(GL_SMOOTH)
	glPolygonMode(GL_FRONT_AND_BACK, rendermode)

	' 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 loadTexturePNG(byval filename as zstring ptr) as GLuint
    dim as GLuint texture = SOIL_load_OGL_texture(filename, 0, 0, 0)
    glBindTexture(GL_TEXTURE_2D, texture)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)

    return texture
end function

function loadTexture(byval filename as zstring ptr) as GLuint
    dim as GLuint texture = any
    dim as integer width_ = any
	dim as integer height = any
    dim data_ as ubyte ptr
    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_ = allocate(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_)

    deallocate(data_)

    return texture
end function


sub init()
    dim as FMOD_RESULT      result = any
    dim as bool             listenerflag = true
    dim as FMOD_VECTOR      listenerpos  = type( 0.0f, 0.0f, 0.0f )
    dim as FMOD_SPEAKERMODE speakermode = any

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

    result = FMOD_EventSystem_Create(@fmodEventSystem)
    ERRCHECK(result)
    result = FMOD_NetEventSystem_Init(fmodEventSystem,0)
    ERRCHECK(result)
    result = FMOD_EventSystem_GetSystemObject(fmodEventSystem, @fmodSystem)
    ERRCHECK(result)
    result = FMOD_System_GetDriverCaps(fmodSystem, 0,0,0,@speakermode)
    ERRCHECK(result)
    result = FMOD_System_setSpeakerMode(fmodSystem, speakermode)
    ERRCHECK(result)
    result = FMOD_EventSystem_Init(fmodEventSystem, 64, FMOD_INIT_3D_RIGHTHANDED or FMOD_INIT_OCCLUSION_LOWPASS or FMOD_INIT_HRTF_LOWPASS, 0, FMOD_EVENT_INIT_NORMAL)
    ERRCHECK(result)
    result = FMOD_EventSystem_SetMediaPath(fmodEventSystem, "media\\")  
    ERRCHECK(result)
    result = FMOD_EventSystem_Load(fmodEventSystem, "examples.fev", 0, @fmodEventProject)
    ERRCHECK(result)
    result = FMOD_EventProject_GetGroup(fmodEventProject, "FeatureDemonstration/3D Events", true, @fmodEventGroup)
    ERRCHECK(result)

    /'
        Create a programmer created lowpass filter to apply to everything.
    '/
    result = FMOD_System_CreateDSPByType(fmodSystem, FMOD_DSP_TYPE_LOWPASS, @global_lowpass)
    ERRCHECK(result)

    result = FMOD_Dsp_SetParameter(global_lowpass, FMOD_DSP_LOWPASS_CUTOFF, 1000)
    ERRCHECK(result)

    result = FMOD_Dsp_SetBypass(global_lowpass, true)   ' turn it off to start with.
    ERRCHECK(result)

    result = FMOD_System_AddDSP(fmodSystem, global_lowpass, 0)
    ERRCHECK(result)


	initObjects()

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

    printf(!"Loading geometry...")

	' load objects
	initGeometry("media/walls.bin", walls, true)
	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))

    printf(!"done.\n")

	' place doors in desired orientatins
	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)

    /'
        Load textures
    '/
    printf(!"Loading textures...\n")

	texture = loadTexture("media/texture.img")
    skyboxTexture(0) = loadTexturePNG("media/skybox/bluesky/front.png")
    skyboxTexture(1) = loadTexturePNG("media/skybox/bluesky/right.png")
    skyboxTexture(2) = loadTexturePNG("media/skybox/bluesky/back.png")
    skyboxTexture(3) = loadTexturePNG("media/skybox/bluesky/left.png")
    skyboxTexture(4) = loadTexturePNG("media/skybox/bluesky/top.png")
    skyboxTexture(5) = loadTexturePNG("media/skybox/bluesky/bottom.png")

    printf(!"done.\n")

	' 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)

    ' setup fog(water)
    dim as GLfloat	fogColor(4-1) = {0.0f,0.1f,0.9f,1.0f}

	glFogi(GL_FOG_MODE, GL_EXP)	    ' Fog Mode
	glFogfv(GL_FOG_COLOR, @fogColor(0))    ' Set Fog Color
	glFogf(GL_FOG_DENSITY, 0.15f)	    ' How Dense Will The Fog Be
	glHint(GL_FOG_HINT, GL_DONT_CARE)	' Fog Hint Value
	glFogf(GL_FOG_START, 0.0f)			' Fog Start Depth
	glFogf(GL_FOG_END, 1.0f)			' Fog End Depth
	
	glEnable(GL_DEPTH_TEST)

	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
end sub

sub GlutCloseClass()
    glDeleteTextures( 1, @texture ) 

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

    FMOD_EventSystem_Release(fmodEventSystem)
    FMOD_NetEventSystem_Shutdown()
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 Ex 3D Demo")
	glutDisplayFunc(@display)	
	glutReshapeFunc(@reshapeFunc)
	glutMouseFunc(@mouseFunc)
	glutMotionFunc(@motionFunc)
	glutKeyboardFunc(@keyboardFunc)
	glutKeyboardUpFunc(@keyboardUpFunc)
    glutSpecialFunc(@specialKeyFunc)
    glutSpecialUpFunc(@specialKeyUpFunc)

	glutTimerFunc(INTERFACE_UPDATETIME, @timerFunc, 0)

	init()

    Sleep(1000)

	glutMainLoop()
GlutCloseClass()    
