From 86639e1fae2121ee4e86727933dfa1f06983b67c Mon Sep 17 00:00:00 2001 From: DeaDDooMER Date: Sat, 11 Jun 2022 18:33:33 +0300 Subject: [PATCH] gl: implement font loading and drawing --- src/game/Doom2DF.lpr | 1 + src/game/renders/opengl/r_draw.pas | 16 +++++ src/game/renders/opengl/r_fonts.pas | 82 ++++++++++++++++++++++ src/game/renders/opengl/r_map.pas | 4 +- src/game/renders/opengl/r_render.pas | 22 +++++- src/game/renders/opengl/r_textures.pas | 96 +++++++++++++++++++++++--- 6 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 src/game/renders/opengl/r_fonts.pas diff --git a/src/game/Doom2DF.lpr b/src/game/Doom2DF.lpr index 6fdc309..ba70051 100644 --- a/src/game/Doom2DF.lpr +++ b/src/game/Doom2DF.lpr @@ -202,6 +202,7 @@ uses r_textures in 'renders/opengl/r_textures.pas', r_draw in 'renders/opengl/r_draw.pas', r_map in 'renders/opengl/r_map.pas', + r_fonts in 'renders/opengl/r_fonts.pas', {$ELSE} {$FATAL render driver not selected} {$ENDIF} diff --git a/src/game/renders/opengl/r_draw.pas b/src/game/renders/opengl/r_draw.pas index c2bfc24..5f01c5b 100644 --- a/src/game/renders/opengl/r_draw.pas +++ b/src/game/renders/opengl/r_draw.pas @@ -33,6 +33,8 @@ interface procedure r_Draw_FillRect (l, t, r, b: Integer; rr, gg, bb, aa: Byte); procedure r_Draw_InvertRect (l, t, r, b: Integer; rr, gg, bb, aa: Byte); + procedure r_Draw_Text (const text: AnsiString; x, y: Integer; r, g, b, a: Byte; f: TGLFont); + implementation uses @@ -255,4 +257,18 @@ implementation glEnd; end; + procedure r_Draw_Text (const text: AnsiString; x, y: Integer; r, g, b, a: Byte; f: TGLFont); + var i, xoff: Integer; t: TGLTexture; ch: AnsiChar; + begin + xoff := x; + for i := 1 to Length(text) do + begin + ch := text[i]; + t := f.GetChar(ch); + if t <> nil then + r_Draw_Texture(t, xoff, y, t.width, t.height, false, r, g, b, a, false); + Inc(xoff, f.GetWidth(ch) + f.GetSpace()); + end; + end; + end. diff --git a/src/game/renders/opengl/r_fonts.pas b/src/game/renders/opengl/r_fonts.pas new file mode 100644 index 0000000..e2ebc90 --- /dev/null +++ b/src/game/renders/opengl/r_fonts.pas @@ -0,0 +1,82 @@ +(* Copyright (C) Doom 2D: Forever Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License ONLY. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *) +{$INCLUDE ../../../shared/a_modes.inc} +unit r_fonts; + +interface + + type + TFontInfo = record + w, h: Integer; + kern: Integer; + ch: array [AnsiChar] of record + w: Byte; + end; + end; + + TFont = class abstract + end; + + function r_Font_LoadInfoFromFile (const filename: AnsiString; var f: TFontInfo): Boolean; + +implementation + + uses + Math, SysUtils, + WADREADER, CONFIG, utils, + e_log + ; + + function r_Font_LoadInfoFromMemory (data: Pointer; size: LongInt; var f: TFontInfo): Boolean; + var cfg: TConfig; c: AnsiChar; + begin + result := false; + if data <> nil then + begin + cfg := TConfig.CreateMem(data, size); + if cfg <> nil then + begin + f.w := MIN(MAX(cfg.ReadInt('FontMap', 'CharWidth', 0), 0), 255); + f.h := MIN(MAX(cfg.ReadInt('FontMap', 'CharHeight', 0), 0), 255); + f.kern := MIN(MAX(cfg.ReadInt('FontMap', 'Kerning', 0), -128), 127); + for c := #0 to #255 do + begin + f.ch[c].w := MIN(MAX(cfg.ReadInt(IntToStr(ORD(c)), 'Width', 0), 0), 255); + end; + result := (f.w > 0) and (f.h > 0); + cfg.Free; + end; + end; + end; + + function r_Font_LoadInfoFromFile (const filename: AnsiString; var f: TFontInfo): Boolean; + var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer; + begin + result := false; + wadName := g_ExtractWadName(filename); + wad := TWADFile.Create(); + if wad.ReadFile(wadName) then + begin + resName := g_ExtractFilePathName(filename); + if wad.GetResource(resName, data, size, false) then + begin + result := r_Font_LoadInfoFromMemory(data, size, f); + FreeMem(data); + end; + wad.Free; + end; + end; + +end. diff --git a/src/game/renders/opengl/r_map.pas b/src/game/renders/opengl/r_map.pas index c974664..11be032 100644 --- a/src/game/renders/opengl/r_map.pas +++ b/src/game/renders/opengl/r_map.pas @@ -357,9 +357,9 @@ implementation SetLength(Models[i].gibs.rect, m.GibsCount); for a := 0 to m.GibsCount - 1 do Models[i].gibs.rect[a] := DefaultGibSize; - if r_Textures_LoadStreamFromFile(prefix + m.GibsResource, 32, 32, m.GibsCount, Models[i].gibs.base, Models[i].gibs.rect) then + if r_Textures_LoadStreamFromFile(prefix + m.GibsResource, 32, 32, m.GibsCount, m.GibsCount, Models[i].gibs.base, Models[i].gibs.rect) then begin - if r_Textures_LoadStreamFromFile(prefix + m.GibsMask, 32, 32, m.GibsCount, Models[i].gibs.mask, nil) then + if r_Textures_LoadStreamFromFile(prefix + m.GibsMask, 32, 32, m.GibsCount, m.GibsCount, Models[i].gibs.mask, nil) then begin // ok end; diff --git a/src/game/renders/opengl/r_render.pas b/src/game/renders/opengl/r_render.pas index 2a385a1..249867d 100644 --- a/src/game/renders/opengl/r_render.pas +++ b/src/game/renders/opengl/r_render.pas @@ -80,11 +80,14 @@ implementation SysUtils, Classes, Math, e_log, utils, g_game, g_options, g_console, - r_textures, r_map + r_draw, r_textures, r_fonts, r_map ; var menuBG: TGLTexture; + stdfont: TGLFont; + smallfont: TGLFont; + menufont: TGLFont; procedure r_Render_LoadTextures; begin @@ -96,15 +99,32 @@ implementation r_Map_FreeTextures; end; + function r_Render_LoadFont (const name: AnsiString): TGLFont; + var info: TFontInfo; skiphack: Integer; + begin + result := nil; + if name = 'STD' then skiphack := 144 else skiphack := 0; + if r_Font_LoadInfoFromFile(GameWad + ':FONTS/' + name + 'TXT', info) then + result := r_Textures_LoadFontFromFile(GameWad + ':FONTS/' + name + 'FONT', info, skiphack, true); + if result = nil then + e_logwritefln('failed to load font %s', [name]); + end; + procedure r_Render_Load; begin menuBG := r_Textures_LoadFromFile(GameWAD + ':' + 'TEXTURES/TITLE'); + stdfont := r_Render_LoadFont('STD'); + smallfont := r_Render_LoadFont('SMALL'); + menufont := r_Render_LoadFont('MENU'); r_Map_Load; end; procedure r_Render_Free; begin r_Map_Free; + menufont.Free; + smallfont.Free; + stdfont.Free; menuBG.Free; end; diff --git a/src/game/renders/opengl/r_textures.pas b/src/game/renders/opengl/r_textures.pas index ce71ebc..5950e36 100644 --- a/src/game/renders/opengl/r_textures.pas +++ b/src/game/renders/opengl/r_textures.pas @@ -25,7 +25,7 @@ interface {$ENDIF} g_base, // TRectHW utils, - r_atlas + r_atlas, r_fonts ; type @@ -102,13 +102,27 @@ interface TRectArray = array of TRectWH; + TGLFont = class sealed (TFont) + private + info: TFontInfo; + ch: TGLTextureArray; + + public + destructor Destroy; override; + function GetChar (c: AnsiChar): TGLTexture; + function GetWidth (c: AnsiChar): Integer; + function GetSpace (): Integer; + end; + procedure r_Textures_Initialize; procedure r_Textures_Finalize; function r_Textures_LoadFromFile (const filename: AnsiString; log: Boolean = True): TGLTexture; function r_Textures_LoadMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture; function r_Textures_LoadMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; backanim: Boolean; log: Boolean = True): TGLMultiTexture; - function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean; + function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean; + + function r_Textures_LoadFontFromFile (const filename: AnsiString; constref f: TFontInfo; skipch: Integer; log: Boolean = true): TGLFont; implementation @@ -376,7 +390,6 @@ implementation function r_Textures_LoadFromImage (var img: TImageData): TGLTexture; var t: TGLTexture; n: TGLAtlasNode; c: TDynImageDataArray; cw, ch, i, j: LongInt; begin - // e_logwritefln('r_Textures_CreateFromImage: w=%s h=%s', [img.width, img.height]); result := nil; if SplitImage(img, c, maxTileSize, maxTileSize, cw, ch, False) then begin @@ -638,22 +651,25 @@ implementation end; end; - function r_Textures_LoadStreamFromImage (var img: TImageData; w, h, c: Integer; st: TGLTextureArray; rs: TRectArray): Boolean; - var i: Integer; t: TImageData; + function r_Textures_LoadStreamFromImage (var img: TImageData; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray): Boolean; + var i, x, y: Integer; t: TImageData; begin ASSERT(w >= 0); ASSERT(h >= 0); ASSERT(c >= 1); + ASSERT(cw >= 1); ASSERT((st <> nil) and (Length(st) >= c)); ASSERT((rs = nil) or (Length(rs) >= c)); result := true; for i := 0 to c - 1 do begin + x := i mod cw; + y := i div cw; InitImage(t); st[i] := nil; if NewImage(w, h, img.Format, t) then begin - if CopyRect(img, w * i, 0, w, h, t, 0, 0) then + if CopyRect(img, x * w, y * h, w, h, t, 0, 0) then begin if rs <> nil then rs[i] := r_Textures_GetRect(t); @@ -665,12 +681,13 @@ implementation end; end; - function r_Textures_LoadStreamFromMemory (data: Pointer; size: LongInt; w, h, c: Integer; st: TGLTextureArray; rs: TRectArray): Boolean; + function r_Textures_LoadStreamFromMemory (data: Pointer; size: LongInt; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray): Boolean; var img: TImageData; begin ASSERT(w >= 0); ASSERT(h >= 0); ASSERT(c >= 1); + ASSERT(cw >= 1); ASSERT((st <> nil) and (Length(st) >= c)); ASSERT((rs = nil) or (Length(rs) >= c)); result := false; @@ -679,20 +696,25 @@ implementation InitImage(img); try if LoadImageFromMemory(data, size, img) then + begin if r_Textures_FixImageData(img) then - result := r_Textures_LoadStreamFromImage(img, w, h, c, st, rs) + begin + result := r_Textures_LoadStreamFromImage(img, w, h, c, cw, st, rs) + end; + end; except end; FreeImage(img); end; end; - function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean; + function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean; var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer; begin ASSERT(w > 0); ASSERT(h > 0); ASSERT(count >= 1); + ASSERT(cw >= 1); ASSERT((st <> nil) and (Length(st) >= count)); ASSERT((rs = nil) or (Length(rs) >= count)); result := false; @@ -703,11 +725,63 @@ implementation resName := g_ExtractFilePathName(filename); if wad.GetResource(resName, data, size, log) then begin - result := r_Textures_LoadStreamFromMemory(data, size, w, h, count, st, rs); + result := r_Textures_LoadStreamFromMemory(data, size, w, h, count, cw, st, rs); FreeMem(data); end; wad.Free - end + end; + end; + + (* --------- TGLFont --------- *) + + function r_Textures_LoadFontFromFile (const filename: AnsiString; constref f: TFontInfo; skipch: Integer; log: Boolean = true): TGLFont; + var i: Integer; st: TGLTextureArray; font: TGLFont; t: TGLTexture; + begin + ASSERT(skipch >= 0); + result := nil; + SetLength(st, 256); + if r_Textures_LoadStreamFromFile(filename, f.w, f.h, 256, 16, st, nil, log) then + begin + if skipch > 0 then + begin + for i := 0 to 255 do + begin + t := st[i]; + st[i] := st[(i + skipch) mod 256]; + st[(i + skipch) mod 256] := t; + end; + end; + font := TGLFont.Create(); + font.info := f; + font.ch := st; + result := font; + end; + end; + + destructor TGLFont.Destroy; + var i: Integer; + begin + if self.ch <> nil then + for i := 0 to High(self.ch) do + self.ch[i].Free; + self.ch := nil; + end; + + function TGLFont.GetChar (c: AnsiChar): TGLTexture; + begin + result := self.ch[ORD(c)]; + end; + + function TGLFont.GetWidth (c: AnsiChar): Integer; + begin + result := self.info.ch[c].w; + if result = 0 then + result := self.info.w; + end; + + function TGLFont.GetSpace (): Integer; + begin + result := self.info.kern; end; end. -- 2.29.2