Kamera (2): Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K (Der Ausdruck ''<pascal>(.*?)</pascal>'' wurde ersetzt mit ''<source lang="pascal">$1</source>''.)
 
(19 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
 +
Eine Beschreibung findet ihr unter [[Kamera (1)]].<br>
 +
Die verwendete Toolsammlung findet ihr unter [[Kamera (3)]].
 +
 +
<source lang="pascal">
 +
{******************************************************************}
 +
(*
 +
  Released as part of the camera example program
 +
  Visit us @ http://www.delphigl.com
 +
  Maintained by Florian Sievert (Phobeus)
 +
  Phobeus@DelphiGL.com
 +
-------------------------------------------------------------------
 +
  License
 +
  -------
 +
  Copyright (C) 2007 Andree Heyroth
 +
 +
  This license applies to everything on www.delphigl.com, except where
 +
  otherwise noted.
 +
 +
  This software is provided 'as-is', without any express or implied warranty.
 +
  In no event will the authors be held liable for any damages arising from
 +
  the use of this software.
 +
 +
  Permission is granted to anyone to use this software for any purpose,
 +
  including commercial applications, and to alter it and redistribute it
 +
  freely, subject to the following restrictions:
 +
 +
  1. The origin of this software must not be misrepresented; you must not
 +
    claim that you wrote the original software. If you use this software in a
 +
    product, an acknowledgment in the product documentation would be
 +
    appreciated but is not required.
 +
  2. Altered source versions must be plainly marked as such, and must not be
 +
    misrepresented as being the original software.
 +
  3. This notice may not be removed or altered from any source distribution.
 +
 +
  Description
 +
  -----------
 +
  Component containing methods and properties which are useful for moving and
 +
  rotating an existing scene in any dimension (yaw, pitcxh and roll/vertical,
 +
  horizontal and depth).
 +
 +
  Requirements
 +
  ------------
 +
  - SDL 1.2 (http://www.libsdl.org/download-1.2.php or your distribution)
 +
  - Borland Delphi version 5.0 and above
 +
 +
  History
 +
  -------
 +
  see below (unfortunately in german)
 +
 +
  Contact
 +
  -------
 +
  I created this source code in my spare time. If you find a bug or just
 +
  wanna say hello, feel free to write to the contributor:
 +
 +
    Andree Heyroth (AndyH)
 +
    email : heyroth@syncro-concept.de
 +
    Visit: http://www.delphigl.com
 +
 +
*******************************************************************************)
 +
 +
// Wie ich selber immer wieder merken muss, ist eine Software anscheinend
 +
// nie fertig. Immer moechte man etwas verbessern oder neue Features
 +
// einbauen.
 +
// Sollte also jemand Änderungs- oder Verbesserungsvorschläge haben oder
 +
// neue Ideen einbringen wollen, oder sollten Verstaendnisfragen bestehen,
 +
// so mailt bitte an
 +
//
 +
// heyroth@syncro-concept.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.
 +
//
 +
// 23.11.2005
 +
// Neues Flag (FFixedAxis) zur Entscheidung zwischen angepasster und realer
 +
// Drehung. Dadurch wird das Bewegungsmodell angepasst. Siehe Methode Offset,
 +
// bzw. UpdateMatrixOffset.
 +
//
 +
// 10.12.2005
 +
// Property PointOfRotation ist jetzt nicht mehr ReadOnly.
 +
// Der Blickpunkt bzw. Rotationspunkt kann jetzt direkt verändert werden.
 +
// So kann eine neue Blickrichtung angenommen werden ohne vorher die Kamera-
 +
// eigenschaften zu sichern und nach einem PositionCamera wieder zu setzen.
 +
//
 +
// 19.12.2005
 +
// Neue Methode PositionCross.
 +
// Die Methode PositionCross schiebt das Fadenkreuz in den angegebenen Punkt
 +
// ohne die Lage der Szene zu verändern. Diese Funktionalitaet ist anders als
 +
// ein neues Setzen der Property FPointOfRotation weil dabei die Szene in das
 +
// Koordinatenkreuz geschoben wird.
 +
//
 +
// 31.01.2006
 +
// Umstellung auf genaueres Zahlenformat
 +
// Da die Darstellung von Szenen ausserhalb des Bereichs von FLOAT-Zahlen
 +
// nicht funktioniert, muss alles auf DOUBLE umgestellt werden. Dazu gehört
 +
// das Ersetzen von Datentypen und der Aufruf der entsprechenden GL-Funktionen.
 +
//
 +
// 24.09.2007
 +
// Um den Einstieg zu erleichtern habe ich die ganzen Steuerfunktionen in die
 +
// Kamera verlegt. Die "normalen" Funktionen wie Drehen und Verschieben werden
 +
// jetzt durch Weitergabe der Tastendrücke und Mauseigenschaften an die
 +
// Kamera abgearbeitet. Die Auswertung erfolgt dort. Sollten andere Tasten
 +
// als die von mir vorgesehenen gewünscht sein, muss dies in der Kamera
 +
// geändert werden.
 +
 
unit Camera;
 
unit Camera;
 +
 
interface
 
interface
Uses DglOpenGL, OpenGLUtil, Windows, Classes;<br>
+
 
type  
+
  Uses
 +
    DglOpenGL,
 +
    Util,
 +
    Windows,
 +
    Classes,
 +
    Messages,
 +
    Controls;
 +
 
 +
  type
 
   TCameraMatrix=Class
 
   TCameraMatrix=Class
 +
    StackMatrix: array [0..9] of TArrMatrix;
 +
    StackCtr: integer;
 
     Matrix: TArrMatrix;
 
     Matrix: TArrMatrix;
 
     InverseMatrix: TArrMatrix;
 
     InverseMatrix: TArrMatrix;
 +
    constructor Create;
 +
    //destructor destroy;
 
     procedure Identity;
 
     procedure Identity;
 +
    procedure Push;
 +
    procedure Pop;
 
     procedure Load(M: TArrMatrix);
 
     procedure Load(M: TArrMatrix);
    constructor Create;
+
   end;
   end;<br>
+
 
 
   TCamera=Class
 
   TCamera=Class
    CameraMatrix: TCameraMatrix;
+
     Enabled: boolean;
     Enabled : boolean;
 
    Initiated: boolean;
 
    constructor Create;
 
    destructor Destroy;
 
    function Position: TGLvector;
 
 
     function UpVector: TGLvector;
 
     function UpVector: TGLvector;
    function ViewDirection: TGLvector;
 
 
     procedure RestorePosition(pos: integer);
 
     procedure RestorePosition(pos: integer);
 
     procedure SavePosition(pos: integer);
 
     procedure SavePosition(pos: integer);
     procedure RotateCamera(ix, iy, iz: TGLdouble);
+
    function GiveStoredPosition(pos: integer): TGLvector;
     procedure TranslateCamera(ix, iy, iz: TGLdouble);
+
     procedure RotateCamera(ix, iy, iz: GLdouble);
 +
     procedure TranslateCamera(ix, iy, iz: GLdouble);
 
     procedure CameraHome;
 
     procedure CameraHome;
 
     procedure PositionCamera(PositionVec: TGLvector; ViewVec: TGLvector; upVec: TGLvector);
 
     procedure PositionCamera(PositionVec: TGLvector; ViewVec: TGLvector; upVec: TGLvector);
 
     procedure Adjust;
 
     procedure Adjust;
 
     procedure Apply;
 
     procedure Apply;
 +
    procedure ApplyForTerrain;
 
   private
 
   private
 +
    // Dreh- und Bewegungsrichtung festlegen (in Mousedown).
 +
    // Pfeil hoch  : Szene nach oben
 +
    // Pfeil runter: Szene nach unten
 +
    // Pfeil rechts: Szene nach rechts
 +
    // Pfeil links : Szene nach links
 +
    // Page Up    : Szene in den Bildschirm schieben (verkleinern)
 +
    // Page Down  : Szene aus dem Bildschirm schieben (vergrößern)
 +
    //
 +
    // Wird FDir auf true gesetzt ist die Tastenfunktion jeweils umgekehrt.
 +
    // so verschiebt sich entweder die szene oder das objekt.
 +
    FDir: boolean;
 +
    FPosition: TGLvector;
 +
    FViewDirection: TGLvector;
 +
 +
    FAltPressed,
 +
    FShiftPressed,
 +
    FCtrlPressed: boolean;
 +
 +
    FRightMousePressed: boolean;
 +
    FLeftMousePressed: boolean;
 +
 +
    FSpeedMultiplier: double;
 +
 +
    FxStart,                  // berechnung der mausbewegungen.
 +
    FxDelta,                  // delta = aktuelle position minus start
 +
    FyStart,                  // start wird erfasst sobald eine maustaste
 +
    FyDelta:integer;          // gedrückt wird.
 +
    FxRot,                    // umrechnung von delta in rotation wenn
 +
    FyRot:double;            // drehung gewünscht ist (je nach taste)
 +
 +
    FPointOfRotation: TGLvector;
 
     HomeMatrix: TCameraMatrix;
 
     HomeMatrix: TCameraMatrix;
 +
    CameraMatrix: TCameraMatrix;
 +
    Initiated: boolean;
 
     PosArray: array [0..9] of TCameraMatrix;
 
     PosArray: array [0..9] of TCameraMatrix;
     FPointOfRotation: TGLvector;
+
     RotArray: array [0..9] of TGLvector;
 +
    FFixedAxis: boolean;
 +
    procedure Debug (Text: string);
 +
    function UpdateMatrixOffset(newMatrix: TArrMatrix): TArrMatrix;
 +
    function GetViewDirection: TGLvector;
 +
    procedure SetViewDirection (View: TGLvector);
 +
    procedure Initiate;
 +
    function GetPosition: TGLvector;
 +
    procedure SetPosition (Pos: TGLvector);
 
     procedure Identity;
 
     procedure Identity;
     procedure RotateMatrix(ix, iy, iz: TGLdouble);
+
     procedure Offset(x, y, z: GLdouble);
     procedure Offset(x, y, z: TGLfloat);
+
    procedure RotateRoundAxis(rx, ry, rz: GLdouble);
     procedure RotateRoundAxis(rx, ry, rz: TGLfloat);
+
    procedure SetPointOfRotation (NewPoint: TGLvector);
 +
    function ProcessArrowKeys (Key: integer; Shift: TShiftState): boolean;
 +
    function GiveSpeed: TGLdouble;
 +
    procedure RotateScene(Direction: byte; Geschwindigkeit: GLdouble);
 +
    procedure MoveScene (Direction: byte; Geschwindigkeit: GLdouble);
 +
  public
 +
    constructor Create;
 +
    destructor Destroy;override;
 +
    function InverseMatrix: TArrMatrix;
 +
    procedure ApplyInvers;
 +
    procedure PositionCross(CrossVec: TGLvector);
 +
    function HandleKeyboardInput (Key: Word; Shift: TShiftState): boolean;
 +
     procedure KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
 +
    function KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState): boolean;
 +
    procedure MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 +
     procedure MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
 +
    function MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer): boolean;
 +
    function MouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean): boolean;
 +
    procedure MouseEnter(var msg:TMessage);
 +
    procedure MouseLeave(var msg: TMessage);
 
   published
 
   published
     property PointOfRotation: TGLvector read FPointOfRotation;
+
     property PointOfRotation: TGLvector read FPointOfRotation write SetPointOfRotation;
 +
    property Position: TGLvector read GetPosition write SetPosition;
 +
    property ViewDirection: TGLvector read GetViewDirection write SetViewDirection;
 +
    property FixedAxis: boolean read FFixedAxis write FFixedAxis;
 +
    property SpeedMultiplier: double read FSpeedMultiplier write FSpeedMultiplier;
 
   end;
 
   end;
   TPCamera=^TCamera;<br>
+
   TPCamera=^TCamera;
implementation<br>
+
 
 +
var
 +
  FDebugFile: Textfile;
 +
  FDebugFileName: string;
 +
  FDebugOn: boolean;
 +
 
 +
const
 +
  VK_0 = $30;
 +
  VK_1 = $31;
 +
  VK_2 = $32;
 +
  VK_3 = $33;
 +
  VK_4 = $34;
 +
  VK_5 = $35;
 +
  VK_6 = $36;
 +
  VK_7 = $37;
 +
  VK_8 = $38;
 +
  VK_9 = $39;
 +
 
 +
  VK_A = $41;
 +
  VK_B = $42;
 +
  VK_C = $43;
 +
  VK_D = $44;
 +
  VK_E = $45;
 +
  VK_F = $46;
 +
  VK_G = $47;
 +
  VK_H = $48;
 +
  VK_I = $49;
 +
  VK_J = $4A;
 +
  VK_K = $4B;
 +
  VK_L = $4C;
 +
  VK_M = $4D;
 +
  VK_N = $4E;
 +
  VK_O = $4F;
 +
  VK_P = $50;
 +
  VK_Q = $51;
 +
  VK_R = $52;
 +
  VK_S = $53;
 +
  VK_T = $54;
 +
  VK_U = $55;
 +
  VK_V = $56;
 +
  VK_W = $57;
 +
  VK_X = $58;
 +
  VK_Y = $59;
 +
  VK_Z = $5A;
 +
 
 +
  // Standard-Keycodes für Tastatureingaben definieren
 +
  CKeyCameraHome = VK_0;
 +
  CKeyCameraAdjust = VK_A;
 +
 
 +
  DIR_HORIZONTAL: byte = 1;
 +
  DIR_VERTIKAL: byte = 2;
 +
  DIR_ZENTRAL: byte = 3;
 +
 
 +
implementation
 +
 
 +
uses SysUtils;
 +
 
 
constructor TCameraMatrix.Create;
 
constructor TCameraMatrix.Create;
// beim erzeugen der Matrix wird die Identitätsmatrix zugewiesen
 
 
begin
 
begin
   Identity;
+
   inherited create;
end;<br>
+
  StackCtr := 0;
 +
end;
 +
 
 +
procedure TCameraMatrix.Push;
 +
begin
 +
  if (StackCtr > -1) and (StackCtr < 10) then begin
 +
    StackMatrix[StackCtr] := Matrix;
 +
    inc (StackCtr);
 +
  end;
 +
end;
 +
 
 +
procedure TCameraMatrix.Pop;
 +
begin
 +
  if (StackCtr > 0) and (StackCtr < 11) then begin
 +
    dec (StackCtr);
 +
    Load (StackMatrix[StackCtr]);
 +
  end;
 +
end;
 +
 
 
procedure TCameraMatrix.Identity;
 
procedure TCameraMatrix.Identity;
// die Funktion GetArrIdentity findet Ihr in der Datei OpenGL.pas
+
// GetIdentity: 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;<br>
+
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;
Zeile 60: Zeile 333:
 
   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.
 +
  // immer zum Benutzer auszurichten
 
   InvertMatrix (M, InverseMatrix);
 
   InvertMatrix (M, InverseMatrix);
end;<br>
+
end;
procedure TCamera.RotateRoundAxis(rx, ry, rz: TGLfloat);
+
 
 +
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;
 +
 
 +
  // standardmaessig immer entlang der bildschirmachsen verschieben
 +
  FFixedAxis := true;
 +
 
 +
  // flags für die tasten alt, ctrl und shift initialisieren
 +
  FAltPressed := false;
 +
  FCtrlPressed := false;
 +
  FShiftPressed := false;
 +
 
 +
  // normalerweise wird die geschwindigkeit nicht verändert
 +
  FSpeedMultiplier := 1.0;
 +
 
 +
  // normale bildschirmbewegung
 +
  FDir := false;
 +
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: GLdouble);
 
// hier drehen wir jetzt um die einzelnen Achsen.
 
// hier drehen wir jetzt um die einzelnen Achsen.
 
// die Parameter geben die "Drehgeschwindigkeit" vor.
 
// die Parameter geben die "Drehgeschwindigkeit" vor.
 
var
 
var
 
   newMatrix: TArrMatrix;
 
   newMatrix: TArrMatrix;
 +
  tempX, tempY, tempZ: TGLvector;
 
begin
 
begin
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
 
   glPushMatrix();
 
   glPushMatrix();
 +
 
   // aktuelle Position und Lage der Kamera herstellen
 
   // aktuelle Position und Lage der Kamera herstellen
   glLoadMatrixf(@CameraMatrix.Matrix);
+
   glLoadMatrixd(@CameraMatrix.Matrix);
 +
 
 +
  if FFixedAxis then begin
 +
    // über die bildschirmachsen drehen
 +
    tempX := GetMatrixX (CameraMatrix.InverseMatrix);
 +
    tempY := GetMatrixY (CameraMatrix.InverseMatrix);
 +
    tempZ := GetMatrixZ (CameraMatrix.InverseMatrix);
 +
    // wenn gewuenscht um die X-Achse drehen
 +
    if(rx <> 0) then
 +
      glRotated(rx,tempX.X,tempX.Y,tempX.Z);
  
  // wenn gewünscht um die X-Achse drehen
+
    // wenn gewuenscht um die Y-Achse drehen
  if(rx <> 0) then
+
    if(ry <> 0) then
    glRotatef(rx,1,0,0);
+
      glRotated(ry,tempY.X,tempY.Y,tempY.Z);
  
  // wenn gewünscht um die Y-Achse drehen
+
    // wenn gewuenscht um die Z-Achse drehen
  if(ry <> 0) then
+
    if(rz <> 0) then
     glRotatef(ry,0,1,0);
+
      glRotated(rz,tempZ.X,tempZ.Y,tempZ.Z);
 +
  end
 +
  else begin
 +
    // über die achsen des koordinatenkreuzes drehen
 +
     // wenn gewuenscht um die X-Achse drehen
 +
    if(rx <> 0) then
 +
      glRotated(rx,1,0,0);
  
  // wenn gewünscht um die Z-Achse drehen
+
    // wenn gewuenscht um die Y-Achse drehen
  if(rz <> 0) then
+
    if(ry <> 0) then
    glRotatef(rz,0,0,1);
+
      glRotated(ry,0,1,0);
 +
 
 +
    // wenn gewuenscht um die Z-Achse drehen
 +
    if(rz <> 0) then
 +
      glRotated(rz,0,0,1);
 +
  end;
  
 
   // die neu erzeugte Matrix auslesen
 
   // die neu erzeugte Matrix auslesen
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
+
   glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);
 +
 
 +
  glPopMatrix();
 +
 
 
   // und in die Kameramatrix sichern
 
   // und in die Kameramatrix sichern
 
   CameraMatrix.Load(newMatrix);
 
   CameraMatrix.Load(newMatrix);
  glPopMatrix();
+
end;
end;<br>
+
 
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;<br><br>
 
 
procedure TCamera.Identity;
 
procedure TCamera.Identity;
 
begin
 
begin
Zeile 112: Zeile 446:
  
 
   Enabled := true;
 
   Enabled := true;
end;<br>
+
end;
destructor TCamera.Destroy;
+
 
var
+
procedure TCamera.Offset(x, y, z: GLdouble);
  i: integer;
+
// verschieben der Kamera auf einer beliebigen Achse
begin
 
  HomeMatrix.Free;
 
  CameraMatrix.Free;
 
  for i := 0 to 9 do
 
    PosArray[i].Free;
 
end;<br>
 
procedure TCamera.Offset(x, y, z: TGLfloat);
 
 
var
 
var
 
   newMatrix: TArrMatrix;
 
   newMatrix: TArrMatrix;
 +
  //OldView: TGLvector;
 
begin
 
begin
 +
  Debug ('- Offset - Start --------------------------------------------------');
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
 +
 
   glPushMatrix();
 
   glPushMatrix();
 
   glLoadIdentity;
 
   glLoadIdentity;
   glTranslatef(x,y,z);
+
   glTranslated(x,y,z);
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
+
   glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);
   // wenn ich mit der funktion glMultMatrixf arbeite, wird die zeichnung
+
   glPopMatrix();
  // immer entlang der richtigen achsen verschoben. wenn ich matrixmultiply
+
 
   // nehme, wird sie auf den bildschirmachsen verschoben. das ist angenehmer
+
   Debug ('Position: '+GLvectorToText (GetMatrixPos (newMatrix)));
   // zum arbeiten
+
   newMatrix := UpdateMatrixOffset (newMatrix);
  MatrixMultiply (newMatrix, CameraMatrix.Matrix, newMatrix);
 
 
   CameraMatrix.Load(newMatrix);
 
   CameraMatrix.Load(newMatrix);
   glPopMatrix();
+
   Debug ('- Offset - Ende- --------------------------------------------------');
end;<br>
+
end;
 +
 
 
procedure TCamera.PositionCamera(PositionVec: TGLvector;
 
procedure TCamera.PositionCamera(PositionVec: TGLvector;
 
                          ViewVec: TGLvector;
 
                          ViewVec: TGLvector;
Zeile 145: Zeile 475:
 
   newMatrix: TArrMatrix;
 
   newMatrix: TArrMatrix;
 
   i: integer;
 
   i: integer;
 +
  P, V, U: TGLvector;
 +
  Laenge: GLdouble;
 
begin
 
begin
 +
  Debug ('- PositionCamera - Start ------------------------------------------');
 +
  // 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;
  FPointOfRotation := ViewVec;
 
  
 
   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);
+
   glGetDoublev(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 zurueck zur kameraposition
 +
  Offset (0, 0, -Laenge);
 +
 +
  // alle positionsspeicher mit der Kameraposition, Blickrichtung
 +
  // und dem upVector belegen. Nur beim ersten Aufruf von
 +
  // PositionCamera
 
   if not Initiated then
 
   if not Initiated then
   begin
+
    Initiate;
    for i := 0 to 9 do
+
 
      SavePosition (i);
+
  FPointOfRotation := ViewVec;
    Initiated := true;
+
   Debug ('PointOfRotation: '+GLvectorToText (FPointOfRotation));
   end;
+
  Debug ('- PositionCamera - Ende -------------------------------------------');
end;<br>
+
end;
 +
 
 +
procedure TCamera.PositionCross (CrossVec: TGLvector);
 +
// diese prozedur verschiebt, im gegensatz zu einem verändern von
 +
// PointOfRotation, das Koordinatenkreuz und nicht die Szene.
 +
var
 +
  newMatrix: TArrMatrix;
 +
  PosDiff: TGLvector;
 +
begin
 +
  Debug ('- PositionCross - Start -------------------------------------------');
 +
 
 +
  PosDiff := SubtractVector (FPointOfRotation, CrossVec);
 +
 
 +
  // Szene in das koordinatenkreuz verschieben
 +
  FPointOfRotation := CrossVec;
 +
 
 +
  // jetzt die Szene wieder um den gleichen betrag zurückverschieben
 +
  // das sieht dann so aus, als ob das koordinatenkreuz verschoben
 +
  // worden wäre
 +
  // zuerst die aktuelle neue Situation herstellen
 +
  // (mit neuem FPointOfRotation)
 +
  Apply;
 +
 
 +
  // jetzt um den Differenzbetrag des alten und neuen
 +
  // Rotationspunkts zurück verschieben
 +
  glTranslated (-PosDiff.X, 0, 0);
 +
  glTranslated (0, -PosDiff.Y, 0);
 +
  glTranslated (0, 0, -PosDiff.Z);
 +
 
 +
  // jetzt vom neuen Rotationspunktes zurück ins Zentrum, damit beim
 +
  // nächsten Apply das glTranslatef (-FPointOfRotation, ...) klappt
 +
  glTranslated (CrossVec.X, 0, 0);
 +
  glTranslated (0, CrossVec.Y, 0);
 +
  glTranslated (0, 0, CrossVec.Z);
 +
 
 +
  // aktuelle Matrix holen...
 +
  glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);
 +
 
 +
  // und als Kameramatrix abspeichern
 +
  CameraMatrix.Load(newmatrix);
 +
   Debug ('- PositionCross - Ende --------------------------------------------');
 +
end;
 +
 
 
procedure TCamera.CameraHome;
 
procedure TCamera.CameraHome;
// Move the camera to the home position
+
// Kamera in die beim letzten Aufruf von PositionCamera uebergebene
 +
// Position/Lage bringen
 
begin
 
begin
 
   CameraMatrix.Load(HomeMatrix.Matrix);
 
   CameraMatrix.Load(HomeMatrix.Matrix);
end;<br>
+
end;
 +
 
 
procedure TCamera.SavePosition (pos: integer);
 
procedure TCamera.SavePosition (pos: integer);
 +
// wie der Prozedurname schon sagt...
 
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;<br>
+
  RotArray[pos] := FPointOfRotation;
 +
end;
 +
 
 
procedure TCamera.RestorePosition (pos: integer);
 
procedure TCamera.RestorePosition (pos: integer);
 +
// wie der Prozedurname schon sagt...
 
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;<br>
+
  FPointOfRotation := RotArray[pos];
procedure TCamera.TranslateCamera(ix, iy, iz: TGLdouble);
+
end;
 +
 
 +
function TCamera.GiveStoredPosition (pos: integer): TGLvector;
 +
// gibt den Inhalt des durch pos bestimmten
 +
// Positionsspecihers zurueck
 +
begin
 +
  if (pos < 0) or (pos > 9) then
 +
    exit;
 +
 
 +
  result := GetMatrixPos (PosArray[pos].Matrix);
 +
end;
 +
 
 +
procedure TCamera.TranslateCamera(ix, iy, iz: GLdouble);
 +
// vom Benutzer aufzurufende Methode um eine Verschiebung
 +
// durchzufuehren
 
begin
 
begin
 
   Offset (ix, iy, iz);
 
   Offset (ix, iy, iz);
end;<br>
+
end;
procedure TCamera.RotateCamera(ix, iy, iz: TGLdouble);
+
 
 +
procedure TCamera.RotateCamera(ix, iy, iz: GLdouble);
 +
// vom Benutzer aufzurufende Methode um eine Drehung
 +
// durchzufuehren
 
begin
 
begin
   RotateMatrix (ix, iy, iz);
+
   RotateRoundAxis (-iy, -ix, -iz);
end;<br>
+
end;
 +
 
 
procedure TCamera.Apply;
 
procedure TCamera.Apply;
 +
// hier wird die Kamera eingeschaltet. Nach dem Aufruf dieser Prozedur
 +
// sollte die Szene mit allen benoetigten Drehungen, Verschiebungen
 +
// 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);
+
   glLoadMatrixd(@CameraMatrix.Matrix);
   glTranslatef (-PointOfRotation.X,
+
   glTranslated (-FPointOfRotation.X,
                 -PointOfRotation.y,
+
                 -FPointOfRotation.y,
                 -PointOfRotation.Z);
+
                 -FPointOfRotation.Z);
end;<br>
+
end;
procedure TCamera.RotateMatrix (ix, iy, iz: TGLdouble);
+
 
 +
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.
 +
var
 +
  pos: TGLvector;
 
begin
 
begin
   RotateRoundAxis (iy, ix, iz);
+
   if not Enabled then
end;<br>
+
    exit;
function TCamera.Position: TGLvector;
+
 
 +
  glMatrixMode (GL_MODELVIEW);
 +
  // für das Terrain nur die Drehung ausführen
 +
  glLoadMatrixd(@CameraMatrix.Matrix);
 +
  // deswegen jetzt die verschiebung zurücknehmen
 +
  Pos := GetMatrixPos (CameraMatrix.InverseMatrix);
 +
  glTranslated (Pos.X, Pos.Y, Pos.Z);
 +
end;
 +
 
 +
function TCamera.GetPosition: TGLvector;
 +
// diese Property-Funktion fragt die aktuelle Position der Kamera ab
 +
begin
 +
  // position: letzte Spalte der Matrix
 +
  result := AddVector (GetMatrixPos (CameraMatrix.InverseMatrix), FPointOfRotation);
 +
end;
 +
 
 +
procedure TCamera.SetPosition (Pos: TGLvector);
 +
// diese Property-Funktion setzt eine neue Position der Kamera
 
var
 
var
   return: TGLvector;
+
   m: TArrMatrix;
 
begin
 
begin
 
   // position: letzte Spalte der Matrix
 
   // position: letzte Spalte der Matrix
   InitVector (return,
+
   m := CameraMatrix.Matrix;
              CameraMatrix.Matrix[12],
+
  SetMatrixPos (m, SubtractVector (Pos, FPointOfRotation));
              CameraMatrix.Matrix[13],
+
  CameraMatrix.Load (m);
              CameraMatrix.Matrix[14]);
+
end;
  result := return;
+
 
end;<br>
+
function TCamera.GetViewDirection: TGLvector;
function TCamera.ViewDirection: TGLvector;
+
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
 +
// abgefragt werden
 
var
 
var
 
   return: TGLvector;
 
   return: TGLvector;
 
begin
 
begin
 
   // view direction: dritte Spalte der Matrix (Z-Achse)
 
   // view direction: dritte Spalte der Matrix (Z-Achse)
   InitVector (return,
+
   result := GetMatrixZ (CameraMatrix.InverseMatrix);
              CameraMatrix.Matrix[08],
+
end;
              CameraMatrix.Matrix[09],
+
 
              CameraMatrix.Matrix[10]);
+
procedure TCamera.SetViewDirection (View: TGLvector);
   result := return;
+
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
end;<br>
+
// gesetzt werden
 +
begin
 +
   // view direction: dritte Spalte der Matrix (Z-Achse)
 +
  SetMatrixZ (CameraMatrix.InverseMatrix, View);
 +
end;
 +
 
 
function TCamera.UpVector: TGLvector;
 
function TCamera.UpVector: TGLvector;
 +
// mit dieser Funktion kann die aktuelle Ausrichtung der Kamera
 +
// abgefragt werden
 
var
 
var
 
   return: TGLvector;
 
   return: TGLvector;
 
begin
 
begin
 
   // upVector: zweite Spalte der Matrix (Y-Achse)
 
   // upVector: zweite Spalte der Matrix (Y-Achse)
   InitVector (return,
+
   result := GetMatrixY (CameraMatrix.InverseMatrix);
              CameraMatrix.Matrix[04],
+
end;
              CameraMatrix.Matrix[05],
+
 
              CameraMatrix.Matrix[06]);
 
result := return;
 
end;<br>
 
 
procedure TCamera.Adjust;
 
procedure TCamera.Adjust;
 +
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhaengig
 +
// von Drehung und Position, zur Y-Achse ausgerichtet werden.
 +
// Die aktuelle Position wird dabei beibehalten.
 +
var
 +
  m: TArrMatrix;
 +
  v: TGLvector;
 +
begin
 +
  // position aus der aktuellen cameramatrix holen
 +
  v := GetMatrixPos (CameraMatrix.Matrix);
 +
  // m mit identitätsmatrix initialisieren
 +
  m := GetIdentity(m);
 +
  // die position aus der aktuellen cameramatrix in m speichern
 +
  SetMatrixPos (m, v);
 +
  // m als aktuelle cameramatrix speichern
 +
  CameraMatrix.Load(m);
 +
end;
 +
 +
function TCamera.InverseMatrix: TArrMatrix;
 +
begin
 +
  result := CameraMatrix.InverseMatrix;
 +
end;
 +
 +
procedure TCamera.Initiate;
 +
var
 +
  i: integer;
 +
begin
 +
  for i := 0 to 9 do
 +
    SavePosition (i);
 +
  Initiated := true;
 +
end;
 +
 +
function TCamera.UpdateMatrixOffset (newMatrix: TArrMatrix): TArrMatrix;
 +
begin
 +
  // wenn ich mit Multiply (FixedAxis) arbeite, wird die zeichnung immer
 +
  // entlang der bildschirmachsen verschoben. wenn ich Multiply, version 2
 +
  // nehme, wird sie auf den errechneten achsen verschoben.
 +
  if FFixedAxis then begin
 +
    result := Multiply (newMatrix, CameraMatrix.Matrix);
 +
  end
 +
  else begin
 +
    result := Multiply (CameraMatrix.Matrix, newMatrix);
 +
  end;
 +
end;
 +
 +
procedure TCamera.SetPointOfRotation (NewPoint: TGLvector);
 +
// setzt den viewpoint oder rotationpoint  ohne die anderen parameter zu
 +
// veraendern. so kann man z.B. eine Kamerafahrt in immer der gleichen
 +
// position simulieren.
 +
begin
 +
  FPointOfRotation := NewPoint;
 +
end;
 +
 +
procedure TCamera.Debug (Text: string);
 +
begin
 +
  if not FDebugOn then
 +
    exit;
 +
  writeln (FDebugFile, DateToStr (date) + ' | ' +
 +
                      TimeToStr (time) + ' | ' +
 +
                      Text);
 +
end;
 +
 +
procedure TCamera.ApplyInvers;
 +
var
 +
  M: TArrMatrix;
 +
begin
 +
  // Um Objekte immer zum benutzer ausrichten, darf nur die drehung angewendet
 +
  // werden und nicht die verschiebung. Verschiebung wird hier zurückgenommen.
 +
  // Vorher muss die Kamera angewendet und die nötigen Verschiebungen/
 +
  // Drehungen ausgeführt werden.
 +
  M := InverseMatrix;
 +
  M[12] := 0;
 +
  M[13] := 0;
 +
  M[14] := 0;
 +
  M[15] := 1;
 +
  glMultMatrixd(@M);
 +
end;
 +
 +
function TCamera.HandleKeyboardInput (Key: Word; Shift: TShiftState): boolean;
 +
// diese funktion sollte zur steuerung des bildschirmablaufs gewählt werden,
 +
// wenn nicht auf die tasteneingabe gewartet wird (bildschirmaufbau nach
 +
// timer-event).
 +
// in der Timermethode zB.:
 +
// if FCamera.HandleDeviceInput then
 +
//  Render;  // oder wie die routine bei euch heißt ...
 +
begin
 +
  result := false;
 +
 +
  if not FAltPressed then FAltPressed := ssAlt in Shift;
 +
  if not FCtrlPressed then FCtrlPressed := ssCtrl in Shift;
 +
  // das mit Key=16 ist ´ne krücke, aber sendmessage mit VK_SHIFT klappt
 +
  // bei mir nicht!
 +
  if not FShiftPressed then FShiftPressed := (ssShift in Shift) or (Key = 16);
 +
 +
  case Key of
 +
    CKeyCameraHome:          // (ausgangsposition)
 +
    begin
 +
      if not (FAltPressed or FCtrlPressed or FShiftPressed) then begin
 +
        CameraHome;
 +
        result := true;
 +
      end;
 +
    end;
 +
 +
    CKeyCameraAdjust:
 +
    begin                    // (ausrichten)
 +
      Adjust;
 +
      result := true;
 +
    end;
 +
 +
    VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT:
 +
      result := ProcessArrowKeys (Key, Shift);
 +
  else                        // case
 +
    if (Key > $29) and (Key < $40) then begin  // 0 - 9
 +
      if FAltPressed then begin                // position speichern?
 +
        SavePosition(Key-48);
 +
        result := true;
 +
      end;
 +
      if FShiftPressed then begin
 +
        RestorePosition(Key-48);
 +
        result := true;
 +
      end;
 +
    end;
 +
  end;
 +
 +
 +
end;
 +
 +
function TCamera.ProcessArrowKeys (Key: integer; Shift: TShiftState): boolean;
 +
// arbeitet die für die jeweiligen kameraaktionen definierten tastendrücke
 +
// ab. hierbei werden wie gewünscht die kameraeigenschaften geändert:
 +
// lage, position, ...
 +
var
 +
  POR: TGLVector;
 +
  nDir: double;
 +
begin
 +
  result := false;
 +
 +
  nDir := 1.0*FSpeedMultiplier;
 +
  if FDir then
 +
    nDir := -1.0;
 +
 +
  case Key of
 +
  VK_UP:            // Szene aus dem Drehpunkt entlang der vertikalen
 +
  begin            // verschieben
 +
    if ssShift in Shift then begin
 +
      RotateScene (DIR_HORIZONTAL, GiveSpeed*-1.0);
 +
    end else begin
 +
      POR := PointOfRotation;
 +
      POR.Y := POR.Y + nDir;
 +
      PointOfRotation := POR;
 +
    end;
 +
    result := true;
 +
  end;
 +
  VK_DOWN:          // Szene aus dem Drehpunkt entlang der vertikalen
 +
  begin            // verschieben
 +
    if ssShift in Shift then begin
 +
      RotateScene (DIR_HORIZONTAL, GiveSpeed);
 +
    end else begin
 +
      POR := PointOfRotation;
 +
      POR.Y := POR.Y - nDir;
 +
      PointOfRotation := POR;
 +
    end;
 +
    result := true;
 +
  end;
 +
  VK_LEFT:          // Szene aus dem Drehpunkt entlang der horizontalen
 +
  begin            // verschieben
 +
    if ssShift in Shift then begin
 +
      RotateScene (DIR_VERTIKAL, GiveSpeed*-1.0);
 +
    end else begin
 +
      POR := PointOfRotation;
 +
      POR.X := POR.X - nDir;
 +
      PointOfRotation := POR;
 +
    end;
 +
    result := true;
 +
  end;
 +
  VK_RIGHT:        // Szene aus dem Drehpunkt entlang der horizontalen
 +
  begin            // verschieben
 +
    if ssShift in Shift then begin
 +
      RotateScene (DIR_VERTIKAL, GiveSpeed);
 +
    end else begin
 +
      POR := PointOfRotation;
 +
      POR.X := POR.X + nDir;
 +
      PointOfRotation := POR;
 +
    end;
 +
    result := true;
 +
  end;
 +
  VK_PRIOR:        // Szene aus dem Drehpunkt entlang der Bildschirmachse
 +
  begin            // verschieben
 +
    if ssShift in Shift then begin
 +
      RotateScene (DIR_ZENTRAL, GiveSpeed*-1.0);
 +
    end else begin
 +
      POR := PointOfRotation;
 +
      POR.Z := POR.Z - nDir;
 +
      PointOfRotation := POR;
 +
    end;
 +
    result := true;
 +
  end;
 +
  VK_NEXT:          // Szene aus dem Drehpunkt entlang der Bildschirmachse
 +
  begin            // verschieben
 +
    if ssShift in Shift then begin
 +
      RotateScene (DIR_ZENTRAL, GiveSpeed);
 +
    end else begin
 +
      POR := PointOfRotation;
 +
      POR.Z := POR.Z + nDir;
 +
      PointOfRotation := POR;
 +
    end;
 +
    result := true;
 +
  end;
 +
  end;
 +
end;
 +
 +
procedure TCamera.KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
 +
// überprüfen ob eine sondertaste wieder losgelassen wurde
 +
begin
 +
  if not (ssAlt in Shift) then
 +
    FAltPressed := false;
 +
  if not (ssCtrl in Shift) then
 +
    FCtrlPressed := false;
 +
  if not (ssAlt in Shift) then
 +
    FShiftPressed := false;
 +
end;
 +
 +
function TCamera.KeyDown(Sender: TObject; var Key: Word;
 +
  Shift: TShiftState): boolean;
 
var
 
var
   temp: TArrMatrix;
+
   POR: TGLVector;
 +
begin
 +
  // hier werden die aktionen für die angezeigten tastenbelegungen
 +
  // umgesetzt
 +
  result := false;
 +
 
 +
  // wie bei den maustasten: merken, ob eine sondertaste gedrückt
 +
  // wurde um feststellen zu können ob diese gehalten wird. in
 +
  // KeyUp wird der zustand entsprechend zurückgesetzt.
 +
  if not FAltPressed then FAltPressed := ssAlt in Shift;
 +
  if not FCtrlPressed then FCtrlPressed := ssCtrl in Shift;
 +
  if not FShiftPressed then FShiftPressed := (ssShift in Shift);
 +
 
 +
  case key of
 +
  ord ('A'):        // Szene zum Benutzer ausrichten. Position wird
 +
  begin            // nicht verändert
 +
    Adjust;
 +
    result := true;
 +
  end;
 +
  VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT:
 +
    result := ProcessArrowKeys (Key, Shift);
 +
  ord('0'):        // den Drehpunkt in den Nullpunkt schieben. Die
 +
  begin            // Kameraposition bleibt unverändert.
 +
    InitVector (POR, 0, 0, 0);
 +
    PointOfRotation := POR;
 +
    result := true;
 +
  end;
 +
  ord('I'):        // Dreh- und Bewegungsrichtung umschalten
 +
  begin
 +
    FDir := not FDir;
 +
    result := true;
 +
  end;
 +
  ord('U'):        // Umschalten zwischen Drehung/Bewegung entlang der
 +
  begin            // angezeigten Achsen bzw. Bildschirmachsen
 +
    FixedAxis := not FixedAxis;
 +
    result := true;
 +
  end;
 +
  end;
 +
end;
 +
 
 +
procedure TCamera.MouseDown(Sender: TObject; Button: TMouseButton;
 +
                            Shift: TShiftState; X, Y: Integer);
 +
// hier merken wir uns, welche Maustaste gedrückt wurde. Solange die
 +
// Tastenvariable true ist, kann abgeprüft werden ob die Taste gedrückt
 +
// ist. In MouseUp wird die entsprechende Variable wieder auf false
 +
// gesetzt.
 
begin
 
begin
   // kamera senkrecht zur Y-Achse ausrichten
+
   if Button = mbRight then begin
   // position beibehalten
+
    //rechte maustaste gedrückt
   temp := GetArrIdentity;
+
    FRightMousePressed:=true;
   temp[12] := CameraMatrix.Matrix[12];
+
  end else if Button = mbLeft then begin
   temp[13] := CameraMatrix.Matrix[13];
+
    //rechte maustaste gedrückt
   temp[14] := CameraMatrix.Matrix[14];
+
    FLeftMousePressed:=true;
   temp[15] := CameraMatrix.Matrix[15];
+
  end;
   CameraMatrix.Load(temp);
+
  //Startposition für die Mausbewegung merken
end;<br><br>
+
  FxStart := X;
 +
  FyStart := Y;
 +
end;
 +
 
 +
procedure TCamera.MouseUp(Sender: TObject; Button: TMouseButton;
 +
                          Shift: TShiftState; X, Y: Integer);
 +
// eine maustaste wurde losgelassen. mal sehen, ob eine aktion
 +
// unterbunden werden muss.
 +
begin
 +
  if Button = mbRight then begin
 +
    FRightMousePressed:=false;
 +
  end else if Button = mbLeft then begin
 +
    FLeftMousePressed:=false;
 +
  end;
 +
end;
 +
 
 +
function TCamera.MouseMove(Sender: TObject; Shift: TShiftState; X,
 +
                          Y: Integer): boolean;
 +
begin
 +
  result := false;
 +
  //Verschieben und Drehen funktioniert nur, wenn eine Maustaste
 +
  // gedrückt ist
 +
  if not (FRightMousePressed or FLeftMousePressed) then
 +
    exit;
 +
 
 +
  //berechnen, wie weit die Maus verschoben wurde
 +
  FxDelta := FxStart-X;
 +
  FyDelta := FyStart-Y;
 +
 
 +
  //anpassen, damit es nicht zu schnell geht
 +
  FxRot := FxRot - FyDelta;
 +
  Fyrot := FyRot - FxDelta;
 +
 
 +
  //Startpunkt für den nächsten Aufruf festlegen
 +
  FxStart := X;
 +
  FyStart := Y;
 +
 
 +
  // Rechte Maustaste:
 +
  // Kamera drehen (vertikal bei Maus hoch/runter,
 +
  //                horizontal bei Maus links/rechts
 +
  //                um die Bildschirmachse bei Mausrad oder wenn shift)
 +
  if FRightMousePressed then begin
 +
    if FShiftPressed then begin
 +
      // wenn kein mausrad vorhanden ist, soll das ja auch gehen
 +
      if FyRot+FxRot <> 0 then begin
 +
        RotateScene (DIR_ZENTRAL, (FyRot+FxRot)*GiveSpeed);
 +
      end;
 +
    end
 +
    else begin
 +
      if FxRot <> 0 then begin
 +
        RotateScene (DIR_HORIZONTAL, FxRot*GiveSpeed);
 +
      end;
 +
      if FyRot <> 0 then begin
 +
        RotateScene (DIR_VERTIKAL, FyRot*GiveSpeed);
 +
      end;
 +
    end;
 +
  end;
 +
 
 +
  // Linke Maustaste:
 +
  // Kamera bewegen (vertikal bei Maus hoch/runter,
 +
  //                horizontal bei Maus links/rechts
 +
  //                entlang der Bildschirmachse bei Mausrad oder wenn shift)
 +
  if FLeftMousePressed then begin
 +
    if FShiftPressed then begin
 +
      // wenn kein mausrad vorhanden ist, soll das ja auch gehen
 +
      if FyRot+FxRot <> 0 then begin
 +
        MoveScene (DIR_ZENTRAL, (FyRot+FxRot)*GiveSpeed);
 +
      end
 +
    end
 +
    else begin
 +
      if FxRot <> 0 then begin
 +
        MoveScene (DIR_HORIZONTAL, FxRot*GiveSpeed);
 +
      end;
 +
      if FyRot <> 0 then begin
 +
        MoveScene (DIR_VERTIKAL, -FyRot*GiveSpeed);
 +
      end;
 +
    end;
 +
  end;
 +
 
 +
  FxRot := 0;
 +
  FyRot := 0;
 +
 
 +
  result := true;
 +
end;
 +
 
 +
procedure TCamera.RotateScene (Direction: byte;
 +
                              Geschwindigkeit: GLdouble);
 +
begin
 +
  if Direction = DIR_HORIZONTAL then // um die X-Achse drehenx
 +
  begin
 +
    RotateCamera(0, Geschwindigkeit, 0);
 +
  end
 +
  else if Direction = DIR_VERTIKAL then // um die y-Achse drehen
 +
  begin
 +
    RotateCamera (Geschwindigkeit, 0, 0);
 +
  end
 +
  else if Direction = DIR_ZENTRAL then // um die z-Achse drehen
 +
  begin
 +
    RotateCamera(0, 0, Geschwindigkeit);
 +
  end;
 +
end;
 +
 
 +
function TCamera.GiveSpeed: TGLdouble;
 +
begin
 +
  // Initiale Geschwindigkeit
 +
  result := 1;
 +
 
 +
  // wenn die Geschwindigkeit für eine Mausbewegung oder -drehung ist, soll
 +
   // es nicht so schnell gehen
 +
   if FRightMousePressed or FLeftMousePressed then begin
 +
    result := result / 20;
 +
  end;
 +
 
 +
  // wenn die alt-taste gedrückt wurde, soll es ja schneller gehen
 +
   if FAltPressed then begin
 +
    result := (result * 60.0);
 +
  end;
 +
 
 +
  // jetzt mit dem individuellen speemultiplier multiplizieren
 +
  result := result * FSpeedMultiplier;
 +
 
 +
  // wenn die drehrichtung umgeschaltet wurde, hier auch
 +
  // umschalten
 +
  if FDir then begin
 +
    result := -result;
 +
   end;
 +
end;
 +
 
 +
procedure TCamera.MoveScene (Direction: byte; Geschwindigkeit: GLdouble);
 +
begin
 +
  if Direction = DIR_VERTIKAL then begin
 +
    TranslateCamera (Geschwindigkeit, 0, 0);
 +
  end;
 +
  if Direction = DIR_HORIZONTAL then begin
 +
    TranslateCamera (0, Geschwindigkeit, 0);
 +
  end;
 +
  if Direction = DIR_ZENTRAL then begin
 +
    TranslateCamera (0, 0, Geschwindigkeit);
 +
  end;
 +
end;
 +
 
 +
function TCamera.MouseWheel(Sender: TObject; Shift: TShiftState;
 +
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean): boolean;
 +
begin
 +
  if FRightMousePressed then begin
 +
    RotateScene (DIR_ZENTRAL, (WheelDelta/10)*GiveSpeed);
 +
  end
 +
  else begin
 +
    MoveScene (DIR_ZENTRAL, (WheelDelta/360)*GiveSpeed);
 +
  end;
 +
   result := true;
 +
end;
 +
 
 +
procedure TCamera.MouseEnter(var msg:TMessage);
 +
begin
 +
  FRightMousePressed:=false;
 +
   FLeftMousePressed:=false;
 +
end;
 +
 
 +
procedure TCamera.MouseLeave(var msg: TMessage);
 +
begin
 +
  FRightMousePressed:=false;
 +
  FLeftMousePressed:=false;
 +
  FAltPressed := false;
 +
  FCtrlPressed := false;
 +
  FShiftPressed := false;
 +
end;
 +
 
 +
Initialization
 +
  FDebugOn := false; // auf true setzen, wenn debug infos gewünscht sind
 +
   FDebugFileName := ExePath + 'SKANAL3D_CAMERA.DBG';
 +
  if FDebugOn then begin
 +
    AssignFile (FDebugFile, FDebugFileName);
 +
    Rewrite (FDebugFile);
 +
  end;
 +
 
 +
finalization
 +
  if FDebugOn then begin
 +
    CloseFile (FDebugFile);
 +
    FDebugFileName := '';
 +
  end;
 +
 
 
end.
 
end.
 +
</source>
 +
[[Kategorie:Anleitung]]

Aktuelle Version vom 10. März 2009, 20:05 Uhr

Eine Beschreibung findet ihr unter Kamera (1).
Die verwendete Toolsammlung findet ihr unter Kamera (3).

{******************************************************************}
(*
  Released as part of the camera example program
  Visit us @ http://www.delphigl.com
  Maintained by Florian Sievert (Phobeus)
  Phobeus@DelphiGL.com
-------------------------------------------------------------------
  License
  -------
  Copyright (C) 2007 Andree Heyroth

  This license applies to everything on www.delphigl.com, except where
  otherwise noted.

  This software is provided 'as-is', without any express or implied warranty.
  In no event will the authors be held liable for any damages arising from
  the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software in a
     product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Description
  -----------
  Component containing methods and properties which are useful for moving and
  rotating an existing scene in any dimension (yaw, pitcxh and roll/vertical,
  horizontal and depth).

  Requirements
  ------------
  - SDL 1.2 (http://www.libsdl.org/download-1.2.php or your distribution)
  - Borland Delphi version 5.0 and above

  History
  -------
  see below (unfortunately in german)

  Contact
  -------
  I created this source code in my spare time. If you find a bug or just
  wanna say hello, feel free to write to the contributor:

    Andree Heyroth (AndyH)
    email : heyroth@syncro-concept.de
    Visit: http://www.delphigl.com

*******************************************************************************)

// Wie ich selber immer wieder merken muss, ist eine Software anscheinend
// nie fertig. Immer moechte man etwas verbessern oder neue Features
// einbauen.
// Sollte also jemand Änderungs- oder Verbesserungsvorschläge haben oder
// neue Ideen einbringen wollen, oder sollten Verstaendnisfragen bestehen,
// so mailt bitte an
//
// heyroth@syncro-concept.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.
//
// 23.11.2005
// Neues Flag (FFixedAxis) zur Entscheidung zwischen angepasster und realer
// Drehung. Dadurch wird das Bewegungsmodell angepasst. Siehe Methode Offset,
// bzw. UpdateMatrixOffset.
//
// 10.12.2005
// Property PointOfRotation ist jetzt nicht mehr ReadOnly.
// Der Blickpunkt bzw. Rotationspunkt kann jetzt direkt verändert werden.
// So kann eine neue Blickrichtung angenommen werden ohne vorher die Kamera-
// eigenschaften zu sichern und nach einem PositionCamera wieder zu setzen.
//
// 19.12.2005
// Neue Methode PositionCross.
// Die Methode PositionCross schiebt das Fadenkreuz in den angegebenen Punkt
// ohne die Lage der Szene zu verändern. Diese Funktionalitaet ist anders als
// ein neues Setzen der Property FPointOfRotation weil dabei die Szene in das
// Koordinatenkreuz geschoben wird.
//
// 31.01.2006
// Umstellung auf genaueres Zahlenformat
// Da die Darstellung von Szenen ausserhalb des Bereichs von FLOAT-Zahlen
// nicht funktioniert, muss alles auf DOUBLE umgestellt werden. Dazu gehört
// das Ersetzen von Datentypen und der Aufruf der entsprechenden GL-Funktionen.
//
// 24.09.2007
// Um den Einstieg zu erleichtern habe ich die ganzen Steuerfunktionen in die
// Kamera verlegt. Die "normalen" Funktionen wie Drehen und Verschieben werden
// jetzt durch Weitergabe der Tastendrücke und Mauseigenschaften an die
// Kamera abgearbeitet. Die Auswertung erfolgt dort. Sollten andere Tasten
// als die von mir vorgesehenen gewünscht sein, muss dies in der Kamera
// geändert werden.

unit Camera;

interface

  Uses
    DglOpenGL,
    Util,
    Windows,
    Classes,
    Messages,
    Controls;

  type
  TCameraMatrix=Class
    StackMatrix: array [0..9] of TArrMatrix;
    StackCtr: integer;
    Matrix: TArrMatrix;
    InverseMatrix: TArrMatrix;
    constructor Create;
    //destructor destroy;
    procedure Identity;
    procedure Push;
    procedure Pop;
    procedure Load(M: TArrMatrix);
  end;

  TCamera=Class
    Enabled: boolean;
    function UpVector: TGLvector;
    procedure RestorePosition(pos: integer);
    procedure SavePosition(pos: integer);
    function GiveStoredPosition(pos: integer): TGLvector;
    procedure RotateCamera(ix, iy, iz: GLdouble);
    procedure TranslateCamera(ix, iy, iz: GLdouble);
    procedure CameraHome;
    procedure PositionCamera(PositionVec: TGLvector; ViewVec: TGLvector; upVec: TGLvector);
    procedure Adjust;
    procedure Apply;
    procedure ApplyForTerrain;
  private
    // Dreh- und Bewegungsrichtung festlegen (in Mousedown).
    // Pfeil hoch  : Szene nach oben
    // Pfeil runter: Szene nach unten
    // Pfeil rechts: Szene nach rechts
    // Pfeil links : Szene nach links
    // Page Up     : Szene in den Bildschirm schieben (verkleinern)
    // Page Down   : Szene aus dem Bildschirm schieben (vergrößern)
    //
    // Wird FDir auf true gesetzt ist die Tastenfunktion jeweils umgekehrt.
    // so verschiebt sich entweder die szene oder das objekt.
    FDir: boolean;
    FPosition: TGLvector;
    FViewDirection: TGLvector;

    FAltPressed,
    FShiftPressed,
    FCtrlPressed: boolean;

    FRightMousePressed: boolean;
    FLeftMousePressed: boolean;

    FSpeedMultiplier: double;

    FxStart,                  // berechnung der mausbewegungen.
    FxDelta,                  // delta = aktuelle position minus start
    FyStart,                  // start wird erfasst sobald eine maustaste
    FyDelta:integer;          // gedrückt wird.
    FxRot,                    // umrechnung von delta in rotation wenn
    FyRot:double;             // drehung gewünscht ist (je nach taste)

    FPointOfRotation: TGLvector;
    HomeMatrix: TCameraMatrix;
    CameraMatrix: TCameraMatrix;
    Initiated: boolean;
    PosArray: array [0..9] of TCameraMatrix;
    RotArray: array [0..9] of TGLvector;
    FFixedAxis: boolean;
    procedure Debug (Text: string);
    function UpdateMatrixOffset(newMatrix: TArrMatrix): TArrMatrix;
    function GetViewDirection: TGLvector;
    procedure SetViewDirection (View: TGLvector);
    procedure Initiate;
    function GetPosition: TGLvector;
    procedure SetPosition (Pos: TGLvector);
    procedure Identity;
    procedure Offset(x, y, z: GLdouble);
    procedure RotateRoundAxis(rx, ry, rz: GLdouble);
    procedure SetPointOfRotation (NewPoint: TGLvector);
    function ProcessArrowKeys (Key: integer; Shift: TShiftState): boolean;
    function GiveSpeed: TGLdouble;
    procedure RotateScene(Direction: byte; Geschwindigkeit: GLdouble);
    procedure MoveScene (Direction: byte; Geschwindigkeit: GLdouble);
  public
    constructor Create;
    destructor Destroy;override;
    function InverseMatrix: TArrMatrix;
    procedure ApplyInvers;
    procedure PositionCross(CrossVec: TGLvector);
    function HandleKeyboardInput (Key: Word; Shift: TShiftState): boolean;
    procedure KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    function KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState): boolean;
    procedure MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    function MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer): boolean;
    function MouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean): boolean;
    procedure MouseEnter(var msg:TMessage);
    procedure MouseLeave(var msg: TMessage);
  published
    property PointOfRotation: TGLvector read FPointOfRotation write SetPointOfRotation;
    property Position: TGLvector read GetPosition write SetPosition;
    property ViewDirection: TGLvector read GetViewDirection write SetViewDirection;
    property FixedAxis: boolean read FFixedAxis write FFixedAxis;
    property SpeedMultiplier: double read FSpeedMultiplier write FSpeedMultiplier;
  end;
  TPCamera=^TCamera;

var
  FDebugFile: Textfile;
  FDebugFileName: string;
  FDebugOn: boolean;

const
  VK_0 = $30;
  VK_1 = $31;
  VK_2 = $32;
  VK_3 = $33;
  VK_4 = $34;
  VK_5 = $35;
  VK_6 = $36;
  VK_7 = $37;
  VK_8 = $38;
  VK_9 = $39;

  VK_A = $41;
  VK_B = $42;
  VK_C = $43;
  VK_D = $44;
  VK_E = $45;
  VK_F = $46;
  VK_G = $47;
  VK_H = $48;
  VK_I = $49;
  VK_J = $4A;
  VK_K = $4B;
  VK_L = $4C;
  VK_M = $4D;
  VK_N = $4E;
  VK_O = $4F;
  VK_P = $50;
  VK_Q = $51;
  VK_R = $52;
  VK_S = $53;
  VK_T = $54;
  VK_U = $55;
  VK_V = $56;
  VK_W = $57;
  VK_X = $58;
  VK_Y = $59;
  VK_Z = $5A;

  // Standard-Keycodes für Tastatureingaben definieren
  CKeyCameraHome = VK_0;
  CKeyCameraAdjust = VK_A;

  DIR_HORIZONTAL: byte = 1;
  DIR_VERTIKAL: byte = 2;
  DIR_ZENTRAL: byte = 3;

implementation

uses SysUtils;

constructor TCameraMatrix.Create;
begin
  inherited create;
  StackCtr := 0;
end;

procedure TCameraMatrix.Push;
begin
  if (StackCtr > -1) and (StackCtr < 10) then begin
    StackMatrix[StackCtr] := Matrix;
    inc (StackCtr);
  end;
end;

procedure TCameraMatrix.Pop;
begin
  if (StackCtr > 0) and (StackCtr < 11) then begin
    dec (StackCtr);
    Load (StackMatrix[StackCtr]);
  end;
end;

procedure TCameraMatrix.Identity;
// GetIdentity: 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;

  // standardmaessig immer entlang der bildschirmachsen verschieben
  FFixedAxis := true;

  // flags für die tasten alt, ctrl und shift initialisieren
  FAltPressed := false;
  FCtrlPressed := false;
  FShiftPressed := false;

  // normalerweise wird die geschwindigkeit nicht verändert
  FSpeedMultiplier := 1.0;

  // normale bildschirmbewegung
  FDir := false;
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: GLdouble);
// hier drehen wir jetzt um die einzelnen Achsen.
// die Parameter geben die "Drehgeschwindigkeit" vor.
var
  newMatrix: TArrMatrix;
  tempX, tempY, tempZ: TGLvector;
begin
  glMatrixMode (GL_MODELVIEW);
  glPushMatrix();

  // aktuelle Position und Lage der Kamera herstellen
  glLoadMatrixd(@CameraMatrix.Matrix);

  if FFixedAxis then begin
    // über die bildschirmachsen drehen
    tempX := GetMatrixX (CameraMatrix.InverseMatrix);
    tempY := GetMatrixY (CameraMatrix.InverseMatrix);
    tempZ := GetMatrixZ (CameraMatrix.InverseMatrix);
    // wenn gewuenscht um die X-Achse drehen
    if(rx <> 0) then
      glRotated(rx,tempX.X,tempX.Y,tempX.Z);

    // wenn gewuenscht um die Y-Achse drehen
    if(ry <> 0) then
      glRotated(ry,tempY.X,tempY.Y,tempY.Z);

    // wenn gewuenscht um die Z-Achse drehen
    if(rz <> 0) then
      glRotated(rz,tempZ.X,tempZ.Y,tempZ.Z);
  end
  else begin
    // über die achsen des koordinatenkreuzes drehen
    // wenn gewuenscht um die X-Achse drehen
    if(rx <> 0) then
      glRotated(rx,1,0,0);

    // wenn gewuenscht um die Y-Achse drehen
    if(ry <> 0) then
      glRotated(ry,0,1,0);

    // wenn gewuenscht um die Z-Achse drehen
    if(rz <> 0) then
      glRotated(rz,0,0,1);
  end;

  // die neu erzeugte Matrix auslesen
  glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);

  glPopMatrix();

  // und in die Kameramatrix sichern
  CameraMatrix.Load(newMatrix);
end;

procedure TCamera.Identity;
begin
  CameraMatrix.Identity;
  HomeMatrix.Identity;

  Enabled := true;
end;

procedure TCamera.Offset(x, y, z: GLdouble);
// verschieben der Kamera auf einer beliebigen Achse
var
  newMatrix: TArrMatrix;
  //OldView: TGLvector;
begin
  Debug ('- Offset - Start --------------------------------------------------');
  glMatrixMode (GL_MODELVIEW);

  glPushMatrix();
  glLoadIdentity;
  glTranslated(x,y,z);
  glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);
  glPopMatrix();

  Debug ('Position: '+GLvectorToText (GetMatrixPos (newMatrix)));
  newMatrix := UpdateMatrixOffset (newMatrix);
  CameraMatrix.Load(newMatrix);
  Debug ('- Offset - Ende- --------------------------------------------------');
end;

procedure TCamera.PositionCamera(PositionVec: TGLvector;
			                           ViewVec: TGLvector;
			                           upVec: TGLvector);
var
  newMatrix: TArrMatrix;
  i: integer;
  P, V, U: TGLvector;
  Laenge: GLdouble;
begin
  Debug ('- PositionCamera - Start ------------------------------------------');
  // 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;

  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);
  glGetDoublev(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 zurueck zur kameraposition
  Offset (0, 0, -Laenge);

  // alle positionsspeicher mit der Kameraposition, Blickrichtung
  // und dem upVector belegen. Nur beim ersten Aufruf von
  // PositionCamera
  if not Initiated then
    Initiate;

  FPointOfRotation := ViewVec;
  Debug ('PointOfRotation: '+GLvectorToText (FPointOfRotation));
  Debug ('- PositionCamera - Ende -------------------------------------------');
end;

procedure TCamera.PositionCross (CrossVec: TGLvector);
// diese prozedur verschiebt, im gegensatz zu einem verändern von
// PointOfRotation, das Koordinatenkreuz und nicht die Szene.
var
  newMatrix: TArrMatrix;
  PosDiff: TGLvector;
begin
  Debug ('- PositionCross - Start -------------------------------------------');

  PosDiff := SubtractVector (FPointOfRotation, CrossVec);

  // Szene in das koordinatenkreuz verschieben
  FPointOfRotation := CrossVec;

  // jetzt die Szene wieder um den gleichen betrag zurückverschieben
  // das sieht dann so aus, als ob das koordinatenkreuz verschoben
  // worden wäre
  // zuerst die aktuelle neue Situation herstellen
  // (mit neuem FPointOfRotation)
  Apply;

  // jetzt um den Differenzbetrag des alten und neuen
  // Rotationspunkts zurück verschieben
  glTranslated (-PosDiff.X, 0, 0);
  glTranslated (0, -PosDiff.Y, 0);
  glTranslated (0, 0, -PosDiff.Z);

  // jetzt vom neuen Rotationspunktes zurück ins Zentrum, damit beim
  // nächsten Apply das glTranslatef (-FPointOfRotation, ...) klappt
  glTranslated (CrossVec.X, 0, 0);
  glTranslated (0, CrossVec.Y, 0);
  glTranslated (0, 0, CrossVec.Z);

  // aktuelle Matrix holen...
  glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);

  // und als Kameramatrix abspeichern
  CameraMatrix.Load(newmatrix);
  Debug ('- PositionCross - Ende --------------------------------------------');
end;

procedure TCamera.CameraHome;
// Kamera in die beim letzten Aufruf von PositionCamera uebergebene
// 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);
  RotArray[pos] := FPointOfRotation;
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);
  FPointOfRotation := RotArray[pos];
end;

function TCamera.GiveStoredPosition (pos: integer): TGLvector;
// gibt den Inhalt des durch pos bestimmten
// Positionsspecihers zurueck
begin
  if (pos < 0) or (pos > 9) then
    exit;

  result := GetMatrixPos (PosArray[pos].Matrix);
end;

procedure TCamera.TranslateCamera(ix, iy, iz: GLdouble);
// vom Benutzer aufzurufende Methode um eine Verschiebung
// durchzufuehren
begin
  Offset (ix, iy, iz);
end;

procedure TCamera.RotateCamera(ix, iy, iz: GLdouble);
// vom Benutzer aufzurufende Methode um eine Drehung
// durchzufuehren
begin
  RotateRoundAxis (-iy, -ix, -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);
  glLoadMatrixd(@CameraMatrix.Matrix);
  glTranslated (-FPointOfRotation.X,
                -FPointOfRotation.y,
                -FPointOfRotation.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.
var
  pos: TGLvector;
begin
  if not Enabled then
    exit;

  glMatrixMode (GL_MODELVIEW);
  // für das Terrain nur die Drehung ausführen
  glLoadMatrixd(@CameraMatrix.Matrix);
  // deswegen jetzt die verschiebung zurücknehmen
  Pos := GetMatrixPos (CameraMatrix.InverseMatrix);
  glTranslated (Pos.X, Pos.Y, Pos.Z);
end;

function TCamera.GetPosition: TGLvector;
// diese Property-Funktion fragt die aktuelle Position der Kamera ab
begin
  // position: letzte Spalte der Matrix
  result := AddVector (GetMatrixPos (CameraMatrix.InverseMatrix), FPointOfRotation);
end;

procedure TCamera.SetPosition (Pos: TGLvector);
// diese Property-Funktion setzt eine neue Position der Kamera
var
  m: TArrMatrix;
begin
  // position: letzte Spalte der Matrix
  m := CameraMatrix.Matrix;
  SetMatrixPos (m, SubtractVector (Pos, FPointOfRotation));
  CameraMatrix.Load (m);
end;

function TCamera.GetViewDirection: TGLvector;
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
// abgefragt werden
var
  return: TGLvector;
begin
  // view direction: dritte Spalte der Matrix (Z-Achse)
  result := GetMatrixZ (CameraMatrix.InverseMatrix);
end;

procedure TCamera.SetViewDirection (View: TGLvector);
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
// gesetzt werden
begin
  // view direction: dritte Spalte der Matrix (Z-Achse)
  SetMatrixZ (CameraMatrix.InverseMatrix, View);
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)
  result := GetMatrixY (CameraMatrix.InverseMatrix);
end;

procedure TCamera.Adjust;
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhaengig
// von Drehung und Position, zur Y-Achse ausgerichtet werden.
// Die aktuelle Position wird dabei beibehalten.
var
  m: TArrMatrix;
  v: TGLvector;
begin
  // position aus der aktuellen cameramatrix holen
  v := GetMatrixPos (CameraMatrix.Matrix);
  // m mit identitätsmatrix initialisieren
  m := GetIdentity(m);
  // die position aus der aktuellen cameramatrix in m speichern
  SetMatrixPos (m, v);
  // m als aktuelle cameramatrix speichern
  CameraMatrix.Load(m);
end;

function TCamera.InverseMatrix: TArrMatrix;
begin
  result := CameraMatrix.InverseMatrix;
end;

procedure TCamera.Initiate;
var
  i: integer;
begin
  for i := 0 to 9 do
    SavePosition (i);
  Initiated := true;
end;

function TCamera.UpdateMatrixOffset (newMatrix: TArrMatrix): TArrMatrix;
begin
  // wenn ich mit Multiply (FixedAxis) arbeite, wird die zeichnung immer
  // entlang der bildschirmachsen verschoben. wenn ich Multiply, version 2
  // nehme, wird sie auf den errechneten achsen verschoben.
  if FFixedAxis then begin
    result := Multiply (newMatrix, CameraMatrix.Matrix);
  end
  else begin
    result := Multiply (CameraMatrix.Matrix, newMatrix);
  end;
end;

procedure TCamera.SetPointOfRotation (NewPoint: TGLvector);
// setzt den viewpoint oder rotationpoint  ohne die anderen parameter zu
// veraendern. so kann man z.B. eine Kamerafahrt in immer der gleichen
// position simulieren.
begin
  FPointOfRotation := NewPoint;
end;

procedure TCamera.Debug (Text: string);
begin
  if not FDebugOn then
    exit;
  writeln (FDebugFile, DateToStr (date) + ' | ' +
                       TimeToStr (time) + ' | ' +
                       Text);
end;

procedure TCamera.ApplyInvers;
var
  M: TArrMatrix;
begin
  // Um Objekte immer zum benutzer ausrichten, darf nur die drehung angewendet
  // werden und nicht die verschiebung. Verschiebung wird hier zurückgenommen.
  // Vorher muss die Kamera angewendet und die nötigen Verschiebungen/
  // Drehungen ausgeführt werden.
  M := InverseMatrix;
  M[12] := 0;
  M[13] := 0;
  M[14] := 0;
  M[15] := 1;
  glMultMatrixd(@M);
end;

function TCamera.HandleKeyboardInput (Key: Word; Shift: TShiftState): boolean;
// diese funktion sollte zur steuerung des bildschirmablaufs gewählt werden,
// wenn nicht auf die tasteneingabe gewartet wird (bildschirmaufbau nach
// timer-event).
// in der Timermethode zB.:
// if FCamera.HandleDeviceInput then
//   Render;  // oder wie die routine bei euch heißt ...
begin
  result := false;

  if not FAltPressed then FAltPressed := ssAlt in Shift;
  if not FCtrlPressed then FCtrlPressed := ssCtrl in Shift;
  // das mit Key=16 ist ´ne krücke, aber sendmessage mit VK_SHIFT klappt
  // bei mir nicht!
  if not FShiftPressed then FShiftPressed := (ssShift in Shift) or (Key = 16);

  case Key of
    CKeyCameraHome:           // (ausgangsposition)
    begin
      if not (FAltPressed or FCtrlPressed or FShiftPressed) then begin
        CameraHome;
        result := true;
      end;
    end;

    CKeyCameraAdjust:
    begin                     // (ausrichten)
      Adjust;
      result := true;
    end;

    VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT:
      result := ProcessArrowKeys (Key, Shift);
  else                        // case
    if (Key > $29) and (Key < $40) then begin   // 0 - 9
      if FAltPressed then begin                 // position speichern?
        SavePosition(Key-48);
        result := true;
      end;
      if FShiftPressed then begin
        RestorePosition(Key-48);
        result := true;
      end;
    end;
  end;


end;

function TCamera.ProcessArrowKeys (Key: integer; Shift: TShiftState): boolean;
// arbeitet die für die jeweiligen kameraaktionen definierten tastendrücke
// ab. hierbei werden wie gewünscht die kameraeigenschaften geändert:
// lage, position, ...
var
  POR: TGLVector;
  nDir: double;
begin
  result := false;

  nDir := 1.0*FSpeedMultiplier;
  if FDir then
    nDir := -1.0;

  case Key of
  VK_UP:            // Szene aus dem Drehpunkt entlang der vertikalen
  begin             // verschieben
    if ssShift in Shift then begin
      RotateScene (DIR_HORIZONTAL, GiveSpeed*-1.0);
    end else begin
      POR := PointOfRotation;
      POR.Y := POR.Y + nDir;
      PointOfRotation := POR;
    end;
    result := true;
  end;
  VK_DOWN:          // Szene aus dem Drehpunkt entlang der vertikalen
  begin             // verschieben
    if ssShift in Shift then begin
      RotateScene (DIR_HORIZONTAL, GiveSpeed);
    end else begin
      POR := PointOfRotation;
      POR.Y := POR.Y - nDir;
      PointOfRotation := POR;
    end;
    result := true;
  end;
  VK_LEFT:          // Szene aus dem Drehpunkt entlang der horizontalen
  begin             // verschieben
    if ssShift in Shift then begin
      RotateScene (DIR_VERTIKAL, GiveSpeed*-1.0);
    end else begin
      POR := PointOfRotation;
      POR.X := POR.X - nDir;
      PointOfRotation := POR;
    end;
    result := true;
  end;
  VK_RIGHT:         // Szene aus dem Drehpunkt entlang der horizontalen
  begin             // verschieben
    if ssShift in Shift then begin
      RotateScene (DIR_VERTIKAL, GiveSpeed);
    end else begin
      POR := PointOfRotation;
      POR.X := POR.X + nDir;
      PointOfRotation := POR;
    end;
    result := true;
  end;
  VK_PRIOR:         // Szene aus dem Drehpunkt entlang der Bildschirmachse
  begin             // verschieben
    if ssShift in Shift then begin
      RotateScene (DIR_ZENTRAL, GiveSpeed*-1.0);
    end else begin
      POR := PointOfRotation;
      POR.Z := POR.Z - nDir;
      PointOfRotation := POR;
    end;
    result := true;
  end;
  VK_NEXT:          // Szene aus dem Drehpunkt entlang der Bildschirmachse
  begin             // verschieben
    if ssShift in Shift then begin
      RotateScene (DIR_ZENTRAL, GiveSpeed);
    end else begin
      POR := PointOfRotation;
      POR.Z := POR.Z + nDir;
      PointOfRotation := POR;
    end;
    result := true;
  end;
  end;
end;

procedure TCamera.KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
// überprüfen ob eine sondertaste wieder losgelassen wurde
begin
  if not (ssAlt in Shift) then
    FAltPressed := false;
  if not (ssCtrl in Shift) then
    FCtrlPressed := false;
  if not (ssAlt in Shift) then
    FShiftPressed := false;
end;

function TCamera.KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState): boolean;
var
  POR: TGLVector;
begin
  // hier werden die aktionen für die angezeigten tastenbelegungen
  // umgesetzt
  result := false;

  // wie bei den maustasten: merken, ob eine sondertaste gedrückt
  // wurde um feststellen zu können ob diese gehalten wird. in
  // KeyUp wird der zustand entsprechend zurückgesetzt.
  if not FAltPressed then FAltPressed := ssAlt in Shift;
  if not FCtrlPressed then FCtrlPressed := ssCtrl in Shift;
  if not FShiftPressed then FShiftPressed := (ssShift in Shift);

  case key of
  ord ('A'):        // Szene zum Benutzer ausrichten. Position wird
  begin             // nicht verändert
    Adjust;
    result := true;
  end;
  VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT:
    result := ProcessArrowKeys (Key, Shift);
  ord('0'):         // den Drehpunkt in den Nullpunkt schieben. Die
  begin             // Kameraposition bleibt unverändert.
    InitVector (POR, 0, 0, 0);
    PointOfRotation := POR;
    result := true;
  end;
  ord('I'):         // Dreh- und Bewegungsrichtung umschalten
  begin
    FDir := not FDir;
    result := true;
  end;
  ord('U'):         // Umschalten zwischen Drehung/Bewegung entlang der
  begin             // angezeigten Achsen bzw. Bildschirmachsen
    FixedAxis := not FixedAxis;
    result := true;
  end;
  end;
end;

procedure TCamera.MouseDown(Sender: TObject; Button: TMouseButton;
                            Shift: TShiftState; X, Y: Integer);
// hier merken wir uns, welche Maustaste gedrückt wurde. Solange die
// Tastenvariable true ist, kann abgeprüft werden ob die Taste gedrückt
// ist. In MouseUp wird die entsprechende Variable wieder auf false
// gesetzt.
begin
  if Button = mbRight then begin
    //rechte maustaste gedrückt
    FRightMousePressed:=true;
  end else if Button = mbLeft then begin
    //rechte maustaste gedrückt
    FLeftMousePressed:=true;
  end;
  //Startposition für die Mausbewegung merken
  FxStart := X;
  FyStart := Y;
end;

procedure TCamera.MouseUp(Sender: TObject; Button: TMouseButton;
                          Shift: TShiftState; X, Y: Integer);
// eine maustaste wurde losgelassen. mal sehen, ob eine aktion
// unterbunden werden muss.
begin
  if Button = mbRight then begin
    FRightMousePressed:=false;
  end else if Button = mbLeft then begin
    FLeftMousePressed:=false;
  end;
end;

function TCamera.MouseMove(Sender: TObject; Shift: TShiftState; X,
                           Y: Integer): boolean;
begin
  result := false;
  //Verschieben und Drehen funktioniert nur, wenn eine Maustaste
  // gedrückt ist
  if not (FRightMousePressed or FLeftMousePressed) then
    exit;

  //berechnen, wie weit die Maus verschoben wurde
  FxDelta := FxStart-X;
  FyDelta := FyStart-Y;

  //anpassen, damit es nicht zu schnell geht
  FxRot := FxRot - FyDelta;
  Fyrot := FyRot - FxDelta;

  //Startpunkt für den nächsten Aufruf festlegen
  FxStart := X;
  FyStart := Y;

  // Rechte Maustaste:
  // Kamera drehen (vertikal bei Maus hoch/runter,
  //                horizontal bei Maus links/rechts
  //                um die Bildschirmachse bei Mausrad oder wenn shift)
  if FRightMousePressed then begin
    if FShiftPressed then begin
      // wenn kein mausrad vorhanden ist, soll das ja auch gehen
      if FyRot+FxRot <> 0 then begin
        RotateScene (DIR_ZENTRAL, (FyRot+FxRot)*GiveSpeed);
      end;
    end
    else begin
      if FxRot <> 0 then begin
        RotateScene (DIR_HORIZONTAL, FxRot*GiveSpeed);
      end;
      if FyRot <> 0 then begin
        RotateScene (DIR_VERTIKAL, FyRot*GiveSpeed);
      end;
    end;
  end;

  // Linke Maustaste:
  // Kamera bewegen (vertikal bei Maus hoch/runter,
  //                horizontal bei Maus links/rechts
  //                entlang der Bildschirmachse bei Mausrad oder wenn shift)
  if FLeftMousePressed then begin
    if FShiftPressed then begin
      // wenn kein mausrad vorhanden ist, soll das ja auch gehen
      if FyRot+FxRot <> 0 then begin
        MoveScene (DIR_ZENTRAL, (FyRot+FxRot)*GiveSpeed);
      end
    end
    else begin
      if FxRot <> 0 then begin
        MoveScene (DIR_HORIZONTAL, FxRot*GiveSpeed);
      end;
      if FyRot <> 0 then begin
        MoveScene (DIR_VERTIKAL, -FyRot*GiveSpeed);
      end;
    end;
  end;

  FxRot := 0;
  FyRot := 0;

  result := true;
end;

procedure TCamera.RotateScene (Direction: byte;
                               Geschwindigkeit: GLdouble);
begin
  if Direction = DIR_HORIZONTAL then // um die X-Achse drehenx
  begin
    RotateCamera(0, Geschwindigkeit, 0);
  end
  else if Direction = DIR_VERTIKAL then // um die y-Achse drehen
  begin
    RotateCamera (Geschwindigkeit, 0, 0);
  end
  else if Direction = DIR_ZENTRAL then // um die z-Achse drehen
  begin
    RotateCamera(0, 0, Geschwindigkeit);
  end;
end;

function TCamera.GiveSpeed: TGLdouble;
begin
  // Initiale Geschwindigkeit
  result := 1;

  // wenn die Geschwindigkeit für eine Mausbewegung oder -drehung ist, soll
  // es nicht so schnell gehen
  if FRightMousePressed or FLeftMousePressed then begin
    result := result / 20;
  end;

  // wenn die alt-taste gedrückt wurde, soll es ja schneller gehen
  if FAltPressed then begin
    result := (result * 60.0);
  end;

  // jetzt mit dem individuellen speemultiplier multiplizieren
  result := result * FSpeedMultiplier;

  // wenn die drehrichtung umgeschaltet wurde, hier auch
  // umschalten
  if FDir then begin
    result := -result;
  end;
end;

procedure TCamera.MoveScene (Direction: byte; Geschwindigkeit: GLdouble);
begin
  if Direction = DIR_VERTIKAL then begin
    TranslateCamera (Geschwindigkeit, 0, 0);
  end;
  if Direction = DIR_HORIZONTAL then begin
    TranslateCamera (0, Geschwindigkeit, 0);
  end;
  if Direction = DIR_ZENTRAL then begin
    TranslateCamera (0, 0, Geschwindigkeit);
  end;
end;

function TCamera.MouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean): boolean;
begin
  if FRightMousePressed then begin
    RotateScene (DIR_ZENTRAL, (WheelDelta/10)*GiveSpeed);
  end
  else begin
    MoveScene (DIR_ZENTRAL, (WheelDelta/360)*GiveSpeed);
  end;
  result := true;
end;

procedure TCamera.MouseEnter(var msg:TMessage);
begin
  FRightMousePressed:=false;
  FLeftMousePressed:=false;
end;

procedure TCamera.MouseLeave(var msg: TMessage);
begin
  FRightMousePressed:=false;
  FLeftMousePressed:=false;
  FAltPressed := false;
  FCtrlPressed := false;
  FShiftPressed := false;
end;

Initialization
  FDebugOn := false; // auf true setzen, wenn debug infos gewünscht sind
  FDebugFileName := ExePath + 'SKANAL3D_CAMERA.DBG';
  if FDebugOn then begin
    AssignFile (FDebugFile, FDebugFileName);
    Rewrite (FDebugFile);
  end;

finalization
  if FDebugOn then begin
    CloseFile (FDebugFile);
    FDebugFileName := '';
  end;

end.