Delphi 3D – Camera with “Heads Up” Display

When allowing a user to control or move the camera in a 3D scene it is useful to have some form of “Heads Up” or “dashboard” display to provide feedback to the user about the orientation etc. of the camera. We wrap all this up in a single “AssignFlyingCameraToForm” procedure.

First we inherit from TDummy to create a TFlyingCamera Class. This gives a 3D control to which we can attach the camera and the control display and to which we can apply any control movements. Initially we attached the “Heads Up” display at Position [0,0,0] and the TCamera at {0,0,-3] but this gives a weird effect when panning the TFlyingCamera near close objects as the actual camera is effectively on a long stick and its position with relation to the objects change. Now the “Heads Up” display at Position [0,0,1.1] and the TCamera at {0,0,0].

I initially constructed the objects on the form to find out how to make it look right but then rolled it all into a single object class definition.

  TFlyingCamera = Class(TDummy)
    F3dForm: TForm3D;
    FForwardView: TCamera;
    FGunsight: TGunsight;
    FArtHorizon: TCube;
    FAHMaterial: TLightMaterialSource;
    FHeadsUp: TDummy;
    FLocText, FSpeedText, FAngleText: TText3D;
    FTextMaterial: TLightMaterialSource;
    FSpeedTimer: TTimer;
    FAngleInc: Real; // In degress
    FSpeed, // in units per second
    FSpeedInc: Real;
    Procedure SetBankAngle( Value:Single);
    Procedure MoveForward(ADistance: Real);
    procedure FmCamera3DKeyDown(Sender: TObject; var Key: Word;
      var KeyChar: Char; Shift: TShiftState); Virtual;
    Procedure SpeedTimer(Sender: TObject);
    Procedure SetText(ATxt: TText3D);
    Procedure ShowIt(AObj: TControl3D);
    procedure SetSpeed(const Value: Real);
    Procedure AlignCammeraToOrigin;
    Procedure Bank(ALeft:Boolean);
    Procedure CreateArtHorizon;
    procedure SetStepFormat(const Value: Boolean);
    Property  BankAngle:Single Read FBankAngle write SetBankAngle;
    Constructor Create(A3dForm: TComponent); override;
    destructor Destroy; override;
    Procedure UpdateHeadUpDisplay;
    Property Speed: Real read FSpeed write SetSpeed; // units per second
    Property StepFormat: Boolean read FStepFormat write SetStepFormat; // units per second

function AssignFlyingCameraToForm(A3dForm: TForm3D; AX:Single=0;AY:Single=0;AZ:Single=0): TFlyingCamera;

The Create Function puts all the bits together and allocates the forms OnKeyDown event to the component so the keys can control the movement.

constructor TFlyingCamera.Create(A3dForm: TComponent);

  if not(A3dForm is TForm3D) then
    raise Exception.Create('Must have a 3D Form');
  F3dForm := A3dForm as TForm3D;
  Inherited Create(A3dForm);
  FAngleInc := 1.0;
  FSpeedInc := 0.01;
  FSpeed := 0;//0.1;

  Parent := A3dForm as TFmxObject;
  if CameraStartPos<>TPoint3D.Zero then
   Position.Point:= CameraStartPos

  F3dForm.OnKeyDown := FmCamera3DKeyDown;

  FSpeedTimer := TTimer.Create(Self);
  FSpeedTimer.Interval := 100; // 1000 millisecs = 1 Second
  FSpeedTimer.OnTimer := SpeedTimer;

  FForwardView := TCamera.Create(Self);
  FForwardView.Parent := Self;
  FHeadsUp :=TDummy.Create(self);
  //Gimble the TCamera RotationAngle.Z Leave The Base Dummy RotationAngle.Z:=0
  FHeadsUp.Scale.Point:= TPoint3D.Create(0.4,0.4,0.4);
  FGunsight := TGunsight.Create(FHeadsUp);

  FLocText := TText3D.Create(FHeadsUp);
  FLocText.Position.X := -1.2;
  FLocText.Position.Y := 1.1;

  FSpeedText := TText3D.Create(FHeadsUp);
  FSpeedText.Position.X := 1.2;
  FSpeedText.Position.Y := 1.1;

  FAngleText := TText3D.Create(FHeadsUp);
  FAngleText.Position.X := -1.2;
  FAngleText.Position.Y := 1.2;

  F3dForm.Camera := FForwardView;
  F3dForm.UsingDesignCamera := false;
  Visible := true;

The requirement to move align the camera initially to the origin and later to move it forward in the viewed direction provided challenges which will be the subject of future submissions but the source code featured in my “Moving Camera Project” contains my solution to date.

While the TFlyingCamera class could be evolved into a toolbar component there seemed little point. Calling AssignFlyingCameraToForm achives all that is required.


function AssignFlyingCameraToForm(A3dForm: TForm3D; AX,AY,AZ:Single): TFlyingCamera;
  if (AX<>0) or (AY<>0) or (AZ<>0) then
  Result := TFlyingCamera.Create(A3dForm);

Leave a Reply

Your email address will not be published. Required fields are marked *