Kamera (2): Unterschied zwischen den Versionen
Aus DGL Wiki
Andyh (Diskussion | Beiträge) |
Andyh (Diskussion | Beiträge) |
||
| Zeile 6: | Zeile 6: | ||
// 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 | + | // 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 | ||
| Zeile 35: | Zeile 35: | ||
// Jetzt wird die Position so gesetzt, dass die gewuenschte Kameraposition | // Jetzt wird die Position so gesetzt, dass die gewuenschte Kameraposition | ||
// 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. | ||
// | // | ||
unit Camera; | unit Camera; | ||
| Zeile 44: | Zeile 62: | ||
type | type | ||
TCameraMatrix=Class | TCameraMatrix=Class | ||
| + | StackMatrix: array [0..9] of TArrMatrix; | ||
| + | StackCtr: integer; | ||
Matrix: TArrMatrix; | Matrix: TArrMatrix; | ||
InverseMatrix: TArrMatrix; | InverseMatrix: TArrMatrix; | ||
| − | + | constructor Create; | |
//destructor destroy; | //destructor destroy; | ||
procedure Identity; | procedure Identity; | ||
| + | procedure Push; | ||
| + | procedure Pop; | ||
procedure Load(M: TArrMatrix); | procedure Load(M: TArrMatrix); | ||
end; | end; | ||
TCamera=Class | TCamera=Class | ||
| − | + | Enabled: boolean; | |
| − | |||
| − | Enabled | ||
| − | |||
| − | |||
| − | |||
| − | |||
function UpVector: TGLvector; | function UpVector: TGLvector; | ||
| − | |||
procedure RestorePosition(pos: integer); | procedure RestorePosition(pos: integer); | ||
procedure SavePosition(pos: integer); | procedure SavePosition(pos: integer); | ||
| − | function | + | function GiveStoredPosition(pos: integer): TGLvector; |
procedure RotateCamera(ix, iy, iz: TGLdouble); | procedure RotateCamera(ix, iy, iz: TGLdouble); | ||
procedure TranslateCamera(ix, iy, iz: TGLdouble); | procedure TranslateCamera(ix, iy, iz: TGLdouble); | ||
procedure CameraHome; | procedure CameraHome; | ||
| − | procedure PositionCamera(PositionVec: TGLvector; | + | procedure PositionCamera(PositionVec: TGLvector; ViewVec: TGLvector; upVec: TGLvector); |
| − | |||
| − | |||
procedure Adjust; | procedure Adjust; | ||
procedure Apply; | procedure Apply; | ||
procedure ApplyForTerrain; | procedure ApplyForTerrain; | ||
private | private | ||
| + | FPosition: TGLvector; | ||
| + | FViewDirection: TGLvector; | ||
| + | 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 Offset(x, y, z: TGLfloat); | procedure Offset(x, y, z: TGLfloat); | ||
procedure RotateRoundAxis(rx, ry, rz: TGLfloat); | procedure RotateRoundAxis(rx, ry, rz: TGLfloat); | ||
| + | procedure SetPointOfRotation (NewPoint: TGLvector); | ||
public | public | ||
constructor Create; | constructor Create; | ||
destructor Destroy;override; | destructor Destroy;override; | ||
| + | function InverseMatrix: TArrMatrix; | ||
| + | procedure PositionCross(CrossVec: TGLvector); | ||
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; | ||
end; | end; | ||
TPCamera=^TCamera; | TPCamera=^TCamera; | ||
| + | |||
| + | var | ||
| + | FDebugFile: Textfile; | ||
| + | FDebugFileName: string; | ||
| + | FDebugOn: boolean; | ||
implementation | implementation | ||
| − | uses SysUtils; | + | uses SysUtils, KanalUtil; |
| − | |||
constructor TCameraMatrix.Create; | constructor TCameraMatrix.Create; | ||
begin | begin | ||
inherited create; | inherited create; | ||
| + | StackCtr := 0; | ||
end; | end; | ||
| − | + | { | |
destructor TCameraMatrix.Destroy; | destructor TCameraMatrix.Destroy; | ||
begin | begin | ||
| Zeile 106: | Zeile 141: | ||
end; | end; | ||
} | } | ||
| + | |||
| + | procedure TCameraMatrix.Push; | ||
| + | begin | ||
| + | if (StackCtr > -1) and (StackCtr < 10) then | ||
| + | begin | ||
| + | StackMatrix[StackCtr] := Matrix; | ||
| + | inc (StackCtr); | ||
| + | end; | ||
| + | end; | ||
| + | |||
| + | procedure TCameraMatrix.Pop; | ||
| + | begin | ||
| + | if (StackCtr > 0) and (StackCtr < 11) then | ||
| + | begin | ||
| + | dec (StackCtr); | ||
| + | Load (StackMatrix[StackCtr]); | ||
| + | end; | ||
| + | end; | ||
procedure TCameraMatrix.Identity; | procedure TCameraMatrix.Identity; | ||
| − | // | + | // GetIdentity: aus OpenGL.pas |
// initialisiert die CameraMatrix mit der Identitaetsmatrix | // initialisiert die CameraMatrix mit der Identitaetsmatrix | ||
begin | begin | ||
| Zeile 133: | Zeile 186: | ||
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 199: | ||
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; | ||
end; | end; | ||
| Zeile 166: | Zeile 222: | ||
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); | glLoadMatrixf(@CameraMatrix.Matrix); | ||
| − | // wenn gewuenscht um die X-Achse drehen | + | if FFixedAxis then |
| − | if(rx <> 0) 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 | ||
| + | glRotatef(rx,tempX.X,tempX.Y,tempX.Z); | ||
| + | |||
| + | // wenn gewuenscht um die Y-Achse drehen | ||
| + | if(ry <> 0) then | ||
| + | glRotatef(ry,tempY.X,tempY.Y,tempY.Z); | ||
| + | |||
| + | // wenn gewuenscht um die Z-Achse drehen | ||
| + | if(rz <> 0) then | ||
| + | glRotatef(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 | ||
| + | glRotatef(rx,1,0,0); | ||
| − | + | // wenn gewuenscht um die Y-Achse drehen | |
| − | + | if(ry <> 0) then | |
| − | + | glRotatef(ry,0,1,0); | |
| − | + | // wenn gewuenscht um die Z-Achse drehen | |
| − | + | if(rz <> 0) then | |
| − | + | glRotatef(rz,0,0,1); | |
| + | end; | ||
// die neu erzeugte Matrix auslesen | // die neu erzeugte Matrix auslesen | ||
glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix); | glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix); | ||
| + | |||
| + | glPopMatrix(); | ||
| + | |||
// und in die Kameramatrix sichern | // und in die Kameramatrix sichern | ||
CameraMatrix.Load(newMatrix); | CameraMatrix.Load(newMatrix); | ||
| − | |||
end; | end; | ||
| Zeile 203: | Zeile 285: | ||
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); | glTranslatef(x,y,z); | ||
glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix); | glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix); | ||
| − | + | glPopMatrix(); | |
| − | + | ||
| − | + | Debug ('Position: '+GLvectorToText (GetMatrixPos (newMatrix))); | |
| − | + | newMatrix := UpdateMatrixOffset (newMatrix); | |
| − | newMatrix := | ||
CameraMatrix.Load(newMatrix); | CameraMatrix.Load(newMatrix); | ||
| − | + | Debug ('- Offset - Ende- --------------------------------------------------'); | |
end; | end; | ||
| − | |||
procedure TCamera.PositionCamera(PositionVec: TGLvector; | procedure TCamera.PositionCamera(PositionVec: TGLvector; | ||
| Zeile 228: | Zeile 311: | ||
Laenge: TGLdouble; | Laenge: TGLdouble; | ||
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 320: | ||
// 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; | ||
| − | |||
glMatrixMode (GL_MODELVIEW); | glMatrixMode (GL_MODELVIEW); | ||
| Zeile 256: | Zeile 340: | ||
// 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 | + | // wieder zurueck zur kameraposition |
Offset (0, 0, -Laenge); | Offset (0, 0, -Laenge); | ||
| − | + | // alle positionsspeicher mit der Kameraposition, Blickrichtung | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | // alle positionsspeicher mit der Kameraposition, Blickrichtung | ||
// und dem upVector belegen. Nur beim ersten Aufruf von | // und dem upVector belegen. Nur beim ersten Aufruf von | ||
// PositionCamera | // PositionCamera | ||
if not Initiated then | if not Initiated then | ||
| − | begin | + | 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 | ||
| + | glTranslatef (-PosDiff.X, 0, 0); | ||
| + | glTranslatef (0, -PosDiff.Y, 0); | ||
| + | glTranslatef (0, 0, -PosDiff.Z); | ||
| + | |||
| + | // jetzt vom neuen Rotationspunktes zurück ins Zentrum, damit beim | ||
| + | // nächsten Apply das glTranslatef (-FPointOfRotation, ...) klappt | ||
| + | glTranslatef (CrossVec.X, 0, 0); | ||
| + | glTranslatef (0, CrossVec.Y, 0); | ||
| + | glTranslatef (0, 0, CrossVec.Z); | ||
| + | |||
| + | // aktuelle Matrix holen... | ||
| + | glGetFloatv(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 | + | // Kamera in die beim letzten Aufruf von PositionCamera uebergebene |
// Position/Lage bringen | // Position/Lage bringen | ||
begin | begin | ||
| Zeile 291: | Zeile 409: | ||
PosArray[pos].Load(CameraMatrix.Matrix); | PosArray[pos].Load(CameraMatrix.Matrix); | ||
| + | RotArray[pos] := FPointOfRotation; | ||
end; | end; | ||
| Zeile 300: | Zeile 419: | ||
CameraMatrix.Load(PosArray[pos].Matrix); | CameraMatrix.Load(PosArray[pos].Matrix); | ||
| + | FPointOfRotation := RotArray[pos]; | ||
end; | end; | ||
| − | function TCamera. | + | 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 429: | ||
exit; | exit; | ||
| − | result | + | result := GetMatrixPos (PosArray[pos].Matrix); |
| − | |||
| − | |||
end; | end; | ||
| Zeile 325: | Zeile 443: | ||
// durchzufuehren | // durchzufuehren | ||
begin | begin | ||
| − | + | 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 338: | Zeile 456: | ||
glMatrixMode (GL_MODELVIEW); | glMatrixMode (GL_MODELVIEW); | ||
glLoadMatrixf(@CameraMatrix.Matrix); | glLoadMatrixf(@CameraMatrix.Matrix); | ||
| − | glTranslatef (- | + | glTranslatef (-FPointOfRotation.X, |
| − | - | + | -FPointOfRotation.y, |
| − | - | + | -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 355: | Zeile 475: | ||
glLoadMatrixf(@CameraMatrix.Matrix); | glLoadMatrixf(@CameraMatrix.Matrix); | ||
// deswegen jetzt die verschiebung zurücknehmen | // deswegen jetzt die verschiebung zurücknehmen | ||
| − | + | Pos := GetMatrixPos (CameraMatrix.InverseMatrix); | |
| − | + | glTranslatef (Pos.X, Pos.Y, Pos.Z); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
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 | ||
| − | |||
| − | |||
begin | begin | ||
// position: letzte Spalte der Matrix | // position: letzte Spalte der Matrix | ||
| − | + | result := AddVector (GetMatrixPos (CameraMatrix.InverseMatrix), FPointOfRotation); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
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 | + | m := CameraMatrix.Matrix; |
| − | + | SetMatrixPos (m, SubtractVector (Pos, FPointOfRotation)); | |
| − | CameraMatrix. | + | CameraMatrix.Load (m); |
end; | end; | ||
| − | function TCamera. | + | 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 504: | ||
begin | begin | ||
// view direction: dritte Spalte der Matrix (Z-Achse) | // 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; | 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 522: | ||
begin | begin | ||
// upVector: zweite Spalte der Matrix (Y-Achse) | // upVector: zweite Spalte der Matrix (Y-Achse) | ||
| − | + | result := GetMatrixY (CameraMatrix.InverseMatrix); | |
| − | |||
| − | |||
| − | |||
| − | |||
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, | + | // von Drehung und Position, zur Y-Achse ausgerichtet werden. |
| − | // | + | // Die aktuelle Position wird dabei beibehalten. |
var | var | ||
| − | + | m: TArrMatrix; | |
| + | v: TGLvector; | ||
begin | 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 | |
| − | CameraMatrix.Load( | + | SetMatrixPos (m, v); |
| + | // m als aktuelle cameramatrix speichern | ||
| + | CameraMatrix.Load(m); | ||
end; | 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; | ||
| + | |||
| + | Initialization | ||
| + | FDebugOn := false; | ||
| + | FDebugFileName := ExePath + 'APPLICATION.DBG'; | ||
| + | if FDebugOn then | ||
| + | begin | ||
| + | AssignFile (FDebugFile, FDebugFileName); | ||
| + | Rewrite (FDebugFile); | ||
| + | end; | ||
| + | |||
| + | finalization | ||
| + | if FDebugOn then | ||
| + | CloseFile (FDebugFile); | ||
end. | end. | ||
</pascal> | </pascal> | ||
[[Kategorie:Anleitung]] | [[Kategorie:Anleitung]] | ||
Version vom 8. Januar 2006, 08:01 Uhr
Eine Beschreibung findet ihr unter Kamera (1).
Die verwendete Toolsammlung findet ihr unter Kamera (3).
// 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
//
// Andree.Heyroth@t-online.de
//
// Danke.
//
// Updates:
//
// 06.11.2005
// Die Deklaration der DESTROY Methode war falsch. Das fuehrte dazu,
// dass in der Methode CREATE erzeugte Objekte nicht richtig frei-
// gegeben wurden und es zu EAccessViolations kam. Die Methode wird
// jetzt in der PUBLIC Section mit der Direktive OVERRIDE aufgerufen.
//
// 11.11.2005
// Die Positionierung funktioniert jetzt.
// Wegen eines Verstaendnisproblems wurden die Positionen immer auf den
// Kamerapunkt aufaddiert. Das bedeutete, dass die Positionierung aus
// dem Ruder lief wenn sie nicht im Nullpunkt stattfand. Je groesser die
// gewuenschte Position war um so groesser war die Abweichung von der
// Darstellung.
// Ich habe nicht bedacht, dass bei einer "Kamera"-bewegung und -drehung
// ja eigentlich die Szene bewegt und gedreht wird. Deswegen wurden nach
// einer Positionierung die folgenden Projektionen immer basierend auf der
// danach gueltigen Kameraposition gemacht.
// Jetzt wird die Position so gesetzt, dass die gewuenschte Kameraposition
// immer vor dem Betrachter steht.
//
// 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.
//
unit Camera;
interface
Uses DglOpenGL, OpenGLUtil, Windows, Classes;
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: TGLdouble);
procedure TranslateCamera(ix, iy, iz: TGLdouble);
procedure CameraHome;
procedure PositionCamera(PositionVec: TGLvector; ViewVec: TGLvector; upVec: TGLvector);
procedure Adjust;
procedure Apply;
procedure ApplyForTerrain;
private
FPosition: TGLvector;
FViewDirection: TGLvector;
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: TGLfloat);
procedure RotateRoundAxis(rx, ry, rz: TGLfloat);
procedure SetPointOfRotation (NewPoint: TGLvector);
public
constructor Create;
destructor Destroy;override;
function InverseMatrix: TArrMatrix;
procedure PositionCross(CrossVec: TGLvector);
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;
end;
TPCamera=^TCamera;
var
FDebugFile: Textfile;
FDebugFileName: string;
FDebugOn: boolean;
implementation
uses SysUtils, KanalUtil;
constructor TCameraMatrix.Create;
begin
inherited create;
StackCtr := 0;
end;
{
destructor TCameraMatrix.Destroy;
begin
inherited destroy;
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;
end;
destructor TCamera.Destroy;
// alle in create belegten Resourcen wieder freigeben
var
i: integer;
begin
FreeAndNil (CameraMatrix);
FreeAndNil (HomeMatrix);
for i := 0 to 9 do
FreeAndNil (PosArray[i]);
inherited destroy;
end;
procedure TCamera.RotateRoundAxis(rx, ry, rz: TGLfloat);
// hier drehen wir jetzt um die einzelnen Achsen.
// die Parameter geben die "Drehgeschwindigkeit" vor.
var
newMatrix: TArrMatrix;
tempX, tempY, tempZ: TGLvector;
begin
glMatrixMode (GL_MODELVIEW);
glPushMatrix();
// aktuelle Position und Lage der Kamera herstellen
glLoadMatrixf(@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
glRotatef(rx,tempX.X,tempX.Y,tempX.Z);
// wenn gewuenscht um die Y-Achse drehen
if(ry <> 0) then
glRotatef(ry,tempY.X,tempY.Y,tempY.Z);
// wenn gewuenscht um die Z-Achse drehen
if(rz <> 0) then
glRotatef(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
glRotatef(rx,1,0,0);
// wenn gewuenscht um die Y-Achse drehen
if(ry <> 0) then
glRotatef(ry,0,1,0);
// wenn gewuenscht um die Z-Achse drehen
if(rz <> 0) then
glRotatef(rz,0,0,1);
end;
// die neu erzeugte Matrix auslesen
glGetFloatv(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: TGLfloat);
// verschieben der Kamera auf einer beliebigen Achse
var
newMatrix: TArrMatrix;
//OldView: TGLvector;
begin
Debug ('- Offset - Start --------------------------------------------------');
glMatrixMode (GL_MODELVIEW);
glPushMatrix();
glLoadIdentity;
glTranslatef(x,y,z);
glGetFloatv(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: TGLdouble;
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);
glGetFloatv(GL_MODELVIEW_MATRIX, @newMatrix);
glPopMatrix;
CameraMatrix.Load(newMatrix);
HomeMatrix.Load(newMatrix);
// da wir uns jetzt am zielpunkt befinden, müssen wir auf der Z-achse
// wieder 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
glTranslatef (-PosDiff.X, 0, 0);
glTranslatef (0, -PosDiff.Y, 0);
glTranslatef (0, 0, -PosDiff.Z);
// jetzt vom neuen Rotationspunktes zurück ins Zentrum, damit beim
// nächsten Apply das glTranslatef (-FPointOfRotation, ...) klappt
glTranslatef (CrossVec.X, 0, 0);
glTranslatef (0, CrossVec.Y, 0);
glTranslatef (0, 0, CrossVec.Z);
// aktuelle Matrix holen...
glGetFloatv(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: TGLdouble);
// vom Benutzer aufzurufende Methode um eine Verschiebung
// durchzufuehren
begin
Offset (ix, iy, iz);
end;
procedure TCamera.RotateCamera(ix, iy, iz: TGLdouble);
// vom Benutzer aufzurufende Methode um eine Drehung
// durchzufuehren
begin
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);
glLoadMatrixf(@CameraMatrix.Matrix);
glTranslatef (-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
glLoadMatrixf(@CameraMatrix.Matrix);
// deswegen jetzt die verschiebung zurücknehmen
Pos := GetMatrixPos (CameraMatrix.InverseMatrix);
glTranslatef (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;
Initialization
FDebugOn := false;
FDebugFileName := ExePath + 'APPLICATION.DBG';
if FDebugOn then
begin
AssignFile (FDebugFile, FDebugFileName);
Rewrite (FDebugFile);
end;
finalization
if FDebugOn then
CloseFile (FDebugFile);
end.