123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.Navigator;
- (* Unit for navigating TgxBaseObjects *)
- interface
- {$I Stage.Defines.inc}
- uses
- System.Types,
- System.SysUtils,
- System.Classes,
- System.Math,
- Stage.VectorGeometry,
- GXS.Scene,
- GXS.Coordinates,
- GXS.Screen,
- Stage.VectorTypes;
- type
- (* TgxNavigator is the component for moving a TgxBaseSceneObject, and all Classes based on it,
- this includes all the objects from the Scene Editor.
- The four calls to get you started is
- TurnHorisontal : it turns left and right.
- TurnVertical : it turns up and down.
- MoveForward : moves back and forth.
- FlyForward : moves back and forth in the movingobject's direction
- The three properties to get you started is
- MovingObject : The Object that you are moving.
- UseVirtualUp : When UseVirtualUp is set you navigate Quake style. If it isn't
- it's more like Descent.
- AngleLock : Allows you to block the Vertical angles. Should only be used in
- conjunction with UseVirtualUp.
- MoveUpWhenMovingForward : Changes movement from Quake to Arcade Airplane...
- (no tilt and flying)
- InvertHorizontalSteeringWhenUpsideDown : When using virtual up, and vertically
- rotating beyond 90 degrees, will make steering seem inverted, so we "invert" back
- to normal. *)
- TgxNavigator = class(TComponent)
- private
- FObject: TgxBaseSceneObject;
- FVirtualRight: TVector4f;
- FVirtualUp: TgxCoordinates;
- FUseVirtualUp: boolean;
- FAutoUpdateObject: boolean;
- FMaxAngle: single;
- FMinAngle: single;
- FCurrentVAngle: single;
- FCurrentHAngle: single;
- FAngleLock: boolean;
- FMoveUpWhenMovingForward: boolean;
- FInvertHorizontalSteeringWhenUpsideDown: boolean;
- protected
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure SetObject(NewObject: TgxBaseSceneObject); virtual;
- procedure SetUseVirtualUp(UseIt: boolean);
- procedure SetVirtualUp(Up: TgxCoordinates);
- function CalcRight: TVector4f;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure TurnHorizontal(Angle: single);
- procedure TurnVertical(Angle: single);
- procedure MoveForward(Distance: single);
- procedure StrafeHorizontal(Distance: single);
- procedure StrafeVertical(Distance: single);
- procedure Straighten;
- procedure FlyForward(Distance: single);
- procedure LoadState(Stream: TStream);
- procedure SaveState(Stream: TStream);
- property CurrentVAngle: single read FCurrentVAngle;
- property CurrentHAngle: single read FCurrentHAngle;
- published
- property MoveUpWhenMovingForward: boolean read FMoveUpWhenMovingForward write FMoveUpWhenMovingForward default False;
- property InvertHorizontalSteeringWhenUpsideDown: boolean read FInvertHorizontalSteeringWhenUpsideDown write FInvertHorizontalSteeringWhenUpsideDown default False;
- property VirtualUp: TgxCoordinates read FVirtualUp write SetVirtualUp;
- property MovingObject: TgxBaseSceneObject read FObject write SetObject;
- property UseVirtualUp: boolean read FUseVirtualUp write SetUseVirtualUp default False;
- property AutoUpdateObject: boolean read FAutoUpdateObject write FAutoUpdateObject default False;
- property MaxAngle: single read FMaxAngle write FMaxAngle;
- property MinAngle: single read FMinAngle write FMinAngle;
- property AngleLock: boolean read FAngleLock write FAngleLock default False;
- end;
- (* TgxUserInterface is the component which reads the userinput and transform it into action.
- The four calls to get you started is
- MouseLookActivate : set us up the bomb.
- MouseLookDeActivate : defuses it.
- Mouselook(deltaTime: double) : handles mouse look... Should be called in the Cadencer event. (Though it works every where!)
- MouseUpdate : Resets mouse position so that you don't notice that the mouse is limited to the screen should be called after Mouselook.
- The four properties to get you started are:
- InvertMouse : Inverts the mouse Y axis.
- MouseSpeed : Also known as mouse sensitivity.
- GXNavigator : The Navigator which receives the user movement.
- GXVertNavigator : The Navigator which if set receives the vertical user movement. Used mostly for cameras.... *)
- TgxUserInterface = class(TComponent)
- private
- FPrevPoint: TPoint;
- midScreenX, midScreenY: integer;
- FMouseActive: boolean;
- FMouseSpeed: single;
- FGLNavigator: TgxNavigator;
- FGLVertNavigator: TgxNavigator;
- FInvertMouse: boolean;
- procedure MouseInitialize;
- procedure SetMouseLookActive(const val: boolean);
- procedure setNavigator(val: TgxNavigator);
- procedure setVertNavigator(val: TgxNavigator);
- protected
- procedure Notification(AComponent: TComponent; operation: TOperation); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure MouseUpdate;
- function MouseLook : Boolean;
- procedure MouseLookActiveToggle;
- procedure MouseLookActivate;
- procedure MouseLookDeactivate;
- function IsMouseLookOn: Boolean;
- procedure TurnHorizontal(Angle : Double);
- procedure TurnVertical(Angle : Double);
- property MouseLookActive : Boolean read FMouseActive write SetMouseLookActive;
- published
- property InvertMouse: boolean read FInvertMouse write FInvertMouse default False;
- property MouseSpeed: single read FMouseSpeed write FMouseSpeed;
- property GLNavigator: TgxNavigator read FGLNavigator write setNavigator;
- property GLVertNavigator: TgxNavigator read FGLVertNavigator write setVertNavigator;
- end;
- //-------------------------------------------------------------------------
- implementation
- //-------------------------------------------------------------------------
- constructor TgxNavigator.Create(AOwner : TComponent);
- Begin
- inherited;
- FVirtualUp := TgxCoordinates.CreateInitialized(Self, ZHmgVector, csPoint);
- FCurrentVAngle := 0;
- FCurrentHAngle := 0;
- End;
- destructor TgxNavigator.Destroy;
- Begin
- FVirtualUp.Free;
- inherited;
- End;
- procedure TgxNavigator.SetObject(NewObject : TgxBaseSceneObject);
- Begin
- If FObject <> NewObject then
- Begin
- If Assigned(FObject) then
- FObject.RemoveFreeNotification(Self);
- FObject := NewObject;
- If Assigned(FObject) then
- Begin
- if csdesigning in componentstate then
- Begin
- If VectorLength(FVirtualUp.AsVector) = 0 then
- Begin
- FVirtualUp.AsVector := FObject.Up.AsVector;
- End;
- Exit;
- End;
- If FUseVirtualUp Then FVirtualRight := CalcRight;
- FObject.FreeNotification(Self);
- End;
- End;
- End;
- procedure TgxNavigator.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- If Operation = opRemove then
- If AComponent = FObject then
- MovingObject := Nil;
- inherited;
- end;
- function TgxNavigator.CalcRight : TVector4f;
- begin
- If Assigned(FObject) then
- If FUseVirtualUp Then
- Begin
- VectorCrossProduct(FObject.Direction.AsVector, FVirtualUp.AsVector, Result);
- ScaleVector(Result,1/VectorLength(Result));
- End else VectorCrossProduct(FObject.Direction.AsVector, FObject.Up.AsVector, Result); { automaticly length(1), if not this is a bug }
- end;
- procedure TgxNavigator.TurnHorizontal(Angle : Single);
- var
- T : TVector4f;
- U : TAffineVector;
- TempVal : Single;
- Begin
- If InvertHorizontalSteeringWhenUpsideDown and ((CurrentVAngle < -90) or (CurrentVAngle > 90)) then
- Angle := -Angle;
- FCurrentHAngle:=(FCurrentHAngle-Angle);
- If (FCurrentHAngle < 0) or (FCurrentHAngle > 360) then
- Begin
- TempVal := (FCurrentHAngle)/360;
- FCurrentHAngle := (TempVal-Floor(TempVal))*360;
- End;
- Angle := DegToRadian(Angle); {make it ready for Cos and Sin }
- If FUseVirtualUp Then
- Begin
- SetVector(U, VirtualUp.AsVector);
- T := FObject.Up.AsVector;
- RotateVector(T,U,Angle);
- FObject.Up.AsVector := T;
- T := FObject.Direction.AsVector;
- RotateVector(T,U,Angle);
- FObject.Direction.AsVector := T;
- End else FObject.Direction.AsVector := VectorCombine(FObject.Direction.AsVector,CalcRight,Cos(Angle),Sin(Angle));
- End;
- procedure TgxNavigator.TurnVertical(Angle : Single);
- var
- ExpectedAngle : Single;
- CosAngle, SinAngle : Single;
- TempVal : Single;
- Direction : TVector4f;
- begin
- ExpectedAngle := FCurrentVAngle+Angle;
- If FAngleLock then
- Begin
- If ExpectedAngle > FMaxAngle then
- Begin
- If FCurrentVAngle = FMaxAngle then Exit;
- Angle := FMaxAngle-FCurrentVAngle;
- ExpectedAngle := FMaxAngle;
- End else
- Begin
- If ExpectedAngle < FMinAngle then
- Begin
- If FCurrentVAngle = FMinAngle then Exit;
- Angle := FMinAngle-FCurrentVAngle;
- ExpectedAngle := FMinAngle;
- End;
- End;
- End;
- FCurrentVAngle := ExpectedAngle;
- If (FCurrentVAngle < -180) or (FCurrentVAngle > 180) then
- Begin
- TempVal := (FCurrentVAngle+180)/360;
- FCurrentVAngle := (TempVal-Floor(TempVal))*360-180;
- End;
- Angle := DegToRadian(Angle); {make it ready for Cos and Sin }
- SinCosine(Angle,SinAngle,CosAngle);
- Direction := VectorCombine(MovingObject.Direction.AsVector,MovingObject.Up.AsVector,CosAngle,SinAngle);
- MovingObject.Up.AsVector := VectorCombine(MovingObject.Direction.AsVector,MovingObject.Up.AsVector,SinAngle,CosAngle);
- MovingObject.Direction.AsVector := Direction;
- end;
- procedure TgxNavigator.MoveForward(Distance : Single);
- Begin
- If (FUseVirtualUp and (not MoveUpWhenMovingForward)) Then
- Begin
- FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,VectorCrossProduct(FVirtualUp.AsVector,CalcRight),1,Distance);
- End else FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FObject.Direction.AsVector,1,Distance);
- End;
- Procedure TgxNavigator.StrafeHorizontal(Distance : Single);
- Begin
- FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,CalcRight,1,Distance);
- End;
- Procedure TgxNavigator.StrafeVertical(Distance : Single);
- Begin
- If UseVirtualUp Then
- Begin
- FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FVirtualUp.AsVector,1,Distance);
- End else FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FObject.Up.AsVector,1,Distance);
- End;
- procedure TgxNavigator.FlyForward(Distance: single);
- begin
- FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector, FObject.Direction.AsVector, 1, Distance);
- end;
- Procedure TgxNavigator.Straighten;
- Var
- R : TVector4f;
- D : TVector4f;
- A : Single;
- Begin
- FCurrentVAngle := 0;
- FCurrentHAngle := 0;
- R := CalcRight;
- A := VectorAngleCosine(AffineVectorMake(MovingObject.Up.AsVector), AffineVectorMake(VirtualUp.AsVector));
- MovingObject.Up.AsVector := VirtualUp.AsVector;
- VectorCrossProduct(R, FVirtualUp.AsVector, D);
- If A >= 0 then
- ScaleVector(D,-1/VectorLength(D))
- else
- ScaleVector(D,1/VectorLength(D));
- MovingObject.Direction.AsVector := D;
- End;
- Procedure TgxNavigator.SetUseVirtualUp(UseIt : Boolean);
- Begin
- FUseVirtualUp := UseIt;
- if csdesigning in componentstate then Exit;
- If FUseVirtualUp then FVirtualRight := CalcRight;
- End;
- Procedure TgxNavigator.SetVirtualUp(Up : TgxCoordinates);
- begin
- FVirtualUp.Assign(Up);
- if csdesigning in componentstate then Exit;
- If FUseVirtualUp then FVirtualRight := CalcRight;
- end;
- procedure TgxNavigator.LoadState(Stream : TStream);
- var
- Vector : TAffineVector;
- B : ByteBool;
- S : Single;
- begin
- Stream.Read(Vector,SizeOf(TAffineVector));
- FObject.Position.AsAffineVector := Vector;
- Stream.Read(Vector,SizeOf(TAffineVector));
- FObject.Direction.AsAffineVector := Vector;
- Stream.Read(Vector,SizeOf(TAffineVector));
- FObject.Up.AsAffineVector := Vector;
- Stream.Read(B,SizeOf(ByteBool));
- UseVirtualUp := B;
- Stream.Read(B,SizeOf(ByteBool));
- FAngleLock := B;
- Stream.Read(S,SizeOf(Single));
- FMaxAngle := S;
- Stream.Read(S,SizeOf(Single));
- FMinAngle := S;
- Stream.Read(S,SizeOf(Single));
- FCurrentVAngle := S;
- Stream.Read(S,SizeOf(Single));
- FCurrentHAngle := S;
- end;
- procedure TgxNavigator.SaveState(Stream : TStream);
- var
- Vector : TAffineVector;
- B : ByteBool;
- S : Single;
- begin
- Vector := FObject.Position.AsAffineVector;
- Stream.Write(Vector,SizeOf(TAffineVector));
- Vector := FObject.Direction.AsAffineVector;
- Stream.Write(Vector,SizeOf(TAffineVector));
- Vector := FObject.Up.AsAffineVector;
- Stream.Write(Vector,SizeOf(TAffineVector));
- B := UseVirtualUp;
- Stream.Write(B,SizeOf(ByteBool));
- B := FAngleLock;
- Stream.Write(B,SizeOf(ByteBool));
- S := FMaxAngle;
- Stream.Write(S,SizeOf(Single));
- S := FMinAngle;
- Stream.Write(S,SizeOf(Single));
- S := FCurrentVAngle;
- Stream.Write(S,SizeOf(Single));
- S := FCurrentHAngle;
- Stream.Write(S,SizeOf(Single));
- end;
- function TgxUserInterface.IsMouseLookOn: Boolean;
- begin
- Result:=FMouseActive;
- end;
- Procedure TgxUserInterface.TurnHorizontal(Angle : Double);
- Begin
- GLNavigator.TurnHorizontal(Angle);
- End;
- Procedure TgxUserInterface.TurnVertical(Angle : Double);
- Begin
- If Assigned(GLVertNavigator) then GLVertNavigator.TurnVertical(Angle)
- else GLNavigator.TurnVertical(Angle);
- End;
- procedure TgxUserInterface.MouseLookActiveToggle;
- begin
- if FMouseActive then
- MouseLookDeactivate
- else MouseLookActivate;
- end;
- procedure TgxUserInterface.MouseLookActivate;
- begin
- if not FMouseActive then begin
- FMouseActive := True;
- MouseInitialize;
- GLShowCursor(False);
- end;
- end;
- procedure TgxUserInterface.MouseLookDeactivate;
- begin
- if FMouseActive then begin
- FMouseActive := False;
- GLShowCursor(True);
- end;
- end;
- procedure TgxUserInterface.MouseInitialize;
- begin
- midScreenX:=GLGetScreenWidth div 2;
- midScreenY:=GLGetScreenHeight div 2;
- FPrevPoint.x:=midScreenX; FPrevPoint.Y:=midScreenY;
- GLSetCursorPos(midScreenX, midScreenY);
- end;
- // SetMouseLookActive
- //
- procedure TgxUserInterface.SetMouseLookActive(const val : Boolean);
- begin
- if val<>FMouseActive then
- if val then
- MouseLookActivate
- else MouseLookDeactivate;
- end;
- procedure TgxUserInterface.MouseUpdate;
- begin
- if FMouseActive then
- GLGetCursorPos(FPrevPoint);
- end;
- function TgxUserInterface.Mouselook : Boolean;
- var
- deltaX, deltaY : Single;
- begin
- Result := False;
- if not FMouseActive then exit;
- deltax:=(FPrevPoint.x-midscreenX)*mousespeed;
- deltay:=-(FPrevPoint.y-midscreenY)*mousespeed;
- If InvertMouse then deltay:=-deltay;
- if deltax <> 0 then begin
- TurnHorizontal(deltax*0.01);
- result := True;
- end;
- if deltay <> 0 then begin
- TurnVertical(deltay*0.01);
- result := True;
- end;
- if (FPrevPoint.x <> midScreenX) or (FPrevPoint.y <> midScreenY) then
- GLSetCursorPos(midScreenX, midScreenY);
- end;
- Constructor TgxUserInterface.Create(AOwner : TComponent);
- Begin
- inherited;
- FMouseSpeed :=0;
- FMouseActive:=False;
- midScreenX:=GLGetScreenWidth div 2;
- midScreenY:=GLGetScreenHeight div 2;
- FPrevPoint.x:=midScreenX; FPrevPoint.Y:=midScreenY;
- End;
- Destructor TgxUserInterface.Destroy;
- begin
- if FMouseActive then MouseLookDeactivate; // added by JAJ
- inherited;
- end;
- procedure TgxUserInterface.Notification(AComponent: TComponent; operation:
- TOperation);
- begin
- if operation = opRemove then begin
- if AComponent = FGLNavigator then
- setNavigator(nil);
- if AComponent = FGLVertNavigator then
- setVertNavigator(nil);
- end;
- inherited;
- end;
- procedure TgxUserInterface.setNavigator(val: TgxNavigator);
- begin
- if assigned(FGLNavigator) then FGLNavigator.RemoveFreeNotification(self);
- FGLNavigator:= val;
- if assigned(val) then val.FreeNotification(self);
- end;
- procedure TgxUserInterface.setVertNavigator(val: TgxNavigator);
- begin
- if assigned(FGLVertNavigator) then FGLVertNavigator.RemoveFreeNotification(self);
- FGLVertNavigator:= val;
- if assigned(val) then val.FreeNotification(self);
- end;
- end.
|