GXS.Navigator.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.Navigator;
  5. (* Unit for navigating TgxBaseObjects *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. System.Types,
  10. System.SysUtils,
  11. System.Classes,
  12. System.Math,
  13. Stage.VectorGeometry,
  14. GXS.Scene,
  15. GXS.Coordinates,
  16. GXS.Screen,
  17. Stage.VectorTypes;
  18. type
  19. (* TgxNavigator is the component for moving a TgxBaseSceneObject, and all Classes based on it,
  20. this includes all the objects from the Scene Editor.
  21. The four calls to get you started is
  22. TurnHorisontal : it turns left and right.
  23. TurnVertical : it turns up and down.
  24. MoveForward : moves back and forth.
  25. FlyForward : moves back and forth in the movingobject's direction
  26. The three properties to get you started is
  27. MovingObject : The Object that you are moving.
  28. UseVirtualUp : When UseVirtualUp is set you navigate Quake style. If it isn't
  29. it's more like Descent.
  30. AngleLock : Allows you to block the Vertical angles. Should only be used in
  31. conjunction with UseVirtualUp.
  32. MoveUpWhenMovingForward : Changes movement from Quake to Arcade Airplane...
  33. (no tilt and flying)
  34. InvertHorizontalSteeringWhenUpsideDown : When using virtual up, and vertically
  35. rotating beyond 90 degrees, will make steering seem inverted, so we "invert" back
  36. to normal. *)
  37. TgxNavigator = class(TComponent)
  38. private
  39. FObject: TgxBaseSceneObject;
  40. FVirtualRight: TVector4f;
  41. FVirtualUp: TgxCoordinates;
  42. FUseVirtualUp: boolean;
  43. FAutoUpdateObject: boolean;
  44. FMaxAngle: single;
  45. FMinAngle: single;
  46. FCurrentVAngle: single;
  47. FCurrentHAngle: single;
  48. FAngleLock: boolean;
  49. FMoveUpWhenMovingForward: boolean;
  50. FInvertHorizontalSteeringWhenUpsideDown: boolean;
  51. protected
  52. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  53. procedure SetObject(NewObject: TgxBaseSceneObject); virtual;
  54. procedure SetUseVirtualUp(UseIt: boolean);
  55. procedure SetVirtualUp(Up: TgxCoordinates);
  56. function CalcRight: TVector4f;
  57. public
  58. constructor Create(AOwner: TComponent); override;
  59. destructor Destroy; override;
  60. procedure TurnHorizontal(Angle: single);
  61. procedure TurnVertical(Angle: single);
  62. procedure MoveForward(Distance: single);
  63. procedure StrafeHorizontal(Distance: single);
  64. procedure StrafeVertical(Distance: single);
  65. procedure Straighten;
  66. procedure FlyForward(Distance: single);
  67. procedure LoadState(Stream: TStream);
  68. procedure SaveState(Stream: TStream);
  69. property CurrentVAngle: single read FCurrentVAngle;
  70. property CurrentHAngle: single read FCurrentHAngle;
  71. published
  72. property MoveUpWhenMovingForward: boolean read FMoveUpWhenMovingForward write FMoveUpWhenMovingForward default False;
  73. property InvertHorizontalSteeringWhenUpsideDown: boolean read FInvertHorizontalSteeringWhenUpsideDown write FInvertHorizontalSteeringWhenUpsideDown default False;
  74. property VirtualUp: TgxCoordinates read FVirtualUp write SetVirtualUp;
  75. property MovingObject: TgxBaseSceneObject read FObject write SetObject;
  76. property UseVirtualUp: boolean read FUseVirtualUp write SetUseVirtualUp default False;
  77. property AutoUpdateObject: boolean read FAutoUpdateObject write FAutoUpdateObject default False;
  78. property MaxAngle: single read FMaxAngle write FMaxAngle;
  79. property MinAngle: single read FMinAngle write FMinAngle;
  80. property AngleLock: boolean read FAngleLock write FAngleLock default False;
  81. end;
  82. (* TgxUserInterface is the component which reads the userinput and transform it into action.
  83. The four calls to get you started is
  84. MouseLookActivate : set us up the bomb.
  85. MouseLookDeActivate : defuses it.
  86. Mouselook(deltaTime: double) : handles mouse look... Should be called in the Cadencer event. (Though it works every where!)
  87. MouseUpdate : Resets mouse position so that you don't notice that the mouse is limited to the screen should be called after Mouselook.
  88. The four properties to get you started are:
  89. InvertMouse : Inverts the mouse Y axis.
  90. MouseSpeed : Also known as mouse sensitivity.
  91. GXNavigator : The Navigator which receives the user movement.
  92. GXVertNavigator : The Navigator which if set receives the vertical user movement. Used mostly for cameras.... *)
  93. TgxUserInterface = class(TComponent)
  94. private
  95. FPrevPoint: TPoint;
  96. midScreenX, midScreenY: integer;
  97. FMouseActive: boolean;
  98. FMouseSpeed: single;
  99. FGLNavigator: TgxNavigator;
  100. FGLVertNavigator: TgxNavigator;
  101. FInvertMouse: boolean;
  102. procedure MouseInitialize;
  103. procedure SetMouseLookActive(const val: boolean);
  104. procedure setNavigator(val: TgxNavigator);
  105. procedure setVertNavigator(val: TgxNavigator);
  106. protected
  107. procedure Notification(AComponent: TComponent; operation: TOperation); override;
  108. public
  109. constructor Create(AOwner: TComponent); override;
  110. destructor Destroy; override;
  111. procedure MouseUpdate;
  112. function MouseLook : Boolean;
  113. procedure MouseLookActiveToggle;
  114. procedure MouseLookActivate;
  115. procedure MouseLookDeactivate;
  116. function IsMouseLookOn: Boolean;
  117. procedure TurnHorizontal(Angle : Double);
  118. procedure TurnVertical(Angle : Double);
  119. property MouseLookActive : Boolean read FMouseActive write SetMouseLookActive;
  120. published
  121. property InvertMouse: boolean read FInvertMouse write FInvertMouse default False;
  122. property MouseSpeed: single read FMouseSpeed write FMouseSpeed;
  123. property GLNavigator: TgxNavigator read FGLNavigator write setNavigator;
  124. property GLVertNavigator: TgxNavigator read FGLVertNavigator write setVertNavigator;
  125. end;
  126. //-------------------------------------------------------------------------
  127. implementation
  128. //-------------------------------------------------------------------------
  129. constructor TgxNavigator.Create(AOwner : TComponent);
  130. Begin
  131. inherited;
  132. FVirtualUp := TgxCoordinates.CreateInitialized(Self, ZHmgVector, csPoint);
  133. FCurrentVAngle := 0;
  134. FCurrentHAngle := 0;
  135. End;
  136. destructor TgxNavigator.Destroy;
  137. Begin
  138. FVirtualUp.Free;
  139. inherited;
  140. End;
  141. procedure TgxNavigator.SetObject(NewObject : TgxBaseSceneObject);
  142. Begin
  143. If FObject <> NewObject then
  144. Begin
  145. If Assigned(FObject) then
  146. FObject.RemoveFreeNotification(Self);
  147. FObject := NewObject;
  148. If Assigned(FObject) then
  149. Begin
  150. if csdesigning in componentstate then
  151. Begin
  152. If VectorLength(FVirtualUp.AsVector) = 0 then
  153. Begin
  154. FVirtualUp.AsVector := FObject.Up.AsVector;
  155. End;
  156. Exit;
  157. End;
  158. If FUseVirtualUp Then FVirtualRight := CalcRight;
  159. FObject.FreeNotification(Self);
  160. End;
  161. End;
  162. End;
  163. procedure TgxNavigator.Notification(AComponent: TComponent; Operation: TOperation);
  164. begin
  165. If Operation = opRemove then
  166. If AComponent = FObject then
  167. MovingObject := Nil;
  168. inherited;
  169. end;
  170. function TgxNavigator.CalcRight : TVector4f;
  171. begin
  172. If Assigned(FObject) then
  173. If FUseVirtualUp Then
  174. Begin
  175. VectorCrossProduct(FObject.Direction.AsVector, FVirtualUp.AsVector, Result);
  176. ScaleVector(Result,1/VectorLength(Result));
  177. End else VectorCrossProduct(FObject.Direction.AsVector, FObject.Up.AsVector, Result); { automaticly length(1), if not this is a bug }
  178. end;
  179. procedure TgxNavigator.TurnHorizontal(Angle : Single);
  180. var
  181. T : TVector4f;
  182. U : TAffineVector;
  183. TempVal : Single;
  184. Begin
  185. If InvertHorizontalSteeringWhenUpsideDown and ((CurrentVAngle < -90) or (CurrentVAngle > 90)) then
  186. Angle := -Angle;
  187. FCurrentHAngle:=(FCurrentHAngle-Angle);
  188. If (FCurrentHAngle < 0) or (FCurrentHAngle > 360) then
  189. Begin
  190. TempVal := (FCurrentHAngle)/360;
  191. FCurrentHAngle := (TempVal-Floor(TempVal))*360;
  192. End;
  193. Angle := DegToRadian(Angle); {make it ready for Cos and Sin }
  194. If FUseVirtualUp Then
  195. Begin
  196. SetVector(U, VirtualUp.AsVector);
  197. T := FObject.Up.AsVector;
  198. RotateVector(T,U,Angle);
  199. FObject.Up.AsVector := T;
  200. T := FObject.Direction.AsVector;
  201. RotateVector(T,U,Angle);
  202. FObject.Direction.AsVector := T;
  203. End else FObject.Direction.AsVector := VectorCombine(FObject.Direction.AsVector,CalcRight,Cos(Angle),Sin(Angle));
  204. End;
  205. procedure TgxNavigator.TurnVertical(Angle : Single);
  206. var
  207. ExpectedAngle : Single;
  208. CosAngle, SinAngle : Single;
  209. TempVal : Single;
  210. Direction : TVector4f;
  211. begin
  212. ExpectedAngle := FCurrentVAngle+Angle;
  213. If FAngleLock then
  214. Begin
  215. If ExpectedAngle > FMaxAngle then
  216. Begin
  217. If FCurrentVAngle = FMaxAngle then Exit;
  218. Angle := FMaxAngle-FCurrentVAngle;
  219. ExpectedAngle := FMaxAngle;
  220. End else
  221. Begin
  222. If ExpectedAngle < FMinAngle then
  223. Begin
  224. If FCurrentVAngle = FMinAngle then Exit;
  225. Angle := FMinAngle-FCurrentVAngle;
  226. ExpectedAngle := FMinAngle;
  227. End;
  228. End;
  229. End;
  230. FCurrentVAngle := ExpectedAngle;
  231. If (FCurrentVAngle < -180) or (FCurrentVAngle > 180) then
  232. Begin
  233. TempVal := (FCurrentVAngle+180)/360;
  234. FCurrentVAngle := (TempVal-Floor(TempVal))*360-180;
  235. End;
  236. Angle := DegToRadian(Angle); {make it ready for Cos and Sin }
  237. SinCosine(Angle,SinAngle,CosAngle);
  238. Direction := VectorCombine(MovingObject.Direction.AsVector,MovingObject.Up.AsVector,CosAngle,SinAngle);
  239. MovingObject.Up.AsVector := VectorCombine(MovingObject.Direction.AsVector,MovingObject.Up.AsVector,SinAngle,CosAngle);
  240. MovingObject.Direction.AsVector := Direction;
  241. end;
  242. procedure TgxNavigator.MoveForward(Distance : Single);
  243. Begin
  244. If (FUseVirtualUp and (not MoveUpWhenMovingForward)) Then
  245. Begin
  246. FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,VectorCrossProduct(FVirtualUp.AsVector,CalcRight),1,Distance);
  247. End else FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FObject.Direction.AsVector,1,Distance);
  248. End;
  249. Procedure TgxNavigator.StrafeHorizontal(Distance : Single);
  250. Begin
  251. FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,CalcRight,1,Distance);
  252. End;
  253. Procedure TgxNavigator.StrafeVertical(Distance : Single);
  254. Begin
  255. If UseVirtualUp Then
  256. Begin
  257. FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FVirtualUp.AsVector,1,Distance);
  258. End else FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector,FObject.Up.AsVector,1,Distance);
  259. End;
  260. procedure TgxNavigator.FlyForward(Distance: single);
  261. begin
  262. FObject.Position.AsVector := VectorCombine(FObject.Position.AsVector, FObject.Direction.AsVector, 1, Distance);
  263. end;
  264. Procedure TgxNavigator.Straighten;
  265. Var
  266. R : TVector4f;
  267. D : TVector4f;
  268. A : Single;
  269. Begin
  270. FCurrentVAngle := 0;
  271. FCurrentHAngle := 0;
  272. R := CalcRight;
  273. A := VectorAngleCosine(AffineVectorMake(MovingObject.Up.AsVector), AffineVectorMake(VirtualUp.AsVector));
  274. MovingObject.Up.AsVector := VirtualUp.AsVector;
  275. VectorCrossProduct(R, FVirtualUp.AsVector, D);
  276. If A >= 0 then
  277. ScaleVector(D,-1/VectorLength(D))
  278. else
  279. ScaleVector(D,1/VectorLength(D));
  280. MovingObject.Direction.AsVector := D;
  281. End;
  282. Procedure TgxNavigator.SetUseVirtualUp(UseIt : Boolean);
  283. Begin
  284. FUseVirtualUp := UseIt;
  285. if csdesigning in componentstate then Exit;
  286. If FUseVirtualUp then FVirtualRight := CalcRight;
  287. End;
  288. Procedure TgxNavigator.SetVirtualUp(Up : TgxCoordinates);
  289. begin
  290. FVirtualUp.Assign(Up);
  291. if csdesigning in componentstate then Exit;
  292. If FUseVirtualUp then FVirtualRight := CalcRight;
  293. end;
  294. procedure TgxNavigator.LoadState(Stream : TStream);
  295. var
  296. Vector : TAffineVector;
  297. B : ByteBool;
  298. S : Single;
  299. begin
  300. Stream.Read(Vector,SizeOf(TAffineVector));
  301. FObject.Position.AsAffineVector := Vector;
  302. Stream.Read(Vector,SizeOf(TAffineVector));
  303. FObject.Direction.AsAffineVector := Vector;
  304. Stream.Read(Vector,SizeOf(TAffineVector));
  305. FObject.Up.AsAffineVector := Vector;
  306. Stream.Read(B,SizeOf(ByteBool));
  307. UseVirtualUp := B;
  308. Stream.Read(B,SizeOf(ByteBool));
  309. FAngleLock := B;
  310. Stream.Read(S,SizeOf(Single));
  311. FMaxAngle := S;
  312. Stream.Read(S,SizeOf(Single));
  313. FMinAngle := S;
  314. Stream.Read(S,SizeOf(Single));
  315. FCurrentVAngle := S;
  316. Stream.Read(S,SizeOf(Single));
  317. FCurrentHAngle := S;
  318. end;
  319. procedure TgxNavigator.SaveState(Stream : TStream);
  320. var
  321. Vector : TAffineVector;
  322. B : ByteBool;
  323. S : Single;
  324. begin
  325. Vector := FObject.Position.AsAffineVector;
  326. Stream.Write(Vector,SizeOf(TAffineVector));
  327. Vector := FObject.Direction.AsAffineVector;
  328. Stream.Write(Vector,SizeOf(TAffineVector));
  329. Vector := FObject.Up.AsAffineVector;
  330. Stream.Write(Vector,SizeOf(TAffineVector));
  331. B := UseVirtualUp;
  332. Stream.Write(B,SizeOf(ByteBool));
  333. B := FAngleLock;
  334. Stream.Write(B,SizeOf(ByteBool));
  335. S := FMaxAngle;
  336. Stream.Write(S,SizeOf(Single));
  337. S := FMinAngle;
  338. Stream.Write(S,SizeOf(Single));
  339. S := FCurrentVAngle;
  340. Stream.Write(S,SizeOf(Single));
  341. S := FCurrentHAngle;
  342. Stream.Write(S,SizeOf(Single));
  343. end;
  344. function TgxUserInterface.IsMouseLookOn: Boolean;
  345. begin
  346. Result:=FMouseActive;
  347. end;
  348. Procedure TgxUserInterface.TurnHorizontal(Angle : Double);
  349. Begin
  350. GLNavigator.TurnHorizontal(Angle);
  351. End;
  352. Procedure TgxUserInterface.TurnVertical(Angle : Double);
  353. Begin
  354. If Assigned(GLVertNavigator) then GLVertNavigator.TurnVertical(Angle)
  355. else GLNavigator.TurnVertical(Angle);
  356. End;
  357. procedure TgxUserInterface.MouseLookActiveToggle;
  358. begin
  359. if FMouseActive then
  360. MouseLookDeactivate
  361. else MouseLookActivate;
  362. end;
  363. procedure TgxUserInterface.MouseLookActivate;
  364. begin
  365. if not FMouseActive then begin
  366. FMouseActive := True;
  367. MouseInitialize;
  368. GLShowCursor(False);
  369. end;
  370. end;
  371. procedure TgxUserInterface.MouseLookDeactivate;
  372. begin
  373. if FMouseActive then begin
  374. FMouseActive := False;
  375. GLShowCursor(True);
  376. end;
  377. end;
  378. procedure TgxUserInterface.MouseInitialize;
  379. begin
  380. midScreenX:=GLGetScreenWidth div 2;
  381. midScreenY:=GLGetScreenHeight div 2;
  382. FPrevPoint.x:=midScreenX; FPrevPoint.Y:=midScreenY;
  383. GLSetCursorPos(midScreenX, midScreenY);
  384. end;
  385. // SetMouseLookActive
  386. //
  387. procedure TgxUserInterface.SetMouseLookActive(const val : Boolean);
  388. begin
  389. if val<>FMouseActive then
  390. if val then
  391. MouseLookActivate
  392. else MouseLookDeactivate;
  393. end;
  394. procedure TgxUserInterface.MouseUpdate;
  395. begin
  396. if FMouseActive then
  397. GLGetCursorPos(FPrevPoint);
  398. end;
  399. function TgxUserInterface.Mouselook : Boolean;
  400. var
  401. deltaX, deltaY : Single;
  402. begin
  403. Result := False;
  404. if not FMouseActive then exit;
  405. deltax:=(FPrevPoint.x-midscreenX)*mousespeed;
  406. deltay:=-(FPrevPoint.y-midscreenY)*mousespeed;
  407. If InvertMouse then deltay:=-deltay;
  408. if deltax <> 0 then begin
  409. TurnHorizontal(deltax*0.01);
  410. result := True;
  411. end;
  412. if deltay <> 0 then begin
  413. TurnVertical(deltay*0.01);
  414. result := True;
  415. end;
  416. if (FPrevPoint.x <> midScreenX) or (FPrevPoint.y <> midScreenY) then
  417. GLSetCursorPos(midScreenX, midScreenY);
  418. end;
  419. Constructor TgxUserInterface.Create(AOwner : TComponent);
  420. Begin
  421. inherited;
  422. FMouseSpeed :=0;
  423. FMouseActive:=False;
  424. midScreenX:=GLGetScreenWidth div 2;
  425. midScreenY:=GLGetScreenHeight div 2;
  426. FPrevPoint.x:=midScreenX; FPrevPoint.Y:=midScreenY;
  427. End;
  428. Destructor TgxUserInterface.Destroy;
  429. begin
  430. if FMouseActive then MouseLookDeactivate; // added by JAJ
  431. inherited;
  432. end;
  433. procedure TgxUserInterface.Notification(AComponent: TComponent; operation:
  434. TOperation);
  435. begin
  436. if operation = opRemove then begin
  437. if AComponent = FGLNavigator then
  438. setNavigator(nil);
  439. if AComponent = FGLVertNavigator then
  440. setVertNavigator(nil);
  441. end;
  442. inherited;
  443. end;
  444. procedure TgxUserInterface.setNavigator(val: TgxNavigator);
  445. begin
  446. if assigned(FGLNavigator) then FGLNavigator.RemoveFreeNotification(self);
  447. FGLNavigator:= val;
  448. if assigned(val) then val.FreeNotification(self);
  449. end;
  450. procedure TgxUserInterface.setVertNavigator(val: TgxNavigator);
  451. begin
  452. if assigned(FGLVertNavigator) then FGLVertNavigator.RemoveFreeNotification(self);
  453. FGLVertNavigator:= val;
  454. if assigned(val) then val.FreeNotification(self);
  455. end;
  456. end.