123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- //
- // The multimedia graphics platform GLScene https://github.com/glscene
- //
- unit GLS.SimpleNavigation;
- (*
- A simple component written by request from someone at the www.glscene.ru forums.
- Allows to view the FPS and do the usual Zoom and MoveAroundTarget stuff
- that all demos usually have in themselves. All that is just by dropping
- this component on the form.
- *)
- interface
- {$I GLScene.inc}
- uses
- System.Types,
- System.Classes,
- System.SysUtils,
- System.TypInfo,
- System.Math,
- VCL.Forms,
- VCL.Controls,
- VCL.ExtCtrls,
- GLS.SceneForm,
- GLS.VectorGeometry,
- GLS.Scene,
- GLS.SceneViewer,
- GLS.Strings;
- type
- TGLSimpleNavigationOption = (
- snoInvertMoveAroundX, snoInvertMoveAroundY, // MoveAroundTarget.
- snoInvertZoom, snoInvertMouseWheel, // Zoom.
- snoInvertRotateX, snoInvertRotateY, // RotateTarget.
- snoMouseWheelHandled, // MouseWheel.
- snoShowFPS // Show FPS
- );
- TGLSimpleNavigationOptions = set of TGLSimpleNavigationOption;
- TGLSimpleNavigationAction = (snaNone, snaMoveAroundTarget, snaZoom, snaRotateTarget, snaCustom);
- TGLSimpleNavigationKeyCombination = class;
- TGLSimpleNavigationCustomActionEvent =
- procedure(Sender: TGLSimpleNavigationKeyCombination; Shift: TShiftState; X, Y: Integer) of object;
- TGLSimpleNavigationKeyCombination = class(TCollectionItem)
- private
- FExitOnMatch: Boolean;
- FAction: TGLSimpleNavigationAction;
- FOnCustomAction: TGLSimpleNavigationCustomActionEvent;
- FShiftState: TShiftState;
- protected
- function GetDisplayName: string; override;
- procedure DoOnCustomAction(Shift: TShiftState; X, Y: Integer); virtual;
- public
- constructor Create(Collection: TCollection); override;
- procedure Assign(Source: TPersistent); override;
- published
- property ShiftState: TShiftState read FShiftState write FShiftState default [];
- property ExitOnMatch: Boolean read FExitOnMatch write FExitOnMatch default True;
- property Action: TGLSimpleNavigationAction read FAction write FAction default snaNone;
- property OnCustomAction: TGLSimpleNavigationCustomActionEvent read FOnCustomAction write FOnCustomAction;
- end;
- TGLSimpleNavigationKeyCombinations = class(TOwnedCollection)
- private
- function GetItems(Index: Integer): TGLSimpleNavigationKeyCombination;
- procedure SetItems(Index: Integer; const Value: TGLSimpleNavigationKeyCombination);
- public
- function Add: TGLSimpleNavigationKeyCombination; overload;
- function Add(const AShiftState: TShiftState; const AAction: TGLSimpleNavigationAction; const AExitOnMatch: Boolean = True): TGLSimpleNavigationKeyCombination; overload;
- property Items[Index: Integer]: TGLSimpleNavigationKeyCombination read GetItems write SetItems; default;
- end;
- TGLSimpleNavigation = class(TComponent)
- private
- FTimer: TTimer;
- FForm: TCustomForm;
- FGLSceneViewer: TGLSceneViewer;
- FOldX, FOldY: Integer;
- FFormCaption: string;
- FMoveAroundTargetSpeed: Single;
- FZoomSpeed: Single;
- FOptions: TGLSimpleNavigationOptions;
- FKeyCombinations: TGLSimpleNavigationKeyCombinations;
- FRotateTargetSpeed: Single;
- FOnMouseMove: TMouseMoveEvent;
- FSceneForm: Boolean;
- procedure ShowFPS(Sender: TObject);
- procedure ViewerMouseMove(Sender: TObject;
- Shift: TShiftState; X, Y: Integer);
- procedure ViewerMouseWheel(Sender: TObject; Shift: TShiftState;
- WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
- procedure SetGLSceneViewer(const Value: TGLSceneViewer);
- procedure SetForm(const Value: TCustomForm);
- function StoreFormCaption: Boolean;
- function StoreMoveAroundTargetSpeed: Boolean;
- function StoreZoomSpeed: Boolean;
- procedure SetKeyCombinations(const Value: TGLSimpleNavigationKeyCombinations);
- function StoreRotateTargetSpeed: Boolean;
- procedure SetOptions(const Value: TGLSimpleNavigationOptions);
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- published
- property Form: TCustomForm read FForm write SetForm;
- property GLSceneViewer: TGLSceneViewer read FGLSceneViewer write SetGLSceneViewer;
- property ZoomSpeed: Single read FZoomSpeed write FZoomSpeed stored StoreZoomSpeed;
- property MoveAroundTargetSpeed: Single read FMoveAroundTargetSpeed write FMoveAroundTargetSpeed stored StoreMoveAroundTargetSpeed;
- property RotateTargetSpeed: Single read FRotateTargetSpeed write FRotateTargetSpeed stored StoreRotateTargetSpeed;
- property FormCaption: string read FFormCaption write FFormCaption stored StoreFormCaption;
- property Options: TGLSimpleNavigationOptions read FOptions write SetOptions default [snoMouseWheelHandled, snoShowFPS];
- property KeyCombinations: TGLSimpleNavigationKeyCombinations read FKeyCombinations write SetKeyCombinations;
- property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
- end;
- //-----------------------------------------------------------------------
- implementation
- //-----------------------------------------------------------------------
- const
- vFPSString = '%FPS';
- EPS = 0.001;
- { TGLSimpleNavigation }
- procedure TGLSimpleNavigation.Assign(Source: TPersistent);
- begin
- if Source is TGLSimpleNavigation then
- begin
- { Don't do that, because that might overide the original component's event handlers
- SetForm(TGLSimpleNavigation(Source).FForm);
- SetGLSceneViewer(TGLSimpleNavigation(Source).FGLSceneViewer);
- }
- FZoomSpeed := TGLSimpleNavigation(Source).FZoomSpeed;
- FMoveAroundTargetSpeed := TGLSimpleNavigation(Source).FMoveAroundTargetSpeed;
- FRotateTargetSpeed := TGLSimpleNavigation(Source).FRotateTargetSpeed;
- FFormCaption := TGLSimpleNavigation(Source).FFormCaption;
- FOptions := TGLSimpleNavigation(Source).FOptions;
- FKeyCombinations.Assign(TGLSimpleNavigation(Source).FKeyCombinations);
- end
- else
- inherited; // Die!
- end;
- constructor TGLSimpleNavigation.Create(AOwner: TComponent);
- var
- I: Integer;
- begin
- inherited;
- FKeyCombinations := TGLSimpleNavigationKeyCombinations.Create(Self, TGLSimpleNavigationKeyCombination);
- FKeyCombinations.Add([ssLeft, ssRight], snaZoom, True);
- FKeyCombinations.Add([ssLeft], snaMoveAroundTarget, True);
- FKeyCombinations.Add([ssRight], snaMoveAroundTarget, True);
- FMoveAroundTargetSpeed := 1;
- FRotateTargetSpeed := 1;
- FZoomSpeed := 1.5;
- FOptions := [snoMouseWheelHandled, snoShowFPS];
- FFormCaption := vFPSString;
- FTimer := TTimer.Create(nil);
- FTimer.OnTimer := ShowFPS;
- FOnMouseMove := nil;
- //Detect form
- if AOwner is TCustomForm then
- SetForm(TCustomForm(AOwner));
- //Detect SceneViewer
- if FForm <> nil then
- begin
- if FForm.ComponentCount <> 0 then
- for I := 0 to FForm.ComponentCount - 1 do
- if FForm.Components[I] is TGLSceneViewer then
- begin
- SetGLSceneViewer(TGLSceneViewer(FForm.Components[I]));
- Exit;
- end;
- end;
- end;
- destructor TGLSimpleNavigation.Destroy;
- begin
- FTimer.Free;
- FKeyCombinations.Free;
- if FForm <> nil then
- TForm(FForm).OnMouseWheel := nil;
- if FGLSceneViewer <> nil then
- FGLSceneViewer.OnMouseMove := nil;
- inherited;
- end;
- procedure TGLSimpleNavigation.ViewerMouseWheel(Sender: TObject;
- Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
- var Handled: Boolean);
- var
- Sign: SmallInt;
- lCamera: TGLCamera;
- begin
- if (csDesigning in ComponentState) or (WheelDelta = 0) then
- Exit;
- if snoInvertMouseWheel in FOptions then
- Sign := 1
- else
- Sign := -1;
- if FGLSceneViewer <> nil then
- lCamera := FGLSceneViewer.Camera
- else if FSceneForm then
- lCamera := TGLSceneForm(FForm).Camera
- else
- lCamera := nil;
- if Assigned(lCamera) then
- begin
- if lCamera.CameraStyle = csOrthogonal then
- lCamera.FocalLength := FGLSceneViewer.Camera.FocalLength
- / Power(FZoomSpeed, Sign * WheelDelta div Abs(WheelDelta))
- else
- lCamera.AdjustDistanceToTarget(
- Power(FZoomSpeed, Sign * WheelDelta div Abs(WheelDelta)));
- end;
- Handled := snoMouseWheelHandled in FOptions;
- end;
- procedure TGLSimpleNavigation.ViewerMouseMove(Sender: TObject;
- Shift: TShiftState; X, Y: Integer);
- var
- lCamera: TGLCamera;
- procedure DoZoom;
- var
- Sign: SmallInt;
- begin
- if snoInvertZoom in FOptions then
- Sign := -1
- else
- Sign := 1;
- lCamera.AdjustDistanceToTarget(
- Power(FZoomSpeed, Sign * (Y - FOldY) / 20));
- end;
- procedure DoMoveAroundTarget;
- var
- SignX: SmallInt;
- SignY: SmallInt;
- begin
- if snoInvertMoveAroundX in FOptions then
- SignX := -1
- else
- SignX := 1;
- if snoInvertMoveAroundY in FOptions then
- SignY := -1
- else
- SignY := 1;
- lCamera.MoveAroundTarget(SignX * FMoveAroundTargetSpeed * (FOldY - Y),
- SignY * FMoveAroundTargetSpeed * (FOldX - X));
- end;
- procedure DoRotateTarget;
- var
- SignX: SmallInt;
- SignY: SmallInt;
- begin
- if snoInvertRotateX in FOptions then
- SignX := -1
- else
- SignX := 1;
- if snoInvertRotateY in FOptions then
- SignY := -1
- else
- SignY := 1;
- lCamera.RotateTarget(SignY * FRotateTargetSpeed * (FOldY - Y),
- SignX * FRotateTargetSpeed * (FOldX - X));
- end;
- var
- I: Integer;
- begin
- if csDesigning in ComponentState then
- exit;
- if FGLSceneViewer <> nil then
- lCamera := FGLSceneViewer.Camera
- else if FSceneForm then
- lCamera := TGLSceneForm(FForm).Camera;
- if Assigned(lCamera) then
- begin
- if FKeyCombinations.Count <> 0 then
- for I := 0 to FKeyCombinations.Count - 1 do
- if FKeyCombinations[I].FShiftState <= Shift then
- begin
- case FKeyCombinations[I].FAction of
- snaNone: ; //Ignore.
- snaMoveAroundTarget: DoMoveAroundTarget;
- snaZoom: DoZoom;
- snaRotateTarget: DoRotateTarget;
- snaCustom: FKeyCombinations[I].DoOnCustomAction(Shift, X, Y);
- else
- Assert(False, strErrorEx + strUnknownType);
- end;
- if FKeyCombinations[I].FExitOnMatch then
- Break;
- end;
- end;
- FOldX := X;
- FOldY := Y;
- if Assigned(FOnMouseMove) then
- FOnMouseMove(Self, Shift, X, Y);
- end;
- procedure TGLSimpleNavigation.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited;
- if (AComponent = FGLSceneViewer) and (Operation = opRemove) then
- FGLSceneViewer := nil;
- if (AComponent = FForm) and (Operation = opRemove) then
- FForm := nil;
- end;
- procedure TGLSimpleNavigation.SetKeyCombinations(
- const Value: TGLSimpleNavigationKeyCombinations);
- begin
- FKeyCombinations.Assign(Value);
- end;
- procedure TGLSimpleNavigation.SetForm(const Value: TCustomForm);
- begin
- if FForm <> nil then
- begin
- FForm.RemoveFreeNotification(Self);
- TForm(FForm).OnMouseWheel := nil;
- TForm(FForm).OnMouseMove := nil;
- FSceneForm := False;
- end;
- FForm := Value;
- if FForm <> nil then
- begin
- if FFormCaption = vFPSString then
- FFormCaption := FForm.Caption + ' - ' + vFPSString;
- TForm(FForm).OnMouseWheel := ViewerMouseWheel;
- FForm.FreeNotification(Self);
- {$IFDEF USE_MULTITHREAD}
- if FForm is TGLSceneForm then
- begin
- FSceneForm := True;
- TForm(FForm).OnMouseMove := ViewerMouseMove;
- end;
- {$ENDIF}
- end;
- end;
- procedure TGLSimpleNavigation.SetGLSceneViewer(
- const Value: TGLSceneViewer);
- begin
- if FGLSceneViewer <> nil then
- begin
- FGLSceneViewer.RemoveFreeNotification(Self);
- FGLSceneViewer.OnMouseMove := nil;
- end;
- FGLSceneViewer := Value;
- if FGLSceneViewer <> nil then
- begin
- FGLSceneViewer.OnMouseMove := ViewerMouseMove;
- FGLSceneViewer.FreeNotification(Self);
- end;
- end;
- procedure TGLSimpleNavigation.ShowFPS(Sender: TObject);
- var
- Index: Integer;
- Temp: string;
- begin
- if (FForm <> nil) and
- not (csDesigning in ComponentState) and
- (snoShowFPS in FOptions) then
- begin
- Temp := FFormCaption;
- Index := Pos(vFPSString, Temp);
- if FForm is TGLSceneForm then
- begin
- if Index <> 0 then
- begin
- Delete(Temp, Index, Length(vFPSString));
- Insert(Format('%.*f FPS', [1, TGLSceneForm(FForm).Buffer.FramesPerSecond]), Temp, Index);
- end;
- TGLSceneForm(FForm).Buffer.ResetPerformanceMonitor;
- end
- else if Assigned(FGLSceneViewer) then
- begin
- if Index <> 0 then
- begin
- Delete(Temp, Index, Length(vFPSString));
- Insert(Format('%.*f FPS', [1, FGLSceneViewer.Buffer.FramesPerSecond]), Temp, Index);
- end;
- FGLSceneViewer.ResetPerformanceMonitor;
- end;
- FForm.Caption := Temp;
- end;
- end;
- function TGLSimpleNavigation.StoreFormCaption: Boolean;
- begin
- Result := (FFormCaption <> vFPSString);
- end;
- function TGLSimpleNavigation.StoreMoveAroundTargetSpeed: Boolean;
- begin
- Result := Abs(FMoveAroundTargetSpeed - 1) > EPS;
- end;
- function TGLSimpleNavigation.StoreZoomSpeed: Boolean;
- begin
- Result := Abs(FZoomSpeed - 1.5) > EPS;
- end;
- function TGLSimpleNavigation.StoreRotateTargetSpeed: Boolean;
- begin
- Result := Abs(FRotateTargetSpeed - 1) > EPS;
- end;
- procedure TGLSimpleNavigation.SetOptions(
- const Value: TGLSimpleNavigationOptions);
- begin
- if FOptions <> Value then
- begin
- FOptions := Value;
- end;
- end;
- { TGLSimpleNavigationKeyCombination }
- procedure TGLSimpleNavigationKeyCombination.Assign(Source: TPersistent);
- begin
- if Source is TGLSimpleNavigationKeyCombination then
- begin
- FExitOnMatch := TGLSimpleNavigationKeyCombination(Source).FExitOnMatch;
- FAction := TGLSimpleNavigationKeyCombination(Source).FAction;
- FOnCustomAction := TGLSimpleNavigationKeyCombination(Source).FOnCustomAction;
- FShiftState := TGLSimpleNavigationKeyCombination(Source).FShiftState;
- end
- else
- inherited; // Die!
- end;
- constructor TGLSimpleNavigationKeyCombination.Create(Collection: TCollection);
- begin
- inherited;
- FAction := snaNone;
- FExitOnMatch := True;
- end;
- procedure TGLSimpleNavigationKeyCombination.DoOnCustomAction(
- Shift: TShiftState; X, Y: Integer);
- begin
- if Assigned(FOnCustomAction) then
- FOnCustomAction(Self, Shift, X, Y);
- end;
- function TGLSimpleNavigationKeyCombination.GetDisplayName: string;
- begin
- Result := GetSetProp(Self, 'ShiftState', True) + ' - ' +
- GetEnumName(TypeInfo(TGLSimpleNavigationAction), Integer(FAction));
- end;
- { TGLSimpleNavigationKeyCombinations }
- function TGLSimpleNavigationKeyCombinations.Add: TGLSimpleNavigationKeyCombination;
- begin
- Result := TGLSimpleNavigationKeyCombination(inherited Add);
- end;
- function TGLSimpleNavigationKeyCombinations.Add(
- const AShiftState: TShiftState; const AAction: TGLSimpleNavigationAction;
- const AExitOnMatch: Boolean): TGLSimpleNavigationKeyCombination;
- begin
- Result := Add;
- with Result do
- begin
- FShiftState := AShiftState;
- FAction := AAction;
- FExitOnMatch := AExitOnMatch;
- end;
- end;
- function TGLSimpleNavigationKeyCombinations.GetItems(
- Index: Integer): TGLSimpleNavigationKeyCombination;
- begin
- Result := TGLSimpleNavigationKeyCombination(inherited GetItem(Index));
- end;
- procedure TGLSimpleNavigationKeyCombinations.SetItems(Index: Integer;
- const Value: TGLSimpleNavigationKeyCombination);
- begin
- inherited SetItem(Index, Value);
- end;
- end.
|