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.

523 lines
17 KiB

  1. unit utsRendererOpenGL;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, syncobjs, dglOpenGL,
  6. utsTextSuite, utsTypes;
  7. type
  8. TtsQuadPosF = array[0..3] of TtsPositionF;
  9. TtsCharRenderRefOpenGL = class(TtsCharRenderRef)
  10. private
  11. TextureID: GLint; // ID of OpenGL texture where the char is stored in
  12. TexCoordSize: TtsPositionF; // size of the char in texture coords (0.0 - 1.0)
  13. TexCoordPos: TtsPositionF; // position of the char in texture coords (0.0 - 1.0)
  14. VertexSize: TtsPositionF; // size of the char in world coords
  15. VertexPos: TtsPositionF; // size of the char in world coords
  16. public
  17. constructor Create;
  18. end;
  19. PtsTextureUsageItem = ^TtsTextureUsageItem;
  20. TtsTextureUsageItem = packed record
  21. children: array[0..3] of PtsTextureUsageItem;
  22. end;
  23. PtsTextureTreeItem = ^TtsTextureTreeItem;
  24. TtsTextureTreeItem = packed record
  25. value: SmallInt;
  26. children: array[0..1] of PtsTextureTreeItem;
  27. ref: TtsCharRenderRefOpenGL;
  28. end;
  29. PtsFontTexture = ^TtsFontTexture;
  30. TtsFontTexture = packed record
  31. ID: GLint; // OpenGL texture ID
  32. Usage: PtsTextureTreeItem ; // tree of used texture space
  33. Next: PtsFontTexture; // next texture in list
  34. Prev: PtsFontTexture; // previouse texture in list
  35. Size: Integer; // size of this texture
  36. Count: Integer; // number of chars stored in this texture
  37. end;
  38. TtsRendererOpenGL = class(TtsRenderer)
  39. private
  40. fVBO: GLuint;
  41. fTextureSize: Integer;
  42. fColor: TtsColor4f;
  43. fRenderPos: TtsPosition;
  44. fIsRendering: Boolean;
  45. fFirstTexture: PtsFontTexture;
  46. fLastTexture: PtsFontTexture;
  47. function CreateNewTexture: PtsFontTexture;
  48. procedure FreeTexture(var aTexture: PtsFontTexture);
  49. procedure FreeTextures(var aTexture: PtsFontTexture);
  50. procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  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 EndRender; override;
  56. procedure SetDrawPos(const X, Y: Integer); override;
  57. function GetDrawPos: TtsPosition; override;
  58. procedure MoveDrawPos(const X, Y: Integer); override;
  59. procedure SetColor(const aColor: TtsColor4f); override;
  60. procedure Render(const aCharRef: TtsCharRenderRef); 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. type
  69. TVertex = packed record
  70. pos: array[0..1] of GLfloat;
  71. tex: array[0..1] of GLfloat;
  72. end;
  73. const
  74. FORMAT_TYPES: array[TtsFormat] of packed record
  75. InternalFormat: GLenum;
  76. Format: GLenum;
  77. DataFormat: GLenum;
  78. end = (
  79. ( //tsFormatEmpty
  80. InternalFormat: 0;
  81. Format: 0;
  82. DataFormat: 0),
  83. ( //tsFormatRGBA8
  84. InternalFormat: GL_RGBA8;
  85. Format: GL_RGBA;
  86. DataFormat: GL_UNSIGNED_BYTE),
  87. ( //tsFormatLumAlpha8
  88. InternalFormat: GL_LUMINANCE8_ALPHA8;
  89. Format: GL_LUMINANCE_ALPHA;
  90. DataFormat: GL_UNSIGNED_BYTE),
  91. ( //tsFormatAlpha8
  92. InternalFormat: GL_ALPHA8;
  93. Format: GL_ALPHA;
  94. DataFormat: GL_UNSIGNED_BYTE)
  95. );
  96. VBO_DATA: array[0..3] of TVertex = (
  97. (pos: (0.0, 0.0); tex: (0.0, 0.0)),
  98. (pos: (0.0, 1.0); tex: (0.0, 1.0)),
  99. (pos: (1.0, 1.0); tex: (1.0, 1.0)),
  100. (pos: (1.0, 0.0); tex: (1.0, 0.0))
  101. );
  102. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  103. //TtsCharRenderRefOpenGL////////////////////////////////////////////////////////////////////////////////////////////////
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. constructor TtsCharRenderRefOpenGL.Create;
  106. begin
  107. inherited Create;
  108. TextureID := 0;
  109. FillByte(TexCoordPos, SizeOf(TexCoordPos), 0);
  110. FillByte(TexCoordSize, SizeOf(TexCoordSize), 0);
  111. FillByte(VertexPos, SizeOf(VertexPos), 0);
  112. FillByte(VertexSize, SizeOf(VertexSize), 0);
  113. end;
  114. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  115. //TtsRendererOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////
  116. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  117. function TtsRendererOpenGL.CreateNewTexture: PtsFontTexture;
  118. begin
  119. new(result);
  120. try
  121. FillByte(result^, SizeOf(result^), 0);
  122. new(result^.Usage);
  123. FillByte(result^.Usage^, SizeOf(result^.Usage^), 0);
  124. result^.Size := TextureSize;
  125. glGenTextures(1, @result^.ID);
  126. glBindTexture(GL_TEXTURE_2D, result^.ID);
  127. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  128. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  129. glTexImage2D(
  130. GL_TEXTURE_2D,
  131. 0,
  132. FORMAT_TYPES[Format].InternalFormat,
  133. result^.Size,
  134. result^.Size,
  135. 0,
  136. FORMAT_TYPES[Format].Format,
  137. FORMAT_TYPES[Format].DataFormat,
  138. nil);
  139. result^.Prev := fLastTexture;
  140. if Assigned(fLastTexture) then
  141. fLastTexture^.Next := result
  142. else
  143. fFirstTexture := result;
  144. fLastTexture := result;
  145. except
  146. if Assigned(result^.Usage) then
  147. Dispose(result^.Usage);
  148. Dispose(result);
  149. end;
  150. end;
  151. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  152. procedure TtsRendererOpenGL.FreeTexture(var aTexture: PtsFontTexture);
  153. begin
  154. if not Assigned(aTexture) then
  155. exit;
  156. glDeleteTextures(1, @aTexture^.ID);
  157. FreeTextureTreeItem(aTexture^.Usage);
  158. if Assigned(aTexture^.Prev) then
  159. aTexture^.Prev^.Next := aTexture^.Next;
  160. if Assigned(aTexture^.Next) then
  161. aTexture^.Next^.Prev := aTexture^.Prev;
  162. Dispose(aTexture);
  163. aTexture := nil;
  164. end;
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. procedure TtsRendererOpenGL.FreeTextures(var aTexture: PtsFontTexture);
  167. begin
  168. if not Assigned(aTexture) then
  169. exit;
  170. FreeTextures(aTexture^.Next);
  171. FreeTexture(aTexture);
  172. end;
  173. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  174. procedure TtsRendererOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  175. begin
  176. if not Assigned(aItem) then
  177. exit;
  178. FreeTextureTreeItem(aItem^.children[0]);
  179. FreeTextureTreeItem(aItem^.children[1]);
  180. Dispose(aItem);
  181. aItem := nil;
  182. end;
  183. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  184. function TtsRendererOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef;
  185. var
  186. GlyphWidth, GlyphHeight: Integer;
  187. function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
  188. var
  189. w, h: Integer;
  190. begin
  191. result := nil;
  192. w := X2 - X1;
  193. h := Y2 - Y1;
  194. if not Assigned(aItem) or
  195. Assigned(aItem^.ref) or
  196. (w < GlyphWidth) or
  197. (h < GlyphHeight) then
  198. exit;
  199. if (aItem^.value > 0) then begin
  200. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  201. if not Assigned(result) then
  202. result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
  203. end else if (aItem^.value < 0) then begin
  204. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
  205. if not Assigned(result) then
  206. result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
  207. end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
  208. X := X1;
  209. Y := Y1;
  210. result := aItem;
  211. end else begin
  212. new(aItem^.children[0]);
  213. new(aItem^.children[1]);
  214. FillByte(aItem^.children[0]^, SizeOf(aItem^.children[0]^), 0);
  215. FillByte(aItem^.children[1]^, SizeOf(aItem^.children[1]^), 0);
  216. if (w - GlyphWidth) < (h - GlyphHeight) then begin
  217. aItem^.value := Y1 + GlyphHeight;
  218. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  219. end else begin
  220. aItem^.value := -(X1 + GlyphWidth);
  221. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
  222. end;
  223. end;
  224. end;
  225. function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL;
  226. var
  227. x, y: Integer;
  228. item: PtsTextureTreeItem;
  229. begin
  230. item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
  231. if not Assigned(item) then
  232. raise EtsRendererOpenGL.Create('unable to add glyph to texture');
  233. item^.ref := TtsCharRenderRefOpenGL.Create;
  234. result := item^.ref;
  235. // Text Coords
  236. result.TextureID := aTexture^.ID;
  237. result.TexCoordPos.x := x / aTexture^.Size;
  238. result.TexCoordPos.y := y / aTexture^.Size;
  239. result.TexCoordSize.x := aCharImage.Width / aTexture^.Size;
  240. result.TexCoordSize.y := aCharImage.Height / aTexture^.Size;
  241. // Vertex Coords
  242. result.VertexPos.x := -aChar.GlyphRect.Left;
  243. result.VertexPos.y := -aChar.GlyphRect.Top - aChar.GlyphOrigin.y;
  244. result.VertexSize.x := aCharImage.Width;
  245. result.VertexSize.y := aCharImage.Height;
  246. glBindTexture(GL_TEXTURE_2D, result.TextureID);
  247. glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  248. glTexSubImage2D(GL_TEXTURE_2D, 0,
  249. x, y, aCharImage.Width, aCharImage.Height,
  250. FORMAT_TYPES[aCharImage.Format].Format,
  251. FORMAT_TYPES[aCharImage.Format].DataFormat,
  252. aCharImage.Data);
  253. end;
  254. var
  255. tex: PtsFontTexture;
  256. begin
  257. result := nil;
  258. if aCharImage.IsEmpty then
  259. exit;
  260. GlyphWidth := aCharImage.Width + 1;
  261. GlyphHeight := aCharImage.Height + 1;
  262. // try to add to existing texture
  263. tex := fFirstTexture;
  264. while Assigned(tex) and not Assigned(result) do begin
  265. result := AddToTexture(tex);
  266. tex := tex^.Next;
  267. end;
  268. // create new texture
  269. if not Assigned(result) then begin
  270. if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then
  271. raise EtsRendererOpenGL.Create('char is to large to fit into a texture: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  272. tex := CreateNewTexture;
  273. result := AddToTexture(tex);
  274. end;
  275. if not Assigned(result) then
  276. raise EtsRendererOpenGL.Create('unable to creat render reference for char: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  277. end;
  278. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  279. procedure TtsRendererOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef);
  280. var
  281. ref: TtsCharRenderRefOpenGL;
  282. tex: PtsFontTexture;
  283. function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
  284. begin
  285. result :=
  286. Assigned(aItem) and
  287. not Assigned(aItem^.children[0]) and
  288. not Assigned(aItem^.children[1]) and
  289. not Assigned(aItem^.ref);
  290. end;
  291. function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
  292. var
  293. w, h: Integer;
  294. begin
  295. w := X2 - X1;
  296. h := Y2 - Y1;
  297. if not Assigned(aItem) or
  298. (w < ref.VertexSize.x) or
  299. (h < ref.VertexSize.y) then
  300. exit;
  301. result := (aItem^.ref = ref);
  302. if not result then begin
  303. if (aItem^.value > 0) then begin
  304. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
  305. result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
  306. end else if (aItem^.value < 0) then begin
  307. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  308. result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  309. end;
  310. end else
  311. aItem^.ref := nil;
  312. if result and
  313. IsEmtpy(aItem^.children[0]) and
  314. IsEmtpy(aItem^.children[1]) then
  315. begin
  316. FreeTextureTreeItem(aItem^.children[0]);
  317. FreeTextureTreeItem(aItem^.children[1]);
  318. FillByte(aItem^, SizeOf(aItem^), 0);
  319. end;
  320. end;
  321. begin
  322. try
  323. if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then
  324. exit;
  325. ref := (aCharRef as TtsCharRenderRefOpenGL);
  326. tex := fFirstTexture;
  327. while Assigned(tex) do begin
  328. if (tex^.ID = ref.TextureID) then begin
  329. if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
  330. raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
  331. if IsEmtpy(tex^.Usage) then begin
  332. if (tex = fFirstTexture) then
  333. fFirstTexture := nil;
  334. FreeTexture(tex);
  335. end;
  336. tex := nil;
  337. end else
  338. tex := tex^.Next;
  339. end;
  340. finally
  341. if Assigned(aCharRef) then
  342. aCharRef.Free;
  343. end;
  344. end;
  345. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  346. procedure TtsRendererOpenGL.BeginRender;
  347. begin
  348. inherited BeginRender;
  349. fIsRendering := true;
  350. fRenderPos.x := 0;
  351. fRenderPos.y := 0;
  352. glPushMatrix;
  353. glColor4fv(@fColor.arr[0]);
  354. end;
  355. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  356. procedure TtsRendererOpenGL.EndRender;
  357. begin
  358. if fIsRendering then begin
  359. glPopMatrix;
  360. fIsRendering := false;
  361. end;
  362. inherited EndRender;
  363. end;
  364. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  365. procedure TtsRendererOpenGL.SetDrawPos(const X, Y: Integer);
  366. begin
  367. fRenderPos.x := X;
  368. fRenderPos.y := Y;
  369. glPopMatrix;
  370. glPushMatrix;
  371. glTranslatef(X, Y, 0);
  372. end;
  373. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  374. function TtsRendererOpenGL.GetDrawPos: TtsPosition;
  375. begin
  376. result := fRenderPos;
  377. end;
  378. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  379. procedure TtsRendererOpenGL.MoveDrawPos(const X, Y: Integer);
  380. begin
  381. fRenderPos.x := fRenderPos.x + X;
  382. fRenderPos.y := fRenderPos.y + Y;
  383. glTranslatef(X, Y, 0);
  384. end;
  385. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  386. procedure TtsRendererOpenGL.SetColor(const aColor: TtsColor4f);
  387. begin
  388. fColor := aColor;
  389. glColor4fv(@fColor.arr[0]);
  390. end;
  391. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  392. procedure TtsRendererOpenGL.Render(const aCharRef: TtsCharRenderRef);
  393. var
  394. ref: TtsCharRenderRefOpenGL;
  395. procedure RenderTreeItem(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer);
  396. begin
  397. glBegin(GL_LINE_LOOP);
  398. glVertex2f(X1, Y1);
  399. glVertex2f(X2, Y1);
  400. glVertex2f(X2, Y2);
  401. glVertex2f(X1, Y2);
  402. glEnd;
  403. if (aItem^.value > 0) then begin
  404. RenderTreeItem(aItem^.children[0], X1, Y1, X2, aItem^.value);
  405. RenderTreeItem(aItem^.children[1], X1, aItem^.value, X2, Y2);
  406. end else if (aItem^.value < 0) then begin
  407. RenderTreeItem(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  408. RenderTreeItem(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  409. end;
  410. end;
  411. begin
  412. if Assigned(aCharRef) and (aCharRef is TtsCharRenderRefOpenGL) then begin
  413. ref := (aCharRef as TtsCharRenderRefOpenGL);
  414. glEnable(GL_TEXTURE_2D);
  415. glBindTexture(GL_TEXTURE_2D, ref.TextureID);
  416. glMatrixMode(GL_TEXTURE);
  417. glPushMatrix;
  418. glLoadIdentity;
  419. glTranslatef(ref.TexCoordPos.x, ref.TexCoordPos.y, 0);
  420. glScalef(ref.TexCoordSize.x, ref.TexCoordSize.y, 1);
  421. glMatrixMode(GL_MODELVIEW);
  422. glPushMatrix;
  423. glTranslatef(ref.VertexPos.x, ref.VertexPos.y, 0);
  424. glScalef(ref.VertexSize.x, ref.VertexSize.y, 1);
  425. glBindBuffer(GL_ARRAY_BUFFER, fVBO);
  426. glEnableClientState(GL_VERTEX_ARRAY);
  427. glVertexPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(0));
  428. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  429. glTexCoordPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(8));
  430. glDrawArrays(GL_QUADS, 0, 4);
  431. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  432. glDisableClientState(GL_VERTEX_ARRAY);
  433. glMatrixMode(GL_TEXTURE);
  434. glPopMatrix;
  435. glMatrixMode(GL_MODELVIEW);
  436. glPopMatrix;
  437. end;
  438. end;
  439. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  440. constructor TtsRendererOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  441. begin
  442. inherited Create(aContext, aFormat);
  443. fIsRendering := false;
  444. fFirstTexture := nil;
  445. fLastTexture := nil;
  446. fTextureSize := 2048;
  447. fColor := tsColor4f(1, 1, 1, 1);
  448. fRenderPos := tsPosition(0, 0);
  449. glGenBuffers(1, @fVBO);
  450. glBindBuffer(GL_ARRAY_BUFFER, fVBO);
  451. glBufferData(GL_ARRAY_BUFFER, SizeOf(TVertex) * Length(VBO_DATA), @VBO_DATA[0].pos[0], GL_STATIC_DRAW);
  452. glBindBuffer(GL_ARRAY_BUFFER, 0);
  453. end;
  454. destructor TtsRendererOpenGL.Destroy;
  455. begin
  456. glDeleteBuffers(1, @fVBO);
  457. FreeTextures(fFirstTexture);
  458. inherited Destroy;
  459. end;
  460. end.