2
// This unit is part of the GLScene Engine https://github.com/glscene
5
DDS File support for GLScene.
8
04/11/10 - DaStr - Added Delphi5 and Delphi6 compatibility
9
23/08/10 - Yar - Replaced OpenGL1x to OpenGLTokens
10
06/06/10 - Yar - Fixes for Linux x64
11
08/05/10 - Yar - Removed check for residency in AssignFromTexture
12
22/04/10 - Yar - Fixes after GLState revision
13
01/03/10 - Yar - Added control of texture detail level
14
27/01/10 - Yar - Bugfix in BlockOffset with negative result
15
23/11/10 - DaStr - Added $I GLScene.inc
16
23/01/10 - Yar - Added to AssignFromTexture CurrentFormat parameter
17
Fixed cube map saving bug
18
20/01/10 - Yar - Creation
30
GLCrossPlatform, OpenGLTokens, GLContext, GLGraphics, GLTextureFormat,
31
GLSRGBE, GLApplicationFileIO, GLVectorGeometry, GLStrings;
38
TGLDDSDetailLevels = (ddsHighDet, ddsMediumDet, ddsLowDet);
43
TGLDDSImage = class(TGLBaseImage)
45
procedure flipSurface(chgData: PGLubyte; w, h, d: integer);
47
class function Capabilities: TGLDataFileCapabilities; override;
49
procedure LoadFromFile(const filename: string); override;
50
procedure SaveToFile(const filename: string); override;
51
procedure LoadFromStream(stream: TStream); override;
52
procedure SaveToStream(stream: TStream); override;
54
{ Assigns from any Texture.}
55
procedure AssignFromTexture(textureContext: TGLContext;
56
const textureHandle: TGLuint;
57
textureTarget: TGLTextureTarget;
58
const CurrentFormat: Boolean;
59
const intFormat: TGLInternalFormat); reintroduce;
63
{ Variable determines which resolution to use textures,
64
high - it loads all levels,
65
midle - skipped the first level,
66
low - skipped the first two levels. }
67
vDDSDetailLevel: TGLDDSDetailLevels = ddsHighDet;
75
// ------------------ TGLDDSImage ------------------
81
procedure TGLDDSImage.LoadFromFile(const filename: string);
85
if FileStreamExists(fileName) then
87
fs := CreateFileStream(fileName, fmOpenRead);
92
ResourceName := filename;
96
raise EInvalidRasterFile.CreateFmt(glsFileNotFound, [filename]);
102
procedure TGLDDSImage.SaveToFile(const filename: string);
106
fs := CreateFileStream(fileName, fmOpenWrite or fmCreate);
112
ResourceName := filename;
118
procedure TGLDDSImage.LoadFromStream(stream: TStream);
121
DX10header: TDDS_HEADER_DXT10;
122
btcCompressed: Boolean;
123
face, faceCount, level: Integer;
124
w, h, d, bw, bh, size, offset: Integer;
125
bDXT10Header: Boolean;
129
if btcCompressed then
141
size := bw * bh * d * fElementSize;
145
stream.Read(header, Sizeof(TDDSHeader));
146
// DDS files always start with the same magic number ("DDS ")
147
if TFOURCC(header.Magic) <> 'DDS ' then
148
raise EInvalidRasterFile.Create('Invalid DDS file');
150
// Verify header to validate DDS file
151
if (header.SurfaceFormat.dwSize <> sizeof(TDDSURFACEDESC2))
152
or (header.SurfaceFormat.ddpf.dwSize <> sizeof(TDDPIXELFORMAT)) then
153
raise EInvalidRasterFile.Create('Invalid DDS file');
155
// Check for DX10 extension
156
bDXT10Header := (header.SurfaceFormat.ddpf.dwFlags and DDPF_FOURCC <> 0)
157
and (header.SurfaceFormat.ddpf.dwFourCC = FOURCC_DX10);
159
stream.Read(DX10header, Sizeof(TDDS_HEADER_DXT10));
161
with header.SurfaceFormat do
163
{ There are flags that are supposed to mark these fields as valid,
164
but some dds files don't set them properly }
166
FLOD[0].Width := dwWidth;
167
FLOD[0].Height := dwHeight;
168
// check if image is a volume texture
169
if ((dwCaps2 and DDSCAPS2_VOLUME) <> 0) and (dwDepth > 0) then
170
FLOD[0].Depth := dwDepth
174
if (dwFlags and DDSD_MIPMAPCOUNT) <> 0 then
175
fLevelCount := MaxInteger(dwMipMapCount, 1)
179
//check cube-map faces
182
if (dwCaps2 and DDSCAPS2_CUBEMAP) <> 0 then
184
//this is a cubemap, count the faces
185
if (dwCaps2 and DDSCAPS2_CUBEMAP_POSITIVEX) <> 0 then
187
if (dwCaps2 and DDSCAPS2_CUBEMAP_NEGATIVEX) <> 0 then
189
if (dwCaps2 and DDSCAPS2_CUBEMAP_POSITIVEY) <> 0 then
191
if (dwCaps2 and DDSCAPS2_CUBEMAP_NEGATIVEY) <> 0 then
193
if (dwCaps2 and DDSCAPS2_CUBEMAP_POSITIVEZ) <> 0 then
195
if (dwCaps2 and DDSCAPS2_CUBEMAP_NEGATIVEZ) <> 0 then
197
//check for a complete cubemap
198
if (faceCount <> 6) or (GetWidth <> GetHeight) then
199
raise EInvalidRasterFile.Create('Invalid cubemap');
202
fTextureArray := false;
204
if not DDSHeaderToGLEnum(header,
211
raise EInvalidRasterFile.Create('DDS errorneus format');
212
btcCompressed := IsCompressedFormat(fInternalFormat);
216
case vDDSDetailLevel of
217
ddsHighDet: ; // Do nothing..
219
if fLevelCount > 1 then
226
FLOD[0].Width := FLOD[0].Width div 2;
227
FLOD[0].Height := FLOD[0].Height div 2;
228
FLOD[0].Depth := FLOD[0].Depth div 2;
232
if fLevelCount > 2 then
243
offset := offset + size;
244
FLOD[0].Width := FLOD[0].Width div 4;
245
FLOD[0].Height := FLOD[0].Height div 4;
246
FLOD[0].Depth := FLOD[0].Depth div 4;
250
Assert(False, glsErrorEx + glsUnknownType);
253
ReallocMem(fData, DataSize);
257
for face := 0 to faceCount - 1 do
260
stream.Seek(offset, soCurrent);
261
for level := 0 to fLevelCount - 1 do
263
stream.Read(GetLevelAddress(level, face)^, GetLevelSizeInByte(level) div faceCount);
264
if not fCubeMap and vVerticalFlipDDS then
265
flipSurface(GetLevelAddress(level, face), FLOD[level].Width, FLOD[level].Height, FLOD[level].Depth);
270
procedure TGLDDSImage.SaveToStream(stream: TStream);
272
Magic: array[0..3] of AnsiChar = 'DDS ';
275
DX10header: TDDS_HEADER_DXT10;
277
level, size: Integer;
279
FillChar(header, SizeOf(TDDSHeader), 0);
280
header.Magic := Cardinal(Magic);
281
header.SurfaceFormat.dwSize := sizeof(TDDSURFACEDESC2);
282
header.SurfaceFormat.ddpf.dwSize := sizeof(TDDPIXELFORMAT);
283
header.SurfaceFormat.dwWidth := GetWidth;
284
header.SurfaceFormat.dwHeight := GetHeight;
285
header.SurfaceFormat.dwDepth := GetDepth;
286
header.SurfaceFormat.dwPitchOrLinearSize := fElementSize * GetWidth;
287
header.SurfaceFormat.dwFlags := DDSD_CAPS or
293
header.SurfaceFormat.dwPitchOrLinearSize :=
294
header.SurfaceFormat.dwPitchOrLinearSize * Cardinal(GetHeight) *
296
header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or DDSD_PITCH;
299
header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or
302
header.SurfaceFormat.dwCaps := DDSCAPS_TEXTURE;
303
header.SurfaceFormat.dwCaps2 := 0;
307
header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or DDSD_DEPTH;
308
header.SurfaceFormat.dwCaps := header.SurfaceFormat.dwCaps or
310
header.SurfaceFormat.dwCaps2 := header.SurfaceFormat.dwCaps2 or
314
if fLevelCount > 1 then
316
header.SurfaceFormat.dwCaps := header.SurfaceFormat.dwCaps or DDSCAPS_COMPLEX
318
header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or
320
header.SurfaceFormat.dwMipMapCount := fLevelCount;
323
header.SurfaceFormat.dwMipMapCount := 0;
327
header.SurfaceFormat.dwCaps := header.SurfaceFormat.dwCaps or
329
header.SurfaceFormat.dwCaps2 := header.SurfaceFormat.dwCaps2 or
331
DDSCAPS2_CUBEMAP_POSITIVEX or
332
DDSCAPS2_CUBEMAP_NEGATIVEX or
333
DDSCAPS2_CUBEMAP_POSITIVEY or
334
DDSCAPS2_CUBEMAP_NEGATIVEY or
335
DDSCAPS2_CUBEMAP_POSITIVEZ or
336
DDSCAPS2_CUBEMAP_NEGATIVEZ;
339
if not GLEnumToDDSHeader(header,
347
EInvalidRasterFile.Create('These image format do not match the DDS format specification.');
349
stream.Write(header, Sizeof(TDDSHeader));
350
// stream.Write(DX10header, Sizeof(TDDS_HEADER_DXT10));
351
if fCubeMap or not vVerticalFlipDDS then
353
stream.Write(fData[0], DataSize);
358
GetMem(buffer, GetLevelSizeInByte(0));
360
for level := 0 to fLevelCount - 1 do
362
size := GetLevelSizeInByte(level);
363
Move(GetLevelAddress(level)^, buffer^, size);
364
flipSurface(buffer, LevelWidth[level], LevelHeight[level], LevelDepth[level]);
365
stream.Write(buffer^, size);
376
procedure TGLDDSImage.AssignFromTexture(textureContext: TGLContext;
377
const textureHandle: TGLuint;
378
textureTarget: TGLTextureTarget;
379
const CurrentFormat: Boolean;
380
const intFormat: TGLInternalFormat);
382
oldContext: TGLContext;
383
contextActivate: Boolean;
384
texFormat, texLod, optLod: Cardinal;
385
level, faceCount, face: Integer;
386
residentFormat: TGLInternalFormat;
387
bCompressed: Boolean;
388
vtcBuffer, top, bottom: PGLubyte;
393
function blockOffset(x, y, z: Integer): Integer;
396
if z >= (FLOD[level].Depth and -4) then
397
Result := fElementSize * (cw * ch * (FLOD[level].Depth and -4) + x +
398
cw * (y + ch * (z - 4 * ch)))
400
Result := fElementSize * (4 * (x + cw * (y + ch * floor(z / 4))) + (z and
407
oldContext := CurrentGLContext;
408
contextActivate := (oldContext <> textureContext);
409
if contextActivate then
411
if Assigned(oldContext) then
412
oldContext.Deactivate;
413
textureContext.Activate;
415
glTarget := DecodeGLTextureTarget(textureTarget);
418
textureContext.GLStates.TextureBinding[0, textureTarget] := textureHandle;
420
GL.GetTexParameteriv(glTarget, GL_TEXTURE_MAX_LEVEL, @texLod);
421
if glTarget = GL_TEXTURE_CUBE_MAP then
425
glTarget := GL_TEXTURE_CUBE_MAP_POSITIVE_X;
432
fTextureArray := (glTarget = GL_TEXTURE_1D_ARRAY)
433
or (glTarget = GL_TEXTURE_2D_ARRAY)
434
or (glTarget = GL_TEXTURE_CUBE_MAP_ARRAY);
437
// Check level existence
438
GL.GetTexLevelParameteriv(glTarget, fLevelCount,
439
GL_TEXTURE_INTERNAL_FORMAT,
441
if texFormat = 1 then
444
if fLevelCount = 1 then
446
GL.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_WIDTH, @FLOD[0].Width);
447
GL.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_HEIGHT, @FLOD[0].Height);
449
if (glTarget = GL_TEXTURE_3D)
450
or (glTarget = GL_TEXTURE_2D_ARRAY)
451
or (glTarget = GL_TEXTURE_CUBE_MAP_ARRAY) then
452
GL.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_DEPTH, @FLOD[0].Depth);
453
residentFormat := OpenGLFormatToInternalFormat(texFormat);
454
if CurrentFormat then
455
fInternalFormat := residentFormat
457
fInternalFormat := intFormat;
458
if not FindDDSCompatibleDataFormat(fInternalFormat,
461
FindCompatibleDataFormat(fInternalFormat,
465
// Get optimal number or MipMap levels
466
optLod := GetImageLodNumber(FLOD[0].Width, FLOD[0].Height, FLOD[0].Depth, glTarget = GL_TEXTURE_3D);
467
if texLod > optLod then
469
// Check for MipMap posibility
470
if ((fInternalFormat >= tfFLOAT_R16)
471
and (fInternalFormat <= tfFLOAT_RGBA32)) then
474
until fLevelCount = Integer(texLod);
476
if fLevelCount > 0 then
478
fElementSize := GetTextureElementSize(fColorFormat, fDataType);
479
ReallocMem(FData, DataSize);
480
bCompressed := IsCompressed;
483
for face := 0 to faceCount - 1 do
486
glTarget := face + GL_TEXTURE_CUBE_MAP_POSITIVE_X;
487
for level := 0 to fLevelCount - 1 do
492
if GL.NV_texture_compression_vtc and (FLOD[level].Depth > 0)
493
and not fTextureArray then
496
GetMem(vtcBuffer, GetLevelSizeInByte(0));
497
GL.GetCompressedTexImage(glTarget, level, vtcBuffer);
498
// Shufle blocks from VTC to S3TC
499
cw := (FLOD[level].Width + 3) div 4;
500
ch := (FLOD[level].Height + 3) div 4;
501
top := GetLevelAddress(level);
502
for k := 0 to FLOD[level].Depth - 1 do
503
for i := 0 to ch - 1 do
504
for j := 0 to cw - 1 do
507
Inc(bottom, blockOffset(j, i, k));
508
Move(bottom^, top^, fElementSize);
509
Inc(top, fElementSize);
513
GL.GetCompressedTexImage(glTarget, level, GetLevelAddress(level));
516
GL.GetTexImage(glTarget, level, fColorFormat, fDataType, GetLevelAddress(level));
520
if Assigned(vtcBuffer) then
522
// Check memory corruption
523
ReallocMem(FData, DataSize);
526
if fLevelCount < 1 then
530
if contextActivate then
532
textureContext.Deactivate;
533
if Assigned(oldContext) then
539
procedure TGLDDSImage.flipSurface(chgData: PGLubyte; w, h, d: integer);
545
top, bottom: PGLubyte;
546
flipblocks: procedure(data: PGLubyte; size: integer);
552
if not isCompressed then
554
lineSize := fElementSize * w;
555
sliceSize := lineSize * h;
556
GetMem(tempBuf, lineSize);
558
for i := 0 to d - 1 do
561
Inc(top, i * sliceSize);
563
Inc(bottom, sliceSize - lineSize);
565
for j := 0 to (h div 2) - 1 do
567
Move(top^, tempBuf^, lineSize);
568
Move(bottom^, top^, lineSize);
569
Move(tempBuf^, bottom^, lineSize);
571
Dec(bottom, lineSize);
583
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: flipblocks := flip_blocks_dxtc1;
584
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: flipblocks := flip_blocks_dxtc3;
585
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: flipblocks := flip_blocks_dxtc5;
590
lineSize := fElementSize * w;
591
sliceSize := lineSize * h;
592
GetMem(tempBuf, lineSize);
593
for i := 0 to d - 1 do
596
Inc(top, i * sliceSize);
598
Inc(bottom, sliceSize - lineSize);
600
for j := 0 to (h div 2) - 1 do
609
flipblocks(bottom, w);
611
Move(top^, tempBuf^, lineSize);
612
Move(bottom^, top^, lineSize);
613
Move(tempBuf^, bottom^, lineSize);
616
Dec(bottom, lineSize);
626
class function TGLDDSImage.Capabilities: TGLDataFileCapabilities;
628
Result := [dfcRead, dfcWrite];
632
{ Register this Fileformat-Handler with GLScene }
633
RegisterRasterFormat('dds', 'Direct Draw Surface', TGLDDSImage);