You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

390 lines
13 KiB

  1. unit utsOpenGLUtils;
  2. {$IFDEF FPC}
  3. {$mode delphi}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils,
  8. utsTextSuite, utsTypes;
  9. type
  10. TtsCharRenderRefOpenGL = class(TtsCharRenderRef)
  11. public
  12. TextureID: Integer; // ID of OpenGL texture where the char is stored in
  13. Size: TtsPosition;
  14. TexMat: TtsMatrix4f;
  15. VertMat: TtsMatrix4f;
  16. constructor Create;
  17. end;
  18. PtsTextureUsageItem = ^TtsTextureUsageItem;
  19. TtsTextureUsageItem = packed record
  20. children: array[0..3] of PtsTextureUsageItem;
  21. end;
  22. PtsTextureTreeItem = ^TtsTextureTreeItem;
  23. TtsTextureTreeItem = packed record
  24. value: SmallInt;
  25. children: array[0..1] of PtsTextureTreeItem;
  26. ref: TtsCharRenderRefOpenGL;
  27. end;
  28. PtsFontTexture = ^TtsFontTexture;
  29. TtsFontTexture = packed record
  30. ID: Integer; // OpenGL texture ID
  31. Usage: PtsTextureTreeItem ; // tree of used texture space
  32. Next: PtsFontTexture; // next texture in list
  33. Prev: PtsFontTexture; // previouse texture in list
  34. Size: Integer; // size of this texture
  35. Count: Integer; // number of chars stored in this texture
  36. end;
  37. TtsBaseOpenGL = class(TtsRenderer)
  38. private
  39. fTextureSize: Integer;
  40. fColor: TtsColor4f;
  41. fRenderPos: TtsPosition;
  42. fFirstTexture: PtsFontTexture;
  43. fLastTexture: PtsFontTexture;
  44. procedure FreeTextures(var aTexture: PtsFontTexture);
  45. procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  46. protected
  47. property Color: TtsColor4f read fColor;
  48. property RenderPos: TtsPosition read fRenderPos;
  49. procedure PushTexture(const aTexture: PtsFontTexture);
  50. function CreateNewTexture: PtsFontTexture; virtual;
  51. procedure FreeTexture(var aTexture: PtsFontTexture); virtual;
  52. procedure UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer); virtual;
  53. protected
  54. function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; override;
  55. procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); override;
  56. procedure BeginRender; override;
  57. procedure SetDrawPos(const X, Y: Integer); override;
  58. function GetDrawPos: TtsPosition; override;
  59. procedure MoveDrawPos(const X, Y: Integer); override;
  60. procedure SetColor(const aColor: TtsColor4f); override;
  61. public
  62. property TextureSize: Integer read fTextureSize write fTextureSize;
  63. constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
  64. destructor Destroy; override;
  65. end;
  66. EtsRendererOpenGL = class(EtsRenderer);
  67. implementation
  68. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  69. //TtsCharRenderRefOpenGL////////////////////////////////////////////////////////////////////////////////////////////////
  70. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  71. constructor TtsCharRenderRefOpenGL.Create;
  72. begin
  73. inherited Create;
  74. TextureID := 0;
  75. FillChar(TexMat, SizeOf(TexMat), #0);
  76. FillChar(VertMat, SizeOf(VertMat), #0);
  77. FillChar(Size, SizeOf(Size), #0);
  78. end;
  79. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  80. //TtsBaseOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////////
  81. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  82. procedure TtsBaseOpenGL.FreeTextures(var aTexture: PtsFontTexture);
  83. begin
  84. if not Assigned(aTexture) then
  85. exit;
  86. FreeTextures(aTexture^.Next);
  87. FreeTexture(aTexture);
  88. end;
  89. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  90. procedure TtsBaseOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  91. begin
  92. if not Assigned(aItem) then
  93. exit;
  94. FreeTextureTreeItem(aItem^.children[0]);
  95. FreeTextureTreeItem(aItem^.children[1]);
  96. Dispose(aItem);
  97. aItem := nil;
  98. end;
  99. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  100. procedure TtsBaseOpenGL.PushTexture(const aTexture: PtsFontTexture);
  101. begin
  102. aTexture^.Prev := fLastTexture;
  103. if Assigned(fLastTexture) then
  104. fLastTexture^.Next := aTexture
  105. else
  106. fFirstTexture := aTexture;
  107. fLastTexture := aTexture;
  108. end;
  109. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  110. function TtsBaseOpenGL.CreateNewTexture: PtsFontTexture;
  111. begin
  112. result := nil;
  113. end;
  114. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  115. procedure TtsBaseOpenGL.FreeTexture(var aTexture: PtsFontTexture);
  116. begin
  117. if not Assigned(aTexture) then
  118. exit;
  119. FreeTextureTreeItem(aTexture^.Usage);
  120. if Assigned(aTexture^.Prev) then
  121. aTexture^.Prev^.Next := aTexture^.Next;
  122. if Assigned(aTexture^.Next) then
  123. aTexture^.Next^.Prev := aTexture^.Prev;
  124. Dispose(aTexture);
  125. aTexture := nil;
  126. end;
  127. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  128. procedure TtsBaseOpenGL.UploadTexData(const aCharRef: TtsCharRenderRefOpenGL; const aCharImage: TtsImage; const X, Y: Integer);
  129. begin
  130. // DUMMY
  131. end;
  132. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  133. function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef;
  134. var
  135. GlyphWidth, GlyphHeight: Integer;
  136. function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
  137. var
  138. w, h: Integer;
  139. begin
  140. result := nil;
  141. w := X2 - X1;
  142. h := Y2 - Y1;
  143. if not Assigned(aItem) or
  144. Assigned(aItem^.ref) or
  145. (w < GlyphWidth) or
  146. (h < GlyphHeight) then
  147. exit;
  148. if (aItem^.value > 0) then begin
  149. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  150. if not Assigned(result) then
  151. result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
  152. end else if (aItem^.value < 0) then begin
  153. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
  154. if not Assigned(result) then
  155. result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
  156. end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
  157. X := X1;
  158. Y := Y1;
  159. result := aItem;
  160. end else begin
  161. new(aItem^.children[0]);
  162. new(aItem^.children[1]);
  163. FillChar(aItem^.children[0]^, SizeOf(aItem^.children[0]^), #0);
  164. FillChar(aItem^.children[1]^, SizeOf(aItem^.children[1]^), #0);
  165. if (w - GlyphWidth) < (h - GlyphHeight) then begin
  166. aItem^.value := Y1 + GlyphHeight;
  167. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  168. end else begin
  169. aItem^.value := -(X1 + GlyphWidth);
  170. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
  171. end;
  172. end;
  173. end;
  174. function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL;
  175. var
  176. x, y, wChar, hChar, l, t: Integer;
  177. item: PtsTextureTreeItem;
  178. begin
  179. item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
  180. if not Assigned(item) then
  181. raise EtsRendererOpenGL.Create('unable to add glyph to texture');
  182. item^.ref := TtsCharRenderRefOpenGL.Create;
  183. result := item^.ref;
  184. wChar := aChar.GlyphRect.Right - aChar.GlyphRect.Left;
  185. hChar := aChar.GlyphRect.Bottom - aChar.GlyphRect.Top;
  186. l := aChar.GlyphRect.Left + x;
  187. t := aChar.GlyphRect.Top + y;
  188. result.TextureID := aTexture^.ID;
  189. result.Size := tsPosition(aCharImage.Width, aCharImage.Height);
  190. result.TexMat := tsMatrix4f(
  191. tsVector4f(wChar / aTexture^.Size, 0.0, 0.0, 0.0),
  192. tsVector4f(0.0, hChar / aTexture^.Size, 0.0, 0.0),
  193. tsVector4f(0.0, 0.0, 1.0, 0.0),
  194. tsVector4f(l / aTexture^.Size, t / aTexture^.Size, 0.0, 1.0));
  195. result.VertMat := tsMatrix4f(
  196. tsVector4f(wChar, 0.0, 0.0, 0.0),
  197. tsVector4f(0.0, hChar, 0.0, 0.0),
  198. tsVector4f(0.0, 0.0, 1.0, 0.0),
  199. tsVector4f(aChar.GlyphOrigin.x, -aChar.GlyphOrigin.y, 0.0, 1.0));
  200. UploadTexData(result, aCharImage, x, y);
  201. end;
  202. var
  203. tex: PtsFontTexture;
  204. begin
  205. result := nil;
  206. if aCharImage.IsEmpty then
  207. exit;
  208. GlyphWidth := aCharImage.Width + 1;
  209. GlyphHeight := aCharImage.Height + 1;
  210. // try to add to existing texture
  211. tex := fFirstTexture;
  212. while Assigned(tex) and not Assigned(result) do begin
  213. result := AddToTexture(tex);
  214. tex := tex^.Next;
  215. end;
  216. // create new texture
  217. if not Assigned(result) then begin
  218. if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then
  219. raise EtsRendererOpenGL.Create('char is to large to fit into a texture: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  220. tex := CreateNewTexture;
  221. result := AddToTexture(tex);
  222. end;
  223. if not Assigned(result) then
  224. raise EtsRendererOpenGL.Create('unable to creat render reference for char: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  225. end;
  226. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  227. procedure TtsBaseOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef);
  228. var
  229. ref: TtsCharRenderRefOpenGL;
  230. tex: PtsFontTexture;
  231. function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
  232. begin
  233. result :=
  234. Assigned(aItem) and
  235. not Assigned(aItem^.children[0]) and
  236. not Assigned(aItem^.children[1]) and
  237. not Assigned(aItem^.ref);
  238. end;
  239. function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
  240. var
  241. w, h: Integer;
  242. begin
  243. result := false;
  244. w := X2 - X1;
  245. h := Y2 - Y1;
  246. if not Assigned(aItem) or
  247. (w < ref.Size.x) or
  248. (h < ref.Size.y) then
  249. exit;
  250. result := (aItem^.ref = ref);
  251. if not result then begin
  252. if (aItem^.value > 0) then begin
  253. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
  254. result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
  255. end else if (aItem^.value < 0) then begin
  256. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  257. result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  258. end;
  259. end else
  260. aItem^.ref := nil;
  261. if result and
  262. IsEmtpy(aItem^.children[0]) and
  263. IsEmtpy(aItem^.children[1]) then
  264. begin
  265. FreeTextureTreeItem(aItem^.children[0]);
  266. FreeTextureTreeItem(aItem^.children[1]);
  267. FillChar(aItem^, SizeOf(aItem^), #0);
  268. end;
  269. end;
  270. begin
  271. try
  272. if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then
  273. exit;
  274. ref := (aCharRef as TtsCharRenderRefOpenGL);
  275. tex := fFirstTexture;
  276. while Assigned(tex) do begin
  277. if (tex^.ID = ref.TextureID) then begin
  278. if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
  279. raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
  280. if IsEmtpy(tex^.Usage) then begin
  281. if (tex = fFirstTexture) then
  282. fFirstTexture := nil;
  283. FreeTexture(tex);
  284. end;
  285. tex := nil;
  286. end else
  287. tex := tex^.Next;
  288. end;
  289. finally
  290. if Assigned(aCharRef) then
  291. aCharRef.Free;
  292. end;
  293. end;
  294. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  295. procedure TtsBaseOpenGL.BeginRender;
  296. begin
  297. inherited BeginRender;
  298. fRenderPos.x := 0;
  299. fRenderPos.y := 0;
  300. end;
  301. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  302. procedure TtsBaseOpenGL.SetDrawPos(const X, Y: Integer);
  303. begin
  304. fRenderPos.x := X;
  305. fRenderPos.y := Y;
  306. end;
  307. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  308. function TtsBaseOpenGL.GetDrawPos: TtsPosition;
  309. begin
  310. result := fRenderPos;
  311. end;
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  313. procedure TtsBaseOpenGL.MoveDrawPos(const X, Y: Integer);
  314. begin
  315. fRenderPos.x := fRenderPos.x + X;
  316. fRenderPos.y := fRenderPos.y + Y;
  317. end;
  318. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  319. procedure TtsBaseOpenGL.SetColor(const aColor: TtsColor4f);
  320. begin
  321. fColor := aColor;
  322. end;
  323. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  324. constructor TtsBaseOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  325. begin
  326. inherited Create(aContext, aFormat);
  327. fFirstTexture := nil;
  328. fLastTexture := nil;
  329. fTextureSize := 2048;
  330. fColor := tsColor4f(1, 1, 1, 1);
  331. fRenderPos := tsPosition(0, 0);
  332. end;
  333. destructor TtsBaseOpenGL.Destroy;
  334. begin
  335. FreeTextures(fFirstTexture);
  336. inherited Destroy;
  337. end;
  338. end.