GLS.SceneForm.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.SceneForm;
  5. (* GLScene form loader *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.Windows,
  10. Winapi.Messages,
  11. System.Classes,
  12. VCL.Controls,
  13. VCL.Forms,
  14. GLS.Scene,
  15. GLS.Context,
  16. GLS.Screen,
  17. GLS.SceneViewer;
  18. const
  19. lcl_major = 0;
  20. lcl_minor = 0;
  21. lcl_release = 0;
  22. type
  23. TGLSceneForm = class;
  24. (* Defines how GLSceneForm will handle fullscreen request
  25. fcWindowMaximize: Use current resolution (just maximize form and hide OS bars)
  26. fcNearestResolution: Change to nearest valid resolution from current window size
  27. fcManualResolution: Use FFullScreenVideoMode settings *)
  28. TGLFullScreenResolution = (
  29. fcUseCurrent,
  30. fcNearestResolution,
  31. fcManualResolution);
  32. // Screen mode settings
  33. TGLFullScreenVideoMode = class(TPersistent)
  34. private
  35. FOwner: TGLSceneForm;
  36. FEnabled: Boolean;
  37. FAltTabSupportEnable: Boolean;
  38. FWidth: Integer;
  39. FHeight: Integer;
  40. FColorDepth: Integer;
  41. FFrequency: Integer;
  42. FResolutionMode: TGLFullScreenResolution;
  43. procedure SetEnabled(aValue: Boolean);
  44. procedure SetAltTabSupportEnable(aValue: Boolean);
  45. public
  46. constructor Create(AOwner: TGLSceneForm);
  47. published
  48. property Enabled: Boolean read FEnabled write SetEnabled default False;
  49. property AltTabSupportEnable: Boolean read FAltTabSupportEnable
  50. write SetAltTabSupportEnable default False;
  51. property ResolutionMode: TGLFullScreenResolution read FResolutionMode
  52. write FResolutionMode default fcUseCurrent;
  53. property Width: Integer read FWidth write FWidth;
  54. property Height: Integer read FHeight write FHeight;
  55. property ColorDepth: Integer read FColorDepth write FColorDepth;
  56. property Frequency: Integer read FFrequency write FFrequency;
  57. end;
  58. TGLSceneForm = class(TForm)
  59. private
  60. FBuffer: TGLSceneBuffer;
  61. FVSync: TGLVSyncMode;
  62. FOwnDC: HDC;
  63. FFullScreenVideoMode: TGLFullScreenVideoMode;
  64. procedure SetBeforeRender(const val: TNotifyEvent);
  65. function GetBeforeRender: TNotifyEvent;
  66. procedure SetPostRender(const val: TNotifyEvent);
  67. function GetPostRender: TNotifyEvent;
  68. procedure SetAfterRender(const val: TNotifyEvent);
  69. function GetAfterRender: TNotifyEvent;
  70. procedure SetCamera(const val: TGLCamera);
  71. function GetCamera: TGLCamera;
  72. procedure SetBuffer(const val: TGLSceneBuffer);
  73. function GetFieldOfView: single;
  74. procedure SetFieldOfView(const Value: single);
  75. function GetIsRenderingContextAvailable: Boolean;
  76. procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
  77. procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  78. procedure WMSize(var Message: TWMSize); message WM_SIZE;
  79. procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY;
  80. procedure LastFocus(var Mess: TMessage); message WM_ACTIVATE;
  81. procedure SetFullScreenVideoMode(AValue: TGLFullScreenVideoMode);
  82. procedure StartupFS;
  83. procedure ShutdownFS;
  84. protected
  85. procedure Notification(AComponent: TComponent; Operation: TOperation);
  86. override;
  87. procedure CreateWnd; override;
  88. procedure Loaded; override;
  89. procedure DoBeforeRender(Sender: TObject); virtual;
  90. procedure DoBufferChange(Sender: TObject); virtual;
  91. procedure DoBufferStructuralChange(Sender: TObject); virtual;
  92. procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  93. public
  94. constructor Create(AOwner: TComponent); override;
  95. destructor Destroy; override;
  96. procedure DestroyWnd; override;
  97. property IsRenderingContextAvailable: Boolean read
  98. GetIsRenderingContextAvailable;
  99. property RenderDC: HDC read FOwnDC;
  100. published
  101. // Camera from which the scene is rendered.
  102. property Camera: TGLCamera read GetCamera write SetCamera;
  103. (* Specifies if the refresh should be synchronized with the VSync signal.
  104. If the underlying OpenGL ICD does not support the WGL_EXT_swap_control
  105. extension, this property is ignored. *)
  106. property VSync: TGLVSyncMode read FVSync write FVSync default vsmNoSync;
  107. (* Triggered before the scene's objects get rendered.
  108. You may use this event to execute your own OpenGL rendering. *)
  109. property BeforeRender: TNotifyEvent read GetBeforeRender write
  110. SetBeforeRender;
  111. (* Triggered just after all the scene's objects have been rendered.
  112. The OpenGL context is still active in this event, and you may use it
  113. to execute your own OpenGL rendering. *)
  114. property PostRender: TNotifyEvent read GetPostRender write SetPostRender;
  115. (* Called after rendering.
  116. You cannot issue OpenGL calls in this event, if you want to do your own
  117. OpenGL stuff, use the PostRender event. *)
  118. property AfterRender: TNotifyEvent read GetAfterRender write SetAfterRender;
  119. // Access to buffer properties.
  120. property Buffer: TGLSceneBuffer read FBuffer write SetBuffer;
  121. (* Returns or sets the field of view for the viewer, in degrees.
  122. This value depends on the camera and the width and height of the scene.
  123. The value isn't persisted, if the width/height or camera.focallength is
  124. changed, FieldOfView is changed also. *)
  125. property FieldOfView: single read GetFieldOfView write SetFieldOfView;
  126. property FullScreenVideoMode: TGLFullScreenVideoMode read
  127. FFullScreenVideoMode
  128. write SetFullScreenVideoMode;
  129. end;
  130. //-----------------------------------------------------------------
  131. implementation
  132. //-----------------------------------------------------------------
  133. constructor TGLSceneForm.Create(AOwner: TComponent);
  134. begin
  135. FBuffer := TGLSceneBuffer.Create(Self);
  136. FVSync := vsmNoSync;
  137. FBuffer.ViewerBeforeRender := DoBeforeRender;
  138. FBuffer.OnChange := DoBufferChange;
  139. FBuffer.OnStructuralChange := DoBufferStructuralChange;
  140. FFullScreenVideoMode := TGLFullScreenVideoMode.Create(Self);
  141. inherited Create(AOwner);
  142. end;
  143. destructor TGLSceneForm.Destroy;
  144. begin
  145. FBuffer.Free;
  146. FBuffer := nil;
  147. FFullScreenVideoMode.Destroy;
  148. inherited Destroy;
  149. end;
  150. procedure TGLSceneForm.Notification(AComponent: TComponent; Operation:
  151. TOperation);
  152. begin
  153. if (Operation = opRemove) and (FBuffer <> nil) then
  154. begin
  155. if (AComponent = FBuffer.Camera) then
  156. FBuffer.Camera := nil;
  157. end;
  158. inherited;
  159. end;
  160. procedure TGLSceneForm.CreateWnd;
  161. begin
  162. inherited CreateWnd;
  163. // initialize and activate the OpenGL rendering context
  164. // need to do this only once per window creation as we have a private DC
  165. FBuffer.Resize(0, 0, Self.Width, Self.Height);
  166. FOwnDC := GetDC(Handle);
  167. FBuffer.CreateRC(FOwnDC, false);
  168. end;
  169. procedure TGLSceneForm.DestroyWnd;
  170. begin
  171. if Assigned(FBuffer) then
  172. begin
  173. FBuffer.DestroyRC;
  174. if FOwnDC <> 0 then
  175. begin
  176. ReleaseDC(Handle, FOwnDC);
  177. FOwnDC := 0;
  178. end;
  179. end;
  180. inherited;
  181. end;
  182. procedure TGLSceneForm.Loaded;
  183. begin
  184. inherited Loaded;
  185. // initiate window creation
  186. HandleNeeded;
  187. if not (csDesigning in ComponentState) then
  188. begin
  189. if FFullScreenVideoMode.FEnabled then
  190. StartupFS;
  191. end;
  192. end;
  193. procedure TGLSceneForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
  194. begin
  195. if GetIsRenderingContextAvailable then
  196. Message.Result := 1
  197. else
  198. inherited;
  199. end;
  200. procedure TGLSceneForm.WMSize(var Message: TWMSize);
  201. begin
  202. inherited;
  203. if Assigned(FBuffer) then
  204. FBuffer.Resize(0, 0, Message.Width, Message.Height);
  205. end;
  206. procedure TGLSceneForm.WMPaint(var Message: TWMPaint);
  207. var
  208. PS: TPaintStruct;
  209. begin
  210. BeginPaint(Handle, PS);
  211. try
  212. if GetIsRenderingContextAvailable and (Width > 0) and (Height > 0) then
  213. FBuffer.Render;
  214. finally
  215. EndPaint(Handle, PS);
  216. Message.Result := 0;
  217. end;
  218. end;
  219. procedure TGLSceneForm.WMDestroy(var Message: TWMDestroy);
  220. begin
  221. if Assigned(FBuffer) then
  222. begin
  223. FBuffer.DestroyRC;
  224. if FOwnDC <> 0 then
  225. begin
  226. ReleaseDC(Handle, FOwnDC);
  227. FOwnDC := 0;
  228. end;
  229. end;
  230. inherited;
  231. end;
  232. procedure TGLSceneForm.LastFocus(var Mess: TMessage);
  233. begin
  234. if not (csDesigning in ComponentState)
  235. and FFullScreenVideoMode.FEnabled
  236. and FFullScreenVideoMode.FAltTabSupportEnable then
  237. begin
  238. if Mess.wParam = WA_INACTIVE then
  239. begin
  240. ShutdownFS;
  241. end
  242. else
  243. begin
  244. StartupFS;
  245. end;
  246. end;
  247. inherited;
  248. end;
  249. procedure TGLFullScreenVideoMode.SetEnabled(aValue: Boolean);
  250. begin
  251. if FEnabled <> aValue then
  252. begin
  253. FEnabled := aValue;
  254. if not ((csDesigning in FOwner.ComponentState)
  255. or (csLoading in FOwner.ComponentState)) then
  256. begin
  257. if FEnabled then
  258. FOwner.StartupFS
  259. else
  260. FOwner.ShutdownFS;
  261. end;
  262. end;
  263. end;
  264. constructor TGLFullScreenVideoMode.Create(AOwner: TGLSceneForm);
  265. begin
  266. inherited Create;
  267. FOwner := AOwner;
  268. FEnabled := False;
  269. FAltTabSupportEnable := False;
  270. ReadVideoModes;
  271. {$IFDEF MSWINDOWS}
  272. FWidth := vVideoModes[0].Width;
  273. FHeight := vVideoModes[0].Height;
  274. FColorDepth := vVideoModes[0].ColorDepth;
  275. FFrequency := vVideoModes[0].MaxFrequency;
  276. {$ENDIF}
  277. if FFrequency = 0 then
  278. FFrequency := 50;
  279. FResolutionMode := fcUseCurrent;
  280. end;
  281. procedure TGLFullScreenVideoMode.SetAltTabSupportEnable(aValue: Boolean);
  282. begin
  283. if FAltTabSupportEnable <> aValue then
  284. FAltTabSupportEnable := aValue;
  285. end;
  286. procedure TGLSceneForm.StartupFS;
  287. begin
  288. case FFullScreenVideoMode.FResolutionMode of
  289. fcNearestResolution:
  290. begin
  291. SetFullscreenMode(GetIndexFromResolution(ClientWidth, ClientHeight,
  292. {$IFDEF MSWINDOWS}
  293. vVideoModes[0].ColorDepth));
  294. {$ELSE}
  295. 32));
  296. {$ENDIF}
  297. end;
  298. fcManualResolution:
  299. begin
  300. SetFullscreenMode(GetIndexFromResolution(FFullScreenVideoMode.Width , FFullScreenVideoMode.Height, FFullScreenVideoMode.ColorDepth), FFullScreenVideoMode.Frequency);
  301. end;
  302. end;
  303. Left := 0;
  304. Top := 0;
  305. BorderStyle := bsNone;
  306. FormStyle := fsStayOnTop;
  307. BringToFront;
  308. WindowState := wsMaximized;
  309. Application.MainFormOnTaskBar := True;
  310. end;
  311. procedure TGLSceneForm.ShutdownFS;
  312. begin
  313. RestoreDefaultMode;
  314. SendToBack;
  315. WindowState := wsNormal;
  316. BorderStyle := bsSingle;
  317. FormStyle := fsNormal;
  318. Left := (Screen.Width div 2) - (Width div 2);
  319. Top := (Screen.Height div 2) - (Height div 2);
  320. end;
  321. procedure TGLSceneForm.DoBeforeRender(Sender: TObject);
  322. begin
  323. SetupVSync(VSync);
  324. end;
  325. procedure TGLSceneForm.DoBufferChange(Sender: TObject);
  326. begin
  327. if (not Buffer.Rendering) and (not Buffer.Freezed) then
  328. Invalidate;
  329. end;
  330. procedure TGLSceneForm.DoBufferStructuralChange(Sender: TObject);
  331. begin
  332. RecreateWnd;
  333. end;
  334. procedure TGLSceneForm.MouseMove(Shift: TShiftState; X, Y: Integer);
  335. begin
  336. inherited;
  337. if csDesignInteractive in ControlStyle then
  338. FBuffer.NotifyMouseMove(Shift, X, Y);
  339. end;
  340. procedure TGLSceneForm.SetBeforeRender(const val: TNotifyEvent);
  341. begin
  342. FBuffer.BeforeRender := val;
  343. end;
  344. function TGLSceneForm.GetBeforeRender: TNotifyEvent;
  345. begin
  346. Result := FBuffer.BeforeRender;
  347. end;
  348. procedure TGLSceneForm.SetPostRender(const val: TNotifyEvent);
  349. begin
  350. FBuffer.PostRender := val;
  351. end;
  352. function TGLSceneForm.GetPostRender: TNotifyEvent;
  353. begin
  354. Result := FBuffer.PostRender;
  355. end;
  356. procedure TGLSceneForm.SetAfterRender(const val: TNotifyEvent);
  357. begin
  358. FBuffer.AfterRender := val;
  359. end;
  360. function TGLSceneForm.GetAfterRender: TNotifyEvent;
  361. begin
  362. Result := FBuffer.AfterRender;
  363. end;
  364. procedure TGLSceneForm.SetCamera(const val: TGLCamera);
  365. begin
  366. FBuffer.Camera := val;
  367. end;
  368. function TGLSceneForm.GetCamera: TGLCamera;
  369. begin
  370. Result := FBuffer.Camera;
  371. end;
  372. procedure TGLSceneForm.SetBuffer(const val: TGLSceneBuffer);
  373. begin
  374. FBuffer.Assign(val);
  375. end;
  376. function TGLSceneForm.GetFieldOfView: single;
  377. begin
  378. if not Assigned(Camera) then
  379. Result := 0
  380. else if Width < Height then
  381. Result := Camera.GetFieldOfView(Width)
  382. else
  383. Result := Camera.GetFieldOfView(Height);
  384. end;
  385. procedure TGLSceneForm.SetFieldOfView(const Value: single);
  386. begin
  387. if Assigned(Camera) then
  388. begin
  389. if Width < Height then
  390. Camera.SetFieldOfView(Value, Width)
  391. else
  392. Camera.SetFieldOfView(Value, Height);
  393. end;
  394. end;
  395. procedure TGLSceneForm.SetFullScreenVideoMode(AValue: TGLFullScreenVideoMode);
  396. begin
  397. end;
  398. function TGLSceneForm.GetIsRenderingContextAvailable: Boolean;
  399. begin
  400. Result := FBuffer.RCInstantiated and FBuffer.RenderingContext.IsValid;
  401. end;
  402. // ------------------------------------------------------------------
  403. initialization
  404. // ------------------------------------------------------------------
  405. RegisterClass(TGLSceneForm);
  406. end.