GXS.SimpleNavigation.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.SimpleNavigation;
  5. (*
  6. A simple component written by request from someone at the www.glscene.ru forums.
  7. Allows to view the FPS and do the usual Zoom and MoveAroundTarget stuff
  8. that all demos usually have in themselves. All that is just by dropping
  9. this component on the form.
  10. *)
  11. interface
  12. {$I Stage.Defines.inc}
  13. uses
  14. System.Types,
  15. System.Classes,
  16. System.SysUtils,
  17. System.TypInfo,
  18. System.Math,
  19. FMX.Forms,
  20. FMX.Controls,
  21. FMX.ExtCtrls,
  22. FMX.Types,
  23. Stage.VectorGeometry,
  24. GXS.Scene,
  25. GXS.SceneViewer,
  26. Stage.Strings;
  27. type
  28. TgxSimpleNavigationOption = (
  29. snoInvertMoveAroundX, snoInvertMoveAroundY, // MoveAroundTarget.
  30. snoInvertZoom, snoInvertMouseWheel, // Zoom.
  31. snoInvertRotateX, snoInvertRotateY, // RotateTarget.
  32. snoMouseWheelHandled, // MouseWheel.
  33. snoShowFPS // Show FPS
  34. );
  35. TgxSimpleNavigationOptions = set of TgxSimpleNavigationOption;
  36. TgxSimpleNavigationAction = (snaNone, snaMoveAroundTarget, snaZoom, snaRotateTarget, snaCustom);
  37. TgxSimpleNavigationKeyCombination = class;
  38. TSimpleNavigationCustomActionEvent =
  39. procedure(Sender: TgxSimpleNavigationKeyCombination; Shift: TShiftState; X, Y: Single) of object;
  40. TgxSimpleNavigationKeyCombination = class(TCollectionItem)
  41. private
  42. FExitOnMatch: Boolean;
  43. FAction: TgxSimpleNavigationAction;
  44. FOnCustomAction: TSimpleNavigationCustomActionEvent;
  45. FShiftState: TShiftState;
  46. protected
  47. function GetDisplayName: string; override;
  48. procedure DoOnCustomAction(Shift: TShiftState; X, Y: Single); virtual;
  49. public
  50. constructor Create(Collection: TCollection); override;
  51. procedure Assign(Source: TPersistent); override;
  52. published
  53. property ShiftState: TShiftState read FShiftState write FShiftState default [];
  54. property ExitOnMatch: Boolean read FExitOnMatch write FExitOnMatch default True;
  55. property Action: TgxSimpleNavigationAction read FAction write FAction default snaNone;
  56. property OnCustomAction: TSimpleNavigationCustomActionEvent read FOnCustomAction write FOnCustomAction;
  57. end;
  58. TgxSimpleNavigationKeyCombinations = class(TOwnedCollection)
  59. private
  60. function GetItems(Index: Integer): TgxSimpleNavigationKeyCombination;
  61. procedure SetItems(Index: Integer; const Value: TgxSimpleNavigationKeyCombination);
  62. public
  63. function Add: TgxSimpleNavigationKeyCombination; overload;
  64. function Add(const AShiftState: TShiftState; const AAction: TgxSimpleNavigationAction; const AExitOnMatch: Boolean = True): TgxSimpleNavigationKeyCombination; overload;
  65. property Items[Index: Integer]: TgxSimpleNavigationKeyCombination read GetItems write SetItems; default;
  66. end;
  67. TgxSimpleNavigation = class(TComponent)
  68. private
  69. FTimer: TTimer;
  70. FForm: TCustomForm;
  71. FGLXceneViewer: TgxSceneViewer;
  72. FOldX, FOldY: Single;
  73. FFormCaption: string;
  74. FMoveAroundTargetSpeed: Single;
  75. FZoomSpeed: Single;
  76. FOptions: TgxSimpleNavigationOptions;
  77. FKeyCombinations: TgxSimpleNavigationKeyCombinations;
  78. FRotateTargetSpeed: Single;
  79. FOnMouseMove: TMouseMoveEvent;
  80. procedure ShowFPS(Sender: TObject);
  81. procedure ViewerMouseMove(Sender: TObject;
  82. Shift: TShiftState; X, Y: Single);
  83. procedure ViewerMouseWheel(Sender: TObject; Shift: TShiftState;
  84. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  85. procedure SetGLXceneViewer(const Value: TgxSceneViewer);
  86. procedure SetForm(const Value: TCustomForm);
  87. function StoreFormCaption: Boolean;
  88. function StoreMoveAroundTargetSpeed: Boolean;
  89. function StoreZoomSpeed: Boolean;
  90. procedure SetKeyCombinations(const Value: TgxSimpleNavigationKeyCombinations);
  91. function StoreRotateTargetSpeed: Boolean;
  92. procedure SetOptions(const Value: TgxSimpleNavigationOptions);
  93. protected
  94. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  95. public
  96. constructor Create(AOwner: TComponent); override;
  97. destructor Destroy; override;
  98. procedure Assign(Source: TPersistent); override;
  99. published
  100. property Form: TCustomForm read FForm write SetForm;
  101. property GLXceneViewer: TgxSceneViewer read FGLXceneViewer write SetGLXceneViewer;
  102. property ZoomSpeed: Single read FZoomSpeed write FZoomSpeed stored StoreZoomSpeed;
  103. property MoveAroundTargetSpeed: Single read FMoveAroundTargetSpeed write FMoveAroundTargetSpeed stored StoreMoveAroundTargetSpeed;
  104. property RotateTargetSpeed: Single read FRotateTargetSpeed write FRotateTargetSpeed stored StoreRotateTargetSpeed;
  105. property FormCaption: string read FFormCaption write FFormCaption stored StoreFormCaption;
  106. property Options: TgxSimpleNavigationOptions read FOptions write SetOptions default [snoMouseWheelHandled, snoShowFPS];
  107. property KeyCombinations: TgxSimpleNavigationKeyCombinations read FKeyCombinations write SetKeyCombinations;
  108. property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
  109. end;
  110. //----------------------------------------------
  111. implementation
  112. //----------------------------------------------
  113. const
  114. vFPSString = '%FPS';
  115. EPS = 0.001;
  116. { TgxSimpleNavigation }
  117. procedure TgxSimpleNavigation.Assign(Source: TPersistent);
  118. begin
  119. if Source is TgxSimpleNavigation then
  120. begin
  121. { Don't do that, because that might overide the original component's event handlers
  122. SetForm(TgxSimpleNavigation(Source).FForm);
  123. SetGLXceneViewer(TgxSimpleNavigation(Source).FGLXceneViewer);
  124. }
  125. FZoomSpeed := TgxSimpleNavigation(Source).FZoomSpeed;
  126. FMoveAroundTargetSpeed := TgxSimpleNavigation(Source).FMoveAroundTargetSpeed;
  127. FRotateTargetSpeed := TgxSimpleNavigation(Source).FRotateTargetSpeed;
  128. FFormCaption := TgxSimpleNavigation(Source).FFormCaption;
  129. FOptions := TgxSimpleNavigation(Source).FOptions;
  130. FKeyCombinations.Assign(TgxSimpleNavigation(Source).FKeyCombinations);
  131. end
  132. else
  133. inherited; // Die!
  134. end;
  135. constructor TgxSimpleNavigation.Create(AOwner: TComponent);
  136. var
  137. I: Integer;
  138. begin
  139. inherited;
  140. FKeyCombinations := TgxSimpleNavigationKeyCombinations.Create(Self, TgxSimpleNavigationKeyCombination);
  141. FKeyCombinations.Add([ssLeft, ssRight], snaZoom, True);
  142. FKeyCombinations.Add([ssLeft], snaMoveAroundTarget, True);
  143. FKeyCombinations.Add([ssRight], snaMoveAroundTarget, True);
  144. FMoveAroundTargetSpeed := 1;
  145. FRotateTargetSpeed := 1;
  146. FZoomSpeed := 1.5;
  147. FOptions := [snoMouseWheelHandled, snoShowFPS];
  148. FFormCaption := vFPSString;
  149. FTimer := TTimer.Create(nil);
  150. FTimer.OnTimer := ShowFPS;
  151. FOnMouseMove := nil;
  152. //Detect form
  153. if AOwner is TCustomForm then SetForm(TCustomForm(AOwner));
  154. //Detect SceneViewer
  155. if FForm <> nil then
  156. begin
  157. if FForm.ComponentCount <> 0 then
  158. for I := 0 to FForm.ComponentCount - 1 do
  159. if FForm.Components[I] is TgxSceneViewer then
  160. begin
  161. SetGLXceneViewer(TgxSceneViewer(FForm.Components[I]));
  162. Exit;
  163. end;
  164. end;
  165. end;
  166. destructor TgxSimpleNavigation.Destroy;
  167. begin
  168. FTimer.Free;
  169. FKeyCombinations.Free;
  170. if FForm <> nil then
  171. TForm(FForm).OnMouseWheel := nil;
  172. if FGLXceneViewer <> nil then
  173. FGLXceneViewer.OnMouseMove := nil;
  174. inherited;
  175. end;
  176. procedure TgxSimpleNavigation.ViewerMouseWheel(Sender: TObject;
  177. Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
  178. var Handled: Boolean);
  179. var
  180. Sign: SmallInt;
  181. begin
  182. if (csDesigning in ComponentState) or (WheelDelta = 0) then
  183. Exit;
  184. if snoInvertMouseWheel in FOptions then
  185. Sign := 1
  186. else
  187. Sign := -1;
  188. if FGLXceneViewer <> nil then
  189. if FGLXceneViewer.Camera <> nil then
  190. FGLXceneViewer.Camera.AdjustDistanceToTarget(
  191. Power(FZoomSpeed, Sign * WheelDelta div Abs(WheelDelta)));
  192. Handled := snoMouseWheelHandled in FOptions;
  193. end;
  194. procedure TgxSimpleNavigation.ViewerMouseMove(Sender: TObject;
  195. Shift: TShiftState; X, Y: Single);
  196. procedure DoZoom;
  197. var
  198. Sign: SmallInt;
  199. begin
  200. if snoInvertZoom in FOptions then
  201. Sign := -1
  202. else
  203. Sign := 1;
  204. FGLXceneViewer.Camera.AdjustDistanceToTarget(
  205. Power(FZoomSpeed, Sign * (Y - FOldY) / 20));
  206. end;
  207. procedure DoMoveAroundTarget;
  208. var
  209. SignX: SmallInt;
  210. SignY: SmallInt;
  211. begin
  212. if snoInvertMoveAroundX in FOptions then
  213. SignX := -1
  214. else
  215. SignX := 1;
  216. if snoInvertMoveAroundY in FOptions then
  217. SignY := -1
  218. else
  219. SignY := 1;
  220. FGLXceneViewer.Camera.MoveAroundTarget(SignX * FMoveAroundTargetSpeed * (FOldY - Y),
  221. SignY * FMoveAroundTargetSpeed * (FOldX - X));
  222. end;
  223. procedure DoRotateTarget;
  224. var
  225. SignX: SmallInt;
  226. SignY: SmallInt;
  227. begin
  228. if snoInvertRotateX in FOptions then
  229. SignX := -1
  230. else
  231. SignX := 1;
  232. if snoInvertRotateY in FOptions then
  233. SignY := -1
  234. else
  235. SignY := 1;
  236. FGLXceneViewer.Camera.RotateTarget(SignY * FRotateTargetSpeed * (FOldY - Y),
  237. SignX * FRotateTargetSpeed * (FOldX - X));
  238. end;
  239. var
  240. I: Integer;
  241. begin
  242. if csDesigning in ComponentState then
  243. exit;
  244. if FGLXceneViewer <> nil then
  245. if FGLXceneViewer.Camera <> nil then
  246. begin
  247. if FKeyCombinations.Count <> 0 then
  248. for I := 0 to FKeyCombinations.Count - 1 do
  249. if FKeyCombinations[I].FShiftState <= Shift then
  250. begin
  251. case FKeyCombinations[I].FAction of
  252. snaNone: ; //Ignore.
  253. snaMoveAroundTarget: DoMoveAroundTarget;
  254. snaZoom: DoZoom;
  255. snaRotateTarget: DoRotateTarget;
  256. snaCustom: FKeyCombinations[I].DoOnCustomAction(Shift, X, Y);
  257. else
  258. Assert(False, strErrorEx + strUnknownType);
  259. end;
  260. if FKeyCombinations[I].FExitOnMatch then
  261. Break;
  262. end;
  263. end;
  264. FOldX := X;
  265. FOldY := Y;
  266. if Assigned(FOnMouseMove) then FOnMouseMove(Self, Shift, X, Y);
  267. end;
  268. procedure TgxSimpleNavigation.Notification(AComponent: TComponent;
  269. Operation: TOperation);
  270. begin
  271. inherited;
  272. if (AComponent = FGLXceneViewer) and (Operation = opRemove) then
  273. FGLXceneViewer := nil;
  274. if (AComponent = FForm) and (Operation = opRemove) then
  275. FForm := nil;
  276. end;
  277. procedure TgxSimpleNavigation.SetKeyCombinations(
  278. const Value: TgxSimpleNavigationKeyCombinations);
  279. begin
  280. FKeyCombinations.Assign(Value);
  281. end;
  282. procedure TgxSimpleNavigation.SetForm(const Value: TCustomForm);
  283. begin
  284. if FForm <> nil then
  285. begin
  286. FForm.RemoveFreeNotification(Self);
  287. TForm(FForm).OnMouseWheel := nil;
  288. TForm(FForm).OnMouseMove := nil;
  289. if FFormCaption = vFPSString then
  290. FFormCaption := FForm.Caption + ' - ' + vFPSString;
  291. FForm.FreeNotification(Self);
  292. end;
  293. FForm := Value;
  294. end;
  295. procedure TgxSimpleNavigation.SetGLXceneViewer(
  296. const Value: TgxSceneViewer);
  297. begin
  298. if FGLXceneViewer <> nil then
  299. begin
  300. FGLXceneViewer.RemoveFreeNotification(Self);
  301. FGLXceneViewer.OnMouseMove := nil;
  302. end;
  303. FGLXceneViewer := Value;
  304. if FGLXceneViewer <> nil then
  305. begin
  306. FGLXceneViewer.OnMouseMove := ViewerMouseMove;
  307. FGLXceneViewer.FreeNotification(Self);
  308. end;
  309. end;
  310. procedure TgxSimpleNavigation.ShowFPS(Sender: TObject);
  311. var
  312. Index: Integer;
  313. Temp: string;
  314. begin
  315. if (FGLXceneViewer <> nil) and
  316. (FForm <> nil) and
  317. not(csDesigning in ComponentState) and
  318. (snoShowFPS in FOptions) then
  319. begin
  320. Temp := FFormCaption;
  321. Index := Pos(vFPSString, Temp);
  322. if Index <> 0 then
  323. begin
  324. Delete(Temp, Index, Length(vFPSString));
  325. Insert(FGLXceneViewer.FramesPerSecondText, Temp, Index);
  326. end;
  327. FForm.Caption := Temp;
  328. FGLXceneViewer.ResetPerformanceMonitor;
  329. end;
  330. end;
  331. function TgxSimpleNavigation.StoreFormCaption: Boolean;
  332. begin
  333. Result := (FFormCaption <> vFPSString);
  334. end;
  335. function TgxSimpleNavigation.StoreMoveAroundTargetSpeed: Boolean;
  336. begin
  337. Result := Abs(FMoveAroundTargetSpeed - 1) > EPS;
  338. end;
  339. function TgxSimpleNavigation.StoreZoomSpeed: Boolean;
  340. begin
  341. Result := Abs(FZoomSpeed - 1.5) > EPS;
  342. end;
  343. function TgxSimpleNavigation.StoreRotateTargetSpeed: Boolean;
  344. begin
  345. Result := Abs(FRotateTargetSpeed - 1) > EPS;
  346. end;
  347. procedure TgxSimpleNavigation.SetOptions(
  348. const Value: TgxSimpleNavigationOptions);
  349. begin
  350. if FOptions <> Value then
  351. begin
  352. FOptions := Value;
  353. end;
  354. end;
  355. { TgxSimpleNavigationKeyCombination }
  356. procedure TgxSimpleNavigationKeyCombination.Assign(Source: TPersistent);
  357. begin
  358. if Source is TgxSimpleNavigationKeyCombination then
  359. begin
  360. FExitOnMatch := TgxSimpleNavigationKeyCombination(Source).FExitOnMatch;
  361. FAction := TgxSimpleNavigationKeyCombination(Source).FAction;
  362. FOnCustomAction := TgxSimpleNavigationKeyCombination(Source).FOnCustomAction;
  363. FShiftState := TgxSimpleNavigationKeyCombination(Source).FShiftState;
  364. end
  365. else
  366. inherited; // Die!
  367. end;
  368. constructor TgxSimpleNavigationKeyCombination.Create(Collection: TCollection);
  369. begin
  370. inherited;
  371. FAction := snaNone;
  372. FExitOnMatch := True;
  373. end;
  374. procedure TgxSimpleNavigationKeyCombination.DoOnCustomAction(
  375. Shift: TShiftState; X, Y: Single);
  376. begin
  377. if Assigned(FOnCustomAction) then
  378. FOnCustomAction(Self, Shift, X, Y);
  379. end;
  380. function TgxSimpleNavigationKeyCombination.GetDisplayName: string;
  381. begin
  382. Result := GetSetProp(Self, 'ShiftState', True) + ' - ' +
  383. GetEnumName(TypeInfo(TgxSimpleNavigationAction), Integer(FAction));
  384. end;
  385. { TgxSimpleNavigationKeyCombinations }
  386. function TgxSimpleNavigationKeyCombinations.Add: TgxSimpleNavigationKeyCombination;
  387. begin
  388. Result := TgxSimpleNavigationKeyCombination(inherited Add);
  389. end;
  390. function TgxSimpleNavigationKeyCombinations.Add(
  391. const AShiftState: TShiftState; const AAction: TgxSimpleNavigationAction;
  392. const AExitOnMatch: Boolean): TgxSimpleNavigationKeyCombination;
  393. begin
  394. Result := Add;
  395. with Result do
  396. begin
  397. FShiftState := AShiftState;
  398. FAction := AAction;
  399. FExitOnMatch := AExitOnMatch;
  400. end;
  401. end;
  402. function TgxSimpleNavigationKeyCombinations.GetItems(
  403. Index: Integer): TgxSimpleNavigationKeyCombination;
  404. begin
  405. Result := TgxSimpleNavigationKeyCombination(inherited GetItem(Index));
  406. end;
  407. procedure TgxSimpleNavigationKeyCombinations.SetItems(Index: Integer;
  408. const Value: TgxSimpleNavigationKeyCombination);
  409. begin
  410. inherited SetItem(Index, Value);
  411. end;
  412. end.