Is this a good OpenGl camera?

BlitzMax Forums/BlitzMax OpenGL Programming/Is this a good OpenGl camera?

I've noticed many people having trouble with cameras and perspective in OpenGl, like Chroma and Vertex.
I have been studying OGl furiously for a little while now and I wrote what I believe to be a fully-functional
3d camera that rotates at it's local center (Vertex's problem)and doesn't change it's Z-axis of rotation(Chroma's bug).
[edit] How naive!


Does this program answer anyone's problems? Or am I missing something huge here?

Strict

Global cubeNum = 120
Global ScreenWidth:Int=800
Global ScreenHeight:Int=600
Global ScreenDepth:Int=32
Global mousex1:Float, mousey1:Float
Global pitch:Float, yaw:Float, roll:Float


'-------------------initialize opengl
GLGraphics(ScreenWidth,ScreenHeight,ScreenDepth,0,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth 1.0
glDepthFunc(GL_LESS)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)
glViewport(0,0,ScreenWidth,ScreenHeight)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0,Float(ScreenWidth)/Float(ScreenHeight),1.0,10000.0)
glMatrixMode(GL_MODELVIEW)
'-------------------done
HideMouse()

Type cameraType
	Field x:Float, y:Float = 8, z:Float = -70, x1:Float, y1:Float, z1:Float
	Method update()
		x:+x1
		y:+y1
		z:+z1
		x1:*.7
		y1:*.7
		z1:*.7
	EndMethod
EndType
Global cam:cameraType = New cameraType

Type cubeType																							'this is 
	Field pitch:Float, pitch1:Float
	Field roll:Float, roll1:Float
	Field x:Float, y:Float, z:Float
	Method draw()
		gltranslatef x,y,z
		glrotatef pitch, 0.8, 0.2, 0.0			'now we can rotate the cubes locally
		glrotatef roll, 0.0, 0.2, 0.8			'
		glbegin gl_quads
			glcolor3f	 0.9, 0.4, 0.1
			glvertex3f -2, 2, 0
			glvertex3f  2, 2, 0
			glcolor3f	 0.2, 0.4, 0.5
			glvertex3f  2,-2, 0
			glvertex3f -2,-2, 0
			glcolor3f	 0.5, 0.3, 0.1
			glvertex3f 0, 2, 2
			glvertex3f 0, 2,-2
			glcolor3f	 0.8, 0.4, 0.5
			glvertex3f 0,-2,-2
			glvertex3f 0,-2, 2
		glend
		pitch:+pitch1
		If pitch> 360 pitch:-360
		roll:+roll1
		If roll> 360 roll:-360
	EndMethod
	Method New()
		x = Rand(-50, 50)
		y = Rand(4, 10)
		z = Rand(-50, 50)
		If Rand(2)-1 
			pitch = Rand(0,360) 
			pitch1 = 1
		Else 
			roll = Rand(0,360) 
			roll1 = 1
		EndIf
	EndMethod
EndType
Global cube:cubeType[cubeNum]
For Local a:Int = 0 To cubeNum-1
	cube[a] = New cubeType
Next

Type mapType
	Field sizeX:Int = 100, sizeY:Int = 100
	Field xRot:Float, yRot:Float, zRot:Float, speedRot:Float
	Field xx:Float, zz:Float																		'for draw()
	Field heightmap:Float[sizeX, sizeY]
	Field mapR:Float[sizeX, sizeY]
	Field mapG:Float[sizeX, sizeY]
	Field mapB:Float[sizeX, sizeY]
	Method createMap()
		For Local y:Float = 0 To sizeY-1
			For Local x:Float = 0 To sizeX-1
				heightmap[x,y] = RndFloat()*25-25
				mapR[x,y] = RndFloat()
				mapG[x,y] = RndFloat()
				mapB[x,y] = RndFloat()
			Next
		Next
		Local c:Float															'smooth map height
		For Local a:Int = 0 To 5
			For Local y:Int = 1 To sizeY-2
				For Local x:Int = 1 To sizeX-2
					c=heightmap[x-1,y]+heightmap[x+1,y]
					c:+heightmap[x,y-1]+heightmap[x,y+1]
					c:*.25
					heightmap[x,y]=c
				Next
			Next
		Next
		For Local a:Int = 0 To 0										'smooth map colors
			For Local y:Int = 1 To sizeY-2
				For Local x:Int = 1 To sizeX-2
					c=mapR[x-1,y]+mapR[x+1,y]
					c:+mapR[x,y-1]+mapR[x,y+1]
					c:*.25
					mapR[x,y]=c
					c=mapG[x-1,y]+mapG[x+1,y]
					c:+mapG[x,y-1]+mapG[x,y+1]
					c:*.25
					mapG[x,y]=c
					c=mapB[x-1,y]+mapB[x+1,y]
					c:+mapB[x,y-1]+mapB[x,y+1]
					c:*.25
					mapB[x,y]=c
				Next
			Next
		Next
	EndMethod
	Method update()
		xRot:*.9
		yRot:*.9
		zRot:*.9
	EndMethod
	Method draw()
		glclear gl_color_buffer_bit | gl_depth_buffer_bit		'clear the previous image and it's z-buffer
		cameraMatrix()																			'this function sets the camera's position and rotation
		glbegin gl_quads																		'start drawing the terrain
		For Local z:Float = 2 To sizeY-4
			zz=z-sizeY*.5
			For Local x:Float = 2 To sizeX-4
				xx=x-sizeX*.5
				
				glcolor3f mapR[x,z], mapG[x,z],mapB[x,z] 									'
				glvertex3f xx,heightmap[x,z], zz
				
				glcolor3f mapR[x+1,z], mapG[x+1,z], mapB[x+1,z]						'
				glvertex3f xx+1,heightmap[x+1,z],zz
				
				glcolor3f mapR[x+1,z+1], mapG[x+1,z+1], mapB[x+1,z+1]			'
				glvertex3f xx+1,heightmap[x+1,z+1],zz+1
				
				glcolor3f mapR[x,z+1], mapG[x,z+1], mapB[x,z+1]						'
				glvertex3f xx,heightmap[x,z+1], zz+1
				
			Next
		Next
		glend
		'so what I'm going to do here is add those spinning cube things to my scene.
		'This is to test if I can rotate the scene(above) to position the camera and THEN, without
		'messing up the matrix, add these things.  Each have their own local rotations.
		
		For Local a:Int = 0 To cube.length-1
			cameraMatrix()			
			cube[a].draw()
		Next
			'end test
	EndMethod
EndType
Global map:mapType = New maptype
map.createMap()




While Not KeyHit(key_escape)
	keys()
	cam.update()
	map.update()
	map.draw()
	'---------extra stuff
'	GLDrawText cam.x+", "+cam.y+", "+cam.z, 20,20
'	GLDrawText pitch+", "+yaw+", "+roll, 20,31
	GLDrawText "move = wasd + mouse", 20, 40
	'-------mouse movement
	mousex1 = MouseX()-400
	mousey1 = MouseY()-300
	MoveMouse 400,300
	'-------
	Flip
Wend
EndGraphics();End

Function cameraMatrix()
	glloadidentity																			'erase any gltranslates or glrotates I did last loop
	glrotatef pitch, 1.0, 0.0, 0.0											'rotate up/down
	glrotatef yaw, 0.0, 1.0, 0.0												'rotate left/right
	glrotatef roll, 0.0, 0.0, 1.0												'roll
	gltranslatef cam.x, cam.y, cam.z										'NOW move the world, after rotating it
EndFunction

Function keys()	
	yaw:+mousex1*.5
	If yaw< 0 yaw:+360
	If yaw >360 yaw:-360
	pitch:+mousey1*.5
	If pitch< 0 pitch:+360
	If pitch >360 pitch:-360
	
	If KeyDown(key_left) Or KeyDown(key_a)
		cam.x1:+Cos(yaw)*.5
		cam.z1:+Sin(yaw)*.5
	EndIf
	If KeyDown(key_right) Or KeyDown(key_d)
		cam.x1:-Cos(yaw)*.5
		cam.z1:-Sin(yaw)*.5
	EndIf
	If KeyDown(key_down) Or KeyDown(key_s)
		cam.x1:-Cos(yaw+90)*.5
		cam.z1:-Sin(yaw+90)*.5
	EndIf
	If KeyDown(key_up) Or KeyDown(key_w)
		cam.x1:+Cos(yaw+90)*.5
		cam.z1:+Sin(yaw+90)*.5
	EndIf
	If KeyDown(key_pageup) cam.y1:-1
	If KeyDown(key_pagedown) cam.y1:+1
	If KeyDown(key_num7) pitch:+1
	If KeyDown(key_num4) pitch:-1
	If KeyDown(key_num9) roll:+1
	If KeyDown(key_num6) roll:-1
	If KeyDown(key_num1) pitch:*.8
EndFunction



alas thats rotating on world axis's not camera local...

Hmm. So what's the difference? As far as I know, there
aren't any ways to make actual cameras in OGL. You have to
move and rotate the world around you. That's not right? :/
This is where I learned that from.

basically using your example look upwards and rotate left/right, if the rotations were local to camera you'd rotate in a straight line through the edge's of the screen.
in your sample when looking directly up you are still rotating round the world Y axis
look @ the code I posted here http://www.blitzbasic.com/Community/posts.php?topic=53272
when you look directly up or down and roate left/right the rotation is different
The apparent z axis rotation is actually not a rotation on the z axis but a natural consequence of rotation around local axes, try the blitz3d sample, it behaves in the same way...

Ah, I understand what you mean. But wasn't that undesirable?
You eventually lose which way is 'up' with local rotations like that.
I modified my code a bit, now it allows vertical movement.
Isn't this exactly the kind of movement you'd want to have?
I don't know about you guys, but this is hugely usefull for me! Hehe.
'wasd keys move.  Mouse looks.  Left/right mouse buttons bank camera.
Strict

Global cubeNum = 120
Global ScreenWidth:Int=1024
Global ScreenHeight:Int=768
Global ScreenDepth:Int=32
Global mousex1:Float, mousey1:Float
Global light= 1
Global PolyColour:Float[] = [1.0, 1.0, 0.5, 1.0]
Global Ambient_Light:Float[] = [0.0, 0.0, 0.0, 1.0]
Global diffuse_Light:Float[] = [1.0, 1.0, 0.7, 1.0]
Global specular_light:Float[] = [1.0, 1.0, 1.0, 1.0]
Global position_Light:Float[] = [0.0,10.0, 0.0, 1.0]
Global fogcolor:Float[]=[0.0,0.0,0.01,1.0]


'-------------------initialize opengl
GLGraphics(ScreenWidth,ScreenHeight,ScreenDepth,0,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER)
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth 1.0
glDepthFunc(GL_LESS)
glEnable(GL_DEPTH_TEST)
glViewport(0,0,ScreenWidth,ScreenHeight)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(95.0,Float(ScreenWidth)/Float(ScreenHeight),1.0,10000.0)
glMatrixMode(GL_MODELVIEW)
glShadeModel(GL_SMOOTH)
'glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)

'---light
'glLightModelfv (Gl_Light_model_ambient, Float Ptr ambient_light)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT1)
gllightfv(gl_light1, gl_ambient, Float Ptr ambient_light)
gllightfv(gl_light1, gl_diffuse, Float Ptr diffuse_light)
gllightfv(gl_light1, gl_specular, Float Ptr specular_light)
gllightfv(gl_light1, gl_position, Float Ptr position_light)
'material
glenable(gl_color_material)
glcolormaterial(gl_front, gl_ambient_and_diffuse)
'glmaterialfv (gl_front, gl_ambient_and_diffuse, Float Ptr polycolour)

'-------------------done

	glFogi(GL_FOG_MODE, gl_linear)								' Fog Mode
	glFogfv(GL_FOG_COLOR, fogColor)										' Set Fog Color
	glFogf(GL_FOG_DENSITY, 0.35)										' How Dense Will The Fog Be
	glHint(GL_FOG_HINT, GL_DONT_CARE)									' Fog Hint Value
	glFogf(GL_FOG_START, 1.0)											' Fog Start Depth
	glFogf(GL_FOG_END, 45.0)											' Fog End Depth
	glEnable(GL_FOG)													' Enables GL_FOG



HideMouse()

Type cameraType
	Field x:Float = 40, y:Float = 0, z:Float = 40, x1:Float, y1:Float, z1:Float
	Field pitch:Float, yaw:Float = 120, roll:Float
	
	Method update()
		x:+x1
		y:+y1
		z:+z1
		x1:*.7
		y1:*.7
		z1:*.7
		roll:*.9
	EndMethod
EndType
Global cam:cameraType = New cameraType

Type cubeType
	Field pitch:Float, pitch1:Float
	Field roll:Float, roll1:Float
	Field x:Float, y:Float, z:Float
	Method draw()
		gltranslatef x,y,z
		glrotatef pitch, 0.8, 0.2, 0.0			'now we can rotate the cubes locally
		glrotatef roll, 0.0, 0.2, 0.8			'
		glbegin gl_quads
			glcolor3f	 0.9, 0.4, 0.1
			glvertex3f -2, 2, 0
			glvertex3f  2, 2, 0
			glcolor3f	 0.2, 0.4, 0.5
			glvertex3f  2,-2, 0
			glvertex3f -2,-2, 0
			glcolor3f	 0.5, 0.3, 0.1
			glvertex3f 0, 2, 2
			glvertex3f 0, 2,-2
			glcolor3f	 0.8, 0.4, 0.5
			glvertex3f 0,-2,-2
			glvertex3f 0,-2, 2
		glend
		pitch:+pitch1
		If pitch> 360 pitch:-360
		roll:+roll1
		If roll> 360 roll:-360
	EndMethod
	Method New()
		x = Rand(-50, 50)
		y = Rand(4, 10)
		z = Rand(-50, 50)
		If Rand(2)-1 
			pitch = Rand(0,360) 
			pitch1 = 1
		Else 
			roll = Rand(0,360) 
			roll1 = 1
		EndIf
	EndMethod
EndType
Global cube:cubeType[cubeNum]
For Local a:Int = 0 To cubeNum-1
	cube[a] = New cubeType
Next

Type mapType
	Field sizeX:Int = 100, sizeY:Int = 100
	Field xRot:Float, yRot:Float, zRot:Float, speedRot:Float
	Field xx:Float, zz:Float																		'for draw()
	Field heightmap:Float[sizeX, sizeY]
	Field mapR:Float[sizeX, sizeY]
	Field mapG:Float[sizeX, sizeY]
	Field mapB:Float[sizeX, sizeY]
	Method createMap()
		For Local y:Float = 0 To sizeY-1
			For Local x:Float = 0 To sizeX-1
				heightmap[x,y] = RndFloat()*25-25
				mapR[x,y] = RndFloat()
				mapG[x,y] = RndFloat()
				mapB[x,y] = RndFloat()
			Next
		Next
		Local c:Float															'smooth map height
		For Local a:Int = 0 To 5
			For Local y:Int = 1 To sizeY-2
				For Local x:Int = 1 To sizeX-2
					c=heightmap[x-1,y]+heightmap[x+1,y]
					c:+heightmap[x,y-1]+heightmap[x,y+1]
					c:*.25
					heightmap[x,y]=c
				Next
			Next
		Next
		For Local a:Int = 0 To 0										'smooth map colors
			For Local y:Int = 1 To sizeY-2
				For Local x:Int = 1 To sizeX-2
					c=mapR[x-1,y]+mapR[x+1,y]
					c:+mapR[x,y-1]+mapR[x,y+1]
					c:*.25
					mapR[x,y]=c
					c=mapG[x-1,y]+mapG[x+1,y]
					c:+mapG[x,y-1]+mapG[x,y+1]
					c:*.25
					mapG[x,y]=c
					c=mapB[x-1,y]+mapB[x+1,y]
					c:+mapB[x,y-1]+mapB[x,y+1]
					c:*.25
					mapB[x,y]=c
				Next
			Next
		Next
	EndMethod
	Method update()
		xRot:*.9
		yRot:*.9
		zRot:*.9
	EndMethod
	Method draw()
		glclear gl_color_buffer_bit | gl_depth_buffer_bit				'clear the previous image and it's z-buffer
		cameraMatrix()																					'this function sets the camera's position and rotation
		gllightfv(gl_light1, gl_position, Float Ptr position_light)
		glbegin gl_quads																				'start drawing the terrain
		For Local z:Float = 2 To sizeY-4
			zz=z-sizeY*.5
			For Local x:Float = 2 To sizeX-4
				xx=x-sizeX*.5
'				glnormal3f
				glcolor3f mapR[x,z], mapG[x,z],mapB[x,z] 									'
				glvertex3f xx,heightmap[x,z], zz
				
				glcolor3f mapR[x+1,z], mapG[x+1,z], mapB[x+1,z]						'
				glvertex3f xx+1,heightmap[x+1,z],zz
				
				glcolor3f mapR[x+1,z+1], mapG[x+1,z+1], mapB[x+1,z+1]			'
				glvertex3f xx+1,heightmap[x+1,z+1],zz+1
				
				glcolor3f mapR[x,z+1], mapG[x,z+1], mapB[x,z+1]						'
				glvertex3f xx,heightmap[x,z+1], zz+1
				
			Next
		Next
		glend
		'so what I'm going to do here is add those spinning cube things to my scene.
		'This is to test if I can rotate the scene(above) to position the camera and THEN, without
		'messing up the matrix, add these things.  Each have their own local rotations.
		
		For Local a:Int = 0 To cube.length-1
			cameraMatrix()			
			cube[a].draw()
		Next
			'end test
	EndMethod
EndType
Global map:mapType = New maptype
map.createMap()
While Not KeyHit(key_escape)
	keys()
	cam.update()
	map.update()
	map.draw()
	'---------extra stuff
	GLDrawText cam.x+", "+cam.y+", "+cam.z, 20,20
	GLDrawText cam.pitch+", "+cam.yaw+", "+cam.roll, 20,31
	GLDrawText "move = wasd + mouse", 20, 45
	'-------mouse movement
	mousex1 = MouseX()-400
	mousey1 = MouseY()-300
	MoveMouse 400,300
	'-------
	Flip
Wend
EndGraphics();End

Function cameraMatrix()
	glloadidentity																			'erase any gltranslates or glrotates I did last loop
	glrotatef cam.roll, 0.0, 0.0, 1.0												'roll
	glrotatef cam.pitch, 1.0, 0.0, 0.0											'rotate up/down
	glrotatef cam.yaw, 0.0, 1.0, 0.0												'rotate left/right
	gltranslatef cam.x, cam.y, cam.z										'NOW move the world, after rotating it
EndFunction

Function keys()
	If MouseDown(1)
		cam.roll:-5
		cam.yaw:+cam.roll*.1
	EndIf
	If MouseDown(2)
		cam.roll:+5
		cam.yaw:+cam.roll*.1
	EndIf
	cam.yaw:+mousex1*.5
	If cam.yaw< 0 cam.yaw:+360
	If cam.yaw >360 cam.yaw:-360
	cam.pitch:+mousey1*.5
	If cam.pitch< 0 cam.pitch:+360
	If cam.pitch >360 cam.pitch:-360
	
	If KeyDown(key_left) Or KeyDown(key_a)
		cam.x1:+Cos(cam.yaw)*.1
		cam.z1:+Sin(cam.yaw)*.1
	EndIf
	If KeyDown(key_right) Or KeyDown(key_d)
		cam.x1:-Cos(cam.yaw)*.1
		cam.z1:-Sin(cam.yaw)*.1
	EndIf
	If KeyDown(key_down) Or KeyDown(key_s)
		cam.x1:-Cos(cam.yaw+90)*.1
		cam.z1:-Sin(cam.yaw+90)*.1
		cam.y1:-Sin(cam.pitch)*.1
	EndIf
	If KeyDown(key_up) Or KeyDown(key_w)
		cam.x1:+Cos(cam.yaw+90)*.1
		cam.z1:+Sin(cam.yaw+90)*.1
		cam.y1:+Sin(cam.pitch)*.1
	EndIf
	If KeyDown(key_pageup) cam.y1:-.1
	If KeyDown(key_pagedown) cam.y1:+.1
	If KeyDown(key_num7) cam.pitch:+1
	If KeyDown(key_num4) cam.pitch:-1
	If KeyDown(key_num9) cam.roll:+1
	If KeyDown(key_num6) cam.roll:-1
	If KeyDown(key_num1) cam.pitch:*.8
	If KeyHit(key_l) 
		light = 1-light
		If light
			glEnable(GL_LIGHTING)
			glEnable(GL_LIGHT1)
		Else
			gldisable(GL_LIGHTING)
			gldisable(GL_LIGHT1)
		EndIf
	EndIf
EndFunction



if you're just doing somthing where you're walking round on a terrain, or in say a quake type level, world axis rotations are generally fine, even desirable.
However if you want a flight sim or space ship type shooter you have to use local (camera) rotations.
Things get further compilcated with flight sims because you have to simulate the tendency for a plane to self level which involves converting local rotations into world rotations to find out how far off "trim" you are...

Hey guys,
I think my OpenGL / BlitzMax Camera Tutorial should help you and make things clear. It is very well commented code that's as easy as possible. I thought the community was aware of it but it seems I thought wrong.
Well this is the url: http://www.eizdealer.net/#coding
I hope you can use it! Maybe someone should tell the guys in the other thread as well...

Damn I forgot: The code won't work with the new BMax update. I have no idea what has to be changed but it's partially based on BlitzGL. Shouldn't be too much trouble though. I haven't been coding for quite a long time now... no time :(

@eizdealer tried using your camera class b4, doesnt seem to be able to do *LOCAL* camera axis rotations...

Chris, did you have AttendLocalMatrix set to True? If so, it's .. ehr .. irritating. It should work. Although I have to admit I haven't tried the rotation using the local matrix.

i had a play with it a while ago, tried a number of different things, but couldnt get it to rotate on the camera's axes...

I bodged together some some trackball code which will rotate on local x,y axes, but I'm not entirelly happy with it...
http://www.blitzbasic.com/Community/posts.php?topic=53272
about 1/2 down the thread

If you could have a look at your code and maybe put together a simple demo I'm sure a number of people would appreciate it (not least me!)

Maybe adding
	If KeyDown(KEY_Q)			Then Camera.Turn(        0.0,        0.0,        0.1)
	If KeyDown(KEY_E)			Then Camera.Turn(        0.0,        0.0,       -0.1)
to the controls can help. Actually I have no idea if this works, but I neither have the time to try it nor the time to work on it :(

shame - its using global co-ords, still my trakball converted C code is doing me well enough while I wait for the 3d module...