Kamera (2): Unterschied zwischen den Versionen
Aus DGL Wiki
Andyh (Diskussion | Beiträge) |
Andyh (Diskussion | Beiträge) |
||
Zeile 3: | Zeile 3: | ||
<pascal> | <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 | ||
Zeile 10: | Zeile 66: | ||
// so mailt bitte an | // so mailt bitte an | ||
// | // | ||
− | // | + | // heyroth@syncro-concept.de |
// | // | ||
// Danke. | // Danke. | ||
Zeile 60: | Zeile 116: | ||
// das Ersetzen von Datentypen und der Aufruf der entsprechenden GL-Funktionen. | // 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, Util, Windows, Classes; | + | Uses |
+ | DglOpenGL, | ||
+ | Util, | ||
+ | Windows, | ||
+ | Classes, | ||
+ | Messages, | ||
+ | Controls; | ||
type | type | ||
Zeile 94: | Zeile 164: | ||
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; | FPosition: TGLvector; | ||
FViewDirection: 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; | FPointOfRotation: TGLvector; | ||
HomeMatrix: TCameraMatrix; | HomeMatrix: TCameraMatrix; | ||
Zeile 114: | Zeile 212: | ||
procedure RotateRoundAxis(rx, ry, rz: GLdouble); | procedure RotateRoundAxis(rx, ry, rz: GLdouble); | ||
procedure SetPointOfRotation (NewPoint: TGLvector); | 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; | ||
Zeile 120: | Zeile 222: | ||
procedure ApplyInvers; | procedure ApplyInvers; | ||
procedure PositionCross(CrossVec: TGLvector); | 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 write SetPointOfRotation; | property PointOfRotation: TGLvector read FPointOfRotation write SetPointOfRotation; | ||
Zeile 125: | Zeile 236: | ||
property ViewDirection: TGLvector read GetViewDirection write SetViewDirection; | property ViewDirection: TGLvector read GetViewDirection write SetViewDirection; | ||
property FixedAxis: boolean read FFixedAxis write FFixedAxis; | property FixedAxis: boolean read FFixedAxis write FFixedAxis; | ||
+ | property SpeedMultiplier: double read FSpeedMultiplier write FSpeedMultiplier; | ||
end; | end; | ||
TPCamera=^TCamera; | TPCamera=^TCamera; | ||
Zeile 132: | Zeile 244: | ||
FDebugFileName: string; | FDebugFileName: string; | ||
FDebugOn: boolean; | 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 142: | Zeile 301: | ||
StackCtr := 0; | StackCtr := 0; | ||
end; | end; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
procedure TCameraMatrix.Push; | procedure TCameraMatrix.Push; | ||
begin | begin | ||
− | if (StackCtr > -1) and (StackCtr < 10) then | + | if (StackCtr > -1) and (StackCtr < 10) then begin |
− | |||
StackMatrix[StackCtr] := Matrix; | StackMatrix[StackCtr] := Matrix; | ||
inc (StackCtr); | inc (StackCtr); | ||
Zeile 160: | Zeile 312: | ||
procedure TCameraMatrix.Pop; | procedure TCameraMatrix.Pop; | ||
begin | begin | ||
− | if (StackCtr > 0) and (StackCtr < 11) then | + | if (StackCtr > 0) and (StackCtr < 11) then begin |
− | |||
dec (StackCtr); | dec (StackCtr); | ||
Load (StackMatrix[StackCtr]); | Load (StackMatrix[StackCtr]); | ||
Zeile 209: | Zeile 360: | ||
// standardmaessig immer entlang der bildschirmachsen verschieben | // standardmaessig immer entlang der bildschirmachsen verschieben | ||
FFixedAxis := true; | 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 237: | Zeile 399: | ||
glLoadMatrixd(@CameraMatrix.Matrix); | glLoadMatrixd(@CameraMatrix.Matrix); | ||
− | if FFixedAxis then | + | if FFixedAxis then begin |
− | |||
// über die bildschirmachsen drehen | // über die bildschirmachsen drehen | ||
tempX := GetMatrixX (CameraMatrix.InverseMatrix); | tempX := GetMatrixX (CameraMatrix.InverseMatrix); | ||
Zeile 255: | Zeile 416: | ||
glRotated(rz,tempZ.X,tempZ.Y,tempZ.Z); | glRotated(rz,tempZ.X,tempZ.Y,tempZ.Z); | ||
end | end | ||
− | else | + | else begin |
− | |||
// über die achsen des koordinatenkreuzes drehen | // über die achsen des koordinatenkreuzes drehen | ||
// wenn gewuenscht um die X-Achse drehen | // wenn gewuenscht um die X-Achse drehen | ||
Zeile 569: | Zeile 729: | ||
// entlang der bildschirmachsen verschoben. wenn ich Multiply, version 2 | // entlang der bildschirmachsen verschoben. wenn ich Multiply, version 2 | ||
// nehme, wird sie auf den errechneten achsen verschoben. | // nehme, wird sie auf den errechneten achsen verschoben. | ||
− | if FFixedAxis then | + | if FFixedAxis then begin |
− | |||
result := Multiply (newMatrix, CameraMatrix.Matrix); | result := Multiply (newMatrix, CameraMatrix.Matrix); | ||
end | end | ||
− | else | + | else begin |
− | |||
result := Multiply (CameraMatrix.Matrix, newMatrix); | result := Multiply (CameraMatrix.Matrix, newMatrix); | ||
end; | end; | ||
Zeile 603: | Zeile 761: | ||
// werden und nicht die verschiebung. Verschiebung wird hier zurückgenommen. | // werden und nicht die verschiebung. Verschiebung wird hier zurückgenommen. | ||
// Vorher muss die Kamera angewendet und die nötigen Verschiebungen/ | // Vorher muss die Kamera angewendet und die nötigen Verschiebungen/ | ||
− | // Drehungen ausgeführt werden. | + | // Drehungen ausgeführt werden. |
M := InverseMatrix; | M := InverseMatrix; | ||
M[12] := 0; | M[12] := 0; | ||
Zeile 610: | Zeile 768: | ||
M[15] := 1; | M[15] := 1; | ||
glMultMatrixd(@M); | 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; | end; | ||
Initialization | Initialization | ||
− | FDebugOn := false; | + | FDebugOn := false; // auf true setzen, wenn debug infos gewünscht sind |
FDebugFileName := ExePath + 'SKANAL3D_CAMERA.DBG'; | FDebugFileName := ExePath + 'SKANAL3D_CAMERA.DBG'; | ||
− | if FDebugOn then | + | if FDebugOn then begin |
− | |||
AssignFile (FDebugFile, FDebugFileName); | AssignFile (FDebugFile, FDebugFileName); | ||
Rewrite (FDebugFile); | Rewrite (FDebugFile); | ||
Zeile 622: | Zeile 1.149: | ||
finalization | finalization | ||
− | if FDebugOn then | + | if FDebugOn then begin |
− | |||
CloseFile (FDebugFile); | CloseFile (FDebugFile); | ||
FDebugFileName := ''; | FDebugFileName := ''; |
Version vom 5. Februar 2008, 06:00 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.