GLS.SimpleNavigation.pas 15 KB

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