xoreos  0.0.5
texture.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <cassert>
26 
27 #include "src/common/types.h"
28 #include "src/common/util.h"
29 #include "src/common/strutil.h"
30 #include "src/common/error.h"
31 #include "src/common/readstream.h"
32 
35 
36 #include "src/graphics/types.h"
37 #include "src/graphics/graphics.h"
47 
48 #include "src/events/requests.h"
49 
50 #include "src/aurora/resman.h"
51 
52 using Events::RequestID;
53 
54 namespace Graphics {
55 
56 namespace Aurora {
57 
58 Texture::Texture() : _type(::Aurora::kFileTypeNone), _width(0), _height(0), _deswizzle(false) {
59 }
60 
62  ::Aurora::FileType type, TXI *txi, bool deswizzle) :
63  _name(name), _type(type), _width(0), _height(0), _deswizzle(deswizzle) {
64 
65  set(name, image, type, txi, deswizzle);
66  addToQueues();
67 }
68 
71 
72  if (_textureID != 0)
73  GfxMan.abandon(&_textureID, 1);
74 }
75 
77  return _width;
78 }
79 
81  return _height;
82 }
83 
84 bool Texture::hasAlpha() const {
85  if (!_image)
86  return false;
87 
88  return _image->hasAlpha();
89 }
90 
91 bool Texture::isDynamic() const {
92  return false;
93 }
94 
95 static const TXI kEmptyTXI;
96 const TXI &Texture::getTXI() const {
97  if (_txi)
98  return *_txi;
99 
100  if (_image)
101  return _image->getTXI();
102 
103  return kEmptyTXI;
104 }
105 
107  assert(_image);
108 
109  return *_image;
110 }
111 
113  if (_name.empty())
114  return false;
115 
117  ImageDecoder *image = 0;
118  TXI *txi = 0;
119 
120  try {
121 
122  txi = loadTXI (_name);
123  image = loadImage(_name, type, txi, _deswizzle);
124 
125  } catch (Common::Exception &e) {
126  delete txi;
127 
128  e.add("Failed to reload texture \"%s\" (%d)", _name.c_str(), type);
129  throw;
130  }
131 
133  set(_name, image, type, txi, _deswizzle);
134  addToQueues();
135 
136  return true;
137 }
138 
139 bool Texture::dumpTGA(const Common::UString &fileName) const {
140  if (!_image)
141  return false;
142 
143  return _image->dumpTGA(fileName);
144 }
145 
147  if (_textureID == 0)
148  return;
149 
150  glDeleteTextures(1, &_textureID);
151 
152  _textureID = 0;
153 }
154 
156  if (!_image)
157  // No image
158  return;
159 
160  // Generate the texture ID
161  if (_textureID == 0)
162  glGenTextures(1, &_textureID);
163 
164  if (_image->isCubeMap()) {
166  return;
167  }
168 
169  create2DTexture();
170 }
171 
172 void Texture::setWrap(GLenum target, GLint wrapModeX, GLint wrapModeY) {
173  glTexParameteri(target, GL_TEXTURE_WRAP_S, wrapModeX);
174  glTexParameteri(target, GL_TEXTURE_WRAP_T, wrapModeY);
175 }
176 
178  /* Set the correct alignment depending on the texture data we have.
179  Can be used for optimized reading of texture data by the driver. */
180 
181  int alignment = 1;
182 
183  switch (_image->getFormatRaw()) {
184  // 4 byte per texel, so always neatly 4-byte aligned
185  case kPixelFormatRGBA8:
186  alignment = 4;
187  break;
188 
189  // 2 byte per texel, so always neatly 2-byte aligned
190  case kPixelFormatRGB5A1:
191  case kPixelFormatRGB5:
192  alignment = 2;
193  break;
194 
195  // All bets are off here
196  case kPixelFormatRGB8:
197  alignment = 1;
198  break;
199 
200  // DXT texture rows should always be 4-byte aligned
201  case kPixelFormatDXT1:
202  case kPixelFormatDXT3:
203  case kPixelFormatDXT5:
204  alignment = 4;
205  break;
206 
207  // Unknown
208  default:
209  alignment = 1;
210  break;
211  }
212 
213  glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
214 }
215 
216 void Texture::setFilter(GLenum target) {
217  const TXI::Features &features = getTXI().getFeatures();
218 
219  if (features.filter) {
220  glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
221  glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
222  } else {
223  glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
224  glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
225  }
226 }
227 
228 void Texture::setMipMaps(GLenum target) {
229  if (_image->getMipMapCount() == 1) {
230  // Texture doesn't specify any mip maps, generate our own
231 
232  glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
233  glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
234  glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 9);
235  } else {
236  // Texture does specify mip maps, use these
237 
238  glTexParameteri(target, GL_GENERATE_MIPMAP, GL_FALSE);
239  glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
240  glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, _image->getMipMapCount() - 1);
241  }
242 }
243 
244 void Texture::setMipMapData(GLenum target, size_t layer, size_t mipMap) {
245  const ImageDecoder::MipMap &m = _image->getMipMap(mipMap, layer);
246 
247  if (_image->isCompressed()) {
248  glCompressedTexImage2D(target, mipMap, _image->getFormatRaw(),
249  m.width, m.height, 0, m.size, m.data.get());
250  } else {
251  glTexImage2D(target, mipMap, _image->getFormatRaw(),
252  m.width, m.height, 0, _image->getFormat(), _image->getDataType(), m.data.get());
253  }
254 }
255 
257  // Bind the texture
258  glBindTexture(GL_TEXTURE_2D, _textureID);
259 
260  // Edge wrap mode
261  setWrap(GL_TEXTURE_2D, GL_REPEAT, GL_REPEAT);
262 
263  // Pixel row alignment
264  setAlign();
265 
266  // Filter method
267  setFilter(GL_TEXTURE_2D);
268 
269  // Mip map parameters
270  setMipMaps(GL_TEXTURE_2D);
271 
272  // Texture image data
273  for (size_t i = 0; i < _image->getMipMapCount(); i++)
274  setMipMapData(GL_TEXTURE_2D, 0, i);
275 }
276 
278  // Bind the texture
279  glBindTexture(GL_TEXTURE_CUBE_MAP, _textureID);
280 
281  // Edge wrap mode
282  setWrap(GL_TEXTURE_CUBE_MAP, GL_REPEAT, GL_REPEAT);
283 
284  // Pixel row alignment
285  setAlign();
286 
287  // Filter method
288  setFilter(GL_TEXTURE_CUBE_MAP);
289 
290  // Mip map parameters
291  setMipMaps(GL_TEXTURE_CUBE_MAP);
292 
293  assert(_image->getLayerCount() == 6);
294 
295  static const GLenum faceTarget[6] = {
296  GL_TEXTURE_CUBE_MAP_POSITIVE_X,
297  GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
298  GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
299  GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
300  GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
301  GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
302  };
303 
304  // Texture image data
305  for (size_t i = 0; i < _image->getLayerCount(); i++)
306  for (size_t j = 0; j < _image->getMipMapCount(); j++)
307  setMipMapData(faceTarget[i], i, j);
308 }
309 
311  Texture *texture = 0;
312  try {
313  texture = new PLTFile(name, *imageStream);
314  } catch (...) {
315  delete imageStream;
316  throw;
317  }
318 
319  delete imageStream;
320  return texture;
321 }
322 
323 Texture *Texture::create(const Common::UString &name, bool deswizzle) {
325  ImageDecoder *image = 0;
326  ImageDecoder *layers[6] = { 0, 0, 0, 0, 0, 0 };
327  TXI *txi = 0;
328 
329  try {
330  txi = loadTXI(name);
331 
332  const bool isFileCubeMap = txi && txi->getFeatures().cube && (txi->getFeatures().fileRange == 6);
333  if (isFileCubeMap) {
334  // A cube map with each side a separate image file
335 
336  for (size_t i = 0; i < 6; i++) {
337  const Common::UString side = name + Common::composeString(i);
338  Common::SeekableReadStream *imageStream = ResMan.getResource(::Aurora::kResourceImage, side, &type);
339  if (!imageStream)
340  throw Common::Exception("No such cube side image resource \"%s\"", side.c_str());
341 
342  layers[i] = loadImage(imageStream, type, txi, deswizzle);
343  }
344 
345  image = new CubeMapCombiner(layers);
346 
347  } else {
348  Common::SeekableReadStream *imageStream = ResMan.getResource(::Aurora::kResourceImage, name, &type);
349  if (!imageStream)
350  throw Common::Exception("No such image resource \"%s\"", name.c_str());
351 
352  // PLT needs extra handling, since they're their own Texture class
353  if (type == ::Aurora::kFileTypePLT) {
354  delete txi;
355 
356  return createPLT(name, imageStream);
357  }
358 
359  image = loadImage(imageStream, type, txi, deswizzle);
360  }
361 
362  } catch (Common::Exception &e) {
363  delete txi;
364  delete image;
365 
366  for (size_t i = 0; i < ARRAYSIZE(layers); i++)
367  delete layers[i];
368 
369  e.add("Failed to create texture \"%s\" (%d)", name.c_str(), type);
370  throw;
371  }
372 
373  return new Texture(name, image, type, txi, deswizzle);
374 }
375 
376 Texture *Texture::create(ImageDecoder *image, ::Aurora::FileType type, TXI *txi, bool deswizzle) {
377  if (!image)
378  throw Common::Exception("Can't create a texture from an empty image");
379 
380  if (image->getMipMapCount() < 1)
381  throw Common::Exception("Texture has no images");
382 
383  return new Texture("", image, type, txi, deswizzle);
384 }
385 
387  TXI *txi, bool deswizzle) {
388 
389  _name = name;
390  _type = type;
391 
392  _image.reset(image);
393  _txi.reset(txi);
394 
395  _width = _image->getMipMap(0).width;
396  _height = _image->getMipMap(0).height;
397 
398  _deswizzle = deswizzle;
399 }
400 
401 ImageDecoder *Texture::loadImage(const Common::UString &name, bool deswizzle) {
402  ::Aurora::FileType type;
403 
404  return loadImage(name, type, deswizzle);
405 }
406 
410 }
411 
415 }
416 
419  addToQueues();
420 }
421 
422 ImageDecoder *Texture::loadImage(const Common::UString &name, ::Aurora::FileType &type, bool deswizzle) {
423  return loadImage(name, type, 0, deswizzle);
424 }
425 
427  TXI *txi, bool deswizzle) {
428 
429  const bool isFileCubeMap = txi && txi->getFeatures().cube && (txi->getFeatures().fileRange == 6);
430  if (!isFileCubeMap) {
431  Common::SeekableReadStream *imageStream = ResMan.getResource(::Aurora::kResourceImage, name, &type);
432  if (!imageStream)
433  throw Common::Exception("No such image resource \"%s\"", name.c_str());
434 
435  return loadImage(imageStream, type, txi, deswizzle);
436  }
437 
438  ImageDecoder *layers[6] = { 0, 0, 0, 0, 0, 0 };
439 
440  try {
441  for (size_t i = 0; i < 6; i++) {
442  const Common::UString side = name + Common::composeString(i);
443  Common::SeekableReadStream *imageStream = ResMan.getResource(::Aurora::kResourceImage, side, &type);
444  if (!imageStream)
445  throw Common::Exception("No such cube side image resource \"%s\"", side.c_str());
446 
447  layers[i] = loadImage(imageStream, type, txi, deswizzle);
448  }
449 
450  return new CubeMapCombiner(layers);
451 
452  } catch (...) {
453  for (size_t i = 0; i < ARRAYSIZE(layers); i++)
454  delete layers[i];
455 
456  throw;
457  }
458 }
459 
461  TXI *txi, bool deswizzle) {
462 
463  // Check for a cube map, but only those that don't use a file for each side
464  const bool isCubeMap = txi && txi->getFeatures().cube && (txi->getFeatures().fileRange == 0);
465 
466  ImageDecoder *image = 0;
467  try {
468  // Loading the different image formats
469  if (type == ::Aurora::kFileTypeTGA)
470  image = new TGA(*imageStream, isCubeMap);
471  else if (type == ::Aurora::kFileTypeDDS)
472  image = new DDS(*imageStream);
473  else if (type == ::Aurora::kFileTypeTPC)
474  image = new TPC(*imageStream);
475  else if (type == ::Aurora::kFileTypeTXB)
476  image = new TXB(*imageStream);
477  else if (type == ::Aurora::kFileTypeSBM)
478  image = new SBM(*imageStream, deswizzle);
479  else if (type == ::Aurora::kFileTypeXEOSITEX)
480  image = new XEOSITEX(*imageStream);
481  else
482  throw Common::Exception("Unsupported image resource type %d", (int) type);
483 
484  if (image->getMipMapCount() < 1)
485  throw Common::Exception("Texture has no images");
486 
487  // Decompress
488  if (GfxMan.needManualDeS3TC())
489  image->decompress();
490 
491  } catch (...) {
492  delete image;
493  delete imageStream;
494 
495  throw;
496  }
497 
498  delete imageStream;
499  return image;
500 }
501 
503  Common::SeekableReadStream *txiStream = ResMan.getResource(name, ::Aurora::kFileTypeTXI);
504  if (!txiStream)
505  return 0;
506 
507  TXI *txi = 0;
508  try {
509  txi = new TXI(*txiStream);
510  } catch (...) {
511  Common::exceptionDispatcherWarning("Failed loading TXI \"%s\"", name.c_str());
512  }
513 
514  delete txiStream;
515  return txi;
516 }
517 
518 } // End of namespace Aurora
519 
520 } // End of namespace Graphics
#define ResMan
Shortcut for accessing the sound manager.
Definition: resman.h:557
A newly created texture.
Definition: types.h:73
static const TXI kEmptyTXI
Definition: texture.cpp:95
Generic image decoder interface.
Inter-thread request events.
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
DDS texture (DirectDraw Surface or BioWare&#39;s own format) loading).
bool hasAlpha() const
Definition: texture.cpp:84
The global graphics manager.
Packed layer texture.
Definition: types.h:63
const TXI & getTXI() const
Return the TXI.
Definition: texture.cpp:96
A class holding an UTF-8 string.
Definition: ustring.h:48
Texture information.
Definition: types.h:88
static Texture * createPLT(const Common::UString &name, Common::SeekableReadStream *imageStream)
Definition: texture.cpp:310
void setMipMapData(GLenum target, size_t layer, size_t mipMap)
Definition: texture.cpp:244
UString composeString(T value)
Convert any POD integer, float/double or bool type into a string.
Definition: strutil.cpp:276
static Texture * create(const Common::UString &name, bool deswizzle=false)
Create a texture from this image resource.
Definition: texture.cpp:323
Common::ScopedArray< byte > data
The mip map&#39;s data.
Definition: decoder.h:56
TarGa image.
Definition: tga.h:66
static TXI * loadTXI(const Common::UString &name)
Definition: texture.cpp:502
#define ARRAYSIZE(x)
Macro which determines the number of entries in a fixed size array.
Definition: util.h:131
uint32 getHeight() const
Definition: texture.cpp:80
void addToQueue(QueueType queue)
Definition: queueable.cpp:45
Basic graphics types.
Texture.
Definition: types.h:182
Utility templates and functions for working with strings and streams.
int height
The mip map&#39;s height.
Definition: decoder.h:53
Exception that provides a stack of explanations.
Definition: error.h:36
void decompress()
Manually decompress the texture image data.
Definition: decoder.cpp:242
void exceptionDispatcherWarning(const char *s,...)
Exception dispatcher that prints the exception as a warning, and adds another reason on top...
Definition: error.cpp:158
const ImageDecoder & getImage() const
Return the image.
Definition: texture.cpp:106
DDS texture.
Definition: dds.h:44
Basic exceptions to throw.
Font, character bitmap data.
Definition: types.h:335
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
BioWare&#39;s own texture format, TPC.
Definition: tpc.h:42
Common::ScopedPtr< TXI > _txi
The TXI.
Definition: texture.h:90
Utility templates and functions.
void set(const Common::UString &name, ImageDecoder *image, ::Aurora::FileType type, TXI *txi, bool deswizzle=false)
Definition: texture.cpp:386
static ImageDecoder * loadImage(const Common::UString &name, bool deswizzle=false)
Load an image in any of the common texture formats.
Definition: texture.cpp:401
An image resource.
Definition: types.h:408
TXB (another one of BioWare&#39;s own texture formats) loading.
A class creating a cube map by combining six images.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
Low-level type definitions to handle fixed width types portably.
void setFilter(GLenum target)
Definition: texture.cpp:216
Texture information.
StackException Exception
Definition: error.h:59
Texture.
Definition: types.h:183
Common::UString _name
The name of the texture&#39;s image&#39;s file.
Definition: texture.h:86
RequestList::iterator RequestID
Definition: requests.h:48
Our very own intermediate texture format.
Intermediate texture.
Definition: types.h:390
void removeFromQueue(QueueType queue)
Definition: queueable.cpp:56
Basic reading stream interfaces.
Decoding SBM (font bitmap data).
uint32 getWidth() const
Definition: texture.cpp:76
virtual bool isDynamic() const
Is this a dynamic texture, or a shared static one?
Definition: texture.cpp:91
TextureID _textureID
OpenGL texture ID.
Definition: texture.h:42
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
bool dumpTGA(const Common::UString &fileName) const
Dump the texture into a TGA.
Definition: texture.cpp:139
int width
The mip map&#39;s width.
Definition: decoder.h:52
uint32 _height
Definition: h263.cpp:54
SBM font bitmap data.
Definition: sbm.h:40
Decoding TGA (TarGa) images.
A texture as used in the Aurora engines.
uint32_t uint32
Definition: types.h:204
Texture information.
Definition: txi.h:40
Texture features.
Definition: txi.h:55
FileType
Various file types used by the Aurora engine and found in archives.
Definition: types.h:56
TPC (BioWare&#39;s own texture format) loading.
Image, Truevision TARGA image.
Definition: types.h:61
A generic interface for image decoders.
Definition: decoder.h:48
Another one of BioWare&#39;s own texture formats, TXB.
Definition: txb.h:44
virtual bool reload()
Try to reload the texture.
Definition: texture.cpp:112
const Features & getFeatures() const
Definition: txi.cpp:283
void setMipMaps(GLenum target)
Definition: texture.cpp:228
size_t getMipMapCount() const
Return the number of mip maps contained in the image.
Definition: decoder.cpp:188
uint32 _width
Definition: h263.cpp:53
::Aurora::FileType _type
The type of the texture&#39;s image&#39;s file.
Definition: texture.h:87
uint32 size
The mip map&#39;s size in bytes.
Definition: decoder.h:54
Interface for a seekable & readable data stream.
Definition: readstream.h:265
Texture, DirectDraw Surface.
Definition: types.h:98
BioWare&#39;s Packed Layered Texture.
#define GfxMan
Shortcut for accessing the graphics manager.
Definition: graphics.h:299
The global resource manager for Aurora resources.
void setWrap(GLenum target, GLint wrapModeX, GLint wrapModeY)
Definition: texture.cpp:172
A texture.
Definition: types.h:72
PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D
Definition: graphics.cpp:71
Common::ScopedPtr< ImageDecoder > _image
The actual image.
Definition: texture.h:89