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}
18 { This unit provides interface to load 24-bit and 32-bit uncompressed images
19 from Truevision Targa (TGA) graphic files, and create OpenGL textures
25 {$INCLUDE ../nogl/noGLuses.inc}
26 SysUtils
, e_log
, ImagingTypes
, Imaging
, ImagingUtility
;
31 width
, height
: Word; // real
32 glwidth
, glheight
: Word; // powerof2
33 u
, v
: Single; // usually 1.0
38 e_DummyTextures
: Boolean = False;
40 function CreateTexture (var tex
: GLTexture
; Width
, Height
, aFormat
: Word; pData
: Pointer; filter
: Boolean = False): Boolean;
42 // Standard set of images loading functions
43 function LoadTexture (Filename
: String; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
44 function LoadTextureEx (Filename
: String; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
45 function LoadTextureMem (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
46 function LoadTextureMemEx (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
48 // `img` must be valid!
49 function LoadTextureImg (var img
: TImageData
; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
55 Classes
, g_options
, utils
;
58 function AlignP2 (n
: Word): Word;
71 // This is auxiliary function that creates OpenGL texture from raw image data
72 function CreateTexture (var tex
: GLTexture
; Width
, Height
, aFormat
: Word; pData
: Pointer; filter
: Boolean = False): Boolean;
78 TEXTUREFILTER
: Integer;
83 tex
.glheight
:= Height
;
89 tex
.glwidth
:= AlignP2(Width
);
90 tex
.glheight
:= AlignP2(Height
);
91 if tex
.glwidth
<> tex
.width
then tex
.u
:= (tex
.width
+0.0)/(tex
.glwidth
+0.0);
92 if tex
.glheight
<> tex
.height
then tex
.v
:= (tex
.height
+0.0)/(tex
.glheight
+0.0);
95 //if (tex.glwidth <> tex.width) or (tex.glheight <> tex.height) then
96 // e_WriteLog(Format('NPOT: orig is %ux%u; gl is %ux%u; u=%f; v=%f', [Width, Height, tex.glwidth, tex.glheight, tex.u, tex.v]), MSG_NOTIFY);
98 if e_DummyTextures
then
100 tex
.id
:= GLuint(-1);
106 glGenTextures(1, @Texture
);
108 glBindTexture(GL_TEXTURE_2D
, Texture
);
110 if (tex
.glwidth
<> tex
.width
) or (tex
.glheight
<> tex
.height
) then
111 e_WriteLog(Format('NPOT: %u is %ux%u; gl is %ux%u; u=%f; v=%f', [tex
.id
, Width
, Height
, tex
.glwidth
, tex
.glheight
, tex
.u
, tex
.v
]), TMsgType
.Notify
);
113 // texture blends with object background
114 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
115 // texture does NOT blend with object background
116 //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
119 Select a filtering type.
120 BiLinear filtering produces very good results with little performance impact
122 GL_NEAREST - Basic texture (grainy looking texture)
123 GL_LINEAR - BiLinear filtering
124 GL_LINEAR_MIPMAP_NEAREST - Basic mipmapped texture
125 GL_LINEAR_MIPMAP_LINEAR - BiLinear Mipmapped texture
128 if filter
then TEXTUREFILTER
:= GL_LINEAR
else TEXTUREFILTER
:= GL_NEAREST
;
130 // for GL_TEXTURE_MAG_FILTER only first two can be used
131 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, TEXTUREFILTER
);
132 // for GL_TEXTURE_MIN_FILTER all of the above can be used
133 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, TEXTUREFILTER
);
135 // create empty texture
136 if aFormat
= GL_RGBA
then fmt
:= GL_RGBA
else fmt
:= GL_RGB
; // silly, yeah?
137 glTexImage2D(GL_TEXTURE_2D
, 0, fmt
, tex
.glwidth
, tex
.glheight
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, nil);
140 GetMem(buf, tex.glwidth*4*tex.glheight);
142 FillChar(buf^, tex.glwidth*4*tex.glheight, 255);
143 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex.glwidth, tex.glheight, fmt, GL_UNSIGNED_BYTE, buf);
144 if (tex.glheight = 128) and (tex.height = 80) then
146 for f := 0 to tex.glheight-1 do
148 for c := 0 to tex.glwidth-1 do
150 buf[f*(tex.glwidth*4)+c*4+0] := 255;
151 buf[f*(tex.glwidth*4)+c*4+1] := 127;
152 buf[f*(tex.glwidth*4)+c*4+2] := 0;
155 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 82, tex.glwidth, {tex.glheight}1, fmt, GL_UNSIGNED_BYTE, buf);
162 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, Width
, Height
, fmt
, GL_UNSIGNED_BYTE
, pData
);
163 //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, tex.glheight-tex.height, Width, Height, fmt, GL_UNSIGNED_BYTE, pData);
165 glBindTexture(GL_TEXTURE_2D
, 0);
167 // so driver will really upload the texture (this is *sometimes* required for buggy videodrivers)
174 // `img` must be valid!
175 function LoadTextureImg (var img
: TImageData
; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
178 width
, height
: Integer;
186 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
188 if (img
.width
< 1) or (img
.width
> 32768) or (img
.height
< 1) or (img
.height
> 32768) then
190 e_WriteLog('Error loading texture: invalid image dimensions', TMsgType
.Warning
);
193 //ConvertImage(img, ifA8R8G8B8);
195 height
:= img
.height
;
198 imageSize
:= Width
*Height
*4;
199 GetMem(image
, imageSize
);
201 // it is slow, but i don't care for now
203 for y
:= height
-1 downto 0 do
205 for x
:= 0 to width
-1 do
207 clr
:= GetPixel32(img
, x
, y
);
208 ii
^ := clr
.r
; Inc(ii
);
209 ii
^ := clr
.g
; Inc(ii
);
210 ii
^ := clr
.b
; Inc(ii
);
211 ii
^ := clr
.a
; Inc(ii
);
214 CreateTexture(Texture
, width
, height
, GL_RGBA
, image
, filter
);
222 function LoadTextureMem (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
225 //width, height: Integer;
226 //imageSize: Integer;
234 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
237 if not LoadImageFromMemory(pData
, dataSize
, img
) then
239 e_WriteLog('Error loading texture: unknown image format', TMsgType
.Warning
);
243 result
:= LoadTextureImg(img
, Texture
, pWidth
, pHeight
, Fmt
, filter
);
250 function LoadTextureMemEx (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
253 //width, height: Integer;
260 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
263 if not LoadImageFromMemory(pData
, dataSize
, img
) then
265 e_WriteLog('Error loading texture: unknown image format', TMsgType
.Warning
);
269 if (img
.width
< 1) or (img
.width
> 32768) or (img
.height
< 1) or (img
.height
> 32768) then
271 e_WriteLog('Error loading texture: invalid image dimensions', TMsgType
.Warning
);
274 //ConvertImage(img, ifA8R8G8B8);
275 if fX
> img
.width
then exit
;
276 if fY
> img
.height
then exit
;
277 if fX
+fWidth
> img
.width
then exit
;
278 if fY
+fHeight
> img
.height
then exit
;
279 //writeln('fX=', fX, '; fY=', fY, '; fWidth=', fWidth, '; fHeight=', fHeight);
280 imageSize
:= img
.width
*img
.height
*4;
281 GetMem(image
, imageSize
);
283 // it is slow, but i don't care for now
285 for y
:= fY
+fHeight
-1 downto fY
do
287 for x
:= fX
to fX
+fWidth
-1 do
289 clr
:= GetPixel32(img
, x
, y
);
290 ii
^ := clr
.r
; Inc(ii
);
291 ii
^ := clr
.g
; Inc(ii
);
292 ii
^ := clr
.b
; Inc(ii
);
293 ii
^ := clr
.a
; Inc(ii
);
296 CreateTexture(Texture
, fWidth
, fHeight
, GL_RGBA
, image
, filter
);
307 function LoadTexture (filename
: AnsiString; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
316 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
320 fs
:= openDiskFileRO(filename
);
326 e_WriteLog('Texture "'+filename
+'" not found', TMsgType
.Warning
);
331 imageSize
:= fs
.size
;
332 GetMem(img
, imageSize
);
334 fs
.readBuffer(img
^, imageSize
);
335 result
:= LoadTextureMem(img
, imageSize
, Texture
, pWidth
, pHeight
, Fmt
, filter
);
345 function LoadTextureEx (filename
: AnsiString; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil; filter
: Boolean = False): Boolean;
352 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
356 fs
:= openDiskFileRO(filename
);
362 e_WriteLog('Texture "'+filename
+'" not found', TMsgType
.Warning
);
367 imageSize
:= fs
.size
;
368 GetMem(img
, imageSize
);
370 fs
.readBuffer(img
^, imageSize
);
371 result
:= LoadTextureMemEx(img
, imageSize
, Texture
, fX
, fY
, fWidth
, fHeight
, Fmt
, filter
);