Quellcode durchsuchen

Create GLSmoothNavigator.pas

GLScene vor 5 Jahren
Ursprung
Commit
ecad9cea2c
1 geänderte Dateien mit 1654 neuen und 0 gelöschten Zeilen
  1. 1654 0
      Source/GLSmoothNavigator.pas

+ 1654 - 0
Source/GLSmoothNavigator.pas

@@ -0,0 +1,1654 @@
+//
+// This unit is part of the GLScene Engine, http://glscene.org
+//
+
+unit GLSmoothNavigator;
+
+(*
+   An extention of TGLNavigator, which allows to move objects with inertia
+   Note: it is not completely FPS-independant. Only Moving code is, but
+   MoveAroundTarget, Turn[Vertical/Horizontal] and AdjustDistanceTo[..] is not.
+     Don't know why, but when I make their code identical, these function stop
+   working completely. So you probably have to call the AutoScaleParameters
+   procedure once in a while for it to adjust to the current framerate.
+   If someone knows a better way to solve this issue, please contact me via
+   glscene newsgroups. 
+
+    TODO:
+      1) Scale "Old values" too, when callin the Scale parameter procedure to
+         avoid the temporary "freeze" of controls.
+      2) AddImpulse procedures.
+*)
+
+interface
+
+{$I GLScene.inc}
+
+uses
+  System.Types,
+  System.Classes,
+  
+  GLScene,
+  GLPersistentClasses,
+  GLVectorTypes, 
+  GLNavigator, 
+  GLVectorGeometry,
+  GLCoordinates,
+  GLScreen, 
+  XCollection;
+
+type
+
+  (* TGLNavigatorAdjustDistanceParameters includes a basic set of parameters
+     that control the smoothness of movement. *)
+  TGLNavigatorAbstractParameters = class(TPersistent)
+  private
+    FOwner: TPersistent;
+    FInertia: Single;
+    FSpeed: Single;
+    FCutoff: Single;
+    function StoreCutoff: Boolean;
+  protected
+    function StoreInertia: Boolean; virtual;
+    function StoreSpeed: Boolean; virtual;
+      
+    function GetOwner: TPersistent; override;
+  public
+    constructor Create(AOwner: TPersistent); virtual;
+    procedure Assign(Source: TPersistent); override;
+    procedure ScaleParameters(const Value: Single); virtual;
+  published
+    property Inertia: Single read FInertia write FInertia stored StoreInertia;
+    property Speed: Single read FSpeed write FSpeed stored StoreSpeed;
+    property Cutoff: Single read FCutoff write FCutoff stored StoreCutoff; 
+  end;
+
+  TGLSmoothNavigator = class;
+
+  (* TGLNavigatorSmoothChangeItem includes a basic set of parameters
+     that control the smoothness of movement. *)
+  TGLNavigatorSmoothChangeItem = class(TXCollectionItem)
+  private
+    FInertia: Single;
+    FSpeed: Single;
+    FEnabled: Boolean;
+    FSpeedLimit: Single;
+    FCutoff: Double;
+    function StoreInertia: Boolean;
+    function StoreSpeed: Boolean;
+    function StoreSpeedLimit: Boolean;
+    function StoreCutoff: Boolean;
+  protected
+    function GetNavigator: TGLSmoothNavigator;
+  public
+    // Returns False if there was no change.
+    function Proceed(ADeltaTime: Double): Boolean; virtual; abstract;
+    constructor Create(aOwner: TXCollection); override;
+    procedure Assign(Source: TPersistent); override;
+    procedure ScaleParameters(const Value: Single); virtual;
+    procedure ResetTargetValue(); virtual; abstract;
+  published
+    property Inertia: Single read FInertia write FInertia stored StoreInertia;
+    property Speed: Single read FSpeed write FSpeed stored StoreSpeed;
+    property SpeedLimit: Single read FSpeedLimit write FSpeedLimit stored StoreSpeedLimit;
+    property Cutoff: Double read FCutoff write FCutoff stored StoreCutoff;
+    property Enabled: Boolean read FEnabled write FEnabled default True;
+  end;
+
+  TGLNavigatorSmoothChangeSingle = class;
+  TGLNavigatorSmoothChangeSingleGetEvent = function(const ASender: TGLNavigatorSmoothChangeSingle): Single of object;
+  TGLNavigatorSmoothChangeSingleSetEvent = procedure(const ASender: TGLNavigatorSmoothChangeSingle; const AValue: Single) of object;
+
+  // Smoothly change any Single value, so it will become TargetValue in the end.
+  TGLNavigatorSmoothChangeSingle = class(TGLNavigatorSmoothChangeItem)
+  private
+    FTargetValue: Single;
+    FOnGetCurrentValue: TGLNavigatorSmoothChangeSingleGetEvent;
+    FOnSetCurrentValue: TGLNavigatorSmoothChangeSingleSetEvent;
+  public
+    class function FriendlyName: string; override;
+    function Proceed(ADeltaTime: Double): Boolean; override;
+    procedure Assign(Source: TPersistent); override;
+    procedure ResetTargetValue(); override;    
+  published
+    property TargetValue: Single read FTargetValue write FTargetValue;
+    property OnGetCurrentValue: TGLNavigatorSmoothChangeSingleGetEvent read FOnGetCurrentValue write FOnGetCurrentValue;
+    property OnSetCurrentValue: TGLNavigatorSmoothChangeSingleSetEvent read FOnSetCurrentValue write FOnSetCurrentValue;
+  end;
+
+  TGLNavigatorSmoothChangeVector = class;
+  TGLNavigatorSmoothChangeVectorGetEvent = function(const ASender: TGLNavigatorSmoothChangeVector): TVector of object;
+  TGLNavigatorSmoothChangeVectorSetEvent = procedure(const ASender: TGLNavigatorSmoothChangeVector; const AValue: TVector) of object;
+
+  // Smoothly change any Vector4f value, so it will become TargetValue in the end.
+  TGLNavigatorSmoothChangeVector = class(TGLNavigatorSmoothChangeItem)
+  private
+    FTargetValue: TGLCoordinates;
+    FOnGetCurrentValue: TGLNavigatorSmoothChangeVectorGetEvent;
+    FOnSetCurrentValue: TGLNavigatorSmoothChangeVectorSetEvent;
+    procedure SetTargetValue(const Value: TGLCoordinates);
+  public
+    class function FriendlyName: string; override;
+    function Proceed(ADeltaTime: Double): Boolean; override;
+    procedure Assign(Source: TPersistent); override;
+    constructor Create(aOwner: TXCollection); override;
+    destructor Destroy; override;
+    procedure ResetTargetValue(); override;
+  published
+    property TargetValue: TGLCoordinates read FTargetValue write SetTargetValue;
+    property OnGetCurrentValue: TGLNavigatorSmoothChangeVectorGetEvent read FOnGetCurrentValue write FOnGetCurrentValue;
+    property OnSetCurrentValue: TGLNavigatorSmoothChangeVectorSetEvent read FOnSetCurrentValue write FOnSetCurrentValue;
+  end;
+
+  TGLNavigatorSmoothChangeItemClass = class of TGLNavigatorSmoothChangeItem;
+
+  // XCollection of TGLNavigatorSmoothChangeItem.
+  TGLNavigatorSmoothChangeItems = class(TXCollection)
+  private
+    function GetItems(const Index : Integer): TGLNavigatorSmoothChangeItem;
+    procedure SetItems(const Index : Integer; const Value: TGLNavigatorSmoothChangeItem);
+  protected
+    procedure DoProceed(ADeltaTime: Double);
+  public
+    function Add(AClass : TGLNavigatorSmoothChangeItemClass): TGLNavigatorSmoothChangeItem;
+    function CanAdd(AClass: TXCollectionItemClass): Boolean; override;
+    class function ItemsClass: TXCollectionItemClass; override;
+    property Items[const Index : Integer]: TGLNavigatorSmoothChangeItem read GetItems write
+            SetItems; default;
+  end;
+
+  (* TGLNavigatorAdjustDistanceParameters is wrapper for all parameters that
+     affect how the AdjustDisanceTo[...] methods work *)
+  TGLNavigatorAdjustDistanceParameters = class(TGLNavigatorAbstractParameters)
+  private
+    FOldDistanceRatio: Single;
+    FImpulseSpeed: Single;
+    function StoreImpulseSpeed: Boolean;
+  public
+    constructor Create(AOwner: TPersistent); override;
+    procedure Assign(Source: TPersistent); override;
+    procedure ScaleParameters(const Value: Single); override;
+
+    procedure AddImpulse(const Impulse: Single); virtual;
+  published
+    property ImpulseSpeed: Single read FImpulseSpeed write FImpulseSpeed stored StoreImpulseSpeed;
+  end;
+
+  (* TGLNavigatorAdjustDistanceParameters is wrapper for all parameters that
+     affect how the AdjustDisanceTo[...]Ex methods work
+     You need to set the TargetObject and desired distance to it,
+     then call AdjustDisanceTo[...]Ex() in your Cadencer.OnProgress code. *)
+  TGLNavigatorAdjustDistanceParametersEx = class(TGLNavigatorAbstractParameters)
+  private
+    FSpeedLimit: Single;
+    FTargetDistance: Single;
+    function StoreSpeedLimit: Boolean;
+    function StoreTargetDistance: Boolean;
+  protected
+    function StoreSpeed: Boolean; override;
+    function StoreInertia: Boolean; override;
+  public
+    constructor Create(AOwner: TPersistent); override;
+    procedure Assign(Source: TPersistent); override;
+  published
+    property TargetDistance: Single read FTargetDistance write FTargetDistance stored StoreTargetDistance;
+    property SpeedLimit: Single read FSpeedLimit write FSpeedLimit stored StoreSpeedLimit;
+  end;
+
+  {TGLNavigatorInertiaParameters is wrapper for all parameters that affect the
+       smoothness of movement 
+  }
+  TGLNavigatorInertiaParameters = class(TPersistent)
+  private
+    FOwner: TPersistent;
+
+    OldTurnHorizontalAngle: Single;
+    OldTurnVerticalAngle: Single;
+
+    OldMoveForwardDistance: Single;
+    OldStrafeHorizontalDistance: Single;
+    OldStrafeVerticalDistance: Single;
+
+    FTurnInertia: Single;
+    FTurnSpeed: Single;
+    FTurnMaxAngle: Single;
+    FMovementAcceleration: Single;
+    FMovementInertia: Single;
+    FMovementSpeed: Single;
+
+    function StoreTurnMaxAngle: Boolean;
+    function StoreMovementAcceleration: Boolean;
+    function StoreMovementInertia: Boolean;
+    function StoreMovementSpeed: Boolean;
+    function StoreTurnInertia: Boolean;
+    function StoreTurnSpeed: Boolean;
+  protected
+    function GetOwner: TPersistent; override;
+  public
+    constructor Create(AOwner: TPersistent); virtual;
+    procedure Assign(Source: TPersistent); override;
+    procedure ScaleParameters(const Value: Single); virtual;
+  published
+    property MovementAcceleration: Single read FMovementAcceleration write FMovementAcceleration stored StoreMovementAcceleration;
+    property MovementInertia: Single read FMovementInertia write FMovementInertia stored StoreMovementInertia;
+    property MovementSpeed: Single read FMovementSpeed write FMovementSpeed stored StoreMovementSpeed;
+
+    property TurnMaxAngle: Single read FTurnMaxAngle write FTurnMaxAngle stored StoreTurnMaxAngle;
+    property TurnInertia: Single read FTurnInertia write FTurnInertia stored StoreTurnInertia;
+    property TurnSpeed: Single read FTurnSpeed write FTurnSpeed stored StoreTurnSpeed;
+  end;
+
+
+  {TGLNavigatorGeneralParameters is a wrapper for all general inertia parameters.
+
+     These properties mean that if ExpectedMaxFPS is 100, FAutoScaleMin is 0.1,
+     FAutoScaleMax is 0.75 then the "safe range" for it to change is [10..75].
+     If these bounds are violated, then ExpectedMaxFPS is automaticly increased
+     or decreased by AutoScaleMult.
+  }
+  TGLNavigatorGeneralParameters = class(TPersistent)
+  private
+    FOwner: TPersistent;
+    FAutoScaleMin: Single;
+    FAutoScaleMax: Single;
+    FAutoScaleMult: Single;
+
+    function StoreAutoScaleMax: Boolean;
+    function StoreAutoScaleMin: Boolean;
+    function StoreAutoScaleMult: Boolean;
+  protected
+    function GetOwner: TPersistent; override;
+  public
+    constructor Create(AOwner: TPersistent); virtual;
+    procedure Assign(Source: TPersistent); override;
+  published
+    property AutoScaleMin: Single read FAutoScaleMin write FAutoScaleMin stored StoreAutoScaleMin;
+    property AutoScaleMax: Single read FAutoScaleMax write FAutoScaleMax stored StoreAutoScaleMax;
+    property AutoScaleMult: Single read FAutoScaleMult write FAutoScaleMult stored StoreAutoScaleMult;
+  end;
+
+
+  {TGLNavigatorMoveAroundParameters is a wrapper for all parameters that
+      effect how the TGLBaseSceneObject.MoveObjectAround() procedure works
+  }
+  TGLNavigatorMoveAroundParameters = class(TPersistent)
+  private
+    FOwner: TPersistent;
+    FTargetObject: TGLBaseSceneObject;
+
+    FOldPitchInertiaAngle : Single;
+    FOldTurnInertiaAngle  : Single;
+
+    FPitchSpeed : Single;
+    FTurnSpeed  : Single;
+    FInertia          : Single;
+    FMaxAngle         : Single;
+    FCutoff: Double;
+
+    function StoreInertia: Boolean;
+    function StoreMaxAngle: Boolean;
+    function StorePitchSpeed: Boolean;
+    function StoreTurnSpeed: Boolean;
+    procedure SetTargetObject(const Value: TGLBaseSceneObject);
+    function StoreCutoff: Boolean;
+  protected
+    function GetOwner: TPersistent; override;
+  public
+    constructor Create(AOwner: TPersistent); virtual;
+    procedure Assign(Source: TPersistent); override;
+    procedure ScaleParameters(const Value: Single); virtual;
+  published
+    property Inertia: Single read FInertia write FInertia stored StoreInertia;
+    property MaxAngle: Single read FMaxAngle write FMaxAngle stored StoreMaxAngle;
+    property PitchSpeed: Single read FPitchSpeed write FPitchSpeed stored StorePitchSpeed;
+    property TurnSpeed: Single read FTurnSpeed write FTurnSpeed stored StoreTurnSpeed;
+    property TargetObject: TGLBaseSceneObject read FTargetObject write SetTargetObject;
+    property Cutoff: Double read FCutoff write FCutoff stored StoreCutoff;    
+  end;
+
+  {TGLSmoothNavigator is the component for moving a TGLBaseSceneObject, and all
+       classes based on it, this includes all the objects from the Scene Editor. 
+
+     It uses complex smoothing algorithms, most of which are FPS-dependant.
+     Make sure your limit your FPS and set MaxExpectedDeltaTime to a value
+     that is aproximatly 5 times less than your usual deltatime.
+  }
+  TGLSmoothNavigator = class(TGLNavigator)
+  private
+    FMaxExpectedDeltaTime: Double;
+    FInertiaParams: TGLNavigatorInertiaParameters;
+    FGeneralParams: TGLNavigatorGeneralParameters;
+    FMoveAroundParams: TGLNavigatorMoveAroundParameters;
+    FAdjustDistanceParams: TGLNavigatorAdjustDistanceParameters;
+    FAdjustDistanceParamsEx: TGLNavigatorAdjustDistanceParametersEx;
+    FCustomAnimatedItems: TGLNavigatorSmoothChangeItems;
+    procedure SetInertiaParams(const Value: TGLNavigatorInertiaParameters);
+    function StoreMaxExpectedDeltaTime: Boolean;
+    procedure SetGeneralParams(const Value: TGLNavigatorGeneralParameters);
+    procedure SetMoveAroundParams(const Value: TGLNavigatorMoveAroundParameters);
+    procedure SetAdjustDistanceParams(const Value: TGLNavigatorAdjustDistanceParameters);
+    procedure SetAdjustDistanceParamsEx(
+      const Value: TGLNavigatorAdjustDistanceParametersEx);
+    procedure SetCustomAnimatedItems(
+      const Value: TGLNavigatorSmoothChangeItems);
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+  public
+    // Constructors-destructors.
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+
+    // From TGLNavigator. Probably, should not be public.
+    procedure SetObject(Value: TGLBaseSceneObject); override;
+
+    // Uses InertiaParams.
+    procedure TurnHorizontal(Angle: Single; ADeltaTime: Double); virtual;
+    procedure TurnVertical(Angle: Single; ADeltaTime: Double); virtual;
+    procedure FlyForward(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False); virtual;
+    procedure MoveForward(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False); virtual;
+    procedure StrafeHorizontal(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False); virtual;
+    procedure StrafeVertical(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False); virtual;
+
+    // Uses MoveAroundParams. Returns True, if object was actually moved.
+    function MoveAroundTarget(const PitchDelta, TurnDelta : Single; const ADeltaTime: Double): Boolean;
+    function MoveObjectAround(const AObject: TGLBaseSceneObject; PitchDelta, TurnDelta : Single; ADeltaTime: Double): Boolean;
+
+    // Uses AdjustDistanceParams.
+    function AdjustDistanceToPoint(const  APoint: TVector; const DistanceRatio : Single; ADeltaTime: Double): Boolean;
+    function AdjustDistanceToTarget(const DistanceRatio : Single; const ADeltaTime: Double): Boolean;
+
+    // Uses AdjustDistanceParamsEx.
+    function AdjustDistanceToPointEx(const  APoint: TVector; ADeltaTime: Double): Boolean;
+    function AdjustDistanceToTargetEx(const ADeltaTime: Double): Boolean;
+
+    // Uses CustomAnimatedItems.
+    procedure AnimateCustomItems(const ADeltaTime: Double); virtual;
+
+    // Uses GeneralParams.
+      {In ScaleParameters, Value should be around 1. }
+    procedure ScaleParameters(const Value: Single); virtual;
+    procedure AutoScaleParameters(const FPS: Single); virtual;
+    procedure AutoScaleParametersUp(const FPS: Single); virtual;
+  published
+    property MaxExpectedDeltaTime: Double read FMaxExpectedDeltaTime write FMaxExpectedDeltaTime stored StoreMaxExpectedDeltaTime;
+    property InertiaParams: TGLNavigatorInertiaParameters read FInertiaParams write SetInertiaParams;
+    property GeneralParams: TGLNavigatorGeneralParameters read FGeneralParams write SetGeneralParams;
+    property MoveAroundParams: TGLNavigatorMoveAroundParameters read FMoveAroundParams write SetMoveAroundParams;
+    property AdjustDistanceParams: TGLNavigatorAdjustDistanceParameters read FAdjustDistanceParams write SetAdjustDistanceParams;
+    property AdjustDistanceParamsEx: TGLNavigatorAdjustDistanceParametersEx read FAdjustDistanceParamsEx write SetAdjustDistanceParamsEx;
+    property CustomAnimatedItems: TGLNavigatorSmoothChangeItems read FCustomAnimatedItems write SetCustomAnimatedItems;
+  end;
+
+
+  {TGLSmoothUserInterface is the component which reads the userinput and transform it into action. 
+	    Mouselook(ADeltaTime: double) : handles mouse look... Should be called
+                           in the Cadencer event. (Though it works everywhere!)
+	   The four properties to get you started are:
+            InvertMouse     : Inverts the mouse Y axis.
+	    AutoUpdateMouse : If enabled (by defaul), than handles all mouse updates.
+	    GLNavigator     : The Navigator which receives the user movement.
+	    GLVertNavigator : The Navigator which if set receives the vertical user
+                           movement. Used mostly for cameras....
+      
+   }
+  TGLSmoothUserInterface = class(TComponent)
+  private
+    FAutoUpdateMouse: Boolean;
+    FMouseLookActive: Boolean;
+    FSmoothNavigator: TGLSmoothNavigator;
+    FSmoothVertNavigator: TGLSmoothNavigator;
+    FInvertMouse: Boolean;
+    FOriginalMousePos: TGLCoordinates2;
+    procedure SetSmoothNavigator(const Value: TGLSmoothNavigator); virtual;
+    procedure SetOriginalMousePos(const Value: TGLCoordinates2); virtual;
+    procedure SetSmoothVertNavigator(const Value: TGLSmoothNavigator); virtual;
+    procedure SetMouseLookActive(const Value: Boolean); virtual;
+  protected
+    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
+  public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+
+    procedure TurnHorizontal(const Angle : Single; const ADeltaTime: Double); virtual;
+    procedure TurnVertical(const Angle : Single; const ADeltaTime: Double); virtual;
+    procedure MouseLookActiveToggle; virtual;
+
+    function MouseLook(const ADeltaTime: Double): Boolean; overload;
+    function MouseLook(const NewXY: TPoint; const ADeltaTime: Double): Boolean; overload;
+    function MouseLook(const NewX, NewY: Integer; const ADeltaTime: Double): Boolean; overload;
+  published
+    property AutoUpdateMouse: Boolean read FAutoUpdateMouse write FAutoUpdateMouse default True;
+    property MouseLookActive: Boolean read FMouseLookActive write SetMouseLookActive default False;
+    property SmoothVertNavigator: TGLSmoothNavigator read FSmoothVertNavigator write SetSmoothVertNavigator;
+    property SmoothNavigator: TGLSmoothNavigator read FSmoothNavigator write SetSmoothNavigator;
+    property InvertMouse: Boolean read FInvertMouse write FInvertMouse default False;
+    property OriginalMousePos: TGLCoordinates2 read FOriginalMousePos write SetOriginalMousePos;
+  end;
+
+implementation
+
+const
+  EPS =  0.001;
+  EPS2 = 0.0001;
+  EPS8 = 0.00000001;
+
+{ TGLSmoothNavigator }
+
+constructor TGLSmoothNavigator.Create(AOwner: TComponent);
+begin
+  inherited;
+  FMaxExpectedDeltaTime := 0.001;
+  FInertiaParams := TGLNavigatorInertiaParameters.Create(Self);
+  FGeneralParams := TGLNavigatorGeneralParameters.Create(Self);
+  FMoveAroundParams := TGLNavigatorMoveAroundParameters.Create(Self);
+  FAdjustDistanceParams := TGLNavigatorAdjustDistanceParameters.Create(Self);
+  FAdjustDistanceParamsEx := TGLNavigatorAdjustDistanceParametersEx.Create(Self);
+  FCustomAnimatedItems := TGLNavigatorSmoothChangeItems.Create(Self);
+end;
+
+destructor TGLSmoothNavigator.Destroy;
+begin
+  FInertiaParams.Free;
+  FGeneralParams.Free;
+  FMoveAroundParams.Free;
+  FAdjustDistanceParams.Free;
+  FAdjustDistanceParamsEx.Free;
+  FCustomAnimatedItems.Free;
+  inherited;
+end;
+
+procedure TGLSmoothNavigator.SetInertiaParams(
+  const Value: TGLNavigatorInertiaParameters);
+begin
+  FInertiaParams.Assign(Value);
+end;
+
+procedure TGLSmoothNavigator.TurnHorizontal(Angle: Single; ADeltaTime: Double);
+var
+  FinalAngle: Single;
+begin
+  with FInertiaParams do
+  begin
+    FinalAngle := 0;
+    Angle := Angle * FTurnSpeed;
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      Angle := ClampValue((Angle * FMaxExpectedDeltaTime + OldTurnHorizontalAngle * FTurnInertia) / (FTurnInertia + 1), -FTurnMaxAngle, FTurnMaxAngle);
+      OldTurnHorizontalAngle := Angle;
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalAngle := FinalAngle + Angle;
+    end;
+  end;
+
+  if (Abs(FinalAngle) > EPS) then
+    inherited TurnHorizontal(FinalAngle);
+end;
+
+procedure TGLSmoothNavigator.TurnVertical(Angle: Single; ADeltaTime: Double);
+var
+  FinalAngle: Single;
+begin
+  with FInertiaParams do
+  begin
+    FinalAngle := 0;
+    Angle := Angle * FTurnSpeed;
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      Angle := ClampValue((Angle * FMaxExpectedDeltaTime + OldTurnVerticalAngle * FTurnInertia) / (FTurnInertia + 1), -FTurnMaxAngle, FTurnMaxAngle);
+      OldTurnVerticalAngle := Angle;
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalAngle := FinalAngle + Angle;
+    end;
+  end;
+
+  if (Abs(FinalAngle) > EPS) then
+    inherited TurnVertical(FinalAngle);
+end;
+
+
+procedure TGLSmoothNavigator.MoveForward(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False);
+var
+  FinalDistance: Single;
+  Distance:      Single;
+begin
+  with FInertiaParams do
+  begin
+    if Plus then
+      Distance := FMovementSpeed
+    else if Minus then
+      Distance := -FMovementSpeed
+    else
+      Distance := 0;
+
+    if Accelerate then
+      Distance := Distance * FMovementAcceleration;
+
+    FinalDistance := 0;
+
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      OldMoveForwardDistance := (Distance * FMaxExpectedDeltaTime + OldMoveForwardDistance * FMovementInertia) / (FMovementInertia + 1);
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalDistance := FinalDistance + OldMoveForwardDistance;
+    end;
+  end;
+
+  if Abs(FinalDistance) > EPS then
+    inherited MoveForward(FinalDistance);
+end;
+
+procedure TGLSmoothNavigator.FlyForward(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False);
+var
+  FinalDistance: Single;
+  Distance:      Single;
+begin
+  with FInertiaParams do
+  begin
+    if Plus then
+      Distance := FMovementSpeed
+    else if Minus then
+      Distance := -FMovementSpeed
+    else
+      Distance := 0;
+
+    if Accelerate then
+      Distance := Distance * FMovementAcceleration;
+
+    FinalDistance := 0;
+
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      OldMoveForwardDistance := (Distance * FMaxExpectedDeltaTime + OldMoveForwardDistance * FMovementInertia) / (FMovementInertia + 1);
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalDistance := FinalDistance + OldMoveForwardDistance;
+    end;
+  end;
+
+  if Abs(FinalDistance) > EPS then
+    inherited FlyForward(FinalDistance);
+end;
+
+procedure TGLSmoothNavigator.StrafeHorizontal(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False);
+var
+  FinalDistance: Single;
+  Distance:      Single;
+begin
+  with FInertiaParams do
+  begin
+    if Plus then
+      Distance := FMovementSpeed
+    else if Minus then
+      Distance := -FMovementSpeed
+    else
+      Distance := 0;
+
+    if Accelerate then
+      Distance := Distance * FMovementAcceleration;
+
+    FinalDistance := 0;
+
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      OldStrafeHorizontalDistance := (Distance * FMaxExpectedDeltaTime + OldStrafeHorizontalDistance * FMovementInertia) / (FMovementInertia + 1);
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalDistance := FinalDistance + OldStrafeHorizontalDistance;
+    end;
+  end;
+
+  if Abs(FinalDistance) > EPS then
+    inherited StrafeHorizontal(FinalDistance);
+end;
+
+procedure TGLSmoothNavigator.StrafeVertical(const Plus, Minus: Boolean; ADeltaTime: Double; const Accelerate: Boolean = False);
+var
+  FinalDistance: Single;
+  Distance:      Single;
+begin
+  with FInertiaParams do
+  begin
+    if Plus then
+      Distance := FMovementSpeed
+    else if Minus then
+      Distance := -FMovementSpeed
+    else
+      Distance := 0;
+
+    if Accelerate then
+      Distance := Distance * FMovementAcceleration;
+
+    FinalDistance := 0;
+
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      OldStrafeVerticalDistance := (Distance * FMaxExpectedDeltaTime + OldStrafeVerticalDistance * FMovementInertia) / (FMovementInertia + 1);
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalDistance := FinalDistance + OldStrafeVerticalDistance;
+    end;
+  end;
+
+  if Abs(FinalDistance) > EPS then
+    inherited StrafeVertical(FinalDistance);
+end;
+
+procedure TGLSmoothNavigator.AutoScaleParameters(const FPS: Single);
+begin
+  with FGeneralParams do
+  begin
+    if FPS > FAutoScaleMax / FMaxExpectedDeltatime then
+      ScaleParameters(FAutoScaleMult)
+    else if FPS < FAutoScaleMin / FMaxExpectedDeltatime then
+      ScaleParameters(1/FAutoScaleMult);
+  end;
+end;
+
+
+procedure TGLSmoothNavigator.AutoScaleParametersUp(const FPS: Single);
+begin
+  with FGeneralParams do
+  begin
+    if FPS > FAutoScaleMax / FMaxExpectedDeltatime then
+      ScaleParameters(FAutoScaleMult)
+  end;
+end;
+
+procedure TGLSmoothNavigator.ScaleParameters(const Value: Single);
+begin
+  Assert(Value > 0);
+  FMaxExpectedDeltatime := FMaxExpectedDeltatime / Value;
+  FInertiaParams.ScaleParameters(Value);
+  FMoveAroundParams.ScaleParameters(Value);
+  FAdjustDistanceParams.ScaleParameters(Value);
+end;
+
+function TGLSmoothNavigator.StoreMaxExpectedDeltaTime: Boolean;
+begin
+  Result := Abs(FMaxExpectedDeltaTime - 0.001) > EPS2;
+end;
+
+procedure TGLSmoothNavigator.SetGeneralParams(
+  const Value: TGLNavigatorGeneralParameters);
+begin
+  FGeneralParams.Assign(Value);
+end;
+
+procedure TGLSmoothNavigator.SetMoveAroundParams(
+  const Value: TGLNavigatorMoveAroundParameters);
+begin
+  FMoveAroundParams.Assign(Value);
+end;
+
+procedure TGLSmoothNavigator.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if Operation = opRemove then
+  begin
+    if AComponent = FMoveAroundParams.FTargetObject then
+      FMoveAroundParams.FTargetObject := nil;
+  end;
+end;
+
+procedure TGLSmoothNavigator.SetObject(Value: TGLBaseSceneObject);
+var
+  I: Integer;
+begin
+  inherited;
+  // Try to detect a TargetObject.
+  if Value <> nil then
+    if FMoveAroundParams.TargetObject = nil then
+    begin
+      // May be it is a camera...
+      if Value is TGLCamera then
+        FMoveAroundParams.TargetObject := TGLCamera(Value).TargetObject
+      else
+      begin
+        // May be it has camera children...
+        if Value.Count <> 0 then
+          for I := 0 to Value.Count - 1 do
+            if Value.Children[I] is TGLCamera then
+            begin
+              FMoveAroundParams.TargetObject := TGLCamera(Value.Children[I]).TargetObject;
+              Exit;
+            end;
+      end;
+    end;
+end;
+
+function TGLSmoothNavigator.MoveAroundTarget(const PitchDelta, TurnDelta: Single;
+  const ADeltaTime: Double): Boolean;
+begin
+  Result := MoveObjectAround(FMoveAroundParams.FTargetObject, PitchDelta, TurnDelta, ADeltaTime);
+end;
+
+function TGLSmoothNavigator.MoveObjectAround(
+  const AObject: TGLBaseSceneObject; PitchDelta, TurnDelta: Single;
+  ADeltaTime: Double): Boolean;
+var
+  FinalPitch: Single;
+  FinalTurn:  Single;
+
+  lUp: TVector;
+begin
+  Result := False;
+  FinalPitch := 0;
+  FinalTurn := 0;
+  with FMoveAroundParams do
+  begin
+    PitchDelta := PitchDelta * FPitchSpeed;
+    TurnDelta := TurnDelta * FTurnSpeed;
+
+    while ADeltaTime > FMaxExpectedDeltatime do
+    begin
+      PitchDelta := ClampValue((PitchDelta * FMaxExpectedDeltatime + FOldPitchInertiaAngle * FInertia) / (FInertia + 1), - FMaxAngle, FMaxAngle);
+      FOldPitchInertiaAngle := PitchDelta;
+      FinalPitch := FinalPitch + PitchDelta;
+      TurnDelta := ClampValue((TurnDelta * FMaxExpectedDeltatime + FOldTurnInertiaAngle * FInertia) / (FInertia + 1), - FMaxAngle, FMaxAngle);
+      FOldTurnInertiaAngle := TurnDelta;
+      FinalTurn := FinalTurn + TurnDelta;
+
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltatime;
+    end;
+
+    if UseVirtualUp then
+      lUp := VirtualUp.AsVector
+    else
+      lUp := MovingObject.AbsoluteUp;
+
+    if (Abs(FinalPitch) > FCutOff) or (Abs(FinalTurn) > FCutOff) then
+    begin
+      MovingObject.AbsolutePosition := GLVectorGeometry.MoveObjectAround(
+        MovingObject.AbsolutePosition, lUp, AObject.AbsolutePosition, FinalPitch, FinalTurn);
+      Result := True;
+    end;
+  end;
+end;
+
+
+function TGLSmoothNavigator.AdjustDistanceToPoint(const APoint: TVector;
+  const DistanceRatio: Single; ADeltaTime: Double): Boolean;
+
+  // Based on TGLCamera.AdjustDistanceToTarget
+  procedure DoAdjustDistanceToPoint(const DistanceRatio: Single);
+  var
+    vect: TVector;
+  begin
+    vect := VectorSubtract(MovingObject.AbsolutePosition, APoint);
+    ScaleVector(vect, (distanceRatio - 1));
+    AddVector(vect, MovingObject.AbsolutePosition);
+    if Assigned(MovingObject.Parent) then
+       vect := MovingObject.Parent.AbsoluteToLocal(vect);
+    MovingObject.Position.AsVector := vect;
+    Result := True;
+  end;
+
+var
+  FinalDistanceRatio: Single;
+  TempDistanceRatio:  Single;
+begin
+  with FAdjustDistanceParams do
+  begin
+    TempDistanceRatio := DistanceRatio * FSpeed;
+    FinalDistanceRatio := 0;
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      TempDistanceRatio := (TempDistanceRatio * FMaxExpectedDeltaTime + FOldDistanceRatio * FInertia) / (FInertia + 1);
+      FOldDistanceRatio := TempDistanceRatio;
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+      FinalDistanceRatio := FinalDistanceRatio + FOldDistanceRatio / FMaxExpectedDeltaTime;
+    end;
+
+    if Abs(FinalDistanceRatio) > FCutoff then
+    begin
+      if FinalDistanceRatio > 0 then
+        DoAdjustDistanceToPoint(1 / (1 + FinalDistanceRatio))
+      else
+        DoAdjustDistanceToPoint(1 * (1 - FinalDistanceRatio))
+    end
+    else
+      Result := False;
+  end;
+end;
+
+function TGLSmoothNavigator.AdjustDistanceToTarget(const DistanceRatio: Single;
+  const ADeltaTime: Double): Boolean;
+begin
+  Assert(FMoveAroundParams.FTargetObject <> nil);
+  Result := AdjustDistanceToPoint(FMoveAroundParams.FTargetObject.AbsolutePosition,
+                        DistanceRatio, ADeltaTime);
+end;
+
+procedure TGLSmoothNavigator.SetAdjustDistanceParams(
+  const Value: TGLNavigatorAdjustDistanceParameters);
+begin
+  FAdjustDistanceParams.Assign(Value);
+end;
+
+function TGLSmoothNavigator.AdjustDistanceToPointEx(const APoint: TVector;
+  ADeltaTime: Double): Boolean;
+
+var
+  lAbsolutePosition: TVector;
+  lCurrentDistance: Single;
+  lDistanceDifference, lTempCurrentDistance: Single;
+
+  procedure DoAdjustDistanceToPoint(const DistanceValue: Single);
+  var
+    vect: TVector;
+  begin
+    vect := VectorSubtract(APoint, lAbsolutePosition);
+    NormalizeVector(vect);
+    ScaleVector(vect, DistanceValue);
+    MovingObject.AbsolutePosition := VectorAdd(lAbsolutePosition, vect);
+    Result := True;
+  end;
+
+begin
+  lAbsolutePosition := MovingObject.AbsolutePosition;
+  lCurrentDistance := VectorDistance(lAbsolutePosition, APoint);
+  lDistanceDifference := lCurrentDistance - FAdjustDistanceParamsEx.FTargetDistance;
+
+  with FAdjustDistanceParamsEx do
+  begin
+    lTempCurrentDistance := 0;
+    while ADeltaTime > FMaxExpectedDeltaTime do
+    begin
+      lTempCurrentDistance := (FSpeed * FMaxExpectedDeltaTime * lDistanceDifference * FInertia) / (FInertia + 1);
+//      lTempCurrentDistance := (FSpeed * FMaxExpectedDeltaTime + lDistanceDifference * FInertia) / (FInertia + 1);-  this also works, but a bit different.
+      ADeltaTime := ADeltaTime - FMaxExpectedDeltaTime;
+    end;
+    
+    lTempCurrentDistance :=  ClampValue(lTempCurrentDistance, -FSpeedLimit * ADeltaTime, FSpeedLimit * ADeltaTime);
+
+    if Abs(lTempCurrentDistance) > FCutoff then
+      DoAdjustDistanceToPoint(lTempCurrentDistance)
+    else
+      Result := False;
+  end;
+end;
+
+function TGLSmoothNavigator.AdjustDistanceToTargetEx(
+  const ADeltaTime: Double): Boolean;
+begin
+  Assert(FMoveAroundParams.FTargetObject <> nil);
+  Result := AdjustDistanceToPointEx(FMoveAroundParams.FTargetObject.AbsolutePosition,
+                          ADeltaTime);
+end;
+
+procedure TGLSmoothNavigator.SetAdjustDistanceParamsEx(
+  const Value: TGLNavigatorAdjustDistanceParametersEx);
+begin
+  FAdjustDistanceParamsEx.Assign(Value);
+end;
+
+procedure TGLSmoothNavigator.AnimateCustomItems(const ADeltaTime: Double);
+begin
+  FCustomAnimatedItems.DoProceed(ADeltaTime);
+end;
+
+procedure TGLSmoothNavigator.SetCustomAnimatedItems(
+  const Value: TGLNavigatorSmoothChangeItems);
+begin
+  FCustomAnimatedItems.Assign(Value);
+end;
+
+{ TGLSmoothUserInterface }
+
+function TGLSmoothUserInterface.MouseLook(
+  const ADeltaTime: Double): Boolean;
+var
+  MousePos: TPoint;
+begin
+  Assert(FAutoUpdateMouse, 'AutoUpdateMouse must be True to use this function');
+  if FMouseLookActive then
+  begin
+    GLGetCursorPos(MousePos);
+    Result := Mouselook(MousePos.X, MousePos.Y, ADeltaTime);
+    GLSetCursorPos(Round(OriginalMousePos.X), Round(OriginalMousePos.Y));
+  end
+  else
+    Result := False;
+end;
+
+function TGLSmoothUserInterface.Mouselook(const NewX, NewY: Integer; const ADeltaTime: Double): Boolean;
+var
+  DeltaX, DeltaY: Single;
+begin
+  Result := False;
+  if FMouseLookActive then
+  begin
+    Deltax := (NewX - FOriginalMousePos.X);
+    Deltay := (FOriginalMousePos.Y - NewY);
+
+    if InvertMouse then
+      DeltaY := -DeltaY;
+
+    SmoothNavigator.TurnHorizontal(DeltaX, ADeltaTime);
+    SmoothNavigator.TurnVertical(DeltaY, ADeltaTime);
+
+    Result := (DeltaX <> 0) or (DeltaY <> 0);
+  end;
+end;
+
+
+function TGLSmoothUserInterface.MouseLook(const NewXY: TPoint; const ADeltaTime: Double): Boolean;
+begin
+  Result := Mouselook(NewXY.X, NewXY.Y, ADeltaTime);
+end;
+
+constructor TGLSmoothUserInterface.Create(AOwner: TComponent);
+begin
+  inherited;
+  FMouseLookActive := False;
+  FAutoUpdateMouse := True;
+  FOriginalMousePos := TGLCoordinates2.CreateInitialized(Self,
+                             VectorMake(GLGetScreenWidth div 2,
+                             GLGetScreenHeight div 2, 0, 0), csPoint2D);
+end;
+
+procedure TGLSmoothUserInterface.Notification(AComponent: TComponent;
+  Operation: TOperation);
+begin
+  inherited;
+  if (Operation = opRemove) then
+  begin
+    if AComponent = FSmoothNavigator then
+      FSmoothNavigator := nil;
+    if AComponent = FSmoothVertNavigator then
+      FSmoothNavigator := nil;
+  end;
+end;
+
+procedure TGLSmoothUserInterface.SetSmoothNavigator(
+  const Value: TGLSmoothNavigator);
+begin
+  if FSmoothNavigator <> nil then
+    FSmoothNavigator.RemoveFreeNotification(Self);
+
+  FSmoothNavigator := Value;
+
+  if FSmoothNavigator <> nil then
+    FSmoothNavigator.FreeNotification(Self);
+end;
+
+destructor TGLSmoothUserInterface.Destroy;
+begin
+  FOriginalMousePos.Destroy;
+  inherited;
+end;
+
+procedure TGLSmoothUserInterface.SetOriginalMousePos(
+  const Value: TGLCoordinates2);
+begin
+  FOriginalMousePos.Assign(Value);
+end;
+
+procedure TGLSmoothUserInterface.SetSmoothVertNavigator(
+  const Value: TGLSmoothNavigator);
+begin
+  if FSmoothVertNavigator <> nil then
+    FSmoothVertNavigator.RemoveFreeNotification(Self);
+
+  FSmoothVertNavigator := Value;
+
+  if FSmoothVertNavigator <> nil then
+    FSmoothVertNavigator.FreeNotification(Self);
+end;
+
+procedure TGLSmoothUserInterface.MouseLookActiveToggle;
+begin
+  if FMouseLookActive then
+    SetMouseLookActive(False)
+  else
+    SetMouseLookActive(True)
+end;
+
+procedure TGLSmoothUserInterface.SetMouseLookActive(const Value: Boolean);
+var
+  MousePos: TPoint;
+begin
+  if FMouseLookActive = Value then Exit;
+  FMouseLookActive := Value;
+  if FMouseLookActive then
+  begin
+    if FAutoUpdateMouse then
+    begin
+      GLGetCursorPos(MousePos);
+      FOriginalMousePos.SetPoint2D(MousePos.X, MousePos.Y);
+      GLShowCursor(False);
+    end;
+  end
+  else
+  begin
+    if FAutoUpdateMouse then
+      GLShowCursor(True);
+  end;
+end;
+
+procedure TGLSmoothUserInterface.TurnHorizontal(const Angle: Single;
+  const ADeltaTime: Double);
+begin
+  FSmoothNavigator.TurnHorizontal(Angle, ADeltaTime);
+end;
+
+procedure TGLSmoothUserInterface.TurnVertical(const Angle: Single;
+  const ADeltaTime: Double);
+begin
+  if Assigned(FSmoothNavigator) then
+    FSmoothNavigator.TurnVertical(Angle, ADeltaTime)
+  else
+    FSmoothVertNavigator.TurnVertical(Angle, ADeltaTime);
+end;
+
+{ TGLNavigatorInertiaParameters }
+
+procedure TGLNavigatorInertiaParameters.Assign(Source: TPersistent);
+begin
+  if Source is TGLNavigatorInertiaParameters then
+  begin
+    FMovementAcceleration := TGLNavigatorInertiaParameters(Source).FMovementAcceleration;
+    FMovementInertia := TGLNavigatorInertiaParameters(Source).FMovementInertia;
+    FMovementSpeed := TGLNavigatorInertiaParameters(Source).FMovementSpeed;
+    FTurnMaxAngle := TGLNavigatorInertiaParameters(Source).FTurnMaxAngle;
+    FTurnInertia := TGLNavigatorInertiaParameters(Source).FTurnInertia;
+    FTurnSpeed := TGLNavigatorInertiaParameters(Source).FTurnSpeed;
+  end
+  else
+    inherited; //to the pit of doom ;)
+end;
+
+constructor TGLNavigatorInertiaParameters.Create(AOwner: TPersistent);
+begin
+  FOwner := AOwner;
+
+  FTurnInertia := 150;
+  FTurnSpeed := 50;
+  FTurnMaxAngle := 0.5;
+
+  FMovementAcceleration := 7;
+  FMovementInertia := 200;
+  FMovementSpeed := 200;
+end;
+
+function TGLNavigatorInertiaParameters.GetOwner: TPersistent;
+begin
+  Result := FOwner;
+end;
+
+procedure TGLNavigatorInertiaParameters.ScaleParameters(
+  const Value: Single);
+begin
+  Assert(Value > 0);
+
+  if Value > 1 then
+  begin
+    FMovementInertia := FMovementInertia * PowerSingle(2, 1 / Value);
+    FTurnInertia := FTurnInertia * PowerSingle(2, 1 / Value);
+  end
+  else
+  begin
+    FMovementInertia := FMovementInertia / PowerSingle(2, Value);
+    FTurnInertia := FTurnInertia / PowerSingle(2, Value);
+  end;
+  FTurnMaxAngle := FTurnMaxAngle / Value;
+  FTurnSpeed := FTurnSpeed * Value;
+end;
+
+function TGLNavigatorInertiaParameters.StoreTurnMaxAngle: Boolean;
+begin
+  Result := Abs(FTurnMaxAngle - 0.5) > EPS;
+end;
+
+function TGLNavigatorInertiaParameters.StoreMovementAcceleration: Boolean;
+begin
+  Result := Abs(FMovementAcceleration - 7) > EPS;
+end;
+
+function TGLNavigatorInertiaParameters.StoreMovementInertia: Boolean;
+begin
+  Result := Abs(FMovementInertia - 200) > EPS;
+end;
+
+function TGLNavigatorInertiaParameters.StoreMovementSpeed: Boolean;
+begin
+  Result := Abs(FMovementSpeed - 200) > EPS;
+end;
+
+function TGLNavigatorInertiaParameters.StoreTurnInertia: Boolean;
+begin
+  Result := Abs(FTurnInertia - 150) > EPS;
+end;
+
+function TGLNavigatorInertiaParameters.StoreTurnSpeed: Boolean;
+begin
+  Result := Abs(FTurnSpeed - 50) > EPS;
+end;
+
+{ TGLNavigatorGeneralParameters }
+
+procedure TGLNavigatorGeneralParameters.Assign(Source: TPersistent);
+begin
+  if Source is TGLNavigatorGeneralParameters then
+  begin
+    FAutoScaleMin := TGLNavigatorGeneralParameters(Source).FAutoScaleMin;
+    FAutoScaleMax := TGLNavigatorGeneralParameters(Source).FAutoScaleMax;
+    FAutoScaleMult := TGLNavigatorGeneralParameters(Source).FAutoScaleMult;
+  end
+  else
+    inherited; //die!
+end;
+
+constructor TGLNavigatorGeneralParameters.Create(AOwner: TPersistent);
+begin
+  FOwner := AOwner;
+  FAutoScaleMin := 0.1;
+  FAutoScaleMax := 0.75;
+  FAutoScaleMult := 2;
+end;
+
+function TGLNavigatorGeneralParameters.GetOwner: TPersistent;
+begin
+  Result := FOwner;
+end;
+
+function TGLNavigatorGeneralParameters.StoreAutoScaleMax: Boolean;
+begin
+  Result := Abs(FAutoScaleMax - 0.75) > EPS;
+end;
+
+function TGLNavigatorGeneralParameters.StoreAutoScaleMin: Boolean;
+begin
+  Result := Abs(FAutoScaleMin - 0.1) > EPS;
+end;
+
+function TGLNavigatorGeneralParameters.StoreAutoScaleMult: Boolean;
+begin
+  Result := Abs(FAutoScaleMult - 2) > EPS;
+end;
+
+{ TGLNavigatorMoveAroundParameters }
+
+procedure TGLNavigatorMoveAroundParameters.Assign(Source: TPersistent);
+begin
+  if Source is TGLNavigatorMoveAroundParameters then
+  begin
+    FMaxAngle := TGLNavigatorMoveAroundParameters(Source).FMaxAngle;
+    FInertia :=  TGLNavigatorMoveAroundParameters(Source).FInertia;
+    FPitchSpeed :=  TGLNavigatorMoveAroundParameters(Source).FPitchSpeed;
+    FTurnSpeed :=  TGLNavigatorMoveAroundParameters(Source).FTurnSpeed;
+    FCutoff :=  TGLNavigatorMoveAroundParameters(Source).FCutoff;
+    SetTargetObject(TGLNavigatorMoveAroundParameters(Source).FTargetObject);
+  end
+  else
+    inherited; //die
+end;
+
+constructor TGLNavigatorMoveAroundParameters.Create(AOwner: TPersistent);
+begin
+  FOwner := AOwner;
+  FPitchSpeed := 500;
+  FTurnSpeed  := 500;
+  FInertia    := 65;
+  FMaxAngle   := 1.5;
+  FCutoff     := EPS2;
+end;
+
+function TGLNavigatorMoveAroundParameters.GetOwner: TPersistent;
+begin
+  Result := FOwner;
+end;
+
+procedure TGLNavigatorMoveAroundParameters.ScaleParameters(
+  const Value: Single);
+begin
+  Assert(Value > 0);
+
+  if Value < 1 then
+    FInertia := FInertia / PowerSingle(2, Value)
+  else
+    FInertia := FInertia * PowerSingle(2, 1 / Value);
+
+  FMaxAngle := FMaxAngle / Value;
+  FPitchSpeed := FPitchSpeed * Value;
+  FTurnSpeed := FTurnSpeed * Value;
+end;
+
+procedure TGLNavigatorMoveAroundParameters.SetTargetObject(
+  const Value: TGLBaseSceneObject);
+begin
+  if FTargetObject <> nil then
+    if FOwner is TGLSmoothNavigator then
+      FTargetObject.RemoveFreeNotification(TGLSmoothNavigator(FOwner));
+
+  FTargetObject := Value;
+
+  if FTargetObject <> nil then
+    if FOwner is TGLSmoothNavigator then
+      FTargetObject.FreeNotification(TGLSmoothNavigator(FOwner));
+end;
+
+function TGLNavigatorMoveAroundParameters.StoreCutoff: Boolean;
+begin
+  Result := Abs(FCutoff - EPS2) > EPS8;
+end;
+
+function TGLNavigatorMoveAroundParameters.StoreInertia: Boolean;
+begin
+  Result := Abs(FInertia - 65) > EPS;
+end;
+
+function TGLNavigatorMoveAroundParameters.StoreMaxAngle: Boolean;
+begin
+  Result := Abs(FMaxAngle - 1.5) > EPS;
+end;
+
+function TGLNavigatorMoveAroundParameters.StorePitchSpeed: Boolean;
+begin
+  Result := Abs(FPitchSpeed - 500) > EPS;
+end;
+
+function TGLNavigatorMoveAroundParameters.StoreTurnSpeed: Boolean;
+begin
+  Result := Abs(FTurnSpeed - 500) > EPS;
+end;
+
+{ TGLNavigatorAdjustDistanceParameters }
+
+procedure TGLNavigatorAdjustDistanceParameters.AddImpulse(
+  const Impulse: Single);
+begin
+  FOldDistanceRatio := FOldDistanceRatio + Impulse * FSpeed / FInertia * FImpulseSpeed;
+end;
+
+procedure TGLNavigatorAdjustDistanceParameters.Assign(Source: TPersistent);
+begin
+  inherited Assign(Source);
+  if Source is TGLNavigatorAdjustDistanceParameters then
+  begin
+    FImpulseSpeed := TGLNavigatorAdjustDistanceParameters(Source).FImpulseSpeed;
+  end;
+end;
+
+constructor TGLNavigatorAdjustDistanceParameters.Create(
+  AOwner: TPersistent);
+begin
+  inherited;
+  FImpulseSpeed := 0.02;
+end;
+
+
+procedure TGLNavigatorAdjustDistanceParameters.ScaleParameters(
+  const Value: Single);
+begin
+  inherited;
+  FImpulseSpeed := FImpulseSpeed / Value;
+end;
+
+function TGLNavigatorAdjustDistanceParameters.StoreImpulseSpeed: Boolean;
+begin
+  Result := Abs(FImpulseSpeed - 0.02) > EPS;
+end;
+
+{ TGLNavigatorAbstractParameters }
+
+
+procedure TGLNavigatorAbstractParameters.Assign(Source: TPersistent);
+begin
+  if Source is TGLNavigatorAbstractParameters then
+  begin
+    FInertia := TGLNavigatorAbstractParameters(Source).FInertia;
+    FSpeed :=   TGLNavigatorAbstractParameters(Source).FSpeed;
+    FCutoff :=  TGLNavigatorAbstractParameters(Source).FCutoff;
+  end
+  else
+    inherited; //to the pit of doom ;)
+end;
+
+constructor TGLNavigatorAbstractParameters.Create(
+  AOwner: TPersistent);
+begin
+  FOwner := AOwner;
+  FInertia := 100;
+  FSpeed := 0.005;
+  FCutoff := EPS;
+end;
+
+function TGLNavigatorAbstractParameters.GetOwner: TPersistent;
+begin
+  Result := FOwner;
+end;
+
+procedure TGLNavigatorAbstractParameters.ScaleParameters(
+  const Value: Single);
+begin
+  Assert(Value > 0);
+
+  if Value < 1 then
+    FInertia := FInertia / PowerSingle(2, Value)
+  else
+    FInertia := FInertia * PowerSingle(2, 1 / Value);
+end;
+
+function TGLNavigatorAbstractParameters.StoreCutoff: Boolean;
+begin
+  Result := Abs(FCutoff - EPS) > EPS2;
+end;
+
+function TGLNavigatorAbstractParameters.StoreInertia: Boolean;
+begin
+  Result := Abs(FInertia - 100) > EPS;
+end;
+
+function TGLNavigatorAbstractParameters.StoreSpeed: Boolean;
+begin
+  Result := Abs(FSpeed - 0.005) > EPS2;
+end;
+
+{ TGLNavigatorAdjustDistanceParametersEx }
+
+procedure TGLNavigatorAdjustDistanceParametersEx.Assign(
+  Source: TPersistent);
+begin
+  if Source is TGLNavigatorAdjustDistanceParametersEx then
+  begin
+    FTargetDistance := TGLNavigatorAdjustDistanceParametersEx(Source).FTargetDistance;
+    FSpeedLimit := TGLNavigatorAdjustDistanceParametersEx(Source).FSpeedLimit;
+  end
+  else
+    inherited;
+end;
+
+constructor TGLNavigatorAdjustDistanceParametersEx.Create(
+  AOwner: TPersistent);
+begin
+  inherited;
+  FInertia := 0.5;
+  FTargetDistance := 100;
+  FSpeed := 100;
+  FSpeedLimit := 20000;
+end;
+
+function TGLNavigatorAdjustDistanceParametersEx.StoreInertia: Boolean;
+begin
+  Result := Abs(FInertia - 0.5) > EPS2;
+end;
+
+function TGLNavigatorAdjustDistanceParametersEx.StoreSpeed: Boolean;
+begin
+  Result := Abs(FSpeed - 100) > EPS2;
+end;
+
+function TGLNavigatorAdjustDistanceParametersEx.StoreSpeedLimit: Boolean;
+begin
+  Result := Abs(FSpeedLimit - 20000) > EPS2;
+end;
+
+function TGLNavigatorAdjustDistanceParametersEx.StoreTargetDistance: Boolean;
+begin
+  Result := Abs(FTargetDistance - 100) > EPS2;
+end;
+
+{ TGLNavigatorSmoothChangeItem }
+
+procedure TGLNavigatorSmoothChangeItem.Assign(Source: TPersistent);
+begin
+  inherited Assign(Source);
+
+  if Source is TGLNavigatorSmoothChangeItem then
+  begin
+    FInertia :=    TGLNavigatorSmoothChangeItem(Source).FInertia;
+    FSpeed :=      TGLNavigatorSmoothChangeItem(Source).FSpeed;
+    FSpeedLimit := TGLNavigatorSmoothChangeItem(Source).FSpeedLimit;
+    FCutoff :=     TGLNavigatorSmoothChangeItem(Source).FCutoff;
+    FEnabled :=    TGLNavigatorSmoothChangeItem(Source).FEnabled;
+  end;
+end;
+
+constructor TGLNavigatorSmoothChangeItem.Create(aOwner: TXCollection);
+begin
+  inherited;
+  FInertia := 1;
+  FSpeed := 5.5;
+  FSpeedLimit := 20000;
+  FCutoff := EPS;
+  FEnabled := True;
+end;
+
+function TGLNavigatorSmoothChangeItem.GetNavigator: TGLSmoothNavigator;
+begin
+  Result := TGLSmoothNavigator(TGLNavigatorSmoothChangeItems(GetOwner).Owner);
+end;
+
+procedure TGLNavigatorSmoothChangeItem.ScaleParameters(
+  const Value: Single);
+begin
+  Assert(Value > 0);
+
+  if Value < 1 then
+    FInertia := FInertia / PowerSingle(2, Value)
+  else
+    FInertia := FInertia * PowerSingle(2, 1 / Value);
+end;
+
+function TGLNavigatorSmoothChangeItem.StoreCutoff: Boolean;
+begin
+  Result := Abs(FCutoff - EPS) > EPS8;
+end;
+
+function TGLNavigatorSmoothChangeItem.StoreInertia: Boolean;
+begin
+  Result := Abs(FInertia - 1) > EPS;
+end;
+
+function TGLNavigatorSmoothChangeItem.StoreSpeed: Boolean;
+begin
+  Result := Abs(FSpeed - 5.5) > EPS2;
+end;
+
+function TGLNavigatorSmoothChangeItem.StoreSpeedLimit: Boolean;
+begin
+  Result := Abs(FSpeedLimit - 20000) > EPS2;
+end;
+
+{ TGLNavigatorSmoothChangeItems }
+
+function TGLNavigatorSmoothChangeItems.Add(AClass : TGLNavigatorSmoothChangeItemClass): TGLNavigatorSmoothChangeItem;
+begin
+  Result := AClass.Create(Self);
+end;
+
+function TGLNavigatorSmoothChangeItems.CanAdd(AClass: TXCollectionItemClass): Boolean;
+begin
+  Result := AClass.InheritsFrom(TGLNavigatorSmoothChangeItem);
+end;
+
+procedure TGLNavigatorSmoothChangeItems.DoProceed(ADeltaTime: Double);
+var
+  I: Integer;
+begin
+  for I := 0 to Count - 1 do
+    GetItems(I).Proceed(ADeltaTime);
+end;
+
+function TGLNavigatorSmoothChangeItems.GetItems(const Index : Integer): TGLNavigatorSmoothChangeItem;
+begin
+  Result := TGLNavigatorSmoothChangeItem(inherited GetItems(Index));
+end;
+
+class function TGLNavigatorSmoothChangeItems.ItemsClass: TXCollectionItemClass;
+begin
+  Result := TGLNavigatorSmoothChangeItem;
+end;
+
+procedure TGLNavigatorSmoothChangeItems.SetItems(const Index : Integer; const Value:
+        TGLNavigatorSmoothChangeItem);
+begin
+  GetItems(Index).Assign(Value);
+end;
+
+{ TGLNavigatorSmoothChangeSingle }
+
+procedure TGLNavigatorSmoothChangeSingle.Assign(Source: TPersistent);
+begin
+  inherited Assign(Source);
+  
+  if Source is TGLNavigatorSmoothChangeVector then
+  begin
+    FTargetValue := TGLNavigatorSmoothChangeSingle(Source).TargetValue;
+    FOnGetCurrentValue := TGLNavigatorSmoothChangeSingle(Source).FOnGetCurrentValue;
+    FOnSetCurrentValue := TGLNavigatorSmoothChangeSingle(Source).FOnSetCurrentValue;
+  end;
+end;
+
+class function TGLNavigatorSmoothChangeSingle.FriendlyName: string;
+begin
+  Result := 'Navigator SmoothChange Single';
+end;
+
+function TGLNavigatorSmoothChangeSingle.Proceed(ADeltaTime: Double): Boolean;
+var
+  lCurrentValue: Single;
+  lCurrentDifference: Single;
+  lTotalDistanceToTravelThisTime, lDistanceToTravelThisTime: Single;
+  lMaxExpectedDeltaTime: Double;
+
+begin
+  Result := False;
+  if not FEnabled then Exit;
+  if not Assigned(FOnGetCurrentValue) then Exit;
+  if not Assigned(FOnSetCurrentValue) then Exit;
+
+  lMaxExpectedDeltaTime := GetNavigator.FMaxExpectedDeltaTime;
+  lCurrentValue := FOnGetCurrentValue(Self);
+  lCurrentDifference := FTargetValue - lCurrentValue;
+
+  lTotalDistanceToTravelThisTime := 0;
+
+  while ADeltaTime > lMaxExpectedDeltaTime do
+  begin
+    lDistanceToTravelThisTime := MinFloat((lCurrentDifference * ADeltaTime * FSpeed * FInertia) / (FInertia + 1), FSpeedLimit);
+//  lDistanceToTravelThisTime := (lCurrentDistance * ADeltaTime + FSpeed * FInertia) / (FInertia + 1);-  this also works, but a bit different.
+
+    lCurrentDifference := lCurrentDifference - lDistanceToTravelThisTime;
+    lTotalDistanceToTravelThisTime := lTotalDistanceToTravelThisTime + lDistanceToTravelThisTime;
+    ADeltaTime := ADeltaTime - lMaxExpectedDeltaTime;
+  end;
+
+  if Abs(lTotalDistanceToTravelThisTime) > FCutoff then
+  begin
+    FOnSetCurrentValue(Self, lCurrentValue + lTotalDistanceToTravelThisTime);
+    Result := True;
+  end;  
+end;
+
+procedure TGLNavigatorSmoothChangeSingle.ResetTargetValue;
+begin
+  FTargetValue := FOnGetCurrentValue(Self);
+end;
+
+{ TGLNavigatorSmoothChangeVector }
+
+procedure TGLNavigatorSmoothChangeVector.Assign(Source: TPersistent);
+begin
+  inherited Assign(Source);
+  
+  if Source is TGLNavigatorSmoothChangeVector then
+  begin
+    FTargetValue.Assign(TGLNavigatorSmoothChangeVector(Source).TargetValue);
+    FOnGetCurrentValue := TGLNavigatorSmoothChangeVector(Source).FOnGetCurrentValue;
+    FOnSetCurrentValue := TGLNavigatorSmoothChangeVector(Source).FOnSetCurrentValue;
+  end;
+end;
+
+constructor TGLNavigatorSmoothChangeVector.Create(aOwner: TXCollection);
+begin
+  inherited;
+  FTargetValue := TGLCoordinates.CreateInitialized(Self, NullHmgVector, csVector);
+end;
+
+destructor TGLNavigatorSmoothChangeVector.Destroy;
+begin
+  FTargetValue.Free;
+  inherited;
+end;
+
+class function TGLNavigatorSmoothChangeVector.FriendlyName: string;
+begin
+  Result := 'Navigator SmoothChange Vector';
+end;
+
+function TGLNavigatorSmoothChangeVector.Proceed(ADeltaTime: Double): Boolean;
+var
+  lAbsolutePosition: TVector;
+  lCurrentDistance: Single;
+  lTotalDistanceToTravelThisTime, lDistanceToTravelThisTime: Single;
+  lMaxExpectedDeltaTime: Double;
+
+  procedure DoAdjustDistanceToPoint();
+  var
+    vect: TVector;
+  begin
+    vect := VectorScale(VectorNormalize(VectorSubtract(FTargetValue.DirectVector, lAbsolutePosition)), lTotalDistanceToTravelThisTime);
+    AddVector(vect, lAbsolutePosition);
+
+    // Did we go too far?
+    if VectorDistance(vect, FTargetValue.DirectVector) > VectorDistance(lAbsolutePosition, FTargetValue.DirectVector) then
+      vect := FTargetValue.DirectVector;
+
+    FOnSetCurrentValue(Self, vect);
+    Result := True;
+  end;
+
+begin
+  Result := False;
+  if not FEnabled then Exit;
+  if not Assigned(FOnGetCurrentValue) then Exit;
+  if not Assigned(FOnSetCurrentValue) then Exit;
+
+  lMaxExpectedDeltaTime := GetNavigator.FMaxExpectedDeltaTime;
+  lAbsolutePosition := FOnGetCurrentValue(Self);
+  lCurrentDistance := VectorDistance(lAbsolutePosition, FTargetValue.DirectVector);
+
+  lTotalDistanceToTravelThisTime := 0;
+
+
+  while ADeltaTime > lMaxExpectedDeltaTime do
+  begin
+    lDistanceToTravelThisTime := MinFloat((lCurrentDistance * ADeltaTime * FSpeed * FInertia) / (FInertia + 1), FSpeedLimit);
+//  lDistanceToTravelThisTime := (lCurrentDistance * ADeltaTime + FSpeed * FInertia) / (FInertia + 1);-  this also works, but a bit different.
+
+    lCurrentDistance := lCurrentDistance - lDistanceToTravelThisTime;
+    lTotalDistanceToTravelThisTime := lTotalDistanceToTravelThisTime + lDistanceToTravelThisTime;
+    ADeltaTime := ADeltaTime - lMaxExpectedDeltaTime;
+  end;
+
+  if Abs(lTotalDistanceToTravelThisTime) > FCutoff then
+    DoAdjustDistanceToPoint();
+end;
+
+procedure TGLNavigatorSmoothChangeVector.ResetTargetValue;
+begin
+  FTargetValue.DirectVector := FOnGetCurrentValue(Self);
+end;
+
+procedure TGLNavigatorSmoothChangeVector.SetTargetValue(
+  const Value: TGLCoordinates);
+begin
+  FTargetValue.Assign(Value);
+end;
+
+//==========================================================
+initialization
+//==========================================================
+
+  RegisterClasses([
+      TGLSmoothNavigator, TGLSmoothUserInterface,
+      TGLNavigatorInertiaParameters, TGLNavigatorGeneralParameters,
+      TGLNavigatorMoveAroundParameters,
+      TGLNavigatorAdjustDistanceParameters, TGLNavigatorAdjustDistanceParametersEx
+                   ]);
+
+  RegisterXCollectionItemClass(TGLNavigatorSmoothChangeSingle);
+  RegisterXCollectionItemClass(TGLNavigatorSmoothChangeVector);
+end.
+
+