1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../../../shared/a_modes.inc}
26 g_base
, g_animations
, // TRectHW, TAnimInfo
34 TGLAtlasNode
= class (TAtlasNode
)
39 constructor Create (base
: TGLAtlas
);
40 destructor Destroy
; override;
42 function GetID (): GLuint
;
44 property base
: TGLAtlas read mBase
;
45 property id
: GLuint read GetID
;
48 TGLAtlas
= class (TAtlas
)
53 constructor Create (ww
, hh
: Integer; id
: GLuint
);
54 destructor Destroy
; override;
56 function CreateNode (): TGLAtlasNode
; override;
57 function Alloc (ww
, hh
: Integer): TGLAtlasNode
; overload
;
59 property id
: GLuint read mID write mID default
0;
67 mTile
: array of TGLAtlasNode
;
70 destructor Destroy
; override;
72 function GetTile (col
, line
: Integer): TGLAtlasNode
;
74 function GetLines (): Integer; inline;
76 property width
: Integer read mWidth
;
77 property height
: Integer read mHeight
;
78 property cols
: Integer read mCols
;
79 property lines
: Integer read GetLines
;
82 TGLMultiTexture
= class
84 mTexture
: array of TGLTexture
;
87 destructor Destroy
; override;
89 function GetWidth (): Integer; inline;
90 function GetHeight (): Integer; inline;
91 function GetCount (): Integer; inline;
92 function GetTexture (i
: Integer): TGLTexture
; {inline;}
94 property width
: Integer read GetWidth
;
95 property height
: Integer read GetHeight
;
96 property count
: Integer read GetCount
;
99 TGLTextureArray
= array of TGLTexture
;
101 TRectArray
= array of TRectWH
;
103 TGLFont
= class sealed (TFont
)
109 destructor Destroy
; override;
110 function GetChar (c
: AnsiChar): TGLTexture
;
111 function GetWidth (c
: AnsiChar): Integer;
112 function GetMaxWidth (): Integer;
113 function GetMaxHeight (): Integer;
114 function GetSpace (): Integer;
117 TAnimTextInfo
= record
123 procedure r_Textures_Initialize
;
124 procedure r_Textures_Finalize
;
126 function r_Textures_LoadFromFile (const filename
: AnsiString; log
: Boolean = True): TGLTexture
;
127 function r_Textures_LoadMultiFromFile (const filename
: AnsiString; log
: Boolean = True): TGLMultiTexture
;
128 function r_Textures_LoadMultiFromFileAndInfo (const filename
: AnsiString; w
, h
, count
: Integer; log
: Boolean = True): TGLMultiTexture
;
129 function r_Textures_LoadMultiTextFromFile (const filename
: AnsiString; var txt
: TAnimTextInfo
; log
: Boolean = True): TGLMultiTexture
;
131 function r_Textures_LoadStreamFromFile (const filename
: AnsiString; w
, h
, count
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
; log
: Boolean = True): Boolean;
133 function r_Textures_LoadFontFromFile (const filename
: AnsiString; constref f
: TFontInfo
; skipch
: Integer; log
: Boolean = true): TGLFont
;
139 e_log
, e_res
, WADReader
, Config
,
140 g_console
, // cvar declaration
141 Imaging
, ImagingTypes
, ImagingUtility
145 r_GL_MaxTexSize
: WORD;
146 maxTileSize
: Integer;
147 atl
: array of TGLAtlas
;
149 (* --------- TGLAtlasNode --------- *)
151 constructor TGLAtlasNode
.Create (base
: TGLAtlas
);
158 destructor TGLAtlasNode
.Destroy
;
163 function TGLAtlasNode
.GetID (): GLuint
;
165 result
:= self
.base
.id
168 procedure r_Textures_UpdateNode (n
: TGLAtlasNode
; data
: Pointer; x
, y
, w
, h
: Integer);
172 ASSERT(n
.base
<> nil);
176 ASSERT(n
.l
+ x
+ w
- 1 <= n
.r
);
177 ASSERT(n
.t
+ y
+ h
- 1 <= n
.b
);
179 glBindTexture(GL_TEXTURE_2D
, n
.id
);
180 glTexSubImage2D(GL_TEXTURE_2D
, 0, n
.l
+ x
, n
.t
+ y
, w
, h
, GL_RGBA
, GL_UNSIGNED_BYTE
, data
);
181 glBindTexture(GL_TEXTURE_2D
, 0);
184 (* --------- TGLAtlas --------- *)
186 constructor TGLAtlas
.Create (ww
, hh
: Integer; id
: GLuint
);
190 inherited Create(ww
, hh
);
194 destructor TGLAtlas
.Destroy
;
199 function TGLAtlas
.CreateNode (): TGLAtlasNode
;
201 result
:= TGLAtlasNode
.Create(self
);
204 function TGLAtlas
.Alloc (ww
, hh
: Integer): TGLAtlasNode
;
206 result
:= TGLAtlasNode(inherited Alloc(ww
, hh
));
209 function r_Textures_AllocHWTexture (w
, h
: Integer): GLuint
;
212 glGenTextures(1, @id
);
215 glBindTexture(GL_TEXTURE_2D
, id
);
216 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
217 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
218 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, w
, h
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, nil);
219 glBindTexture(GL_TEXTURE_2D
, 0);
224 function r_Textures_AllocAtlas (): TGLAtlas
;
225 var i
: Integer; id
: GLuint
;
228 id
:= r_Textures_AllocHWTexture(maxTileSize
, maxTileSize
);
232 SetLength(atl
, i
+ 1);
233 atl
[i
] := TGLAtlas
.Create(maxTileSize
, maxTileSize
, id
);
238 function r_Textures_AllocNode (w
, h
: Integer): TGLAtlasNode
;
239 var i
: Integer; n
: TGLAtlasNode
; a
: TGLAtlas
;
245 while (i
>= 0) and (n
= nil) do
247 n
:= atl
[i
].Alloc(w
, h
);
253 a
:= r_Textures_AllocAtlas();
260 (* --------- TGLTexture --------- *)
262 destructor TGLTexture
.Destroy
;
265 if self
.mTile
<> nil then
267 for i
:= 0 to High(self
.mTile
) do
269 if self
.mTile
[i
] <> nil then
271 self
.mTile
[i
].Dealloc
;
272 self
.mTile
[i
] := nil;
280 function TGLTexture
.GetLines (): Integer;
282 ASSERT(self
.mTile
<> nil);
283 result
:= Length(self
.mTile
) div self
.mCols
286 function TGLTexture
.GetTile (col
, line
: Integer): TGLAtlasNode
;
290 ASSERT(col
<= mCols
);
291 ASSERT(self
.mTile
<> nil);
292 i
:= line
* mCols
+ col
;
294 ASSERT(i
< Length(mTile
));
296 ASSERT(result
<> nil)
299 function r_Textures_Alloc (w
, h
: Integer): TGLTexture
;
300 var x
, y
, mw
, mh
, cols
, lines
: Integer; t
: TGLTexture
;
304 cols
:= (w
+ maxTileSize
- 1) div maxTileSize
;
305 lines
:= (h
+ maxTileSize
- 1) div maxTileSize
;
306 t
:= TGLTexture
.Create
;
310 // t.mLines := lines;
311 SetLength(t
.mTile
, cols
* lines
);
312 for y
:= 0 to lines
- 1 do
314 mh
:= Min(maxTileSize
, h
- y
* maxTileSize
);
316 for x
:= 0 to cols
- 1 do
318 mw
:= Min(maxTileSize
, w
- x
* maxTileSize
);
320 t
.mTile
[y
* cols
+ x
] := r_Textures_AllocNode(mw
, mh
);
326 (* --------- TGLMultiTexture --------- *)
328 destructor TGLMultiTexture
.Destroy
;
331 for i
:= 0 to self
.count
- 1 do
332 self
.mTexture
[i
].Free
;
333 self
.mTexture
:= nil;
337 function TGLMultiTexture
.GetWidth (): Integer;
339 result
:= self
.mTexture
[0].width
342 function TGLMultiTexture
.GetHeight (): Integer;
344 result
:= self
.mTexture
[0].height
347 function TGLMultiTexture
.GetCount (): Integer;
349 result
:= Length(self
.mTexture
)
352 function TGLMultiTexture
.GetTexture (i
: Integer): TGLTexture
;
355 ASSERT(i
< self
.count
);
356 result
:= self
.mTexture
[i
];
357 ASSERT(result
<> nil);
360 (* --------- Init / Fin --------- *)
362 function IsPOT (v
: LongWord): Boolean;
364 result
:= (v
<> 0) and ((v
and (v
- 1)) = 0)
367 function NextPOT (v
: LongWord): LongWord;
379 function r_Textures_GetMaxHardwareSize (): Integer;
382 if r_GL_MaxTexSize
<= 0 then
384 // auto, max possible reccomended by driver
385 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, @size
);
386 if size
< 1 then size
:= 64;
391 if IsPOT(r_GL_MaxTexSize
) then
392 size
:= r_GL_MaxTexSize
394 size
:= NextPOT(r_GL_MaxTexSize
);
399 procedure r_Textures_Initialize
;
401 maxTileSize
:= r_Textures_GetMaxHardwareSize();
402 e_LogWritefln('TEXTURE SIZE: %s', [maxTileSize
]);
405 procedure r_Textures_Finalize
;
410 for i
:= 0 to High(atl
) do
412 glDeleteTextures(1, @atl
[i
].id
);
420 function r_Textures_FixImageData (var img
: TImageData
): Boolean;
423 if ConvertImage(img
, TImageFormat
.ifA8R8G8B8
) then
424 if SwapChannels(img
, ChannelRed
, ChannelBlue
) then // wtf
428 function r_Textures_LoadFromImage (var img
: TImageData
): TGLTexture
;
429 var t
: TGLTexture
; n
: TGLAtlasNode
; c
: TDynImageDataArray
; cw
, ch
, i
, j
: LongInt;
432 if SplitImage(img
, c
, maxTileSize
, maxTileSize
, cw
, ch
, False) then
434 t
:= r_Textures_Alloc(img
.width
, img
.height
);
438 ASSERT(ch
= t
.lines
);
439 for j
:= 0 to ch
- 1 do
441 for i
:= 0 to cw
- 1 do
443 n
:= t
.GetTile(i
, j
);
445 r_Textures_UpdateNode(n
, c
[j
* cw
+ i
].bits
, 0, 0, n
.width
, n
.height
)
450 FreeImagesInArray(c
);
454 function r_Textures_LoadFromMemory (data
: Pointer; size
: LongInt): TGLTexture
;
458 if (data
<> nil) and (size
> 0) then
462 if LoadImageFromMemory(data
, size
, img
) then
463 if r_Textures_FixImageData(img
) then
464 result
:= r_Textures_LoadFromImage(img
)
471 function r_Textures_LoadFromFile (const filename
: AnsiString; log
: Boolean = True): TGLTexture
;
472 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
475 wadName
:= g_ExtractWadName(filename
);
476 wad
:= TWADFile
.Create();
477 if wad
.ReadFile(wadName
) then
479 resName
:= g_ExtractFilePathName(filename
);
480 if wad
.GetResource(resName
, data
, size
, log
) then
482 result
:= r_Textures_LoadFromMemory(data
, size
);
489 function r_Textures_LoadMultiFromImageAndInfo (var img
: TImageData
; w
, h
, c
: Integer): TGLMultiTexture
;
490 var t
: TImageData
; a
: array of TGLTexture
; i
: Integer; m
: TGLMultiTexture
;
497 for i
:= 0 to c
- 1 do
500 if NewImage(w
, h
, img
.Format
, t
) then
501 if CopyRect(img
, w
* i
, 0, w
, h
, t
, 0, 0) then
502 a
[i
] := r_Textures_LoadFromImage(t
);
506 m
:= TGLMultiTexture
.Create();
508 ASSERT(m
.mTexture
<> nil);
512 function r_Textures_LoadMultiFromDataAndInfo (data
: Pointer; size
: LongInt; w
, h
, c
: Integer): TGLMultiTexture
;
519 if (data
<> nil) and (size
> 0) then
523 if LoadImageFromMemory(data
, size
, img
) then
524 if r_Textures_FixImageData(img
) then
525 result
:= r_Textures_LoadMultiFromImageAndInfo(img
, w
, h
, c
)
532 function r_Textures_LoadTextFromMemory (data
: Pointer; size
: LongInt; var txt
: TAnimTextInfo
): Boolean;
538 cfg
:= TConfig
.CreateMem(data
, size
);
541 txt
.name
:= cfg
.ReadStr('', 'resource', '');
542 txt
.w
:= MAX(0, cfg
.ReadInt('', 'framewidth', 0));
543 txt
.h
:= MAX(0, cfg
.ReadInt('', 'frameheight', 0));
544 txt
.anim
.loop
:= true;
545 txt
.anim
.delay
:= MAX(0, cfg
.ReadInt('', 'waitcount', 0));
546 txt
.anim
.frames
:= MAX(0, cfg
.ReadInt('', 'framecount', 0));
547 txt
.anim
.back
:= cfg
.ReadBool('', 'backanim', false);
549 result
:= (txt
.name
<> '') and (txt
.w
> 0) and (txt
.h
> 0) and (txt
.anim
.delay
> 0) and (txt
.anim
.frames
> 0);
554 function r_Textures_LoadMultiFromWad (wad
: TWADFile
; var txt
: TAnimTextInfo
): TGLMultiTexture
;
555 var data
: Pointer; size
: LongInt; img
: TImageData
;
559 if wad
.GetResource('TEXT/ANIM', data
, size
) then
561 if r_Textures_LoadTextFromMemory(data
, size
, txt
) then
564 if wad
.GetResource('TEXTURES/' + txt
.name
, data
, size
) then
568 if LoadImageFromMemory(data
, size
, img
) then
569 if r_Textures_FixImageData(img
) then
570 result
:= r_Textures_LoadMultiFromImageAndInfo(img
, txt
.w
, txt
.h
, txt
.anim
.frames
);
582 function r_Textures_LoadMultiFromMemory (data
: Pointer; size
: LongInt; var txt
: TAnimTextInfo
): TGLMultiTexture
;
583 var wad
: TWADFile
; t
: TGLTexture
; m
: TGLMultiTexture
;
586 if (data
<> nil) and (size
> 0) then
588 t
:= r_Textures_LoadFromMemory(data
, size
);
591 m
:= TGLMultiTexture
.Create();
592 SetLength(m
.mTexture
, 1);
597 txt
.anim
.loop
:= true;
599 txt
.anim
.frames
:= 1;
600 txt
.anim
.back
:= false;
603 else if IsWadData(data
, size
) then
605 wad
:= TWADFile
.Create();
606 if wad
.ReadMemory(data
, size
) then
608 result
:= r_Textures_LoadMultiFromWad(wad
, txt
);
615 function r_Textures_LoadMultiTextFromFile (const filename
: AnsiString; var txt
: TAnimTextInfo
; log
: Boolean = True): TGLMultiTexture
;
616 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
619 wadName
:= g_ExtractWadName(filename
);
620 wad
:= TWADFile
.Create();
621 if wad
.ReadFile(wadName
) then
623 resName
:= g_ExtractFilePathName(filename
);
624 if wad
.GetResource(resName
, data
, size
, log
) then
626 result
:= r_Textures_LoadMultiFromMemory(data
, size
, txt
);
633 function r_Textures_LoadMultiFromFile (const filename
: AnsiString; log
: Boolean = True): TGLMultiTexture
;
634 var txt
: TAnimTextInfo
;
636 result
:= r_Textures_LoadMultiTextFromFile(filename
, txt
, log
);
639 function r_Textures_LoadMultiFromFileAndInfo (const filename
: AnsiString; w
, h
, count
: Integer; log
: Boolean = True): TGLMultiTexture
;
640 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
646 wadName
:= g_ExtractWadName(filename
);
647 wad
:= TWADFile
.Create();
648 if wad
.ReadFile(wadName
) then
650 resName
:= g_ExtractFilePathName(filename
);
651 if wad
.GetResource(resName
, data
, size
, log
) then
653 result
:= r_Textures_LoadMultiFromDataAndInfo(data
, size
, w
, h
, count
);
660 function r_Textures_GetRect (var img
: TImageData
): TRectWH
;
661 var i
, j
, w
, h
: Integer; done
: Boolean;
663 function IsVoid (i
, j
: Integer): Boolean; inline;
665 result
:= GetPixel32(img
, i
, j
).Channels
[3] = 0
672 (* trace x from right to left *)
673 done
:= false; i
:= 0;
674 while not done
and (i
< w
) do
677 while (j
< h
) and IsVoid(i
, j
) do inc(j
);
678 done
:= (j
< h
) and (IsVoid(i
, j
) = false);
683 (* trace y from up to down *)
684 done
:= false; j
:= 0;
685 while not done
and (j
< h
) do
688 while (i
< w
) and IsVoid(i
, j
) do inc(i
);
689 done
:= (i
< w
) and (IsVoid(i
, j
) = false);
694 (* trace x from right to left *)
695 done
:= false; i
:= w
- 1;
696 while not done
and (i
>= 0) do
699 while (j
< h
) and IsVoid(i
, j
) do inc(j
);
700 done
:= (j
< h
) and (IsVoid(i
, j
) = false);
701 result
.width
:= i
- result
.x
+ 1;
705 (* trace y from down to up *)
706 done
:= false; j
:= h
- 1;
707 while not done
and (j
>= 0) do
710 while (i
< w
) and IsVoid(i
, j
) do inc(i
);
711 done
:= (i
< w
) and (IsVoid(i
, j
) = false);
712 result
.height
:= j
- result
.y
+ 1;
717 function r_Textures_LoadStreamFromImage (var img
: TImageData
; w
, h
, c
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
): Boolean;
718 var i
, x
, y
: Integer; t
: TImageData
;
724 ASSERT((st
<> nil) and (Length(st
) >= c
));
725 ASSERT((rs
= nil) or (Length(rs
) >= c
));
727 for i
:= 0 to c
- 1 do
733 if NewImage(w
, h
, img
.Format
, t
) then
735 if CopyRect(img
, x
* w
, y
* h
, w
, h
, t
, 0, 0) then
738 rs
[i
] := r_Textures_GetRect(t
);
739 st
[i
] := r_Textures_LoadFromImage(t
);
742 ASSERT(st
[i
] <> nil);
747 function r_Textures_LoadStreamFromMemory (data
: Pointer; size
: LongInt; w
, h
, c
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
): Boolean;
754 ASSERT((st
<> nil) and (Length(st
) >= c
));
755 ASSERT((rs
= nil) or (Length(rs
) >= c
));
757 if (data
<> nil) and (size
> 0) then
761 if LoadImageFromMemory(data
, size
, img
) then
763 if r_Textures_FixImageData(img
) then
765 result
:= r_Textures_LoadStreamFromImage(img
, w
, h
, c
, cw
, st
, rs
)
774 function r_Textures_LoadStreamFromFile (const filename
: AnsiString; w
, h
, count
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
; log
: Boolean = True): Boolean;
775 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
781 ASSERT((st
<> nil) and (Length(st
) >= count
));
782 ASSERT((rs
= nil) or (Length(rs
) >= count
));
784 wadName
:= g_ExtractWadName(filename
);
785 wad
:= TWADFile
.Create();
786 if wad
.ReadFile(wadName
) then
788 resName
:= g_ExtractFilePathName(filename
);
789 if wad
.GetResource(resName
, data
, size
, log
) then
791 result
:= r_Textures_LoadStreamFromMemory(data
, size
, w
, h
, count
, cw
, st
, rs
);
798 (* --------- TGLFont --------- *)
800 function r_Textures_LoadFontFromFile (const filename
: AnsiString; constref f
: TFontInfo
; skipch
: Integer; log
: Boolean = true): TGLFont
;
801 var i
: Integer; st
: TGLTextureArray
; font
: TGLFont
; t
: TGLTexture
;
806 if r_Textures_LoadStreamFromFile(filename
, f
.w
, f
.h
, 256, 16, st
, nil, log
) then
813 st
[i
] := st
[(i
+ skipch
) mod 256];
814 st
[(i
+ skipch
) mod 256] := t
;
817 font
:= TGLFont
.Create();
824 destructor TGLFont
.Destroy
;
827 if self
.ch
<> nil then
828 for i
:= 0 to High(self
.ch
) do
833 function TGLFont
.GetChar (c
: AnsiChar): TGLTexture
;
835 result
:= self
.ch
[ORD(c
)];
838 function TGLFont
.GetWidth (c
: AnsiChar): Integer;
840 result
:= self
.info
.ch
[c
].w
;
842 result
:= self
.info
.w
;
843 if self
.info
.kern
< 0 then
844 result
:= result
+ self
.info
.kern
;
847 function TGLFont
.GetMaxWidth (): Integer;
849 result
:= self
.info
.w
;
850 if self
.info
.kern
< 0 then
851 result
:= result
+ self
.info
.kern
;
854 function TGLFont
.GetMaxHeight (): Integer;
856 result
:= self
.info
.h
;
859 function TGLFont
.GetSpace (): Integer;
861 result
:= self
.info
.kern
;
865 conRegVar('r_gl_maxtexsize', @r_GL_MaxTexSize
, '', '');
866 r_GL_MaxTexSize
:= 0; // default is automatic value