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>''.)
 
(4 dazwischenliegende Versionen von einem anderen Benutzer werden nicht angezeigt)
Zeile 2: Zeile 2:
 
Die verwendete Toolsammlung findet ihr unter [[Kamera (3)]].
 
Die verwendete Toolsammlung findet ihr unter [[Kamera (3)]].
  
<pascal>
+
<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
 
// Wie ich selber immer wieder merken muss, ist eine Software anscheinend
 
// nie fertig. Immer moechte man etwas verbessern oder neue Features
 
// nie fertig. Immer moechte man etwas verbessern oder neue Features
 
// einbauen.
 
// einbauen.
// Sollte also jemand Aenderungs- oder Verbesserungsvorschlaege haben oder
+
// Sollte also jemand Änderungs- oder Verbesserungsvorschläge haben oder
 
// neue Ideen einbringen wollen, oder sollten Verstaendnisfragen bestehen,
 
// neue Ideen einbringen wollen, oder sollten Verstaendnisfragen bestehen,
 
// so mailt bitte an
 
// so mailt bitte an
 
//
 
//
// Andree.Heyroth@t-online.de
+
// heyroth@syncro-concept.de
 
//
 
//
 
// Danke.
 
// Danke.
Zeile 36: Zeile 92:
 
// immer vor dem Betrachter steht.
 
// 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;
+
   Uses
 +
    DglOpenGL,
 +
    Util,
 +
    Windows,
 +
    Classes,
 +
    Messages,
 +
    Controls;
  
 
   type
 
   type
 
   TCameraMatrix=Class
 
   TCameraMatrix=Class
 +
    StackMatrix: array [0..9] of TArrMatrix;
 +
    StackCtr: integer;
 
     Matrix: TArrMatrix;
 
     Matrix: TArrMatrix;
 
     InverseMatrix: TArrMatrix;
 
     InverseMatrix: TArrMatrix;
     //constructor Create;
+
     constructor Create;
 
     //destructor destroy;
 
     //destructor destroy;
 
     procedure Identity;
 
     procedure Identity;
 +
    procedure Push;
 +
    procedure Pop;
 
     procedure Load(M: TArrMatrix);
 
     procedure Load(M: TArrMatrix);
 
   end;
 
   end;
  
 
   TCamera=Class
 
   TCamera=Class
    CameraMatrix: TCameraMatrix;
+
     Enabled: boolean;
    HomeMatrix: TCameraMatrix;
 
     Enabled : boolean;
 
    Initiated: boolean;
 
    FPosition: TGLvector;
 
    FPointOfRotation: TGLvector;
 
    FPositionOffset: 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);
     function GivePosition(pos: integer): TGLvector;
+
     function GiveStoredPosition(pos: integer): TGLvector;
     procedure RotateCamera(ix, iy, iz: TGLdouble);
+
     procedure RotateCamera(ix, iy, iz: GLdouble);
     procedure TranslateCamera(ix, iy, iz: TGLdouble);
+
     procedure TranslateCamera(ix, iy, iz: GLdouble);
 
     procedure CameraHome;
 
     procedure CameraHome;
     procedure PositionCamera(PositionVec: TGLvector;
+
     procedure PositionCamera(PositionVec: TGLvector; ViewVec: TGLvector; upVec: TGLvector);
                    ViewVec: TGLvector;
 
    upVec: TGLvector);
 
 
     procedure Adjust;
 
     procedure Adjust;
 
     procedure Apply;
 
     procedure Apply;
 
     procedure ApplyForTerrain;
 
     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;
 +
    CameraMatrix: TCameraMatrix;
 +
    Initiated: boolean;
 
     PosArray: array [0..9] of TCameraMatrix;
 
     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;
 
     function GetPosition: TGLvector;
 
     procedure SetPosition (Pos: 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
 
   public
 
     constructor Create;
 
     constructor Create;
 
     destructor Destroy;override;
 
     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 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;
 
   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
 
implementation
Zeile 95: Zeile 296:
 
uses SysUtils;
 
uses SysUtils;
  
{
 
 
constructor TCameraMatrix.Create;
 
constructor TCameraMatrix.Create;
 
begin
 
begin
 
   inherited create;
 
   inherited create;
 +
  StackCtr := 0;
 
end;
 
end;
  
destructor TCameraMatrix.Destroy;
+
procedure TCameraMatrix.Push;
 
begin
 
begin
   inherited destroy;
+
   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;
 
end;
}
 
  
 
procedure TCameraMatrix.Identity;
 
procedure TCameraMatrix.Identity;
// GetArrIdentity: aus OpenGL.pas
+
// GetIdentity: aus OpenGL.pas
 
// initialisiert die CameraMatrix mit der Identitaetsmatrix
 
// initialisiert die CameraMatrix mit der Identitaetsmatrix
 
begin
 
begin
Zeile 133: Zeile 344:
 
   inherited create;
 
   inherited create;
  
   // Initiated wird gebraucht um einmal alle Positionsspeicher  
+
   // Initiated wird gebraucht um einmal alle Positionsspeicher
 
   // mit der Anfangsposition zu belegen
 
   // mit der Anfangsposition zu belegen
 
   Initiated := false;
 
   Initiated := false;
 
+
 
 
   // Kameramatrix anlegen
 
   // Kameramatrix anlegen
 
   CameraMatrix := TCameraMatrix.Create;
 
   CameraMatrix := TCameraMatrix.Create;
 
+
 
 
   // Matrix der letzten Position von PositionCamera anlegen
 
   // Matrix der letzten Position von PositionCamera anlegen
 
   HomeMatrix := TCameraMatrix.Create;
 
   HomeMatrix := TCameraMatrix.Create;
Zeile 146: Zeile 357:
 
   for i := 0 to 9 do
 
   for i := 0 to 9 do
 
     PosArray[i] := TCameraMatrix.Create;
 
     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;
 
end;
  
Zeile 161: Zeile 386:
 
end;
 
end;
  
procedure TCamera.RotateRoundAxis(rx, ry, rz: TGLfloat);
+
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 gewuenscht 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 gewuenscht 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 gewuenscht 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;
  
Zeile 199: Zeile 448:
 
end;
 
end;
  
procedure TCamera.Offset(x, y, z: TGLfloat);
+
procedure TCamera.Offset(x, y, z: GLdouble);
 
// verschieben der Kamera auf einer beliebigen Achse
 
// verschieben der Kamera auf einer beliebigen Achse
 
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);
   newMatrix := Multiply (newMatrix, CameraMatrix.Matrix);
 
 
   CameraMatrix.Load(newMatrix);
 
   CameraMatrix.Load(newMatrix);
   glPopMatrix();
+
   Debug ('- Offset - Ende- --------------------------------------------------');
 
end;
 
end;
 
  
 
procedure TCamera.PositionCamera(PositionVec: TGLvector;
 
procedure TCamera.PositionCamera(PositionVec: TGLvector;
Zeile 226: Zeile 476:
 
   i: integer;
 
   i: integer;
 
   P, V, U: TGLvector;
 
   P, V, U: TGLvector;
   Laenge: TGLdouble;
+
   Laenge: GLdouble;
 
begin
 
begin
 +
  Debug ('- PositionCamera - Start ------------------------------------------');
 
   // die gewuenschte konstruktion immer auf die Z-ebene projizieren.
 
   // die gewuenschte konstruktion immer auf die Z-ebene projizieren.
 
   // zuerst die position in den nullpunkt holen
 
   // zuerst die position in den nullpunkt holen
Zeile 236: Zeile 487:
 
   // U ist halt schneller geschrieben als upVec...
 
   // U ist halt schneller geschrieben als upVec...
 
   U := upVec;
 
   U := upVec;
   // den betrag ermitteln, um den die kamera nachher auf der Z-Achse  
+
 
 +
   // den betrag ermitteln, um den die kamera nachher auf der Z-Achse
 
   // verschoben werden muss
 
   // verschoben werden muss
 
   Laenge := Magnitude (SubtractVector (P, V));
 
   Laenge := Magnitude (SubtractVector (P, V));
  
 
   Identity;
 
   Identity;
  FPointOfRotation := ViewVec;
 
  
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
Zeile 249: Zeile 500:
 
   // glulookat wird die matrix parallel zur Z-achse ausrichten
 
   // 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);
 
   gluLookAt (P.X, P.Y, P.Z, V.X, V.Y, V.Z, U.X, U.Y, U.Z);
   glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
+
   glGetDoublev(GL_MODELVIEW_MATRIX, @newMatrix);
 
   glPopMatrix;
 
   glPopMatrix;
  
Zeile 256: Zeile 507:
  
 
   // da wir uns jetzt am zielpunkt befinden, müssen wir auf der Z-achse
 
   // da wir uns jetzt am zielpunkt befinden, müssen wir auf der Z-achse
   // wieder zurück zur kameraposition
+
   // wieder zurueck zur kameraposition
 
   Offset (0, 0, -Laenge);
 
   Offset (0, 0, -Laenge);
  
  // damit GetPosition den Wert zurückgibt den wir mit PositionCamera
+
   // alle positionsspeicher mit der Kameraposition, Blickrichtung
  // 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
 
   // und dem upVector belegen. Nur beim ersten Aufruf von
 
   // PositionCamera
 
   // 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;
 +
 
 +
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;
 
end;
  
 
procedure TCamera.CameraHome;
 
procedure TCamera.CameraHome;
// Kamera in die beim letzten Aufruf von PositionCamera übergebene
+
// Kamera in die beim letzten Aufruf von PositionCamera uebergebene
 
// Position/Lage bringen
 
// Position/Lage bringen
 
begin
 
begin
Zeile 291: Zeile 576:
  
 
   PosArray[pos].Load(CameraMatrix.Matrix);
 
   PosArray[pos].Load(CameraMatrix.Matrix);
 +
  RotArray[pos] := FPointOfRotation;
 
end;
 
end;
  
Zeile 300: Zeile 586:
  
 
   CameraMatrix.Load(PosArray[pos].Matrix);
 
   CameraMatrix.Load(PosArray[pos].Matrix);
 +
  FPointOfRotation := RotArray[pos];
 
end;
 
end;
  
function TCamera.GivePosition (pos: integer): TGLvector;
+
function TCamera.GiveStoredPosition (pos: integer): TGLvector;
// gibt den Inhalt des durch pos bestimmten  
+
// gibt den Inhalt des durch pos bestimmten
 
// Positionsspecihers zurueck
 
// Positionsspecihers zurueck
 
begin
 
begin
Zeile 309: Zeile 596:
 
     exit;
 
     exit;
  
   result.X := PosArray[pos].Matrix[12];
+
   result := GetMatrixPos (PosArray[pos].Matrix);
  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: GLdouble);
 
// vom Benutzer aufzurufende Methode um eine Verschiebung
 
// vom Benutzer aufzurufende Methode um eine Verschiebung
 
// durchzufuehren
 
// durchzufuehren
Zeile 321: Zeile 606:
 
end;
 
end;
  
procedure TCamera.RotateCamera(ix, iy, iz: TGLdouble);
+
procedure TCamera.RotateCamera(ix, iy, iz: GLdouble);
 
// vom Benutzer aufzurufende Methode um eine Drehung
 
// vom Benutzer aufzurufende Methode um eine Drehung
 
// durchzufuehren
 
// durchzufuehren
 
begin
 
begin
   RotateMatrix (ix, iy, iz);
+
   RotateRoundAxis (-iy, -ix, -iz);
 
end;
 
end;
  
 
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 benoetigten Drehungen, Verschiebungen  
+
// sollte die Szene mit allen benoetigten Drehungen, Verschiebungen
 
// gezeichnet werden.
 
// gezeichnet werden.
 
begin
 
begin
Zeile 337: Zeile 622:
  
 
   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;
 
end;
  
 
procedure TCamera.ApplyForTerrain;
 
procedure TCamera.ApplyForTerrain;
// hier wird wie in Apply die Kamera eingeschaltet. da man um ein terrain  
+
// hier wird wie in Apply die Kamera eingeschaltet. da man um ein terrain
 
// (skycube, ...) anzuzeigen aber immer die gleiche entfernung zur welt
 
// (skycube, ...) anzuzeigen aber immer die gleiche entfernung zur welt
 
// einhalten muss, wird hier nur gedreht und nicht verschoben.
 
// einhalten muss, wird hier nur gedreht und nicht verschoben.
 +
var
 +
  pos: TGLvector;
 
begin
 
begin
 
   if not Enabled then
 
   if not Enabled then
Zeile 353: Zeile 640:
 
   glMatrixMode (GL_MODELVIEW);
 
   glMatrixMode (GL_MODELVIEW);
 
   // für das Terrain nur die Drehung ausführen
 
   // für das Terrain nur die Drehung ausführen
   glLoadMatrixf(@CameraMatrix.Matrix);
+
   glLoadMatrixd(@CameraMatrix.Matrix);
 
   // deswegen jetzt die verschiebung zurücknehmen
 
   // deswegen jetzt die verschiebung zurücknehmen
   glTranslatef (CameraMatrix.InverseMatrix[12],
+
   Pos := GetMatrixPos (CameraMatrix.InverseMatrix);
                CameraMatrix.InverseMatrix[13],
+
  glTranslated (Pos.X, Pos.Y, Pos.Z);
                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;
 
end;
  
 
function TCamera.GetPosition: TGLvector;
 
function TCamera.GetPosition: TGLvector;
 
// diese Property-Funktion fragt die aktuelle Position der Kamera ab
 
// diese Property-Funktion fragt die aktuelle Position der Kamera ab
var
 
  return: TGLvector;
 
 
begin
 
begin
 
   // position: letzte Spalte der Matrix
 
   // position: letzte Spalte der Matrix
   InitVector (return,
+
   result := AddVector (GetMatrixPos (CameraMatrix.InverseMatrix), FPointOfRotation);
              CameraMatrix.Matrix[12],
 
              CameraMatrix.Matrix[13],
 
              CameraMatrix.Matrix[14]);
 
  return := AddVector (return, FPositionOffset);
 
  result := return;
 
 
end;
 
end;
  
 
procedure TCamera.SetPosition (Pos: TGLvector);
 
procedure TCamera.SetPosition (Pos: TGLvector);
 
// diese Property-Funktion setzt eine neue Position der Kamera
 
// diese Property-Funktion setzt eine neue Position der Kamera
 +
var
 +
  m: TArrMatrix;
 
begin
 
begin
 
   // position: letzte Spalte der Matrix
 
   // position: letzte Spalte der Matrix
   CameraMatrix.Matrix[12] := Pos.X;
+
   m := CameraMatrix.Matrix;
   CameraMatrix.Matrix[13] := Pos.Y;
+
   SetMatrixPos (m, SubtractVector (Pos, FPointOfRotation));
   CameraMatrix.Matrix[14] := Pos.Z;
+
   CameraMatrix.Load (m);
 
end;
 
end;
  
function TCamera.ViewDirection: TGLvector;
+
function TCamera.GetViewDirection: TGLvector;
 
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
 
// mit dieser Funktion kann die aktuelle Blickrichtung der Kamera
 
// abgefragt werden
 
// abgefragt werden
Zeile 397: Zeile 671:
 
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
 +
// gesetzt werden
 +
begin
 +
   // view direction: dritte Spalte der Matrix (Z-Achse)
 +
  SetMatrixZ (CameraMatrix.InverseMatrix, View);
 
end;
 
end;
  
 
function TCamera.UpVector: TGLvector;
 
function TCamera.UpVector: TGLvector;
// mit dieser Funktion kann die aktuelle Ausrichtung der Kamera  
+
// mit dieser Funktion kann die aktuelle Ausrichtung der Kamera
 
// abgefragt werden
 
// abgefragt werden
 
var
 
var
Zeile 411: Zeile 689:
 
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],
 
              CameraMatrix.Matrix[05],
 
              CameraMatrix.Matrix[06]);
 
  result := return;
 
 
end;
 
end;
  
 
procedure TCamera.Adjust;
 
procedure TCamera.Adjust;
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhaengig  
+
// mit dieser Prozedur kann die Kamera zu jeder Zeit, unabhaengig
// von Drehung und Position, senkrecht zur Y-Achse ausgerichtet
+
// von Drehung und Position, zur Y-Achse ausgerichtet werden.
// werden. Die aktuelle Position wird dabei beibehalten.
+
// 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
 +
  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
 
begin
   temp := GetIdentity(temp);
+
   if Direction = DIR_VERTIKAL then begin
   temp[12] := CameraMatrix.Matrix[12];
+
    TranslateCamera (Geschwindigkeit, 0, 0);
   temp[13] := CameraMatrix.Matrix[13];
+
   end;
   temp[14] := CameraMatrix.Matrix[14];
+
   if Direction = DIR_HORIZONTAL then begin
   temp[15] := CameraMatrix.Matrix[15];
+
    TranslateCamera (0, Geschwindigkeit, 0);
   CameraMatrix.Load(temp);
+
   end;
 +
   if Direction = DIR_ZENTRAL then begin
 +
    TranslateCamera (0, 0, Geschwindigkeit);
 +
   end;
 
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.
</pascal>
+
</source>
 
[[Kategorie:Anleitung]]
 
[[Kategorie:Anleitung]]

Aktuelle Version vom 10. März 2009, 19: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.