4 { This unit provides interface to load 24-bit and 32-bit uncompressed images
5 from Truevision Targa (TGA) graphic files, and create OpenGL textures
11 GL
, GLExt
, SysUtils
, e_log
,
12 ImagingTypes
, Imaging
, ImagingUtility
;
17 width
, height
: Word; // real
18 glwidth
, glheight
: Word; // powerof2
19 u
, v
: Single; // usually 1.0
23 e_DummyTextures
: Boolean = False;
24 TEXTUREFILTER
: Integer = GL_NEAREST
;
26 function CreateTexture (var tex
: GLTexture
; Width
, Height
, aFormat
: Word; pData
: Pointer): Boolean;
28 // Standard set of images loading functions
29 function LoadTexture (Filename
: String; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil): Boolean;
30 function LoadTextureEx (Filename
: String; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil): Boolean;
31 function LoadTextureMem (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil): Boolean;
32 function LoadTextureMemEx (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil): Boolean;
34 // `img` must be valid!
35 function LoadTextureImg (var img
: TImageData
; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil): Boolean;
41 Classes
, BinEditor
, g_options
, utils
;
44 function AlignP2 (n
: Word): Word;
59 TTGAHeader = packed record
63 ColorMapSpec: array[0..4] of Byte;
64 OrigX: array[0..1] of Byte;
65 OrigY: array[0..1] of Byte;
66 Width: array[0..1] of Byte;
67 Height: array[0..1] of Byte;
74 // This is auxiliary function that creates OpenGL texture from raw image data
75 function CreateTexture (var tex
: GLTexture
; Width
, Height
, aFormat
: Word; pData
: Pointer): Boolean;
83 tex
.glwidth
:= AlignP2(Width
);
84 tex
.glheight
:= AlignP2(Height
);
89 tex
.glheight
:= Height
;
93 if tex
.glwidth
<> tex
.width
then tex
.u
:= (tex
.width
+0.0)/(tex
.glwidth
+0.0);
94 if tex
.glheight
<> tex
.height
then tex
.v
:= (tex
.height
+0.0)/(tex
.glheight
+0.0);
96 if (tex
.glwidth
<> tex
.width
) or (tex
.glheight
<> tex
.height
) then
98 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
);
101 if e_DummyTextures
then
103 tex
.id
:= GLuint(-1);
108 glGenTextures(1, @Texture
);
110 glBindTexture(GL_TEXTURE_2D
, Texture
);
112 // texture blends with object background
113 glTexEnvi( GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
114 // texture does NOT blend with object background
115 //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
118 Select a filtering type.
119 BiLinear filtering produces very good results with little performance impact
121 GL_NEAREST - Basic texture (grainy looking texture)
122 GL_LINEAR - BiLinear filtering
123 GL_LINEAR_MIPMAP_NEAREST - Basic mipmapped texture
124 GL_LINEAR_MIPMAP_LINEAR - BiLinear Mipmapped texture
127 // for GL_TEXTURE_MAG_FILTER only first two can be used
128 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, TEXTUREFILTER
);
129 // for GL_TEXTURE_MIN_FILTER all of the above can be used
130 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, TEXTUREFILTER
);
132 // create empty texture
133 if aFormat
= GL_RGBA
then
135 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, tex
.glwidth
, tex
.glheight
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, nil);
136 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, Width
, Height
, GL_RGBA
, GL_UNSIGNED_BYTE
, pData
);
140 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGB
, tex
.glwidth
, tex
.glheight
, 0, GL_RGB
, GL_UNSIGNED_BYTE
, nil);
141 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, Width
, Height
, GL_RGB
, GL_UNSIGNED_BYTE
, pData
);
144 // the following is ok too
146 //glTextureSubImage2D(tid, 0, 0, 0, img.width, img.height, GL_RGBA, GL_UNSIGNED_BYTE, img.imageData.bytes.ptr);
149 if (tex.glwidth = tex.glwidth) and (tex.glheight = tex.height) then
151 if aFormat = GL_RGBA then
153 glTexImage2D(GL_TEXTURE_2D, 0, 4, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pData);
157 glTexImage2D(GL_TEXTURE_2D, 0, 3, Width, Height, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
162 glBindTexture(GL_TEXTURE_2D
, 0);
167 // `img` must be valid!
168 function LoadTextureImg (var img
: TImageData
; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil): Boolean;
171 width
, height
: Integer;
179 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
181 if (img
.width
< 1) or (img
.width
> 32768) or (img
.height
< 1) or (img
.height
> 32768) then
183 e_WriteLog('Error loading texture: invalid image dimensions', MSG_WARNING
);
186 //ConvertImage(img, ifA8R8G8B8);
188 height
:= img
.height
;
191 imageSize
:= Width
*Height
*4;
192 GetMem(image
, imageSize
);
194 // it's slow, but i don't care for now
196 for y
:= height
-1 downto 0 do
198 for x
:= 0 to width
-1 do
200 clr
:= GetPixel32(img
, x
, y
);
201 ii
^ := clr
.r
; Inc(ii
);
202 ii
^ := clr
.g
; Inc(ii
);
203 ii
^ := clr
.b
; Inc(ii
);
204 ii
^ := clr
.a
; Inc(ii
);
207 CreateTexture(Texture
, width
, height
, GL_RGBA
, image
);
215 function LoadTextureMem (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil): Boolean;
218 width
, height
: Integer;
227 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
230 if not LoadImageFromMemory(pData
, dataSize
, img
) then
232 e_WriteLog('Error loading texture: unknown image format', MSG_WARNING
);
236 result
:= LoadTextureImg(img
, Texture
, pWidth
, pHeight
, Fmt
);
243 function LoadTextureMemEx (pData
: Pointer; dataSize
: LongInt; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil): Boolean;
246 width
, height
: Integer;
253 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
256 if not LoadImageFromMemory(pData
, dataSize
, img
) then
258 e_WriteLog('Error loading texture: unknown image format', MSG_WARNING
);
262 if (img
.width
< 1) or (img
.width
> 32768) or (img
.height
< 1) or (img
.height
> 32768) then
264 e_WriteLog('Error loading texture: invalid image dimensions', MSG_WARNING
);
267 //ConvertImage(img, ifA8R8G8B8);
268 if fX
> img
.width
then exit
;
269 if fY
> img
.height
then exit
;
270 if fX
+fWidth
> img
.width
then exit
;
271 if fY
+fHeight
> img
.height
then exit
;
272 imageSize
:= img
.width
*img
.height
*4;
273 GetMem(image
, imageSize
);
275 // it's slow, but i don't care for now
277 for y
:= fY
+fHeight
-1 downto 0 do
279 for x
:= fX
to fX
+fWidth
-1 do
281 clr
:= GetPixel32(img
, x
, y
);
282 ii
^ := clr
.r
; Inc(ii
);
283 ii
^ := clr
.g
; Inc(ii
);
284 ii
^ := clr
.b
; Inc(ii
);
285 ii
^ := clr
.a
; Inc(ii
);
288 CreateTexture(Texture
, fWidth
, fHeight
, GL_RGBA
, image
);
299 function LoadTexture (filename
: AnsiString; var Texture
: GLTexture
; var pWidth
, pHeight
: Word; Fmt
: PWord=nil): Boolean;
308 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
312 fs
:= openDiskFileRO(filename
);
318 e_WriteLog('Texture "'+filename
+'" not found', MSG_WARNING
);
323 imageSize
:= fs
.size
;
324 GetMem(img
, imageSize
);
326 fs
.readBuffer(img
^, imageSize
);
327 result
:= LoadTextureMem(img
, imageSize
, Texture
, pWidth
, pHeight
, Fmt
);
337 function LoadTextureEx (filename
: AnsiString; var Texture
: GLTexture
; fX
, fY
, fWidth
, fHeight
: Word; Fmt
: PWord=nil): Boolean;
344 if Fmt
<> nil then Fmt
^ := GL_RGBA
; // anyway
348 fs
:= openDiskFileRO(filename
);
354 e_WriteLog('Texture "'+filename
+'" not found', MSG_WARNING
);
359 imageSize
:= fs
.size
;
360 GetMem(img
, imageSize
);
362 fs
.readBuffer(img
^, imageSize
);
363 result
:= LoadTextureMemEx(img
, imageSize
, Texture
, fX
, fY
, fWidth
, fHeight
, Fmt
);