GLFileDDS.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLFileDDS;
  5. (* DDS File support *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. System.Classes,
  12. System.SysUtils,
  13. System.Math,
  14. GLContext,
  15. GLGraphics,
  16. GLTextureFormat,
  17. Scene.RGBE,
  18. GLApplicationFileIO,
  19. Scene.VectorGeometry,
  20. Scene.Strings;
  21. type
  22. TGLDDSDetailLevels = (ddsHighDet, ddsMediumDet, ddsLowDet);
  23. TGLDDSImage = class(TGLBaseImage)
  24. private
  25. procedure flipSurface(chgData: PGLubyte; w, h, d: integer);
  26. public
  27. class function Capabilities: TGLDataFileCapabilities; override;
  28. procedure LoadFromFile(const filename: string); override;
  29. procedure SaveToFile(const filename: string); override;
  30. procedure LoadFromStream(stream: TStream); override;
  31. procedure SaveToStream(stream: TStream); override;
  32. // Assigns from any Texture.
  33. procedure AssignFromTexture(textureContext: TGLContext;
  34. const textureHandle: Cardinal;
  35. textureTarget: TGLTextureTarget;
  36. const CurrentFormat: Boolean;
  37. const intFormat: TGLInternalFormat); reintroduce;
  38. end;
  39. var
  40. (* Variable determines which resolution to use textures,
  41. high - it loads all levels,
  42. midle - skipped the first level,
  43. low - skipped the first two levels. *)
  44. vDDSDetailLevel: TGLDDSDetailLevels = ddsHighDet;
  45. //----------------------------------------------------------------------
  46. implementation
  47. //----------------------------------------------------------------------
  48. uses
  49. FileDXTC;
  50. // ------------------
  51. // ------------------ TGLDDSImage ------------------
  52. // ------------------
  53. procedure TGLDDSImage.LoadFromFile(const filename: string);
  54. var
  55. fs: TStream;
  56. begin
  57. if FileStreamExists(fileName) then
  58. begin
  59. fs := TFileStream.Create(fileName, fmOpenRead);
  60. try
  61. LoadFromStream(fs);
  62. finally
  63. fs.Free;
  64. ResourceName := filename;
  65. end;
  66. end
  67. else
  68. raise EInvalidRasterFile.CreateFmt(strFileNotFound, [filename]);
  69. end;
  70. procedure TGLDDSImage.SaveToFile(const filename: string);
  71. var
  72. fs: TStream;
  73. begin
  74. fs := TFileStream.Create(fileName, fmOpenWrite or fmCreate);
  75. try
  76. SaveToStream(fs);
  77. finally
  78. fs.Free;
  79. end;
  80. ResourceName := filename;
  81. end;
  82. procedure TGLDDSImage.LoadFromStream(stream: TStream);
  83. var
  84. header: TDDSHeader;
  85. DX10header: TDDS_HEADER_DXT10;
  86. btcCompressed: Boolean;
  87. face, faceCount, level: Integer;
  88. w, h, d, bw, bh, size, offset: Integer;
  89. bDXT10Header: Boolean;
  90. procedure CalcSize;
  91. begin
  92. if btcCompressed then
  93. begin
  94. bw := (w + 3) div 4;
  95. bh := (h + 3) div 4;
  96. end
  97. else
  98. begin
  99. bw := w;
  100. bh := h;
  101. end;
  102. if d = 0 then
  103. d := 1;
  104. size := bw * bh * d * fElementSize;
  105. end;
  106. begin
  107. stream.Read(header, Sizeof(TDDSHeader));
  108. // DDS files always start with the same magic number ("DDS ")
  109. if TFOURCC(header.Magic) <> 'DDS ' then
  110. raise EInvalidRasterFile.Create('Invalid DDS file');
  111. // Verify header to validate DDS file
  112. if (header.SurfaceFormat.dwSize <> sizeof(TDDSURFACEDESC2))
  113. or (header.SurfaceFormat.ddpf.dwSize <> sizeof(TDDPIXELFORMAT)) then
  114. raise EInvalidRasterFile.Create('Invalid DDS file');
  115. // Check for DX10 extension
  116. bDXT10Header := (header.SurfaceFormat.ddpf.dwFlags and DDPF_FOURCC <> 0)
  117. and (header.SurfaceFormat.ddpf.dwFourCC = FOURCC_DX10);
  118. if bDXT10Header then
  119. stream.Read(DX10header, Sizeof(TDDS_HEADER_DXT10));
  120. with header.SurfaceFormat do
  121. begin
  122. (* There are flags that are supposed to mark these fields as valid,
  123. but some dds files don't set them properly *)
  124. UnMipmap;
  125. FLOD[0].Width := dwWidth;
  126. FLOD[0].Height := dwHeight;
  127. // check if image is a volume texture
  128. if ((dwCaps2 and DDSCAPS2_VOLUME) <> 0) and (dwDepth > 0) then
  129. FLOD[0].Depth := dwDepth
  130. else
  131. FLOD[0].Depth := 0;
  132. if (dwFlags and DDSD_MIPMAPCOUNT) <> 0 then
  133. fLevelCount := MaxInteger(dwMipMapCount, 1)
  134. else
  135. fLevelCount := 1;
  136. //check cube-map faces
  137. fCubeMap := false;
  138. faceCount := 0;
  139. if (dwCaps2 and DDSCAPS2_CUBEMAP) <> 0 then
  140. begin
  141. //this is a cubemap, count the faces
  142. if (dwCaps2 and DDSCAPS2_CUBEMAP_POSITIVEX) <> 0 then
  143. Inc(faceCount);
  144. if (dwCaps2 and DDSCAPS2_CUBEMAP_NEGATIVEX) <> 0 then
  145. Inc(faceCount);
  146. if (dwCaps2 and DDSCAPS2_CUBEMAP_POSITIVEY) <> 0 then
  147. Inc(faceCount);
  148. if (dwCaps2 and DDSCAPS2_CUBEMAP_NEGATIVEY) <> 0 then
  149. Inc(faceCount);
  150. if (dwCaps2 and DDSCAPS2_CUBEMAP_POSITIVEZ) <> 0 then
  151. Inc(faceCount);
  152. if (dwCaps2 and DDSCAPS2_CUBEMAP_NEGATIVEZ) <> 0 then
  153. Inc(faceCount);
  154. //check for a complete cubemap
  155. if (faceCount <> 6) or (GetWidth <> GetHeight) then
  156. raise EInvalidRasterFile.Create('Invalid cubemap');
  157. fCubeMap := true;
  158. end;
  159. fTextureArray := false;
  160. if not DDSHeaderToGLEnum(header,
  161. DX10header,
  162. bDXT10Header,
  163. fInternalFormat,
  164. fColorFormat,
  165. fDataType,
  166. fElementSize) then
  167. raise EInvalidRasterFile.Create('DDS errorneus format');
  168. btcCompressed := IsCompressedFormat(fInternalFormat);
  169. end; // of with
  170. offset := 0;
  171. case vDDSDetailLevel of
  172. ddsHighDet: ; // Do nothing..
  173. ddsMediumDet:
  174. if fLevelCount > 1 then
  175. begin
  176. w := FLOD[0].Width;
  177. h := FLOD[0].Height;
  178. d := FLOD[0].Depth;
  179. CalcSize;
  180. offset := size;
  181. FLOD[0].Width := FLOD[0].Width div 2;
  182. FLOD[0].Height := FLOD[0].Height div 2;
  183. FLOD[0].Depth := FLOD[0].Depth div 2;
  184. Dec(fLevelCount);
  185. end;
  186. ddsLowDet:
  187. if fLevelCount > 2 then
  188. begin
  189. w := FLOD[0].Width;
  190. h := FLOD[0].Height;
  191. d := FLOD[0].Depth;
  192. CalcSize;
  193. offset := size;
  194. Div2(w);
  195. Div2(h);
  196. Div2(d);
  197. CalcSize;
  198. offset := offset + size;
  199. FLOD[0].Width := FLOD[0].Width div 4;
  200. FLOD[0].Height := FLOD[0].Height div 4;
  201. FLOD[0].Depth := FLOD[0].Depth div 4;
  202. Dec(fLevelCount, 2);
  203. end;
  204. else
  205. Assert(False, strErrorEx + strUnknownType);
  206. end;
  207. ReallocMem(fData, DataSize);
  208. if not fCubeMap then
  209. faceCount := 1;
  210. for face := 0 to faceCount - 1 do
  211. begin
  212. if offset > 0 then
  213. stream.Seek(offset, soCurrent);
  214. for level := 0 to fLevelCount - 1 do
  215. begin
  216. stream.Read(GetLevelAddress(level, face)^, GetLevelSizeInByte(level) div faceCount);
  217. if not fCubeMap and vVerticalFlipDDS then
  218. flipSurface(GetLevelAddress(level, face), FLOD[level].Width, FLOD[level].Height, FLOD[level].Depth);
  219. end;
  220. end; // for level
  221. end;
  222. procedure TGLDDSImage.SaveToStream(stream: TStream);
  223. const
  224. Magic: array[0..3] of AnsiChar = 'DDS ';
  225. var
  226. header: TDDSHeader;
  227. DX10header: TDDS_HEADER_DXT10;
  228. buffer: PGLubyte;
  229. level, size: Integer;
  230. begin
  231. FillChar(header, SizeOf(TDDSHeader), 0);
  232. header.Magic := Cardinal(Magic);
  233. header.SurfaceFormat.dwSize := sizeof(TDDSURFACEDESC2);
  234. header.SurfaceFormat.ddpf.dwSize := sizeof(TDDPIXELFORMAT);
  235. header.SurfaceFormat.dwWidth := GetWidth;
  236. header.SurfaceFormat.dwHeight := GetHeight;
  237. header.SurfaceFormat.dwDepth := GetDepth;
  238. header.SurfaceFormat.dwPitchOrLinearSize := fElementSize * GetWidth;
  239. header.SurfaceFormat.dwFlags := DDSD_CAPS or
  240. DDSD_HEIGHT or
  241. DDSD_WIDTH or
  242. DDSD_PIXELFORMAT;
  243. if IsCompressed then
  244. begin
  245. header.SurfaceFormat.dwPitchOrLinearSize :=
  246. header.SurfaceFormat.dwPitchOrLinearSize * Cardinal(GetHeight) *
  247. Cardinal(GetDepth);
  248. header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or DDSD_PITCH;
  249. end
  250. else
  251. header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or
  252. DDSD_LINEARSIZE;
  253. header.SurfaceFormat.dwCaps := DDSCAPS_TEXTURE;
  254. header.SurfaceFormat.dwCaps2 := 0;
  255. if IsVolume then
  256. begin
  257. header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or DDSD_DEPTH;
  258. header.SurfaceFormat.dwCaps := header.SurfaceFormat.dwCaps or
  259. DDSCAPS_COMPLEX;
  260. header.SurfaceFormat.dwCaps2 := header.SurfaceFormat.dwCaps2 or
  261. DDSCAPS2_VOLUME;
  262. end;
  263. if fLevelCount > 1 then
  264. begin
  265. header.SurfaceFormat.dwCaps := header.SurfaceFormat.dwCaps or DDSCAPS_COMPLEX
  266. or DDSCAPS_MIPMAP;
  267. header.SurfaceFormat.dwFlags := header.SurfaceFormat.dwFlags or
  268. DDSD_MIPMAPCOUNT;
  269. header.SurfaceFormat.dwMipMapCount := fLevelCount;
  270. end
  271. else
  272. header.SurfaceFormat.dwMipMapCount := 0;
  273. if fCubeMap then
  274. begin
  275. header.SurfaceFormat.dwCaps := header.SurfaceFormat.dwCaps or
  276. DDSCAPS_COMPLEX;
  277. header.SurfaceFormat.dwCaps2 := header.SurfaceFormat.dwCaps2 or
  278. DDSCAPS2_CUBEMAP or
  279. DDSCAPS2_CUBEMAP_POSITIVEX or
  280. DDSCAPS2_CUBEMAP_NEGATIVEX or
  281. DDSCAPS2_CUBEMAP_POSITIVEY or
  282. DDSCAPS2_CUBEMAP_NEGATIVEY or
  283. DDSCAPS2_CUBEMAP_POSITIVEZ or
  284. DDSCAPS2_CUBEMAP_NEGATIVEZ;
  285. end;
  286. if not GLEnumToDDSHeader(header,
  287. DX10header,
  288. false,
  289. fInternalFormat,
  290. fColorFormat,
  291. fDataType,
  292. fElementSize) then
  293. raise
  294. EInvalidRasterFile.Create('These image format do not match the DDS format specification.');
  295. stream.Write(header, Sizeof(TDDSHeader));
  296. // stream.Write(DX10header, Sizeof(TDDS_HEADER_DXT10));
  297. if fCubeMap or not vVerticalFlipDDS then
  298. begin
  299. stream.Write(fData[0], DataSize);
  300. Exit;
  301. end
  302. else
  303. begin
  304. GetMem(buffer, GetLevelSizeInByte(0));
  305. try
  306. for level := 0 to fLevelCount - 1 do
  307. begin
  308. size := GetLevelSizeInByte(level);
  309. Move(GetLevelAddress(level)^, buffer^, size);
  310. flipSurface(buffer, LevelWidth[level], LevelHeight[level], LevelDepth[level]);
  311. stream.Write(buffer^, size);
  312. end;
  313. finally
  314. FreeMem(buffer);
  315. end;
  316. end;
  317. end;
  318. procedure TGLDDSImage.AssignFromTexture(textureContext: TGLContext;
  319. const textureHandle: Cardinal;
  320. textureTarget: TGLTextureTarget;
  321. const CurrentFormat: Boolean;
  322. const intFormat: TGLInternalFormat);
  323. var
  324. oldContext: TGLContext;
  325. contextActivate: Boolean;
  326. texFormat, texLod, optLod: Cardinal;
  327. level, faceCount, face: Integer;
  328. residentFormat: TGLInternalFormat;
  329. bCompressed: Boolean;
  330. vtcBuffer, top, bottom: PGLubyte;
  331. i, j, k: Integer;
  332. cw, ch: Integer;
  333. glTarget: Cardinal;
  334. function blockOffset(x, y, z: Integer): Integer;
  335. begin
  336. if z >= (FLOD[level].Depth and -4) then
  337. Result := fElementSize * (cw * ch * (FLOD[level].Depth and -4) + x +
  338. cw * (y + ch * (z - 4 * ch)))
  339. else
  340. Result := fElementSize * (4 * (x + cw * (y + ch * Floor(z / 4))) + (z and
  341. 3));
  342. if Result < 0 then
  343. Result := 0;
  344. end;
  345. begin
  346. oldContext := CurrentGLContext;
  347. contextActivate := (oldContext <> textureContext);
  348. if contextActivate then
  349. begin
  350. if Assigned(oldContext) then
  351. oldContext.Deactivate;
  352. textureContext.Activate;
  353. end;
  354. glTarget := DecodeTextureTarget(textureTarget);
  355. try
  356. textureContext.GLStates.TextureBinding[0, textureTarget] := textureHandle;
  357. fLevelCount := 0;
  358. gl.GetTexParameteriv(glTarget, GL_TEXTURE_MAX_LEVEL, @texLod);
  359. if glTarget = GL_TEXTURE_CUBE_MAP then
  360. begin
  361. fCubeMap := true;
  362. faceCount := 6;
  363. glTarget := GL_TEXTURE_CUBE_MAP_POSITIVE_X;
  364. end
  365. else
  366. begin
  367. fCubeMap := false;
  368. faceCount := 1;
  369. end;
  370. fTextureArray := (glTarget = GL_TEXTURE_1D_ARRAY)
  371. or (glTarget = GL_TEXTURE_2D_ARRAY)
  372. or (glTarget = GL_TEXTURE_CUBE_MAP_ARRAY);
  373. repeat
  374. // Check level existence
  375. gl.GetTexLevelParameteriv(glTarget, fLevelCount,
  376. GL_TEXTURE_INTERNAL_FORMAT,
  377. @texFormat);
  378. if texFormat = 1 then
  379. Break;
  380. Inc(fLevelCount);
  381. if fLevelCount = 1 then
  382. begin
  383. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_WIDTH, @FLOD[0].Width);
  384. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_HEIGHT, @FLOD[0].Height);
  385. FLOD[0].Depth := 0;
  386. if (glTarget = GL_TEXTURE_3D)
  387. or (glTarget = GL_TEXTURE_2D_ARRAY)
  388. or (glTarget = GL_TEXTURE_CUBE_MAP_ARRAY) then
  389. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_DEPTH, @FLOD[0].Depth);
  390. residentFormat := OpenGLFormatToInternalFormat(texFormat);
  391. if CurrentFormat then
  392. fInternalFormat := residentFormat
  393. else
  394. fInternalFormat := intFormat;
  395. if not FindDDSCompatibleDataFormat(fInternalFormat,
  396. fColorFormat,
  397. fDataType) then
  398. FindCompatibleDataFormat(fInternalFormat,
  399. fColorFormat,
  400. fDataType);
  401. // Get optimal number or MipMap levels
  402. optLod := GetImageLodNumber(FLOD[0].Width, FLOD[0].Height, FLOD[0].Depth, glTarget = GL_TEXTURE_3D);
  403. if texLod > optLod then
  404. texLod := optLod;
  405. // Check for MipMap posibility
  406. if ((fInternalFormat >= tfFLOAT_R16)
  407. and (fInternalFormat <= tfFLOAT_RGBA32)) then
  408. texLod := 1;
  409. end;
  410. until fLevelCount = Integer(texLod);
  411. if fLevelCount > 0 then
  412. begin
  413. fElementSize := GetTextureElementSize(fColorFormat, fDataType);
  414. ReallocMem(FData, DataSize);
  415. bCompressed := IsCompressed;
  416. vtcBuffer := nil;
  417. for face := 0 to faceCount - 1 do
  418. begin
  419. if fCubeMap then
  420. glTarget := face + GL_TEXTURE_CUBE_MAP_POSITIVE_X;
  421. for level := 0 to fLevelCount - 1 do
  422. begin
  423. if bCompressed then
  424. begin
  425. if gl.NV_texture_compression_vtc and (FLOD[level].Depth > 0)
  426. and not fTextureArray then
  427. begin
  428. if level = 0 then
  429. GetMem(vtcBuffer, GetLevelSizeInByte(0));
  430. gl.GetCompressedTexImage(glTarget, level, vtcBuffer);
  431. // Shufle blocks from VTC to S3TC
  432. cw := (FLOD[level].Width + 3) div 4;
  433. ch := (FLOD[level].Height + 3) div 4;
  434. top := GetLevelAddress(level);
  435. for k := 0 to FLOD[level].Depth - 1 do
  436. for i := 0 to ch - 1 do
  437. for j := 0 to cw - 1 do
  438. begin
  439. bottom := vtcBuffer;
  440. Inc(bottom, blockOffset(j, i, k));
  441. Move(bottom^, top^, fElementSize);
  442. Inc(top, fElementSize);
  443. end;
  444. end
  445. else
  446. gl.GetCompressedTexImage(glTarget, level, GetLevelAddress(level));
  447. end
  448. else
  449. gl.GetTexImage(glTarget, level, fColorFormat, fDataType, GetLevelAddress(level));
  450. end; // for level
  451. end; // for face
  452. if Assigned(vtcBuffer) then
  453. FreeMem(vtcBuffer);
  454. // Check memory corruption
  455. ReallocMem(FData, DataSize);
  456. end;
  457. if fLevelCount < 1 then
  458. fLevelCount := 1;
  459. gl.CheckError;
  460. finally
  461. if contextActivate then
  462. begin
  463. textureContext.Deactivate;
  464. if Assigned(oldContext) then
  465. oldContext.Activate;
  466. end;
  467. end;
  468. end;
  469. procedure TGLDDSImage.flipSurface(chgData: PGLubyte; w, h, d: integer);
  470. var
  471. lineSize: integer;
  472. sliceSize: integer;
  473. tempBuf: PGLubyte;
  474. i, j: integer;
  475. top, bottom: PGLubyte;
  476. flipblocks: procedure(data: PGLubyte; size: integer);
  477. begin
  478. if d = 0 then
  479. d := 1;
  480. if not isCompressed then
  481. begin
  482. lineSize := fElementSize * w;
  483. sliceSize := lineSize * h;
  484. GetMem(tempBuf, lineSize);
  485. for i := 0 to d - 1 do
  486. begin
  487. top := chgData;
  488. Inc(top, i * sliceSize);
  489. bottom := top;
  490. Inc(bottom, sliceSize - lineSize);
  491. for j := 0 to (h div 2) - 1 do
  492. begin
  493. Move(top^, tempBuf^, lineSize);
  494. Move(bottom^, top^, lineSize);
  495. Move(tempBuf^, bottom^, lineSize);
  496. Inc(top, lineSize);
  497. Dec(bottom, lineSize);
  498. end;
  499. end;
  500. FreeMem(tempBuf);
  501. end
  502. else
  503. begin
  504. w := (w + 3) div 4;
  505. h := (h + 3) div 4;
  506. case fColorFormat of
  507. GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: flipblocks := flip_blocks_dxtc1;
  508. GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: flipblocks := flip_blocks_dxtc3;
  509. GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: flipblocks := flip_blocks_dxtc5;
  510. else
  511. exit;
  512. end;
  513. lineSize := fElementSize * w;
  514. sliceSize := lineSize * h;
  515. GetMem(tempBuf, lineSize);
  516. for i := 0 to d - 1 do
  517. begin
  518. top := chgData;
  519. Inc(top, i * sliceSize);
  520. bottom := top;
  521. Inc(bottom, sliceSize - lineSize);
  522. for j := 0 to (h div 2) - 1 do
  523. begin
  524. if top = bottom then
  525. begin
  526. flipblocks(top, w);
  527. break;
  528. end;
  529. flipblocks(top, w);
  530. flipblocks(bottom, w);
  531. Move(top^, tempBuf^, lineSize);
  532. Move(bottom^, top^, lineSize);
  533. Move(tempBuf^, bottom^, lineSize);
  534. Inc(top, lineSize);
  535. Dec(bottom, lineSize);
  536. end;
  537. end;
  538. FreeMem(tempBuf);
  539. end;
  540. end;
  541. class function TGLDDSImage.Capabilities: TGLDataFileCapabilities;
  542. begin
  543. Result := [dfcRead, dfcWrite];
  544. end;
  545. //----------------------------------------------
  546. initialization
  547. //----------------------------------------------
  548. RegisterRasterFormat('dds', 'Direct Draw Surface', TGLDDSImage);
  549. end.