I consciously kept the var keyword in there so that it is clear that the Object will be changed. Maybe I should explicitly define it as an out parameter, but I am unsure if FreePascal supports that keyword.
Thanks to Sly and co for all their input on the Vector class.
Following is the proposed camera class....
[pascal]
unit camera;
interface
uses
vector;
type
TCamera = class
private
CalcVector : TVector; // A sort of cached dummry vector used in Update methods.
protected
// these are used for moving and changing camera orientation
// through the MoveTo/LookTo methods
initPosition, finalPosition : TVector;
initLookAt, finalLookAt : TVector;
LookAtVelocity : TVector; // velocity for looking at objects
LookAtAcceleration : TVector; // acceleration for looking at objects
procedure UpdateLookAt;
procedure UpdateMoveTo;
public
CameraPosition : TVector; // position of camera
CameraVelocity : TVector; // velocity of camera
CameraAcceleration : TVector; // acceleration of camera
CameraLookAt : TVector; // lookat vector
// up, forward, right vectors
CameraUp : TVector;
CameraForward : TVector;
CameraRight : TVector;
// yaw and pitch angles
CameraYaw : TScalar;
CameraPitch : TScalar;
ScreenWidth, ScreenHeight : integer;
ScreenCenterX, ScreenCenterY : integer;
constructor Create; overload;
constructor Create( aLook : TVector ); overload;
constructor Create( aPosition : TVector; aLook : TVector ); overload;
destructor Destroy; override;
procedure LookAt( aX : TScalar; aY : TScalar; aZ : TScalar ); overload;
procedure LookAt( aLook : TVector ); overload;
procedure MoveTo( aX : TScalar; aY : TScalar; aZ : TScalar ); overload;
procedure MoveTo( aPosition : TVector ); overload;
// right rotation along y-axis (yaw)
procedure RotateYaw( aRadians : TScalar );
procedure RotatePitch( aRadians : TScalar );
procedure RotateRoll( aRadians : TScalar );
end;
implementation
{ TCamera }
constructor TCamera.Create;
begin
inherited;
CameraPosition := TVector.Create( 0.0, 0.0, 0.0 );
CameraLookAt := TVector.Create( 0.0, 0.0, 1.0 );
CameraForward := CameraLookAt;
CameraUp := TVector.Create( 0.0, 1.0, 0.0 );
CameraRight := TVector.Create( 1.0, 0.0, 0.0 );
CameraVelocity := TVector.Create( 0.0, 0.0, 0.0 );
CameraAcceleration := TVector.Create( 0.0, 0.0, 0.0 );
CameraYaw := 0.0;
CameraPitch := 0.0;
CalcVector := TVector.Create( 0.0, 0.0, 0.0 );
end;
constructor TCamera.Create( aPosition, aLook : TVector );
begin
inherited Create;
CameraPosition := aPosition;
CameraLookAt := aLook.UnitVector;
CameraForward := CameraLookAt;
CameraUp := TVector.Create( 0.0, 1.0, 0.0 );
CameraRight := TVector.Create( 1.0, 0.0, 0.0 );
CameraVelocity := TVector.Create( 0.0, 0.0, 0.0 );
CameraAcceleration := TVector.Create( 0.0, 0.0, 0.0 );
CameraYaw := 0.0;
CameraPitch := 0.0;
CalcVector := TVector.Create( 0.0, 0.0, 0.0 );
end;
constructor TCamera.Create( aLook : TVector );
begin
inherited Create;
CameraPosition := TVector.Create( 0.0, 0.0, 0.0 );
CameraLookAt := aLook.UnitVector;
CameraForward := CameralookAt;
CameraUp := TVector.Create( 0.0, 1.0, 0.0 );
CameraForward.CrossProduct( CameraUp, CameraRight );
CameraVelocity := TVector.Create( 0.0, 0.0, 0.0 );
CameraAcceleration := TVector.Create( 0.0, 0.0, 0.0 );
CameraYaw := 0.0;
CameraPitch := 0.0;
CalcVector := TVector.Create( 0.0, 0.0, 0.0 );
end;
destructor TCamera.Destroy;
begin
if CameraPosition <> nil then
CameraPosition.Free;
if CameraLookAt <> nil then
CameraLookAt.Free;
if CameraForward <> nil then
CameraForward.Free;
if CameraUp <> nil then
CameraUp.Free;
if CameraRight <> nil then
CameraRight.Free;
if CameraVelocity <> nil then
CameraVelocity.Free;
if CameraAcceleration <> nil then
CameraAcceleration.Free;
if CalcVector <> nil then
CalcVector.Free;
inherited;
end;
procedure TCamera.LookAt( aX, aY, aZ : TScalar );
begin
CameraLookAt.x := aX;
CameraLookAt.y := aY;
CameraLookAt.y := aZ;
end;
procedure TCamera.LookAt( aLook : TVector );
begin
CameraLookAt.Assign( aLook );
end;
procedure TCamera.MoveTo( aX, aY, aZ : TScalar );
begin
CameraPosition.x := aX;
CameraPosition.y := aY;
CameraPosition.z := aZ;
end;
procedure TCamera.MoveTo( aPosition : TVector );
begin
CameraPosition.Assign( aPosition );
end;
procedure TCamera.RotatePitch( aRadians : TScalar );
var
sine, cosine : TScalar;
begin
sine := sin( aRadians );
cosine := cos( aRadians );
CameraUp.y := cosine * CameraUp.Length;
CameraUp.z := sine * CameraUp.Length;
CameraForward.y := -sine * CameraForward.Length;
CameraForward.z := cosine * CameraForward.Length;
{* x y z p
| 1 0 0 0 |
M = | 0 cos(A) -sin(A) 0 |
| 0 sin(A) cos(A) 0 |
| 0 0 0 1 |
*}
end;
procedure TCamera.RotateRoll( aRadians : TScalar );
var
sine, cosine : TScalar;
begin
sine := sin( aRadians );
cosine := cos( aRadians );
CameraRight.x := cosine * CameraRight.Length;
CameraRight.y := sine * CameraRight.Length;
CameraUp.x := -sine * CameraForward.Length;
CameraUp.y := cosine * CameraForward.Length;
{*
| cos(A) -sin(A) 0 0 |
M = | sin(A) cos(A) 0 0 |
| 0 0 1 0 |
| 0 0 0 1 |
*}
end;
procedure TCamera.RotateYaw( aRadians : TScalar );
var
sine, cosine : TScalar;
begin
sine := sin( aRadians );
cosine := cos( aRadians );
CameraRight.x := cosine * CameraRight.Length;
CameraRight.z := sine * CameraRight.Length;
CameraForward.x := -sine * CameraForward.Length;
CameraForward.z := cosine * CameraForward.Length;
{* x y z p
| cos(A) 0 -sin(A) 0 |
M = | 0 1 0 0 |
| sin(A) 0 cos(A) 0 |
| 0 0 0 1 |
*}
end;
procedure TCamera.UpdateLookAt;
begin
CalcVector.Assign( finalLookAt.x - CameralookAt.x,
finalLookAt.y - CameralookAt.y,
finalLookAt.z - CameralookAt.z );
LookAtVelocity.Assign( CalcVector.Scale( 0.5 ) );
end;
procedure TCamera.UpdateMoveTo;
begin
CalcVector.Assign( finalPosition.x - Cameraposition.x,
finalPosition.y - Cameraposition.y,
finalPosition.z - Cameraposition.z );
CameraVelocity.Assign( CalcVector.Scale( 0.5 ) );
end;
end.
[/pascal]
The Sine and Cosine operations should probably be a look-up table to speed things up, if you are so inclined .
Bookmarks