Redline/source/textures.cpp

365 lines
11 KiB
C++
Raw Normal View History

//textures.cpp
//load textures into OpenGL
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#include <OpenGL/glu.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "fileio.h"
#include "gamemem.h"
#include "textures.h"
#include "texturesimport.h"
#include "config.h"
#include "gametime.h"
#include "error.h"
#include "screen.h"
#include "interfaceutil.h"
#include "writeout.h"
#include "S3Decompression.h"
#define gCanMultiTexture true
typedef struct{
void *next;
tFileRef tex;
} tTextureList;
typedef struct{
GLuint ref;
GLenum target;
} tGLTextureInfo;
tTextureList *gTextureList=NULL;
int gEnableTextureLoad=true;
int gNumTexturesRequested=0;
int gNumTexturesLoaded=0;
int gTexturesQualityModifier=0;
#define kTexturePriorityListFileName "texturepriorities.cfg"
//pass raw pixel data to OpenGL
void TexturesPassPixelsToGL(tFileRef tex,void *data,GLuint channels,GLuint pixType,GLuint pixSize,int xSize,int ySize,GLenum target,int quality)
{
int xQual=(quality+1)/3;
int yQual=quality/3;
if(xQual<0)xQual=0;
if(yQual<0)yQual=0;
if((xSize>=1024||ySize>=1024)&&(xQual==0||yQual==0))
if(ScreenNoBigTextures())
xQual=yQual=1;
//are we using reduced texture quality?
if(yQual)
{
//Calculate size of downsampled texture
int newXSize=xSize>>xQual>0?xSize>>xQual:1;
int newYSize=ySize>>yQual>0?ySize>>yQual:1;
//allocate spce for downsampled texture
void *buffer=MemoryAllocateBlock(newXSize*newYSize*4);
//create downsample texture
gluScaleImage(pixType,xSize,ySize,pixSize,data,newXSize,newYSize,pixSize,buffer);
gluBuild2DMipmaps(target,channels,newXSize,newYSize,pixType,pixSize,buffer);
MemoryFreeBlock(buffer);
}
else
gluBuild2DMipmaps(target,channels,xSize,ySize,pixType,pixSize,data);
}
void TexturesLoadImport(tFileRef tex,GLuint target,int quality)
{
int xSize,ySize;
//Get Buffer
void *imageBuffer=TexturesLoadImportBuffer(tex,&xSize,&ySize);
//TexturesPassPixelsToGL(tex,imageBuffer,GL_COMPRESSED_RGBA_ARB,GL_RGBA,GL_UNSIGNED_BYTE,xSize,ySize,target,quality);
TexturesPassPixelsToGL(tex,imageBuffer,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,xSize,ySize,target,quality);
MemoryFreeBlock(imageBuffer);
}
void TexturesPassPixelsToGL3d(tFileRef tex,void *data,GLuint channels,GLuint pixType,GLuint pixSize,int xSize,int ySize,int zSize,GLenum target,int quality)
{
quality/=3;
if(quality<0)
quality=0;
//are we using reduced texture quality?
if(quality)
{
//Calculate size of downsampled texture
int newXSize=xSize>>quality>0?xSize>>quality:1;
int newYSize=ySize>>quality>0?ySize>>quality:1;
//allocate spce for downsampled texture
void *buffer=MemoryAllocateBlock(newXSize*newYSize*zSize*4);
//create downsample texture
for(int z=0;z<zSize;z++)
gluScaleImage(pixType,xSize,ySize,pixSize,(char*)data+xSize*ySize*z*4,newXSize,newYSize,pixSize,(char*)buffer+newXSize*newYSize*z*4);
gluBuild3DMipmaps(target,channels,newXSize,newYSize,zSize,pixType,pixSize,buffer);
MemoryFreeBlock(buffer);
}
else
gluBuild3DMipmaps(target,channels,xSize,ySize,zSize,pixType,pixSize,data);
}
static inline void ByteSwap(UInt16 *i)
{
*i = ((*i << 8) | (*i >> 8));
}
void LoadCompressedTexture(tFileRef tex,int quality)
{
quality/=3;
if(quality<0)quality=0;
tTextureCacheFile *cache=(tTextureCacheFile*)FileGetDataPtr(tex);
tCachedTexture *t0=(tCachedTexture*)(((char*)cache)+EndianS32_BtoN(cache->offsets[0]));
if((t0->width>=1024||t0->height>=1024)&&quality==0)
if(ScreenNoBigTextures())
quality=1;
for(int i=quality;i<EndianS32_BtoN(cache->numEntries);i++)
{
tCachedTexture *t=(tCachedTexture*)(((char*)cache)+EndianS32_BtoN(cache->offsets[i]));
if(EndianS32_BtoN(t->width)&&EndianS32_BtoN(t->height))
if(EndianS32_BtoN(t->compressionSize))
if(ScreenSupportsTextureCompression())
glCompressedTexImage2DARB(
EndianS32_BtoN(t->target),
EndianS32_BtoN(t->level)-quality,
EndianS32_BtoN(t->internalformat),
EndianS32_BtoN(t->width),
EndianS32_BtoN(t->height),
EndianS32_BtoN(t->border),
EndianS32_BtoN(t->compressionSize),
t->data
);
else
{
UInt16* temp = (UInt16*)t->data;
#if TARGET_RT_BIG_ENDIAN
for (int i=0;i<t->compressionSize/2;i++)
ByteSwap(temp++);
#endif
void* decompData=MemoryAllocateBlock(sizeof(GLuint)*EndianS32_BtoN(t->width)*EndianS32_BtoN(t->height));
void* decompData2=MemoryAllocateBlock(sizeof(GLuint)*EndianS32_BtoN(t->width)*EndianS32_BtoN(t->height));
DecompressDXT3(EndianS32_BtoN(t->width),EndianS32_BtoN(t->height),(UInt32*)t->data,(UInt32*)decompData);
#if TARGET_RT_BIG_ENDIAN
for(int y=0;y<t->height;y++)
for(int x=0;x<t->width;x++)
{
UInt32 d=((UInt32*)decompData)[x+(y&1?y-1:y+1)*t->width];
UInt8 b=d>>24;
UInt8 g=d>>16;
UInt8 r=d>>8;
UInt8 a=((UInt32*)decompData)[x+y*t->width];
((UInt32*)decompData2)[x+y*t->width]=(r<<24)|(g<<16)|(b<<8)|a;
}
glTexImage2D(EndianS32_BtoN(t->target)
,EndianS32_BtoN(t->level)-quality
,GL_RGBA
,EndianS32_BtoN(t->width)
,EndianS32_BtoN(t->height)
,EndianS32_BtoN(t->border)
,GL_RGBA,GL_UNSIGNED_BYTE,decompData2);
#else
glTexImage2D(EndianS32_BtoN(t->target)
,EndianS32_BtoN(t->level)-quality
,GL_RGBA
,EndianS32_BtoN(t->width)
,EndianS32_BtoN(t->height)
,EndianS32_BtoN(t->border)
,GL_RGBA,GL_UNSIGNED_BYTE,decompData);
#endif
MemoryFreeBlock(decompData);
MemoryFreeBlock(decompData2);
}
else
glTexImage2D(
EndianS32_BtoN(t->target),
EndianS32_BtoN(t->level)-quality,
EndianS32_BtoN(t->internalformat),
EndianS32_BtoN(t->width),
EndianS32_BtoN(t->height),
EndianS32_BtoN(t->border),
EndianS32_BtoN(t->format),
EndianS32_BtoN(t->type),
t->data
);
}
}
#include "error.h"
//load a texture from the file referenced by tex
void TexturesLoadFromFile(tFileRef tex,GLenum target)
{
int quality=gConfig->textureQuality+gTexturesQualityModifier;
//if the file type is a raw pixel format, just pass the data to OpenGL
char *fileExtension=FileGetExtension(tex);
if(!_stricmp(fileExtension,kFileTypeCompressedTexture)){ //precompressed texture
LoadCompressedTexture(tex,quality);
}else if(!_stricmp(fileExtension,kFileTypeRAW)){ //RAW RGB image
if(void *textureData=FileGetDataPtr(tex)){
int size=sqrt(FileGetSize(tex)/3);
TexturesPassPixelsToGL(tex,textureData,GL_RGB,GL_RGB,GL_UNSIGNED_BYTE,size,size,target,quality);
}
}else if(!_stricmp(fileExtension,kFileTypeRAW3d)){ //RAW 3D RGBA image
if(void *textureData=FileGetDataPtr(tex)){
int size=sqrt(FileGetSize(tex)/8);
if(ScreenSupports3DTextures())
TexturesPassPixelsToGL3d(tex,textureData,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,size,size,2,target,quality);
else
TexturesPassPixelsToGL(tex,textureData,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,size,size,target,quality);
}
}else if(!_stricmp(fileExtension,kFileTypeGrayscaleRAW)){ //RAW grayscale image
if(void *textureData=FileGetDataPtr(tex)){
int size=sqrt(FileGetSize(tex));
TexturesPassPixelsToGL(tex,textureData,1,GL_LUMINANCE,GL_UNSIGNED_BYTE,size,size,target,quality);
}
}else if(!_stricmp(fileExtension,".RGBA")){ //RAW RGBA image
if(void *textureData=FileGetDataPtr(tex)){
int size=sqrt(FileGetSize(tex)/4);
TexturesPassPixelsToGL(tex,textureData,GL_RGBA,GL_RGBA,GL_UNSIGNED_BYTE,size,size,target,quality);
}
}
//otherwise decompress image data
else TexturesLoadImport(tex,target,quality); //other image formats
}
int TexturesSelectTextureUnit(int unit)
{
GLint numTextureUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&numTextureUnits);
if(unit<numTextureUnits)
{
glActiveTextureARB(GL_TEXTURE0_ARB+unit);
return true;
}
else
return false;
}
//fades out the alpha values of mip map LOD textures
//(used to avoid subpixel artifacts for the blended skid marks)
void TexturesAlphaFadeMipMaps()
{
int n=1,err;
GLint width,height;
unsigned char *buffer;
glGetError(); //Clear Error info
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
if(!glGetError())
{
buffer=(unsigned char*)MemoryAllocateBlock(width*height*4);
do
{
glGetTexImage(GL_TEXTURE_2D,n,GL_RGBA,GL_UNSIGNED_BYTE,buffer);
if(!(err=glGetError())){
glGetTexLevelParameteriv(GL_TEXTURE_2D,n,GL_TEXTURE_WIDTH,&width);
glGetTexLevelParameteriv(GL_TEXTURE_2D,n,GL_TEXTURE_HEIGHT,&height);
for(int pix=0;pix<width*height*4;pix++)
buffer[pix+3]/=n;
glTexImage2D(GL_TEXTURE_2D,n,4,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,buffer);
n++;
}
}while(!err);
}
MemoryFreeBlock(buffer);
}
//create a new texture reference, and load the texture file referenced to by tex
void TexturesLoadTex(tFileRef tex)
{
if(!gEnableTextureLoad)
{
if(!gFileTable[tex].tagged)
{
gFileTable[tex].tagged=-1+gTexturesQualityModifier;
gNumTexturesRequested++;
}
return;
}
tGLTextureInfo *info=(tGLTextureInfo*)MemoryAllocateBlock(sizeof(tGLTextureInfo));
info->target=GL_TEXTURE_2D;
char *fileExtension=FileGetExtension(tex);
if(fileExtension)
if(!_stricmp(fileExtension,kFileTypeRAW3d))
if(ScreenSupports3DTextures())
info->target=GL_TEXTURE_3D;
glGenTextures(1,&info->ref);
glBindTexture(info->target,info->ref);
//glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE,GL_TRUE);
//glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_SHARED_APPLE);
TexturesLoadFromFile(tex,info->target);
glTexParameteri(info->target,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(info->target,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(info->target,GL_TEXTURE_MIN_FILTER,gConfig->textureFilter?GL_LINEAR_MIPMAP_LINEAR:GL_LINEAR_MIPMAP_NEAREST);
if(gConfig->textureFilter==2)
glTexParameterf(info->target,GL_TEXTURE_MAX_ANISOTROPY_EXT,10);
tTextureList *textureList=gTextureList;
gTextureList=(tTextureList*)MemoryAllocateBlock(sizeof(tTextureList));
gTextureList->next=(void*)textureList;
gTextureList->tex=tex;
gNumTexturesLoaded++;
gFileTable[tex].parsedData=(void*)info;
gFileTable[tex].parsed=true;
}
void TexturesUnloadAll()
{
while(gTextureList!=NULL)
{
tTextureList *next=(tTextureList*)gTextureList->next;
glDeleteTextures(1,&((tGLTextureInfo*)(gFileTable[gTextureList->tex].parsedData))->ref);
MemoryFreeBlock(gFileTable[gTextureList->tex].parsedData);
gFileTable[gTextureList->tex].parsed=false;
FileReleaseData(gTextureList->tex);
MemoryFreeBlock(gTextureList);
gTextureList=next;
}
}
//Selects the texture in the file referenced to by tex
void TexturesSelectTex(tFileRef tex)
{
if(tex==kFileErr)
tex=FileGetReference("null.raw");
//is the texture loaded?
if(!gFileTable[tex].parsed)
TexturesLoadTex(tex);
if(gFileTable[tex].parsed){
if(((tGLTextureInfo*)gFileTable[tex].parsedData)->target==GL_TEXTURE_3D)
{
glDisable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_3D);
}
else{
glEnable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_3D);
}
// GLboolean b;
// if(!glAreTexturesResident(1,(GLuint*)gFileTable[tex].parsedData,&b))
// SysBeep(0);
glBindTexture(((tGLTextureInfo*)gFileTable[tex].parsedData)->target,((tGLTextureInfo*)gFileTable[tex].parsedData)->ref);
}
}