GLFileDDS.pas 17 KB

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