''*****************************************************************************
'' 
''   2.5D Engine (Textured Mode)
''
''   Relminator (Richard Eric M. Lope)
''	 http://rel.phatcode.net
''
''	 This is probably the easiest way to make a 3d game
''   Features:
''   * 1:1 ratio of 3D coords to Screen
''   * Collisions are in tilebased 2D
''   * Rendering can be camera based (uber fast)
''   * Any 2D tilebased game can be converted to this
''
''   Controls
''   
''   SPACE = Jump
''   ARROW KEYS = Move
''   F1 = Paper mario mode?
''   F2/F3 = Zoom
''
''   GFX by Marc Russel
''   
''*****************************************************************************

#include once "fbgfx.bi"
#include once "/gl/gl.bi" 
#include once "/gl/glu.bi"   


''*****************************************************************************
''
''  Stuff I ripped from my Easy GL2D Lib
''
''*****************************************************************************

#define ARGB_A(u) (((u) shr 24) and &H000000FF)
#define ARGB_R(u) (((u) shr 16) and &H000000FF)
#define ARGB_G(u) (((u) shr 8)  and &H000000FF)
#define ARGB_B(u) (((u) shr 0)  and &H000000FF)
#define ARGB( r, g, b, a )   rgba( (b), (g), (r), (a) )
#define GL2D_RGBA( r, g, b, a )   rgba( (b), (g), (r), (a) )

'' Flipping mode for Sprite3D(also used to flip textures when drawing 3D boxes)
enum E_FLIP_MODE

	FLIP_NONE = 0,
	FLIP_V,
	FLIP_H,
	FLIP_VH
	
end enum

'' FB Image Type I modified for use with Easy GL2D
type IMAGE field = 1

	type 			as uinteger
	bpp 			as integer
	width 			as uinteger
	height 			as uinteger
	pitch 			as uinteger
	
	'' _reserved(1 to 12) as ubyte
	'' 12 bytes are used to be compatible 

	textureID 		as uinteger			'' 4 bytes
	u_offset		as ushort			'' 2 bytes
	v_offset		as ushort			'' 2 bytes
	texture_width	as ushort			'' 2 bytes
	texture_height	as ushort			'' 2 bytes

end type


''*****************************************************************************
''
''  Routines ripped from Easy GL2D
''
''*****************************************************************************
declare sub GL_Screen_Init(byval screen_wid as integer, byval screen_hei as integer, byval flags as integer = 0)
declare function load_image( byval spr as any ptr , byval filter_mode as GLuint = GL_NEAREST) as GLuint
declare function load_image_24bit_alpha( byval spr as any ptr, byval filter_mode as GLuint = GL_NEAREST ) as GLuint
declare function load_image_8bit_alpha( byval spr as any ptr, byval filter_mode as GLuint = GL_NEAREST ) as GLuint
declare function Load_BMP_To_Texture(byref filename as string) as GLuint
declare function InitSprites overload(spriteset() as IMAGE ptr, texcoords() as uinteger,_
				 byref filename as string, byval filter_mode as GLuint = GL_NEAREST ) as integer

declare function InitSprites overload(spriteset() as IMAGE ptr, byval tile_wid as integer, byval tile_hei as integer, byref filename as string, byval filter_mode as GLuint = GL_NEAREST ) as integer

declare sub DestroySprites(spriteset() as IMAGE ptr)
declare sub Sprite3D( byval x as single, byval y as single, byval z as single, byval mode as E_FLIP_MODE, byval spr as image ptr)



''*****************************************************************************
''
''  Routines for this engine
''
''*****************************************************************************
declare sub DrawMap( Map() as integer,  spriteset() as IMAGE ptr )
declare sub DrawFace( byval PolyIdx as integer, byval Size as single, byval Flipmode as integer, byval spr as image ptr )
declare sub DrawCube( byval x as single, byval y as single, byval z as single, byval Size as single, byval spr as image ptr )

declare sub ConvertMap( Map() as integer, StrMap() as string )

''*****************************************************************************
''
''  The usual constants
''
''*****************************************************************************
const as integer SCREEN_WIDTH = 640
const as integer SCREEN_HEIGHT = 480

const as integer TILE_SIZE = 32      	'' Size of each 3d block

const as integer FALSE = 0
const as integer TRUE = not FALSE

const as single Gravity = 1.75			'' Gravity for jumping
const as single JumpHeight = 17			'' Height of each jump





''*****************************************************************************
''
''  Global array for our 3D block
''
''*****************************************************************************

''Vertices for the cube
dim shared as single CubeVectors(23) =>_
{	(-0.5), (-0.5), ( 0.5),_ 
	( 0.5), (-0.5), ( 0.5),_
	( 0.5), (-0.5), (-0.5),_
	(-0.5), (-0.5), (-0.5),_
	(-0.5), ( 0.5), ( 0.5),_ 
	( 0.5), ( 0.5),	( 0.5),_
	( 0.5), ( 0.5), (-0.5),_
	(-0.5), ( 0.5), (-0.5) }


'' Poly Index
dim shared as integer CubeFaces(23) =>_
{	3,2,1,0,_
	0,1,5,4,_
	1,2,6,5,_
	2,3,7,6,_
	3,0,4,7,_
	4,5,6,7 }

''*****************************************************************************
''
''  Player class
''
''*****************************************************************************
type Player
	
	
	declare constructor()
	declare sub Update( Map() as integer )
	declare function CollideFloors(byval ix as integer, byval iy as integer, byref TileY as integer, Map() as integer ) as integer
	declare function CollideWalls(byval ix as integer, byval iy as integer, byref TileX as integer, Map() as integer ) as integer
	declare sub DoStuff()
	declare sub CollideOnMap( Map() as integer )
	declare sub Draw3D(spriteset() as IMAGE ptr)

	'' Movement and collision related vars
	x		as single			'' x coordinate of the player
	y		as single			
	Speed	as single			'' Speed of the player when walking
	xv		as single			 
	yv		as single
	Wid		as integer			'' Width of the player tile
	Hei		as integer			'' Height of the player tile
	CanJump as integer			'' if the player can jump
	
	'' Animation related vars
	Direction 	as integer     	'' -1 left,0 normal, 1 right (Used for walking and stopping animations)		
	Counter 	as Integer		'' Player counter
	Flipmode 	as Integer		'' Mode to flip the sprite
	SpriteFrame as Integer		'' Animation frame
	
	
end type

''*************************************
'' Constructor
''*************************************
constructor Player()
	x = 32 * 2 
	y = 32 * 3 
	speed = 4.5
	wid = 24
	hei = 30
	CanJump = false
	
	Direction = 0
	Counter = 0
	Flipmode = FLIP_NONE
	SpriteFrame = 0
	
End Constructor

''*************************************
'' Updates everything for the player
''*************************************
sub Player.Update( Map() as integer )
	
	Counter += 1
	
	DoStuff()     '' Do player movements
	CollideOnMap( Map() )		'' Do collisions in 2D
	
	'' Animate
	if( Direction <> 0 ) then
		if( (Counter and 3) = 0 ) then
			SpriteFrame = ( SpriteFrame + 1 ) and 7
		EndIf
			
	else
		SpriteFrame = 2
	EndIf
	 	
End Sub

''*************************************
'' Collides the player box with the tiles on the x-axis
''*************************************
function Player.CollideWalls(byval ix as integer, byval iy as integer, byref TileX as integer, Map() as integer ) as integer
	
	dim as integer TileYpixels = iy - (iy mod TILE_SIZE)   '' Pixel of the player's head snapped to map grid
	dim as integer TestEnd = iy + hei					   '' Foot of the player
	
	TileX = ix\TILE_SIZE								   '' Current X map coord the player is on + x-velocity(+ width when moving right)
	
	dim as integer TileY = TileYpixels\TILE_SIZE		   '' Current Y map coord of the player's head
	
	'' Scan downwards from head to foot if we collided with a tile on the right or left
	while( TileYpixels <= TestEnd )
		if( Map(TileX, TileY) > 0 )	then return true	   '' Found a tile

		TileY += 1										   '' Next tile downward
		TileYpixels +=TILE_SIZE							   '' Next tile (in pixels) downward
		
	Wend
	
	return false
	
End Function

''*************************************
'' Collides the player box with the tiles on the y-axis
''*************************************
function Player.CollideFloors(byval ix as integer, byval iy as integer, byref TileY as integer, Map() as integer ) as integer
	
	dim as integer TileXpixels = ix - (ix mod TILE_SIZE)
	dim as integer TestEnd = ix + wid
	
	TileY = iy\TILE_SIZE
	
	dim as integer TileX = TileXpixels\TILE_SIZE
	
	while (TileXpixels <= TestEnd)
		if (Map(TileX, TileY))	then return true	

		TileX += 1
		TileXpixels +=TILE_SIZE
		
	Wend
	
	return false
End Function



''*************************************
'' Player movement from Keyboard
''*************************************
sub Player.DoStuff()

	xv = 0
	Direction = 0
	
	if multikey(FB.SC_LEFT) then 
		xv = -speed
		Direction = - 1
		Flipmode = FLIP_H
	EndIf
	
    if multikey(FB.SC_RIGHT) then 
    	xv = speed
    	Direction = 1
    	Flipmode = FLIP_NONE
    EndIf
    
    if multikey(FB.SC_SPACE) then 
	    if( CanJump ) then
	    	yv = -JumpHeight
	    	CanJump = false
	    end if
    EndIf
    
    
    
End Sub

''*************************************
'' Checks player collisions on the map
''*************************************
sub Player.CollideOnMap( Map() as integer )
	
	dim as Integer TileX, TileY

	
	if( xv > 0 ) then 		'' Right movement
		
		if( CollideWalls( x + xv + wid, y, TileX, Map() ) ) then    '' (x + xv + wid) = Right side of player
			x = TileX * TILE_SIZE - wid - 1							'' Snap left when there's a collision
		else
			x += xv													'' No collision, so move
		EndIf
	
	elseif( xv < 0 ) then 	'' Left movement																					
		
		if( CollideWalls( x + xv, y, TileX, Map() ) ) then			'' (x + xv) = Left side of player
			x = ( TileX + 1 ) * TILE_SIZE + 1						'' Snap to right of tile
		else
			x += xv													'' No collision, so move
		EndIf
		
	EndIf
	
	
	if( yv < 0 ) then   	'' moving Up
		
		if( CollideFloors( x, y + yv, TileY, Map() ) ) then   		'' hit the roof
			y = ( TileY + 1 ) * TILE_SIZE + 1						'' Snap below the tile
			yv = 0    												'' Arrest movement
		else
			y += yv													'' No collision so move
			yv += Gravity											'' with gravity
		EndIf
			
	else					'' Stationary or moving down
		
		if( CollideFloors( x, y + yv + hei, TileY, Map() ) ) then	'' (y + yv + hei) = Foot of player
			y = ( TileY ) * TILE_SIZE - hei - 1						'' Snap above the tile
			yv = 1
			if not multikey(FB.SC_SPACE) then CanJump = true		'' We are on the floor so we can jump
		else
			y += yv													'' No collision so move
			yv += Gravity
			CanJump = false											'' No jumping in-between jumps
		EndIf
		
	EndIf
	
	
End Sub


''*************************************
'' Draws a 3D sprite parallel to the screen
''*************************************
sub Player.Draw3D(spriteset() as IMAGE ptr)

	'' Calculate the offset of the sprite in regard to TILE_SIZE
	'' since Wid = 24 and TILE_SIZE = 32
	'' Same with Y
	dim as integer xoff = ( TILE_SIZE - Wid ) / 2		
	dim as integer yoff = ( TILE_SIZE - Hei ) / 2	
	Sprite3D( x - xoff, y - yoff, 0, Flipmode, spriteset(Spriteframe))
	
end sub

	
''*****************************************************************************
''
''	Vector3D class needed by the camera class
''
''*****************************************************************************
Type Vector3D

	x as Single
	y as Single
	z as single
	
end type


''*****************************************************************************
''
''	Simple camera class that just follows the player position
''
''*****************************************************************************
Type Camera
	
	
	declare Constructor()
	declare sub Follow( x as single, y as single )
	declare sub Follow( x as single, y as single, skew as integer )
	declare sub Look()
	declare sub Look( value as single )
	declare sub Zoom( value as single )

	
	'' Variables for gluLookAt() transform
	Position as Vector3D 
	Target as Vector3D 
	Up as Vector3D 
	
	EyeDistanceFromScreen as integer	'' The distance of your eye from screen

end type


''*************************************
'' Constructor
''*************************************
constructor Camera()

	EyeDistanceFromScreen = TILE_SIZE * 18   '' Distance of player's eye from screen
	
	Position.x = 0
	Position.y = 0
	Position.z = EyeDistanceFromScreen       '' Yep we are drawing at z = 0 so pos should be positive
	
	Target.x = 0
	Target.y = 0
	Target.z = 0
	
	Up.x = 0
	Up.y = 1
	Up.z = 0
	
	
end constructor	


''*************************************
''	Looks at( x, y, 0) from your "eye" 
''*************************************
sub Camera.Follow( x as single, y as single )

	'' Camera position is always behind the screen
	'' Think of this a the "lens" of your eye
	Position.x = x
	Position.y = y - TILE_SIZE * 1    		'' offset a little above so we look a little down
	Position.z = EyeDistanceFromScreen      '' move zpos n units from screen to eye
	
	'' Target is usually the player sprite on z = 0
	'' But can be anywhere you like (See other examples)
	Target.x = x
	Target.y = y
	Target.z = 0
	
	'' Up vector is always (0,1,0)
	'' Unless you want to do something like a top view space shooter( like I did )
	Up.x = 0
	Up.y = 1
	Up.z = 0
	
end sub

sub Camera.Follow( x as single, y as single, skew as integer )

	static as single rot = 0
	if( skew > 0 ) then
		if( rot < 30 ) then rot += 3  
	elseif ( skew < 0 ) then
		if( rot > -30 ) then rot -= 3
	else  '' 0
		if( rot > 0 ) then rot -= 3
		if( rot < 0 ) then rot += 3
	end if
	
	Position.x = x - rot 
	Position.y = y - TILE_SIZE * 1    		'' offset a little above so we look a little down
	Position.z = EyeDistanceFromScreen      '' move zpos n units from screen to eye
	
	Target.x = x + rot
	Target.y = y
	Target.z = 0
	
	Up.x = 0
	Up.y = 1
	Up.z = 0
	
end sub

''*************************************
'' Does the world transformation using our orthogonal basis vectors
''*************************************
sub Camera.Look()

	
	gluLookAt( Position.x, Position.y, Position.z,_    	'' camera pos
			   Target.x, Target.y, Target.z,_     	   	'' camera target
               Up.x, Up.y, Up.z)						'' Up

end sub


''*************************************
'' Zooms/Pans in and out of the playing field
''*************************************
sub Camera.Zoom( value as single )

	'' Just mess around with your eye position to zoom
	EyeDistanceFromScreen +=value

end sub
	

''*****************************************************************************
''
''	Our Main sub
''
''*****************************************************************************
sub main()

	'' Our map for drawing and collision
	'' On a real tilebased engine, it is better to use another
	'' map for collision aka "collision maps"
	dim as integer Map()
	
	'' Temporary string array for easy map making
	'' See ConvertMap() and "Tiles.BMP" for more details
	'' @ = Tile 1 
	'' # = Tile 2
	'' () = Tile 3 and 4 
	dim as String TempMap(20) =>_
	{	"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",_  		'' 0
		"@                                       @",_		'' 1
		"@                                       @",_		'' 2
		"@                                 (###) @",_		'' 3
		"@                              #       #@",_		'' 4
		"@(#####)                                @",_		'' 5
		"@       @     () #######    ######      @",_		'' 6
		"@                       @         ()    @",_		'' 7
		"@       (##)                         (##@",_		'' 8
		"@             #          (@)   ()       @",_		'' 9
		"@                                       @",_		'' 0
		"@          #####              (####)    @",_		'' 1
		"@        ()     () ()      @            @",_		'' 2
		"@                                       @",_		'' 3
		"@     ()               (####)           @",_		'' 4
		"@()                           ()        @",_		'' 5
		"@  ()      ##   (####)                  @",_		'' 6
		"@                           (####) (####@",_		'' 7
		"@ (##) (##)               ()            @",_		'' 8
		"@            ()        ()               @",_		'' 9
		"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"	}		'' 0    
	    '01234567890123456789012345678901234567890

	'' Convert the 1D Ascii map to a 2D integer map
	ConvertMap( Map(), TempMap() )

	'' Our Spritesets
	redim as IMAGE ptr Tiles(0)
	redim as IMAGE ptr PlayerTiles(0)

	'' Frame counter
	dim as integer Frame = 0
	
	'' Camera and Player 
	dim as Camera Cam
	dim as Player Mario
	
	 
	GL_Screen_Init( SCREEN_WIDTH, SCREEN_HEIGHT )   ''Set up GL screen

	'' Load our BMPs to our tile arrays
	'' Tiles
	dim as GLuint TextureID = InitSprites( Tiles(), 32, 32,"Tiles.bmp")
	
	'' Player
	dim as GLuint PlayerTextureID = InitSprites( PlayerTiles(), 32, 32,"Gripe.bmp")

	
	do
		
		Frame += 1

		Mario.Update( Map() )
		
		'' Zoom if you want
		if( multikey(FB.SC_F2) ) then Cam.Zoom(10)
		if( multikey(FB.SC_F3) ) then Cam.Zoom(-10)  
		
		
		'' Set up some opengl crap (some are not needed)
		glMatrixMode( GL_MODELVIEW )
		glLoadIdentity() 
		glPolygonMode( GL_FRONT, GL_FILL )
		glPolygonMode( GL_BACK, GL_FILL )
		glEnable( GL_DEPTH_TEST )
		glDepthFunc( GL_LEQUAL )
		
		glDisable( GL_TEXTURE_2D )
		glEnable( GL_TEXTURE_2D )
	
		
		glColor4ub( 255, 255, 255, 255 )

		'' Move cam according to player's pos
		Cam.Follow( Mario.x, Mario.y, sgn(Mario.xv) )
		
		'' reverse y direction for oldskool coords(FBGFX friendly)
		glScalef( 1, -1, 1 )
		
		'' Look
		Cam.Look()
		
		
		'' Clear screen
		glClear(GL_COLOR_BUFFER_BIT OR GL_DEPTH_BUFFER_BIT)
		
		'' Draw 3D stuff
		glPushMatrix()   
			
			'' Enable Depth testing in 3D mode
			glEnable( GL_DEPTH_TEST )
			glDepthFunc( GL_LEQUAL )
	
			'' Use Tiles texture
			glBindtexture( GL_TEXTURE_2D, TextureID )		
			DrawMap( Map(), Tiles() )
			
			'' Disable depth testing for sprites
			glDisable( GL_DEPTH_TEST )
			glBindtexture( GL_TEXTURE_2D, PlayerTextureID )		
			Mario.Draw3D( PlayerTiles() )

		glPopMatrix()    
		
		'' limit FPS
		dim as single TimeStart = timer
		while( (timer - TimeStart) < (1.0/60.0) )
			sleep 1,1
		wend
		
		
		flip
		
	Loop until multikey(FB.SC_ESCAPE)

	
	'' Clean up
	DestroySprites( Tiles() )	
	DestroySprites( PlayerTiles() )	
	
End Sub


'' Call it
main()

'' bye
end





''*****************************************************************************
''
''  Routines for this engine
''
''*****************************************************************************

''*************************************
'' Draws the map in 3D
''*************************************
sub DrawMap( Map() as integer, spriteset() as IMAGE ptr )
	
	glPushMatrix()
	glScalef( 1.0, 1.0, 3.0)		'' Elongate tiles on the z-axis
	
	'' Read Tile values on the 2D array
	'' Then draw if not empty(0)
	for y as integer = 0 to Ubound(Map,2)
		for x as integer = 0 to Ubound(Map,1)
			dim as integer Tile = Map(x,y)
			if( Tile > 0 ) then
				'' Could have done DrawCube(.....,Spriteset(Tile-1)) 
				'' but this is supposed to teach algo not speed
				select case Tile
					case 1
						DrawCube( x * TILE_SIZE, y * TILE_SIZE, 0, TILE_SIZE, Spriteset(0) )
					case 2
						DrawCube( x * TILE_SIZE, y * TILE_SIZE, 0, TILE_SIZE, Spriteset(1) )
					case 3
						DrawCube( x * TILE_SIZE, y * TILE_SIZE, 0, TILE_SIZE, Spriteset(2) )
					case 4
						DrawCube( x * TILE_SIZE, y * TILE_SIZE, 0, TILE_SIZE, Spriteset(3) )
				End Select
			end if
		next x	
	next y
	
	glPopMatrix()
	
end sub



''*************************************
'' Draws one of the 6 cube faces
'' Size = TILE_SIZE
''*************************************
sub DrawFace( byval PolyIdx as integer, byval Size as single, byval Flipmode as integer, byval spr as image ptr )
	
	dim as integer f1 = CubeFaces(PolyIdx * 4 + 0)
	dim as integer f2 = CubeFaces(PolyIdx * 4 + 1)
	dim as integer f3 = CubeFaces(PolyIdx * 4 + 2)
	dim as integer f4 = CubeFaces(PolyIdx * 4 + 3)

	dim as single u_off = spr->u_offset/spr->texture_width
	dim as single v_off = spr->v_offset/spr->texture_height
	
	dim as single u_width = spr->width/spr->texture_width
	dim as single v_height = spr->height/spr->texture_height
	
	
	dim as single u1, v1, u2, v2 
	select case Flipmode
		case FLIP_NONE:	
			u1 = u_off			 : v1 = v_off
			u2 = u_off + u_width : v2 = v_off + v_height
		case FLIP_V:
			u1 = u_off			 : v1 = v_off + v_height
			u2 = u_off + u_width : v2 = v_off
		case FLIP_H:
			u1 = u_off + u_width : v1 = v_off
			u2 = u_off  		 : v2 = v_off + v_height
		case FLIP_VH:
			u1 = u_off + u_width : v1 = v_off + v_height
			u2 = u_off			 : v2 = v_off 
		case else
			u1 = u_off			 : v1 = v_off
			u2 = u_off + u_width : v2 = v_off + v_height
	end select
	 
	
	dim as single S = Size
	
	glTexCoord2f( u1, v1 )
	glVertex3f(CubeVectors(f1*3) * S, CubeVectors(f1*3 + 1) * S, CubeVectors(f1*3 +  2) * S )
	
	glTexCoord2f( u2, v1 )
	glVertex3f(CubeVectors(f2*3) * S, CubeVectors(f2*3 + 1) * S, CubeVectors(f2*3 + 2) * S )
	
	glTexCoord2f( u2, v2 )
	glVertex3f(CubeVectors(f3*3) * S, CubeVectors(f3*3 + 1) * S, CubeVectors(f3*3 + 2) * S )

	glTexCoord2f( u1, v2 )
	glVertex3f(CubeVectors(f4*3) * S, CubeVectors(f4*3 + 1) * S, CubeVectors(f4*3 + 2) * S )
	
	
End Sub


''*************************************
'' Draws the level blocks
''*************************************
sub DrawCube( byval x as single, byval y as single, byval z as single, byval Size as single, byval spr as image ptr  )

	glPushMatrix()
		glTranslatef( x + Size/2, y + Size/2, z)  '' Offset by Size/2 so that we draw from top-left at (0,0)
		glBegin( GL_QUADS )
		
			DrawFace( 0, Size, FLIP_V   , spr )   '' top
			DrawFace( 1, Size, FLIP_NONE, spr )   '' front
			DrawFace( 2, Size, FLIP_H   , spr )   '' right
			''DrawFace( 3, Size, 0, FLIP_NONE, spr )   '' back so don't draw
			DrawFace( 4, Size, FLIP_H   , spr )   '' left
			DrawFace( 5, Size, FLIP_V   , spr )   '' bottom
			
		glEnd()
		
	glPopMatrix()

end sub



''*************************************
'' Draws a sprite at (x,y,0)
''*************************************
sub Sprite3D( byval x as single, byval y as single, byval z as single, byval mode as E_FLIP_MODE, byval spr as image ptr)
	
	dim  as single x1 = x
	dim  as single y1 = y
	dim  as single x2 = x + (spr->width)
	dim  as single y2 = y + (spr->height)
	
	dim as single u_off = spr->u_offset/spr->texture_width
	dim as single v_off = spr->v_offset/spr->texture_height
	
	dim as single u_width = spr->width/spr->texture_width
	dim as single v_height = spr->height/spr->texture_height
	
	
	dim as single u1, v1, u2, v2 
	select case mode
		case FLIP_NONE:	
			u1 = u_off			 : v1 = v_off
			u2 = u_off + u_width : v2 = v_off + v_height
		case FLIP_V:
			u1 = u_off			 : v1 = v_off + v_height
			u2 = u_off + u_width : v2 = v_off
		case FLIP_H:
			u1 = u_off + u_width : v1 = v_off
			u2 = u_off  		 : v2 = v_off + v_height
		case FLIP_VH:
			u1 = u_off + u_width : v1 = v_off + v_height
			u2 = u_off			 : v2 = v_off 
		case else
			u1 = u_off			 : v1 = v_off
			u2 = u_off + u_width : v2 = v_off + v_height
	end select
	
	glBegin(GL_QUADS)
		
		glTexcoord2f(u1, v1)
		glVertex3f	(x1,y1,z)
		
		glTexcoord2f(u1, v2)
		glVertex3f	(x1,y2,z)
		
		glTexcoord2f(u2, v2)
		glVertex3f	(x2,y2,z)
		
		glTexcoord2f(u2, v1)
		glVertex3f	(x2,y1,z)
		
	glEnd()
	
end sub

''*************************************
'' Converts an Ascii map to integer map 
''*************************************
sub ConvertMap( Map() as integer, StrMap() as string )
	
	'' Resize array according to size of ascii map
	redim Map(len(Strmap(0))-1, ubound(StrMap) )
	
	'' @ = Tile 1 
	'' # = Tile 2
	'' () = Tile 3 and 4
	'' Only used 4 tiles
	 
	for y as integer = 0 to ubound(StrMap)	
		for x as integer = 1 to Len(StrMap(y))
			dim as string a = Mid(StrMap(y), x, 1)
			select case a
				case "@"
					Map(x-1,y) = 1
				case "#"
					Map(x-1,y) = 2
				case "("
					Map(x-1,y) = 3
				case ")"
					Map(x-1,y) = 4
			End Select
		Next
	Next

	
End Sub


''*****************************************************************************
''
''  Routines ripped from Easy GL2D
''
''*****************************************************************************

''*************************************
'' Sets up GL screen 32 bit 
''*************************************
sub GL_Screen_Init(byval screen_wid as integer, byval screen_hei as integer, byval flags as integer = 0)
	
	if flags then
		screenres screen_wid, screen_hei, 32, 2, FB.GFX_OPENGL or flags
	else
		screenres screen_wid, screen_hei, 32, 2, FB.GFX_OPENGL
	endif
	
	
	'screen information 
	dim w as integer, h as integer 
	'OpenGL params for gluerspective 
	dim FOVy as double            'Field of view angle in Y 
	dim Aspect as double          'Aspect of screen 
	dim znear as double           'z-near clip distance 
	dim zfar as double            'z-far clip distance 

	'using screen info w and h as params 
	glViewport(0, 0, screen_wid, screen_hei)
	
	'Set current Mode to projection(ie: 3d) 
	glMatrixMode(GL_PROJECTION) 
	
	'Load identity matrix to projection matrix 
	glLoadIdentity() 

	'Set gluPerspective params 
	FOVy = 90/2                                     '45 deg fovy 
	Aspect = screen_wid / screen_hei
	znear = 1                                       'Near clip 
	zfar = 1000                                     'far clip 
	
	'use glu Perspective to set our 3d frustum dimension up 
	gluPerspective(FOVy, aspect, znear, zfar) 
	
	'Modelview mode 
	'ie. Matrix that does things to anything we draw 
	'as in lines, points, tris, etc. 
	glMatrixMode(GL_MODELVIEW) 
	'load identity(clean) matrix to modelview 
	glLoadIdentity() 
	
	glShadeModel(GL_SMOOTH)                 'set shading to smooth(try GL_FLAT) 
	glClearColor(0.0, 0.0, 0.0, 1.0)        'set Clear color to BLACK 
	glClearDepth(1.0)                       'Set Depth buffer to 1(z-Buffer) 
	glEnable(GL_DEPTH_TEST)                 'Enable Depth Testing so that our z-buffer works 
	
	'compare each incoming pixel z value with the z value present in the depth buffer 
	'LEQUAL means than pixel is drawn if the incoming z value is less than 
	'or equal to the stored z value 
	glDepthFunc(GL_LEQUAL) 
	
	'have one or more material parameters track the current color 
	'Material is your 3d model 
	glEnable(GL_COLOR_MATERIAL) 


    'Enable Texturing 
    glEnable(GL_TEXTURE_2D) 
    

   	'Tell openGL that we want the best possible perspective transform 
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) 
	
	'Disable Backface culling
	glDisable (GL_CULL_FACE)
	
	glPolygonMode(GL_FRONT, GL_FILL) 
	
	'' enable blending for transparency 
	glEnable(GL_BLEND)    	    
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
	
	
	glEnable( GL_ALPHA_TEST )
	glAlphaFunc(GL_GREATER, 0)


	
	glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)
	glHint(GL_LINE_SMOOTH_HINT , GL_NICEST)

	glPointSize( 1 )
	glLineWidth( 1 )
	
	
end sub

''*************************************
'' Loads an FB sprite to a texture
''*************************************
function load_image( byval spr as any ptr , byval filter_mode as GLuint = GL_NEAREST) as GLuint
	
        dim as GLuint TextureID
   		dim as ubyte r, g, b, a
   		dim As FB.IMAGE ptr temp = cast(FB.IMAGE ptr, spr)
   		       
        glEnable( GL_TEXTURE_2D )
        glGenTextures(1, @TextureID)
        glBindTexture(GL_TEXTURE_2D, TextureID)
        
   		glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT )
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT )
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_mode )
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mode )
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE )
               
        glTexImage2d( GL_TEXTURE_2D, 0, GL_RGBA, temp->width, temp->height,_
        			  0, GL_BGRA, GL_UNSIGNED_BYTE, spr + sizeof(FB.IMAGE) )
        glBindTexture(GL_TEXTURE_2D, 0)
        
        
        return TextureID
        
End function

''*************************************
'' 24 bit BMP mode
''*************************************
function load_image_24bit_alpha( byval spr as any ptr, byval filter_mode as GLuint = GL_NEAREST ) as GLuint
	
        dim as GLuint TextureID
   		dim as ubyte r, g, b, a
   		dim As FB.IMAGE ptr temp = spr
   		
   		for y as integer = 0 to temp->height-1
	    	dim as uinteger ptr p = cast(uinteger ptr,(spr + sizeof (FB.IMAGE)) + y * temp->pitch)	   
		    for x as integer = 0 to temp->width-1
		    	a = argb_a(p[x])
		    	r = argb_r(p[x])
		    	g = argb_g(p[x])
		    	b = argb_b(p[x])
		    	'' check for transparency
				if ( g = 0 ) then
					if ( (r = 255) and (b = 255) ) then
						p[x] = rgba(r,g,b,0)
					endif
				endif
		    next x
   		next y
   		
   		
   		glEnable( GL_TEXTURE_2D )
        glGenTextures(1, @TextureID)
        glBindTexture(GL_TEXTURE_2D, TextureID)
        
   		glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT )
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT )
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_mode )
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mode )
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE )
               
        glTexImage2d( GL_TEXTURE_2D, 0, GL_RGBA, temp->width, temp->height,_
        			  0, GL_BGRA, GL_UNSIGNED_BYTE, spr + sizeof(FB.IMAGE) )
        glBindTexture(GL_TEXTURE_2D, 0)
        
        
        return TextureID
        
end function


''*************************************
'' 8 bit mode converted to 32 bits
''*************************************
function load_image_8bit_alpha( byval spr as any ptr, byval filter_mode as GLuint = GL_NEAREST ) as GLuint
	
        dim as GLuint TextureID
   		dim as ubyte r, g, b, a
   		dim As FB.IMAGE ptr temp = spr
   
		    	
   		for y as integer = 0 to temp->height-1
	    	dim as uinteger ptr p = cast(uinteger ptr,(spr + sizeof(FB.IMAGE)) + y * temp->pitch)	   
		    for x as integer = 0 to temp->width-1
		    	a = argb_a(p[x])
		    	r = argb_r(p[x])
		    	g = argb_g(p[x])
		    	b = argb_b(p[x])
		    	'' check for transparency
		    	if ( ( p[x] and &H00FFFFFF ) = 0 ) then
		    		p[x] = rgba(r,g,b,0)
		    	endif
				
		    next x
   		next y
   		
   		
   		glEnable( GL_TEXTURE_2D )
        glGenTextures(1, @TextureID)
        glBindTexture(GL_TEXTURE_2D, TextureID)
        
   		glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT )
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT )
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_mode )
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_mode )
        glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE )
               
        glTexImage2d( GL_TEXTURE_2D, 0, GL_RGBA, temp->width, temp->height,_
        			  0, GL_BGRA, GL_UNSIGNED_BYTE, spr + sizeof(FB.IMAGE) )
        glBindTexture(GL_TEXTURE_2D, 0)
        
        
        return TextureID
        
end function

''*************************************
'' Loads a single BMP to a texture
''*************************************
function Load_BMP_To_Texture(byref filename as string) as GLuint
	
        if filename = "" then exit function
        
        dim as GLuint TextureID
        
        dim as integer F = freefile
        dim as integer w, h		'' width and height
        dim as ushort bpp		'' bits per pixel
        
        '' test header
        open filename for binary as #f
        get #f, 19, w
        get #f, 23, h
        get #f, 29, bpp
        close #f
       
        dim as FB.IMAGE ptr spr = imagecreate(w,h)
        bload filename, spr
       
       
        select case bpp
        	case 8
        		TextureID = load_image_8bit_alpha(spr, GL_LINEAR)
        	case 24
        		TextureID = load_image_24bit_alpha(spr, GL_LINEAR)
        	case else
        		TextureID = load_image(spr, GL_LINEAR)
        end select
        
        
        imagedestroy(spr)
        
        return TextureID
        
end function 


''*************************************
'' Loads a tileset using my texture packer
'' generated coords
'' Use this is you wan't bilinear filtering
'' Just set the Margin in texture packer to
'' Something like 2
''*************************************
function InitSprites overload(spriteset() as IMAGE ptr, texcoords() as uinteger,_
		       		     byref filename as string, byval filter_mode as GLuint = GL_NEAREST ) as integer


    if filename = "" then exit function
     
    dim as GLuint textureID
    
    dim as integer F = freefile
    dim as integer w, h		'' width and height
    dim as ushort bpp		'' bits per pixel
    
    '' test header
    open filename for binary as #f
    get #f, 19, w
    get #f, 23, h
    get #f, 29, bpp
    close #f
   
    dim as FB.IMAGE ptr spr = imagecreate(w,h)
    bload filename, spr
   
   
    select case bpp
    	case 8
    		textureID = load_image_8bit_alpha(spr,filter_mode)
    	case 24
    		textureID = load_image_24bit_alpha(spr,filter_mode)
    	case else
    		textureID = load_image(spr,filter_mode)
    end select
    
    
	dim as integer max_sprites = ubound(texcoords)\4
	redim spriteset(max_sprites)
	
	'' init sprites texture coords and texture ID
	for i as integer = 0 to max_sprites
		dim as integer  j = i * 4
		dim as integer sw = texcoords(j+2)
		dim as integer sh = texcoords(j+3)
		dim as integer su = texcoords(j)
		dim as integer sv = texcoords(j+1) 
		spriteset(i) 					= imagecreate(sw, sh)
		get spr, (su,sv)-(su+(sw-1),sv+(sh-1)), spriteset(i)
		spriteset(i)->textureID 		= textureID
		spriteset(i)->width 			= texcoords(j+2)
		spriteset(i)->height 			= texcoords(j+3)
		spriteset(i)->u_offset			= texcoords(j)
		spriteset(i)->v_offset			= texcoords(j+1) 
		spriteset(i)->texture_width 	= w
		spriteset(i)->texture_height	= h 	
	next i
	
	
	imagedestroy spr
	
	return TextureID
	
end function

''*************************************
'' Loads a tileset from a BMP
'' Use this for GL_NEAREST and your
'' Tileset is fixed sizes
''*************************************
function InitSprites overload(spriteset() as IMAGE ptr, byval tile_wid as integer, byval tile_hei as integer, byref filename as string, byval filter_mode as GLuint = GL_NEAREST ) as integer


    if filename = "" then exit function
     
    dim as GLuint textureID
    
    dim as integer F = freefile
    dim as integer w, h		'' width and height
    dim as ushort bpp		'' bits per pixel
    
    '' test header
    open filename for binary as #f
    get #f, 19, w
    get #f, 23, h
    get #f, 29, bpp
    close #f
   
    dim as FB.IMAGE ptr spr = imagecreate(w,h)
    bload filename, spr
    
    
    select case bpp
    	case 8
    		textureID = load_image_8bit_alpha(spr,filter_mode)
    	case 24
    		textureID = load_image_24bit_alpha(spr,filter_mode)
    	case else
    		textureID = load_image(spr,filter_mode)
    end select
    
   
     
	dim as integer max_sprites = (w\tile_wid) * (h\tile_hei)
	redim spriteset(max_sprites-1)
	
	
	dim as integer i = 0
    for y as integer = 0 to (h\tile_hei) - 1 
    for x as integer = 0 to (w\tile_wid) - 1 
    	spriteset(i) = imagecreate(tile_wid,tile_hei)
        get spr,(x*tile_wid, y*tile_hei)-step(tile_wid-1,tile_hei-1), spriteset(i)	
   		spriteset(i)->textureID 		= textureID
		spriteset(i)->width 			= tile_wid
		spriteset(i)->height 			= tile_hei
		spriteset(i)->u_offset			= x*tile_wid
		spriteset(i)->v_offset			= y*tile_hei 
		spriteset(i)->texture_width 	= w
		spriteset(i)->texture_height	= h 	
        i += 1
    next x
    next y
    
	imagedestroy spr
	
	return TextureID
	
end function

''*************************************
'' Clean up code
''*************************************
sub DestroySprites(spriteset() as IMAGE ptr)
	
	glDeleteTextures( 1, @spriteset(0)->textureID )
	
	for i as integer = 0 to ubound(spriteset)
		spriteset(i)->textureID 	= 0
		imagedestroy(spriteset(i))
	next i
	
end sub
