/* Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html OpenGL Texture Manager, by Loren Petrich, March 12, 2000 This implements texture handling for OpenGL. May 2, 2000: Fixed silhouette-texture bug: color 0 is now transparent May 24, 2000: Added support for setting landscape aspect ratios from outside; also added more graceful degradation for mis-sized textures. Walls must be a power of 2 horizontally and vertical; landscapes must be a power of 2 horizontally in order for the tiling to work properly. June 11, 2000: Added support for opacity shift factor (OpacityShift alongside OpacityScale); should be good for making dark colors somewhat opaque. Jul 10, 2000: Fixed crashing bug when OpenGL is inactive with ResetTextures() Sep 9, 2000: Restored old fix for AppleGL texturing as an option; this fix consists of setting the minimum size of a texture to be 128. Nov 12, 2000 (Loren Petrich): Cleaned up some of the code to avoid explicit endianness usage; also implemented texture substitution. Nov 18, 2000 (Loren Petrich): Added support for landscape vertical repeats; also added support for glow mapping of wall textures Dec 16, 2000 (Loren Petrich): Fixed substitution of landscape textures June 14, 2001 (Loren Petrich): Changed Width*Height to TxtrWidth*TxtrHeight in some places to ensure that some operations are done over complete textures Nov 30, 2001 (Alexander Strange): Added Ian Rickard's texture purging to save VRAM. Jan 25, 2002 (Br'fin (Jeremy Parsons)): Added TARGET_API_MAC_CARBON for AGL.h May 3, 2003 (Br'fin (Jeremy Parsons)) Added LowLevelShape workaround for passing LowLevelShape info of sprites instead of abusing/overflowing shape_descriptors */ #include #include #include #include #include #include "cseries.h" #ifdef HAVE_OPENGL #ifdef __MVCPP__ #include #endif #ifdef HAVE_OPENGL # if defined (__APPLE__) && defined (__MACH__) # include # include # include # elif defined mac # include # include # include # else #ifndef __WIN32__ # ifndef GL_GLEXT_PROTOTYPES # define GL_GLEXT_PROTOTYPES 1 #endif # endif # include # include # ifdef HAVE_GL_GLEXT_H # include # endif # endif #endif #ifdef __WIN32__ #include "OGL_Win32.h" #endif #ifdef mac # if defined(EXPLICIT_CARBON_HEADER) # include # else # include # endif #endif #include "SDL.h" #include "SDL_endian.h" #include "interface.h" #include "render.h" #include "map.h" #include "collection_definition.h" #include "OGL_Setup.h" #include "OGL_Render.h" #include "OGL_Textures.h" OGL_TexturesStats gGLTxStats = {0,0,0,500000,0,0, 0}; // Texture mapping struct TxtrTypeInfoData { GLenum NearFilter; // OpenGL parameter for near filter (GL_NEAREST, etc.) GLenum FarFilter; // OpenGL parameter for far filter (GL_NEAREST, etc.) int Resolution; // 0 is full-sized, 1 is half-sized, 2 is fourth-sized GLenum ColorFormat; // OpenGL parameter for stored color format (RGBA8, etc.) }; static TxtrTypeInfoData TxtrTypeInfoList[OGL_NUMBER_OF_TEXTURE_TYPES]; static bool useSGISMipmaps = false; // Infravision: use algorithm (red + green + blue)/3 to compose intensity, // then shade with these colors, one color for each collection. struct InfravisionData { GLfloat Red, Green, Blue; // Infravision tint components: 0 to 1 bool IsTinted; // whether to use infravision with this collection }; struct InfravisionData IVDataList[NUMBER_OF_COLLECTIONS] = { {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false}, {1,1,1,false} }; // Is infravision currently active? static bool InfravisionActive = false; static list sgActiveTextureStates; // Allocate some textures and indicate whether an allocation had happened. bool TextureState::Allocate(short txType) { TextureType = txType; if (!IsUsed) { sgActiveTextureStates.push_front(this); gGLTxStats.inUse++; glGenTextures(NUMBER_OF_TEXTURES,IDs); IsUsed = true; unusedFrames=0; return true; } return false; } // Use a texture and indicate whether to load it bool TextureState::Use(int Which) { glBindTexture(GL_TEXTURE_2D,IDs[Which]); bool result = !TexGened[Which]; TexGened[Which] = true; IDUsage[Which]++; return result; } // Resets the object's texture state void TextureState::Reset() { if (IsUsed) { sgActiveTextureStates.remove(this); gGLTxStats.inUse--; glDeleteTextures(NUMBER_OF_TEXTURES,IDs); } IsUsed = IsGlowing = TexGened[Normal] = TexGened[Glowing] = false; IDUsage[Normal] = IDUsage[Glowing] = unusedFrames = 0; } void TextureState::FrameTick() { if (!IsUsed) return; gGLTxStats.totalAge += (TextureType!=OGL_Txtr_Landscape)?unusedFrames:0; bool used = false; for (int i=0 ; i 300) Reset(); // at least 10 seconds till wall textures are released break; case OGL_Txtr_Landscape: // never release landscapes break; case OGL_Txtr_Inhabitant: if (unusedFrames > 450) Reset(); // release unused sprites in 15 seconds break; case OGL_Txtr_WeaponsInHand: if (unusedFrames > 600) Reset(); // release weapons in hand in 20 seconds break; } } } // Will distinguish by texture type as well as by collection; // this is because different rendering modes deserve different treatment. static CollBitmapTextureState* TextureStateSets[OGL_NUMBER_OF_TEXTURE_TYPES][MAXIMUM_COLLECTIONS]; #ifdef SDL extern GLuint OGL_Term_Texture; #endif // Initialize the texture accounting void OGL_StartTextures() { // Initialize the texture accounting proper for (int it=0; it::iterator i; for (i=sgActiveTextureStates.begin() ; i!= sgActiveTextureStates.end() ; i++) { (*i)->FrameTick(); } } // Find an OpenGL-friendly color table from a Marathon shading table static void FindOGLColorTable(int NumSrcBytes, byte *OrigColorTable, uint32 *ColorTable) { // Stretch the original color table to 4 bytes per value for OpenGL convenience; // all the intermediate calculations will be done in RGBA 8888 form, // because that is what OpenGL prefers as a texture input switch(NumSrcBytes) { case 2: for (int k=0; k>= 8; } } // This processes the bytes from highest to lowest GLuint AvgVal = 0; for (int q=0; q<4; q++) { AvgVal <<= 8; // Must come before adding in a byte AvgVal |= PIN(int(Sum[3-q]/Length + 0.5),0,255); } for (int k=0; kIsPresent()) { // Override if textures had been substituted; // if the normal texture had been substituted, it will be assumed to be // non-glowing unless the glow texture has also been substituted. IsGlowing = true; } else { IsGlowing = false; } CTState.IsGlowing = IsGlowing; // Load the fake landscape if selected if (TextureType == OGL_Txtr_Landscape) { if (IsLandscapeFlatColored()) { NormalImage.edit(new ImageDescriptor(TxtrWidth, TxtrHeight, GetFakeLandscape())); } } // If not, then load the expected textures if (!NormalImage.get() || !NormalImage.get()->IsPresent()) NormalImage.edit(new ImageDescriptor(TxtrWidth, TxtrHeight, GetOGLTexture(NormalColorTable))); if (IsGlowing && (!GlowImage.get() || !GlowImage.get()->IsPresent())) GlowImage.edit(new ImageDescriptor(TxtrWidth, TxtrHeight, GetOGLTexture(GlowColorTable))); // Display size: may be shrunk int MaxWidth = MAX(TxtrWidth >> TxtrTypeInfo.Resolution, 1); int MaxHeight = MAX(TxtrHeight >> TxtrTypeInfo.Resolution, 1); // Fit the image into the maximum size allowed by the OpenGL implementation in use GLint MaxTextureSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE,&MaxTextureSize); while (MaxWidth > MaxTextureSize || MaxHeight > MaxTextureSize) { LoadedWidth >>= 1; LoadedHeight >>= 1; } while (NormalImage.get()->GetWidth() > MaxWidth || NormalImage.get()->GetHeight() > MaxHeight) { if (!NormalImage.edit()->Minify()) break; if (GlowImage.get() && GlowImage.get()->IsPresent()) { if (!GlowImage.edit()->Minify()) break; } } // Kludge for making top and bottom look flat /* if (TextureType == OGL_Txtr_Landscape) { MakeAverage(LoadedWidth,NormalBuffer); MakeAverage(LoadedWidth,NormalBuffer+LoadedWidth*(LoadedHeight-1)); } */ } else { // Get sprite scale/offset U_Scale = CBTS.U_Scale; V_Scale = CBTS.V_Scale; U_Offset = CBTS.U_Offset; V_Offset = CBTS.V_Offset; // Get glow state IsGlowing = CTState.IsGlowing; } // Done!!! return true; } inline bool WhetherTextureFix() { OGL_ConfigureData& ConfigureData = Get_OGL_ConfigureData(); return TEST_FLAG(ConfigureData.Flags,OGL_Flag_TextureFix); } // Conversion of color data types inline int MakeEightBit(GLfloat Chan) { return int(PIN(int(255*Chan+0.5),0,255)); } uint32 MakeIntColor(GLfloat *FloatColor) { uint32 IntColor; uint8 *ColorPtr = (uint8 *)(&IntColor); for (int k=0; k<4; k++) ColorPtr[k] = MakeEightBit(FloatColor[k]); return IntColor; } void MakeFloatColor(uint32 IntColor, GLfloat *FloatColor) { uint8 *ColorPtr = (uint8 *)(&IntColor); for (int k=0; k<4; k++) FloatColor[k] = float(ColorPtr[k])/float(255); } bool TextureManager::LoadSubstituteTexture() { // Is there a texture to be substituted? ImageDescriptor& NormalImg = TxtrOptsPtr->NormalImg; if (!NormalImg.IsPresent()) return false; // Be sure to take care of the glowing version, where supported ImageDescriptor& GlowImg = TxtrOptsPtr->GlowImg; // Idiot-proofing if (NormalBuffer) { delete []NormalBuffer; NormalBuffer = NULL; } if (GlowBuffer) { delete []GlowBuffer; GlowBuffer = NULL; } NormalImage.set(&TxtrOptsPtr->NormalImg); GlowImage.set(&TxtrOptsPtr->GlowImg); int Width = NormalImg.GetWidth(); int Height = NormalImg.GetHeight(); int HeightOffset; int OrigHeightOffset, OGLHeightOffset, CopyingHeight; switch(TextureType) { case OGL_Txtr_Wall: // For tiling to be possible, the width and height must be powers of 2; // also, be sure to transpose the texture TxtrHeight = Height; TxtrWidth = Width; if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false; if (TxtrHeight != NextPowerOfTwo(TxtrHeight)) return false; TxtrOptsPtr->Substitution = true; break; case OGL_Txtr_Landscape: // For tiling to be possible, the width must be a power of 2; // the height need not be such a power. TxtrWidth = Width; TxtrHeight = (Landscape_AspRatExp >= 0) ? (TxtrWidth >> Landscape_AspRatExp) : (TxtrWidth << (-Landscape_AspRatExp)); if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false; // the renderer doesn't use these, // so I'll use them to get the texture matrix set up right U_Scale = (float) TxtrHeight / NormalImg.GetHeight(); U_Offset = -1.0 + ((TxtrHeight - NormalImg.GetHeight()) / 2.0 / TxtrHeight) + (1.0 - NormalImg.GetUScale()) / 2.0; TxtrOptsPtr->Substitution = true; GlowImage.set((ImageDescriptor *) NULL); break; case OGL_Txtr_Inhabitant: case OGL_Txtr_WeaponsInHand: // Much of the code here has been copied from elsewhere. // Set these for convenience; sprites are transposed, as walls are. TxtrHeight = Height; TxtrWidth = Width; // ImageLoader now stores these as powers of two sized if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false; if (TxtrHeight != NextPowerOfTwo(TxtrHeight)) return false; // We can calculate the scales and offsets here V_Scale = NormalImg.GetVScale(); V_Offset = 0; U_Scale = NormalImg.GetUScale(); U_Offset = 0; TxtrOptsPtr->Substitution = true; break; } // Use the Tomb Raider opacity hack if selected SetPixelOpacities(*TxtrOptsPtr, NormalImage); // Modify if infravision is active if (CTable == INFRAVISION_BITMAP_SET) { FindInfravisionVersion(Collection, NormalImage); // Infravision textures don't glow GlowImage.set((ImageDescriptor *) NULL); } else if (CTable == SILHOUETTE_BITMAP_SET) { FindSilhouetteVersion(NormalImage); GlowImage.set((ImageDescriptor *) NULL); } return true; } bool TextureManager::SetupTextureGeometry() { // How many rows (scanlines) and columns if (Texture->flags&_COLUMN_ORDER_BIT) { BaseTxtrWidth = Texture->height; BaseTxtrHeight = Texture->width; } else { BaseTxtrWidth = Texture->width; BaseTxtrHeight = Texture->height; } short RowBytes = Texture->bytes_per_row; if (RowBytes != NONE) if (BaseTxtrWidth != RowBytes) return false; // The default WidthOffset = HeightOffset = 0; switch(TextureType) { case OGL_Txtr_Wall: // For tiling to be possible, the width and height must be powers of 2 TxtrWidth = BaseTxtrWidth; TxtrHeight = BaseTxtrHeight; if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false; if (TxtrHeight != NextPowerOfTwo(TxtrHeight)) return false; break; case OGL_Txtr_Landscape: if (IsLandscapeFlatColored()) { TxtrWidth = 128; TxtrHeight = 128; } else { // Width is horizontal direction here TxtrWidth = BaseTxtrWidth; if (TxtrWidth != NextPowerOfTwo(TxtrWidth)) return false; // Use the landscape height here TxtrHeight = (Landscape_AspRatExp >= 0) ? (TxtrWidth >> Landscape_AspRatExp) : (TxtrWidth << (-Landscape_AspRatExp)); // Offsets WidthOffset = (TxtrWidth - BaseTxtrWidth) >> 1; HeightOffset = (TxtrHeight - BaseTxtrHeight) >> 1; } break; case OGL_Txtr_Inhabitant: case OGL_Txtr_WeaponsInHand: { // The 2 here is so that there will be an empty border around a sprite, // so that the texture can be conveniently mipmapped. TxtrWidth = NextPowerOfTwo(BaseTxtrWidth+2); TxtrHeight = NextPowerOfTwo(BaseTxtrHeight+2); // This kludge no longer necessary // Restored due to some people still having AppleGL 1.1.2 if (WhetherTextureFix()) { TxtrWidth = MAX(TxtrWidth,128); TxtrHeight = MAX(TxtrHeight,128); } // Offsets WidthOffset = (TxtrWidth - BaseTxtrWidth) >> 1; HeightOffset = (TxtrHeight - BaseTxtrHeight) >> 1; // We can calculate the scales and offsets here double TWidRecip = 1/double(TxtrWidth); double THtRecip = 1/double(TxtrHeight); U_Scale = TWidRecip*double(BaseTxtrWidth); U_Offset = TWidRecip*WidthOffset; V_Scale = THtRecip*double(BaseTxtrHeight); V_Offset = THtRecip*HeightOffset; } break; } // Success! return true; } void TextureManager::FindColorTables() { // Default IsGlowing = false; // The silhouette case is easy if (CTable == SILHOUETTE_BITMAP_SET) { NormalColorTable[0] = 0; for (int k=1; k> 8; p[idx * 4 + 1] = q[k].green >> 8; p[idx * 4 + 2] = q[k].blue >> 8; p[idx * 4 + 3] = 0xff; } SetPixelOpacitiesRGBA(*TxtrOptsPtr, MAXIMUM_SHADING_TABLE_INDEXES, NormalColorTable); NormalColorTable[0] = 0; return; } // Number of source bytes, for reading off of the shading table // IR change: dithering short NumSrcBytes = bit_depth / 8; // Shadeless polygons use the first, instead of the last, shading table byte *OrigColorTable = (byte *)ShadingTables; byte *OrigGlowColorTable = OrigColorTable; if (!IsShadeless) OrigColorTable += NumSrcBytes*(number_of_shading_tables - 1)*MAXIMUM_SHADING_TABLE_INDEXES; // Find the normal color table, // and set its opacities as if there was no glow table. FindOGLColorTable(NumSrcBytes,OrigColorTable,NormalColorTable); SetPixelOpacitiesRGBA(*TxtrOptsPtr,MAXIMUM_SHADING_TABLE_INDEXES,NormalColorTable); // Find the glow-map color table; // only inhabitants are glowmapped. // Also, it seems that only infravision textures are shadeless. if (!IsShadeless && (TextureType != OGL_Txtr_Landscape)) { // Find the glow table from the lowest-illumination color table FindOGLColorTable(NumSrcBytes,OrigGlowColorTable,GlowColorTable); // Search for self-luminous colors; ignore the first one as the transparent one for (int k=1; k= 0x0f) EntryIsGlowing = true; // Make the glow color the original color, to get continuity for (int q=0; q<3; q++) GlowEntry[q] = NormalEntry[q]; if (EntryIsGlowing && NormalEntry[3]) { IsGlowing = true; // Make half-opaque, to get more like the software rendering float Opacity = NormalEntry[3]/float(0xff); NormalEntry[3] = MakeEightBit(Opacity/(2-Opacity)); GlowEntry[3] = MakeEightBit(Opacity/2); } else { // Make transparent, to get appropriate continuity GlowEntry[3] = 0; } } } // The first color is always the transparent color, // except if it is a landscape color if (TextureType != OGL_Txtr_Landscape) {NormalColorTable[0] = 0; GlowColorTable[0] = 0;} // PremultiplyColorTables(); } void TextureManager::PremultiplyColorTables() { #ifdef ALEPHONE_LITTLE_ENDIAN uint32 alphaMask = 0xff000000; #else uint32 alphaMask = 0x000000ff; #endif uint32 *tables[2]; tables[0] = NormalColorTable; if (!IsShadeless && (TextureType != OGL_Txtr_Landscape)) tables[1] = GlowColorTable; else tables[1] = 0; for (int table = 0; table < 2; table++) { if (!tables[table]) continue; for (int k = 0; k < MAXIMUM_SHADING_TABLE_INDEXES; k++) { if ((tables[table][k] & alphaMask) == alphaMask) continue; if ((tables[table][k] & alphaMask) == 0) { tables[table][k] = 0; continue; } short r, g, b, a; uint8 *PxlPtr = (uint8 *) &tables[table][k]; r = PxlPtr[0]; g = PxlPtr[1]; b = PxlPtr[2]; a = PxlPtr[3]; r = (a * r + 127) / 255; g = (a * g + 127) / 255; b = (a * b + 127) / 255; PxlPtr[0] = (uint8) r; PxlPtr[1] = (uint8) g; PxlPtr[2] = (uint8) b; } } } uint32 *TextureManager::GetOGLTexture(uint32 *ColorTable) { // Allocate and set to black and transparent int NumPixels = int(TxtrWidth)*int(TxtrHeight); uint32 *Buffer = new uint32[NumPixels]; objlist_clear(Buffer,NumPixels); // The dimension, the offset in the original texture, and the offset in the OpenGL texture short Width, OrigWidthOffset, OGLWidthOffset; short Height, OrigHeightOffset, OGLHeightOffset; // Calculate original-texture and OpenGL-texture offsets // and how many scanlines to do. // The loop start points and counts are set so that // only valid source and destination pixels // will get worked with (no off-edge ones, that is). if (HeightOffset >= 0) { Height = BaseTxtrHeight; OrigHeightOffset = 0; OGLHeightOffset = HeightOffset; } else { Height = TxtrHeight; OrigHeightOffset = - HeightOffset; OGLHeightOffset = 0; } if (Texture->bytes_per_row == NONE) { short horig = OrigHeightOffset; uint32 *OGLRowStart = Buffer + TxtrWidth*OGLHeightOffset; for (short h=0; hrow_addresses[horig]; uint32 *OGLStrip = OGLRowStart; // Cribbed from textures.c: // This is the Marathon 2 sprite-interpretation scheme; // assumes big-endian data // First destination location uint16 First = uint16(*(OrigStrip++)) << 8; First |= uint16(*(OrigStrip++)); // Last destination location (last pixel is just before it) uint16 Last = uint16(*(OrigStrip++)) << 8; Last |= uint16(*(OrigStrip++)); // Calculate original-texture and OpenGL-texture offsets // and how many pixels to do OrigWidthOffset = 0; OGLWidthOffset = WidthOffset + First; if (OGLWidthOffset < 0) { OrigWidthOffset -= OGLWidthOffset; OGLWidthOffset = 0; } short OrigWidthFinish = Last - First; short OGLWidthFinish = WidthOffset + Last; short OGLWidthExcess = OGLWidthFinish - TxtrWidth; if (OGLWidthExcess > 0) { OrigWidthFinish -= OGLWidthExcess; OGLWidthFinish = TxtrWidth; } short Width = OrigWidthFinish - OrigWidthOffset; OrigStrip += OrigWidthOffset; OGLStrip += OGLWidthOffset; for (short w=0; w= 0) { Width = BaseTxtrWidth; OrigWidthOffset = 0; OGLWidthOffset = WidthOffset; } else { Width = TxtrWidth; OrigWidthOffset = - WidthOffset; OGLWidthOffset = 0; } short horig = OrigHeightOffset; uint32 *OGLRowStart = Buffer + TxtrWidth*OGLHeightOffset + OGLWidthOffset; for (short h=0; hrow_addresses[horig] + OrigWidthOffset; uint32 *OGLStrip = OGLRowStart; for (short w=0; wsong_index; if (!LandscapesLoaded || (LscpIndx < 0 && LscpIndx >= 4)) { memset(Buffer,0,NumPixels*sizeof(uint32)); return Buffer; } RGBColor OrigLandColor = ConfigureData.LscpColors[LscpIndx][0]; RGBColor OrigSkyColor = ConfigureData.LscpColors[LscpIndx][1]; // Set up floating-point ones, complete with alpha channel GLfloat LandColor[4], SkyColor[4]; MakeFloatColor(OrigLandColor,LandColor); LandColor[3] = 1; MakeFloatColor(OrigSkyColor,SkyColor); SkyColor[3] = 1; // Modify if infravision is active if (CTable == INFRAVISION_BITMAP_SET) { FindInfravisionVersionRGBA(Collection,LandColor); FindInfravisionVersionRGBA(Collection,SkyColor); } uint32 TxtrLandColor = MakeIntColor(LandColor); uint32 TxtrSkyColor = MakeIntColor(SkyColor); // Textures' vertical dimension is upward; // put in the land after the sky uint32 *BufPtr = Buffer; for (int h=0; hGetFormat() == ImageDescriptor::RGBA8) { switch (TxtrTypeInfo.FarFilter) { case GL_NEAREST: case GL_LINEAR: glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->GetBuffer()); break; case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: if (Image->GetMipMapCount() > 1) { #ifdef GL_SGIS_generate_mipmap if (useSGISMipmaps) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); } #endif int i = 0; for (i = 0; i < Image->GetMipMapCount(); i++) { glTexImage2D(GL_TEXTURE_2D, i, internalFormat, max(1, Image->GetWidth() >> i), max(1, Image->GetHeight() >> i), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->GetMipMapPtr(i)); } mipmapsLoaded = true; } else { #ifdef GL_SGIS_generate_mipmap if (useSGISMipmaps) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, Image->GetBuffer()); } else #endif { gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, Image->GetWidth(), Image->GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, Image->GetBuffer()); } mipmapsLoaded = true; } break; default: assert(false); } } else if (Image->GetFormat() == ImageDescriptor::DXTC1 || Image->GetFormat() == ImageDescriptor::DXTC3 || Image->GetFormat() == ImageDescriptor::DXTC5) { #if defined(GL_ARB_texture_compression) && defined(GL_COMPRESSED_RGB_S3TC_DXT1_EXT) if (Image->GetFormat() == ImageDescriptor::DXTC1) internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; else if (Image->GetFormat() == ImageDescriptor::DXTC3) internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; else if (Image->GetFormat() == ImageDescriptor::DXTC5) internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; switch(TxtrTypeInfo.FarFilter) { case GL_NEAREST: case GL_LINEAR: glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, Image->GetMipMapSize(0), Image->GetBuffer()); break; case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: if (Image->GetMipMapCount() > 1) { #ifdef GL_SGIS_generate_mipmap if (useSGISMipmaps) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); } #endif int i = 0; for (i = 0; i < Image->GetMipMapCount(); i++) { glCompressedTexImage2DARB(GL_TEXTURE_2D, i, internalFormat, max(1, Image->GetWidth() >> i), max(1, Image->GetHeight() >> i), 0, Image->GetMipMapSize(i), Image->GetMipMapPtr(i)); } mipmapsLoaded = true; } else { #if defined GL_SGIS_generate_mipmap if (useSGISMipmaps) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); mipmapsLoaded = true; } #endif glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, internalFormat, Image->GetWidth(), Image->GetHeight(), 0, Image->GetMipMapSize(0), Image->GetBuffer()); } break; default: // Shouldn't happen assert(false); } #else assert(false); #endif } // Set texture-mapping features glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TxtrTypeInfo.NearFilter); if ((TxtrTypeInfo.FarFilter == GL_NEAREST_MIPMAP_NEAREST || TxtrTypeInfo.FarFilter == GL_LINEAR_MIPMAP_NEAREST || TxtrTypeInfo.FarFilter == GL_NEAREST_MIPMAP_LINEAR || TxtrTypeInfo.FarFilter == GL_LINEAR_MIPMAP_LINEAR) && !mipmapsLoaded) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TxtrTypeInfo.FarFilter); } switch(TextureType) { case OGL_Txtr_Wall: // Walls are tiled in both direction glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); #if defined(GL_TEXTURE_MAX_ANISOTROPY_EXT) // enable anisotropic filtering { float anisoLevel = Get_OGL_ConfigureData().AnisotropyLevel; if (anisoLevel > 0.0) { GLfloat max_aniso; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0F + ((anisoLevel-1.0F)/15.0F)*(max_aniso-1.0F)); } } #endif break; case OGL_Txtr_Landscape: // Landscapes repeat horizontally, have vertical limits or repeats vertically glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); if (LandscapeVertRepeat) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); else glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); break; case OGL_Txtr_Inhabitant: case OGL_Txtr_WeaponsInHand: // Sprites have both horizontal and vertical limits glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); break; } } // What to render: // Always call this one and call it first; safe to allocate texture ID's in it void TextureManager::RenderNormal() { TxtrStatePtr->Allocate(TextureType); if (TxtrStatePtr->UseNormal()) { assert(NormalBuffer || (NormalImage.get() && NormalImage.get()->IsPresent())); if (NormalImage.get() && NormalImage.get()->IsPresent()) { PlaceTexture(NormalImage.get()); } } gGLTxStats.binds++; int time = 0; gGLTxStats.totalBind += time; if (gGLTxStats.minBind > time) gGLTxStats.minBind = time; if (gGLTxStats.maxBind < time) gGLTxStats.maxBind = time; if (time>2) gGLTxStats.longNormalSetups++; } // Call this one after RenderNormal() void TextureManager::RenderGlowing() { if (TxtrStatePtr->UseGlowing()) { assert(GlowBuffer || (GlowImage.get() && GlowImage.get()->IsPresent())); if (GlowImage.get() && GlowImage.get()->IsPresent()) { PlaceTexture(GlowImage.get()); } } gGLTxStats.binds++; int time = 0; gGLTxStats.totalBind += time; if (gGLTxStats.minBind > time) gGLTxStats.minBind = time; if (gGLTxStats.maxBind < time) gGLTxStats.maxBind = time; if (time>2) gGLTxStats.longGlowSetups++; } void TextureManager::SetupTextureMatrix() { // set up the texture matrix switch(TextureType) { case OGL_Txtr_Wall: case OGL_Txtr_WeaponsInHand: case OGL_Txtr_Inhabitant: glMatrixMode(GL_TEXTURE); glLoadIdentity(); if (TxtrOptsPtr->Substitution) { // these come in right side up, but the renderer // expects them to be upside down and sideways glRotatef(90.0, 0.0, 0.0, 1.0); glScalef(1.0, -1.0, 1.0); } glMatrixMode(GL_MODELVIEW); break; case OGL_Txtr_Landscape: glMatrixMode(GL_TEXTURE); glLoadIdentity(); if (TxtrOptsPtr->Substitution) { // these come in right side up, and un-centered // the renderer expects them upside down, and centered glScalef(1.0, -U_Scale, 1.0); glTranslatef(0.0, U_Offset, 0.0); } glMatrixMode(GL_MODELVIEW); break; } } void TextureManager::RestoreTextureMatrix() { switch(TextureType) { case OGL_Txtr_Wall: case OGL_Txtr_WeaponsInHand: case OGL_Txtr_Inhabitant: case OGL_Txtr_Landscape: glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); } } // Init TextureManager::TextureManager() { NormalBuffer = 0; GlowBuffer = 0; ShadingTables = NULL; TransferMode = 0; TransferData = 0; IsShadeless = false; TextureType = 0; LandscapeVertRepeat = false; TxtrStatePtr = 0; TxtrOptsPtr = 0; FastPath = 0; LowLevelShape = 0; // Marathon default Landscape_AspRatExp = 1; } // Cleanup TextureManager::~TextureManager() { if (NormalBuffer != 0) delete []NormalBuffer; if (GlowBuffer != 0) delete []GlowBuffer; } extern void ResetScreenFont(); extern void OGL_ResetMapFonts(bool IsStarting); extern void OGL_ResetHUDFonts(bool IsStarting); void OGL_ResetTextures() { // Fix for crashing bug when OpenGL is inactive if (!OGL_IsActive()) return; // Reset the textures: for (int it=0; it ImageBuffer; vector ShrunkBuffer; int TxtrWidth = Image.GetWidth(); int TxtrHeight = Image.GetHeight(); Buffer = (GLuint*)Image.GetPixelBasePtr(); bool IsInfravision = (CLUT == INFRAVISION_BITMAP_SET); bool IsSilhouette = (CLUT == SILHOUETTE_BITMAP_SET); // Use special texture buffer because the originals may be needed for "normal" textures int ImageSize = Image.GetNumPixels(); if (IsInfravision || IsSilhouette) { ImageBuffer.resize(ImageSize); objlist_copy(&ImageBuffer[0],Buffer,ImageSize); Buffer = (GLuint*)&ImageBuffer[0]; } if (IsInfravision) FindInfravisionVersionRGBA(Collection,ImageSize,(uint32*)Buffer);//AS added cast for Darwin compileability if (IsSilhouette) { for (int k=0; k> TxtrTypeInfo.Resolution, 1); int LoadedHeight = MAX(TxtrHeight >> TxtrTypeInfo.Resolution, 1); // Fit the image into the maximum size allowed by the OpenGL implementation in use GLint MaxTextureSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE,&MaxTextureSize); while (LoadedWidth > MaxTextureSize || LoadedHeight > MaxTextureSize) { LoadedWidth >>= 1; LoadedHeight >>= 1; } if (LoadedWidth != TxtrWidth || LoadedHeight != TxtrHeight) { int NumPixels = int(LoadedWidth)*int(LoadedHeight); ShrunkBuffer.resize(NumPixels); gluScaleImage(GL_RGBA, TxtrWidth, TxtrHeight, GL_UNSIGNED_BYTE, Buffer, LoadedWidth, LoadedHeight, GL_UNSIGNED_BYTE, &ShrunkBuffer[0]); Buffer = &ShrunkBuffer[0]; } // Load the texture switch(TxtrTypeInfo.FarFilter) { case GL_NEAREST: case GL_LINEAR: glTexImage2D(GL_TEXTURE_2D, 0, TxtrTypeInfo.ColorFormat, LoadedWidth, LoadedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, Buffer); break; case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: gluBuild2DMipmaps(GL_TEXTURE_2D, TxtrTypeInfo.ColorFormat, LoadedWidth, LoadedHeight, GL_RGBA, GL_UNSIGNED_BYTE, Buffer); break; default: // Shouldn't happen assert(false); } // Set texture-mapping features glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TxtrTypeInfo.NearFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TxtrTypeInfo.FarFilter); // Like sprites, model textures have both horizontal and vertical limits glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } // Infravision (I'm blue, are you?) bool& IsInfravisionActive() {return InfravisionActive;} // Sets the infravision tinting color for a shapes collection, and whether to use such tinting; // the color values are from 0 to 1. bool SetInfravisionTint(short Collection, bool IsTinted, float Red, float Green, float Blue) { assert(Collection >= 0 && Collection < NUMBER_OF_COLLECTIONS); InfravisionData& IVData = IVDataList[Collection]; IVData.Red = Red; IVData.Green = Green; IVData.Blue = Blue; IVData.IsTinted = IsTinted; return true; } // Finds the infravision version of a color for some collection set; // it makes no change if infravision is inactive. void FindInfravisionVersionRGBA(short Collection, GLfloat *Color) { if (!InfravisionActive) return; InfravisionData& IVData = IVDataList[Collection]; if (!IVData.IsTinted) return; GLfloat AvgColor = (Color[0] + Color[1] + Color[2])/3; Color[0] = IVData.Red*AvgColor; Color[1] = IVData.Green*AvgColor; Color[2] = IVData.Blue*AvgColor; } // Mass-production version of above; suitable for textures void FindInfravisionVersionRGBA(short Collection, int NumPixels, uint32 *Pixels) { if (!InfravisionActive) return; InfravisionData& IVData = IVDataList[Collection]; if (!IVData.IsTinted) return; // OK to use marching-pointer optimization here; // the float-to-int and int-to-float conversions have been simplified, // because the infravision-value-finding does not care if the values // had been multipled by 255 (int <-> float color-value multiplier/divider) for (int k=0; k> 11) + ((color & 0x7e0) >> 6) + (color & 0x1f)) / 3; uint16 r = (grayscale * tint.r) / 256; uint16 g = (grayscale * tint.g) / 256; uint16 b = (grayscale * tint.b) / 256; return (r << 11) | (g << 6) | (g > 15 ? 0x20 : 0) | b; } void FindInfravisionVersionDXTC1(InfravisionData& IVData, int NumBytes, unsigned char *buffer) { assert(NumBytes % 8 == 0); uint16 *pixels = (uint16 *) buffer; SDL_Color tint; tint.r = PIN(int(IVData.Red * 256), 0, 255); tint.g = PIN(int(IVData.Green * 256), 0, 255); tint.b = PIN(int(IVData.Blue* 256), 0, 255); // the first two uint16s in each block are our colors for (int i = 0; i < NumBytes / 4; i++) { uint16 c1 = SDL_SwapLE16(pixels[i * 4]); uint16 c2 = SDL_SwapLE16(pixels[i * 4 + 1]); uint16 new_c1 = FindInfravisionVersionDXTCColor(tint, c1); uint16 new_c2 = FindInfravisionVersionDXTCColor(tint, c2); // DXTC1 uses c1 > c2 to determine whether to make certain // pixels transparent // if c1 and c2 happen to come out of the infravision algorithm // the same, we have to chamge one so that pixels don't become // transparent that were opaque, or vice versa if (new_c1 == new_c2) { if (c1 > c2) if (new_c2) new_c2 -= 1; else new_c1 += 1; else if (new_c1) new_c1 -= 1; else new_c2 += 1; } // likewise, in the unlikely state that infravision ends up // making one bigger than the other, swap them back else if ((new_c1 > new_c2) != (c1 > c2)) { SWAP(new_c1, new_c2); } pixels[i * 4] = SDL_SwapLE16(new_c1); pixels[i * 4 + 1] = SDL_SwapLE16(new_c2); } } void FindInfavisionVersionDXTC35(InfravisionData &IVData, int NumBytes, unsigned char *buffer) { assert(NumBytes % 16 == 0); uint16 *pixels = (uint16 *) buffer; SDL_Color tint; tint.r = PIN(int(IVData.Red * 256), 0, 255); tint.g = PIN(int(IVData.Green * 256), 0, 255); tint.b = PIN(int(IVData.Blue* 256), 0, 255); for (int i = 0; i < NumBytes / 8; i++) { uint16 *c1 = &pixels[i * 8 + 4]; uint16 *c2 = &pixels[i * 8 + 5]; uint16 new_c1 = FindInfravisionVersionDXTCColor(tint, SDL_SwapLE16(*c1)); uint16 new_c2 = FindInfravisionVersionDXTCColor(tint, SDL_SwapLE16(*c2)); *c1 = SDL_SwapLE16(new_c1); *c2 = SDL_SwapLE16(new_c2); } } void FindInfravisionVersion(short Collection, ImageDescriptorManager &imageManager) { if (!InfravisionActive) return; InfravisionData& IVData = IVDataList[Collection]; if (!IVData.IsTinted) return; if (!imageManager.get() || !imageManager.get()->IsPresent()) return; if (imageManager.get()->GetFormat() == ImageDescriptor::RGBA8) { FindInfravisionVersionRGBA(Collection, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer()); } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC1) { FindInfravisionVersionDXTC1(IVData, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer()); } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC3 || imageManager.get()->GetFormat() == ImageDescriptor::DXTC5) { FindInfavisionVersionDXTC35(IVData, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer()); } } void FindSilhouetteVersionDXTC1(int NumBytes, unsigned char *buffer) { uint16 *pixels = (uint16 *) buffer; for (int i = 0; i < NumBytes / 4; i++) { if (SDL_SwapLE16(pixels[i * 4]) > SDL_SwapLE16(pixels[i * 4 + 1])) { #ifdef ALEPHONE_LITTLE_ENDIAN pixels[i * 4 + 1] = 0xffdf; #else pixels[i * 4 + 1] = 0xdfff; #endif } else { pixels[i * 4 + 1] = 0xffff; } pixels[i * 4] = 0xffff; } } void FindSilhouetteVersionDXTC35(int NumBytes, unsigned char *buffer) { uint16 *pixels = (uint16 *) buffer; for (int i = 0; i < NumBytes / 8; i++) { pixels[i * 8 + 4] = 0xffff; #ifdef ALEPHONE_LITTLE_ENDIAN pixels[i * 8 + 5] = 0xffdf; #else pixels[i * 8 + 5] = 0xdfff; #endif } } void FindSilhouetteVersionRGBA(int NumPixels, uint32 *Pixels) { for (int i = 0; i < NumPixels; i++) { #ifdef ALEPHONE_LITTLE_ENDIAN Pixels[i] |= 0x00ffffff; #else Pixels[i] |= 0xffffff00; #endif } } void FindSilhouetteVersion(ImageDescriptorManager &imageManager) { if (imageManager.get()->GetFormat() == ImageDescriptor::RGBA8) { FindSilhouetteVersionRGBA(imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer()); } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC1) { FindSilhouetteVersionDXTC1(imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer()); } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC3 || imageManager.get()->GetFormat() == ImageDescriptor::DXTC5) { FindSilhouetteVersionDXTC35(imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer()); } imageManager.edit()->PremultipliedAlpha = false; } static inline uint16 SetPixelOpacitiesDXTC3Row(int scale, int shift, uint16 alpha) { uint16 a1 = (alpha >> 12) & 0xf; uint16 a2 = (alpha >> 8) & 0xf; uint16 a3 = (alpha >> 4) & 0xf; uint16 a4 = alpha & 0xf; a1 = PIN((a1 * scale) / 16 + shift, 0, 15); a2 = PIN((a2 * scale) / 16 + shift, 0, 15); a3 = PIN((a3 * scale) / 16 + shift, 0, 15); a4 = PIN((a4 * scale) / 16 + shift, 0, 15); return ((a1 << 12) | (a2 << 8) | (a3 << 4) | a4); } void SetPixelOpacitiesDXTC3(OGL_TextureOptions& Options, int NumBytes, unsigned char *buffer) { assert(NumBytes % 16 == 0); uint16 *rows = (uint16 *) buffer; int scale = PIN(int(Options.OpacityScale * 16), 0, 16); int shift = PIN(int(Options.OpacityShift * 16), -16, 16); for (int i = 0; i < NumBytes / 8; i++) { uint16 *a1 = &rows[i * 8]; uint16 *a2 = &rows[i * 8 + 1]; uint16 *a3 = &rows[i * 8 + 2]; uint16 *a4 = &rows[i * 8 + 3]; *a1 = SetPixelOpacitiesDXTC3Row(scale, shift, *a1); *a2 = SetPixelOpacitiesDXTC3Row(scale, shift, *a2); *a3 = SetPixelOpacitiesDXTC3Row(scale, shift, *a3); *a4 = SetPixelOpacitiesDXTC3Row(scale, shift, *a4); } } static inline uint16 SetPixelOpacitiesDXTC5Pair(int scale, int shift, uint16 alpha) { uint16 a1 = alpha >> 8; uint16 a2 = alpha & 0xff; uint16 new_a1 = PIN((a1 * scale) / 256 + shift, 0, 255); uint16 new_a2 = PIN((a2 * scale) / 256 + shift, 0, 255); if (new_a1 == new_a2 && a1 != a2) if (a1 > a2) if (new_a2) new_a2--; else new_a1++; else if (new_a1) new_a1--; else new_a2++; else if ((new_a1 > new_a2) != (a1 > a2)) SWAP(new_a1, new_a2); return (new_a1 << 8 | new_a2); } void SetPixelOpacitiesDXTC5(OGL_TextureOptions& Options, int NumBytes, unsigned char *buffer) { assert (NumBytes % 16 == 0); uint16 *pixels = (uint16 *) buffer; int scale = PIN(int(Options.OpacityScale * 256), 0, 256); int shift = PIN(int(Options.OpacityShift * 256), -256, 256); for (int i = 0; i < NumBytes / 8; i++) { pixels[i * 8] = SDL_SwapLE16(SetPixelOpacitiesDXTC5Pair(scale, shift, SDL_SwapLE16(pixels[i * 8]))); } } void SetPixelOpacities(OGL_TextureOptions& Options, ImageDescriptorManager &imageManager) { if (Options.OpacityType != OGL_OpacType_Avg && Options.OpacityType != OGL_OpacType_Max && Options.OpacityScale == 1.0 && Options.OpacityShift == 0.0) return; if (imageManager.get()->GetFormat() == ImageDescriptor::RGBA8) { SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer()); } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC3) { // to do opac_type we have to decompress the texture if (Options.OpacityType == OGL_OpacType_Avg || Options.OpacityType == OGL_OpacType_Max) { if (imageManager.edit()->MakeRGBA()) { SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer()); } else if (Options.OpacityScale == 1.0 && Options.OpacityShift == 0.0) { return; } else { SetPixelOpacitiesDXTC3(Options, imageManager.edit()->GetBufferSize() / 4, (unsigned char *) imageManager.edit()->GetBuffer()); } } else { // if it's just scale/shift, we can do without decompressing SetPixelOpacitiesDXTC3(Options, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer()); } } else if (imageManager.get()->GetFormat() == ImageDescriptor::DXTC5) { if (Options.OpacityType == OGL_OpacType_Avg || Options.OpacityType == OGL_OpacType_Max) { if (imageManager.edit()->MakeRGBA()) { SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer()); } else if (Options.OpacityScale == 1.0 && Options.OpacityShift == 0.0) { return; } else { SetPixelOpacitiesDXTC5(Options, imageManager.edit()->GetBufferSize() / 4, (unsigned char *) imageManager.edit()->GetBuffer()); } } else { SetPixelOpacitiesDXTC5(Options, imageManager.edit()->GetBufferSize(), (unsigned char *) imageManager.edit()->GetBuffer()); } } else { // we have to decompress DXTC1 to do anything if (imageManager.edit()->MakeRGBA()) { SetPixelOpacitiesRGBA(Options, imageManager.edit()->GetBufferSize() / 4, imageManager.edit()->GetBuffer()); } } } // Does this for a set of several pixel values or color-table values; // the pixels are assumed to be in OpenGL-friendly byte-by-byte RGBA format. void SetPixelOpacitiesRGBA(OGL_TextureOptions& Options, int NumPixels, uint32 *Pixels) { for (int k=0; k 255). PxlPtr[3] = PIN(int32(Options.OpacityScale*Opacity + 255*Options.OpacityShift + 0.5),0,255); } } /* // Stuff for doing 16->32 pixel-format conversion, 1555 ARGB to 8888 RGBA GLuint *ConversionTable_16to32 = NULL; void MakeConversion_16to32(int BitDepth) { // This is for allocating a 16->32 conversion table only when necessary if (BitDepth == 16 && (!ConversionTable_16to32)) { // Allocate it int TableSize = (1 << 15); ConversionTable_16to32 = new GLuint[TableSize]; // Fill it for (word InVal = 0; InVal < TableSize; InVal++) ConversionTable_16to32[InVal] = Convert_16to32(InVal); } else if (ConversionTable_16to32) { // Get rid of it delete []ConversionTable_16to32; ConversionTable_16to32 = NULL; } } */ #endif // def HAVE_OPENGL