/* Create a tinted texture by combining the tint map with the tint colors. * * This is currently all done here in software and has several drawbacks: * - Slow * - We're creating lots of uncompressed RBGA texture, so it * takes up a lot of memory, both on the GPU and in system RAM * * TODO: We really need to do this in shaders in the future. */ void ModelNode_NWN2::createTint() { if (_tintMap.empty()) return; ImageDecoder *tintMap = 0; Surface *tintedMap = 0; try { // Load and uncompress the texture tintMap = Texture::loadImage(_tintMap); if (tintMap->isCompressed()) tintMap->decompress(); const ImageDecoder::MipMap &tintImg = tintMap->getMipMap(0); // Create a new target surface with the same dimensions tintedMap = new Surface(tintImg.width, tintImg.height); ImageDecoder::MipMap &tintedImg = tintedMap->getMipMap(); // Iterate over all pixels: read values, mix tints, write pixel to target surface for (int n = 0; n < tintImg.width * tintImg.height; n++) { float srcColor[4], dstColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; tintImg.getPixel(n, srcColor[0], srcColor[1], srcColor[2], srcColor[3]); if (srcColor[3] != 0.0f) { // Mix using the value and the alpha components as intensities // TODO: Verify how the mixing is actually done in NWN2! for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) dstColor[j] += _tint[i][j] * srcColor[i] * _tint[i][3] * srcColor[3] * _diffuse[i]; } } else // Source alpha is 0.0f: No tinting for this pixel for (int i = 0; i < 3; i++) dstColor[i] = _diffuse[i] * _tint[i][3]; tintedImg.setPixel(n, dstColor[0], dstColor[1], dstColor[2], dstColor[3]); } } catch (...) { delete tintMap; delete tintedMap; return; } delete tintMap; // And add the new texture to the TextureManager TextureHandle tintedTexture = TextureMan.add(Texture::create(tintedMap)); _textures.push_back(tintedTexture); _tintedMapIndex = _textures.size() - 1; }
ImageDecoder *Texture::loadImage(Common::SeekableReadStream *imageStream, ::Aurora::FileType type, TXI *txi) { // Check for a cube map, but only those that don't use a file for each side const bool isCubeMap = txi && txi->getFeatures().cube && (txi->getFeatures().fileRange == 0); ImageDecoder *image = 0; try { // Loading the different image formats if (type == ::Aurora::kFileTypeTGA) image = new TGA(*imageStream, isCubeMap); else if (type == ::Aurora::kFileTypeDDS) image = new DDS(*imageStream); else if (type == ::Aurora::kFileTypeTPC) image = new TPC(*imageStream); else if (type == ::Aurora::kFileTypeTXB) image = new TXB(*imageStream); else if (type == ::Aurora::kFileTypeSBM) image = new SBM(*imageStream); else if (type == ::Aurora::kFileTypeXEOSITEX) image = new XEOSITEX(*imageStream); else throw Common::Exception("Unsupported image resource type %d", (int) type); if (image->getMipMapCount() < 1) throw Common::Exception("Texture has no images"); // Decompress if (GfxMan.needManualDeS3TC()) image->decompress(); } catch (...) { delete image; delete imageStream; throw; } delete imageStream; return image; }
ImageDecoder *Texture::loadImage(Common::SeekableReadStream *imageStream, ::Aurora::FileType type) { ImageDecoder *image = 0; try { // Loading the different image formats if (type == ::Aurora::kFileTypeTGA) image = new TGA(*imageStream); else if (type == ::Aurora::kFileTypeDDS) image = new DDS(*imageStream); else if (type == ::Aurora::kFileTypeTPC) image = new TPC(*imageStream); else if (type == ::Aurora::kFileTypeTXB) image = new TXB(*imageStream); else if (type == ::Aurora::kFileTypeSBM) image = new SBM(*imageStream); else if (type == ::Aurora::kFileTypeXEOSITEX) image = new XEOSITEX(*imageStream); else throw Common::Exception("Unsupported image resource type %d", (int) type); if (image->getMipMapCount() < 1) throw Common::Exception("Texture has no images"); // Decompress if (GfxMan.needManualDeS3TC()) image->decompress(); } catch (...) { delete image; delete imageStream; throw; } delete imageStream; return image; }