[Edited 09/23/12] Imagine FPS controls (strafe, turn) from a top-down perspective. Now imagine when the player turns the entire screen rotates so they're always facing the top of the monitor. Now imagine the "camera" projecting this is always 10 feet above and behind the player relative to their orientation. This means the camera's looking down at the landscape at a 45 degree angle, rotating as the player turns. I'm losing my mind trying to grasp the code necessary for this, and this is what I have so far with mixed results:
Public Sub Mode_Perspective()
' Set OpenGL matrices to perspective mode.
' Set projection mode.
Gl.MatrixMode(Gl.PROJECTION)
Gl.LoadIdentity()
Glu.Perspective(45, sWidth / sHeight, Camera.Near, Camera.Far)
Gl.MatrixMode(Gl.MODELVIEW)
Gl.LoadIdentity()
Gl.Scalef(1, -1, 1 * ElevationScale)
End
Public Sub Update_Camera()
' Calculate translated camera position.
CameraXY = Calculate.TranslateInDirection(Camera.WorldX, Camera.WorldY, 10, Camera.Orientation + 90)
' Calculate up vector.
CameraTarget[0] = Client.PlayerData[Client.Number].WorldX_Current
CameraTarget[1] = Client.PlayerData[Client.Number].WorldY_Current
CameraTarget[2] = Client.PlayerData[Client.Number].WorldZ_Current
CameraLeft[0] = Calculate.RotateX(CameraXY[0], CameraXY[1], Camera.WorldX, Camera.WorldY, 1)
CameraLeft[1] = Calculate.RotateY(CameraXY[0], CameraXY[1], Camera.WorldX, Camera.WorldY, 1)
CameraLeft[2] = Camera.WorldZ
CameraRight[0] = Calculate.RotateX(CameraXY[0], CameraXY[1], Camera.WorldX, Camera.WorldY, -1)
CameraRight[1] = Calculate.RotateY(CameraXY[0], CameraXY[1], Camera.WorldX, Camera.WorldY, -1)
CameraRight[2] = Camera.WorldZ
UpVector = Calculate.Normal(CameraTarget, CameraLeft, CameraRight)
' Update OpenGL matrix.
Glu.LookAt(CameraXY[0], CameraXY[1], Camera.WorldZ, Client.PlayerData[Client.Number].WorldX_Current, Client.PlayerData[Client.Number].WorldY_Current, Client.PlayerData[Client.Number].WorldZ_Current, UpVector[0], UpVector[1], UpVector[2])
End
Public Function TranslateInDirection(StartX As Single, StartY As Single, Distance As Single, Direction As Single) As Single[]
' Translate specified coordinates by specified units at specified direction.
' General declarations.
Dim NewXY As New Single[2]
NewXY[0] = StartX + Distance * Cos(Rad(Direction))
NewXY[1] = StartY + Distance * Sin(Rad(Direction))
' Return new coordinates.
Return NewXY
End
Public Function RotateX(PWOX As Single, PWOY As Single, OriginX As Single, OriginY As Single, Orientation As Single) As Single
' Rotate specified point about specified point and return new X coordinate.
Return (OriginX + (Cos(Rad(Orientation)) * (PWOX - OriginX) - Sin(Rad(Orientation)) * (PWOY - OriginY)))
End
Public Function RotateY(PWOX As Single, PWOY As Single, OriginX As Single, OriginY As Single, Orientation As Single) As Single
' Rotate specified point about specified point and return new Y coordinate.
Return (OriginY + (Sin(Rad(Orientation)) * (PWOX - OriginX) + Cos(Rad(Orientation)) * (PWOY - OriginY)))
End
Public Function Normal(p1 As Single[], p2 As Single[], p3 As Single[]) As Single[]
' Calculate and return surface normal of specified triangle.
' General declarations.
Dim N As New Single[3]
Dim Magnitude As Single
' Calculate normal.
N[0] = (p2[1] - p1[1]) * (p3[2] - p1[2]) - (p2[2] - p1[2]) * (p3[1] - p1[1])
N[1] = (p2[2] - p1[2]) * (p3[0] - p1[0]) - (p2[0] - p1[0]) * (p3[2] - p1[2])
N[2] = ((p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0])) * -1
' Normalize normal.
Magnitude = Sqr(N[0] ^ 2 + N[1] ^ 2 + N[2] ^ 2)
N[0] = N[0] / Magnitude
N[1] = N[1] / Magnitude
N[2] = N[2] / Magnitude
Return N
End
Here's a summary of what I'm doing with the Update_Camera procedure:
1) Temporarily move the camera ten feet behind itself (it loosely follows the player).
2) Calculate three points. One is the player's position, or camera target. The second and third are the moved camera's position, one rotated around the player slightly clockwise and the other slightly counterclockwise.
3) Calculate the "up" vector using the three points from step 2.
4) Use glu.LookAt to properly position and orient the "camera".
This seems to work well at first, but when I lower the camera or increase it's distance from the player things start to distort. The camera appears to zoom in and the vertical scale of the landscape appears to increase. Sprite quads rendered parallel to the landscape also are still rendered as if the camera is directly above them, when they should be rendered at increasing perspective as the camera is lowered. What could I be missing?