NeHe's Line Tutorial (Lesson 21)

Share your advanced PureBasic knowledge/code with the community.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

NeHe's Line Tutorial (Lesson 21)

Post by hagibaba »

Code updated for 5.20+

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
Last edited by hagibaba on Sun Jul 01, 2007 11:18 pm, edited 11 times in total.
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

Awesome ... just awesome.

Do you plan to contact the official NeHe site in order to add the purebasic
conversion there :?:

Keep up the great work.
regards,
benny!
-
pe0ple ar3 str4nge!!!
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

benny wrote:Do you plan to contact the official NeHe site in order to add the purebasic
conversion there :?:
I'm not sure if this makes sense as long as the conversions are not 1:1 (?)
Good programmers don't comment their code. It was hard to write, should be hard to read.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Post by hagibaba »

yes, it would be cool to have PB listed on there!

I would rather see how many I can do first before contacting NeHe. I might get stuck soon as they're getting harder. So when I get stuck, or just fed up! I'll let them know.

They're pretty faithful translations, apart from the bitmap loading code using the glAux replacement code, so I don't think that would be a problem.

Also, I just updated this. There was a tiny bug printing one too many lives.
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

traumatic wrote:
benny wrote:Do you plan to contact the official NeHe site in order to add the purebasic
conversion there :?:
I'm not sure if this makes sense as long as the conversions are not 1:1 (?)
@traumatic:

1st: hi ... long time no see ... hope everything goes fine ;-)

2nd: what exactly do you mean ? are there any code guidelines for nehe
translations ? :roll:
regards,
benny!
-
pe0ple ar3 str4nge!!!
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

benny wrote:1st: hi ... long time no see ... hope everything goes fine ;-)
Uhm, moved town again, married, started running my own business, yes,
everything's fine, thank you! And you? :)

I know it's a shame I don't show up too often lately, sorry for that.
Hmm... time's change I guess...
benny wrote:2nd: what exactly do you mean ? are there any code guidelines for nehe translations ? :roll:
Well I don't think so but IMHO it'd be cool to have all codes behave exactly
the same. Now with LoadImage_() loading 24bit BMPs only (in opposite to
the glAux one), there's quite some difference. This of course doesn't mean
I don't appreciate hagibaba's efforts or something, I just wanted to point
it out.

While we're at it: Hagibaba, I'm curious, why don't you actually use the glAux
routines?
Good programmers don't comment their code. It was hard to write, should be hard to read.
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

traumatic wrote:
benny wrote:1st: hi ... long time no see ... hope everything goes fine ;-)
Uhm, moved town again, married, started running my own business, yes,
everything's fine, thank you! And you? :)
check pm - don't want to hijack this post and getting it off topic.
regards,
benny!
-
pe0ple ar3 str4nge!!!
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

Yep, sorry. :oops:
Good programmers don't comment their code. It was hard to write, should be hard to read.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Post by hagibaba »

hi,

I don't use the glAux routine mainly because I couldn't get it working! If I could get that code working I might use it. The only thing I don't like is the dependancy on the glAux.dll, I would prefer code that didn't depend on a dll so I'm happy enough using LoadImage_() as it is a quick and easy way to just get it working.

Also, btw, I can't translate lesson 22. It uses "glext.h" and needs procedures, like glActiveTextureARB, which if I am correct would need the "glext.lib" which PB doesn't have? So I'll just move onto lesson 23.
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

hagibaba wrote:Also, btw, I can't translate lesson 22. It uses "glext.h" and needs procedures, like glActiveTextureARB, which if I am correct would need the "glext.lib" which PB doesn't have? So I'll just move onto lesson 23.
Move back! :)

You should check for available extensions in runtime anyhow, no .lib needed.

See here (as an example, there may be other good threads as well)
http://purebasic.fr/english/viewtopic.php?t=12485
Good programmers don't comment their code. It was hard to write, should be hard to read.
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

Concerning glaux, since it's deprecated, it may be a good choice not using it
but what about adding support for other bit-depths than 24 then? ;)
Good programmers don't comment their code. It was hard to write, should be hard to read.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Post by hagibaba »

Thanks for the link traumatic. I translated lesson 22 mostly, but it's not working. I think I am using the CallFastFunction properly but it's not right somewhere. Maybe you, or someone else, can figure out where I went wrong?? Here is what I got (Press M, B, E, F, Arrows, Pageup/Pagedown).

Edit: code improved and moved to here
http://www.purebasic.fr/english/viewtopic.php?t=25549
Last edited by hagibaba on Wed Jan 24, 2007 9:27 pm, edited 1 time in total.
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

Well, I can't give you working code as I simply don't have time for that, but:

Either write a *working* replacement or use auxDIBImageLoad() as you'll
need 8bit textures for the bumpmap.

Code: Select all

Structure AUX_RGBImageRec
  sizeX.l : sizeY.l
  *Data.c
EndStructure

ImportC "msvcrt.lib"
EndImport

!public __imp__wsprintfA ; should actually work linking user32.lib IIRC but doesn't...
!__imp__wsprintfA dw 0 ; sadly I don't have the time to check where the problem is

Import "glaux.lib"
  auxDIBImageLoad(a.p-ascii) As "_auxDIBImageLoadA@4"
EndImport

.
.
.
.
.

*bumpMap.AUX_RGBImageRec = auxDIBImageLoad("Data/Bump.bmp")

.
.
.
.
.

glTexImage2D_(#GL_TEXTURE_2D, 0, #GL_RGB8,  *bumpMap\sizeX,  *bumpMap\sizeY, 0, #GL_RGB, #GL_UNSIGNED_BYTE,  *bumpMap\Data)

Then, inverting the texture is indispensable, maybe something like this will do the trick:

Code: Select all

For i=0 To (3 * *bumpMap\sizeX * *bumpMap\sizeY)-1
  *bumpMap\data = 255 - *bumpMap\data
  *bumpMap+SizeOf(*bumpMap)
Next

Hope that helps, sorry for the brief answer.
Good programmers don't comment their code. It was hard to write, should be hard to read.
hagibaba
Enthusiast
Enthusiast
Posts: 170
Joined: Fri Mar 05, 2004 2:55 am
Location: UK
Contact:

Post by hagibaba »

> Concerning glaux, since it's deprecated, it may be a good choice not using it but what about adding support for other bit-depths than 24 then?

Done! I've updated all the code entrys too.

The new LoadBMP procedure supports 1,4,8 and 24 bits. It converts 1,4 and 8 bits to 24. I tried generating 8-bit bitmaps but it didn't really work, there was nothing wrong with my bitmap loading code (it came from a Microsoft source) so I decided to convert to 24.

Maybe that is what glAux.dll does, but I can't tell as I don't have the source. My educated guess from studying the glTexImage2D command is that it is very likely converting to 24-bit.

Also, I'd like to point out that I didn't have much fun doing that.
User avatar
Rings
Moderator
Moderator
Posts: 1427
Joined: Sat Apr 26, 2003 1:11 am

Post by Rings »

traumatic wrote:.... moved town again, married, started running my own business, yes,
welcome in jail then ;)
SPAMINATOR NR.1
Post Reply