2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Unit for navigating TGLBaseObjects.
8
20/02/07 - DaStr - Moved Notification(), SetObject(), SetUseVirtualUp(),
9
SetVirtualUp(), CalcRight() to the "protected" section
10
Private "point1" renamed to FPrevPoint
12
TGLNavigator.SetObject made virtual
13
19/02/07 - DaStr - TGLNavigator.Create - FVirtualUp creation fixed
14
Added default values to TGLNavigator and TGLUserInterface
15
29/01/07 - DaStr - Moved registration to GLSceneRegister.pas
16
08/03/06 - ur - Fixed warnigs for Delphi 2006
17
31/10/05 - Mathx - Fixed bug 1340637 relating to freeNotifications on
18
the TGLUserInterface component.
19
18/12/04 - PhP - Added FlyForward
20
03/07/04 - LR - Added GLShowCursor, GLSetCursorPos, GLGetCursorPos,
21
GLGetScreenWidth, GLGetScreenHeight for Linux compatibility
22
11/05/04 - JAJ - Added some features and fixed a bug.
23
01/06/03 - JAJ - Added notification to movingobject...
24
01/06/03 - fig - CurrentHangle implementet...
25
14/07/02 - EG - InvertMouse (Joen A. Joensen)
26
18/03/02 - EG - Added MouseLookActive property, Fixed framerate dependency
27
15/03/02 - JAJ - Structure Change - Mouselook moved to newly created TGLUserInterface.
28
15/03/02 - RMCH - Added Mouselook capability.
29
09/11/00 - JAJ - First submitted. Base Class TGLNavigator included.
42
GLVectorGeometry, GLScene, GLCrossPlatform, GLCoordinates, GLScreen
49
{ TGLNavigator is the component for moving a TGLBaseSceneObject, and all Classes based on it,
50
this includes all the objects from the Scene Editor.
52
The four calls to get you started is
54
TurnHorisontal : it turns left and right.
55
TurnVertical : it turns up and down.
56
MoveForward : moves back and forth.
57
FlyForward : moves back and forth in the movingobject's direction
59
The three properties to get you started is
61
MovingObject : The Object that you are moving.
62
UseVirtualUp : When UseVirtualUp is set you navigate Quake style. If it isn't
63
it's more like Descent.
64
AngleLock : Allows you to block the Vertical angles. Should only be used in
65
conjunction with UseVirtualUp.
66
MoveUpWhenMovingForward : Changes movement from Quake to Arcade Airplane...
68
InvertHorizontalSteeringWhenUpsideDown : When using virtual up, and vertically
69
rotating beyond 90 degrees, will make steering seem inverted, so we "invert" back
73
TGLNavigator = class(TComponent)
75
FObject: TGLBaseSceneObject;
76
FVirtualRight: TVector;
77
FVirtualUp: TGLCoordinates;
78
FUseVirtualUp: boolean;
79
FAutoUpdateObject: boolean;
82
FCurrentVAngle: single;
83
FCurrentHAngle: single;
85
FMoveUpWhenMovingForward: boolean;
86
FInvertHorizontalSteeringWhenUpsideDown: boolean;
88
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
89
procedure SetObject(NewObject: TGLBaseSceneObject); virtual;
90
procedure SetUseVirtualUp(UseIt: boolean);
91
procedure SetVirtualUp(Up: TGLCoordinates);
92
function CalcRight: TVector;
94
constructor Create(AOwner: TComponent); override;
95
destructor Destroy; override;
97
procedure TurnHorizontal(Angle: single);
98
procedure TurnVertical(Angle: single);
99
procedure MoveForward(Distance: single);
100
procedure StrafeHorizontal(Distance: single);
101
procedure StrafeVertical(Distance: single);
102
procedure Straighten;
103
procedure FlyForward(Distance: single);
105
procedure LoadState(Stream: TStream);
106
procedure SaveState(Stream: TStream);
108
property CurrentVAngle: single read FCurrentVAngle;
109
property CurrentHAngle: single read FCurrentHAngle;
111
property MoveUpWhenMovingForward: boolean read FMoveUpWhenMovingForward write FMoveUpWhenMovingForward default False;
112
property InvertHorizontalSteeringWhenUpsideDown: boolean read FInvertHorizontalSteeringWhenUpsideDown write FInvertHorizontalSteeringWhenUpsideDown default False;
113
property VirtualUp: TGLCoordinates read FVirtualUp write SetVirtualUp;
114
property MovingObject: TGLBaseSceneObject read FObject write SetObject;
115
property UseVirtualUp: boolean read FUseVirtualUp write SetUseVirtualUp default False;
116
property AutoUpdateObject: boolean read FAutoUpdateObject write FAutoUpdateObject default False;
117
property MaxAngle: single read FMaxAngle write FMaxAngle;
118
property MinAngle: single read FMinAngle write FMinAngle;
119
property AngleLock: boolean read FAngleLock write FAngleLock default False;
124
{ TGLUserInterface is the component which reads the userinput and transform it into action.
126
The four calls to get you started is
128
MouseLookActivate : set us up the bomb.
129
MouseLookDeActivate : defuses it.
130
Mouselook(deltaTime: double) : handles mouse look... Should be called in the Cadencer event. (Though it works every where!)
131
MouseUpdate : Resets mouse position so that you don't notice that the mouse is limited to the screen should be called after Mouselook.
133
The four properties to get you started are:
135
InvertMouse : Inverts the mouse Y axis.
136
MouseSpeed : Also known as mouse sensitivity.
137
GLNavigator : The Navigator which receives the user movement.
138
GLVertNavigator : The Navigator which if set receives the vertical user movement. Used mostly for cameras....
142
TGLUserInterface = class(TComponent)
144
FPrevPoint: TGLPoint;
145
midScreenX, midScreenY: integer;
146
FMouseActive: boolean;
148
FGLNavigator: TGLNavigator;
149
FGLVertNavigator: TGLNavigator;
150
FInvertMouse: boolean;
151
procedure MouseInitialize;
152
procedure SetMouseLookActive(const val: boolean);
153
procedure setNavigator(val: TGLNavigator);
154
procedure setVertNavigator(val: TGLNavigator);
156
procedure Notification(AComponent: TComponent; operation: TOperation); override;
158
constructor Create(AOwner: TComponent); override;
159
destructor Destroy; override;
160
procedure MouseUpdate;
161
function MouseLook : Boolean;
162
procedure MouseLookActiveToggle;
163
procedure MouseLookActivate;
164
procedure MouseLookDeactivate;
165
function IsMouseLookOn: Boolean;
166
procedure TurnHorizontal(Angle : Double);
167
procedure TurnVertical(Angle : Double);
168
property MouseLookActive : Boolean read FMouseActive write SetMouseLookActive;
170
property InvertMouse: boolean read FInvertMouse write FInvertMouse default False;
171
property MouseSpeed: single read FMouseSpeed write FMouseSpeed;
172
property GLNavigator: TGLNavigator read FGLNavigator write setNavigator;
173
property GLVertNavigator: TGLNavigator read FGLVertNavigator write setVertNavigator;
178
Constructor TGLNavigator.Create(AOwner : TComponent);
181
FVirtualUp := TGLCoordinates.CreateInitialized(Self, ZHmgVector, csPoint);
186
Destructor TGLNavigator.Destroy;
194
Procedure TGLNavigator.SetObject(NewObject : TGLBaseSceneObject);
196
If FObject <> NewObject then
198
If Assigned(FObject) then
199
FObject.RemoveFreeNotification(Self);
201
FObject := NewObject;
202
If Assigned(FObject) then
204
if csdesigning in componentstate then
206
If VectorLength(FVirtualUp.AsVector) = 0 then
208
FVirtualUp.AsVector := FObject.Up.AsVector;
213
If FUseVirtualUp Then FVirtualRight := CalcRight;
215
FObject.FreeNotification(Self);
220
procedure TGLNavigator.Notification(AComponent: TComponent; Operation: TOperation);
223
If Operation = opRemove then
224
If AComponent = FObject then
230
Function TGLNavigator.CalcRight : TVector;
233
If Assigned(FObject) then
234
If FUseVirtualUp Then
236
VectorCrossProduct(FObject.Direction.AsVector, FVirtualUp.AsVector, Result);
237
ScaleVector(Result,1/VectorLength(Result));
238
End else VectorCrossProduct(FObject.Direction.AsVector, FObject.Up.AsVector, Result); { automaticly length(1), if not this is a bug }
241
Procedure TGLNavigator.TurnHorizontal(Angle : Single);
250
If InvertHorizontalSteeringWhenUpsideDown and ((CurrentVAngle < -90) or (CurrentVAngle > 90)) then
253
FCurrentHAngle:=(FCurrentHAngle-Angle);
255
If (FCurrentHAngle < 0) or (FCurrentHAngle > 360) then
257
TempVal := (FCurrentHAngle)/360;
258
FCurrentHAngle := (TempVal-Floor(TempVal))*360;
261
Angle := DegToRad(Angle); {make it ready for Cos and Sin }
262
If FUseVirtualUp Then
264
SetVector(U, VirtualUp.AsVector);
265
T := FObject.Up.AsVector;
266
RotateVector(T,U,Angle);
267
FObject.Up.AsVector := T;
269
T := FObject.Direction.AsVector;
270
RotateVector(T,U,Angle);
271
FObject.Direction.AsVector := T;
272
End else FObject.Direction.AsVector := VectorCombine(FObject.Direction.AsVector,CalcRight,Cos(Angle),Sin(Angle));
275
Procedure TGLNavigator.TurnVertical(Angle : Single);
278
ExpectedAngle : Single;
279
CosAngle, SinAngle : Single;
284
ExpectedAngle := FCurrentVAngle+Angle;
287
If ExpectedAngle > FMaxAngle then
289
If FCurrentVAngle = FMaxAngle then Exit;
290
Angle := FMaxAngle-FCurrentVAngle;
291
ExpectedAngle := FMaxAngle;
294
If ExpectedAngle < FMinAngle then
296
If FCurrentVAngle = FMinAngle then Exit;
297
Angle := FMinAngle-FCurrentVAngle;
298
ExpectedAngle := FMinAngle;
302
FCurrentVAngle := ExpectedAngle;
304
If (FCurrentVAngle < -180) or (FCurrentVAngle > 180) then
306
TempVal := (FCurrentVAngle+180)/360;
307
FCurrentVAngle := (TempVal-Floor(TempVal))*360-180;
310
Angle := DegToRad(Angle); {make it ready for Cos and Sin }
311
SinCos(Angle,SinAngle,CosAngle);
312
Direction := VectorCombine(MovingObject.Direction.AsVector,MovingObject.Up.AsVector,CosAngle,SinAngle);
313
MovingObject.Up.AsVector := VectorCombine(MovingObject.Direction.AsVector,MovingObject.Up.AsVector,SinAngle,CosAngle);
314
MovingObject.Direction.AsVector := Direction;
317
Procedure TGLNavigator.MoveForward(Distance : Single);
319
If (FUseVirtualUp and (not MoveUpWhenMovingForward)) Then
321
FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,VectorCrossProduct(FVirtualUp.AsVector,CalcRight),1,Distance);
322
End else FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FObject.Direction.AsVector,1,Distance);
325
Procedure TGLNavigator.StrafeHorizontal(Distance : Single);
327
FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,CalcRight,1,Distance);
330
Procedure TGLNavigator.StrafeVertical(Distance : Single);
334
FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FVirtualUp.AsVector,1,Distance);
335
End else FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FObject.Up.AsVector,1,Distance);
338
procedure TGLNavigator.FlyForward(Distance: single);
340
FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector, FObject.Direction.AsVector, 1, Distance);
343
Procedure TGLNavigator.Straighten;
355
A := VectorAngleCosine(AffineVectorMake(MovingObject.Up.AsVector), AffineVectorMake(VirtualUp.AsVector));
356
MovingObject.Up.AsVector := VirtualUp.AsVector;
358
VectorCrossProduct(R, FVirtualUp.AsVector, D);
361
ScaleVector(D,-1/VectorLength(D))
363
ScaleVector(D,1/VectorLength(D));
365
MovingObject.Direction.AsVector := D;
368
Procedure TGLNavigator.SetUseVirtualUp(UseIt : Boolean);
371
FUseVirtualUp := UseIt;
372
if csdesigning in componentstate then Exit;
373
If FUseVirtualUp then FVirtualRight := CalcRight;
377
Procedure TGLNavigator.SetVirtualUp(Up : TGLCoordinates);
379
FVirtualUp.Assign(Up);
380
if csdesigning in componentstate then Exit;
381
If FUseVirtualUp then FVirtualRight := CalcRight;
384
Procedure TGLNavigator.LoadState(Stream : TStream);
387
Vector : TAffineVector;
392
Stream.Read(Vector,SizeOf(TAffineVector));
393
FObject.Position.AsAffineVector := Vector;
394
Stream.Read(Vector,SizeOf(TAffineVector));
395
FObject.Direction.AsAffineVector := Vector;
396
Stream.Read(Vector,SizeOf(TAffineVector));
397
FObject.Up.AsAffineVector := Vector;
398
Stream.Read(B,SizeOf(ByteBool));
400
Stream.Read(B,SizeOf(ByteBool));
402
Stream.Read(S,SizeOf(Single));
404
Stream.Read(S,SizeOf(Single));
406
Stream.Read(S,SizeOf(Single));
408
Stream.Read(S,SizeOf(Single));
412
Procedure TGLNavigator.SaveState(Stream : TStream);
415
Vector : TAffineVector;
420
Vector := FObject.Position.AsAffineVector;
421
Stream.Write(Vector,SizeOf(TAffineVector));
422
Vector := FObject.Direction.AsAffineVector;
423
Stream.Write(Vector,SizeOf(TAffineVector));
424
Vector := FObject.Up.AsAffineVector;
425
Stream.Write(Vector,SizeOf(TAffineVector));
427
Stream.Write(B,SizeOf(ByteBool));
429
Stream.Write(B,SizeOf(ByteBool));
431
Stream.Write(S,SizeOf(Single));
433
Stream.Write(S,SizeOf(Single));
435
Stream.Write(S,SizeOf(Single));
437
Stream.Write(S,SizeOf(Single));
440
function TGLUserInterface.IsMouseLookOn: Boolean;
442
Result:=FMouseActive;
445
Procedure TGLUserInterface.TurnHorizontal(Angle : Double);
448
GLNavigator.TurnHorizontal(Angle);
451
Procedure TGLUserInterface.TurnVertical(Angle : Double);
454
If Assigned(GLVertNavigator) then GLVertNavigator.TurnVertical(Angle)
455
else GLNavigator.TurnVertical(Angle);
458
procedure TGLUserInterface.MouseLookActiveToggle;
462
else MouseLookActivate;
465
procedure TGLUserInterface.MouseLookActivate;
467
if not FMouseActive then begin
468
FMouseActive := True;
474
procedure TGLUserInterface.MouseLookDeactivate;
476
if FMouseActive then begin
477
FMouseActive := False;
482
procedure TGLUserInterface.MouseInitialize;
484
midScreenX:=GLGetScreenWidth div 2;
485
midScreenY:=GLGetScreenHeight div 2;
487
FPrevPoint.x:=midScreenX; FPrevPoint.Y:=midScreenY;
488
GLSetCursorPos(midScreenX, midScreenY);
493
procedure TGLUserInterface.SetMouseLookActive(const val : Boolean);
495
if val<>FMouseActive then
498
else MouseLookDeactivate;
501
procedure TGLUserInterface.MouseUpdate;
504
GLGetCursorPos(FPrevPoint);
509
function TGLUserInterface.Mouselook : Boolean;
511
deltaX, deltaY : Single;
514
if not FMouseActive then exit;
516
deltax:=(FPrevPoint.x-midscreenX)*mousespeed;
517
deltay:=-(FPrevPoint.y-midscreenY)*mousespeed;
518
If InvertMouse then deltay:=-deltay;
520
if deltax <> 0 then begin
521
TurnHorizontal(deltax*0.01);
524
if deltay <> 0 then begin
525
TurnVertical(deltay*0.01);
529
if (FPrevPoint.x <> midScreenX) or (FPrevPoint.y <> midScreenY) then
530
GLSetCursorPos(midScreenX, midScreenY);
533
Constructor TGLUserInterface.Create(AOwner : TComponent);
538
midScreenX:=GLGetScreenWidth div 2;
539
midScreenY:=GLGetScreenHeight div 2;
540
FPrevPoint.x:=midScreenX; FPrevPoint.Y:=midScreenY;
543
Destructor TGLUserInterface.Destroy;
546
if FMouseActive then MouseLookDeactivate; // added by JAJ
550
procedure TGLUserInterface.Notification(AComponent: TComponent; operation:
553
if operation = opRemove then begin
554
if AComponent = FGLNavigator then
556
if AComponent = FGLVertNavigator then
557
setVertNavigator(nil);
562
procedure TGLUserInterface.setNavigator(val: TGLNavigator);
564
if assigned(FGLNavigator) then FGLNavigator.RemoveFreeNotification(self);
566
if assigned(val) then val.FreeNotification(self);
569
procedure TGLUserInterface.setVertNavigator(val: TGLNavigator);
571
if assigned(FGLVertNavigator) then FGLVertNavigator.RemoveFreeNotification(self);
572
FGLVertNavigator:= val;
573
if assigned(val) then val.FreeNotification(self);