24ea1642e7aa37ec447a509ed4ce2c02627f7bbf
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 TConvProc
= function (x
: Integer): Integer;
125 procedure r_Textures_Initialize
;
126 procedure r_Textures_Finalize
;
128 function r_Textures_LoadFromFile (const filename
: AnsiString; log
: Boolean = True): TGLTexture
;
129 function r_Textures_LoadMultiFromFile (const filename
: AnsiString; log
: Boolean = True): TGLMultiTexture
;
130 function r_Textures_LoadMultiFromFileAndInfo (const filename
: AnsiString; w
, h
, count
: Integer; log
: Boolean = True): TGLMultiTexture
;
131 function r_Textures_LoadMultiTextFromFile (const filename
: AnsiString; var txt
: TAnimTextInfo
; log
: Boolean = True): TGLMultiTexture
;
133 function r_Textures_LoadStreamFromFile (const filename
: AnsiString; w
, h
, count
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
; log
: Boolean = True): Boolean;
135 function r_Textures_LoadFontFromFile (const filename
: AnsiString; constref f
: TFontInfo
; font2enc
: TConvProc
; log
: Boolean = true): TGLFont
;
142 e_log
, e_res
, WADReader
, Config
,
143 g_console
, // cvar declaration
144 Imaging
, ImagingTypes
, ImagingUtility
148 r_GL_MaxTexSize
: WORD;
149 maxTileSize
: Integer;
150 atl
: array of TGLAtlas
;
152 (* --------- TGLAtlasNode --------- *)
154 constructor TGLAtlasNode
.Create (base
: TGLAtlas
);
161 destructor TGLAtlasNode
.Destroy
;
166 function TGLAtlasNode
.GetID (): GLuint
;
168 result
:= self
.base
.id
171 procedure r_Textures_UpdateNode (n
: TGLAtlasNode
; data
: Pointer; x
, y
, w
, h
: Integer);
175 ASSERT(n
.base
<> nil);
179 ASSERT(n
.l
+ x
+ w
- 1 <= n
.r
);
180 ASSERT(n
.t
+ y
+ h
- 1 <= n
.b
);
182 glBindTexture(GL_TEXTURE_2D
, n
.id
);
183 glTexSubImage2D(GL_TEXTURE_2D
, 0, n
.l
+ x
, n
.t
+ y
, w
, h
, GL_RGBA
, GL_UNSIGNED_BYTE
, data
);
184 glBindTexture(GL_TEXTURE_2D
, 0);
187 (* --------- TGLAtlas --------- *)
189 constructor TGLAtlas
.Create (ww
, hh
: Integer; id
: GLuint
);
193 inherited Create(ww
, hh
);
197 destructor TGLAtlas
.Destroy
;
202 function TGLAtlas
.CreateNode (): TGLAtlasNode
;
204 result
:= TGLAtlasNode
.Create(self
);
207 function TGLAtlas
.Alloc (ww
, hh
: Integer): TGLAtlasNode
;
209 result
:= TGLAtlasNode(inherited Alloc(ww
, hh
));
212 function r_Textures_AllocHWTexture (w
, h
: Integer): GLuint
;
215 glGenTextures(1, @id
);
218 glBindTexture(GL_TEXTURE_2D
, id
);
219 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
220 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
221 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, w
, h
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, nil);
222 glBindTexture(GL_TEXTURE_2D
, 0);
227 function r_Textures_AllocAtlas (): TGLAtlas
;
228 var i
: Integer; id
: GLuint
;
231 id
:= r_Textures_AllocHWTexture(maxTileSize
, maxTileSize
);
235 SetLength(atl
, i
+ 1);
236 atl
[i
] := TGLAtlas
.Create(maxTileSize
, maxTileSize
, id
);
241 function r_Textures_AllocNode (w
, h
: Integer): TGLAtlasNode
;
242 var i
: Integer; n
: TGLAtlasNode
; a
: TGLAtlas
;
248 while (i
>= 0) and (n
= nil) do
250 n
:= atl
[i
].Alloc(w
, h
);
256 a
:= r_Textures_AllocAtlas();
263 (* --------- TGLTexture --------- *)
265 destructor TGLTexture
.Destroy
;
268 if self
.mTile
<> nil then
270 for i
:= 0 to High(self
.mTile
) do
272 if self
.mTile
[i
] <> nil then
274 self
.mTile
[i
].Dealloc
;
275 self
.mTile
[i
] := nil;
283 function TGLTexture
.GetLines (): Integer;
285 ASSERT(self
.mTile
<> nil);
286 result
:= Length(self
.mTile
) div self
.mCols
289 function TGLTexture
.GetTile (col
, line
: Integer): TGLAtlasNode
;
293 ASSERT(col
<= mCols
);
294 ASSERT(self
.mTile
<> nil);
295 i
:= line
* mCols
+ col
;
297 ASSERT(i
< Length(mTile
));
299 ASSERT(result
<> nil)
302 function r_Textures_Alloc (w
, h
: Integer): TGLTexture
;
303 var x
, y
, mw
, mh
, cols
, lines
: Integer; t
: TGLTexture
;
307 cols
:= (w
+ maxTileSize
- 1) div maxTileSize
;
308 lines
:= (h
+ maxTileSize
- 1) div maxTileSize
;
309 t
:= TGLTexture
.Create
;
313 // t.mLines := lines;
314 SetLength(t
.mTile
, cols
* lines
);
315 for y
:= 0 to lines
- 1 do
317 mh
:= Min(maxTileSize
, h
- y
* maxTileSize
);
319 for x
:= 0 to cols
- 1 do
321 mw
:= Min(maxTileSize
, w
- x
* maxTileSize
);
323 t
.mTile
[y
* cols
+ x
] := r_Textures_AllocNode(mw
, mh
);
329 (* --------- TGLMultiTexture --------- *)
331 destructor TGLMultiTexture
.Destroy
;
334 for i
:= 0 to self
.count
- 1 do
335 r_Common_FreeAndNil(self
.mTexture
[i
]);
336 SetLength(self
.mTexture
, 0);
340 function TGLMultiTexture
.GetWidth (): Integer;
342 result
:= self
.mTexture
[0].width
345 function TGLMultiTexture
.GetHeight (): Integer;
347 result
:= self
.mTexture
[0].height
350 function TGLMultiTexture
.GetCount (): Integer;
352 result
:= Length(self
.mTexture
)
355 function TGLMultiTexture
.GetTexture (i
: Integer): TGLTexture
;
358 ASSERT(i
< self
.count
);
359 result
:= self
.mTexture
[i
];
360 ASSERT(result
<> nil);
363 (* --------- Init / Fin --------- *)
365 function IsPOT (v
: LongWord): Boolean;
367 result
:= (v
<> 0) and ((v
and (v
- 1)) = 0)
370 function NextPOT (v
: LongWord): LongWord;
382 function r_Textures_GetMaxHardwareSize (): Integer;
385 if r_GL_MaxTexSize
<= 0 then
387 // auto, max possible reccomended by driver
388 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, @size
);
389 if size
< 1 then size
:= 64;
394 if IsPOT(r_GL_MaxTexSize
) then
395 size
:= r_GL_MaxTexSize
397 size
:= NextPOT(r_GL_MaxTexSize
);
402 procedure r_Textures_Initialize
;
404 maxTileSize
:= r_Textures_GetMaxHardwareSize();
405 e_LogWritefln('TEXTURE SIZE: %s', [maxTileSize
]);
408 procedure r_Textures_Finalize
;
413 for i
:= 0 to High(atl
) do
415 glDeleteTextures(1, @atl
[i
].id
);
417 r_Common_FreeAndNil(atl
[i
]);
423 function r_Textures_FixImageData (var img
: TImageData
): Boolean;
426 if ConvertImage(img
, TImageFormat
.ifA8R8G8B8
) then
427 if SwapChannels(img
, ChannelRed
, ChannelBlue
) then // wtf
431 function r_Textures_LoadFromImage (var img
: TImageData
): TGLTexture
;
432 var t
: TGLTexture
; n
: TGLAtlasNode
; c
: TDynImageDataArray
; cw
, ch
, i
, j
: LongInt;
435 if SplitImage(img
, c
, maxTileSize
, maxTileSize
, cw
, ch
, False) then
437 t
:= r_Textures_Alloc(img
.width
, img
.height
);
441 ASSERT(ch
= t
.lines
);
442 for j
:= 0 to ch
- 1 do
444 for i
:= 0 to cw
- 1 do
446 n
:= t
.GetTile(i
, j
);
448 r_Textures_UpdateNode(n
, c
[j
* cw
+ i
].bits
, 0, 0, n
.width
, n
.height
)
453 FreeImagesInArray(c
);
457 function r_Textures_LoadFromMemory (data
: Pointer; size
: LongInt): TGLTexture
;
461 if (data
<> nil) and (size
> 0) then
465 if LoadImageFromMemory(data
, size
, img
) then
466 if r_Textures_FixImageData(img
) then
467 result
:= r_Textures_LoadFromImage(img
)
474 function r_Textures_LoadFromFile (const filename
: AnsiString; log
: Boolean = True): TGLTexture
;
475 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
478 wadName
:= g_ExtractWadName(filename
);
479 wad
:= TWADFile
.Create();
480 if wad
.ReadFile(wadName
) then
482 resName
:= g_ExtractFilePathName(filename
);
483 if wad
.GetResource(resName
, data
, size
, log
) then
485 result
:= r_Textures_LoadFromMemory(data
, size
);
492 function r_Textures_LoadMultiFromImageAndInfo (var img
: TImageData
; w
, h
, c
: Integer): TGLMultiTexture
;
493 var t
: TImageData
; a
: array of TGLTexture
; i
: Integer; m
: TGLMultiTexture
;
500 for i
:= 0 to c
- 1 do
503 if NewImage(w
, h
, img
.Format
, t
) then
504 if CopyRect(img
, w
* i
, 0, w
, h
, t
, 0, 0) then
505 a
[i
] := r_Textures_LoadFromImage(t
);
509 m
:= TGLMultiTexture
.Create();
511 ASSERT(m
.mTexture
<> nil);
515 function r_Textures_LoadMultiFromDataAndInfo (data
: Pointer; size
: LongInt; w
, h
, c
: Integer): TGLMultiTexture
;
522 if (data
<> nil) and (size
> 0) then
526 if LoadImageFromMemory(data
, size
, img
) then
527 if r_Textures_FixImageData(img
) then
528 result
:= r_Textures_LoadMultiFromImageAndInfo(img
, w
, h
, c
)
535 function r_Textures_LoadTextFromMemory (data
: Pointer; size
: LongInt; var txt
: TAnimTextInfo
): Boolean;
541 cfg
:= TConfig
.CreateMem(data
, size
);
544 txt
.name
:= cfg
.ReadStr('', 'resource', '');
545 txt
.w
:= MAX(0, cfg
.ReadInt('', 'framewidth', 0));
546 txt
.h
:= MAX(0, cfg
.ReadInt('', 'frameheight', 0));
547 txt
.anim
.loop
:= true;
548 txt
.anim
.delay
:= MAX(0, cfg
.ReadInt('', 'waitcount', 0));
549 txt
.anim
.frames
:= MAX(0, cfg
.ReadInt('', 'framecount', 0));
550 txt
.anim
.back
:= cfg
.ReadBool('', 'backanim', false);
552 result
:= (txt
.name
<> '') and (txt
.w
> 0) and (txt
.h
> 0) and (txt
.anim
.delay
> 0) and (txt
.anim
.frames
> 0);
557 function r_Textures_LoadMultiFromWad (wad
: TWADFile
; var txt
: TAnimTextInfo
): TGLMultiTexture
;
558 var data
: Pointer; size
: LongInt; img
: TImageData
;
562 if wad
.GetResource('TEXT/ANIM', data
, size
) then
564 if r_Textures_LoadTextFromMemory(data
, size
, txt
) then
567 if wad
.GetResource('TEXTURES/' + txt
.name
, data
, size
) then
571 if LoadImageFromMemory(data
, size
, img
) then
572 if r_Textures_FixImageData(img
) then
573 result
:= r_Textures_LoadMultiFromImageAndInfo(img
, txt
.w
, txt
.h
, txt
.anim
.frames
);
585 function r_Textures_LoadMultiFromMemory (data
: Pointer; size
: LongInt; var txt
: TAnimTextInfo
): TGLMultiTexture
;
586 var wad
: TWADFile
; t
: TGLTexture
; m
: TGLMultiTexture
;
589 if (data
<> nil) and (size
> 0) then
591 t
:= r_Textures_LoadFromMemory(data
, size
);
594 m
:= TGLMultiTexture
.Create();
595 SetLength(m
.mTexture
, 1);
600 txt
.anim
.loop
:= true;
602 txt
.anim
.frames
:= 1;
603 txt
.anim
.back
:= false;
606 else if IsWadData(data
, size
) then
608 wad
:= TWADFile
.Create();
609 if wad
.ReadMemory(data
, size
) then
611 result
:= r_Textures_LoadMultiFromWad(wad
, txt
);
618 function r_Textures_LoadMultiTextFromFile (const filename
: AnsiString; var txt
: TAnimTextInfo
; log
: Boolean = True): TGLMultiTexture
;
619 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
622 wadName
:= g_ExtractWadName(filename
);
623 wad
:= TWADFile
.Create();
624 if wad
.ReadFile(wadName
) then
626 resName
:= g_ExtractFilePathName(filename
);
627 if wad
.GetResource(resName
, data
, size
, log
) then
629 result
:= r_Textures_LoadMultiFromMemory(data
, size
, txt
);
636 function r_Textures_LoadMultiFromFile (const filename
: AnsiString; log
: Boolean = True): TGLMultiTexture
;
637 var txt
: TAnimTextInfo
;
639 result
:= r_Textures_LoadMultiTextFromFile(filename
, txt
, log
);
642 function r_Textures_LoadMultiFromFileAndInfo (const filename
: AnsiString; w
, h
, count
: Integer; log
: Boolean = True): TGLMultiTexture
;
643 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
649 wadName
:= g_ExtractWadName(filename
);
650 wad
:= TWADFile
.Create();
651 if wad
.ReadFile(wadName
) then
653 resName
:= g_ExtractFilePathName(filename
);
654 if wad
.GetResource(resName
, data
, size
, log
) then
656 result
:= r_Textures_LoadMultiFromDataAndInfo(data
, size
, w
, h
, count
);
663 function r_Textures_GetRect (var img
: TImageData
): TRectWH
;
664 var i
, j
, w
, h
: Integer; done
: Boolean;
666 function IsVoid (i
, j
: Integer): Boolean; inline;
668 result
:= GetPixel32(img
, i
, j
).Channels
[3] = 0
675 (* trace x from right to left *)
676 done
:= false; i
:= 0;
677 while not done
and (i
< w
) do
680 while (j
< h
) and IsVoid(i
, j
) do inc(j
);
681 done
:= (j
< h
) and (IsVoid(i
, j
) = false);
686 (* trace y from up to down *)
687 done
:= false; j
:= 0;
688 while not done
and (j
< h
) do
691 while (i
< w
) and IsVoid(i
, j
) do inc(i
);
692 done
:= (i
< w
) and (IsVoid(i
, j
) = false);
697 (* trace x from right to left *)
698 done
:= false; i
:= w
- 1;
699 while not done
and (i
>= 0) do
702 while (j
< h
) and IsVoid(i
, j
) do inc(j
);
703 done
:= (j
< h
) and (IsVoid(i
, j
) = false);
704 result
.width
:= i
- result
.x
+ 1;
708 (* trace y from down to up *)
709 done
:= false; j
:= h
- 1;
710 while not done
and (j
>= 0) do
713 while (i
< w
) and IsVoid(i
, j
) do inc(i
);
714 done
:= (i
< w
) and (IsVoid(i
, j
) = false);
715 result
.height
:= j
- result
.y
+ 1;
720 function r_Textures_LoadStreamFromImage (var img
: TImageData
; w
, h
, c
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
): Boolean;
721 var i
, x
, y
: Integer; t
: TImageData
;
727 ASSERT((st
<> nil) and (Length(st
) >= c
));
728 ASSERT((rs
= nil) or (Length(rs
) >= c
));
730 for i
:= 0 to c
- 1 do
736 if NewImage(w
, h
, img
.Format
, t
) then
738 if CopyRect(img
, x
* w
, y
* h
, w
, h
, t
, 0, 0) then
741 rs
[i
] := r_Textures_GetRect(t
);
742 st
[i
] := r_Textures_LoadFromImage(t
);
745 ASSERT(st
[i
] <> nil);
750 function r_Textures_LoadStreamFromMemory (data
: Pointer; size
: LongInt; w
, h
, c
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
): Boolean;
757 ASSERT((st
<> nil) and (Length(st
) >= c
));
758 ASSERT((rs
= nil) or (Length(rs
) >= c
));
760 if (data
<> nil) and (size
> 0) then
764 if LoadImageFromMemory(data
, size
, img
) then
766 if r_Textures_FixImageData(img
) then
768 result
:= r_Textures_LoadStreamFromImage(img
, w
, h
, c
, cw
, st
, rs
)
777 function r_Textures_LoadStreamFromFile (const filename
: AnsiString; w
, h
, count
, cw
: Integer; st
: TGLTextureArray
; rs
: TRectArray
; log
: Boolean = True): Boolean;
778 var wad
: TWADFile
; wadName
, resName
: AnsiString; data
: Pointer; size
: Integer;
784 ASSERT((st
<> nil) and (Length(st
) >= count
));
785 ASSERT((rs
= nil) or (Length(rs
) >= count
));
787 wadName
:= g_ExtractWadName(filename
);
788 wad
:= TWADFile
.Create();
789 if wad
.ReadFile(wadName
) then
791 resName
:= g_ExtractFilePathName(filename
);
792 if wad
.GetResource(resName
, data
, size
, log
) then
794 result
:= r_Textures_LoadStreamFromMemory(data
, size
, w
, h
, count
, cw
, st
, rs
);
801 (* --------- TGLFont --------- *)
803 function r_Textures_LoadFontFromFile (const filename
: AnsiString; constref f
: TFontInfo
; font2enc
: TConvProc
; log
: Boolean = true): TGLFont
;
804 var i
, ch
: Integer; st
, stch
: TGLTextureArray
; font
: TGLFont
;
808 if r_Textures_LoadStreamFromFile(filename
, f
.w
, f
.h
, 256, 16, st
, nil, log
) then
810 font
:= TGLFont
.Create();
813 if Assigned(font2enc
) then
815 SetLength(stch
, 256);
819 ASSERT((ch
>= 0) and (ch
<= 255));
829 destructor TGLFont
.Destroy
;
832 if self
.ch
<> nil then
833 for i
:= 0 to High(self
.ch
) do
838 function TGLFont
.GetChar (c
: AnsiChar): TGLTexture
;
840 result
:= self
.ch
[ORD(c
)];
843 function TGLFont
.GetWidth (c
: AnsiChar): Integer;
845 result
:= self
.info
.ch
[c
].w
;
847 result
:= self
.info
.w
;
848 if self
.info
.kern
< 0 then
849 result
:= result
+ self
.info
.kern
;
852 function TGLFont
.GetMaxWidth (): Integer;
854 result
:= self
.info
.w
;
855 if self
.info
.kern
< 0 then
856 result
:= result
+ self
.info
.kern
;
859 function TGLFont
.GetMaxHeight (): Integer;
861 result
:= self
.info
.h
;
864 function TGLFont
.GetSpace (): Integer;
866 result
:= self
.info
.kern
;
870 conRegVar('r_gl_maxtexsize', @r_GL_MaxTexSize
, '', '');
871 r_GL_MaxTexSize
:= 0; // default is automatic value