GLFileO3TC.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLFileO3TC;
  5. (* O3TC file loading *)
  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. GLApplicationFileIO;
  19. type
  20. TGLO3TCImage = class(TGLBaseImage)
  21. public
  22. class function Capabilities: TGLDataFileCapabilities; override;
  23. procedure LoadFromFile(const filename: string); override;
  24. procedure SaveToFile(const filename: string); override;
  25. procedure LoadFromStream(stream: TStream); override;
  26. procedure SaveToStream(stream: TStream); override;
  27. procedure AssignFromTexture(textureContext: TGLContext;
  28. const textureHandle: Cardinal; textureTarget: TGLTextureTarget;
  29. const CurrentFormat: Boolean;
  30. const intFormat: TGLInternalFormat); reintroduce;
  31. end;
  32. //----------------------------------------------------------
  33. implementation
  34. //----------------------------------------------------------
  35. uses
  36. GLVectorGeometry;
  37. const
  38. O3_TC_RGB_S3TC_DXT1 = 1;
  39. O3_TC_RGBA_S3TC_DXT5 = 4;
  40. O3_TC_ATI3DC_ATI2N = 16;
  41. const
  42. O3_TC_CUBE_MAP = $0001;
  43. O3_TC_ARRAY = $0002;
  44. type
  45. TO3TC_Header = record
  46. Useless: Cardinal;
  47. Magic: Cardinal; // Magic number: Must be O3TC.
  48. Size: Cardinal; // Must be filled with sizeof(TO3TC_Header).
  49. Version: Cardinal; // Version.
  50. end;
  51. TO3TC_ChunkHeader = record
  52. // Must be filled with sizeof(TO3TC_Chunk_Header):
  53. ChunkHeaderSize: Cardinal;
  54. // Reserved in JeGX's version 1.0
  55. Extension: Cardinal;
  56. // The size of the data chunk that follows this one.
  57. Size: Cardinal;
  58. // Reserved
  59. reserved2: Cardinal;
  60. // Pixel format:
  61. // - O3_TC_RGB_S3TC_DXT1 = 1
  62. // - O3_TC_RGBA_S3TC_DXT5 = 4
  63. // - O3_TC_ATI3DC_ATI2N = 16
  64. InternalPixelFormat: Cardinal;
  65. // Texture width.
  66. Width: Cardinal;
  67. // Texture height.
  68. Height: Cardinal;
  69. // Texture depth.
  70. Depth: Cardinal;
  71. // Number of mipmaps.
  72. NumMipmaps: Cardinal;
  73. // The texture name (optional).
  74. TextureName: array[0..127] of AnsiChar;
  75. // The texture id (optional).
  76. TextureId: Cardinal;
  77. end;
  78. // ------------------
  79. // ------------------ TGLO3TCImage ------------------
  80. // ------------------
  81. procedure TGLO3TCImage.LoadFromFile(const filename: string);
  82. var
  83. fs: TStream;
  84. begin
  85. if FileStreamExists(fileName) then
  86. begin
  87. fs := TFileStream.Create(fileName, fmOpenRead);
  88. try
  89. LoadFromStream(fs);
  90. finally
  91. fs.Free;
  92. ResourceName := filename;
  93. end;
  94. end
  95. else
  96. raise EInvalidRasterFile.CreateFmt('File %s not found.', [filename]);
  97. end;
  98. procedure TGLO3TCImage.SaveToFile(const filename: string);
  99. var
  100. fs: TStream;
  101. begin
  102. fs := TFileStream.Create(fileName, fmOpenWrite or fmCreate);
  103. try
  104. SaveToStream(fs);
  105. finally
  106. fs.Free;
  107. end;
  108. ResourceName := filename;
  109. end;
  110. procedure TGLO3TCImage.LoadFromStream(stream: TStream);
  111. type
  112. TFOURCC = array[0..3] of AnsiChar;
  113. var
  114. Header: TO3TC_Header;
  115. ChunkHeader: TO3TC_ChunkHeader;
  116. begin
  117. // Get the O3TC_Header.
  118. stream.Read(Header, Sizeof(TO3TC_Header));
  119. // Check for O3TC magic number...
  120. if TFOURCC(Header.Magic) <> 'O3TC' then
  121. raise EInvalidRasterFile.Create('Invalid O3TC file.');
  122. // Get the O3TC_Chunk_Header
  123. stream.Read(ChunkHeader, Sizeof(TO3TC_ChunkHeader));
  124. FLOD[0].Width := ChunkHeader.Width;
  125. FLOD[0].Height := ChunkHeader.Height;
  126. FLOD[0].Depth := ChunkHeader.Depth;
  127. // Get the number of mipmaps
  128. if ChunkHeader.NumMipmaps <> 0 then
  129. fLevelCount := MaxInteger(ChunkHeader.NumMipmaps, 1)
  130. else
  131. fLevelCount := 1;
  132. if Header.Version > 1 then
  133. begin
  134. fCubeMap := (ChunkHeader.Extension and O3_TC_CUBE_MAP) <> 0;
  135. fTextureArray := (ChunkHeader.Extension and O3_TC_ARRAY) <> 0;
  136. end
  137. else
  138. begin
  139. fCubeMap := false;
  140. fTextureArray := false;
  141. end;
  142. // Set format properties
  143. case ChunkHeader.InternalPixelFormat of
  144. O3_TC_RGB_S3TC_DXT1:
  145. begin
  146. fColorFormat := GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
  147. fInternalFormat := tfCOMPRESSED_RGB_S3TC_DXT1;
  148. fDataType := GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
  149. fElementSize := 8;
  150. end;
  151. O3_TC_RGBA_S3TC_DXT5:
  152. begin
  153. fColorFormat := GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  154. fInternalFormat := tfCOMPRESSED_RGBA_S3TC_DXT5;
  155. fDataType := GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  156. fElementSize := 16;
  157. end;
  158. O3_TC_ATI3DC_ATI2N:
  159. begin
  160. fColorFormat := GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI;
  161. fInternalFormat := tfCOMPRESSED_LUMINANCE_ALPHA_3DC;
  162. fDataType := GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI;
  163. fElementSize := 16;
  164. end;
  165. else
  166. raise EInvalidRasterFile.Create('Unsupported O3TC format.')
  167. end;
  168. if ChunkHeader.Size <> DataSize then
  169. EInvalidRasterFile.Create('O3TC erroneous image data size.');
  170. ReallocMem(fData, ChunkHeader.Size);
  171. // Read raw data
  172. stream.Read(fData^, ChunkHeader.Size);
  173. end;
  174. procedure TGLO3TCImage.SaveToStream(stream: TStream);
  175. const
  176. Magic: array[0..3] of AnsiChar = 'O3TC';
  177. var
  178. Header: TO3TC_Header;
  179. ChunkHeader: TO3TC_ChunkHeader;
  180. begin
  181. if not ((fColorFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
  182. or (fColorFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
  183. or (fColorFormat = GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI)) then
  184. raise
  185. EInvalidRasterFile.Create('These image format do not match the O3TC format specification.');
  186. // Setup Header
  187. Header.Magic := Cardinal(Magic);
  188. Header.Size := SizeOf(TO3TC_Header) - SizeOf(Cardinal);
  189. ChunkHeader.Extension := 0;
  190. if not (fCubeMap or fTextureArray) then
  191. begin
  192. Header.Version := 1;
  193. end
  194. else
  195. begin
  196. Header.Version := 2;
  197. if fCubeMap then
  198. ChunkHeader.Extension := ChunkHeader.Extension or O3_TC_CUBE_MAP;
  199. if fTextureArray then
  200. ChunkHeader.Extension := ChunkHeader.Extension or O3_TC_ARRAY;
  201. end;
  202. ChunkHeader.ChunkHeaderSize := SizeOf(TO3TC_ChunkHeader);
  203. ChunkHeader.Width := GetWidth;
  204. ChunkHeader.Height := GetHeight;
  205. ChunkHeader.Depth := GetDepth;
  206. ChunkHeader.NumMipmaps := fLevelCount;
  207. ChunkHeader.reserved2 := 1;
  208. FillChar(ChunkHeader.TextureName, 128, 0);
  209. ChunkHeader.TextureId := 0;
  210. case fColorFormat of
  211. GL_COMPRESSED_RGB_S3TC_DXT1_EXT: ChunkHeader.InternalPixelFormat :=
  212. O3_TC_RGB_S3TC_DXT1;
  213. GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ChunkHeader.InternalPixelFormat :=
  214. O3_TC_RGBA_S3TC_DXT5;
  215. GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI: ChunkHeader.InternalPixelFormat :=
  216. O3_TC_ATI3DC_ATI2N;
  217. end;
  218. ChunkHeader.Size := DataSize;
  219. stream.Write(Header, Sizeof(TO3TC_Header));
  220. stream.Write(ChunkHeader, Sizeof(TO3TC_ChunkHeader));
  221. stream.Write(fData[0], ChunkHeader.Size);
  222. end;
  223. procedure TGLO3TCImage.AssignFromTexture(textureContext: TGLContext;
  224. const textureHandle: Cardinal;
  225. textureTarget: TGLTextureTarget;
  226. const CurrentFormat: Boolean;
  227. const intFormat: TGLInternalFormat);
  228. var
  229. oldContext: TGLContext;
  230. contextActivate: Boolean;
  231. texFormat, texLod, optLod: Cardinal;
  232. level, faceCount, face: Integer;
  233. residentFormat: TGLInternalFormat;
  234. bCompressed: Boolean;
  235. vtcBuffer, top, bottom: PByte;
  236. i, j, k: Integer;
  237. cw, ch: Integer;
  238. glTarget: Cardinal;
  239. function blockOffset(x, y, z: Integer): Integer;
  240. begin
  241. if z >= (FLOD[level].Depth and -4) then
  242. Result := fElementSize * (cw * ch * (FLOD[level].Depth and -4) + x +
  243. cw * (y + ch * (z - 4 * ch)))
  244. else
  245. Result := fElementSize * (4 * (x + cw * (y + ch * Floor(z / 4))) + (z and
  246. 3));
  247. if Result < 0 then
  248. Result := 0;
  249. end;
  250. begin
  251. oldContext := CurrentGLContext;
  252. contextActivate := (oldContext <> textureContext);
  253. if contextActivate then
  254. begin
  255. if Assigned(oldContext) then
  256. oldContext.Deactivate;
  257. textureContext.Activate;
  258. end;
  259. glTarget := DecodeTextureTarget(textureTarget);
  260. try
  261. textureContext.GLStates.TextureBinding[0, textureTarget] := textureHandle;
  262. fLevelCount := 0;
  263. gl.GetTexParameteriv(glTarget, GL_TEXTURE_MAX_LEVEL, @texLod);
  264. if glTarget = GL_TEXTURE_CUBE_MAP then
  265. begin
  266. fCubeMap := true;
  267. faceCount := 6;
  268. glTarget := GL_TEXTURE_CUBE_MAP_POSITIVE_X;
  269. end
  270. else
  271. begin
  272. fCubeMap := false;
  273. faceCount := 1;
  274. end;
  275. fTextureArray := (glTarget = GL_TEXTURE_1D_ARRAY)
  276. or (glTarget = GL_TEXTURE_2D_ARRAY)
  277. or (glTarget = GL_TEXTURE_CUBE_MAP_ARRAY);
  278. repeat
  279. // Check level existence
  280. gl.GetTexLevelParameteriv(glTarget, fLevelCount, GL_TEXTURE_INTERNAL_FORMAT,
  281. @texFormat);
  282. if texFormat = 1 then
  283. Break;
  284. Inc(fLevelCount);
  285. if fLevelCount = 1 then
  286. begin
  287. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_WIDTH, @FLOD[0].Width);
  288. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_HEIGHT, @FLOD[0].Height);
  289. FLOD[0].Depth := 0;
  290. if (glTarget = GL_TEXTURE_3D)
  291. or (glTarget = GL_TEXTURE_2D_ARRAY)
  292. or (glTarget = GL_TEXTURE_CUBE_MAP_ARRAY) then
  293. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_DEPTH, @FLOD[0].Depth);
  294. residentFormat := OpenGLFormatToInternalFormat(texFormat);
  295. if CurrentFormat then
  296. fInternalFormat := residentFormat
  297. else
  298. fInternalFormat := intFormat;
  299. FindCompatibleDataFormat(fInternalFormat, fColorFormat, fDataType);
  300. // Get optimal number or MipMap levels
  301. optLod := GetImageLodNumber(GetWidth, GetHeight, GetDepth, IsVolume);
  302. if texLod > optLod then
  303. texLod := optLod;
  304. // Check for MipMap posibility
  305. if ((fInternalFormat >= tfFLOAT_R16)
  306. and (fInternalFormat <= tfFLOAT_RGBA32)) then
  307. texLod := 1;
  308. end;
  309. until fLevelCount = Integer(texLod);
  310. if fLevelCount > 0 then
  311. begin
  312. fElementSize := GetTextureElementSize(fColorFormat, fDataType);
  313. ReallocMem(FData, DataSize);
  314. bCompressed := IsCompressed;
  315. vtcBuffer := nil;
  316. for face := 0 to faceCount - 1 do
  317. begin
  318. if fCubeMap then
  319. glTarget := face + GL_TEXTURE_CUBE_MAP_POSITIVE_X;
  320. for level := 0 to fLevelCount - 1 do
  321. begin
  322. if bCompressed then
  323. begin
  324. if gl.NV_texture_compression_vtc and IsVolume then
  325. begin
  326. if level = 0 then
  327. GetMem(vtcBuffer, GetLevelSizeInByte(0));
  328. gl.GetCompressedTexImage(glTarget, level, vtcBuffer);
  329. // Shufle blocks from VTC to S3TC
  330. cw := (FLOD[level].Width + 3) div 4;
  331. ch := (FLOD[level].Height + 3) div 4;
  332. top := GetLevelAddress(level);
  333. for k := 0 to FLOD[level].Depth - 1 do
  334. for i := 0 to ch - 1 do
  335. for j := 0 to cw - 1 do
  336. begin
  337. bottom := vtcBuffer;
  338. Inc(bottom, blockOffset(j, i, k));
  339. Move(bottom^, top^, fElementSize);
  340. Inc(top, fElementSize);
  341. end;
  342. end
  343. else
  344. gl.GetCompressedTexImage(glTarget, level, GetLevelAddress(level));
  345. end
  346. else
  347. gl.GetTexImage(glTarget, level, fColorFormat, fDataType, GetLevelAddress(level));
  348. end; // for level
  349. end; // for face
  350. if Assigned(vtcBuffer) then
  351. FreeMem(vtcBuffer);
  352. // Check memory corruption
  353. ReallocMem(FData, DataSize);
  354. if fLevelCount = 0 then
  355. fLevelCount := 1;
  356. gl.CheckError;
  357. end;
  358. finally
  359. if contextActivate then
  360. begin
  361. textureContext.Deactivate;
  362. if Assigned(oldContext) then
  363. oldContext.Activate;
  364. end;
  365. end;
  366. end;
  367. class function TGLO3TCImage.Capabilities: TGLDataFileCapabilities;
  368. begin
  369. Result := [dfcRead, dfcWrite];
  370. end;
  371. //--------------------------------------------
  372. initialization
  373. //--------------------------------------------
  374. RegisterRasterFormat('o3tc', 'oZone3D Texture Compression', TGLO3TCImage);
  375. end.