DEADSOFTWARE

fixed wadeditor; added nosound mode; fixed codepage problems; fixed pointers; cleanup
[d2df-editor.git] / src / engine / e_textures.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit e_textures;
19 { This unit provides interface to load 24-bit and 32-bit uncompressed images
20 from Truevision Targa (TGA) graphic files, and create OpenGL textures
21 from it's data. }
23 interface
25 uses
26 GL, GLExt, SysUtils, e_log,
27 ImagingTypes, Imaging, ImagingUtility;
29 type
30 GLTexture = record
31 id: GLuint;
32 width, height: Word; // real
33 glwidth, glheight: Word; // powerof2
34 u, v: Single; // usually 1.0
35 fmt: GLuint;
36 end;
38 var
39 e_DummyTextures: Boolean = False;
40 e_glLegacyNPOT: Boolean = False;
41 TEXTUREFILTER: Integer = GL_NEAREST;
43 function CreateTexture (var tex: GLTexture; Width, Height, aFormat: Word; pData: Pointer): Boolean;
45 // Standard set of images loading functions
46 function LoadTexture (Filename: String; var Texture: GLTexture; var pWidth, pHeight: Word; Fmt: PWord=nil): Boolean;
47 function LoadTextureEx (Filename: String; var Texture: GLTexture; fX, fY, fWidth, fHeight: Word; Fmt: PWord=nil): Boolean;
48 function LoadTextureMem (pData: Pointer; dataSize: LongInt; var Texture: GLTexture; var pWidth, pHeight: Word; Fmt: PWord=nil): Boolean;
49 function LoadTextureMemEx (pData: Pointer; dataSize: LongInt; var Texture: GLTexture; fX, fY, fWidth, fHeight: Word; Fmt: PWord=nil): Boolean;
51 // `img` must be valid!
52 function LoadTextureImg (var img: TImageData; var Texture: GLTexture; var pWidth, pHeight: Word; Fmt: PWord=nil): Boolean;
55 implementation
57 uses
58 Classes, BinEditor, utils;
61 function AlignP2 (n: Word): Word;
62 begin
63 Dec(n);
64 n := n or (n shr 1);
65 n := n or (n shr 2);
66 n := n or (n shr 4);
67 n := n or (n shr 8);
68 n := n or (n shr 16);
69 Inc(n);
70 Result := n;
71 end;
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;
76 var
77 Texture: GLuint;
78 fmt: GLenum;
79 buf: PByte;
80 f, c: Integer;
81 begin
82 tex.width := Width;
83 tex.height := Height;
84 tex.glwidth := Width;
85 tex.glheight := Height;
86 tex.u := 1;
87 tex.v := 1;
89 if e_glLegacyNPOT then
90 begin
91 tex.glwidth := AlignP2(Width);
92 tex.glheight := AlignP2(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);
95 end;
97 //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);
100 if e_DummyTextures then
101 begin
102 tex.id := GLuint(-1);
103 Result := True;
104 Exit;
105 end;
107 glGenTextures(1, @Texture);
108 tex.id := Texture;
109 glBindTexture(GL_TEXTURE_2D, Texture);
111 if (tex.glwidth <> tex.width) or (tex.glheight <> tex.height) then
112 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]), MSG_NOTIFY);
114 // texture blends with object background
115 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
116 // texture does NOT blend with object background
117 //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
120 Select a filtering type.
121 BiLinear filtering produces very good results with little performance impact
123 GL_NEAREST - Basic texture (grainy looking texture)
124 GL_LINEAR - BiLinear filtering
125 GL_LINEAR_MIPMAP_NEAREST - Basic mipmapped texture
126 GL_LINEAR_MIPMAP_LINEAR - BiLinear Mipmapped texture
129 // for GL_TEXTURE_MAG_FILTER only first two can be used
130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TEXTUREFILTER);
131 // for GL_TEXTURE_MIN_FILTER all of the above can be used
132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TEXTUREFILTER);
134 // create empty texture
135 if aFormat = GL_RGBA then fmt := GL_RGBA else fmt := GL_RGB; // silly, yeah?
136 glTexImage2D(GL_TEXTURE_2D, 0, fmt, tex.glwidth, tex.glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
138 (*
139 GetMem(buf, tex.glwidth*4*tex.glheight);
140 try
141 FillChar(buf^, tex.glwidth*4*tex.glheight, 255);
142 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex.glwidth, tex.glheight, fmt, GL_UNSIGNED_BYTE, buf);
143 if (tex.glheight = 128) and (tex.height = 80) then
144 begin
145 for f := 0 to tex.glheight-1 do
146 begin
147 for c := 0 to tex.glwidth-1 do
148 begin
149 buf[f*(tex.glwidth*4)+c*4+0] := 255;
150 buf[f*(tex.glwidth*4)+c*4+1] := 127;
151 buf[f*(tex.glwidth*4)+c*4+2] := 0;
152 end;
153 end;
154 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 82, tex.glwidth, {tex.glheight}1, fmt, GL_UNSIGNED_BYTE, buf);
155 end;
156 finally
157 FreeMem(buf);
158 end;
159 *)
161 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, fmt, GL_UNSIGNED_BYTE, pData);
162 //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, tex.glheight-tex.height, Width, Height, fmt, GL_UNSIGNED_BYTE, pData);
164 glBindTexture(GL_TEXTURE_2D, 0);
166 Result := true;
167 end;
169 // `img` must be valid!
170 function LoadTextureImg (var img: TImageData; var Texture: GLTexture; var pWidth, pHeight: Word; Fmt: PWord=nil): Boolean;
171 var
172 image, ii: PByte;
173 width, height: Integer;
174 imageSize: Integer;
175 x, y: Integer;
176 clr: TColor32Rec;
177 begin
178 result := false;
179 pWidth := 0;
180 pHeight := 0;
181 if Fmt <> nil then Fmt^ := GL_RGBA; // anyway
183 if (img.width < 1) or (img.width > 32768) or (img.height < 1) or (img.height > 32768) then
184 begin
185 e_WriteLog('Error loading texture: invalid image dimensions', MSG_WARNING);
186 exit;
187 end;
188 //ConvertImage(img, ifA8R8G8B8);
189 width := img.width;
190 height := img.height;
191 pWidth := width;
192 pHeight := height;
193 imageSize := Width*Height*4;
194 GetMem(image, imageSize);
195 try
196 // it's slow, but i don't care for now
197 ii := image;
198 for y := height-1 downto 0 do
199 begin
200 for x := 0 to width-1 do
201 begin
202 clr := GetPixel32(img, x, y);
203 ii^ := clr.r; Inc(ii);
204 ii^ := clr.g; Inc(ii);
205 ii^ := clr.b; Inc(ii);
206 ii^ := clr.a; Inc(ii);
207 end;
208 end;
209 CreateTexture(Texture, width, height, GL_RGBA, image);
210 result := true;
211 finally
212 FreeMem(image);
213 end;
214 end;
217 function LoadTextureMem (pData: Pointer; dataSize: LongInt; var Texture: GLTexture; var pWidth, pHeight: Word; Fmt: PWord=nil): Boolean;
218 var
219 image, ii: PByte;
220 width, height: Integer;
221 imageSize: Integer;
222 img: TImageData;
223 x, y: Integer;
224 clr: TColor32Rec;
225 begin
226 result := false;
227 pWidth := 0;
228 pHeight := 0;
229 if Fmt <> nil then Fmt^ := GL_RGBA; // anyway
231 InitImage(img);
232 if not LoadImageFromMemory(pData, dataSize, img) then
233 begin
234 e_WriteLog('Error loading texture: unknown image format', MSG_WARNING);
235 exit;
236 end;
237 try
238 result := LoadTextureImg(img, Texture, pWidth, pHeight, Fmt);
239 finally
240 FreeImage(img);
241 end;
242 end;
245 function LoadTextureMemEx (pData: Pointer; dataSize: LongInt; var Texture: GLTexture; fX, fY, fWidth, fHeight: Word; Fmt: PWord=nil): Boolean;
246 var
247 image, ii: PByte;
248 width, height: Integer;
249 imageSize: Integer;
250 img: TImageData;
251 x, y: Integer;
252 clr: TColor32Rec;
253 begin
254 result := false;
255 if Fmt <> nil then Fmt^ := GL_RGBA; // anyway
257 InitImage(img);
258 if not LoadImageFromMemory(pData, dataSize, img) then
259 begin
260 e_WriteLog('Error loading texture: unknown image format', MSG_WARNING);
261 exit;
262 end;
263 try
264 if (img.width < 1) or (img.width > 32768) or (img.height < 1) or (img.height > 32768) then
265 begin
266 e_WriteLog('Error loading texture: invalid image dimensions', MSG_WARNING);
267 exit;
268 end;
269 //ConvertImage(img, ifA8R8G8B8);
270 if fX > img.width then exit;
271 if fY > img.height then exit;
272 if fX+fWidth > img.width then exit;
273 if fY+fHeight > img.height then exit;
274 //writeln('fX=', fX, '; fY=', fY, '; fWidth=', fWidth, '; fHeight=', fHeight);
275 imageSize := img.width*img.height*4;
276 GetMem(image, imageSize);
277 try
278 // it's slow, but i don't care for now
279 ii := image;
280 for y := fY+fHeight-1 downto fY do
281 begin
282 for x := fX to fX+fWidth-1 do
283 begin
284 clr := GetPixel32(img, x, y);
285 ii^ := clr.r; Inc(ii);
286 ii^ := clr.g; Inc(ii);
287 ii^ := clr.b; Inc(ii);
288 ii^ := clr.a; Inc(ii);
289 end;
290 end;
291 CreateTexture(Texture, fWidth, fHeight, GL_RGBA, image);
292 result := true;
293 finally
294 FreeMem(image);
295 end;
296 finally
297 FreeImage(img);
298 end;
299 end;
302 function LoadTexture (filename: AnsiString; var Texture: GLTexture; var pWidth, pHeight: Word; Fmt: PWord=nil): Boolean;
303 var
304 fs: TStream;
305 img: Pointer;
306 imageSize: LongInt;
307 begin
308 result := False;
309 pWidth := 0;
310 pHeight := 0;
311 if Fmt <> nil then Fmt^ := GL_RGBA; // anyway
312 fs := nil;
314 try
315 fs := openDiskFileRO(filename);
316 except
317 fs := nil;
318 end;
319 if fs = nil then
320 begin
321 e_WriteLog('Texture "'+filename+'" not found', MSG_WARNING);
322 exit;
323 end;
325 try
326 imageSize := fs.size;
327 GetMem(img, imageSize);
328 try
329 fs.readBuffer(img^, imageSize);
330 result := LoadTextureMem(img, imageSize, Texture, pWidth, pHeight, Fmt);
331 finally
332 FreeMem(img);
333 end;
334 finally
335 fs.Free();
336 end;
337 end;
340 function LoadTextureEx (filename: AnsiString; var Texture: GLTexture; fX, fY, fWidth, fHeight: Word; Fmt: PWord=nil): Boolean;
341 var
342 fs: TStream;
343 img: Pointer;
344 imageSize: LongInt;
345 begin
346 result := False;
347 if Fmt <> nil then Fmt^ := GL_RGBA; // anyway
348 fs := nil;
350 try
351 fs := openDiskFileRO(filename);
352 except
353 fs := nil;
354 end;
355 if fs = nil then
356 begin
357 e_WriteLog('Texture "'+filename+'" not found', MSG_WARNING);
358 exit;
359 end;
361 try
362 imageSize := fs.size;
363 GetMem(img, imageSize);
364 try
365 fs.readBuffer(img^, imageSize);
366 result := LoadTextureMemEx(img, imageSize, Texture, fX, fY, fWidth, fHeight, Fmt);
367 finally
368 FreeMem(img);
369 end;
370 finally
371 fs.Free();
372 end;
373 end;
375 end.