|
- unit utsRendererOpenGL;
-
- {$mode objfpc}{$H+}
-
- interface
-
- uses
- Classes, SysUtils, syncobjs, dglOpenGL,
- utsTextSuite, utsTypes;
-
- type
- TtsQuadPosF = array[0..3] of TtsPositionF;
- TtsCharRenderRefOpenGL = class(TtsCharRenderRef)
- private
- TextureID: GLint; // ID of OpenGL texture where the char is stored in
- TexCoordSize: TtsPositionF; // size of the char in texture coords (0.0 - 1.0)
- TexCoordPos: TtsPositionF; // position of the char in texture coords (0.0 - 1.0)
- VertexSize: TtsPositionF; // size of the char in world coords
- VertexPos: TtsPositionF; // size of the char in world coords
- public
- constructor Create;
- end;
-
- PtsTextureUsageItem = ^TtsTextureUsageItem;
- TtsTextureUsageItem = packed record
- children: array[0..3] of PtsTextureUsageItem;
- end;
-
- PtsTextureTreeItem = ^TtsTextureTreeItem;
- TtsTextureTreeItem = packed record
- value: SmallInt;
- children: array[0..1] of PtsTextureTreeItem;
- ref: TtsCharRenderRefOpenGL;
- end;
-
- PtsFontTexture = ^TtsFontTexture;
- TtsFontTexture = packed record
- ID: GLint; // OpenGL texture ID
- Usage: PtsTextureTreeItem ; // tree of used texture space
- Next: PtsFontTexture; // next texture in list
- Prev: PtsFontTexture; // previouse texture in list
- Size: Integer; // size of this texture
- Count: Integer; // number of chars stored in this texture
- end;
-
- TtsRendererOpenGL = class(TtsRenderer)
- private
- fVBO: GLuint;
- fTextureSize: Integer;
- fColor: TtsColor4f;
- fRenderPos: TtsPosition;
- fIsRendering: Boolean;
- fFirstTexture: PtsFontTexture;
- fLastTexture: PtsFontTexture;
-
- function CreateNewTexture: PtsFontTexture;
- procedure FreeTexture(var aTexture: PtsFontTexture);
- procedure FreeTextures(var aTexture: PtsFontTexture);
- procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
- protected
- function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; override;
- procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); override;
-
- procedure BeginRender; override;
- procedure EndRender; override;
-
- procedure SetDrawPos(const X, Y: Integer); override;
- function GetDrawPos: TtsPosition; override;
- procedure MoveDrawPos(const X, Y: Integer); override;
- procedure SetColor(const aColor: TtsColor4f); override;
- procedure Render(const aCharRef: TtsCharRenderRef); override;
- public
- property TextureSize: Integer read fTextureSize write fTextureSize;
-
- constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
- destructor Destroy; override;
- end;
-
- EtsRendererOpenGL = class(EtsRenderer);
-
- implementation
-
- type
- TVertex = packed record
- pos: array[0..1] of GLfloat;
- tex: array[0..1] of GLfloat;
- end;
-
- const
- FORMAT_TYPES: array[TtsFormat] of packed record
- InternalFormat: GLenum;
- Format: GLenum;
- DataFormat: GLenum;
- end = (
- ( //tsFormatEmpty
- InternalFormat: 0;
- Format: 0;
- DataFormat: 0),
- ( //tsFormatRGBA8
- InternalFormat: GL_RGBA8;
- Format: GL_RGBA;
- DataFormat: GL_UNSIGNED_BYTE),
- ( //tsFormatLumAlpha8
- InternalFormat: GL_LUMINANCE8_ALPHA8;
- Format: GL_LUMINANCE_ALPHA;
- DataFormat: GL_UNSIGNED_BYTE),
- ( //tsFormatAlpha8
- InternalFormat: GL_ALPHA8;
- Format: GL_ALPHA;
- DataFormat: GL_UNSIGNED_BYTE)
- );
-
- VBO_DATA: array[0..3] of TVertex = (
- (pos: (0.0, 0.0); tex: (0.0, 0.0)),
- (pos: (0.0, 1.0); tex: (0.0, 1.0)),
- (pos: (1.0, 1.0); tex: (1.0, 1.0)),
- (pos: (1.0, 0.0); tex: (1.0, 0.0))
- );
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //TtsCharRenderRefOpenGL////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- constructor TtsCharRenderRefOpenGL.Create;
- begin
- inherited Create;
- TextureID := 0;
- FillByte(TexCoordPos, SizeOf(TexCoordPos), 0);
- FillByte(TexCoordSize, SizeOf(TexCoordSize), 0);
- FillByte(VertexPos, SizeOf(VertexPos), 0);
- FillByte(VertexSize, SizeOf(VertexSize), 0);
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //TtsRendererOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- function TtsRendererOpenGL.CreateNewTexture: PtsFontTexture;
- begin
- new(result);
- try
- FillByte(result^, SizeOf(result^), 0);
- new(result^.Usage);
- FillByte(result^.Usage^, SizeOf(result^.Usage^), 0);
- result^.Size := TextureSize;
- glGenTextures(1, @result^.ID);
- glBindTexture(GL_TEXTURE_2D, result^.ID);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(
- GL_TEXTURE_2D,
- 0,
- FORMAT_TYPES[Format].InternalFormat,
- result^.Size,
- result^.Size,
- 0,
- FORMAT_TYPES[Format].Format,
- FORMAT_TYPES[Format].DataFormat,
- nil);
-
- result^.Prev := fLastTexture;
- if Assigned(fLastTexture) then
- fLastTexture^.Next := result
- else
- fFirstTexture := result;
- fLastTexture := result;
- except
- if Assigned(result^.Usage) then
- Dispose(result^.Usage);
- Dispose(result);
- end;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.FreeTexture(var aTexture: PtsFontTexture);
- begin
- if not Assigned(aTexture) then
- exit;
- glDeleteTextures(1, @aTexture^.ID);
- FreeTextureTreeItem(aTexture^.Usage);
- if Assigned(aTexture^.Prev) then
- aTexture^.Prev^.Next := aTexture^.Next;
- if Assigned(aTexture^.Next) then
- aTexture^.Next^.Prev := aTexture^.Prev;
- Dispose(aTexture);
- aTexture := nil;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.FreeTextures(var aTexture: PtsFontTexture);
- begin
- if not Assigned(aTexture) then
- exit;
- FreeTextures(aTexture^.Next);
- FreeTexture(aTexture);
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
- begin
- if not Assigned(aItem) then
- exit;
- FreeTextureTreeItem(aItem^.children[0]);
- FreeTextureTreeItem(aItem^.children[1]);
- Dispose(aItem);
- aItem := nil;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- function TtsRendererOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef;
- var
- GlyphWidth, GlyphHeight: Integer;
-
- function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
- var
- w, h: Integer;
- begin
- result := nil;
- w := X2 - X1;
- h := Y2 - Y1;
- if not Assigned(aItem) or
- Assigned(aItem^.ref) or
- (w < GlyphWidth) or
- (h < GlyphHeight) then
- exit;
-
- if (aItem^.value > 0) then begin
- result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
- if not Assigned(result) then
- result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
- end else if (aItem^.value < 0) then begin
- result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
- if not Assigned(result) then
- result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
- end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
- X := X1;
- Y := Y1;
- result := aItem;
- end else begin
- new(aItem^.children[0]);
- new(aItem^.children[1]);
- FillByte(aItem^.children[0]^, SizeOf(aItem^.children[0]^), 0);
- FillByte(aItem^.children[1]^, SizeOf(aItem^.children[1]^), 0);
- if (w - GlyphWidth) < (h - GlyphHeight) then begin
- aItem^.value := Y1 + GlyphHeight;
- result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
- end else begin
- aItem^.value := -(X1 + GlyphWidth);
- result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
- end;
- end;
- end;
-
- function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL;
- var
- x, y: Integer;
- item: PtsTextureTreeItem;
- begin
- item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
- if not Assigned(item) then
- raise EtsRendererOpenGL.Create('unable to add glyph to texture');
- item^.ref := TtsCharRenderRefOpenGL.Create;
- result := item^.ref;
-
- // Text Coords
- result.TextureID := aTexture^.ID;
- result.TexCoordPos.x := x / aTexture^.Size;
- result.TexCoordPos.y := y / aTexture^.Size;
- result.TexCoordSize.x := aCharImage.Width / aTexture^.Size;
- result.TexCoordSize.y := aCharImage.Height / aTexture^.Size;
-
- // Vertex Coords
- result.VertexPos.x := -aChar.GlyphRect.Left;
- result.VertexPos.y := -aChar.GlyphRect.Top - aChar.GlyphOrigin.y;
- result.VertexSize.x := aCharImage.Width;
- result.VertexSize.y := aCharImage.Height;
-
- glBindTexture(GL_TEXTURE_2D, result.TextureID);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- x, y, aCharImage.Width, aCharImage.Height,
- FORMAT_TYPES[aCharImage.Format].Format,
- FORMAT_TYPES[aCharImage.Format].DataFormat,
- aCharImage.Data);
- end;
-
- var
- tex: PtsFontTexture;
- begin
- result := nil;
- if aCharImage.IsEmpty then
- exit;
-
- GlyphWidth := aCharImage.Width + 1;
- GlyphHeight := aCharImage.Height + 1;
-
- // try to add to existing texture
- tex := fFirstTexture;
- while Assigned(tex) and not Assigned(result) do begin
- result := AddToTexture(tex);
- tex := tex^.Next;
- end;
-
- // create new texture
- if not Assigned(result) then begin
- if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then
- raise EtsRendererOpenGL.Create('char is to large to fit into a texture: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
- tex := CreateNewTexture;
- result := AddToTexture(tex);
- end;
-
- if not Assigned(result) then
- raise EtsRendererOpenGL.Create('unable to creat render reference for char: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef);
- var
- ref: TtsCharRenderRefOpenGL;
- tex: PtsFontTexture;
-
- function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
- begin
- result :=
- Assigned(aItem) and
- not Assigned(aItem^.children[0]) and
- not Assigned(aItem^.children[1]) and
- not Assigned(aItem^.ref);
- end;
-
- function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
- var
- w, h: Integer;
- begin
- w := X2 - X1;
- h := Y2 - Y1;
- if not Assigned(aItem) or
- (w < ref.VertexSize.x) or
- (h < ref.VertexSize.y) then
- exit;
-
- result := (aItem^.ref = ref);
- if not result then begin
- if (aItem^.value > 0) then begin
- result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
- result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
- end else if (aItem^.value < 0) then begin
- result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
- result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
- end;
- end else
- aItem^.ref := nil;
-
- if result and
- IsEmtpy(aItem^.children[0]) and
- IsEmtpy(aItem^.children[1]) then
- begin
- FreeTextureTreeItem(aItem^.children[0]);
- FreeTextureTreeItem(aItem^.children[1]);
- FillByte(aItem^, SizeOf(aItem^), 0);
- end;
- end;
-
- begin
- try
- if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then
- exit;
- ref := (aCharRef as TtsCharRenderRefOpenGL);
- tex := fFirstTexture;
- while Assigned(tex) do begin
- if (tex^.ID = ref.TextureID) then begin
- if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
- raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
- if IsEmtpy(tex^.Usage) then begin
- if (tex = fFirstTexture) then
- fFirstTexture := nil;
- FreeTexture(tex);
- end;
- tex := nil;
- end else
- tex := tex^.Next;
- end;
- finally
- if Assigned(aCharRef) then
- aCharRef.Free;
- end;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.BeginRender;
- begin
- inherited BeginRender;
- fIsRendering := true;
- fRenderPos.x := 0;
- fRenderPos.y := 0;
- glPushMatrix;
- glColor4fv(@fColor.arr[0]);
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.EndRender;
- begin
- if fIsRendering then begin
- glPopMatrix;
- fIsRendering := false;
- end;
- inherited EndRender;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.SetDrawPos(const X, Y: Integer);
- begin
- fRenderPos.x := X;
- fRenderPos.y := Y;
- glPopMatrix;
- glPushMatrix;
- glTranslatef(X, Y, 0);
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- function TtsRendererOpenGL.GetDrawPos: TtsPosition;
- begin
- result := fRenderPos;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.MoveDrawPos(const X, Y: Integer);
- begin
- fRenderPos.x := fRenderPos.x + X;
- fRenderPos.y := fRenderPos.y + Y;
- glTranslatef(X, Y, 0);
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.SetColor(const aColor: TtsColor4f);
- begin
- fColor := aColor;
- glColor4fv(@fColor.arr[0]);
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- procedure TtsRendererOpenGL.Render(const aCharRef: TtsCharRenderRef);
- var
- ref: TtsCharRenderRefOpenGL;
-
- procedure RenderTreeItem(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer);
- begin
- glBegin(GL_LINE_LOOP);
- glVertex2f(X1, Y1);
- glVertex2f(X2, Y1);
- glVertex2f(X2, Y2);
- glVertex2f(X1, Y2);
- glEnd;
- if (aItem^.value > 0) then begin
- RenderTreeItem(aItem^.children[0], X1, Y1, X2, aItem^.value);
- RenderTreeItem(aItem^.children[1], X1, aItem^.value, X2, Y2);
- end else if (aItem^.value < 0) then begin
- RenderTreeItem(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
- RenderTreeItem(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
- end;
- end;
-
- begin
- if Assigned(aCharRef) and (aCharRef is TtsCharRenderRefOpenGL) then begin
- ref := (aCharRef as TtsCharRenderRefOpenGL);
-
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, ref.TextureID);
-
- glMatrixMode(GL_TEXTURE);
- glPushMatrix;
- glLoadIdentity;
- glTranslatef(ref.TexCoordPos.x, ref.TexCoordPos.y, 0);
- glScalef(ref.TexCoordSize.x, ref.TexCoordSize.y, 1);
-
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix;
- glTranslatef(ref.VertexPos.x, ref.VertexPos.y, 0);
- glScalef(ref.VertexSize.x, ref.VertexSize.y, 1);
-
- glBindBuffer(GL_ARRAY_BUFFER, fVBO);
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(0));
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(8));
-
- glDrawArrays(GL_QUADS, 0, 4);
-
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisableClientState(GL_VERTEX_ARRAY);
-
- glMatrixMode(GL_TEXTURE);
- glPopMatrix;
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix;
- end;
- end;
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- constructor TtsRendererOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
- begin
- inherited Create(aContext, aFormat);
- fIsRendering := false;
- fFirstTexture := nil;
- fLastTexture := nil;
- fTextureSize := 2048;
- fColor := tsColor4f(1, 1, 1, 1);
- fRenderPos := tsPosition(0, 0);
-
- glGenBuffers(1, @fVBO);
- glBindBuffer(GL_ARRAY_BUFFER, fVBO);
- glBufferData(GL_ARRAY_BUFFER, SizeOf(TVertex) * Length(VBO_DATA), @VBO_DATA[0].pos[0], GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- end;
-
- destructor TtsRendererOpenGL.Destroy;
- begin
- glDeleteBuffers(1, @fVBO);
- FreeTextures(fFirstTexture);
- inherited Destroy;
- end;
-
- end.
|