25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
14 KiB

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