Kamera (2): Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
Zeile 6: Zeile 6:
 
// was leider direkt zu Beginn meiner OpenGL Karriere geschehen
 
// was leider direkt zu Beginn meiner OpenGL Karriere geschehen
 
// musste, halte ich dieses Produkt schon fuer ziemlich ausgereift.
 
// musste, halte ich dieses Produkt schon fuer ziemlich ausgereift.
// Sollten allerdings Verbesserungen eingebracht werden oder  
+
// Sollten allerdings Verbesserungen eingebracht werden oder
// Verständnisfragen entstehen, so mailt bitte an
+
// Verstaendnisfragen entstehen, so mailt bitte an
 
//
 
//
 
// Andree.Heyroth@t-online.de
 
// Andree.Heyroth@t-online.de
Zeile 20: Zeile 20:
 
// gegeben wurden und es zu EAccessViolations kam. Die Methode wird
 
// gegeben wurden und es zu EAccessViolations kam. Die Methode wird
 
// jetzt in der PUBLIC Section mit der Direktive OVERRIDE aufgerufen.
 
// 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;
 
unit Camera;
Zeile 25: Zeile 39:
 
interface
 
interface
  
Uses DglOpenGL, OpenGLUtil, Windows, Classes;<br>
+
  Uses DglOpenGL, OpenGLUtil, Windows, Classes;
  
type  
+
  type
 
   TCameraMatrix=Class
 
   TCameraMatrix=Class
 
     Matrix: TArrMatrix;
 
     Matrix: TArrMatrix;
 
     InverseMatrix: TArrMatrix;
 
     InverseMatrix: TArrMatrix;
 +
    //constructor Create;
 +
    //destructor destroy;
 
     procedure Identity;
 
     procedure Identity;
 
     procedure Load(M: TArrMatrix);
 
     procedure Load(M: TArrMatrix);
    constructor Create;
 
 
   end;
 
   end;
  
 
   TCamera=Class
 
   TCamera=Class
 
     CameraMatrix: TCameraMatrix;
 
     CameraMatrix: TCameraMatrix;
 +
    HomeMatrix: TCameraMatrix;
 
     Enabled  : boolean;
 
     Enabled  : boolean;
 
     Initiated: boolean;
 
     Initiated: boolean;
     function Position: TGLvector;
+
     FPosition: TGLvector;
 +
    FPointOfRotation: TGLvector;
 +
    FPositionOffset: TGLvector;
 
     function UpVector: TGLvector;
 
     function UpVector: TGLvector;
 
     function ViewDirection: TGLvector;
 
     function ViewDirection: TGLvector;
 
     procedure RestorePosition(pos: integer);
 
     procedure RestorePosition(pos: integer);
 
     procedure SavePosition(pos: integer);
 
     procedure SavePosition(pos: integer);
 +
    function GivePosition(pos: integer): TGLvector;
 
     procedure RotateCamera(ix, iy, iz: TGLdouble);
 
     procedure RotateCamera(ix, iy, iz: TGLdouble);
 
     procedure TranslateCamera(ix, iy, iz: TGLdouble);
 
     procedure TranslateCamera(ix, iy, iz: TGLdouble);
 
     procedure CameraHome;
 
     procedure CameraHome;
     procedure PositionCamera(PositionVec: TGLvector;  
+
     procedure PositionCamera(PositionVec: TGLvector;
                            ViewVec: TGLvector;  
+
                    ViewVec: TGLvector;
                            upVec: TGLvector);
+
    upVec: TGLvector);
 
     procedure Adjust;
 
     procedure Adjust;
 
     procedure Apply;
 
     procedure Apply;
 
     procedure ApplyForTerrain;
 
     procedure ApplyForTerrain;
 
   private
 
   private
    HomeMatrix: TCameraMatrix;
 
 
     PosArray: array [0..9] of TCameraMatrix;
 
     PosArray: array [0..9] of TCameraMatrix;
     FPointOfRotation: TGLvector;
+
     function GetPosition: TGLvector;
 +
    procedure SetPosition (Pos: TGLvector);
 
     procedure Identity;
 
     procedure Identity;
 
     procedure RotateMatrix(ix, iy, iz: TGLdouble);
 
     procedure RotateMatrix(ix, iy, iz: TGLdouble);
Zeile 67: Zeile 86:
 
   published
 
   published
 
     property PointOfRotation: TGLvector read FPointOfRotation;
 
     property PointOfRotation: TGLvector read FPointOfRotation;
 +
    property Position: TGLvector read GetPosition write SetPosition;
 
   end;
 
   end;
 
   TPCamera=^TCamera;
 
   TPCamera=^TCamera;
Zeile 72: Zeile 92:
 
implementation
 
implementation
  
 +
uses SysUtils;
 +
 +
{
 
constructor TCameraMatrix.Create;
 
constructor TCameraMatrix.Create;
// beim erzeugen der Matrix wird die Identitätsmatrix zugewiesen
 
 
begin
 
begin
   Identity;
+
   inherited create;
 +
end;
 +
 
 +
destructor TCameraMatrix.Destroy;
 +
begin
 +
  inherited destroy;
 
end;
 
end;
 +
}
  
 
procedure TCameraMatrix.Identity;
 
procedure TCameraMatrix.Identity;
// die Funktion GetArrIdentity findet Ihr in der Datei OpenGL.pas
+
// GetArrIdentity: aus OpenGL.pas
var
+
// initialisiert die CameraMatrix mit der Identitaetsmatrix
  i: integer;
 
 
begin
 
begin
   Matrix := GetArrIdentity;  
+
   Matrix := GetIdentity(Matrix);
   InverseMatrix := GetArrIdentity;
+
   InverseMatrix := GetIdentity(InverseMatrix);
 
end;
 
end;
  
 
procedure TCameraMatrix.Load(M: TArrMatrix);
 
procedure TCameraMatrix.Load(M: TArrMatrix);
// die Matrix mit den Werten einer beliebigen anderen matrix füllen
+
// die Matrix mit den Werten einer beliebigen anderen matrix fuellen
 
var
 
var
 
   i: integer;
 
   i: integer;
 
begin
 
begin
 
   for i:=0 to 15 do
 
   for i:=0 to 15 do
     Matrix[i]:=M[i];  
+
     Matrix[i]:=M[i];
 
   // die invertierte Matrix kann benutzt werden um Objkekte z.B.
 
   // die invertierte Matrix kann benutzt werden um Objkekte z.B.
 
   // immer zum Benutzer auszurichten
 
   // immer zum Benutzer auszurichten
   InvertMatrix (M, InverseMatrix); // die inverse Matrix erzeugen
+
   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;
 
end;
  
Zeile 108: Zeile 169:
 
   glPushMatrix();
 
   glPushMatrix();
 
   // aktuelle Position und Lage der Kamera herstellen
 
   // aktuelle Position und Lage der Kamera herstellen
   glLoadMatrixf(@CameraMatrix.Matrix);
+
   glLoadMatrixf(@CameraMatrix.Matrix);
   // wenn gewünscht um die X-Achse drehen
+
 
 +
   // wenn gewuenscht um die X-Achse drehen
 
   if(rx <> 0) then
 
   if(rx <> 0) then
     glRotatef(rx,1,0,0);              
+
     glRotatef(rx,1,0,0);
   // wenn gewünscht um die Y-Achse drehen
+
 
 +
   // wenn gewuenscht um die Y-Achse drehen
 
   if(ry <> 0) then
 
   if(ry <> 0) then
     glRotatef(ry,0,1,0);              
+
     glRotatef(ry,0,1,0);
   // wenn gewünscht um die Z-Achse drehen
+
 
 +
   // wenn gewuenscht um die Z-Achse drehen
 
   if(rz <> 0) then
 
   if(rz <> 0) then
     glRotatef(rz,0,0,1);              
+
     glRotatef(rz,0,0,1);
 +
 
 
   // die neu erzeugte Matrix auslesen
 
   // die neu erzeugte Matrix auslesen
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);  
+
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
 
   // und in die Kameramatrix sichern
 
   // und in die Kameramatrix sichern
   CameraMatrix.Load(newMatrix);        
+
   CameraMatrix.Load(newMatrix);
 
   glPopMatrix();
 
   glPopMatrix();
end;
 
 
constructor TCamera.Create;
 
var
 
  i: integer;
 
begin
 
  // 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;
 
end;
  
Zeile 147: Zeile 196:
  
 
   Enabled := true;
 
   Enabled := true;
end;
 
 
destructor TCamera.Destroy;
 
// alle belegten Resourcen wieder freigeben
 
var
 
  i: integer;
 
begin
 
  HomeMatrix.Free;
 
  CameraMatrix.Free;
 
  for i := 0 to 9 do
 
    PosArray[i].Free;
 
 
end;
 
end;
  
Zeile 174: Zeile 212:
 
   // nehme, wird sie auf den bildschirmachsen verschoben. das ist angenehmer
 
   // nehme, wird sie auf den bildschirmachsen verschoben. das ist angenehmer
 
   // zum arbeiten
 
   // zum arbeiten
   MatrixMultiply (newMatrix, CameraMatrix.Matrix, newMatrix);
+
   newMatrix := Multiply (newMatrix, CameraMatrix.Matrix);
 
   CameraMatrix.Load(newMatrix);
 
   CameraMatrix.Load(newMatrix);
 
   glPopMatrix();
 
   glPopMatrix();
 
end;
 
end;
  
procedure TCamera.PositionCamera(PositionVec: TGLvector;  
+
 
                                ViewVec: TGLvector;  
+
procedure TCamera.PositionCamera(PositionVec: TGLvector;
                                upVec: TGLvector);
+
                          ViewVec: TGLvector;
// erstes positionieren der kamera. hierbei werden einige
+
                          upVec: TGLvector);
// initialisierungen vorgenommen
 
 
var
 
var
 
   newMatrix: TArrMatrix;
 
   newMatrix: TArrMatrix;
 
   i: integer;
 
   i: integer;
 +
  P, V, U: TGLvector;
 +
  Laenge: TGLdouble;
 
begin
 
begin
   // alle kameraeinstellungen zurücknehmen
+
   // 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;
 
   Identity;
  // den aktuellen drehpunkt setzen
 
 
   FPointOfRotation := ViewVec;
 
   FPointOfRotation := ViewVec;
  // an die arbeit...
+
 
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
 
   glPushMatrix;
 
   glPushMatrix;
 
   glLoadIdentity;
 
   glLoadIdentity;
   glTranslatef (PositionVec.X, PositionVec.Y, PositionVec.Z);
+
 
   gluLookAt (PositionVec.X, PositionVec.Y, PositionVec.Z,
+
   // glulookat wird die matrix parallel zur Z-achse ausrichten
            ViewVec.X, ViewVec.Y, ViewVec.Z,
+
   gluLookAt (P.X, P.Y, P.Z, V.X, V.Y, V.Z, U.X, U.Y, U.Z);
            upVec.X, upVec.Y, upVec.Z);
 
 
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
 
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
 +
  glPopMatrix;
 +
 
   CameraMatrix.Load(newMatrix);
 
   CameraMatrix.Load(newMatrix);
 
   HomeMatrix.Load(newMatrix);
 
   HomeMatrix.Load(newMatrix);
   glPopMatrix;
+
 
 +
   // 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  
 
   // alle positionsspeicher mit der Kameraposition, Blickrichtung  
 
   // und dem upVector belegen. Nur beim ersten Aufruf von
 
   // und dem upVector belegen. Nur beim ersten Aufruf von
Zeile 216: Zeile 277:
  
 
procedure TCamera.CameraHome;
 
procedure TCamera.CameraHome;
// Kamera in die beim letzten Aufruf von PositionCamera übergebene
+
// Kamera in die beim letzten Aufruf von PositionCamera übergebene
 
// Position/Lage bringen
 
// Position/Lage bringen
 
begin
 
begin
Zeile 226: Zeile 287:
 
begin
 
begin
 
   if (pos < 0) or (pos > 9) then
 
   if (pos < 0) or (pos > 9) then
     exit;<br>
+
     exit;
 +
 
 
   PosArray[pos].Load(CameraMatrix.Matrix);
 
   PosArray[pos].Load(CameraMatrix.Matrix);
 
end;
 
end;
Zeile 234: Zeile 296:
 
begin
 
begin
 
   if (pos < 0) or (pos > 9) then
 
   if (pos < 0) or (pos > 9) then
     exit;<br>
+
     exit;
 +
 
 
   CameraMatrix.Load(PosArray[pos].Matrix);
 
   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;
 
end;
  
 
procedure TCamera.TranslateCamera(ix, iy, iz: TGLdouble);
 
procedure TCamera.TranslateCamera(ix, iy, iz: TGLdouble);
 +
// vom Benutzer aufzurufende Methode um eine Verschiebung
 +
// durchzufuehren
 
begin
 
begin
 
   Offset (ix, iy, iz);
 
   Offset (ix, iy, iz);
Zeile 244: Zeile 321:
  
 
procedure TCamera.RotateCamera(ix, iy, iz: TGLdouble);
 
procedure TCamera.RotateCamera(ix, iy, iz: TGLdouble);
 +
// vom Benutzer aufzurufende Methode um eine Drehung
 +
// durchzufuehren
 
begin
 
begin
 
   RotateMatrix (ix, iy, iz);
 
   RotateMatrix (ix, iy, iz);
Zeile 250: Zeile 329:
 
procedure TCamera.Apply;
 
procedure TCamera.Apply;
 
// hier wird die Kamera eingeschaltet. Nach dem Aufruf dieser Prozedur  
 
// hier wird die Kamera eingeschaltet. Nach dem Aufruf dieser Prozedur  
// sollte die Szene mit allen benötigten Drehungen, Verschiebungen  
+
// sollte die Szene mit allen benoetigten Drehungen, Verschiebungen  
 
// gezeichnet werden.
 
// gezeichnet werden.
 
begin
 
begin
 
   if not Enabled then
 
   if not Enabled then
     exit;<br><br>
+
     exit;
 +
 
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
 
   glLoadMatrixf(@CameraMatrix.Matrix);
 
   glLoadMatrixf(@CameraMatrix.Matrix);
   glTranslatef (-PointOfRotation.X, -PointOfRotation.y, -PointOfRotation.Z);
+
   glTranslatef (-PointOfRotation.X,
 +
                -PointOfRotation.y,
 +
                -PointOfRotation.Z);
 
end;
 
end;
  
Zeile 266: Zeile 348:
 
begin
 
begin
 
   if not Enabled then
 
   if not Enabled then
     exit;<br><br>
+
     exit;
 +
 
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
 +
  // für das Terrain nur die Drehung ausführen
 
   glLoadMatrixf(@CameraMatrix.Matrix);
 
   glLoadMatrixf(@CameraMatrix.Matrix);
 +
  // deswegen jetzt die verschiebung zurücknehmen
 
   glTranslatef (CameraMatrix.InverseMatrix[12],
 
   glTranslatef (CameraMatrix.InverseMatrix[12],
 
                 CameraMatrix.InverseMatrix[13],
 
                 CameraMatrix.InverseMatrix[13],
Zeile 276: Zeile 361:
 
procedure TCamera.RotateMatrix (ix, iy, iz: TGLdouble);
 
procedure TCamera.RotateMatrix (ix, iy, iz: TGLdouble);
 
begin
 
begin
   RotateRoundAxis (iy, ix, iz);
+
  // um den austausch von verschiedenen kameramodulen so einfach
 +
  // wie möglich zu gestalten, kann hier ein bisschen variiert werden...
 +
   RotateRoundAxis (-iy, -ix, -iz);
 
end;
 
end;
  
function TCamera.Position: TGLvector;
+
function TCamera.GetPosition: TGLvector;
// mit dieser Funktion kann die aktuelle Position der Kamera  
+
// diese Property-Funktion fragt die aktuelle Position der Kamera ab
// abgefragt werden
 
 
var
 
var
 
   return: TGLvector;
 
   return: TGLvector;
Zeile 290: Zeile 376:
 
               CameraMatrix.Matrix[13],
 
               CameraMatrix.Matrix[13],
 
               CameraMatrix.Matrix[14]);
 
               CameraMatrix.Matrix[14]);
 +
  return := AddVector (return, FPositionOffset);
 
   result := return;
 
   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;
 
end;
  
Zeile 318: Zeile 414:
 
               CameraMatrix.Matrix[05],
 
               CameraMatrix.Matrix[05],
 
               CameraMatrix.Matrix[06]);
 
               CameraMatrix.Matrix[06]);
result := return;
+
  result := return;
 
end;
 
end;
  
 
procedure TCamera.Adjust;
 
procedure TCamera.Adjust;
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhängig
+
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhaengig
 
// von Drehung und Position, senkrecht zur Y-Achse ausgerichtet
 
// von Drehung und Position, senkrecht zur Y-Achse ausgerichtet
 
// werden. Die aktuelle Position wird dabei beibehalten.
 
// werden. Die aktuelle Position wird dabei beibehalten.
Zeile 328: Zeile 424:
 
   temp: TArrMatrix;
 
   temp: TArrMatrix;
 
begin
 
begin
  // kamera senkrecht zur Y-Achse ausrichten
+
   temp := GetIdentity(temp);
  // position beibehalten
 
   temp := GetArrIdentity;
 
 
   temp[12] := CameraMatrix.Matrix[12];
 
   temp[12] := CameraMatrix.Matrix[12];
 
   temp[13] := CameraMatrix.Matrix[13];
 
   temp[13] := CameraMatrix.Matrix[13];

Version vom 13. November 2005, 13:53 Uhr

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.