Kamera (2)
Aus DGL Wiki
Version vom 13. November 2005, 12:53 Uhr von Andyh (Diskussion | Beiträge)
Eine Beschreibung findet ihr unter Kamera (1).
Die verwendete Toolsammlung findet ihr unter Kamera (3).
// Da ich einige Monate gebraucht habe um diese Kamera zu bauen,
// was leider direkt zu Beginn meiner OpenGL Karriere geschehen
// musste, halte ich dieses Produkt schon fuer ziemlich ausgereift.
// Sollten allerdings Verbesserungen eingebracht werden oder
// Verstaendnisfragen entstehen, so mailt bitte an
//
// Andree.Heyroth@t-online.de
//
// Danke.
//
// Updates:
//
// 06.11.2005
// Die Deklaration der DESTROY Methode war falsch. Das fuehrte dazu,
// dass in der Methode CREATE erzeugte Objekte nicht richtig frei-
// gegeben wurden und es zu EAccessViolations kam. Die Methode wird
// jetzt in der PUBLIC Section mit der Direktive OVERRIDE aufgerufen.
//
// 11.11.2005
// Die Positionierung funktioniert jetzt.
// Wegen eines Verstaendnisproblems wurden die Positionen immer auf den
// Kamerapunkt aufaddiert. Das bedeutete, dass die Positionierung aus
// dem Ruder lief wenn sie nicht im Nullpunkt stattfand. Je groesser die
// gewuenschte Position war um so groesser war die Abweichung von der
// Darstellung.
// Ich habe nicht bedacht, dass bei einer "Kamera"-bewegung und -drehung
// ja eigentlich die Szene bewegt und gedreht wird. Deswegen wurden nach
// einer Positionierung die folgenden Projektionen immer basierend auf der
// danach gueltigen Kameraposition gemacht.
// Jetzt wird die Position so gesetzt, dass die gewuenschte Kameraposition
// immer vor dem Betrachter steht.
//
unit Camera;
interface
Uses DglOpenGL, OpenGLUtil, Windows, Classes;
type
TCameraMatrix=Class
Matrix: TArrMatrix;
InverseMatrix: TArrMatrix;
//constructor Create;
//destructor destroy;
procedure Identity;
procedure Load(M: TArrMatrix);
end;
TCamera=Class
CameraMatrix: TCameraMatrix;
HomeMatrix: TCameraMatrix;
Enabled : boolean;
Initiated: boolean;
FPosition: TGLvector;
FPointOfRotation: TGLvector;
FPositionOffset: TGLvector;
function UpVector: TGLvector;
function ViewDirection: TGLvector;
procedure RestorePosition(pos: integer);
procedure SavePosition(pos: integer);
function GivePosition(pos: integer): TGLvector;
procedure RotateCamera(ix, iy, iz: TGLdouble);
procedure TranslateCamera(ix, iy, iz: TGLdouble);
procedure CameraHome;
procedure PositionCamera(PositionVec: TGLvector;
ViewVec: TGLvector;
upVec: TGLvector);
procedure Adjust;
procedure Apply;
procedure ApplyForTerrain;
private
PosArray: array [0..9] of TCameraMatrix;
function GetPosition: TGLvector;
procedure SetPosition (Pos: TGLvector);
procedure Identity;
procedure RotateMatrix(ix, iy, iz: TGLdouble);
procedure Offset(x, y, z: TGLfloat);
procedure RotateRoundAxis(rx, ry, rz: TGLfloat);
public
constructor Create;
destructor Destroy;override;
published
property PointOfRotation: TGLvector read FPointOfRotation;
property Position: TGLvector read GetPosition write SetPosition;
end;
TPCamera=^TCamera;
implementation
uses SysUtils;
{
constructor TCameraMatrix.Create;
begin
inherited create;
end;
destructor TCameraMatrix.Destroy;
begin
inherited destroy;
end;
}
procedure TCameraMatrix.Identity;
// GetArrIdentity: aus OpenGL.pas
// initialisiert die CameraMatrix mit der Identitaetsmatrix
begin
Matrix := GetIdentity(Matrix);
InverseMatrix := GetIdentity(InverseMatrix);
end;
procedure TCameraMatrix.Load(M: TArrMatrix);
// die Matrix mit den Werten einer beliebigen anderen matrix fuellen
var
i: integer;
begin
for i:=0 to 15 do
Matrix[i]:=M[i];
// die invertierte Matrix kann benutzt werden um Objkekte z.B.
// immer zum Benutzer auszurichten
InvertMatrix (M, InverseMatrix);
end;
constructor TCamera.Create;
var
i: integer;
begin
inherited create;
// Initiated wird gebraucht um einmal alle Positionsspeicher
// mit der Anfangsposition zu belegen
Initiated := false;
// Kameramatrix anlegen
CameraMatrix := TCameraMatrix.Create;
// Matrix der letzten Position von PositionCamera anlegen
HomeMatrix := TCameraMatrix.Create;
// Positionsspeicher anlegen
for i := 0 to 9 do
PosArray[i] := TCameraMatrix.Create;
end;
destructor TCamera.Destroy;
// alle in create belegten Resourcen wieder freigeben
var
i: integer;
begin
FreeAndNil (CameraMatrix);
FreeAndNil (HomeMatrix);
for i := 0 to 9 do
FreeAndNil (PosArray[i]);
inherited destroy;
end;
procedure TCamera.RotateRoundAxis(rx, ry, rz: TGLfloat);
// hier drehen wir jetzt um die einzelnen Achsen.
// die Parameter geben die "Drehgeschwindigkeit" vor.
var
newMatrix: TArrMatrix;
begin
glMatrixMode (GL_MODELVIEW);
glPushMatrix();
// aktuelle Position und Lage der Kamera herstellen
glLoadMatrixf(@CameraMatrix.Matrix);
// wenn gewuenscht um die X-Achse drehen
if(rx <> 0) then
glRotatef(rx,1,0,0);
// wenn gewuenscht um die Y-Achse drehen
if(ry <> 0) then
glRotatef(ry,0,1,0);
// wenn gewuenscht um die Z-Achse drehen
if(rz <> 0) then
glRotatef(rz,0,0,1);
// die neu erzeugte Matrix auslesen
glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
// und in die Kameramatrix sichern
CameraMatrix.Load(newMatrix);
glPopMatrix();
end;
procedure TCamera.Identity;
begin
CameraMatrix.Identity;
HomeMatrix.Identity;
Enabled := true;
end;
procedure TCamera.Offset(x, y, z: TGLfloat);
// verschieben der Kamera auf einer beliebigen Achse
var
newMatrix: TArrMatrix;
begin
glMatrixMode (GL_MODELVIEW);
glPushMatrix();
glLoadIdentity;
glTranslatef(x,y,z);
glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
// wenn ich mit der funktion glMultMatrixf arbeite, wird die zeichnung
// immer entlang der richtigen achsen verschoben. wenn ich matrixmultiply
// nehme, wird sie auf den bildschirmachsen verschoben. das ist angenehmer
// zum arbeiten
newMatrix := Multiply (newMatrix, CameraMatrix.Matrix);
CameraMatrix.Load(newMatrix);
glPopMatrix();
end;
procedure TCamera.PositionCamera(PositionVec: TGLvector;
ViewVec: TGLvector;
upVec: TGLvector);
var
newMatrix: TArrMatrix;
i: integer;
P, V, U: TGLvector;
Laenge: TGLdouble;
begin
// die gewuenschte konstruktion immer auf die Z-ebene projizieren.
// zuerst die position in den nullpunkt holen
InitVector (P, 0, 0, 0);
// jetzt den viewpoint um den gleichen betrag reduzieren, damit
// die gerade parallel verschoben wird.
V := SubtractVector (ViewVec, PositionVec);
// U ist halt schneller geschrieben als upVec...
U := upVec;
// den betrag ermitteln, um den die kamera nachher auf der Z-Achse
// verschoben werden muss
Laenge := Magnitude (SubtractVector (P, V));
Identity;
FPointOfRotation := ViewVec;
glMatrixMode (GL_MODELVIEW);
glPushMatrix;
glLoadIdentity;
// glulookat wird die matrix parallel zur Z-achse ausrichten
gluLookAt (P.X, P.Y, P.Z, V.X, V.Y, V.Z, U.X, U.Y, U.Z);
glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
glPopMatrix;
CameraMatrix.Load(newMatrix);
HomeMatrix.Load(newMatrix);
// da wir uns jetzt am zielpunkt befinden, müssen wir auf der Z-achse
// wieder zurück zur kameraposition
Offset (0, 0, -Laenge);
// damit GetPosition den Wert zurückgibt den wir mit PositionCamera
// eingestellt haben, müssen wir die zu der von gluLookAt errechneten
// Position bestehende Differenz errechnen
FPositionOffset.X := PositionVec.X - CameraMatrix.Matrix[12];
FPositionOffset.Y := PositionVec.Y - CameraMatrix.Matrix[13];
FPositionOffset.Z := PositionVec.Z - CameraMatrix.Matrix[14];
// alle positionsspeicher mit der Kameraposition, Blickrichtung
// und dem upVector belegen. Nur beim ersten Aufruf von
// PositionCamera
if not Initiated then
begin
for i := 0 to 9 do
SavePosition (i);
Initiated := true;
end;
end;
procedure TCamera.CameraHome;
// Kamera in die beim letzten Aufruf von PositionCamera übergebene
// Position/Lage bringen
begin
CameraMatrix.Load(HomeMatrix.Matrix);
end;
procedure TCamera.SavePosition (pos: integer);
// wie der Prozedurname schon sagt...
begin
if (pos < 0) or (pos > 9) then
exit;
PosArray[pos].Load(CameraMatrix.Matrix);
end;
procedure TCamera.RestorePosition (pos: integer);
// wie der Prozedurname schon sagt...
begin
if (pos < 0) or (pos > 9) then
exit;
CameraMatrix.Load(PosArray[pos].Matrix);
end;
function TCamera.GivePosition (pos: integer): TGLvector;
// gibt den Inhalt des durch pos bestimmten
// Positionsspecihers zurueck
begin
if (pos < 0) or (pos > 9) then
exit;
result.X := PosArray[pos].Matrix[12];
result.Y := PosArray[pos].Matrix[13];
result.Z := PosArray[pos].Matrix[14];
end;
procedure TCamera.TranslateCamera(ix, iy, iz: TGLdouble);
// vom Benutzer aufzurufende Methode um eine Verschiebung
// durchzufuehren
begin
Offset (ix, iy, iz);
end;
procedure TCamera.RotateCamera(ix, iy, iz: TGLdouble);
// vom Benutzer aufzurufende Methode um eine Drehung
// durchzufuehren
begin
RotateMatrix (ix, iy, iz);
end;
procedure TCamera.Apply;
// hier wird die Kamera eingeschaltet. Nach dem Aufruf dieser Prozedur
// sollte die Szene mit allen benoetigten Drehungen, Verschiebungen
// gezeichnet werden.
begin
if not Enabled then
exit;
glMatrixMode (GL_MODELVIEW);
glLoadMatrixf(@CameraMatrix.Matrix);
glTranslatef (-PointOfRotation.X,
-PointOfRotation.y,
-PointOfRotation.Z);
end;
procedure TCamera.ApplyForTerrain;
// hier wird wie in Apply die Kamera eingeschaltet. da man um ein terrain
// (skycube, ...) anzuzeigen aber immer die gleiche entfernung zur welt
// einhalten muss, wird hier nur gedreht und nicht verschoben.
begin
if not Enabled then
exit;
glMatrixMode (GL_MODELVIEW);
// für das Terrain nur die Drehung ausführen
glLoadMatrixf(@CameraMatrix.Matrix);
// deswegen jetzt die verschiebung zurücknehmen
glTranslatef (CameraMatrix.InverseMatrix[12],
CameraMatrix.InverseMatrix[13],
CameraMatrix.InverseMatrix[14]);
end;
procedure TCamera.RotateMatrix (ix, iy, iz: TGLdouble);
begin
// um den austausch von verschiedenen kameramodulen so einfach
// wie möglich zu gestalten, kann hier ein bisschen variiert werden...
RotateRoundAxis (-iy, -ix, -iz);
end;
function TCamera.GetPosition: TGLvector;
// diese Property-Funktion fragt die aktuelle Position der Kamera ab
var
return: TGLvector;
begin
// position: letzte Spalte der Matrix
InitVector (return,
CameraMatrix.Matrix[12],
CameraMatrix.Matrix[13],
CameraMatrix.Matrix[14]);
return := AddVector (return, FPositionOffset);
result := return;
end;
procedure TCamera.SetPosition (Pos: TGLvector);
// diese Property-Funktion setzt eine neue Position der Kamera
begin
// position: letzte Spalte der Matrix
CameraMatrix.Matrix[12] := Pos.X;
CameraMatrix.Matrix[13] := Pos.Y;
CameraMatrix.Matrix[14] := Pos.Z;
end;
function TCamera.ViewDirection: TGLvector;
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
// abgefragt werden
var
return: TGLvector;
begin
// view direction: dritte Spalte der Matrix (Z-Achse)
InitVector (return,
CameraMatrix.Matrix[08],
CameraMatrix.Matrix[09],
CameraMatrix.Matrix[10]);
result := return;
end;
function TCamera.UpVector: TGLvector;
// mit dieser Funktion kann die aktuelle Ausrichtung der Kamera
// abgefragt werden
var
return: TGLvector;
begin
// upVector: zweite Spalte der Matrix (Y-Achse)
InitVector (return,
CameraMatrix.Matrix[04],
CameraMatrix.Matrix[05],
CameraMatrix.Matrix[06]);
result := return;
end;
procedure TCamera.Adjust;
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhaengig
// von Drehung und Position, senkrecht zur Y-Achse ausgerichtet
// werden. Die aktuelle Position wird dabei beibehalten.
var
temp: TArrMatrix;
begin
temp := GetIdentity(temp);
temp[12] := CameraMatrix.Matrix[12];
temp[13] := CameraMatrix.Matrix[13];
temp[14] := CameraMatrix.Matrix[14];
temp[15] := CameraMatrix.Matrix[15];
CameraMatrix.Load(temp);
end;
end.