DEADSOFTWARE

gl: implement font loading and drawing
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Sat, 11 Jun 2022 15:33:33 +0000 (18:33 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Fri, 9 Jun 2023 08:42:56 +0000 (11:42 +0300)
src/game/Doom2DF.lpr
src/game/renders/opengl/r_draw.pas
src/game/renders/opengl/r_fonts.pas [new file with mode: 0644]
src/game/renders/opengl/r_map.pas
src/game/renders/opengl/r_render.pas
src/game/renders/opengl/r_textures.pas

index 6fdc3094a2b9844920eca1de146363107f052080..ba700515606c038b292305dc5c2f8e03591ebd2f 100644 (file)
@@ -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}
index c2bfc245726f040e249c9455835317666c7726a3..5f01c5b4c10947afc90195ae181816ce3c7dfe68 100644 (file)
@@ -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 (file)
index 0000000..e2ebc90
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ *)
+{$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.
index c974664acf4e043c7f1f96edfb988b8878b8b556..11be032553b5fe48717cb92f1dfc25730b3fe871 100644 (file)
@@ -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;
index 2a385a15b4724838172a520fb0d2bf0ed2214b13..249867d920e51482f181ee2f7b459b7faa7d1f89 100644 (file)
@@ -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;
 
index ce71ebc727d7e96b0001e0a44a512ba1b9269846..5950e36f6f272bf67b39af7fd2cfac386291ae6a 100644 (file)
@@ -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.