1 // dds.cpp
2 //
3 // Copyright (C) 2001, Chris Laurel
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9
10 #include <iostream>
11 #include <fstream>
12 #include <algorithm>
13 #include <celutil/debug.h>
14 #include <celutil/bytes.h>
15 #include <celengine/gl.h>
16 #include <celengine/glext.h>
17 #include <celengine/image.h>
18
19 using namespace std;
20
21
22
23 struct DDPixelFormat
24 {
25 uint32 size;
26 uint32 flags;
27 uint32 fourCC;
28 uint32 bpp;
29 uint32 redMask;
30 uint32 greenMask;
31 uint32 blueMask;
32 uint32 alphaMask;
33 };
34
35 struct DDSCaps
36 {
37 uint32 caps;
38 uint32 caps2;
39 uint32 caps3;
40 uint32 caps4;
41 };
42
43 struct DDColorKey
44 {
45 uint32 lowVal;
46 uint32 highVal;
47 };
48
49 struct DDSurfaceDesc
50 {
51 uint32 size;
52 uint32 flags;
53 uint32 height;
54 uint32 width;
55 uint32 pitch;
56 uint32 depth;
57 uint32 mipMapLevels;
58 uint32 alphaBitDepth;
59 uint32 reserved;
60 uint32 surface;
61
62 DDColorKey ckDestOverlay;
63 DDColorKey ckDestBlt;
64 DDColorKey ckSrcOverlay;
65 DDColorKey ckSrcBlt;
66
67 DDPixelFormat format;
68 DDSCaps caps;
69
70 uint32 textureStage;
71 };
72
73
FourCC(const char * s)74 static uint32 FourCC(const char* s)
75 {
76 return (((uint32) s[3] << 24) |
77 ((uint32) s[2] << 16) |
78 ((uint32) s[1] << 8) |
79 (uint32) s[0]);
80 }
81
82
83 #define DDPF_RGB 0x40
84 #define DDPF_FOURCC 0x04
85
86
LoadDDSImage(const string & filename)87 Image* LoadDDSImage(const string& filename)
88 {
89 ifstream in(filename.c_str(), ios::in | ios::binary);
90 if (!in.good())
91 {
92 DPRINTF(0, "Error opening DDS texture file %s.\n", filename.c_str());
93 return NULL;
94 }
95
96 char header[4];
97 in.read(header, sizeof header);
98 if (header[0] != 'D' || header[1] != 'D' ||
99 header[2] != 'S' || header[3] != ' ')
100 {
101 DPRINTF(0, "DDS texture file %s has bad header.\n", filename.c_str());
102 return NULL;
103 }
104
105 DDSurfaceDesc ddsd;
106 in.read(reinterpret_cast<char*>(&ddsd), sizeof ddsd);
107 LE_TO_CPU_INT32(ddsd.size, ddsd.size);
108 LE_TO_CPU_INT32(ddsd.pitch, ddsd.pitch);
109 LE_TO_CPU_INT32(ddsd.width, ddsd.width);
110 LE_TO_CPU_INT32(ddsd.height, ddsd.height);
111 LE_TO_CPU_INT32(ddsd.mipMapLevels, ddsd.mipMapLevels);
112 LE_TO_CPU_INT32(ddsd.format.flags, ddsd.format.flags);
113 LE_TO_CPU_INT32(ddsd.format.redMask, ddsd.format.redMask);
114 LE_TO_CPU_INT32(ddsd.format.greenMask, ddsd.format.greenMask);
115 LE_TO_CPU_INT32(ddsd.format.blueMask, ddsd.format.blueMask);
116 LE_TO_CPU_INT32(ddsd.format.alphaMask, ddsd.format.alphaMask);
117 LE_TO_CPU_INT32(ddsd.format.bpp, ddsd.format.bpp);
118 LE_TO_CPU_INT32(ddsd.format.fourCC, ddsd.format.fourCC);
119
120 int format = -1;
121
122 if (ddsd.format.fourCC != 0)
123 {
124 if (ddsd.format.fourCC == FourCC("DXT1"))
125 {
126 format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
127 }
128 else if (ddsd.format.fourCC == FourCC("DXT3"))
129 {
130 format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
131 }
132 else if (ddsd.format.fourCC == FourCC("DXT5"))
133 {
134 format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
135 }
136 else
137 {
138 cerr << "Unknown FourCC in DDS file: " << ddsd.format.fourCC;
139 }
140 }
141 else
142 {
143 clog << "DDS Format: " << ddsd.format.fourCC << '\n';
144 if (ddsd.format.bpp == 32)
145 {
146 if (ddsd.format.redMask == 0x00ff0000 &&
147 ddsd.format.greenMask == 0x0000ff00 &&
148 ddsd.format.blueMask == 0x000000ff &&
149 ddsd.format.alphaMask == 0xff000000)
150 {
151 format = GL_BGRA_EXT;
152 }
153 else if (ddsd.format.redMask == 0x000000ff &&
154 ddsd.format.greenMask == 0x0000ff00 &&
155 ddsd.format.blueMask == 0x00ff0000 &&
156 ddsd.format.alphaMask == 0xff000000)
157 {
158 format = GL_RGBA;
159 }
160 }
161 else if (ddsd.format.bpp == 24)
162 {
163 if (ddsd.format.redMask == 0x00ff0000 &&
164 ddsd.format.greenMask == 0x0000ff00 &&
165 ddsd.format.blueMask == 0x000000ff)
166 {
167 format = GL_BGR_EXT;
168 }
169 else if (ddsd.format.redMask == 0x000000ff &&
170 ddsd.format.greenMask == 0x0000ff00 &&
171 ddsd.format.blueMask == 0x00ff0000)
172 {
173 format = GL_RGB;
174 }
175 }
176 }
177
178 if (format == -1)
179 {
180 DPRINTF(0, "Unsupported format for DDS texture file %s.\n",
181 filename.c_str());
182 return NULL;
183 }
184
185 // If we have a compressed format, give up if S3 texture compression
186 // isn't supported
187 if (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
188 format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
189 format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
190 {
191 if (!ExtensionSupported("GL_EXT_texture_compression_s3tc"))
192 return NULL;
193 }
194
195 // TODO: Verify that the reported texture size matches the amount of
196 // data expected.
197
198 Image* img = new Image(format,
199 (int) ddsd.width,
200 (int) ddsd.height,
201 max(ddsd.mipMapLevels, 1u));
202 if (img == NULL)
203 return NULL;
204
205 in.read(reinterpret_cast<char*>(img->getPixels()), img->getSize());
206 if (!in.eof() && !in.good())
207 {
208 DPRINTF(0, "Failed reading data from DDS texture file %s.\n",
209 filename.c_str());
210 delete img;
211 return NULL;
212 }
213
214 #if 0
215 cout << "sizeof(ddsd) = " << sizeof(ddsd) << '\n';
216 cout << "dimensions: " << ddsd.width << 'x' << ddsd.height << '\n';
217 cout << "mipmap levels: " << ddsd.mipMapLevels << '\n';
218 cout << "fourCC: " << ddsd.format.fourCC << '\n';
219 cout << "bpp: " << ddsd.format.bpp << '\n';
220 #endif
221
222 return img;
223 }
224