DEADSOFTWARE

gl: optimize repeatable textures
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Wed, 28 Dec 2022 19:35:20 +0000 (22:35 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Fri, 9 Jun 2023 09:08:17 +0000 (12:08 +0300)
src/game/renders/opengl/r_common.pas
src/game/renders/opengl/r_console.pas
src/game/renders/opengl/r_draw.pas
src/game/renders/opengl/r_gui.pas
src/game/renders/opengl/r_loadscreen.pas
src/game/renders/opengl/r_map.pas
src/game/renders/opengl/r_render.pas
src/game/renders/opengl/r_textures.pas

index c12cbbbb44bfcdc446cd92f9ace3f041fe98fee1..331988249b287a83355b7df4e07a522875e6ef15 100644 (file)
@@ -68,11 +68,11 @@ interface
   procedure r_Common_StepLoading (incval: Integer);
   procedure r_Common_DrawLoading (force: Boolean);
 
-  function r_Common_LoadTextureFromFile (const filename: AnsiString; log: Boolean = True): TGLTexture;
-  function r_Common_LoadTextureMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture;
-  function r_Common_LoadTextureMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; log: Boolean = True): TGLMultiTexture;
-  function r_Common_LoadTextureMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; log: Boolean = True): TGLMultiTexture;
-  function r_Common_LoadTextureStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean;
+  function r_Common_LoadTextureFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLTexture;
+  function r_Common_LoadTextureMultiFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
+  function r_Common_LoadTextureMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
+  function r_Common_LoadTextureMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
+  function r_Common_LoadTextureStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; hints: TGLHintsSet; log: Boolean = True): Boolean;
   function r_Common_LoadTextureFontFromFile (const filename: AnsiString; constref f: TFontInfo; font2enc: TConvProc; log: Boolean = true): TGLFont;
 
   procedure r_Common_Load;
@@ -328,7 +328,7 @@ implementation
     if name <> here.name then
       r_Common_FreeThis(here);
     if (name <> '') and (here.name <> name) then
-      here.id := r_Textures_LoadFromFile(name);
+      here.id := r_Textures_LoadFromFile(name, []); // !!!
 
     result := here.id <> nil;
 
@@ -437,33 +437,33 @@ implementation
     r_Common_DrawLoading(false);
   end;
 
-  function r_Common_LoadTextureFromFile (const filename: AnsiString; log: Boolean = True): TGLTexture;
+  function r_Common_LoadTextureFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLTexture;
   begin
-    result := r_Textures_LoadFromFile(filename, log);
+    result := r_Textures_LoadFromFile(filename, hints, log);
     r_Common_StepLoading(1);
   end;
 
-  function r_Common_LoadTextureMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture;
+  function r_Common_LoadTextureMultiFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
   begin
-    result := r_Textures_LoadMultiFromFile(filename, log);
+    result := r_Textures_LoadMultiFromFile(filename, hints, log);
     r_Common_StepLoading(1);
   end;
 
-  function r_Common_LoadTextureMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; log: Boolean = True): TGLMultiTexture;
+  function r_Common_LoadTextureMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
   begin
-    result := r_Textures_LoadMultiFromFileAndInfo(filename, w, h, count, log);
+    result := r_Textures_LoadMultiFromFileAndInfo(filename, w, h, count, hints, log);
     r_Common_StepLoading(1);
   end;
 
-  function r_Common_LoadTextureMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; log: Boolean = True): TGLMultiTexture;
+  function r_Common_LoadTextureMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
   begin
-    result := r_Textures_LoadMultiTextFromFile(filename, txt, log);
+    result := r_Textures_LoadMultiTextFromFile(filename, txt, hints, log);
     r_Common_StepLoading(1);
   end;
 
-  function r_Common_LoadTextureStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; log: Boolean = True): Boolean;
+  function r_Common_LoadTextureStreamFromFile (const filename: AnsiString; w, h, count, cw: Integer; st: TGLTextureArray; rs: TRectArray; hints: TGLHintsSet; log: Boolean = True): Boolean;
   begin
-    r_Textures_LoadStreamFromFile(filename, w, h, count, cw, st, rs, log);
+    result := r_Textures_LoadStreamFromFile(filename, w, h, count, cw, st, rs, hints, log);
     r_Common_StepLoading(1);
   end;
 
index e5adc322b25c30ce0bfa11167cfaec7e7f254fbe..5c2f51999d266304d91e2b9ef120d152d6cf3636 100644 (file)
@@ -57,7 +57,7 @@ implementation
   procedure r_Console_Load;
   begin
     r_Common_SetLoading(_lc[I_LOAD_CONSOLE], 1);
-    Background := r_Textures_LoadFromFile(GameWad + ':TEXTURES/CONSOLE');
+    Background := r_Textures_LoadFromFile(GameWad + ':TEXTURES/CONSOLE', [TGLHints.txNoRepeat]);
   end;
 
   procedure r_Console_Free;
index 9db91836645d5460279debd7a4937a1ab3471bb9..67bd23041275b237de894398f5960198e4691c30 100644 (file)
@@ -126,6 +126,29 @@ implementation
     end
   end;
 
+  procedure DrawHWTexture (gltex: GLint; nw, nh, x, y, w, h: Integer; flip: Boolean; rr, gg, bb, aa: Byte; blend: Boolean);
+    var ax, bx, ay, by: GLfloat; l, t, r, b: Integer;
+  begin
+    ax := IfThen(flip, 0, w) / nw;
+    bx := IfThen(flip, w, 0) / nh;
+    ay := 0 / nw;
+    by := h / nh;
+    l := x; t := y; r := x + w; b := y + h;
+    glBindTexture(GL_TEXTURE_2D, gltex);
+    glColor4ub(rr, gg, bb, aa);
+    if blend then glBlendFunc(GL_SRC_ALPHA, GL_ONE) else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBegin(GL_QUADS);
+      glTexCoord2f(ax, ay); glVertex2i(r, t);
+      glTexCoord2f(bx, ay); glVertex2i(l, t);
+      glTexCoord2f(bx, by); glVertex2i(l, b);
+      glTexCoord2f(ax, by); glVertex2i(r, b);
+    glEnd();
+    glDisable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, 0);
+  end;
+
   procedure r_Draw_Texture (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
     var i, j, first, last, step: Integer; n: TGLAtlasNode;
   begin
@@ -157,6 +180,22 @@ implementation
     end
   end;
 
+  function r_Draw_IsHWRepeatable (img: TGLTexture): Boolean;
+    var n: TGLAtlasNode; a: TGLAtlas;
+  begin
+    ASSERT(img <> nil);
+    result := false;
+    if (img.cols = 1) and (img.lines = 1) then
+    begin
+      n := img.GetTile(0, 0);
+      if (n.width = img.width) and (n.height = img.height) then
+      begin
+        a := n.base;
+        result := (a.GetWidth() = img.width) and (a.GetHeight() = img.height)
+      end;
+    end;
+  end;
+
   procedure r_Draw_TextureRepeat (img: TGLTexture; x, y, w, h: Integer; flip: Boolean; r, g, b, a: Byte; blend: Boolean);
     var i, j: Integer;
   begin
@@ -164,6 +203,8 @@ implementation
     ASSERT(h >= 0);
     if img = nil then
       r_Draw_Texture(nil, x, y, w, h, flip, NTR, NTG, NTB, NTB, blend)
+    else if r_Draw_IsHWRepeatable(img) then
+      DrawHWTexture(img.GetTile(0, 0).base.id, img.width, img.height, x, y, w, h, flip, r, g, b, a, blend)
     else
       for j := 0 to (h - 1) div img.height do
         for i := 0 to (w - 1) div img.width do
index 04d3c3cc100d657d055b5d1cde802690cfa99ae4..37fb2179daa3dea752185bf4504072a0fa296821 100644 (file)
@@ -65,29 +65,29 @@ implementation
 
     r_Common_SetLoading('GUI', 2 + 9 + 14);
 
-    MarkerID[FALSE] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/MARKER1');
-    MarkerID[TRUE] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/MARKER2');
+    MarkerID[FALSE] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/MARKER1', [TGLHints.txNoRepeat]);
+    MarkerID[TRUE] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/MARKER2', [TGLHints.txNoRepeat]);
 
     for i := 0 to 8 do
-      Box[i] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/BOX' + IntToStr(i + 1));
+      Box[i] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/BOX' + IntToStr(i + 1), []); // !!!
 
-    ScrollLeft := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SLEFT');
-    ScrollRight := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SRIGHT');
-    ScrollMiddle := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SMIDDLE');
-    ScrollMarker := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SMARKER');
+    ScrollLeft := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SLEFT', [TGLHints.txNoRepeat]);
+    ScrollRight := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SRIGHT', [TGLHints.txNoRepeat]);
+    ScrollMiddle := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SMIDDLE', []);
+    ScrollMarker := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SMARKER', [TGLHints.txNoRepeat]);
 
-    EditLeft := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/ELEFT');
-    EditRight := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/ERIGHT');
-    EditMiddle := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/EMIDDLE');
+    EditLeft := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/ELEFT', [TGLHints.txNoRepeat]);
+    EditRight := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/ERIGHT', [TGLHints.txNoRepeat]);
+    EditMiddle := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/EMIDDLE', []);
 
-    BScrollUp[true] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLUPA');
-    BScrollUp[false] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLUPU');
-    BScrollDown[true] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLDOWNA');
-    BScrollDown[false] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLDOWNU');
-    BScrollMiddle := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLMIDDLE');
+    BScrollUp[true] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLUPA', [TGLHints.txNoRepeat]);
+    BScrollUp[false] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLUPU', [TGLHints.txNoRepeat]);
+    BScrollDown[true] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLDOWNA', [TGLHints.txNoRepeat]);
+    BScrollDown[false] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLDOWNU', [TGLHints.txNoRepeat]);
+    BScrollMiddle := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/SCROLLMIDDLE', []);
 
-    LogoTex := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/MAINLOGO');
-    nopic := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/NOPIC');
+    LogoTex := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/MAINLOGO', [TGLHints.txNoRepeat]);
+    nopic := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/NOPIC', [TGLHints.txNoRepeat]);
   end;
 
   procedure r_GUI_Free;
index bc2f23684ccfc8aaf177570b291adde317a255da..31d29346a9aca33ae316560d0a53713709e7d72e 100644 (file)
@@ -63,10 +63,10 @@ implementation
 
   procedure r_LoadScreen_Load;
   begin
-    BarL := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LLEFT');
-    BarM := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LMIDDLE');
-    BarR := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LRIGHT');
-    BarF := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LMARKER');
+    BarL := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LLEFT', [TGLHints.txNoRepeat]);
+    BarM := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LMIDDLE', []);
+    BarR := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LRIGHT', [TGLHints.txNoRepeat]);
+    BarF := r_Textures_LoadFromFile(GameWAD + ':TEXTURES/LMARKER', []);
   end;
 
   procedure r_LoadScreen_Free;
index 95c76a39787fc0297e1ca2c408548c2323a7dff8..028c724541e5cd119949d364bef86ea4848e940d 100644 (file)
@@ -343,9 +343,9 @@ implementation
         Models[i].anim[d, a].base := nil;
         Models[i].anim[d, a].mask := nil;
         if m.anim[d, a].resource <> '' then
-          Models[i].anim[d, a].base := r_Textures_LoadMultiFromFileAndInfo(prefix + m.anim[d, a].resource, 64, 64, m.anim[d, a].frames, true);
+          Models[i].anim[d, a].base := r_Textures_LoadMultiFromFileAndInfo(prefix + m.anim[d, a].resource, 64, 64, m.anim[d, a].frames, [TGLHints.txNoRepeat], true);
         if m.anim[d, a].mask <> '' then
-          Models[i].anim[d, a].mask := r_Textures_LoadMultiFromFileAndInfo(prefix + m.anim[d, a].mask, 64, 64, m.anim[d, a].frames, true);
+          Models[i].anim[d, a].mask := r_Textures_LoadMultiFromFileAndInfo(prefix + m.anim[d, a].mask, 64, 64, m.anim[d, a].frames, [TGLHints.txNoRepeat], true);
       end
     end;
     {$IFDEF ENABLE_GIBS}
@@ -359,9 +359,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, 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, [TGLHints.txNoRepeat]) then
         begin
-          if r_Textures_LoadStreamFromFile(prefix + m.GibsMask, 32, 32, m.GibsCount, 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, [TGLHints.txNoRepeat]) then
           begin
             // ok
           end;
@@ -390,6 +390,7 @@ implementation
         w,
         h,
         count,
+        [TGLHints.txNoRepeat],
         False
       );
     end
@@ -415,6 +416,7 @@ implementation
         ItemAnim[i].w,
         ItemAnim[i].h,
         ItemAnim[i].anim.frames,
+        [TGLHints.txNoRepeat],
         false
       );
       Items[i].frame := 0;
@@ -428,7 +430,7 @@ implementation
           r_Map_LoadMonsterAnim(i, j, d);
       r_Common_StepLoading(1);
     end;
-    VileFire := r_Textures_LoadMultiFromFileAndInfo(GameWAD + ':TEXTURES/FIRE', 64, 128, VileFireAnim.frames);
+    VileFire := r_Textures_LoadMultiFromFileAndInfo(GameWAD + ':TEXTURES/FIRE', 64, 128, VileFireAnim.frames, [TGLHints.txNoRepeat]);
     // --------- player models --------- //
     if PlayerModelsArray <> nil then
     begin
@@ -446,7 +448,7 @@ implementation
     begin
       for j := 0 to W_POS_LAST do
         for k := 0 to W_ACT_LAST do
-          WeapTextures[i, j, k] := r_Textures_LoadFromFile(GameWAD + ':WEAPONS\' + WeapName[i] + WeapPos[j] + WeapAct[k]);
+          WeapTextures[i, j, k] := r_Textures_LoadFromFile(GameWAD + ':WEAPONS\' + WeapName[i] + WeapPos[j] + WeapAct[k], [TGLHints.txNoRepeat]);
       r_Common_StepLoading(1);
     end;
     // --------- gfx animations --------- //
@@ -455,7 +457,7 @@ implementation
       for i := 1 to R_GFX_LAST do
       begin
         if GFXAnim[i].anim.frames > 0 then
-          GFXTextures[i] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/' + GFXAnim[i].name, GFXAnim[i].w, GFXAnim[i].h, GFXAnim[i].anim.frames);
+          GFXTextures[i] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/' + GFXAnim[i].name, GFXAnim[i].w, GFXAnim[i].h, GFXAnim[i].anim.frames, [TGLHints.txNoRepeat]);
       end;
     {$ENDIF}
     // --------- shots --------- //
@@ -463,30 +465,30 @@ implementation
     for i := 0 to WEAPON_LAST do
     begin
       if ShotAnim[i].anim.frames > 0 then
-        ShotTextures[i] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/' + ShotAnim[i].name, ShotAnim[i].w, ShotAnim[i].h, ShotAnim[i].anim.frames);
+        ShotTextures[i] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/' + ShotAnim[i].name, ShotAnim[i].w, ShotAnim[i].h, ShotAnim[i].anim.frames, [TGLHints.txNoRepeat]);
     end;
     // --------- flags --------- //
     r_Common_SetLoading('Flags', 2);
     FlagTextures[FLAG_NONE] := nil;
-    FlagTextures[FLAG_RED]  := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/FLAGRED',  64, 64, 5);
-    FlagTextures[FLAG_BLUE] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/FLAGBLUE', 64, 64, 5);
-    // FlagTextures[FLAG_DOM]  := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/FLAGDOM',  64, 64, 8);
+    FlagTextures[FLAG_RED]  := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/FLAGRED',  64, 64, 5, [TGLHints.txNoRepeat]);
+    FlagTextures[FLAG_BLUE] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/FLAGBLUE', 64, 64, 5, [TGLHints.txNoRepeat]);
+    // FlagTextures[FLAG_DOM]  := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':TEXTURES/FLAGDOM',  64, 64, 8, [TGLHints.txNoRepeat]);
     // --------- shells --------- //
     {$IFDEF ENABLE_SHELLS}
       r_Common_SetLoading('Shells', SHELL_LAST + 1);
       for i := 0 to SHELL_LAST do
-        ShellTextures[i] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/' + ShellAnim[i].name);
+        ShellTextures[i] := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/' + ShellAnim[i].name, [TGLHints.txNoRepeat]);
     {$ENDIF}
     // --------- other --------- //
     r_Common_SetLoading('Effects', 3 * 2 + 3);
     for b := false to true do
     begin
       for i := 0 to 2 do
-        PunchTextures[b, i] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':WEAPONS/' + PunchName[b] + WeapPos[i], 64, 64, PunchAnim.frames);
+        PunchTextures[b, i] := r_Common_LoadTextureMultiFromFileAndInfo(GameWad + ':WEAPONS/' + PunchName[b] + WeapPos[i], 64, 64, PunchAnim.frames, [TGLHints.txNoRepeat]);
     end;
-    InvulPenta := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/PENTA');
-    IndicatorTexture := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/PLRIND');
-    TalkTexture := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/TALKBUBBLE');
+    InvulPenta := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/PENTA', [TGLHints.txNoRepeat]);
+    IndicatorTexture := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/PLRIND', [TGLHints.txNoRepeat]);
+    TalkTexture := r_Common_LoadTextureFromFile(GameWad + ':TEXTURES/TALKBUBBLE', [TGLHints.txNoRepeat]);
   end;
 
   procedure r_Map_Free;
@@ -545,7 +547,7 @@ implementation
           TEXTURE_NAME_ACID2: RenTextures[i].spec := TEXTURE_SPECIAL_ACID2;
           else
             RenTextures[i].spec := 0;
-            RenTextures[i].tex := r_Textures_LoadMultiTextFromFile(Textures[i].FullName, txt);
+            RenTextures[i].tex := r_Textures_LoadMultiTextFromFile(Textures[i].FullName, txt, []);
             if RenTextures[i].tex = nil then
               e_LogWritefln('r_Map_LoadTextures: failed to load texture: %s', [Textures[i].FullName])
             else
@@ -557,7 +559,7 @@ implementation
     if gMapInfo.SkyFullName <> '' then
     begin
       r_Common_SetLoading(_lc[I_LOAD_SKY], 1);
-      SkyTexture := r_Common_LoadTextureFromFile(gMapInfo.SkyFullName);
+      SkyTexture := r_Common_LoadTextureFromFile(gMapInfo.SkyFullName, [TGLHints.txNoRepeat]);
     end;
     plist.Clear;
   end;
index 4c1f097115acbf4a563cff0920db1970a0fc6225..7f52d9d74567f573656870c6f1a955114e214f24 100644 (file)
@@ -127,24 +127,24 @@ implementation
     r_LoadScreen_Load;
     r_Common_Load;
     r_Common_SetLoading('HUD Textures', 5 + (WP_LAST + 1) + 11);
-    hud :=  r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/HUD');
-    hudbg :=  r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/HUDBG');
-    hudhp[false] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/MED2');
-    hudhp[true] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/BMED');
-    hudap := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/ARMORHUD');
+    hud :=  r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/HUD', [TGLHints.txNoRepeat]);
+    hudbg :=  r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/HUDBG', []);
+    hudhp[false] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/MED2', [TGLHints.txNoRepeat]);
+    hudhp[true] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/BMED', [TGLHints.txNoRepeat]);
+    hudap := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/ARMORHUD', [TGLHints.txNoRepeat]);
     for i := 0 to WP_LAST do
-      hudwp[i] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/' + WeapName[i]);
-    hudkey[0] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/KEYR');
-    hudkey[1] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/KEYG');
-    hudkey[2] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/KEYB');
-    hudair := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/AIRBAR');
-    hudjet := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/JETBAR');
-    hudrflag := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_R_BASE');
-    hudrflags := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_R_STOLEN');
-    hudrflagd := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_R_DROP');
-    hudbflag := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_B_BASE');
-    hudbflags := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_B_STOLEN');
-    hudbflagd := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_B_DROP');
+      hudwp[i] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/' + WeapName[i], [TGLHints.txNoRepeat]);
+    hudkey[0] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/KEYR', [TGLHints.txNoRepeat]);
+    hudkey[1] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/KEYG', [TGLHints.txNoRepeat]);
+    hudkey[2] := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/KEYB', [TGLHints.txNoRepeat]);
+    hudair := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/AIRBAR', [TGLHints.txNoRepeat]);
+    hudjet := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/JETBAR', [TGLHints.txNoRepeat]);
+    hudrflag := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_R_BASE', [TGLHints.txNoRepeat]);
+    hudrflags := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_R_STOLEN', [TGLHints.txNoRepeat]);
+    hudrflagd := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_R_DROP', [TGLHints.txNoRepeat]);
+    hudbflag := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_B_BASE', [TGLHints.txNoRepeat]);
+    hudbflags := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_B_STOLEN', [TGLHints.txNoRepeat]);
+    hudbflagd := r_Common_LoadTextureFromFile(GameWAD + ':TEXTURES/FLAGHUD_B_DROP', [TGLHints.txNoRepeat]);
     r_Console_Load;
     r_Map_Load;
     {$IFDEF ENABLE_MENU}
index 24ea1642e7aa37ec447a509ed4ce2c02627f7bbf..34b5b9e7027c8863ad32a0777d22297047fb5dc2 100644 (file)
@@ -29,6 +29,9 @@ interface
   ;
 
   type
+    TGLHints = (txNoRepeat);
+    TGLHintsSet = set of TGLHints;
+
     TGLAtlas = class;
 
     TGLAtlasNode = class (TAtlasNode)
@@ -65,6 +68,7 @@ interface
         mHeight: Integer;
         mCols: Integer;
         mTile: array of TGLAtlasNode;
+        mHints: TGLHintsSet;
 
       public
         destructor Destroy; override;
@@ -77,6 +81,7 @@ interface
         property height: Integer read mHeight;
         property cols: Integer read mCols;
         property lines: Integer read GetLines;
+        property hints: TGLHintsSet read mHints;
     end;
 
     TGLMultiTexture = class
@@ -125,12 +130,12 @@ interface
   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; log: Boolean = True): TGLMultiTexture;
-  function r_Textures_LoadMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLTexture;
+  function r_Textures_LoadMultiFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
 
-  function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count, cw: 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; hints: TGLHintsSet; log: Boolean = True): Boolean;
 
   function r_Textures_LoadFontFromFile (const filename: AnsiString; constref f: TFontInfo; font2enc: TConvProc; log: Boolean = true): TGLFont;
 
@@ -146,8 +151,9 @@ implementation
 
   var
     r_GL_MaxTexSize: WORD;
+    r_GL_RepeatOpt: Boolean;
     maxTileSize: Integer;
-    atl: array of TGLAtlas;
+    atl, ratl: array of TGLAtlas;
 
   (* --------- TGLAtlasNode --------- *)
 
@@ -216,6 +222,8 @@ implementation
     if id <> 0 then
     begin
       glBindTexture(GL_TEXTURE_2D, id);
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
@@ -238,6 +246,20 @@ implementation
     end;
   end;
 
+  function r_Textures_AllocRepeatAtlas (w, h: Integer): TGLAtlas;
+    var i: Integer; id: GLuint;
+  begin
+    result := nil;
+    id := r_Textures_AllocHWTexture(w, h);
+    if id <> 0 then
+    begin
+      i := Length(ratl);
+      SetLength(ratl, i + 1);
+      ratl[i] := TGLAtlas.Create(w, h, id);
+      result := ratl[i];
+    end;
+  end;
+
   function r_Textures_AllocNode (w, h: Integer): TGLAtlasNode;
     var i: Integer; n: TGLAtlasNode; a: TGLAtlas;
   begin
@@ -260,22 +282,56 @@ implementation
     result := n
   end;
 
+  function r_Textures_AllocRepeatNode (w, h: Integer): TGLAtlasNode;
+    var i: Integer; n: TGLAtlasNode; a: TGLAtlas;
+  begin
+    n := nil; a := nil;
+    if ratl <> nil then
+    begin
+      i := High(ratl);
+      while (i >= 0) and (ratl[i] <> nil) do DEC(i);
+      if i >= 0 then a := ratl[i];
+    end;
+    if a = nil then a := r_Textures_AllocRepeatAtlas(w, h);
+    if a <> nil then
+    begin
+      n := a.Alloc(w, h);
+      if n = nil then
+      begin
+        i := High(ratl); while (i >= 0) and (ratl[i] <> a) do DEC(i);
+        if i >= 0 then ratl[i] := nil;
+        r_Common_FreeAndNil(a);
+      end;
+    end;
+    result := n
+  end;
+
   (* --------- TGLTexture --------- *)
 
   destructor TGLTexture.Destroy;
-    var i: Integer;
+    var i: Integer; a: TGLAtlas;
   begin
     if self.mTile <> nil then
     begin
-      for i := 0 to High(self.mTile) do
+      if TGLHints.txNoRepeat in self.hints then (* non repeatable texture -> delete tiles only *)
       begin
-        if self.mTile[i] <> nil then
+        for i := 0 to High(self.mTile) do
         begin
-          self.mTile[i].Dealloc;
-          self.mTile[i] := nil;
-        end;
+          if self.mTile[i] <> nil then
+          begin
+            self.mTile[i].Dealloc;
+            self.mTile[i] := nil
+          end
+        end
+      end
+      else (* repeatable texture -> delete whole atlas *)
+      begin
+        a := self.mTile[0].base;
+        i := High(ratl); while (i >= 0) and (ratl[i] <> a) do DEC(i);
+        if i >= 0 then ratl[i] := nil;
+        r_Common_FreeAndNil(a);
       end;
-      self.mTile := nil;
+      SetLength(self.mTile, 0);
     end;
     inherited;
   end;
@@ -299,29 +355,44 @@ implementation
     ASSERT(result <> nil)
   end;
 
-  function r_Textures_Alloc (w, h: Integer): TGLTexture;
+  function r_Textures_Alloc (w, h: Integer; hints: TGLHintsSet): TGLTexture;
     var x, y, mw, mh, cols, lines: Integer; t: TGLTexture;
   begin
     ASSERT(w > 0);
     ASSERT(h > 0);
-    cols := (w + maxTileSize - 1) div maxTileSize;
-    lines := (h + maxTileSize - 1) div maxTileSize;
-    t := TGLTexture.Create;
-    t.mWidth := w;
-    t.mHeight := h;
-    t.mCols := cols;
-    // t.mLines := lines;
-    SetLength(t.mTile, cols * lines);
-    for y := 0 to lines - 1 do
-    begin
-      mh := Min(maxTileSize, h - y * maxTileSize);
-      ASSERT(mh > 0);
-      for x := 0 to cols - 1 do
+    if TGLHints.txNoRepeat in hints then
+    begin
+      cols := (w + maxTileSize - 1) div maxTileSize;
+      lines := (h + maxTileSize - 1) div maxTileSize;
+      t := TGLTexture.Create;
+      t.mWidth := w;
+      t.mHeight := h;
+      t.mCols := cols;
+      // t.mLines := lines;
+      t.mHints := hints;
+      SetLength(t.mTile, cols * lines);
+      for y := 0 to lines - 1 do
       begin
-        mw := Min(maxTileSize, w - x * maxTileSize);
-        ASSERT(mw > 0);
-        t.mTile[y * cols + x] := r_Textures_AllocNode(mw, mh);
-      end
+        mh := Min(maxTileSize, h - y * maxTileSize);
+        ASSERT(mh > 0);
+        for x := 0 to cols - 1 do
+        begin
+          mw := Min(maxTileSize, w - x * maxTileSize);
+          ASSERT(mw > 0);
+          t.mTile[y * cols + x] := r_Textures_AllocNode(mw, mh);
+        end
+      end;
+    end
+    else
+    begin
+      t := TGLTexture.Create;
+      t.mWidth := w;
+      t.mHeight := h;
+      t.mCols := 1;
+      // t.mLines := 1
+      t.mHints := hints;
+      SetLength(t.mTile, 1);
+      t.mTile[0] := r_Textures_AllocRepeatNode(w, h);
     end;
     result := t;
   end;
@@ -418,6 +489,17 @@ implementation
       end;
     end;
     SetLength(atl, 0);
+
+    if ratl <> nil then
+    begin
+      for i := 0 to High(ratl) do
+      begin
+        glDeleteTextures(1, @ratl[i].id);
+        ratl[i].id := 0;
+        r_Common_FreeAndNil(ratl[i]);
+      end;
+    end;
+    SetLength(ratl, 0);
   end;
 
   function r_Textures_FixImageData (var img: TImageData): Boolean;
@@ -428,13 +510,34 @@ implementation
         result := true;
   end;
 
-  function r_Textures_LoadFromImage (var img: TImageData): TGLTexture;
+  function r_Textures_ValidRepeatTexture (w, h: Integer; hints: TGLHintsSet): Boolean;
+  begin
+    result := r_GL_RepeatOpt and
+              not (TGLHints.txNoRepeat in hints) and
+              (w <= maxTileSize) and
+              (h <= maxTileSize) and
+              IsPOT(w) and
+              IsPOT(h)
+  end;
+
+  function r_Textures_LoadFromImage (var img: TImageData; hints: TGLHintsSet): TGLTexture; // !!!
     var t: TGLTexture; n: TGLAtlasNode; c: TDynImageDataArray; cw, ch, i, j: LongInt;
   begin
     result := nil;
-    if SplitImage(img, c, maxTileSize, maxTileSize, cw, ch, False) then
+    if r_Textures_ValidRepeatTexture(img.width, img.height, hints) then
+    begin
+      t := r_Textures_Alloc(img.width, img.height, hints - [TGLHints.txNoRepeat]);
+      if t <> nil then
+      begin
+        n := t.GetTile(0, 0);
+        ASSERT(n <> nil);
+        r_Textures_UpdateNode(n, img.bits, 0, 0, n.width, n.height);
+        result := t
+      end
+    end
+    else if SplitImage(img, c, maxTileSize, maxTileSize, cw, ch, False) then
     begin
-      t := r_Textures_Alloc(img.width, img.height);
+      t := r_Textures_Alloc(img.width, img.height, hints + [TGLHints.txNoRepeat]);
       if t <> nil then
       begin
         ASSERT(cw = t.cols);
@@ -454,7 +557,7 @@ implementation
     end;
   end;
 
-  function r_Textures_LoadFromMemory (data: Pointer; size: LongInt): TGLTexture;
+  function r_Textures_LoadFromMemory (data: Pointer; size: LongInt; hints: TGLHintsSet): TGLTexture;
     var img: TImageData;
   begin
     result := nil;
@@ -464,14 +567,14 @@ implementation
       try
         if LoadImageFromMemory(data, size, img) then
           if r_Textures_FixImageData(img) then
-            result := r_Textures_LoadFromImage(img)
+            result := r_Textures_LoadFromImage(img, hints)
       except
       end;
       FreeImage(img);
     end;
   end;
 
-  function r_Textures_LoadFromFile (const filename: AnsiString; log: Boolean = True): TGLTexture;
+  function r_Textures_LoadFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLTexture;
     var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
   begin
     result := nil;
@@ -482,14 +585,14 @@ implementation
       resName := g_ExtractFilePathName(filename);
       if wad.GetResource(resName, data, size, log) then
       begin
-        result := r_Textures_LoadFromMemory(data, size);
+        result := r_Textures_LoadFromMemory(data, size, hints);
         FreeMem(data);
       end;
       wad.Free
     end
   end;
 
-  function r_Textures_LoadMultiFromImageAndInfo (var img: TImageData; w, h, c: Integer): TGLMultiTexture;
+  function r_Textures_LoadMultiFromImageAndInfo (var img: TImageData; w, h, c: Integer; hints: TGLHintsSet): TGLMultiTexture;
     var t: TImageData; a: array of TGLTexture; i: Integer; m: TGLMultiTexture;
   begin
     ASSERT(w >= 0);
@@ -502,7 +605,7 @@ implementation
       InitImage(t);
       if NewImage(w, h, img.Format, t) then
         if CopyRect(img, w * i, 0, w, h, t, 0, 0) then
-          a[i] := r_Textures_LoadFromImage(t);
+          a[i] := r_Textures_LoadFromImage(t, hints);
       ASSERT(a[i] <> nil);
       FreeImage(t);
     end;
@@ -512,7 +615,7 @@ implementation
     result := m;
   end;
 
-  function r_Textures_LoadMultiFromDataAndInfo (data: Pointer; size: LongInt; w, h, c: Integer): TGLMultiTexture;
+  function r_Textures_LoadMultiFromDataAndInfo (data: Pointer; size: LongInt; w, h, c: Integer; hints: TGLHintsSet): TGLMultiTexture;
     var img: TImageData;
   begin
     ASSERT(w > 0);
@@ -525,7 +628,7 @@ implementation
       try
         if LoadImageFromMemory(data, size, img) then
           if r_Textures_FixImageData(img) then
-            result := r_Textures_LoadMultiFromImageAndInfo(img, w, h, c)
+            result := r_Textures_LoadMultiFromImageAndInfo(img, w, h, c, hints)
       except
       end;
       FreeImage(img);
@@ -554,7 +657,7 @@ implementation
     end;
   end;
 
-  function r_Textures_LoadMultiFromWad (wad: TWADFile; var txt: TAnimTextInfo): TGLMultiTexture;
+  function r_Textures_LoadMultiFromWad (wad: TWADFile; var txt: TAnimTextInfo; hints: TGLHintsSet): TGLMultiTexture;
     var data: Pointer; size: LongInt; img: TImageData;
   begin
     ASSERT(wad <> nil);
@@ -570,7 +673,7 @@ implementation
           try
             if LoadImageFromMemory(data, size, img) then
               if r_Textures_FixImageData(img) then
-                result := r_Textures_LoadMultiFromImageAndInfo(img, txt.w, txt.h, txt.anim.frames);
+                result := r_Textures_LoadMultiFromImageAndInfo(img, txt.w, txt.h, txt.anim.frames, hints);
           finally
             FreeMem(data);
           end;
@@ -582,13 +685,13 @@ implementation
     end;
   end;
 
-  function r_Textures_LoadMultiFromMemory (data: Pointer; size: LongInt; var txt: TAnimTextInfo): TGLMultiTexture;
+  function r_Textures_LoadMultiFromMemory (data: Pointer; size: LongInt; var txt: TAnimTextInfo; hints: TGLHintsSet): TGLMultiTexture;
     var wad: TWADFile; t: TGLTexture; m: TGLMultiTexture;
   begin
     result := nil;
     if (data <> nil) and (size > 0) then
     begin
-      t := r_Textures_LoadFromMemory(data, size);
+      t := r_Textures_LoadFromMemory(data, size, hints);
       if t <> nil then
       begin
         m := TGLMultiTexture.Create();
@@ -608,14 +711,14 @@ implementation
         wad := TWADFile.Create();
         if wad.ReadMemory(data, size) then
         begin
-          result := r_Textures_LoadMultiFromWad(wad, txt);
+          result := r_Textures_LoadMultiFromWad(wad, txt, hints);
           wad.Free;
         end
       end
     end
   end;
 
-  function r_Textures_LoadMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadMultiTextFromFile (const filename: AnsiString; var txt: TAnimTextInfo; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
     var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
   begin
     result := nil;
@@ -626,20 +729,20 @@ implementation
       resName := g_ExtractFilePathName(filename);
       if wad.GetResource(resName, data, size, log) then
       begin
-        result := r_Textures_LoadMultiFromMemory(data, size, txt);
+        result := r_Textures_LoadMultiFromMemory(data, size, txt, hints);
         FreeMem(data);
       end;
       wad.Free
     end
   end;
 
-  function r_Textures_LoadMultiFromFile (const filename: AnsiString; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadMultiFromFile (const filename: AnsiString; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
     var txt: TAnimTextInfo;
   begin
-    result := r_Textures_LoadMultiTextFromFile(filename, txt, log);
+    result := r_Textures_LoadMultiTextFromFile(filename, txt, hints, log);
   end;
 
-  function r_Textures_LoadMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; log: Boolean = True): TGLMultiTexture;
+  function r_Textures_LoadMultiFromFileAndInfo (const filename: AnsiString; w, h, count: Integer; hints: TGLHintsSet; log: Boolean = True): TGLMultiTexture;
     var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
   begin
     ASSERT(w > 0);
@@ -653,7 +756,7 @@ implementation
       resName := g_ExtractFilePathName(filename);
       if wad.GetResource(resName, data, size, log) then
       begin
-        result := r_Textures_LoadMultiFromDataAndInfo(data, size, w, h, count);
+        result := r_Textures_LoadMultiFromDataAndInfo(data, size, w, h, count, hints);
         FreeMem(data);
       end;
       wad.Free
@@ -717,7 +820,7 @@ implementation
     end;
   end;
 
-  function r_Textures_LoadStreamFromImage (var img: TImageData; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray): Boolean;
+  function r_Textures_LoadStreamFromImage (var img: TImageData; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray; hints: TGLHintsSet): Boolean;
     var i, x, y: Integer; t: TImageData;
   begin
     ASSERT(w >= 0);
@@ -739,7 +842,7 @@ implementation
         begin
           if rs <> nil then
             rs[i] := r_Textures_GetRect(t);
-          st[i] := r_Textures_LoadFromImage(t);
+          st[i] := r_Textures_LoadFromImage(t, hints);
         end;
       end;
       ASSERT(st[i] <> nil);
@@ -747,7 +850,7 @@ implementation
     end;
   end;
 
-  function r_Textures_LoadStreamFromMemory (data: Pointer; size: LongInt; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray): Boolean;
+  function r_Textures_LoadStreamFromMemory (data: Pointer; size: LongInt; w, h, c, cw: Integer; st: TGLTextureArray; rs: TRectArray; hints: TGLHintsSet): Boolean;
     var img: TImageData;
   begin
     ASSERT(w >= 0);
@@ -765,7 +868,7 @@ implementation
         begin
           if r_Textures_FixImageData(img) then
           begin
-            result := r_Textures_LoadStreamFromImage(img, w, h, c, cw, st, rs)
+            result := r_Textures_LoadStreamFromImage(img, w, h, c, cw, st, rs, hints)
           end;
         end;
       except
@@ -774,7 +877,7 @@ implementation
     end;
   end;
 
-  function r_Textures_LoadStreamFromFile (const filename: AnsiString; w, h, count, cw: 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; hints: TGLHintsSet; log: Boolean = True): Boolean;
     var wad: TWADFile; wadName, resName: AnsiString; data: Pointer; size: Integer;
   begin
     ASSERT(w > 0);
@@ -791,7 +894,7 @@ implementation
       resName := g_ExtractFilePathName(filename);
       if wad.GetResource(resName, data, size, log) then
       begin
-        result := r_Textures_LoadStreamFromMemory(data, size, w, h, count, cw, st, rs);
+        result := r_Textures_LoadStreamFromMemory(data, size, w, h, count, cw, st, rs, hints);
         FreeMem(data);
       end;
       wad.Free
@@ -805,7 +908,7 @@ implementation
   begin
     result := nil;
     SetLength(st, 256);
-    if r_Textures_LoadStreamFromFile(filename, f.w, f.h, 256, 16, st, nil, log) then
+    if r_Textures_LoadStreamFromFile(filename, f.w, f.h, 256, 16, st, nil, [TGLHints.txNoRepeat], log) then
     begin
       font := TGLFont.Create();
       font.info := f;
@@ -868,5 +971,7 @@ implementation
 
 initialization
   conRegVar('r_gl_maxtexsize', @r_GL_MaxTexSize, '', '');
+  conRegVar('r_gl_repeat', @r_GL_RepeatOpt, '', '');
   r_GL_MaxTexSize := 0; // default is automatic value
+  r_GL_RepeatOpt := true;
 end.