Well, I've fixed it. It's taken 3 days of head smashing and now something clicked. I wasn't keeping a track of my current rotation. And how should one keep a track of 3D rotations? Enter Quaternion's.
Simply put, whenever I need to rotate, I have to apply it to the my previous rotation, and Quaternion's do this very well. That and most of the Quaternion code was
I am sure that there are other (probably better) ways of doing this, and if you can, please tell all!
Code: Select all
Global WallTexture
Structure Matrix4
W1.f
X1.f
Y1.f
Z1.f
W2.f
X2.f
Y2.f
Z2.f
W3.f
X3.f
Y3.f
Z3.f
W4.f
X4.f
Y4.f
Z4.f
EndStructure
Structure QUATERNION
X.f
Y.f
Z.f
w.f
EndStructure
Procedure Quaternion_ToMatrix(*ReturnMatrix.Matrix4, *q.QUATERNION)
*ReturnMatrix\W1 = 1.0 - 2.0 * ( *q\Y * *q\Y + *q\Z * *q\Z );
*ReturnMatrix\X1 = 2.0 * (*q\X * *q\Y + *q\Z * *q\w);
*ReturnMatrix\Y1 = 2.0 * (*q\X * *q\Z - *q\Y * *q\w);
*ReturnMatrix\Z1 = 0.0;
; Second row
*ReturnMatrix\W2 = 2.0 * ( *q\X * *q\Y - *q\Z * *q\w );
*ReturnMatrix\X2 = 1.0 - 2.0 * ( *q\X * *q\X + *q\Z * *q\Z );
*ReturnMatrix\Y2 = 2.0 * (*q\Z * *q\Y + *q\X * *q\w );
*ReturnMatrix\Z2 = 0.0;
; Third row
*ReturnMatrix\W3 = 2.0 * ( *q\X * *q\Z + *q\Y * *q\w );
*ReturnMatrix\X3 = 2.0 * ( *q\Y * *q\Z - *q\X * *q\w );
*ReturnMatrix\Y3 = 1.0 - 2.0 * ( *q\X * *q\X + *q\Y * *q\Y );
*ReturnMatrix\Z3 = 0.0;
; Fourth row
*ReturnMatrix\W4 = 0;
*ReturnMatrix\X4 = 0;
*ReturnMatrix\Y4 = 0;
*ReturnMatrix\Z4 = 1.0;
EndProcedure
Procedure.f Quaternion_Length(*TempQuat.QUATERNION)
ProcedureReturn Sqr(Pow(*TempQuat\X,2) + Pow(*TempQuat\Y,2) + Pow(*TempQuat\Z,2) + Pow(*TempQuat\w,2))
EndProcedure
Procedure Quaternion_Multiply(*NewQuat.QUATERNION, *Quat1.QUATERNION, *Quat2.QUATERNION)
*NewQuat\w = *Quat1\w * *Quat2\w - *Quat1\X * *Quat2\X - *Quat1\Y * *Quat2\Y - *Quat1\Z * *Quat2\Z
*NewQuat\X = *Quat1\w * *Quat2\X + *Quat1\X * *Quat2\w + *Quat1\Y * *Quat2\Z - *Quat1\Z * *Quat2\Y
*NewQuat\Y = *Quat1\w * *Quat2\Y + *Quat1\Y * *Quat2\w + *Quat1\Z * *Quat2\X - *Quat1\X * *Quat2\Z
*NewQuat\Z = *Quat1\w * *Quat2\Z + *Quat1\Z * *Quat2\w + *Quat1\X * *Quat2\Y - *Quat1\Y * *Quat2\X
EndProcedure
Procedure Quaternion_Normalize(*TempQuat.QUATERNION)
TEMP_length.f = Quaternion_Length(*TempQuat)
*TempQuat\X = *TempQuat\X / TEMP_length
*TempQuat\Y = *TempQuat\Y / TEMP_length
*TempQuat\Z = *TempQuat\Z / TEMP_length
*TempQuat\w = *TempQuat\w / TEMP_length
EndProcedure
Procedure Quaternion_FromAngleAxis(*TempQuat.QUATERNION, X.f, Y.f, Z.f, angleDegrees.f)
If X = 0 And Y = 0 And Z = 0
*TempQuat\X = 0
*TempQuat\Y = 0
*TempQuat\Z = 0
*TempQuat\w = 1
ProcedureReturn 0
EndIf
TEMP_angle.f = angleDegrees * 0.01745329
TEMP_angle = TEMP_angle / 2
TEMP_scale.f = Sin(TEMP_angle)
*TempQuat\X = X * TEMP_scale
*TempQuat\Y = Y * TEMP_scale
*TempQuat\Z = Z * TEMP_scale
*TempQuat\w = Cos(TEMP_angle)
Quaternion_Normalize(*TempQuat)
EndProcedure
Structure POINTZ
x.f
y.f
z.f
EndStructure
Global NewList StarField.POINTZ()
For i = 1 To 2500
AddElement(StarField())
StarField()\x = Random(200) - 100
StarField()\y = Random(200) - 100
StarField()\z = Random(200) - 100
Next
Global Speed.f = 1
Global Pitch.f = 0
Global Roll.f = 0
Global Position.POINTZ
Position\x = 0
Position\y = 0
Position\z = 0
Global qRotation.QUATERNION
qRotation\w = 1
qRotation\X = 0
qRotation\Y = 0
qRotation\Z = 0
;{--- OpenGL Setup
CompilerIf Subsystem("OpenGL")
CompilerElse
MessageRequester("ERROR", "Set the subsystem To OpenGL")
End
CompilerEndIf
; pb workaround fix
Import ""
PB_Screen_Target
EndImport
#GL_TEXTURE_RECTANGLE_ARB = $84F5
;----------------------------
#GL_BLEND = $0BE2
#GL_COLOR_BUFFER_BIT = $00004000
#GL_DEPTH_BUFFER_BIT = $00000100
#GL_DEPTH_TEST = $0B71
#GL_LEQUAL = $0203
#GL_LINEAR = $2601
#GL_LINEAR_MIPMAP_NEAREST = $2701
#GL_LUMINANCE = $1909
#GL_UNSIGNED_INT = $1405
#GL_UNSIGNED_INT_8_8_8_8 = $8035
#GL_BGRA = $80E1
#GL_RGBA = $1908
#GL_RGBA8 = $8058
#GL_MODELVIEW = $1700
#GL_MODULATE = $2100
#GL_NICEST = $1102
#GL_PERSPECTIVE_CORRECTION_HINT = $0C50
#GL_PROJECTION = $1701
#GL_SMOOTH = $1D01
#GL_TEXTURE_2D = $0DE1
#GL_TEXTURE_ENV = $2300
#GL_TEXTURE_ENV_MODE = $2200
#GL_TEXTURE_MAG_FILTER = $2800
#GL_TEXTURE_MIN_FILTER = $2801
#GL_TEXTURE_WRAP_S = $2802
#GL_TEXTURE_WRAP_T = $2803
#GL_TRUE = 1
#GL_UNSIGNED_BYTE = $1401
#GL_SRC_ALPHA = $0302
#GL_ONE = 1
#GL_UNPACK_ALIGNMENT = $0CF5
; BeginMode
#GL_POINTS = $0000
#GL_LINES = $0001
#GL_LINE_LOOP = $0002
#GL_LINE_STRIP = $0003
#GL_TRIANGLES = $0004
#GL_TRIANGLE_STRIP = $0005
#GL_TRIANGLE_FAN = $0006
#GL_QUADS = $0007
#GL_QUAD_STRIP = $0008
#GL_POLYGON = $0009
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
ImportC "/usr/lib/libX11.a"+Chr('"')+" -l"+Chr('"')+"GL"
CompilerElse
Import "Opengl32.lib"
CompilerEndIf
;{-
glBegin(a.l)
glBindTexture(a.l,b.l)
glClear(a.l)
glClearColor(a.f,b.f,c.f,d.f)
glClearDepth(a.d)
glDepthFunc(a.l)
glDepthMask(a.c)
glDisable(a.l)
glEnable(a.l)
glEnd()
glGenTextures(a.l,b.l)
glGetError()
glHint(a.l,b.l)
glLoadIdentity()
glMatrixMode(a.l)
glShadeModel(a.l)
glTexCoord2f(a.f,b.f)
glTexEnvf(a.l,b.l,c.f)
glTexParameterf(a.l,b.l,c.f)
glTexParameteri(a.l,b.l,c.i)
glTranslatef(a.f,b.f,c.f)
glVertex3f(a.f,b.f,c.f)
glVertex2f(a.f,b.f)
glViewport(a.l,b.l,c.l,d.l)
glBlendFunc(a.l,b.l) ; As "glBlendFunc@8"
glPixelStorei(a.l,b.l) ; As "glPixelStorei@8"
glColor3f(r.f, g.f, b.f)
glColor4f(r.f, g.f, b.f, a.f)
glRotatef(degrees.f, x.f, y.f, z.f)
glPushMatrix()
glPopMatrix()
glMultMatrixf(*matrix)
;}
EndImport
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
ImportC "/usr/lib/libGLU.a"
CompilerElse
Import "Glu32.lib"
CompilerEndIf
;{-
gluBuild2DMipmaps(a.l,b.l,c.l,d.l,e.l,f.l,g.l)
gluErrorString(a.l)
gluPerspective(a.d,b.d,c.d,d.d)
gluLookAt(CameraX.f, CameraY.f, CameraZ.f, LookAtX.f, LookAtY.f, LookAtZ.f, UpX.f, UpY.f, UpZ.f)
;}
EndImport
;}
Procedure Noise(x, y)
Static salt = 0
salt = salt ! x & y
ProcedureReturn salt
EndProcedure
Procedure.i GenerateTexture(pWidth.i, pHeight.i)
Protected img = CreateImage(#PB_Any, pWidth, pHeight, 32)
StartDrawing(ImageOutput(img))
Protected *mem = DrawingBuffer()
For y = 0 To pHeight-1
For x = 0 To pWidth-1
n = Noise(x, y) * 4
If n < 0 : n = 0 : EndIf
If n > 255 : n = 255 : EndIf
Plot(x, y, RGBA(n, n, n, n))
Next
Next
DrawingMode(#PB_2DDrawing_Transparent )
DrawText(10,10,"Hello", RGBA(255,255,255,255))
StopDrawing()
Protected TexID = 0
glGenTextures(1, @TexID)
glBindTexture(#GL_TEXTURE_2D, TexID)
glTexEnvf(#GL_TEXTURE_ENV, #GL_TEXTURE_ENV_MODE, #GL_MODULATE )
; when texture area is small, bilinear filter the closest mipmap
glTexParameterf(#GL_TEXTURE_2D, #GL_TEXTURE_MIN_FILTER, #GL_LINEAR_MIPMAP_NEAREST );
; when texture area is large, bilinear filter the original
glTexParameterf(#GL_TEXTURE_2D, #GL_TEXTURE_MAG_FILTER, #GL_LINEAR );
rv = gluBuild2DMipmaps(#GL_TEXTURE_2D, 4, pWidth, pHeight, #GL_RGBA, #GL_UNSIGNED_BYTE, *mem ) ;
If rv <> 0
Debug Str(rv) + " :: " + PeekS(gluErrorString(rv))
ProcedureReturn 0
EndIf
ProcedureReturn TexID
EndProcedure
Procedure InitGL()
glClearColor(0.0, 0.0, 0.0, 0.0)
glEnable(#GL_TEXTURE_2D)
glEnable(#GL_BLEND)
glBlendFunc(#GL_SRC_ALPHA, #GL_ONE);
glPixelStorei(#GL_UNPACK_ALIGNMENT, 1);
glShadeModel(#GL_SMOOTH)
glClearDepth(1.0)
glEnable(#GL_DEPTH_TEST)
glDepthMask(#GL_TRUE);
glDepthFunc(#GL_LEQUAL)
glHint(#GL_PERSPECTIVE_CORRECTION_HINT, #GL_NICEST)
EndProcedure
Procedure ResizeScene(width, height)
If height < 1 : height = 1 : EndIf
If width < 1 : width = 1 : EndIf
glViewport(0, 0, width, height)
glMatrixMode(#GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, width / height, 0, 100)
glMatrixMode(#GL_MODELVIEW)
glLoadIdentity()
EndProcedure
Procedure DrawScene()
glClear(#GL_COLOR_BUFFER_BIT | #GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
If Roll <> 0 Or Pitch <> 0
Protected qResult.QUATERNION
Protected qPitch.QUATERNION
Protected qRoll.QUATERNION
Quaternion_FromAngleAxis(@qRoll, 0,0,1, Roll)
Quaternion_Multiply(@qResult, @qRoll, @qRotation)
CopyStructure(@qResult, @qRotation, QUATERNION)
Quaternion_FromAngleAxis(@qPitch, 1,0,0, Pitch)
Quaternion_Multiply(@qResult, @qPitch, @qRotation)
CopyStructure(@qResult, @qRotation, QUATERNION)
Roll = 0
Pitch = 0
EndIf
Protected m.Matrix4
Quaternion_ToMatrix(@m, @qRotation)
glMultMatrixf(@m)
glEnable(#GL_TEXTURE_2D)
glBindTexture(#GL_TEXTURE_2D, WallTexture)
glPushMatrix()
glTranslatef(0, 0, -5)
glBegin(#GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3f(0.0, 0.0, 1.0); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0, 0.0);
glVertex3f( 1.0, 0.0, 1.0); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0, 1.0);
glVertex3f( 1.0, 1.0, 1.0); // Top Right Of The Texture and Quad
glTexCoord2f(0.0, 1.0);
glVertex3f(0.0, 1.0, 1.0); // Top Left Of The Texture and Quad
glEnd( );
glPopMatrix()
glDisable(#GL_TEXTURE_2D);
glPushMatrix()
glBegin(#GL_POINTS)
ForEach StarField()
c.f = 1
glColor4f(c,c,c,c)
glVertex3f( StarField()\x, StarField()\y, StarField()\z )
Next
glEnd()
glPopMatrix()
EndProcedure
InitSprite() : InitKeyboard()
OpenWindow (0, 100, 100, 512, 512, "OpenGL Window")
OpenWindowedScreen(WindowID(0), 0, 0, 512, 512, 0, 0, 0, #PB_Screen_WaitSynchronization)
glDisable(#GL_TEXTURE_RECTANGLE_ARB)
PB_Screen_Target = #GL_TEXTURE_2D
; load textures
#width = 64
#height = 64
WallTexture = GenerateTexture(#width, #height)
InitGL()
Repeat
TimerStart = ElapsedMilliseconds()
Select WindowEvent()
Case #PB_Event_CloseWindow
Break
EndSelect
ExamineKeyboard()
If KeyboardReleased(#PB_Key_Escape)
Break
EndIf
If KeyboardPushed(#PB_Key_Up)
Pitch + 1
EndIf
If KeyboardPushed(#PB_Key_Down)
Pitch - 1
EndIf
If KeyboardPushed(#PB_Key_Left)
Roll - 1
EndIf
If KeyboardPushed(#PB_Key_Right)
Roll + 1
EndIf
DrawScene()
FlipBuffers()
ResizeScene(WindowWidth(0), WindowHeight(0))
Repeat
glError = glGetError()
Select glError
Case 0
Break
Default
Debug glError ; + " :: " + PeekS(gluErrorString(glError))
EndSelect
ForEver
Pausing = Int((1000.0 / 60.0) - (ElapsedMilliseconds() - TimerStart))
If Pausing < 1 : Pausing = 1 : EndIf
Delay( Pausing )
ForEver