GXS.HUDObjects.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.HUDObjects;
  5. (* Objects that get rendered in 2D coordinates *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. System.Classes,
  12. GXS.XOpenGL,
  13. Stage.VectorTypes,
  14. GXS.PersistentClasses,
  15. GXS.Scene,
  16. GXS.Coordinates,
  17. Stage.VectorGeometry,
  18. GXS.Objects,
  19. GXS.BitmapFont,
  20. GXS.ImageUtils,
  21. GXS.Color,
  22. GXS.RenderContextInfo,
  23. GXS.Context,
  24. GXS.State;
  25. type
  26. (* A rectangular area, NOT perspective projected.
  27. (x, y) coordinates map directly to the viewport (in pixels) and refer
  28. the center of the area.
  29. The coordinate system is that of an equivalent TCanvas, ie. top-left
  30. point is the origin (0, 0).
  31. The z component is ignored and Z-Buffer is disabled when rendering.
  32. Using TgxHUDSprite in 2D only scenes :
  33. The most convenient way to use a TgxHUDSprite as a simple 2D sprite with
  34. blending capabilities (transparency or additive), is to set the texture
  35. mode to tmModulate, in FrontProperties, to use the Emission color to
  36. control coloring/intensity, and finally use the Diffuse color's alpha
  37. to control transparency (while setting the other RGB components to 0).
  38. You can also control aplha-blending by defining a <1 value in the sprite's
  39. AlphaChannel field. This provides you with hardware accelerated,
  40. alpha-blended blitting.
  41. Note : since TgxHUDSprite works in absolute coordinates, TgxProxyObject
  42. can't be used to duplicate an hud sprite. *)
  43. TgxHUDSprite = class(TgxSprite)
  44. private
  45. FXTiles, FYTiles: Integer;
  46. function StoreWidth: Boolean;
  47. function StoreHeight: Boolean;
  48. protected
  49. procedure SetXTiles(const val: Integer);
  50. procedure SetYTiles(const val: Integer);
  51. public
  52. constructor Create(AOwner: TComponent); override;
  53. procedure DoRender(var rci: TgxRenderContextInfo;
  54. renderSelf, renderChildren: Boolean); override;
  55. published
  56. property XTiles: Integer read FXTiles write SetXTiles default 1;
  57. property YTiles: Integer read FYTiles write SetYTiles default 1;
  58. // Redeclare them with new default values.
  59. property Width stored StoreWidth;
  60. property Height stored StoreHeight;
  61. end;
  62. (* A 2D text displayed and positionned in 2D coordinates.
  63. The HUDText uses a character font defined and stored by a TgxBitmapFont
  64. component. The text can be scaled and rotated (2D), the layout and
  65. alignment can also be controled. *)
  66. TgxHUDText = class(TgxImmaterialSceneObject)
  67. private
  68. FBitmapFont: TgxCustomBitmapFont;
  69. FText: UnicodeString;
  70. FRotation: Single;
  71. FAlignment: TAlignment;
  72. FLayout: TgxTextLayout;
  73. FModulateColor: TgxColor;
  74. protected
  75. procedure SetBitmapFont(const val: TgxCustomBitmapFont);
  76. procedure SetText(const val: UnicodeString);
  77. procedure SetRotation(const val: Single);
  78. procedure SetAlignment(const val: TAlignment);
  79. procedure SetLayout(const val: TgxTextLayout);
  80. procedure SetModulateColor(const val: TgxColor);
  81. procedure Notification(AComponent: TComponent;
  82. Operation: TOperation); override;
  83. procedure RenderTextAtPosition(const X, Y, Z: Single;
  84. var rci: TgxRenderContextInfo);
  85. public
  86. constructor Create(AOwner: TComponent); override;
  87. destructor Destroy; override;
  88. procedure DoRender(var rci: TgxRenderContextInfo;
  89. renderSelf, renderChildren: Boolean); override;
  90. published
  91. (* Refers the bitmap font to use.
  92. The referred bitmap font component stores and allows access to
  93. individual character bitmaps. *)
  94. property BitmapFont: TgxCustomBitmapFont read FBitmapFont
  95. write SetBitmapFont;
  96. (* Text to render.
  97. Be aware that only the characters available in the bitmap font will
  98. be rendered. CR LF sequences are allowed. *)
  99. property Text: UnicodeString read FText write SetText;
  100. // Rotation angle in degrees (2d).
  101. property Rotation: Single read FRotation write SetRotation;
  102. (* Controls the text alignment (horizontal).
  103. Possible values : taLeftJustify, taRightJustify, taCenter *)
  104. property Alignment: TAlignment read FAlignment write SetAlignment
  105. default taLeftJustify;
  106. (* Controls the text layout (vertical).
  107. Possible values : tlTop, tlCenter, tlBottom *)
  108. property Layout: TgxTextLayout read FLayout write SetLayout default tlTop;
  109. { Color modulation, can be used for fade in/out too. }
  110. property ModulateColor: TgxColor read FModulateColor write SetModulateColor;
  111. end;
  112. (* Position (X, Y and X) is in absolute coordinates. This component converts
  113. them to screen coordinates and renderes text there. *)
  114. TgxAbsoluteHUDText = class(TgxHUDText)
  115. public
  116. procedure DoRender(var rci: TgxRenderContextInfo;
  117. renderSelf, renderChildren: Boolean); override;
  118. end;
  119. (* Position (X and Y) is expected in a [0..1] range (from Screen size)
  120. This component converts this position to the actual screen position and
  121. renders the text there. This way a HUD text always appears to be in the
  122. the same place, regardless of the currect screen resolution.
  123. Note: this still does not solve the font scaling problem. *)
  124. TgxResolutionIndependantHUDText = class(TgxHUDText)
  125. public
  126. procedure DoRender(var rci: TgxRenderContextInfo;
  127. renderSelf, renderChildren: Boolean); override;
  128. constructor Create(AOwner: TComponent); override;
  129. end;
  130. //=====================================================================
  131. implementation
  132. //=====================================================================
  133. // ------------------
  134. // ------------------ TgxHUDSprite ------------------
  135. // ------------------
  136. constructor TgxHUDSprite.Create(AOwner: TComponent);
  137. begin
  138. inherited;
  139. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  140. Width := 16;
  141. Height := 16;
  142. FXTiles := 1;
  143. FYTiles := 1;
  144. end;
  145. procedure TgxHUDSprite.SetXTiles(const val: Integer);
  146. begin
  147. if val <> FXTiles then
  148. begin
  149. FXTiles := val;
  150. StructureChanged;
  151. end;
  152. end;
  153. procedure TgxHUDSprite.SetYTiles(const val: Integer);
  154. begin
  155. if val <> FYTiles then
  156. begin
  157. FYTiles := val;
  158. StructureChanged;
  159. end;
  160. end;
  161. procedure TgxHUDSprite.DoRender(var rci: TgxRenderContextInfo;
  162. renderSelf, renderChildren: Boolean);
  163. var
  164. vx, vy, vx1, vy1, f: Single;
  165. u0, v0, u1, v1: Integer;
  166. begin
  167. if rci.ignoreMaterials then
  168. Exit;
  169. Material.Apply(rci);
  170. repeat
  171. if AlphaChannel <> 1 then
  172. begin
  173. if stLighting in rci.gxStates.States then
  174. rci.gxStates.SetMaterialAlphaChannel(GL_FRONT, AlphaChannel)
  175. else
  176. with Material.GetActualPrimaryMaterial.FrontProperties.Diffuse do
  177. glColor4f(Red, Green, Blue, AlphaChannel);
  178. end;
  179. // Prepare matrices
  180. glMatrixMode(GL_MODELVIEW);
  181. glPushMatrix;
  182. glLoadMatrixf(@TgxSceneBuffer(rci.buffer).BaseProjectionMatrix);
  183. if rci.renderDPI = 96 then
  184. f := 1
  185. else
  186. f := rci.renderDPI / 96;
  187. glScalef(2 / rci.viewPortSize.cx, 2 / rci.viewPortSize.cy, 1);
  188. glTranslatef(f * Position.X - rci.viewPortSize.cx * 0.5,
  189. rci.viewPortSize.cy * 0.5 - f * Position.Y, Position.Z);
  190. if Rotation <> 0 then
  191. glRotatef(Rotation, 0, 0, 1);
  192. glMatrixMode(GL_PROJECTION);
  193. glPushMatrix;
  194. glLoadIdentity;
  195. rci.gxStates.Disable(stDepthTest);
  196. rci.gxStates.DepthWriteMask := False;
  197. // precalc coordinates
  198. vx := -Width * 0.5 * f;
  199. vx1 := vx + Width * f;
  200. vy := +Height * 0.5 * f;
  201. vy1 := vy - Height * f;
  202. // Texture coordinates
  203. if MirrorU then
  204. begin
  205. u0 := FXTiles;
  206. u1 := 0;
  207. end
  208. else
  209. begin
  210. u0 := 0;
  211. u1 := FXTiles;
  212. end;
  213. if MirrorV then
  214. begin
  215. v0 := FYTiles;
  216. v1 := 0;
  217. end
  218. else
  219. begin
  220. v0 := 0;
  221. v1 := FYTiles;
  222. end;
  223. // issue quad
  224. glBegin(GL_QUADS);
  225. glNormal3fv(@YVector);
  226. glTexCoord2f(u0, v0);
  227. glVertex2f(vx, vy1);
  228. glTexCoord2f(u1, v0);
  229. glVertex2f(vx1, vy1);
  230. glTexCoord2f(u1, v1);
  231. glVertex2f(vx1, vy);
  232. glTexCoord2f(u0, v1);
  233. glVertex2f(vx, vy);
  234. glEnd;
  235. // restore state
  236. glPopMatrix;
  237. glMatrixMode(GL_MODELVIEW);
  238. glPopMatrix;
  239. until not Material.UnApply(rci);
  240. if Count > 0 then
  241. Self.renderChildren(0, Count - 1, rci);
  242. end;
  243. function TgxHUDSprite.StoreHeight: Boolean;
  244. begin
  245. Result := Abs(Height - 16) > 0.001;
  246. end;
  247. function TgxHUDSprite.StoreWidth: Boolean;
  248. begin
  249. Result := Abs(Height - 16) > 0.001;
  250. end;
  251. // ------------------
  252. // ------------------ TgxHUDText ------------------
  253. // ------------------
  254. constructor TgxHUDText.Create(AOwner: TComponent);
  255. begin
  256. inherited;
  257. ObjectStyle := ObjectStyle + [osDirectDraw, osNoVisibilityCulling];
  258. FModulateColor := TgxColor.CreateInitialized(Self, clrWhite);
  259. end;
  260. destructor TgxHUDText.Destroy;
  261. begin
  262. FModulateColor.Free;
  263. BitmapFont := nil;
  264. inherited;
  265. end;
  266. procedure TgxHUDText.Notification(AComponent: TComponent;
  267. Operation: TOperation);
  268. begin
  269. if (Operation = opRemove) and (AComponent = FBitmapFont) then
  270. BitmapFont := nil;
  271. inherited;
  272. end;
  273. procedure TgxHUDText.SetBitmapFont(const val: TgxCustomBitmapFont);
  274. begin
  275. if val <> FBitmapFont then
  276. begin
  277. if Assigned(FBitmapFont) then
  278. FBitmapFont.UnRegisterUser(Self);
  279. FBitmapFont := val;
  280. if Assigned(FBitmapFont) then
  281. begin
  282. FBitmapFont.RegisterUser(Self);
  283. FBitmapFont.FreeNotification(Self);
  284. end;
  285. StructureChanged;
  286. end;
  287. end;
  288. procedure TgxHUDText.SetText(const val: UnicodeString);
  289. begin
  290. FText := val;
  291. StructureChanged;
  292. end;
  293. procedure TgxHUDText.SetRotation(const val: Single);
  294. begin
  295. FRotation := val;
  296. StructureChanged;
  297. end;
  298. procedure TgxHUDText.SetAlignment(const val: TAlignment);
  299. begin
  300. FAlignment := val;
  301. StructureChanged;
  302. end;
  303. procedure TgxHUDText.SetLayout(const val: TgxTextLayout);
  304. begin
  305. FLayout := val;
  306. StructureChanged;
  307. end;
  308. procedure TgxHUDText.SetModulateColor(const val: TgxColor);
  309. begin
  310. FModulateColor.Assign(val);
  311. end;
  312. procedure TgxHUDText.RenderTextAtPosition(const X, Y, Z: Single;
  313. var rci: TgxRenderContextInfo);
  314. var
  315. f: Single;
  316. begin
  317. if Assigned(FBitmapFont) and (Text <> '') then
  318. begin
  319. rci.gxStates.PolygonMode := pmFill;
  320. // Prepare matrices
  321. glMatrixMode(GL_MODELVIEW);
  322. glPushMatrix;
  323. glLoadMatrixf(@TgxSceneBuffer(rci.buffer).BaseProjectionMatrix);
  324. f := rci.renderDPI / 96;
  325. glScalef(2 / rci.viewPortSize.cx, 2 / rci.viewPortSize.cy, 1);
  326. glTranslatef(X * f - rci.viewPortSize.cx / 2, rci.viewPortSize.cy / 2 -
  327. Y * f, Z);
  328. if FRotation <> 0 then
  329. glRotatef(FRotation, 0, 0, 1);
  330. glScalef(Scale.DirectX * f, Scale.DirectY * f, 1);
  331. glMatrixMode(GL_PROJECTION);
  332. glPushMatrix;
  333. glLoadIdentity;
  334. rci.gxStates.Disable(stDepthTest);
  335. // render text
  336. FBitmapFont.RenderString(rci, Text, FAlignment, FLayout,
  337. FModulateColor.Color);
  338. // restore state
  339. rci.gxStates.Enable(stDepthTest);
  340. glPopMatrix;
  341. glMatrixMode(GL_MODELVIEW);
  342. glPopMatrix;
  343. end;
  344. end;
  345. procedure TgxHUDText.DoRender(var rci: TgxRenderContextInfo;
  346. renderSelf, renderChildren: Boolean);
  347. begin
  348. RenderTextAtPosition(Position.X, Position.Y, Position.Z, rci);
  349. if Count > 0 then
  350. Self.renderChildren(0, Count - 1, rci);
  351. end;
  352. // ------------------
  353. // ------------------ TgxResolutionIndependantHUDText ------------------
  354. // ------------------
  355. constructor TgxResolutionIndependantHUDText.Create(AOwner: TComponent);
  356. begin
  357. inherited;
  358. Position.X := 0.5;
  359. Position.Y := 0.5;
  360. end;
  361. procedure TgxResolutionIndependantHUDText.DoRender(var rci: TgxRenderContextInfo;
  362. renderSelf, renderChildren: Boolean);
  363. begin
  364. RenderTextAtPosition(Position.X * rci.viewPortSize.cx,
  365. Position.Y * rci.viewPortSize.cy, Position.Z, rci);
  366. if Count > 0 then
  367. Self.renderChildren(0, Count - 1, rci);
  368. end;
  369. // ------------------
  370. // ------------------ TgxAbsoluteHUDText ------------------
  371. // ------------------
  372. procedure TgxAbsoluteHUDText.DoRender(var rci: TgxRenderContextInfo;
  373. renderSelf, renderChildren: Boolean);
  374. var
  375. Temp: TAffineVector;
  376. begin
  377. Temp := TgxSceneBuffer(rci.buffer).WorldToScreen(Self.AbsoluteAffinePosition);
  378. Temp.Y := rci.viewPortSize.cy - Temp.Y;
  379. RenderTextAtPosition(Temp.X, Temp.Y, Temp.Z, rci);
  380. if Count > 0 then
  381. Self.renderChildren(0, Count - 1, rci);
  382. end;
  383. //=======================================================================
  384. initialization
  385. //=======================================================================
  386. RegisterClasses([TgxHUDText, TgxHUDSprite, TgxResolutionIndependantHUDText,
  387. TgxAbsoluteHUDText]);
  388. end.