NeHe's Multitexture & Bump Mapping Tutorial (Lesson 22)

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 Multitexture & Bump Mapping Tutorial (Lesson 22)

Post by hagibaba »

Code updated for 5.20+

This shows how to do multi-texturing, "fake" bump mapping and textures with an alpha channel.
Press M key for multitexturing, B key for bump mapping, E for emboss effect, F key for filters, Arrows to rotate cube, Pageup/Pagedown to zoom.
You can get the "Base.bmp", "Bump.bmp", "OpenGL.bmp", "OpenGL_ALPHA.bmp", "Multi_On.bmp" and "multi_on_alpha.bmp"
from the "lesson22.zip" here:
http://nehe.gamedev.net/data/lessons/vc/lesson22.zip

Note: multitextured bump mapping does not work right ie. doMesh2TexelUnits().

Last edited on 20 Feb 2007.

Code: Select all

;NeHe's GL_ARB_multitexture & Bump Mapping Tutorial (Lesson 22)
;http://nehe.gamedev.net
;Credits: Nico Gruener, Dreglor, traumatic
;Author: hagibaba
;Date: 24 Jan 2007
;Note: up-to-date with PB v4.02 (Windows)
;Note: requires bitmaps in paths "Data/Base.bmp", "Data/Bump.bmp",
;"Data/OpenGL.bmp", "Data/OpenGL_Alpha.bmp", "Data/Multi_On.bmp",
;"Data/Multi_On_Alpha.bmp"

;Section for standard constants, structures, macros and declarations

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

;glext.h constants
#GL_MAX_TEXTURE_UNITS_ARB=$84E2
#GL_COMBINE_EXT=$8570
#GL_COMBINE_RGB_EXT=$8571
#GL_TEXTURE0_ARB=$84C0
#GL_TEXTURE1_ARB=$84C1

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 "glu32.lib"
  gluPerspective(fovy.d,aspect.d,zNear.d,zFar.d) ;sets up a perspective projection matrix
EndImport

Import "opengl32.lib"
  glClearDepth(depth.d) ;specifies the clear value for the depth buffer
EndImport

;Start of Lesson 22

;Maximum Emboss-Translate. Increase To Get Higher Immersion
;At A Cost Of Lower Quality (More Artifacts Will Occur!)
#MAX_EMBOSS=0.008

;Here Comes The ARB-Multitexture Support.
;There Are (Optimally) 6 New Commands To The OpenGL Set:
;glMultiTexCoordifARB i=1..4 : Sets Texture-Coordinates For Texel-Pipeline #i
;glActiveTextureARB : Sets Active Texel-Pipeline
;glClientActiveTextureARB : Sets Active Texel-Pipeline For The Pointer-Array-Commands
;There Are Even More For The Various Formats Of glMultiTexCoordi{f,fv,d,i} But We Don't Need Them.

#__ARB_ENABLE=#True ;Used To Disable ARB Extensions Entirely
#EXT_INFO=#False ;Do You Want To See Your Extensions At Start-Up?

Global multitextureSupported.b=#False ;Flag Indicating Whether Multitexturing Is Supported
Global useMultitexture.b;=#True ;Use It If It Is Supported?
Global maxTexelUnits.l=1 ;Number Of Texel-Pipelines. This Is At Least 1.

Global glMultiTexCoord1fARB.l
Global glMultiTexCoord2fARB.l
Global glMultiTexCoord3fARB.l
Global glMultiTexCoord4fARB.l
Global glActiveTextureARB.l
Global glClientActiveTextureARB.l

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 emboss.b=#False ;Emboss Only, No Basetexture?
Global bumps.b=#True ;Do Bumpmapping?

Global xrot.f ;X Rotation
Global yrot.f ;Y Rotation
Global xspeed.f ;X Rotation Speed
Global yspeed.f ;Y Rotation Speed
Global z.f=-5.0 ;Depth Into The Screen

Global filter.l=1 ;Which Filter To Use
Global Dim texture.l(3) ;Storage For 3 Textures
Global Dim bump.l(3) ;Our Bumpmappings
Global Dim invbump.l(3) ;Inverted Bumpmaps
Global glLogo.l ;Handle For OpenGL-Logo
Global multiLogo.l ;Handle For Multitexture-Enabled-Logo

Global Dim LightAmbient.f(3) ;Ambient Light is 20% white
LightAmbient(0)=0.2 ;red
LightAmbient(1)=0.2 ;green
LightAmbient(2)=0.2 ;blue

Global Dim LightDiffuse.f(3) ;Diffuse Light is white
LightDiffuse(0)=1.0 ;red
LightDiffuse(1)=1.0 ;green
LightDiffuse(2)=1.0 ;blue

Global Dim LightPosition.f(3) ;Position is somewhat in front of screen
LightPosition(0)=0.0 ;x
LightPosition(1)=0.0 ;y
LightPosition(2)=2.0 ;z

Global Dim Gray.f(4)
Gray(0)=0.5 ;red
Gray(1)=0.5 ;green
Gray(2)=0.5 ;blue
Gray(3)=1.0 ;alpha

;Data Contains The Faces For The Cube In Format 2xTexCoord, 3xVertex;
;Note That The Tesselation Of The Cube Is Only Absolute Minimum.
Global Dim dat.f(120)
;Front Face
dat(  0)=0.0: dat(  1)=0.0: dat(  2)=-1.0: dat(  3)=-1.0: dat(  4)= 1.0
dat(  5)=1.0: dat(  6)=0.0: dat(  7)= 1.0: dat(  8)=-1.0: dat(  9)= 1.0
dat( 10)=1.0: dat( 11)=1.0: dat( 12)= 1.0: dat( 13)= 1.0: dat( 14)= 1.0
dat( 15)=0.0: dat( 16)=1.0: dat( 17)=-1.0: dat( 18)= 1.0: dat( 19)= 1.0
;Back Face
dat( 20)=1.0: dat( 21)=0.0: dat( 22)=-1.0: dat( 23)=-1.0: dat( 24)=-1.0
dat( 25)=1.0: dat( 26)=1.0: dat( 27)=-1.0: dat( 28)= 1.0: dat( 29)=-1.0
dat( 30)=0.0: dat( 31)=1.0: dat( 32)= 1.0: dat( 33)= 1.0: dat( 34)=-1.0
dat( 35)=0.0: dat( 36)=0.0: dat( 37)= 1.0: dat( 38)=-1.0: dat( 39)=-1.0
;Top Face
dat( 40)=0.0: dat( 41)=1.0: dat( 42)=-1.0: dat( 43)= 1.0: dat( 44)=-1.0
dat( 45)=0.0: dat( 46)=0.0: dat( 47)=-1.0: dat( 48)= 1.0: dat( 49)= 1.0
dat( 50)=1.0: dat( 51)=0.0: dat( 52)= 1.0: dat( 53)= 1.0: dat( 54)= 1.0
dat( 55)=1.0: dat( 56)=1.0: dat( 57)= 1.0: dat( 58)= 1.0: dat( 59)=-1.0
;Bottom Face
dat( 60)=1.0: dat( 61)=1.0: dat( 62)=-1.0: dat( 63)=-1.0: dat( 64)=-1.0
dat( 65)=0.0: dat( 66)=1.0: dat( 67)= 1.0: dat( 68)=-1.0: dat( 69)=-1.0
dat( 70)=0.0: dat( 71)=0.0: dat( 72)= 1.0: dat( 73)=-1.0: dat( 74)= 1.0
dat( 75)=1.0: dat( 76)=0.0: dat( 77)=-1.0: dat( 78)=-1.0: dat( 79)= 1.0
;Right Face
dat( 80)=1.0: dat( 81)=0.0: dat( 82)= 1.0: dat( 83)=-1.0: dat( 84)=-1.0
dat( 85)=1.0: dat( 86)=1.0: dat( 87)= 1.0: dat( 88)= 1.0: dat( 89)=-1.0
dat( 90)=0.0: dat( 91)=1.0: dat( 92)= 1.0: dat( 93)= 1.0: dat( 94)= 1.0
dat( 95)=0.0: dat( 96)=0.0: dat( 97)= 1.0: dat( 98)=-1.0: dat( 99)= 1.0
;Left Face
dat(100)=0.0: dat(101)=0.0: dat(102)=-1.0: dat(103)=-1.0: dat(104)=-1.0
dat(105)=1.0: dat(106)=0.0: dat(107)=-1.0: dat(108)=-1.0: dat(109)= 1.0
dat(110)=1.0: dat(111)=1.0: dat(112)=-1.0: dat(113)= 1.0: dat(114)= 1.0
dat(115)=0.0: dat(116)=1.0: dat(117)=-1.0: dat(118)= 1.0: dat(119)=-1.0

Declare.l WndProc(hWnd.l,uMsg.l,wParam.l,lParam.l) ;Declaration For WndProc

;Always Check For Extension-Availability During Run-Time!
;isMultitextureSupported() Checks At Run-Time If Multitexturing Is Supported

Procedure.b initMultitexture()
  
  Protected extensions.s
  extensions=PeekS(glGetString_(#GL_EXTENSIONS)) ;Fetch Extension String
  
  If #EXT_INFO=#True
    MessageBox_(hWnd,extensions,"supported GL extensions",#MB_OK | #MB_ICONINFORMATION)
  EndIf
  
  ;Is Multitexturing Supported? And Override-Flag And Is texture_env_combining Supported?
  If FindString(extensions,"GL_ARB_multitexture",1) And #__ARB_ENABLE And FindString(extensions,"GL_EXT_texture_env_combine",1)
    glGetIntegerv_(#GL_MAX_TEXTURE_UNITS_ARB,@maxTexelUnits)
    glMultiTexCoord1fARB=wglGetProcAddress_("glMultiTexCoord1fARB")
    glMultiTexCoord2fARB=wglGetProcAddress_("glMultiTexCoord2fARB")
    glMultiTexCoord3fARB=wglGetProcAddress_("glMultiTexCoord3fARB")
    glMultiTexCoord4fARB=wglGetProcAddress_("glMultiTexCoord4fARB")
    glActiveTextureARB=wglGetProcAddress_("glActiveTextureARB")
    glClientActiveTextureARB=wglGetProcAddress_("glClientActiveTextureARB")
    If #EXT_INFO
      MessageBox_(hWnd,"The GL_ARB_multitexture extension will be used.","feature supported!",#MB_OK | #MB_ICONINFORMATION)
    EndIf
    ProcedureReturn #True
  EndIf
  
  useMultitexture=#False ;We Can't Use It If It Isn't Supported!
  ProcedureReturn #False
  
EndProcedure

Procedure initLights()
  
  ;Load Light-Parameters Into GL_LIGHT1
  glLightfv_(#GL_LIGHT1,#GL_AMBIENT,LightAmbient())
  glLightfv_(#GL_LIGHT1,#GL_DIFFUSE,LightDiffuse())
  glLightfv_(#GL_LIGHT1,#GL_POSITION,LightPosition())
  
  glEnable_(#GL_LIGHT1)
  
EndProcedure

Procedure.l LoadGLTextures() ;Load Bitmaps And Convert To Textures
  
  Protected status.b=#True ;Status Indicator
  Protected *Image.AUX_RGBImageRec ;Create Storage Space For The Texture
  Protected Dim alpha2.b(4)
  Protected i.l
  
  ;Use GL_RGB8 Instead Of "3" In glTexImage2D. Also Defined By GL: GL_RGBA8 Etc.
  ;NEW: Now Creating GL_RGBA8 Textures, Alpha Is 1.0 Where Not Specified By Format.
  
  ;Load The Tile-Bitmap For Base-Texture
  *Image=auxDIBImageLoad("Data/Base.bmp")
  If *Image
    glGenTextures_(3,@texture(0)) ;Create Three Textures
    
    ;Create Nearest Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,texture(0))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_NEAREST)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_NEAREST)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,*Image\sizeX,*Image\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    ;Create Linear Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,texture(1))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,*Image\sizeX,*Image\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    ;Create MipMapped Texture
    glBindTexture_(#GL_TEXTURE_2D,texture(2))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps_(#GL_TEXTURE_2D,#GL_RGB8,*Image\sizeX,*Image\sizeY,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
  Else
    status=#False
  EndIf
  
  If *Image ;If Texture Exists
    If *Image\Data
      ;FreeMemory(*Image\Data)
    EndIf ;If Texture Image Exists
    ;FreeMemory(*Image)
    *Image=#Null
  EndIf
  
  ;Load The Bumpmaps
  *Image=auxDIBImageLoad("Data/Bump.bmp")
  If *Image
    glPixelTransferf_(#GL_RED_SCALE,0.5) ;Scale RGB By 50%, So That We Have Only   
    glPixelTransferf_(#GL_GREEN_SCALE,0.5) ;Half Intenstity
    glPixelTransferf_(#GL_BLUE_SCALE,0.5)
    
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_WRAP_S,#GL_CLAMP) ;No Wrapping, Please!
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_WRAP_T,#GL_CLAMP)
    glTexParameterfv_(#GL_TEXTURE_2D,#GL_TEXTURE_BORDER_COLOR,Gray())
    
    glGenTextures_(3,@bump(0)) ;Create Three Textures
    
    ;Create Nearest Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,bump(0))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_NEAREST)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_NEAREST)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,*Image\sizeX,*Image\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    ;Create Linear Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,bump(1))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,*Image\sizeX,*Image\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    ;Create MipMapped Texture
    glBindTexture_(#GL_TEXTURE_2D,bump(2))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps_(#GL_TEXTURE_2D,#GL_RGB8,*Image\sizeX,*Image\sizeY,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    For i=0 To (3**Image\sizeX**Image\sizeY)-1 ;Invert The Bumpmap
      PokeB(*Image\Data+i,255-PeekB(*Image\Data+i))
    Next
    
    glGenTextures_(3,@invbump(0)) ;Create Three Textures
    
    ;Create Nearest Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,invbump(0))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_NEAREST)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_NEAREST)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,*Image\sizeX,*Image\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    ;Create Linear Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,invbump(1))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,*Image\sizeX,*Image\sizeY,0,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    ;Create MipMapped Texture
    glBindTexture_(#GL_TEXTURE_2D,invbump(2))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps_(#GL_TEXTURE_2D,#GL_RGB8,*Image\sizeX,*Image\sizeY,#GL_RGB,#GL_UNSIGNED_BYTE,*Image\Data)
    
    glPixelTransferf_(#GL_RED_SCALE,1.0) ;Scale RGB Back To 100% Again 
    glPixelTransferf_(#GL_GREEN_SCALE,1.0)
    glPixelTransferf_(#GL_BLUE_SCALE,1.0)
  Else
    status=#False
  EndIf
  
  If *Image ;If Texture Exists
    If *Image\Data ;If Texture Image Exists
      ;FreeMemory(*Image\Data)
    EndIf
    ;FreeMemory(*Image)
    *Image=#Null
  EndIf
  
  ;Load The Logo-Bitmaps
  *Image=auxDIBImageLoad("Data/OpenGL_Alpha.bmp")
  If *Image       
    ReDim alpha2.b(4**Image\sizeX**Image\sizeY) ;Create Memory For RGBA8-Texture
    
    For i=0 To (*Image\sizeX**Image\sizeY)-1
      Alpha2((4*i)+3)=PeekB(*Image\Data+(i*3)) ;Pick Only Red Value As Alpha!
    Next
    
    *Image=auxDIBImageLoad("Data/OpenGL.bmp")
    If *Image=0
      status=#False
    EndIf
    
    For i=0 To (*Image\sizeX**Image\sizeY)-1
      Alpha2(4*i)=PeekB(*Image\Data+(i*3)) ;Red
      Alpha2((4*i)+1)=PeekB(*Image\Data+(i*3)+1) ;Green
      Alpha2((4*i)+2)=PeekB(*Image\Data+(i*3)+2) ;Blue
    Next
    
    glGenTextures_(1,@glLogo) ;Create One Textures
    
    ;Create Linear Filtered RGBA8-Texture
    glBindTexture_(#GL_TEXTURE_2D,glLogo)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGBA8,*Image\sizeX,*Image\sizeY,0,#GL_RGBA,#GL_UNSIGNED_BYTE,Alpha2())
    Dim alpha2.b(0)
  Else
    status=#False
  EndIf
  
  If *Image ;If Texture Exists
    If *Image\Data ;If Texture Image Exists
      ;FreeMemory(*Image\Data)
    EndIf
    ;FreeMemory(*Image)
    *Image=#Null
  EndIf
  
  ;Load The "Extension Enabled"-Logo
  *Image=auxDIBImageLoad("Data/Multi_On_Alpha.bmp")
  If *Image
    ReDim alpha2.b(4**Image\sizeX**Image\sizeY) ;Create Memory For RGBA8-Texture
    
    For i=0 To (*Image\sizeX**Image\sizeY)-1
      Alpha2((4*i)+3)=PeekB(*Image\Data+(i*3)) ;Pick Only Red Value As Alpha!
    Next
    
    *Image=auxDIBImageLoad("Data/Multi_On.bmp")
    If *Image=0
      status=#False
    EndIf
    
    For i=0 To (*Image\sizeX**Image\sizeY)-1
      Alpha2(4*i)=PeekB(*Image\Data+(i*3)) ;Red
      Alpha2((4*i)+1)=PeekB(*Image\Data+(i*3)+1) ;Green
      Alpha2((4*i)+2)=PeekB(*Image\Data+(i*3)+2) ;Blue
    Next
    
    glGenTextures_(1,@multiLogo) ;Create One Textures
    
    ;Create Linear Filtered RGBA8-Texture
    glBindTexture_(#GL_TEXTURE_2D,multiLogo)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGBA8,*Image\sizeX,*Image\sizeY,0,#GL_RGBA, #GL_UNSIGNED_BYTE,Alpha2())
    Dim alpha2.b(0)
  Else
    status=#False
  EndIf
  
  If *Image ;If Texture Exists
    If *Image\Data ;If Texture Image Exists
      ;FreeMemory(*Image\Data)
    EndIf
    ;FreeMemory(*Image)
    *Image=#Null
  EndIf 
  
  ProcedureReturn status ;Return The Status
  
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
  
  gluPerspective(45.0,Abs(width/height),0.1,100.0) ;Calculate The Aspect Ratio Of The Window
  
  glMatrixMode_(#GL_MODELVIEW) ;Select The Modelview Matrix
  glLoadIdentity_() ;Reset The Modelview Matrix
  
EndProcedure

Procedure doCube()
  
  Protected i.l
  
  glBegin_(#GL_QUADS)
  ;Front Face
  glNormal3f_( 0.0, 0.0, 1.0)
  For i=0 To 4-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Back Face
  glNormal3f_( 0.0, 0.0,-1.0)
  For i=4 To 8-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Top Face
  glNormal3f_( 0.0, 1.0, 0.0)
  For i=8 To 12-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Bottom Face
  glNormal3f_( 0.0,-1.0, 0.0)
  For i=12 To 16-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Right face
  glNormal3f_( 1.0, 0.0, 0.0)
  For i=16 To 20-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Left Face
  glNormal3f_(-1.0, 0.0, 0.0)
  For i=20 To 24-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  glEnd_()
  
EndProcedure

Procedure.l InitGL() ;All Setup For OpenGL Goes Here
  
  multitextureSupported=initMultitexture()
  
  If LoadGLTextures()=0 ;Jump To Texture Loading Routine
    ProcedureReturn #False ;If Texture Didn't Load Return FALSE
  EndIf
  
  glEnable_(#GL_TEXTURE_2D) ;Enable Texture Mapping
  glShadeModel_(#GL_SMOOTH) ;Enable Smooth Shading
  glClearColor_(0.0,0.0,0.0,0.5) ;Black Background
  glClearDepth(1.0) ;Depth Buffer Setup
  glEnable_(#GL_DEPTH_TEST) ;Enables Depth Testing
  glDepthFunc_(#GL_LEQUAL) ;The Type Of Depth Testing To Do
  glHint_(#GL_PERSPECTIVE_CORRECTION_HINT,#GL_NICEST) ;Really Nice Perspective Calculations
  
  initLights() ;Initialize OpenGL Light
  
  ProcedureReturn #True ;Initialization Went OK
  
EndProcedure

;Calculates v=vM, M Is 4x4 In Column-Major, v Is 4dim. Row (i.e. "Transposed")
Procedure VMatMult(Array M.f(1), Array v.f(1))
  
  Protected Dim res.f(3)
  res(0)=M( 0)*v(0)+M( 1)*v(1)+M( 2)*v(2)+M( 3)*v(3)
  res(1)=M( 4)*v(0)+M( 5)*v(1)+M( 6)*v(2)+M( 7)*v(3)
  res(2)=M( 8)*v(0)+M( 9)*v(1)+M(10)*v(2)+M(11)*v(3)
  v(0)=res(0)
  v(1)=res(1)
  v(2)=res(2)
  v(3)=M(15) ;Homogenous Coordinate
  
EndProcedure

Procedure SetUpBumps(Array n.f(1), Array c.f(1), Array l.f(1), Array s.f(1), Array t.f(1))
  
  Protected Dim v.f(3) ;Vertex From Current Position To Light
  Protected lenQ.f ;Used To Normalize 
  
  ;Calculate v From Current Vector c To Lightposition And Normalize v
  v(0)=l(0)-c(0)
  v(1)=l(1)-c(1)
  v(2)=l(2)-c(2)
  lenQ=Sqr(v(0)*v(0)+v(1)*v(1)+v(2)*v(2))
  v(0)/lenQ : v(1)/lenQ : v(2)/lenQ
  ;Project v Such That We Get Two Values Along Each Texture-Coordinate Axis.
  c(0)=(s(0)*v(0)+s(1)*v(1)+s(2)*v(2))*#MAX_EMBOSS
  c(1)=(t(0)*v(0)+t(1)*v(1)+t(2)*v(2))*#MAX_EMBOSS
  
EndProcedure

Procedure doLogo() ;MUST CALL THIS LAST!!!, Billboards The Two Logos.
  
  glDepthFunc_(#GL_ALWAYS)
  glBlendFunc_(#GL_SRC_ALPHA,#GL_ONE_MINUS_SRC_ALPHA)
  glEnable_(#GL_BLEND)
  glDisable_(#GL_LIGHTING)
  glLoadIdentity_()
  glBindTexture_(#GL_TEXTURE_2D,glLogo)
  glBegin_(#GL_QUADS)
  glTexCoord2f_(0.0,0.0) : glVertex3f_(0.23,-0.40,-1.0)
  glTexCoord2f_(1.0,0.0) : glVertex3f_(0.53,-0.40,-1.0)
  glTexCoord2f_(1.0,1.0) : glVertex3f_(0.53,-0.25,-1.0)
  glTexCoord2f_(0.0,1.0) : glVertex3f_(0.23,-0.25,-1.0)
  glEnd_()
  If useMultitexture
    glBindTexture_(#GL_TEXTURE_2D,multiLogo)
    glBegin_(#GL_QUADS)
    glTexCoord2f_(0.0,0.0) : glVertex3f_(-0.53,-0.4,-1.0)
    glTexCoord2f_(1.0,0.0) : glVertex3f_(-0.33,-0.4,-1.0)
    glTexCoord2f_(1.0,1.0) : glVertex3f_(-0.33,-0.3,-1.0)
    glTexCoord2f_(0.0,1.0) : glVertex3f_(-0.53,-0.3,-1.0)
    glEnd_()
  EndIf
  glDepthFunc_(#GL_LEQUAL)
  
EndProcedure

Procedure.b doMesh1TexelUnits()
  
  Protected Dim c.f(4) ;Holds Current Vertex
  c(0)=0.0 : c(1)=0.0 : c(2)=0.0 : c(3)=1.0
  Protected Dim n.f(4) ;Normalized Normal Of Current Surface 
  n(0)=0.0 : n(1)=0.0 : n(2)=0.0 : n(3)=1.0
  Protected Dim s.f(4) ;s-Texture Coordinate Direction, Normalized
  s(0)=0.0 : s(1)=0.0 : s(2)=0.0 : s(3)=1.0
  Protected Dim t.f(4) ;t-Texture Coordinate Direction, Normalized
  t(0)=0.0 : t(1)=0.0 : t(2)=0.0 : t(3)=1.0
  Protected Dim l.f(4) ;Holds Our Lightposition To Be Transformed Into Object Space
  Protected Dim Minv.f(16) ;Holds The Inverted Modelview Matrix To Do So.
  Protected i.l
  
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear The Screen And The Depth Buffer
  
  ;Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
  ;Simply Build It By Doing All Transformations Negated And In Reverse Order.
  glLoadIdentity_()
  glRotatef_(-yrot,0.0,1.0,0.0)
  glRotatef_(-xrot,1.0,0.0,0.0)
  glTranslatef_(0.0,0.0,-z)
  glGetFloatv_(#GL_MODELVIEW_MATRIX,Minv())
  glLoadIdentity_()
  glTranslatef_(0.0,0.0,z)
  glRotatef_(xrot,1.0,0.0,0.0)
  glRotatef_(yrot,0.0,1.0,0.0)
  
  ;Transform The Lightposition Into Object Coordinates:
  l(0)=LightPosition(0)
  l(1)=LightPosition(1)
  l(2)=LightPosition(2)
  l(3)=1.0 ;Homogenous Coordinate
  VMatMult(Minv(),l())
  
  ;PASS#1: Use Texture "Bump"
  ; No Blend
  ; No Lighting
  ; No Offset Texture-Coordinates
  glBindTexture_(#GL_TEXTURE_2D,bump(filter))
  glDisable_(#GL_BLEND)
  glDisable_(#GL_LIGHTING)
  doCube()
  
  ;PASS#2: Use Texture "Invbump"
  ; Blend GL_ONE To GL_ONE
  ; No Lighting
  ; Offset Texture Coordinates
  glBindTexture_(#GL_TEXTURE_2D,invbump(filter))
  glBlendFunc_(#GL_ONE,#GL_ONE)
  glDepthFunc_(#GL_LEQUAL)
  glEnable_(#GL_BLEND)
  
  glBegin_(#GL_QUADS)
  ;Front Face
  n(0)=0.0 : n(1)=0.0 : n(2)=1.0
  s(0)=1.0 : s(1)=0.0 : s(2)=0.0
  t(0)=0.0 : t(1)=1.0 : t(2)=0.0
  For i=0 To 4-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Back Face
  n(0)= 0.0 : n(1)=0.0 : n(2)=-1.0
  s(0)=-1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)=1.0 : t(2)= 0.0
  For i=4 To 8-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Top Face
  n(0)=0.0 : n(1)=1.0 : n(2)= 0.0
  s(0)=1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)=0.0 : t(1)=0.0 : t(2)=-1.0
  For i=8 To 12-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Bottom Face
  n(0)= 0.0 : n(1)=-1.0 : n(2)= 0.0
  s(0)=-1.0 : s(1)= 0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)= 0.0 : t(2)=-1.0
  For i=12 To 16-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Right Face
  n(0)=1.0 : n(1)=0.0 : n(2)= 0.0
  s(0)=0.0 : s(1)=0.0 : s(2)=-1.0
  t(0)=0.0 : t(1)=1.0 : t(2)= 0.0
  For i=16 To 20-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Left Face
  n(0)=-1.0 : n(1)=0.0 : n(2)=0.0
  s(0)= 0.0 : s(1)=0.0 : s(2)=1.0
  t(0)= 0.0 : t(1)=1.0 : t(2)=0.0
  For i=20 To 24-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  glEnd_()
  
  ;PASS#3: Use Texture "Base"
  ; Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
  ; Lighting Enabled
  ; No Offset Texture-Coordinates
  If emboss=0
    glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_MODULATE)
    glBindTexture_(#GL_TEXTURE_2D,texture(filter))
    glBlendFunc_(#GL_DST_COLOR,#GL_SRC_COLOR)
    glEnable_(#GL_LIGHTING)
    doCube()
  EndIf
  
  xrot+xspeed
  yrot+yspeed
  If xrot>360.0 : xrot-360.0 : EndIf
  If xrot<0.0 : xrot+360.0 : EndIf
  If yrot>360.0 : yrot-360.0 : EndIf
  If yrot<0.0 : yrot+360.0 : EndIf
  
  ;LAST PASS: Do The Logos!
  doLogo()
  
  ProcedureReturn #True ;Keep Going
  
EndProcedure

Procedure.b doMesh2TexelUnits()
  
  Protected Dim c.f(4) ;holds current vertex
  c(0)=0.0 : c(1)=0.0 : c(2)=0.0 : c(3)=1.0
  Protected Dim n.f(4) ;normalized normal of current surface
  n(0)=0.0 : n(1)=0.0 : n(2)=0.0 : n(3)=1.0
  Protected Dim s.f(4) ;s-texture coordinate direction, normalized
  s(0)=0.0 : s(1)=0.0 : s(2)=0.0 : s(3)=1.0
  Protected Dim t.f(4) ;t-texture coordinate direction, normalized
  t(0)=0.0 : t(1)=0.0 : t(2)=0.0 : t(3)=1.0
  Protected Dim l.f(4) ;holds our lightposition to be transformed into object space
  Protected Dim Minv.f(16) ;holds the inverted modelview matrix to do so.
  Protected i.l
  
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear The Screen And The Depth Buffer
  
  ;Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
  ;Simply Build It By Doing All Transformations Negated And In Reverse Order.
  glLoadIdentity_()     
  glRotatef_(-yrot,0.0,1.0,0.0)
  glRotatef_(-xrot,1.0,0.0,0.0)
  glTranslatef_(0.0,0.0,-z)
  glGetFloatv_(#GL_MODELVIEW_MATRIX,Minv())
  glLoadIdentity_()
  glTranslatef_(0.0,0.0,z)
  glRotatef_(xrot,1.0,0.0,0.0)
  glRotatef_(yrot,0.0,1.0,0.0)
  
  ;Transform The Lightposition Into Object Coordinates:
  l(0)=LightPosition(0)
  l(1)=LightPosition(1)
  l(2)=LightPosition(2)
  l(3)=1.0 ;Homogenous Coordinate
  VMatMult(Minv(),l())
  
  ;PASS#1: Texel-Unit 0: Use Texture "Bump"
  ;    No Blend
  ;    No Lighting
  ;    No Offset Texture-Coordinates
  ;    Texture-Operation "Replace"
  ;Texel-Unit 1: Use Texture "Invbump"
  ;    No Lighting
  ;    Offset Texture Coordinates
  ;    Texture-Operation "Replace"
  
  ;TEXTURE-UNIT #0
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE0_ARB)
  glEnable_(#GL_TEXTURE_2D)
  glBindTexture_(#GL_TEXTURE_2D,bump(filter))
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_COMBINE_EXT)
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_COMBINE_RGB_EXT,#GL_REPLACE)
  ;TEXTURE-UNIT #1:
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE1_ARB)
  glEnable_(#GL_TEXTURE_2D)
  glBindTexture_(#GL_TEXTURE_2D,invbump(filter))
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_COMBINE_EXT)
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_COMBINE_RGB_EXT,#GL_ADD)
  ;General Switches:
  glDisable_(#GL_BLEND)
  glDisable_(#GL_LIGHTING)
  
  glBegin_(#GL_QUADS)
  ;Front Face
  n(0)=0.0 : n(1)=0.0 : n(2)=1.0
  s(0)=1.0 : s(1)=0.0 : s(2)=0.0
  t(0)=0.0 : t(1)=1.0 : t(2)=0.0
  For i=0 To 4-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1))
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Back Face
  n(0)= 0.0 : n(1)=0.0 : n(2)=-1.0
  s(0)=-1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)=1.0 : t(2)= 0.0
  For i=4 To 8-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1))
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Top Face
  n(0)=0.0 : n(1)=1.0 : n(2)= 0.0
  s(0)=1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)=0.0 : t(1)=0.0 : t(2)=-1.0
  For i=8 To 12-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Bottom Face
  n(0)= 0.0 : n(1)=-1.0 : n(2)= 0.0
  s(0)=-1.0 : s(1)= 0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)= 0.0 : t(2)=-1.0
  For i=12 To 16-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Right Face
  n(0)=1.0 : n(1)=0.0 : n(2)= 0.0
  s(0)=0.0 : s(1)=0.0 : s(2)=-1.0
  t(0)=0.0 : t(1)=1.0 : t(2)= 0.0
  For i=16 To 20-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Left Face
  n(0)=-1.0 : n(1)=0.0 : n(2)=0.0
  s(0)= 0.0 : s(1)=0.0 : s(2)=1.0
  t(0)= 0.0 : t(1)=1.0 : t(2)=0.0
  For i=20 To 24-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  glEnd_()
  
  ;PASS#2 Use Texture "Base"
  ; Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
  ; Lighting Enabled
  ; No Offset Texture-Coordinates
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE1_ARB)
  glDisable_(#GL_TEXTURE_2D)
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE0_ARB)
  If emboss=0
    glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_MODULATE)
    glBindTexture_(#GL_TEXTURE_2D,texture(filter))
    glBlendFunc_(#GL_DST_COLOR,#GL_SRC_COLOR)
    glEnable_(#GL_BLEND)
    glEnable_(#GL_LIGHTING)
    doCube()
  EndIf
  
  xrot+xspeed
  yrot+yspeed
  If xrot>360.0 : xrot-360.0 : EndIf
  If xrot<0.0 : xrot+360.0 : EndIf
  If yrot>360.0 : yrot-360.0 : EndIf
  If yrot<0.0 : yrot+360.0 : EndIf
  
  ;LAST PASS: Do The Logos!
  doLogo()
  
  ProcedureReturn #True ;Keep Going
  
EndProcedure

Procedure.b doMeshNoBumps()
  
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear The Screen And The Depth Buffer
  glLoadIdentity_() ;Reset The View
  glTranslatef_(0.0,0.0,z)
  
  glRotatef_(xrot,1.0,0.0,0.0)
  glRotatef_(yrot,0.0,1.0,0.0)
  If useMultitexture
    CallFunctionFast(glActiveTextureARB,#GL_TEXTURE1_ARB)
    glDisable_(#GL_TEXTURE_2D)
    CallFunctionFast(glActiveTextureARB,#GL_TEXTURE0_ARB)
  EndIf
  glDisable_(#GL_BLEND)
  glBindTexture_(#GL_TEXTURE_2D,texture(filter))
  glBlendFunc_(#GL_DST_COLOR,#GL_SRC_COLOR)
  glEnable_(#GL_LIGHTING)
  doCube()
  
  xrot+xspeed
  yrot+yspeed
  If xrot>360.0 : xrot-360.0 : EndIf
  If xrot<0.0 : xrot+360.0 : EndIf
  If yrot>360.0 : yrot-360.0 : EndIf
  If yrot<0.0 : yrot+360.0 : EndIf
  
  ;LAST PASS: Do The Logos!
  doLogo()
  
  ProcedureReturn #True ;Keep Going
  
EndProcedure

Procedure.b DrawGLScene() ;Here's Where We Do All The Drawing
  
  If bumps
    If useMultitexture And maxTexelUnits>1
      ProcedureReturn doMesh2TexelUnits()
    Else
      ProcedureReturn doMesh1TexelUnits()
    EndIf
  Else
    ProcedureReturn doMeshNoBumps()
  EndIf
  
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
  
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
  
  ;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 GL_ARB_multitexture & Bump Mapping Tutorial",640,480,16,fullscreen)=0 ;Create The Window
    ProcedureReturn 0 ;Quit If Window Was Not Created
  EndIf
  
  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
      
      ;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
        
        Delay(10)
        SwapBuffers_(hDC) ;Swap Buffers (Double Buffering)
        
        If keys(#VK_E) ;Is E Key Being Pressed?
          keys(#VK_E)=#False ;If So Make Key FALSE
          emboss=~emboss & 1 ;toggle emboss
        EndIf
        
        If keys(#VK_M) ;Is M Key Being Pressed?
          keys(#VK_M)=#False ;If So Make Key FALSE
          useMultitexture=Bool(useMultitexture=0 And multitextureSupported) ;toggle useMultitexture
        EndIf
        
        If keys(#VK_B) ;Is B Key Being Pressed?
          keys(#VK_B)=#False ;If So Make Key FALSE
          bumps=~bumps & 1 ;toggle bumps
        EndIf
        
        If keys(#VK_F) ;Is F Key Being Pressed?
          keys(#VK_F)=#False ;If So Make Key FALSE
          filter+1 ;increase filter
          filter=filter % 3 ;set filter range 0..2
        EndIf
        
        If keys(#VK_PRIOR) ;Is Page Up Being Pressed?
          z-0.02 ;If So, Move Into The Screen
        EndIf
        If keys(#VK_NEXT) ;Is Page Down Being Pressed?
          z+0.02 ;If So, Move Towards The Viewer
        EndIf
        
        If keys(#VK_UP) And xspeed>-0.5 ;Is Up Arrow Being Pressed?
          xspeed-0.01 ;If So, Decrease xspeed
        EndIf
        If keys(#VK_DOWN) And xspeed<0.5 ;Is Down Arrow Being Pressed?
          xspeed+0.01 ;If So, Increase xspeed
        EndIf
        If keys(#VK_RIGHT) And yspeed<0.5 ;Is Right Arrow Being Pressed?
          yspeed+0.01 ;If So, Increase yspeed
        EndIf
        If keys(#VK_LEFT) And yspeed>-0.5 ;Is Left Arrow Being Pressed?
          yspeed-0.01 ;If So, Decrease yspeed
        EndIf
        
      EndIf
      
      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 GL_ARB_multitexture & Bump Mapping Tutorial",640,480,16,fullscreen)=0
          ProcedureReturn 0 ;Quit If Window Was Not Created
        EndIf
      EndIf
      
    EndIf
    
  Wend
  
  ;Shutdown
  KillGLWindow() ;Kill The Window
  End ;Exit The Program
  
EndProcedure

WinMain() ;run the main program
mpz
Enthusiast
Enthusiast
Posts: 497
Joined: Sat Oct 11, 2008 9:07 pm
Location: Germany, Berlin > member German forum

Re: NeHe's Multitexture & Bump Mapping Tutorial (Lesson 22)

Post by mpz »

Hello,

next code actualised for x86/x64, PB 5.73.

Sorry, but you "must" get the original texture from the internet, i have not purebasic textures for that

P.S:I think the code can work with linux and mac Osx too, but anybody must translate the windows funktion:
glMultiTexCoord1fARB=wglGetProcAddress_("glMultiTexCoord1fARB")
glMultiTexCoord2fARB=wglGetProcAddress_("glMultiTexCoord2fARB")
glMultiTexCoord3fARB=wglGetProcAddress_("glMultiTexCoord3fARB")
glMultiTexCoord4fARB=wglGetProcAddress_("glMultiTexCoord4fARB")
glActiveTextureARB=wglGetProcAddress_("glActiveTextureARB")
glClientActiveTextureARB=wglGetProcAddress_("glClientActiveTextureARB")

to linux/OSX

Greetings Michael

C Code and texture
iamyaker.googlepages.com/22_MultiTexture.rar

Code: Select all

;NeHe's GL_ARB_multitexture & Bump Mapping Tutorial (Lesson 22)
;http://nehe.gamedev.net
;https://nehe.gamedev.net/tutorial/bump-mapping,_multi-texturing_&_extensions/16009/
;Credits: Nico Gruener, Dreglor, traumatic, hagibaba
;Author: MPz
;Date: 29 Oct 2021
;Note: up-to-date with PB v5.73 (Windows)
;Note: requires bitmaps in paths "Data/Base.bmp", "Data/Bump.bmp",
;"Data/OpenGL.bmp", "Data/OpenGL_Alpha.bmp", "Data/Multi_On.bmp",
;"Data/Multi_On_Alpha.bmp"


;glext.h constants
#GL_MAX_TEXTURE_UNITS_ARB=$84E2
#GL_COMBINE_EXT=$8570
#GL_COMBINE_RGB_EXT=$8571
#GL_TEXTURE0_ARB=$84C0
#GL_TEXTURE1_ARB=$84C1

;Maximum Emboss-Translate. Increase To Get Higher Immersion
;At A Cost Of Lower Quality (More Artifacts Will Occur!)
#MAX_EMBOSS=0.008

;Here Comes The ARB-Multitexture Support.
;There Are (Optimally) 6 New Commands To The OpenGL Set:
;glMultiTexCoordifARB i=1..4 : Sets Texture-Coordinates For Texel-Pipeline #i
;glActiveTextureARB : Sets Active Texel-Pipeline
;glClientActiveTextureARB : Sets Active Texel-Pipeline For The Pointer-Array-Commands
;There Are Even More For The Various Formats Of glMultiTexCoordi{f,fv,d,i} But We Don't Need Them.

#__ARB_ENABLE=#True ;Used To Disable ARB Extensions Entirely
#EXT_INFO=#False ;Do You Want To See Your Extensions At Start-Up?

Global multitextureSupported.b=#False ;Flag Indicating Whether Multitexturing Is Supported
Global useMultitexture.b;=#True ;Use It If It Is Supported?
Global maxTexelUnits.l=1 ;Number Of Texel-Pipelines. This Is At Least 1.

Global glMultiTexCoord1fARB.i
Global glMultiTexCoord2fARB.i
Global glMultiTexCoord3fARB.i
Global glMultiTexCoord4fARB.i
Global glActiveTextureARB.i
Global glClientActiveTextureARB.i

Global emboss.b=#False ;Emboss Only, No Basetexture?
Global bumps.b=#True ;Do Bumpmapping?

Global xrot.f ;X Rotation
Global yrot.f ;Y Rotation
Global xspeed.f ;X Rotation Speed
Global yspeed.f ;Y Rotation Speed
Global z.f=-5.0 ;Depth Into The Screen

Global filter.l=1 ;Which Filter To Use
Global Dim texture.l(3) ;Storage For 3 Textures
Global Dim bump.l(3) ;Our Bumpmappings
Global Dim invbump.l(3) ;Inverted Bumpmaps
Global glLogo.l ;Handle For OpenGL-Logo
Global multiLogo.l ;Handle For Multitexture-Enabled-Logo

Global Dim LightAmbient.f(3) ;Ambient Light is 20% white
LightAmbient(0)=0.2 ;red
LightAmbient(1)=0.2 ;green
LightAmbient(2)=0.2 ;blue

Global Dim LightDiffuse.f(3) ;Diffuse Light is white
LightDiffuse(0)=1.0 ;red
LightDiffuse(1)=1.0 ;green
LightDiffuse(2)=1.0 ;blue

Global Dim LightPosition.f(3) ;Position is somewhat in front of screen
LightPosition(0)=0.0 ;x
LightPosition(1)=0.0 ;y
LightPosition(2)=2.0 ;z

Global Dim Gray.f(4)
Gray(0)=0.5 ;red
Gray(1)=0.5 ;green
Gray(2)=0.5 ;blue
Gray(3)=1.0 ;alpha

;Data Contains The Faces For The Cube In Format 2xTexCoord, 3xVertex;
;Note That The Tesselation Of The Cube Is Only Absolute Minimum.
Global Dim dat.f(120)
;Front Face
dat(  0)=0.0: dat(  1)=0.0: dat(  2)=-1.0: dat(  3)=-1.0: dat(  4)= 1.0
dat(  5)=1.0: dat(  6)=0.0: dat(  7)= 1.0: dat(  8)=-1.0: dat(  9)= 1.0
dat( 10)=1.0: dat( 11)=1.0: dat( 12)= 1.0: dat( 13)= 1.0: dat( 14)= 1.0
dat( 15)=0.0: dat( 16)=1.0: dat( 17)=-1.0: dat( 18)= 1.0: dat( 19)= 1.0
;Back Face
dat( 20)=1.0: dat( 21)=0.0: dat( 22)=-1.0: dat( 23)=-1.0: dat( 24)=-1.0
dat( 25)=1.0: dat( 26)=1.0: dat( 27)=-1.0: dat( 28)= 1.0: dat( 29)=-1.0
dat( 30)=0.0: dat( 31)=1.0: dat( 32)= 1.0: dat( 33)= 1.0: dat( 34)=-1.0
dat( 35)=0.0: dat( 36)=0.0: dat( 37)= 1.0: dat( 38)=-1.0: dat( 39)=-1.0
;Top Face
dat( 40)=0.0: dat( 41)=1.0: dat( 42)=-1.0: dat( 43)= 1.0: dat( 44)=-1.0
dat( 45)=0.0: dat( 46)=0.0: dat( 47)=-1.0: dat( 48)= 1.0: dat( 49)= 1.0
dat( 50)=1.0: dat( 51)=0.0: dat( 52)= 1.0: dat( 53)= 1.0: dat( 54)= 1.0
dat( 55)=1.0: dat( 56)=1.0: dat( 57)= 1.0: dat( 58)= 1.0: dat( 59)=-1.0
;Bottom Face
dat( 60)=1.0: dat( 61)=1.0: dat( 62)=-1.0: dat( 63)=-1.0: dat( 64)=-1.0
dat( 65)=0.0: dat( 66)=1.0: dat( 67)= 1.0: dat( 68)=-1.0: dat( 69)=-1.0
dat( 70)=0.0: dat( 71)=0.0: dat( 72)= 1.0: dat( 73)=-1.0: dat( 74)= 1.0
dat( 75)=1.0: dat( 76)=0.0: dat( 77)=-1.0: dat( 78)=-1.0: dat( 79)= 1.0
;Right Face
dat( 80)=1.0: dat( 81)=0.0: dat( 82)= 1.0: dat( 83)=-1.0: dat( 84)=-1.0
dat( 85)=1.0: dat( 86)=1.0: dat( 87)= 1.0: dat( 88)= 1.0: dat( 89)=-1.0
dat( 90)=0.0: dat( 91)=1.0: dat( 92)= 1.0: dat( 93)= 1.0: dat( 94)= 1.0
dat( 95)=0.0: dat( 96)=0.0: dat( 97)= 1.0: dat( 98)=-1.0: dat( 99)= 1.0
;Left Face
dat(100)=0.0: dat(101)=0.0: dat(102)=-1.0: dat(103)=-1.0: dat(104)=-1.0
dat(105)=1.0: dat(106)=0.0: dat(107)=-1.0: dat(108)=-1.0: dat(109)= 1.0
dat(110)=1.0: dat(111)=1.0: dat(112)=-1.0: dat(113)= 1.0: dat(114)= 1.0
dat(115)=0.0: dat(116)=1.0: dat(117)=-1.0: dat(118)= 1.0: dat(119)=-1.0

Procedure.b initMultitexture()
  
  Protected extensions.s
  extensions=PeekS(glGetString_(#GL_EXTENSIONS),-1,#PB_Ascii  ) ;Fetch Extension String
  
  If #EXT_INFO=#True
    MessageRequester(extensions,"supported GL extensions",#PB_MessageRequester_Ok )
  EndIf
  
  ;Is Multitexturing Supported? And Override-Flag And Is texture_env_combining Supported?
  If FindString(extensions,"GL_ARB_multitexture",1) And #__ARB_ENABLE And FindString(extensions,"GL_EXT_texture_env_combine",1)
    glGetIntegerv_(#GL_MAX_TEXTURE_UNITS_ARB,@maxTexelUnits)
    glMultiTexCoord1fARB=wglGetProcAddress_("glMultiTexCoord1fARB")
    glMultiTexCoord2fARB=wglGetProcAddress_("glMultiTexCoord2fARB")
    glMultiTexCoord3fARB=wglGetProcAddress_("glMultiTexCoord3fARB")
    glMultiTexCoord4fARB=wglGetProcAddress_("glMultiTexCoord4fARB")
    glActiveTextureARB=wglGetProcAddress_("glActiveTextureARB")
    glClientActiveTextureARB=wglGetProcAddress_("glClientActiveTextureARB")
    If #EXT_INFO
      MessageRequester("The GL_ARB_multitexture extension will be used.","feature supported!",#PB_MessageRequester_Ok )
    EndIf
    ProcedureReturn #True
  EndIf
  
  useMultitexture=#False ;We Can't Use It If It Isn't Supported!
  ProcedureReturn #False
  
EndProcedure

Procedure initLights()
  
  ;Load Light-Parameters Into GL_LIGHT1
  glLightfv_(#GL_LIGHT1,#GL_AMBIENT,LightAmbient())
  glLightfv_(#GL_LIGHT1,#GL_DIFFUSE,LightDiffuse())
  glLightfv_(#GL_LIGHT1,#GL_POSITION,LightPosition())
  
  glEnable_(#GL_LIGHT1)
  
EndProcedure


Procedure LoadGLTextures()
  
  Protected status.b=#True ;Status Indicator
  Protected Dim alpha2.b(4)
  Protected i.l
  
  LoadImage(0,"Data/Base.bmp") ; Load texture with name
  *pointer1 = EncodeImage(0, #PB_ImagePlugin_BMP,0,24 );  
  FreeImage(0)
  
  If *pointer1
    glGenTextures_(3,@texture(0)) ;Create Three Textures
    
    ;Create Nearest Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,texture(0))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_NEAREST)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_NEAREST)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_BGR_EXT,#GL_UNSIGNED_BYTE, *pointer1+54)
    
    ;Create Linear Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,texture(1))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_BGR_EXT,#GL_UNSIGNED_BYTE, *pointer1+54)
    
    ;Create MipMapped Texture
    glBindTexture_(#GL_TEXTURE_2D,texture(2))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps_(#GL_TEXTURE_2D,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),#GL_BGR_EXT,#GL_UNSIGNED_BYTE, *pointer1+54)
    FreeMemory(*pointer1)
  
  Else
    status=#False
  EndIf
  
  LoadImage(0,"Data/Bump.bmp") ; Load texture with name
  *pointer1 = EncodeImage(0, #PB_ImagePlugin_BMP,0,24 );  
  FreeImage(0)
  
  If *pointer1
    glPixelTransferf_(#GL_RED_SCALE,0.5) ;Scale RGB By 50%, So That We Have Only   
    glPixelTransferf_(#GL_GREEN_SCALE,0.5) ;Half Intenstity
    glPixelTransferf_(#GL_BLUE_SCALE,0.5)
    
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_WRAP_S,#GL_CLAMP) ;No Wrapping, Please!
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_WRAP_T,#GL_CLAMP)
    glTexParameterfv_(#GL_TEXTURE_2D,#GL_TEXTURE_BORDER_COLOR,Gray())
    
    glGenTextures_(3,@bump(0)) ;Create Three Textures
    
    ;Create Nearest Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,bump(0))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_NEAREST)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_NEAREST)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_RGB,#GL_UNSIGNED_BYTE,*pointer1+54)
    
    ;Create Linear Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,bump(1))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_RGB,#GL_UNSIGNED_BYTE,*pointer1+54)
    
    ;Create MipMapped Texture
    glBindTexture_(#GL_TEXTURE_2D,bump(2))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps_(#GL_TEXTURE_2D,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),#GL_RGB,#GL_UNSIGNED_BYTE,*pointer1+54)
    
    For i=0 To (3*PeekL(*pointer1+18)*PeekL(*pointer1+22))-1 ;Invert The Bumpmap
      PokeB(*pointer1+54+i,255-PeekB(*pointer1+54+i))
    Next
    
    glGenTextures_(3,@invbump(0)) ;Create Three Textures
    
    ;Create Nearest Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,invbump(0))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_NEAREST)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_NEAREST)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_RGB,#GL_UNSIGNED_BYTE,*pointer1+54)
    
    ;Create Linear Filtered Texture
    glBindTexture_(#GL_TEXTURE_2D,invbump(1))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_RGB,#GL_UNSIGNED_BYTE,*pointer1+54)
    
    ;Create MipMapped Texture
    glBindTexture_(#GL_TEXTURE_2D,invbump(2))
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR_MIPMAP_NEAREST)
    gluBuild2DMipmaps_(#GL_TEXTURE_2D,#GL_RGB8,PeekL(*pointer1+18), PeekL(*pointer1+22),#GL_RGB,#GL_UNSIGNED_BYTE,*pointer1+54)
    
    glPixelTransferf_(#GL_RED_SCALE,1.0) ;Scale RGB Back To 100% Again 
    glPixelTransferf_(#GL_GREEN_SCALE,1.0)
    glPixelTransferf_(#GL_BLUE_SCALE,1.0)
    FreeMemory(*pointer1)
  Else
    status=#False
  EndIf
  
  LoadImage(0,"Data/OpenGL_Alpha.bmp") ; Load texture with name
  *pointer1 = EncodeImage(0, #PB_ImagePlugin_BMP,0,24 );  
  FreeImage(0)

  ;Load The Logo-Bitmaps
  If *pointer1 
    
    ReDim alpha2.b(4*PeekL(*pointer1+18)* PeekL(*pointer1+22)) ;Create Memory For RGBA8-Texture
    
    For i=0 To (PeekL(*pointer1+18)* PeekL(*pointer1+22))-1
      Alpha2((4*i)+3)=PeekB(*pointer1+54+(i*3)) ;Pick Only Red Value As Alpha!
    Next
    FreeMemory(*pointer1)
    
    LoadImage(0,"Data/OpenGL.bmp") ; Load texture with name
    *pointer1 = EncodeImage(0, #PB_ImagePlugin_BMP,0,24 );  
    FreeImage(0)
    
    For i=0 To (PeekL(*pointer1+18)* PeekL(*pointer1+22))-1
      Alpha2(4*i)=PeekB(*pointer1+54+(i*3)) ;Red
      Alpha2((4*i)+1)=PeekB(*pointer1+54+(i*3)+1) ;Green
      Alpha2((4*i)+2)=PeekB(*pointer1+54+(i*3)+2) ;Blue
    Next
    
    glGenTextures_(1,@glLogo) ;Create One Textures
    
    ;Create Linear Filtered RGBA8-Texture
    glBindTexture_(#GL_TEXTURE_2D,glLogo)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGBA8,PeekL(*pointer1+18), PeekL(*pointer1+22),0,#GL_RGBA,#GL_UNSIGNED_BYTE,Alpha2())
    Dim alpha2.b(0)
    FreeMemory(*pointer1)
  
  Else
    status=#False
  EndIf
  
  LoadImage(0,"Data/Multi_On_Alpha.bmp") ; Load texture with name
     *pointer1 = EncodeImage(0, #PB_ImagePlugin_BMP,0,24 );  
  FreeImage(0)

  If *pointer1
    ReDim alpha2.b(4*PeekL(*pointer1+18)*PeekL(*pointer1+22)) ;Create Memory For RGBA8-Texture
    
    For i=0 To (PeekL(*pointer1+18)*PeekL(*pointer1+22))-1
      Alpha2((4*i)+3)=PeekB(*pointer1+54+(i*3)) ;Pick Only Red Value As Alpha!
    Next
    FreeMemory(*pointer1)
    
    LoadImage(0,"Data/Multi_On.bmp") ; Load texture with name
     *pointer1 = EncodeImage(0, #PB_ImagePlugin_BMP,0,24 );  
    FreeImage(0)

    If *pointer1=0
      status=#False
    EndIf
    
    For i=0 To (PeekL(*pointer1+18)*PeekL(*pointer1+22))-1
      Alpha2(4*i)=PeekB(*pointer1+54+(i*3)+2) ;Red
      Alpha2((4*i)+1)=PeekB(*pointer1+54+(i*3)+1) ;Green
      Alpha2((4*i)+2)=PeekB(*pointer1+54+(i*3)) ;Blue
    Next
    
    glGenTextures_(1,@multiLogo) ;Create One Textures
    
    ;Create Linear Filtered RGBA8-Texture
    glBindTexture_(#GL_TEXTURE_2D,multiLogo)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MAG_FILTER,#GL_LINEAR)
    glTexParameteri_(#GL_TEXTURE_2D,#GL_TEXTURE_MIN_FILTER,#GL_LINEAR)
    glTexImage2D_(#GL_TEXTURE_2D,0,#GL_RGBA8,PeekL(*pointer1+18),PeekL(*pointer1+22),0,#GL_RGBA, #GL_UNSIGNED_BYTE,Alpha2())
    Dim alpha2.b(0)
    
  Else
    status=#False
  EndIf
  
  ProcedureReturn status ;Return The Status
  
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
 
 ResizeGadget(0, 0, 0, width, height)
 
 glViewport_(0,0,width,height) ;Reset The Current Viewport
 
 glMatrixMode_(#GL_PROJECTION) ;Select The Projection Matrix
 glLoadIdentity_() ;Reset The Projection Matrix
 
 gluPerspective_(45.0,Abs(width/height),0.1,100.0) ;Calculate The Aspect Ratio Of The Window
 
 glMatrixMode_(#GL_MODELVIEW) ;Select The Modelview Matrix
 glLoadIdentity_() ;Reset The Modelview Matrix
 
EndProcedure

Procedure doCube()
  
  Protected i.l
  
  glBegin_(#GL_QUADS)
  ;Front Face
  glNormal3f_( 0.0, 0.0, 1.0)
  For i=0 To 4-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Back Face
  glNormal3f_( 0.0, 0.0,-1.0)
  For i=4 To 8-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Top Face
  glNormal3f_( 0.0, 1.0, 0.0)
  For i=8 To 12-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Bottom Face
  glNormal3f_( 0.0,-1.0, 0.0)
  For i=12 To 16-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Right face
  glNormal3f_( 1.0, 0.0, 0.0)
  For i=16 To 20-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  ;Left Face
  glNormal3f_(-1.0, 0.0, 0.0)
  For i=20 To 24-1
    glTexCoord2f_(dat(5*i),dat(5*i+1))
    glVertex3f_(dat(5*i+2),dat(5*i+3),dat(5*i+4))
  Next
  glEnd_()
  
EndProcedure

Procedure InitGL() ;All Setup For OpenGL Goes Here

  multitextureSupported=initMultitexture()
  
  If LoadGLTextures()=0 ;Jump To Texture Loading Routine
    ProcedureReturn #False ;If Texture Didn't Load Return FALSE
  EndIf

  glEnable_(#GL_TEXTURE_2D) ;Enable Texture Mapping
  glShadeModel_(#GL_SMOOTH) ;Enable Smooth Shading
  glClearColor_(0.0,0.0,0.0,0.5) ;Black Background
  glClearDepth_(1.0) ;Depth Buffer Setup
  glEnable_(#GL_DEPTH_TEST) ;Enables Depth Testing
  glDepthFunc_(#GL_LEQUAL) ;The Type Of Depth Testing To Do
  glHint_(#GL_PERSPECTIVE_CORRECTION_HINT,#GL_NICEST) ;Really Nice Perspective Calculations
  
  initLights() ;Initialize OpenGL Light
  
  ProcedureReturn #True ;Initialization Went OK

EndProcedure

;Calculates v=vM, M Is 4x4 In Column-Major, v Is 4dim. Row (i.e. "Transposed")
Procedure VMatMult(Array M.f(1), Array v.f(1))
  
  Protected Dim res.f(3)
  res(0)=M( 0)*v(0)+M( 1)*v(1)+M( 2)*v(2)+M( 3)*v(3)
  res(1)=M( 4)*v(0)+M( 5)*v(1)+M( 6)*v(2)+M( 7)*v(3)
  res(2)=M( 8)*v(0)+M( 9)*v(1)+M(10)*v(2)+M(11)*v(3)
  v(0)=res(0)
  v(1)=res(1)
  v(2)=res(2)
  v(3)=M(15) ;Homogenous Coordinate
  
EndProcedure

Procedure SetUpBumps(Array n.f(1), Array c.f(1), Array l.f(1), Array s.f(1), Array t.f(1))
  
  Protected Dim v.f(3) ;Vertex From Current Position To Light
  Protected lenQ.f ;Used To Normalize 
  
  ;Calculate v From Current Vector c To Lightposition And Normalize v
  v(0)=l(0)-c(0)
  v(1)=l(1)-c(1)
  v(2)=l(2)-c(2)
  lenQ=Sqr(v(0)*v(0)+v(1)*v(1)+v(2)*v(2))
  v(0)/lenQ : v(1)/lenQ : v(2)/lenQ
  ;Project v Such That We Get Two Values Along Each Texture-Coordinate Axis.
  c(0)=(s(0)*v(0)+s(1)*v(1)+s(2)*v(2))*#MAX_EMBOSS
  c(1)=(t(0)*v(0)+t(1)*v(1)+t(2)*v(2))*#MAX_EMBOSS
  
EndProcedure

Procedure doLogo() ;MUST CALL THIS LAST!!!, Billboards The Two Logos.
  
  glDepthFunc_(#GL_ALWAYS)
  glBlendFunc_(#GL_SRC_ALPHA,#GL_ONE_MINUS_SRC_ALPHA)
  glEnable_(#GL_BLEND)
  glDisable_(#GL_LIGHTING)
  glLoadIdentity_()
  glBindTexture_(#GL_TEXTURE_2D,glLogo)
  glBegin_(#GL_QUADS)
  glTexCoord2f_(0.0,0.0) : glVertex3f_(0.23,-0.40,-1.0)
  glTexCoord2f_(1.0,0.0) : glVertex3f_(0.53,-0.40,-1.0)
  glTexCoord2f_(1.0,1.0) : glVertex3f_(0.53,-0.25,-1.0)
  glTexCoord2f_(0.0,1.0) : glVertex3f_(0.23,-0.25,-1.0)
  glEnd_()
  If useMultitexture
    glBindTexture_(#GL_TEXTURE_2D,multiLogo)
    glBegin_(#GL_QUADS)
    glTexCoord2f_(0.0,0.0) : glVertex3f_(-0.53,-0.4,-1.0)
    glTexCoord2f_(1.0,0.0) : glVertex3f_(-0.33,-0.4,-1.0)
    glTexCoord2f_(1.0,1.0) : glVertex3f_(-0.33,-0.3,-1.0)
    glTexCoord2f_(0.0,1.0) : glVertex3f_(-0.53,-0.3,-1.0)
    glEnd_()
  EndIf
  glDepthFunc_(#GL_LEQUAL)
  
EndProcedure

Procedure.b doMesh1TexelUnits()
  
  Protected Dim c.f(4) ;Holds Current Vertex
  c(0)=0.0 : c(1)=0.0 : c(2)=0.0 : c(3)=1.0
  Protected Dim n.f(4) ;Normalized Normal Of Current Surface 
  n(0)=0.0 : n(1)=0.0 : n(2)=0.0 : n(3)=1.0
  Protected Dim s.f(4) ;s-Texture Coordinate Direction, Normalized
  s(0)=0.0 : s(1)=0.0 : s(2)=0.0 : s(3)=1.0
  Protected Dim t.f(4) ;t-Texture Coordinate Direction, Normalized
  t(0)=0.0 : t(1)=0.0 : t(2)=0.0 : t(3)=1.0
  Protected Dim l.f(4) ;Holds Our Lightposition To Be Transformed Into Object Space
  Protected Dim Minv.f(16) ;Holds The Inverted Modelview Matrix To Do So.
  Protected i.l
  
  SetGadgetAttribute(0, #PB_OpenGL_SetContext, #True)
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear The Screen And The Depth Buffer
  
  ;Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
  ;Simply Build It By Doing All Transformations Negated And In Reverse Order.
  glLoadIdentity_()
  glRotatef_(-yrot,0.0,1.0,0.0)
  glRotatef_(-xrot,1.0,0.0,0.0)
  glTranslatef_(0.0,0.0,-z)
  glGetFloatv_(#GL_MODELVIEW_MATRIX,Minv())
  glLoadIdentity_()
  glTranslatef_(0.0,0.0,z)
  glRotatef_(xrot,1.0,0.0,0.0)
  glRotatef_(yrot,0.0,1.0,0.0)
  
  ;Transform The Lightposition Into Object Coordinates:
  l(0)=LightPosition(0)
  l(1)=LightPosition(1)
  l(2)=LightPosition(2)
  l(3)=1.0 ;Homogenous Coordinate
  VMatMult(Minv(),l())
  
  ;PASS#1: Use Texture "Bump"
  ; No Blend
  ; No Lighting
  ; No Offset Texture-Coordinates
  glBindTexture_(#GL_TEXTURE_2D,bump(filter))
  glDisable_(#GL_BLEND)
  glDisable_(#GL_LIGHTING)
  doCube()
  
  ;PASS#2: Use Texture "Invbump"
  ; Blend GL_ONE To GL_ONE
  ; No Lighting
  ; Offset Texture Coordinates
  glBindTexture_(#GL_TEXTURE_2D,invbump(filter))
  glBlendFunc_(#GL_ONE,#GL_ONE)
  glDepthFunc_(#GL_LEQUAL)
  glEnable_(#GL_BLEND)
  
  glBegin_(#GL_QUADS)
  ;Front Face
  n(0)=0.0 : n(1)=0.0 : n(2)=1.0
  s(0)=1.0 : s(1)=0.0 : s(2)=0.0
  t(0)=0.0 : t(1)=1.0 : t(2)=0.0
  For i=0 To 4-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Back Face
  n(0)= 0.0 : n(1)=0.0 : n(2)=-1.0
  s(0)=-1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)=1.0 : t(2)= 0.0
  For i=4 To 8-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Top Face
  n(0)=0.0 : n(1)=1.0 : n(2)= 0.0
  s(0)=1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)=0.0 : t(1)=0.0 : t(2)=-1.0
  For i=8 To 12-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Bottom Face
  n(0)= 0.0 : n(1)=-1.0 : n(2)= 0.0
  s(0)=-1.0 : s(1)= 0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)= 0.0 : t(2)=-1.0
  For i=12 To 16-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Right Face
  n(0)=1.0 : n(1)=0.0 : n(2)= 0.0
  s(0)=0.0 : s(1)=0.0 : s(2)=-1.0
  t(0)=0.0 : t(1)=1.0 : t(2)= 0.0
  For i=16 To 20-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Left Face
  n(0)=-1.0 : n(1)=0.0 : n(2)=0.0
  s(0)= 0.0 : s(1)=0.0 : s(2)=1.0
  t(0)= 0.0 : t(1)=1.0 : t(2)=0.0
  For i=20 To 24-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    glTexCoord2f_(dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  glEnd_()
  
  ;PASS#3: Use Texture "Base"
  ; Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
  ; Lighting Enabled
  ; No Offset Texture-Coordinates
  If emboss=0
    glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_MODULATE)
    glBindTexture_(#GL_TEXTURE_2D,texture(filter))
    glBlendFunc_(#GL_DST_COLOR,#GL_SRC_COLOR)
    glEnable_(#GL_LIGHTING)
    doCube()
  EndIf
  
  xrot+xspeed
  yrot+yspeed
  If xrot>360.0 : xrot-360.0 : EndIf
  If xrot<0.0 : xrot+360.0 : EndIf
  If yrot>360.0 : yrot-360.0 : EndIf
  If yrot<0.0 : yrot+360.0 : EndIf
  ;LAST PASS: Do The Logos!
  doLogo()
  
  SetGadgetAttribute(0, #PB_OpenGL_FlipBuffers, #True)
  
  ProcedureReturn #True ;Keep Going
  
EndProcedure

Procedure.b doMesh2TexelUnits()
  
  Protected Dim c.f(4) ;holds current vertex
  c(0)=0.0 : c(1)=0.0 : c(2)=0.0 : c(3)=1.0
  Protected Dim n.f(4) ;normalized normal of current surface
  n(0)=0.0 : n(1)=0.0 : n(2)=0.0 : n(3)=1.0
  Protected Dim s.f(4) ;s-texture coordinate direction, normalized
  s(0)=0.0 : s(1)=0.0 : s(2)=0.0 : s(3)=1.0
  Protected Dim t.f(4) ;t-texture coordinate direction, normalized
  t(0)=0.0 : t(1)=0.0 : t(2)=0.0 : t(3)=1.0
  Protected Dim l.f(4) ;holds our lightposition to be transformed into object space
  Protected Dim Minv.f(16) ;holds the inverted modelview matrix to do so.
  Protected i.l
  
  SetGadgetAttribute(0, #PB_OpenGL_SetContext, #True)
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear The Screen And The Depth Buffer
  
  ;Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
  ;Simply Build It By Doing All Transformations Negated And In Reverse Order.
  glLoadIdentity_()     
  glRotatef_(-yrot,0.0,1.0,0.0)
  glRotatef_(-xrot,1.0,0.0,0.0)
  glTranslatef_(0.0,0.0,-z)
  glGetFloatv_(#GL_MODELVIEW_MATRIX,Minv())
  glLoadIdentity_()
  glTranslatef_(0.0,0.0,z)
  glRotatef_(xrot,1.0,0.0,0.0)
  glRotatef_(yrot,0.0,1.0,0.0)
  
  ;Transform The Lightposition Into Object Coordinates:
  l(0)=LightPosition(0)
  l(1)=LightPosition(1)
  l(2)=LightPosition(2)
  l(3)=1.0 ;Homogenous Coordinate
  VMatMult(Minv(),l())
  
  ;PASS#1: Texel-Unit 0: Use Texture "Bump"
  ;    No Blend
  ;    No Lighting
  ;    No Offset Texture-Coordinates
  ;    Texture-Operation "Replace"
  ;Texel-Unit 1: Use Texture "Invbump"
  ;    No Lighting
  ;    Offset Texture Coordinates
  ;    Texture-Operation "Replace"
  
  ;TEXTURE-UNIT #0
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE0_ARB)
  glEnable_(#GL_TEXTURE_2D)
  glBindTexture_(#GL_TEXTURE_2D,bump(filter))
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_COMBINE_EXT)
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_COMBINE_RGB_EXT,#GL_REPLACE)
  ;TEXTURE-UNIT #1:
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE1_ARB)
  glEnable_(#GL_TEXTURE_2D)
  glBindTexture_(#GL_TEXTURE_2D,invbump(filter))
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_COMBINE_EXT)
  glTexEnvf_(#GL_TEXTURE_ENV,#GL_COMBINE_RGB_EXT,#GL_ADD)
  ;General Switches:
  glDisable_(#GL_BLEND)
  glDisable_(#GL_LIGHTING)
  
  glBegin_(#GL_QUADS)
  ;Front Face
  n(0)=0.0 : n(1)=0.0 : n(2)=1.0
  s(0)=1.0 : s(1)=0.0 : s(2)=0.0
  t(0)=0.0 : t(1)=1.0 : t(2)=0.0
  For i=0 To 4-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1))
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Back Face
  n(0)= 0.0 : n(1)=0.0 : n(2)=-1.0
  s(0)=-1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)=1.0 : t(2)= 0.0
  For i=4 To 8-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1))
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Top Face
  n(0)=0.0 : n(1)=1.0 : n(2)= 0.0
  s(0)=1.0 : s(1)=0.0 : s(2)= 0.0
  t(0)=0.0 : t(1)=0.0 : t(2)=-1.0
  For i=8 To 12-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Bottom Face
  n(0)= 0.0 : n(1)=-1.0 : n(2)= 0.0
  s(0)=-1.0 : s(1)= 0.0 : s(2)= 0.0
  t(0)= 0.0 : t(1)= 0.0 : t(2)=-1.0
  For i=12 To 16-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Right Face
  n(0)=1.0 : n(1)=0.0 : n(2)= 0.0
  s(0)=0.0 : s(1)=0.0 : s(2)=-1.0
  t(0)=0.0 : t(1)=1.0 : t(2)= 0.0
  For i=16 To 20-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  ;Left Face
  n(0)=-1.0 : n(1)=0.0 : n(2)=0.0
  s(0)= 0.0 : s(1)=0.0 : s(2)=1.0
  t(0)= 0.0 : t(1)=1.0 : t(2)=0.0
  For i=20 To 24-1
    c(0)=dat((5*i)+2)
    c(1)=dat((5*i)+3)
    c(2)=dat((5*i)+4)
    SetUpBumps(n(),c(),l(),s(),t())
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE0_ARB,dat(5*i)     , dat((5*i)+1)     )
    CallFunctionFast(glMultiTexCoord2fARB,#GL_TEXTURE1_ARB,dat(5*i)+c(0), dat((5*i)+1)+c(1))
    glVertex3f_(dat((5*i)+2), dat((5*i)+3), dat((5*i)+4))
  Next
  glEnd_()
  
  ;PASS#2 Use Texture "Base"
  ; Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
  ; Lighting Enabled
  ; No Offset Texture-Coordinates
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE1_ARB)
  glDisable_(#GL_TEXTURE_2D)
  CallFunctionFast(glActiveTextureARB,#GL_TEXTURE0_ARB)
  If emboss=0
    glTexEnvf_(#GL_TEXTURE_ENV,#GL_TEXTURE_ENV_MODE,#GL_MODULATE)
    glBindTexture_(#GL_TEXTURE_2D,texture(filter))
    glBlendFunc_(#GL_DST_COLOR,#GL_SRC_COLOR)
    glEnable_(#GL_BLEND)
    glEnable_(#GL_LIGHTING)
    doCube()
  EndIf
  
  xrot+xspeed
  yrot+yspeed
  If xrot>360.0 : xrot-360.0 : EndIf
  If xrot<0.0 : xrot+360.0 : EndIf
  If yrot>360.0 : yrot-360.0 : EndIf
  If yrot<0.0 : yrot+360.0 : EndIf
  
  ;LAST PASS: Do The Logos!
  doLogo()
  
  SetGadgetAttribute(0, #PB_OpenGL_FlipBuffers, #True)
  ProcedureReturn #True ;Keep Going
  
EndProcedure

Procedure.b doMeshNoBumps()
  
  SetGadgetAttribute(0, #PB_OpenGL_SetContext, #True)
  glClear_(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT) ;Clear The Screen And The Depth Buffer
  glLoadIdentity_() ;Reset The View
  glTranslatef_(0.0,0.0,z)
  
  glRotatef_(xrot,1.0,0.0,0.0)
  glRotatef_(yrot,0.0,1.0,0.0)
  If useMultitexture
    CallFunctionFast(glActiveTextureARB,#GL_TEXTURE1_ARB)
    glDisable_(#GL_TEXTURE_2D)
    CallFunctionFast(glActiveTextureARB,#GL_TEXTURE0_ARB)
  EndIf
  glDisable_(#GL_BLEND)
  glBindTexture_(#GL_TEXTURE_2D,texture(filter))
  glBlendFunc_(#GL_DST_COLOR,#GL_SRC_COLOR)
  glEnable_(#GL_LIGHTING)
  doCube()
  
  xrot+xspeed
  yrot+yspeed
  If xrot>360.0 : xrot-360.0 : EndIf
  If xrot<0.0 : xrot+360.0 : EndIf
  If yrot>360.0 : yrot-360.0 : EndIf
  If yrot<0.0 : yrot+360.0 : EndIf
  
  ;LAST PASS: Do The Logos!
  doLogo()
  
  SetGadgetAttribute(0, #PB_OpenGL_FlipBuffers, #True)
  ProcedureReturn #True ;Keep Going
  
EndProcedure


Procedure DrawScene(Gadget)
 
  If bumps
    If useMultitexture And maxTexelUnits>1
      ProcedureReturn doMesh2TexelUnits()
    Else
      ProcedureReturn doMesh1TexelUnits()
    EndIf
  Else
    ProcedureReturn doMeshNoBumps()
  EndIf
      
EndProcedure

Procedure CreateGLWindow(title.s,WindowWidth.l,WindowHeight.l,bits.l=16,fullscreenflag.b=0,Vsync.b=0)
  
  If InitKeyboard() = 0 Or InitSprite() = 0 Or InitMouse() = 0
    MessageRequester("Error", "Can't initialize Keyboards or Mouse", 0)
    End
  EndIf

  If fullscreenflag
    hWnd = OpenWindow(0, 0, 0, WindowWidth, WindowHeight, title, #PB_Window_BorderLess|#PB_Window_Maximize )
    OpenWindowedScreen(WindowID(0), 0, 0,WindowWidth(0),WindowHeight(0)) 
  Else  
    hWnd = OpenWindow(0, 1, 1, WindowWidth, WindowHeight, title,#PB_Window_MinimizeGadget |  #PB_Window_MaximizeGadget | #PB_Window_SizeGadget ) 
    OpenWindowedScreen(WindowID(0), 1, 1, WindowWidth,WindowHeight) 
  EndIf
  
  If bits = 24
    OpenGlFlags + #PB_OpenGL_24BitDepthBuffer
  EndIf
  
  If Vsync = 0
    OpenGlFlags + #PB_OpenGL_NoFlipSynchronization
  EndIf
  
  OpenGLGadget(0, 0, 0, WindowWidth(0),WindowHeight(0),OpenGlFlags)
  
  SetActiveGadget(0) 
  
  ReSizeGLScene(WindowWidth(0),WindowHeight(0))
  ;hDC = GetDC_(hWnd)
  
EndProcedure




CreateGLWindow("NeHe's GL_ARB_multitexture & Bump Mapping Tutorial (Lesson 22)",640,480,16,0,1)

InitGL() 


Repeat

  Repeat 
    Event = WindowEvent()
    Select Event
      Case #PB_Event_CloseWindow
        Quit = 1
      Case #PB_Event_SizeWindow  
        ReSizeGLScene(WindowWidth(0),WindowHeight(0)) ;LoWord=Width, HiWord=Height
    EndSelect
  
  Until Event = 0
  
  ExamineKeyboard()
        
  If KeyboardPushed(#PB_Key_Escape)    ; // push ESC key
    Quit = 1                               ; // This is the end
  EndIf
  
        If KeyboardPushed(#PB_Key_E) And Ep=0 ;Is E Key Being Pressed?
          Ep=#True
          emboss=~emboss & 1 ;toggle emboss
        ElseIf Not KeyboardPushed(#PB_Key_E) ;Has L Key Been Released?
          Ep=#False ;If So, lp Becomes FALSE
        EndIf
        
        If KeyboardPushed(#PB_Key_M) And Mp=0 ;Is M Key Being Pressed?
          Mp=#True
          useMultitexture=Bool(useMultitexture=0 And multitextureSupported) ;toggle useMultitexture
        ElseIf Not KeyboardPushed(#PB_Key_M) ;Has L Key Been Released?
          Mp=#False ;If So, lp Becomes FALSE
        EndIf
        
        If KeyboardPushed(#PB_Key_B) And Bp=0;Is B Key Being Pressed?
          Bp=#True
          bumps=~bumps & 1 ;toggle bumps
        ElseIf Not KeyboardPushed(#PB_Key_B)
          Bp=#False ;If So, lp Becomes FALSE
        EndIf
        
        If KeyboardPushed(#PB_Key_F) And Fp=0 ;Is F Key Being Pressed?
          Fp=#True
          filter+1 ;increase filter
          filter=filter % 3 ;set filter range 0..2
        ElseIf Not KeyboardPushed(#PB_Key_F) ;Has L Key Been Released?
          Fp=#False ;If So, lp Becomes FALSE
        EndIf
       
        If KeyboardPushed(#PB_Key_PageDown) ;Is Page Up Being Pressed?
          z-0.02 ;If So, Move Into The Screen
        EndIf
        If KeyboardPushed(#PB_Key_PageUp) ;Is Page Down Being Pressed?
          z+0.02 ;If So, Move Towards The Viewer
        EndIf
        
        If KeyboardPushed(#PB_Key_Up) And xspeed>-0.5 ;Is Up Arrow Being Pressed?
          xspeed-0.01 ;If So, Decrease xspeed
        EndIf
        If KeyboardPushed(#PB_Key_Down) And xspeed<0.5 ;Is Down Arrow Being Pressed?
          xspeed+0.01 ;If So, Increase xspeed
        EndIf
        If KeyboardPushed(#PB_Key_Right) And yspeed<0.5 ;Is Right Arrow Being Pressed?
          yspeed+0.01 ;If So, Increase yspeed
        EndIf
        If KeyboardPushed(#PB_Key_Left) And yspeed>-0.5 ;Is Left Arrow Being Pressed?
          yspeed-0.01 ;If So, Decrease yspeed
        EndIf    
  DrawScene(0)
  
Until Quit = 1
Working on - MP3D Library - PB 5.73 version ready for download
Post Reply