This shows how to do a lot of different things:
draw lines, antialiasing, timing, orthographic projection, simple sounds and some game logic.
Press Arrows to move player, Spacebar to restart game and A key to toggle antialiasing.
You can get the "Font.bmp", "Image.bmp", "Complete.wav", "Die.wav", "Freeze.wav" and "Hourglass.wav"
from the "lesson21.zip" here: http://nehe.gamedev.net/data/lessons/vc/lesson21.zip
Last edited on 20 Feb 2007.
Code: Select all
;NeHe's Line Tutorial (Lesson 21)
;http://nehe.gamedev.net
;Credits: Nico Gruener, Dreglor, traumatic
;Author: hagibaba
;Date: 15 Jan 2007
;Note: up-to-date with PB v4.02 (Windows)
;Note: requires bitmaps in paths "Data/Font.bmp", "Data/Image.bmp"
;Note: requires wave files in paths "Data/Complete.wav", "Data/Die.wav",
;"Data/Freeze.wav", "Data/Hourglass.wav"
;Section for standard constants, structures, macros and declarations
CompilerIf #PB_Compiler_Unicode
CompilerError "only works in ascii mode"
CompilerEndIf
XIncludeFile #PB_Compiler_Home+"Examples\Sources - Advanced\OpenGL Cube\OpenGL.pbi" ;include the gl.h constants
;wingdi.h constants
#DM_BITSPERPEL=$40000
#DM_PELSWIDTH=$80000
#DM_PELSHEIGHT=$100000
;winuser.h constants
#CDS_FULLSCREEN=4
#DISP_CHANGE_SUCCESSFUL=0
#SC_MONITORPOWER=$F170
Structure AUX_RGBImageRec ;glaux.h structure
sizeX.l : sizeY.l
Data.l
EndStructure
Procedure.w LoWord(value.l) ;windef.h macro
ProcedureReturn (value & $FFFF)
EndProcedure
Procedure.w HiWord(value.l) ;windef.h macro
ProcedureReturn ((value >> 16) & $FFFF)
EndProcedure
;glaux.lib symbols
!public ___ftoll
!___ftoll dw 0
!public __imp__wsprintfA
!__imp__wsprintfA dw 0
Import "glaux.lib"
CompilerIf #PB_Compiler_Unicode
auxDIBImageLoad.l(filename.s) As "_auxDIBImageLoadW@4" ;loads a 24-bit Windows DIB
CompilerElse
auxDIBImageLoad.l(filename.s) As "_auxDIBImageLoadA@4" ;loads a 24-bit Windows DIB
CompilerEndIf
EndImport
Import "opengl32.lib"
glClearDepth(depth.d) ;specifies the clear value for the depth buffer
glOrtho(left.d,right.d,bottom.d,top.d,near.d,far.d) ;multiplies the current matrix by an orthographic matrix
glTranslated(x.d,y.d,z.d) ;moves the current matrix to the point specified
EndImport
;Start of Lesson 21
Global hDC.l ;Private GDI Device Context
Global hRC.l ;Permanent Rendering Context
Global hWnd.l ;Holds Our Window Handle
Global hInstance.l ;Holds The Instance Of The Application
Global Dim keys.b(256) ;Array Used For The Keyboard Routine
Global active.b=#True ;Window Active Flag Set To TRUE By Default
Global fullscreen.b=#True ;Fullscreen Flag Set To Fullscreen Mode By Default
Global Dim vline.b(11,10) ;Keeps Track Of Vertical Lines
Global Dim hline.b(10,11) ;Keeps Track Of Horizontal Lines
Global ap.b ;A Key Pressed?
Global filled.b ;Done Filling In The Grid?
Global gameover.b ;Is The Game Over?
Global anti.b=#True ;Antialiasing?
Global loop1.l ;Generic Loop1
Global loop2.l ;Generic Loop2
Global delay.l ;Enemy Delay
Global adjust.l=3 ;Speed Adjustment For Really Slow Video Cards (0..5)
Global lives.l=5 ;Player Lives
Global level.l=1 ;Internal Game Level
Global level2.l=level ;Displayed Game Level
Global stage.l=1 ;Game Stage
Structure OBJECT ;Create A Structure For Our Player And Enemies
fx.l : fy.l ;Fine Movement Position
x.l : y.l ;Current Player Position
spin.f ;Spin Direction
EndStructure
Global player.OBJECT ;Player Information
Global Dim enemy.OBJECT(9) ;Enemy Information
Global hourglass.OBJECT ;Hourglass Information
Structure TIMER ;Create A Structure For The Timer Information
frequency.q ;Timer Frequency
resolution.f ;Timer Resolution
mm_timer_start.l ;Multimedia Timer Start Value
mm_timer_elapsed.l ;Multimedia Timer Elapsed Time
performance_timer.b ;Using The Performance Timer?
performance_timer_start.q ;Performance Timer Start Value
performance_timer_elapsed.q ;Performance Timer Elapsed Time
EndStructure
Global timer.TIMER ;timer information
Global Dim steps.l(6) ;Stepping Values For Slow Video Adjustment
steps(0)=1 : steps(1)=2 : steps(2)=4
steps(3)=5 : steps(4)=10 : steps(5)=20
Global Dim texture.l(2) ;Font Texture Storage Space
Global base.l ;Base Display List For The Font
Declare.l WndProc(hWnd.l,uMsg.l,wParam.l,lParam.l) ;Declaration For WndProc
Procedure TimerInit() ;Initialize Our Timer (Get It Ready)
;Check To See If A Performance Counter Is Available
;If One Is Available The Timer Frequency Will Be Updated
If QueryPerformanceFrequency_(@timer\frequency)=0
;No Performace Counter Available
timer\performance_timer=#False ;Set Performance Timer To FALSE
timer\mm_timer_start=timeGetTime_() ;Use timeGetTime() To Get Current Time
timer\resolution=1.0/1000.0 ;Set Our Timer Resolution To 0.001
timer\frequency=1000 ;Set Our Timer Frequency To 1000
timer\mm_timer_elapsed=timer\mm_timer_start ;Set The Elapsed Time To The Current Time
Else
;Performance Counter Is Available, Use It Instead Of The Multimedia Timer
QueryPerformanceCounter_(@timer\performance_timer_start) ;Get The Current Time And Store It In performance_timer_start
timer\performance_timer=#True ;Set Performance Timer To TRUE
timer\resolution=1.0/timer\frequency ;Calculate The Timer Resolution Using The Timer Frequency
timer\performance_timer_elapsed=timer\performance_timer_start ;Set The Elapsed Time To The Current Time
EndIf
EndProcedure
Procedure.f TimerGetTime() ;Get Time In Milliseconds
Protected time.q ;time Will Hold A 64 Bit Integer
If timer\performance_timer ;Are We Using The Performance Timer?
QueryPerformanceCounter_(@time) ;Grab The Current Performance Time
;Return The Current Time Minus The Start Time Multiplied By The Resolution And 1000 (To Get MS)
ProcedureReturn ((time-timer\performance_timer_start)*timer\resolution)*1000.0
Else
;Return The Current Time Minus The Start Time Multiplied By The Resolution And 1000 (To Get MS)
ProcedureReturn ((timeGetTime_()-timer\mm_timer_start)*timer\resolution)*1000.0
EndIf
EndProcedure
Procedure ResetObjects() ;Reset Player And Enemies
player\x=0 ;Reset Player X Position To Far Left Of The Screen
player\y=0 ;Reset Player Y Position To The Top Of The Screen
player\fx=0 ;Set Fine X Position To Match
player\fy=0 ;Set Fine Y Position To Match
For loop1=0 To (stage*level)-1 ;Loop Through All The Enemies
enemy(loop1)\x=5+Random(5) ;Select A Random X Position
enemy(loop1)\y=Random(10) ;Select A Random Y Position
enemy(loop1)\fx=enemy(loop1)\x*60 ;Set Fine X To Match
enemy(loop1)\fy=enemy(loop1)\y*40 ;Set Fine Y To Match
Next
EndProcedure
Procedure.l LoadBMP(Filename.s) ;Loads A Bitmap Image
Protected File.l=#Null ;File Handle
If Filename="" ;Make Sure A Filename Was Given
ProcedureReturn #Null ;If Not Return NULL
EndIf
File=ReadFile(#PB_Any,Filename) ;Check To See If The File Exists
If File ;Does The File Exist?
CloseFile(File) ;Close The Handle
ProcedureReturn auxDIBImageLoad(Filename) ;Load The Bitmap And Return A Pointer
EndIf
ProcedureReturn #Null ;If Load Failed Return NULL
EndProcedure
Procedure.l LoadGLTextures() ;Load Bitmaps And Convert To Textures
Protected Status.l=#False ;Status Indicator
Protected Dim *TextureImage.AUX_RGBImageRec(2) ;Create Storage Space For The Textures
*TextureImage(0)=LoadBMP("Data/Font.bmp") ;Load The Font
*TextureImage(1)=LoadBMP("Data/Image.bmp") ;Load Background Image
If *TextureImage(0) And *TextureImage(1)
Status=#True ;Set The Status To TRUE
glGenTextures_(2,@texture(0)) ;Create The Texture
For loop1=0 To 2-1 ;Loop Through 2 Textures
glBindTexture_(#GL_TEXTURE_2D,texture(loop1))
glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
glTexImage2D_(#GL_TEXTURE_2D,0,3,*TextureImage(loop1)\sizeX,*TextureImage(loop1)\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*TextureImage(loop1)\Data)
Next
EndIf
For loop1=0 To 2-1 ;Loop Through 2 Textures
If *TextureImage(loop1) ;If Texture Exists
If *TextureImage(loop1)\Data ;If Texture Image Exists
;FreeMemory(*TextureImage(loop1)\Data) ;Free The Texture Image Memory
EndIf
;FreeMemory(*TextureImage(loop1)) ;Free The Image Structure
EndIf
Next
ProcedureReturn Status ;Return The Status
EndProcedure
Procedure BuildFont() ;Build Our Font Display List
Protected cx.f,cy.f,modx.l
base=glGenLists_(256) ;Creating 256 Display Lists
glBindTexture_(#GL_TEXTURE_2D,texture(0)) ;Select Our Font Texture
For loop1=0 To 256-1 ;Loop Through All 256 Lists
modx=loop1 % 16 ;Note: can't use % with floats
cx=modx/16.0 ;X Position Of Current Character
cy=Int(loop1/16)/16.0 ;Y Position Of Current Character
glNewList_(base+loop1,#GL_COMPILE) ;Start Building A List
glBegin_(#GL_QUADS) ;Use A Quad For Each Character
glTexCoord2f_(cx,1.0-cy-0.0625) ;Texture Coord (Bottom Left)
glVertex2i_(0,16) ;Vertex Coord (Bottom Left)
glTexCoord2f_(cx+0.0625,1.0-cy-0.0625) ;Texture Coord (Bottom Right)
glVertex2i_(16,16) ;Vertex Coord (Bottom Right)
glTexCoord2f_(cx+0.0625,1.0-cy) ;Texture Coord (Top Right)
glVertex2i_(16,0) ;Vertex Coord (Top Right)
glTexCoord2f_(cx,1.0-cy) ;Texture Coord (Top Left)
glVertex2i_(0,0) ;Vertex Coord (Top Left)
glEnd_() ;Done Building Our Quad (Character)
glTranslated(15,0,0) ;Move To The Right Of The Character
glEndList_() ;Done Building The Display List
Next ;Loop Until All 256 Are Built
EndProcedure
Procedure KillFont() ;Delete The Font From Memory
glDeleteLists_(base,256) ;Delete All 256 Display Lists
EndProcedure
Procedure glPrint(x.l,y.l,set.l,text.s) ;Where The Printing Happens
If text="" ;If There's No Text
ProcedureReturn #False ;Do Nothing
EndIf
If set ;Did User Choose An Invalid Character Set?
set=1 ;If So, Select Set 1 (Italic)
EndIf
glEnable_(#GL_TEXTURE_2D) ;Enable Texture Mapping
glLoadIdentity_() ;Reset The Modelview Matrix
glTranslated(x,y,0) ;Position The Text (0,0 - Bottom Left)
glListBase_(base-32+(128*set)) ;Choose The Font Set (0 or 1)
If set=0 ;If Set 0 Is Being Used Enlarge Font
glScalef_(1.5,2.0,1.0) ;Enlarge Font Width And Height
EndIf
glCallLists_(Len(text),#GL_UNSIGNED_BYTE,text) ;Write The Text To The Screen
glDisable_(#GL_TEXTURE_2D) ;Disable Texture Mapping
EndProcedure
Procedure ReSizeGLScene(width.l,height.l) ;Resize And Initialize The GL Window
If height=0 : height=1 : EndIf ;Prevent A Divide By Zero Error
glViewport_(0,0,width,height) ;Reset The Current Viewport
glMatrixMode_(#GL_PROJECTION) ;Select The Projection Matrix
glLoadIdentity_() ;Reset The Projection Matrix
glOrtho(0.0,width,height,0.0,-1.0,1.0) ;Create Ortho 640x480 View (0,0 At Top Left)
glMatrixMode_(#GL_MODELVIEW) ;Select The Modelview Matrix
glLoadIdentity_() ;Reset The Modelview Matrix
EndProcedure
Procedure.l InitGL() ;All Setup For OpenGL Goes Here
If LoadGLTextures()=0 ;Jump To Texture Loading Routine
ProcedureReturn #False ;If Texture Didn't Load Return FALSE
EndIf
BuildFont() ;Build The Font
glShadeModel_(#GL_SMOOTH) ;Enable Smooth Shading
glClearColor_(0.0,0.0,0.0,0.5) ;Black Background
glClearDepth(1.0) ;Depth Buffer Setup
glHint_(#GL_LINE_SMOOTH_HINT,#GL_NICEST) ;Set Line Antialiasing
glEnable_(#GL_BLEND) ;Enable Blending
glBlendFunc_(#GL_SRC_ALPHA,#GL_ONE_MINUS_SRC_ALPHA) ;Type Of Blending To Use
ProcedureReturn #True ;Initialization Went OK
EndProcedure
Procedure.l DrawGLScene() ;Here's Where We Do All The Drawing
glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear Screen And Depth Buffer
glBindTexture_(#GL_TEXTURE_2D,texture(0)) ;Select Our Font Texture
glColor3f_(1.0,0.5,1.0) ;Set Color To Purple
glPrint(207,24,0,"GRID CRAZY") ;Write GRID CRAZY On The Screen
glColor3f_(1.0,1.0,0.0) ;Set Color To Yellow
glPrint(20,20,1,"Level:"+Str(level2)) ;Write Actual Level Stats
glPrint(20,40,1,"Stage:"+Str(stage)) ;Write Stage Stats
If gameover ;Is The Game Over?
glColor3ub_(Random(255),Random(255),Random(255)) ;Pick A Random Color
glPrint(472,20,1,"GAME OVER") ;Write GAME OVER To The Screen
glPrint(456,40,1,"PRESS SPACE") ;Write PRESS SPACE To The Screen
EndIf
For loop1=0 To (lives-1)-1 ;Loop Through Lives Minus Current Life
glLoadIdentity_() ;Reset The View
glTranslatef_(490+(loop1*40.0),40.0,0.0) ;Move To The Right Of Our Title Text
glRotatef_(-player\spin,0.0,0.0,1.0) ;Rotate Counter Clockwise
glColor3f_(0.0,1.0,0.0) ;Set Player Color To Light Green
glBegin_(#GL_LINES) ;Start Drawing Our Player Using Lines
glVertex2i_(-5,-5) ;Top Left Of Player
glVertex2i_( 5, 5) ;Bottom Right Of Player
glVertex2i_( 5,-5) ;Top Right Of Player
glVertex2i_(-5, 5) ;Bottom Left Of Player
glEnd_() ;Done Drawing The Player
glRotatef_(-player\spin*0.5,0.0,0.0,1.0) ;Rotate Counter Clockwise
glColor3f_(0.0,0.75,0.0) ;Set Player Color To Dark Green
glBegin_(#GL_LINES) ;Start Drawing Our Player Using Lines
glVertex2i_(-7, 0) ;Left Center Of Player
glVertex2i_( 7, 0) ;Right Center Of Player
glVertex2i_( 0,-7) ;Top Center Of Player
glVertex2i_( 0, 7) ;Bottom Center Of Player
glEnd_() ;Done Drawing The Player
Next
filled=#True ;Set Filled To True Before Testing
glLineWidth_(2.0) ;Set Line Width For Cells To 2.0
glDisable_(#GL_LINE_SMOOTH) ;Disable Antialiasing
glLoadIdentity_() ;Reset The Current Modelview Matrix
For loop1=0 To 11-1 ;Loop From Left To Right
For loop2=0 To 11-1 ;Loop From Top To Bottom
glColor3f_(0.0,0.5,1.0) ;Set Line Color To Blue
If hline(loop1,loop2) ;Has The Horizontal Line Been Traced
glColor3f_(1.0,1.0,1.0) ;If So, Set Line Color To White
EndIf
If loop1<10 ;Dont Draw To Far Right
If hline(loop1,loop2)=0 ;If A Horizontal Line Isn't Filled
filled=#False ;filled Becomes False
EndIf
glBegin_(#GL_LINES) ;Start Drawing Horizontal Cell Borders
glVertex2i_(20+(loop1*60),70+(loop2*40)) ;Left Side Of Horizontal Line
glVertex2i_(80+(loop1*60),70+(loop2*40)) ;Right Side Of Horizontal Line
glEnd_() ;Done Drawing Horizontal Cell Borders
EndIf
glColor3f_(0.0,0.5,1.0) ;Set Line Color To Blue
If vline(loop1,loop2) ;Has The Horizontal Line Been Traced
glColor3f_(1.0,1.0,1.0) ;If So, Set Line Color To White
EndIf
If loop2<10 ;Dont Draw To Far Down
If vline(loop1,loop2)=0 ;If A Vertical Line Isn't Filled
filled=#False ;filled Becomes False
EndIf
glBegin_(#GL_LINES) ;Start Drawing Vertical Cell Borders
glVertex2i_(20+(loop1*60),70+(loop2*40)) ;Left Side Of Horizontal Line
glVertex2i_(20+(loop1*60),110+(loop2*40)) ;Right Side Of Horizontal Line
glEnd_() ;Done Drawing Vertical Cell Borders
EndIf
glEnable_(#GL_TEXTURE_2D) ;Enable Texture Mapping
glColor3f_(1.0,1.0,1.0) ;Bright White Color
glBindTexture_(#GL_TEXTURE_2D,texture(1)) ;Select The Tile Image
If loop1<10 And loop2<10 ;If In Bounds, Fill In Traced Boxes
;Are All Sides Of The Box Traced?
If hline(loop1,loop2) And hline(loop1,loop2+1) And vline(loop1,loop2) And vline(loop1+1,loop2)
glBegin_(#GL_QUADS) ;Draw A Textured Quad
glTexCoord2f_((loop1/10.0)+0.1,1.0-(loop2/10.0)) ;Top Right (1,1)
glVertex2i_(79+(loop1*60),71+(loop2*40)) ;Top Right
glTexCoord2f_((loop1/10.0),1.0-(loop2/10.0)) ;Top Left (0,1)
glVertex2i_(21+(loop1*60),71+(loop2*40)) ;Top Left
glTexCoord2f_((loop1/10.0),1.0-(loop2/10.0)-0.1) ;Bottom Left (0,0)
glVertex2i_(21+(loop1*60),109+(loop2*40)) ;Bottom Left
glTexCoord2f_((loop1/10.0)+0.1,1.0-(loop2/10.0)-0.1) ;Bottom Right (1,0)
glVertex2i_(79+(loop1*60),109+(loop2*40)) ;Bottom Right
glEnd_() ;Done Texturing The Box
EndIf
EndIf
glDisable_(#GL_TEXTURE_2D) ;Disable Texture Mapping
Next
Next
glLineWidth_(1.0) ;Set The Line Width To 1.0
If anti ;Is Anti TRUE?
glEnable_(#GL_LINE_SMOOTH) ;If So, Enable Antialiasing
EndIf
If hourglass\fx=1 ;If fx=1 (visible) Draw The Hourglass
glLoadIdentity_() ;Reset The Modelview Matrix
glTranslatef_(20.0+(hourglass\x*60),70.0+(hourglass\y*40),0.0) ;Move To The Fine Hourglass Position
glRotatef_(hourglass\spin,0.0,0.0,1.0) ;Rotate Clockwise
glColor3ub_(Random(255),Random(255),Random(255)) ;Set Hourglass Color To Random Color
glBegin_(#GL_LINES) ;Start Drawing Our Hourglass Using Lines
glVertex2i_(-5,-5) ;Top Left Of Hourglass
glVertex2i_( 5, 5) ;Bottom Right Of Hourglass
glVertex2i_( 5,-5) ;Top Right Of Hourglass
glVertex2i_(-5, 5) ;Bottom Left Of Hourglass
glVertex2i_(-5, 5) ;Bottom Left Of Hourglass
glVertex2i_( 5, 5) ;Bottom Right Of Hourglass
glVertex2i_(-5,-5) ;Top Left Of Hourglass
glVertex2i_( 5,-5) ;Top Right Of Hourglass
glEnd_() ;Done Drawing The Hourglass
EndIf
glLoadIdentity_() ;Reset The Modelview Matrix
glTranslatef_(player\fx+20.0,player\fy+70.0,0.0); Move To The Fine Player Position
glRotatef_(player\spin,0.0,0.0,1.0) ;Rotate Clockwise
glColor3f_(0.0,1.0,0.0) ;Set Player Color To Light Green
glBegin_(#GL_LINES) ;Start Drawing Our Player Using Lines
glVertex2i_(-5,-5) ;Top Left Of Player
glVertex2i_( 5, 5) ;Bottom Right Of Player
glVertex2i_( 5,-5) ;Top Right Of Player
glVertex2i_(-5, 5) ;Bottom Left Of Player
glEnd_() ;Done Drawing The Player
glRotatef_(player\spin*0.5,0.0,0.0,1.0) ;Rotate Clockwise
glColor3f_(0.0,0.75,0.0) ;Set Player Color To Dark Green
glBegin_(#GL_LINES) ;Start Drawing Our Player Using Lines
glVertex2i_(-7, 0) ;Left Center Of Player
glVertex2i_( 7, 0) ;Right Center Of Player
glVertex2i_( 0,-7) ;Top Center Of Player
glVertex2i_( 0, 7) ;Bottom Center Of Player
glEnd_() ;Done Drawing The Player
For loop1=0 To (stage*level)-1 ;Loop To Draw Enemies
glLoadIdentity_() ;Reset The Modelview Matrix
glTranslatef_(enemy(loop1)\fx+20.0,enemy(loop1)\fy+70.0,0.0)
glColor3f_(1.0,0.5,0.5) ;Make Enemy Body Pink
glBegin_(#GL_LINES) ;Start Drawing Enemy
glVertex2i_( 0,-7) ;Top Point Of Body
glVertex2i_(-7, 0) ;Left Point Of Body
glVertex2i_(-7, 0) ;Left Point Of Body
glVertex2i_( 0, 7) ;Bottom Point Of Body
glVertex2i_( 0, 7) ;Bottom Point Of Body
glVertex2i_( 7, 0) ;Right Point Of Body
glVertex2i_( 7, 0) ;Right Point Of Body
glVertex2i_( 0,-7) ;Top Point Of Body
glEnd_() ;Done Drawing Enemy Body
glRotatef_(enemy(loop1)\spin,0.0,0.0,1.0) ;Rotate The Enemy Blade
glColor3f_(1.0,0.0,0.0) ;Make Enemy Blade Red
glBegin_(#GL_LINES) ;Start Drawing Enemy Blade
glVertex2i_(-7,-7) ;Top Left Of Enemy
glVertex2i_( 7, 7) ;Bottom Right Of Enemy
glVertex2i_(-7, 7) ;Bottom Left Of Enemy
glVertex2i_( 7,-7) ;Top Right Of Enemy
glEnd_() ;Done Drawing Enemy Blade
Next
ProcedureReturn #True ;Keep Going
EndProcedure
Procedure KillGLWindow() ;Properly Kill The Window
If fullscreen ;Are We In Fullscreen Mode?
ChangeDisplaySettings_(#Null,0) ;If So Switch Back To The Desktop
ShowCursor_(#True) ;Show Mouse Pointer
EndIf
If hRC ;Do We Have A Rendering Context?
If wglMakeCurrent_(#Null,#Null)=0 ;Are We Able To Release The DC And RC Contexts?
MessageBox_(#Null,"Release Of DC And RC Failed.","SHUTDOWN ERROR",#MB_OK | #MB_ICONINFORMATION)
EndIf
If wglDeleteContext_(hRC)=0 ;Are We Able To Delete The RC?
MessageBox_(#Null,"Release Rendering Context Failed.","SHUTDOWN ERROR",#MB_OK | #MB_ICONINFORMATION)
EndIf
hRC=#Null ;Set RC To NULL
EndIf
If hDC And ReleaseDC_(hWnd,hDC)=0 ;Are We Able To Release The DC
MessageBox_(#Null,"Release Device Context Failed.","SHUTDOWN ERROR",#MB_OK | #MB_ICONINFORMATION)
hDC=#Null ;Set DC To NULL
EndIf
If hWnd And DestroyWindow_(hWnd)=0 ;Are We Able To Destroy The Window?
MessageBox_(#Null,"Could Not Release hWnd.","SHUTDOWN ERROR",#MB_OK | #MB_ICONINFORMATION)
hWnd=#Null ;Set hWnd To NULL
EndIf
If UnregisterClass_("OpenGL",hInstance)=0 ;Are We Able To Unregister Class
MessageBox_(#Null,"Could Not Unregister Class.","SHUTDOWN ERROR",#MB_OK | #MB_ICONINFORMATION)
hInstance=#Null ;Set hInstance To NULL
EndIf
KillFont() ;Kill The Font We Built
EndProcedure
;This Code Creates Our OpenGL Window. Parameters Are:
;title - Title To Appear At The Top Of The Window
;width - Width Of The GL Window Or Fullscreen Mode
;height - Height Of The GL Window Or Fullscreen Mode
;bits - Number Of Bits To Use For Color (8/16/24/32)
;fullscreenflag - Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)
Procedure.b CreateGLWindow(title.s,width.l,height.l,bits.l,fullscreenflag.b)
Protected PixelFormat.l ;Holds The Results After Searching For A Match
Protected wc.WNDCLASS ;Windows Class Structure
Protected dwExStyle.l ;Window Extended Style
Protected dwStyle.l ;Window Style
Protected WindowRect.RECT ;Grabs Rectangle Upper Left / Lower Right Values
Protected wpos.POINT ;Window position
WindowRect\left=0 ;Set Left Value To 0
WindowRect\right=width ;Set Right Value To Requested Width
WindowRect\top=0 ;Set Top Value To 0
WindowRect\bottom=height ;Set Bottom Value To Requested Height
fullscreen=fullscreenflag ;Set The Global Fullscreen Flag
hInstance=GetModuleHandle_(#Null) ;Grab An Instance For Our Window
wc\style=#CS_HREDRAW | #CS_VREDRAW | #CS_OWNDC ;Redraw On Size, And Own DC For Window
wc\lpfnWndProc=@WndProc() ;WndProc Handles Messages
wc\cbClsExtra=0 ;No Extra Window Data
wc\cbWndExtra=0 ;No Extra Window Data
wc\hInstance=hInstance ;Set The Instance
wc\hIcon=LoadIcon_(#Null,#IDI_WINLOGO) ;Load The Default Icon
wc\hCursor=LoadCursor_(#Null,#IDC_ARROW) ;Load The Arrow Pointer
wc\hbrBackground=#Null ;No Background Required For GL
wc\lpszMenuName=#Null ;We Don't Want A Menu
wc\lpszClassName=@"OpenGL" ;Set The Class Name
If RegisterClass_(wc)=0 ;Attempt To Register The Window Class
MessageBox_(#Null,"Failed To Register The Window Class.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
If fullscreen ;Attempt Fullscreen Mode?
Protected dmScreenSettings.DEVMODE ;Device Mode
dmScreenSettings\dmSize=SizeOf(DEVMODE) ;Size Of The Devmode Structure
dmScreenSettings\dmFields=#DM_BITSPERPEL | #DM_PELSWIDTH | #DM_PELSHEIGHT ;bit flags to specify the members of DEVMODE that were initialized
dmScreenSettings\dmBitsPerPel=bits ;Selected Bits Per Pixel
dmScreenSettings\dmPelsWidth=width ;Selected Screen Width in pixels
dmScreenSettings\dmPelsHeight=height ;Selected Screen Height in pixels
;Try To Set Selected Mode And Get Results. Note: CDS_FULLSCREEN Gets Rid Of Start Bar
If ChangeDisplaySettings_(dmScreenSettings,#CDS_FULLSCREEN)<>#DISP_CHANGE_SUCCESSFUL
;If The Mode Fails, Offer Two Options. Quit Or Use Windowed Mode
If MessageBox_(#Null,"The Requested Fullscreen Mode Is Not Supported By"+Chr(10)+"Your Video Card. Use Windowed Mode Instead?","NeHe GL",#MB_YESNO | #MB_ICONEXCLAMATION)=#IDYES
fullscreen=#False ;Windowed Mode Selected. Fullscreen = FALSE
Else
;Pop Up A Message Box Letting User Know The Program Is Closing
MessageBox_(#Null,"Program Will Now Close.","ERROR",#MB_OK | #MB_ICONSTOP)
ProcedureReturn #False
EndIf
EndIf
EndIf
If fullscreen ;Are We Still In Fullscreen Mode?
dwExStyle=#WS_EX_APPWINDOW ;Window Extended Style
dwStyle=#WS_POPUP ;Windows Style
ShowCursor_(#False) ;Hide Mouse Pointer
Else
dwExStyle=#WS_EX_APPWINDOW | #WS_EX_WINDOWEDGE ;Window Extended Style
dwStyle=#WS_OVERLAPPEDWINDOW ;Windows Style
EndIf
AdjustWindowRectEx_(WindowRect,dwStyle,#False,dwExStyle) ;Adjust Window To True Requested Size
If fullscreen=0 ;if not fullscreen mode calculate screen centered window
wpos\x=(GetSystemMetrics_(#SM_CXSCREEN)/2)-((WindowRect\right-WindowRect\left)/2)
wpos\y=(GetSystemMetrics_(#SM_CYSCREEN)/2)-((WindowRect\bottom-WindowRect\top)/2)
EndIf
;CreateWindowEx_(Extended Window Style, Class Name, Window Title, Window Style, Window X Position, Window Y Position, Width, Height, No Parent Window, No Menu, Instance, No Creation Data)
hWnd=CreateWindowEx_(dwExStyle,"OpenGL",title,dwStyle | #WS_CLIPSIBLINGS | #WS_CLIPCHILDREN,wpos\x,wpos\y,WindowRect\right-WindowRect\left,WindowRect\bottom-WindowRect\top,#Null,#Null,hInstance,#Null)
If hWnd=0
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Window Creation Error.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
Protected pfd.PIXELFORMATDESCRIPTOR ;pfd Tells Windows How We Want Things To Be
pfd\nSize=SizeOf(PIXELFORMATDESCRIPTOR) ;Size Of This Structure
pfd\nVersion=1 ;Version Number
pfd\dwFlags=#PFD_SUPPORT_OPENGL | #PFD_DOUBLEBUFFER | #PFD_DRAW_TO_WINDOW ;Format Must Support Window, OpenGL, Double Buffering
pfd\iPixelType=#PFD_TYPE_RGBA ;Request An RGBA Format
pfd\cColorBits=bits ;Select Our Color Depth
pfd\cRedBits=0 ;Color Bits Ignored
pfd\cRedShift=0
pfd\cGreenBits=0
pfd\cGreenShift=0
pfd\cBlueBits=0
pfd\cBlueShift=0
pfd\cAlphaBits=0 ;No Alpha Buffer
pfd\cAlphaShift=0 ;Shift Bit Ignored
pfd\cAccumBits=0 ;No Accumulation Buffer
pfd\cAccumRedBits=0 ;Accumulation Bits Ignored
pfd\cAccumGreenBits=0
pfd\cAccumBlueBits=0
pfd\cAccumAlphaBits=0
pfd\cDepthBits=16 ;16Bit Z-Buffer (Depth Buffer)
pfd\cStencilBits=0 ;No Stencil Buffer
pfd\cAuxBuffers=0 ;No Auxiliary Buffer
pfd\iLayerType=#PFD_MAIN_PLANE ;Main Drawing Layer
pfd\bReserved=0 ;Reserved
pfd\dwLayerMask=0 ;Layer Masks Ignored
pfd\dwVisibleMask=0
pfd\dwDamageMask=0
hDC=GetDC_(hWnd)
If hDC=0 ;Did We Get A Device Context?
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Can't Create A GL Device Context.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
PixelFormat=ChoosePixelFormat_(hDC,pfd)
If PixelFormat=0 ;Did Windows Find A Matching Pixel Format?
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Can't Find A Suitable PixelFormat.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
If SetPixelFormat_(hDC,PixelFormat,pfd)=0 ;Are We Able To Set The Pixel Format?
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Can't Set The PixelFormat.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
hRC=wglCreateContext_(hDC)
If hRC=0 ;Are We Able To Get A Rendering Context?
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Can't Create A GL Rendering Context.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
If wglMakeCurrent_(hDC,hRC)=0 ;Try To Activate The Rendering Context
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Can't Activate The GL Rendering Context.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
ShowWindow_(hWnd,#SW_SHOW) ;Show The Window
SetForegroundWindow_(hWnd) ;Slightly Higher Priority
SetFocus_(hWnd) ;Sets Keyboard Focus To The Window
ReSizeGLScene(width,height) ;Set Up Our Perspective GL Screen
If InitGL()=0 ;Initialize Our Newly Created GL Window
KillGLWindow() ;Reset The Display
MessageBox_(#Null,"Initialization Failed.","ERROR",#MB_OK | #MB_ICONEXCLAMATION)
ProcedureReturn #False
EndIf
ProcedureReturn #True ;Success
EndProcedure
Procedure.l WndProc(hWnd.l,uMsg.l,wParam.l,lParam.l)
Select uMsg ;Check For Windows Messages
Case #WM_ACTIVATE ;Watch For Window Activate Message
If HiWord(wParam)=0 ;Check Minimization State
active=#True ;Program Is Active
Else
active=#False ;Program Is No Longer Active
EndIf
ProcedureReturn 0 ;Return To The Message Loop
Case #WM_SYSCOMMAND ;Intercept System Commands
Select wParam ;Check System Calls
Case #SC_SCREENSAVE ;Screensaver Trying To Start?
ProcedureReturn 0 ;Prevent From Happening
Case #SC_MONITORPOWER ;Monitor Trying To Enter Powersave?
ProcedureReturn 0 ;Prevent From Happening
EndSelect
Case #WM_CLOSE ;Did We Receive A Close Message?
PostQuitMessage_(0) ;Send A Quit Message
ProcedureReturn 0 ;Jump Back
Case #WM_KEYDOWN ;Is A Key Being Held Down?
keys(wParam)=#True ;If So, Mark It As TRUE
ProcedureReturn 0 ;Jump Back
Case #WM_KEYUP ;Has A Key Been Released?
keys(wParam)=#False ;If So, Mark It As FALSE
ProcedureReturn 0 ;Jump Back
Case #WM_SIZE ;Resize The OpenGL Window
ReSizeGLScene(LoWord(lParam),HiWord(lParam)) ;LoWord=Width, HiWord=Height
ProcedureReturn 0 ;Jump Back
EndSelect
;Pass All Unhandled Messages To DefWindowProc
ProcedureReturn DefWindowProc_(hWnd,uMsg,wParam,lParam)
EndProcedure
Procedure.l WinMain() ;Main Program
Protected msg.MSG ;Windows Message Structure
Protected done.b ;Bool Variable To Exit Loop
Protected start.f
;Ask The User Which Screen Mode They Prefer
If MessageBox_(#Null,"Would You Like To Run In Fullscreen Mode?","Start FullScreen?",#MB_YESNO | #MB_ICONQUESTION)=#IDNO
fullscreen=#False ;Windowed Mode
EndIf
If CreateGLWindow("NeHe's Line Tutorial",640,480,16,fullscreen)=0 ;Create The Window
ProcedureReturn 0 ;Quit If Window Was Not Created
EndIf
ResetObjects() ;Set Player / Enemy Starting Positions
TimerInit() ;Initialize The Timer
While done=#False ;Loop That Runs While done=FALSE
If PeekMessage_(msg,#Null,0,0,#PM_REMOVE) ;Is There A Message Waiting?
If msg\message=#WM_QUIT ;Have We Received A Quit Message?
done=#True ;If So done=TRUE
Else ;If Not, Deal With Window Messages
TranslateMessage_(msg) ;Translate The Message
DispatchMessage_(msg) ;Dispatch The Message
EndIf
Else ;If There Are No Messages
start=TimerGetTime() ;Grab Timer Value Before We Draw
;Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
If (active And DrawGLScene()=0) Or keys(#VK_ESCAPE) ;Active? Was There A Quit Received?
done=#True ;ESC or DrawGLScene Signalled A Quit
Else ;Not Time To Quit, Update Screen
SwapBuffers_(hDC) ;Swap Buffers (Double Buffering)
EndIf
While TimerGetTime()<start+(steps(adjust)*2.0)
Wend ;Waste Cycles On Fast Systems
If keys(#VK_F1) ;Is F1 Being Pressed?
keys(#VK_F1)=#False ;If So Make Key FALSE
KillGLWindow() ;Kill Our Current Window
fullscreen=~fullscreen & 1 ;Toggle Fullscreen / Windowed Mode
;Recreate Our OpenGL Window
If CreateGLWindow("NeHe's Line Tutorial",640,480,16,fullscreen)=0
ProcedureReturn 0 ;Quit If Window Was Not Created
EndIf
EndIf
If keys(#VK_A) And ap=0 ;If 'A' Key Is Pressed And Not Held
ap=#True ;ap Becomes TRUE
anti=~anti & 1 ;Toggle Antialiasing
EndIf
If keys(#VK_A)=0 ;If 'A' Key Has Been Released
ap=#False ;ap Becomes FALSE
EndIf
If gameover=0 And active ;If Game Isn't Over And Programs Active Move Objects
For loop1=0 To (stage*level)-1 ;Loop Through All The Enemies
If enemy(loop1)\x<player\x And enemy(loop1)\fy=enemy(loop1)\y*40
enemy(loop1)\x+1 ;Move The Enemy Right
EndIf
If enemy(loop1)\x>player\x And enemy(loop1)\fy=enemy(loop1)\y*40
enemy(loop1)\x-1 ;Move The Enemy Left
EndIf
If enemy(loop1)\y<player\y And enemy(loop1)\fx=enemy(loop1)\x*60
enemy(loop1)\y+1 ;Move The Enemy Down
EndIf
If enemy(loop1)\y>player\y And enemy(loop1)\fx=enemy(loop1)\x*60
enemy(loop1)\y-1 ;Move The Enemy Up
EndIf
If delay>3-level And hourglass\fx<>2 ;If Our Delay Is Done And Player Doesn't Have Hourglass
delay=0 ;Reset The Delay Counter Back To Zero
For loop2=0 To (stage*level)-1 ;Loop Through All The Enemies
If enemy(loop2)\fx<enemy(loop2)\x*60 ;Is Fine Position On X Axis Lower Than Intended Position?
enemy(loop2)\fx+steps(adjust) ;If So, Increase Fine Position On X Axis
enemy(loop2)\spin+steps(adjust) ;Spin Enemy Clockwise
EndIf
If enemy(loop2)\fx>enemy(loop2)\x*60 ;Is Fine Position On X Axis Higher Than Intended Position?
enemy(loop2)\fx-steps(adjust) ;If So, Decrease Fine Position On X Axis
enemy(loop2)\spin-steps(adjust) ;Spin Enemy Counter Clockwise
EndIf
If enemy(loop2)\fy<enemy(loop2)\y*40 ;Is Fine Position On Y Axis Lower Than Intended Position?
enemy(loop2)\fy+steps(adjust) ;If So, Increase Fine Position On Y Axis
enemy(loop2)\spin+steps(adjust) ;Spin Enemy Clockwise
EndIf
If enemy(loop2)\fy>enemy(loop2)\y*40 ;Is Fine Position On Y Axis Higher Than Intended Position?
enemy(loop2)\fy-steps(adjust) ;If So, Decrease Fine Position On Y Axis
enemy(loop2)\spin-steps(adjust) ;Spin Enemy Counter Clockwise
EndIf
Next
EndIf
;Are Any Of The Enemies On Top Of The Player?
If enemy(loop1)\fx=player\fx And enemy(loop1)\fy=player\fy
lives-1 ;If So, Player Loses A Life
If lives=0 ;Are We Out Of Lives?
gameover=#True ;If So, gameover Becomes TRUE
EndIf
ResetObjects() ;Reset Player / Enemy Positions
PlaySound_("Data/Die.wav",#Null,#SND_SYNC) ;Play The Death Sound
EndIf
Next
If keys(#VK_RIGHT) And player\x<10 And player\fx=player\x*60 And player\fy=player\y*40
hline(player\x,player\y)=#True ;Mark The Current Horizontal Border As Filled
player\x+1 ;Move The Player Right
EndIf
If keys(#VK_LEFT) And player\x>0 And player\fx=player\x*60 And player\fy=player\y*40
player\x-1 ;Move The Player Left
hline(player\x,player\y)=#True ;Mark The Current Horizontal Border As Filled
EndIf
If keys(#VK_DOWN) And player\y<10 And player\fx=player\x*60 And player\fy=player\y*40
vline(player\x,player\y)=#True ;Mark The Current Vertical Border As Filled
player\y+1 ;Move The Player Down
EndIf
If keys(#VK_UP) And player\y>0 And player\fx=player\x*60 And player\fy=player\y*40
player\y-1 ;Move The Player Up
vline(player\x,player\y)=#True ;Mark The Current Vertical Border As Filled
EndIf
If player\fx<player\x*60 ;Is Fine Position On X Axis Lower Than Intended Position?
player\fx+steps(adjust) ;If So, Increase The Fine X Position
EndIf
If player\fx>player\x*60 ;Is Fine Position On X Axis Greater Than Intended Position?
player\fx-steps(adjust) ;If So, Decrease The Fine X Position
EndIf
If player\fy<player\y*40 ;Is Fine Position On Y Axis Lower Than Intended Position?
player\fy+steps(adjust) ;If So, Increase The Fine Y Position
EndIf
If player\fy>player\y*40 ;Is Fine Position On Y Axis Lower Than Intended Position?
player\fy-steps(adjust) ;If So, Decrease The Fine Y Position
EndIf
Else ;Otherwise
If keys(#VK_SPACE) ;If Spacebar Is Being Pressed
gameover=#False ;gameover Becomes FALSE
filled=#True ;filled Becomes TRUE
level=1 ;Starting Level Is Set Back To One
level2=1 ;Displayed Level Is Also Set To One
stage=0 ;Game Stage Is Set To Zero
lives=5 ;Lives Is Set To Five
EndIf
EndIf
If filled ;Is The Grid Filled In?
PlaySound_("Data/Complete.wav",#Null,#SND_SYNC) ;If So, Play The Level Complete Sound
stage+1 ;Increase The Stage
If stage>3 ;Is The Stage Higher Than 3?
stage=1 ;If So, Set The Stage To One
level+1 ;Increase The Level
level2+1 ;Increase The Displayed Level
If level>3 ;Is The Level Greater Than 3?
level=3 ;If So, Set The Level To 3
lives+1 ;Give The Player A Free Life
If lives>5 ;Does The Player Have More Than 5 Lives?
lives=5 ;If So, Set Lives To Five
EndIf
EndIf
EndIf
ResetObjects() ;Reset Player / Enemy Positions
For loop1=0 To 11-1 ;Loop Through The Grid X Coordinates
For loop2=0 To 11-1 ;Loop Through The Grid Y Coordinates
If loop1<10 ;If X Coordinate Is Less Than 10
hline(loop1,loop2)=#False ;Set The Current Horizontal Value To FALSE
EndIf
If loop2<10 ;If Y Coordinate Is Less Than 10
vline(loop1,loop2)=#False ;Set The Current Vertical Value To FALSE
EndIf
Next
Next
EndIf
;If The Player Hits The Hourglass While It's Being Displayed On The Screen
If player\fx=hourglass\x*60 And player\fy=hourglass\y*40 And hourglass\fx=1
PlaySound_("Data/Freeze.wav",#Null,#SND_ASYNC | #SND_LOOP) ;Play Freeze Enemy Sound
hourglass\fx=2 ;Set The hourglass fx Variable To Two
hourglass\fy=0 ;Set The hourglass fy Variable To Zero
EndIf
player\spin+0.5*steps(adjust) ;Spin The Player Clockwise
If player\spin>360.0 ;Is The spin Value Greater Than 360?
player\spin-360 ;If So, Subtract 360
EndIf
hourglass\spin-0.25*steps(adjust) ;Spin The Hourglass Counter Clockwise
If hourglass\spin<0.0 ;Is The spin Value Less Than 0?
hourglass\spin+360.0 ;If So, Add 360
EndIf
hourglass\fy+steps(adjust) ;Increase The hourglass fy Variable
;Is The hourglass fx Variable Equal To 0 (invisible) And The fy
;Variable Greater Than 6000 Divided By The Current Level?
If hourglass\fx=0 And hourglass\fy>6000/level
PlaySound_("Data/Hourglass.wav",#Null,#SND_ASYNC) ;If So, Play The Hourglass Appears Sound
hourglass\x=Random(9)+1 ;Give The Hourglass A Random X Value
hourglass\y=Random(10) ;Give The Hourglass A Random Y Value
hourglass\fx=1 ;Set hourglass fx Variable To One (Hourglass Stage)
hourglass\fy=0 ;Set hourglass fy Variable To Zero (Counter)
EndIf
;Is The hourglass fx Variable Equal To 1 (visible) And The fy
;Variable Greater Than 6000 Divided By The Current Level?
If hourglass\fx=1 And hourglass\fy>6000/level
hourglass\fx=0 ;If So, Set fx To Zero (Hourglass Will Vanish)
hourglass\fy=0 ;Set fy to Zero (Counter Is Reset)
EndIf
;Is The hourglass fx Variable Equal To 2 (activated) And The fy
;Variable Greater Than 500 Plus 500 Times The Current Level?
If hourglass\fx=2 And hourglass\fy>500+(500*level)
PlaySound_(#Null,#Null,0) ;If So, Kill The Freeze Sound
hourglass\fx=0 ;Set hourglass fx Variable To Zero
hourglass\fy=0 ;Set hourglass fy Variable To Zero
EndIf
delay+1 ;Increase The Enemy Delay Counter
EndIf
Wend
;Shutdown
KillGLWindow() ;Kill The Window
End ;Exit The Program
EndProcedure
WinMain() ;run the main program