365 lines
11 KiB
C++
365 lines
11 KiB
C++
|
//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);
|
||
|
}
|
||
|
}
|