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.

387 lines
13 KiB

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