/******************************Module*Header*******************************\
* Module Name: texture.c
*
* Texture handling functions
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include <windows.h>
#include <scrnsave.h>
#include <commdlg.h>
#include <GL/gl.h>
#include "scrnsave.h" // for hMainInstance
#include "sscommon.h"
#include "texture.h"
static int ProcessTexture( TEXTURE *pTex );
static int ProcessTkTexture( AUX_RGBImageRec *image, TEXTURE *pTex );
static int VerifyTextureFile( TEXFILE *pTexFile );
static int GetTexFileType( TEXFILE *pTexFile );
static TEX_STRINGS gts = {0};
BOOL gbTextureObjects = FALSE;
static BOOL gbPalettedTexture = FALSE;
static PFNGLCOLORTABLEEXTPROC pfnColorTableEXT;
static PFNGLCOLORSUBTABLEEXTPROC pfnColorSubTableEXT;
static BOOL gbEnableErrorMsgs = FALSE;
/******************************Public*Routine******************************\
*
* ss_LoadTextureResourceStrings
*
* Load various messages and strings that are used in processing textures,
* into global TEX_STRINGS structure
*
\**************************************************************************/
BOOL
ss_LoadTextureResourceStrings()
{
LPTSTR pszStr;
// title for choose texture File dialog
LoadString(hMainInstance, IDS_TEXTUREDIALOGTITLE, gts.szTextureDialogTitle,
GEN_STRING_SIZE);
LoadString(hMainInstance, IDS_BMP, gts.szBmp, GEN_STRING_SIZE);
LoadString(hMainInstance, IDS_DOTBMP, gts.szDotBmp, GEN_STRING_SIZE);
LoadString(hMainInstance, IDS_TEXTUREFILTER, gts.szTextureFilter,
GEN_STRING_SIZE);
pszStr = >s.szTextureFilter[lstrlen(gts.szTextureFilter)+1];
LoadString(hMainInstance, IDS_STARDOTBMP, pszStr, GEN_STRING_SIZE);
pszStr += lstrlen(pszStr);
*pszStr++ = TEXT(';');
LoadString(hMainInstance, IDS_STARDOTRGB, pszStr, GEN_STRING_SIZE);
pszStr += lstrlen(pszStr);
pszStr++;
*pszStr = TEXT('\0');
LoadString(hMainInstance, IDS_WARNING, gts.szWarningMsg, MAX_PATH);
LoadString(hMainInstance, IDS_SELECT_ANOTHER_BITMAP,
gts.szSelectAnotherBitmapMsg, MAX_PATH );
LoadString(hMainInstance, IDS_BITMAP_INVALID,
gts.szBitmapInvalidMsg, MAX_PATH );
LoadString(hMainInstance, IDS_BITMAP_SIZE,
gts.szBitmapSizeMsg, MAX_PATH );
// assumed here that all above calls loaded properly
return TRUE;
}
/******************************Public*Routine******************************\
*
*
\**************************************************************************/
void
ss_DisableTextureErrorMsgs()
{
gbEnableErrorMsgs = FALSE;
}
/******************************Public*Routine******************************\
*
* ss_LoadBMPTextureFile
*
* Loads a BMP file and prepares it for GL usage
*
\**************************************************************************/
int
ss_LoadBMPTextureFile( LPCTSTR pszBmpfile, TEXTURE *pTex )
{
AUX_RGBImageRec *image = (AUX_RGBImageRec *) NULL;
#ifdef UNICODE
image = auxDIBImageLoad( (LPCWSTR) pszBmpfile);
#else
image = auxDIBImageLoad( (LPCSTR) pszBmpfile);
#endif
if( !image ) {
return 0;
}
return ProcessTkTexture( image, pTex );
}
/******************************Public*Routine******************************\
*
* ss_LoadTextureFile
*
* Loads a BMP file and prepares it for GL usage
*
\**************************************************************************/
int
ss_LoadTextureFile( TEXFILE *pTexFile, TEXTURE *pTex )
{
AUX_RGBImageRec *image = (AUX_RGBImageRec *) NULL;
LPTSTR pszBmpfile = pTexFile->szPathName;
int type;
// Verify file / set type
if( !(type = VerifyTextureFile( pTexFile )) )
return 0;
if( type == TEX_BMP ) {
#ifdef UNICODE
image = auxDIBImageLoad( (LPCWSTR) pszBmpfile);
#else
image = auxDIBImageLoad( (LPCSTR) pszBmpfile);
#endif
} else {
#ifdef UNICODE
image = auxRGBImageLoad( (LPCWSTR) pszBmpfile);
#else
image = auxRGBImageLoad( (LPCSTR) pszBmpfile);
#endif
}
if( !image ) {
return 0;
}
return ProcessTkTexture( image, pTex );
}
/******************************Public*Routine******************************\
*
* ss_LoadTextureResource
*
* Loads a BMP or RGB texture resource and prepares it for GL usage
*
\**************************************************************************/
int
ss_LoadTextureResource( TEX_RES *pTexRes, TEXTURE *pTex )
{
HMODULE ghmodule;
HRSRC hr;
HGLOBAL hg;
LPVOID pv;
LPCTSTR lpType;
BOOL fLoaded = FALSE;
ghmodule = GetModuleHandle(NULL);
switch(pTexRes->type)
{
case TEX_RGB:
lpType = MAKEINTRESOURCE(RT_RGB);
break;
case TEX_BMP:
lpType = MAKEINTRESOURCE(RT_MYBMP);
break;
case TEX_A8:
lpType = MAKEINTRESOURCE(RT_A8);
break;
}
hr = FindResource(ghmodule, MAKEINTRESOURCE(pTexRes->name), lpType);
if (hr == NULL)
{
goto EH_NotFound;
}
hg = LoadResource(ghmodule, hr);
if (hg == NULL)
{
goto EH_FreeResource;
}
pv = (PSZ)LockResource(hg);
if (pv == NULL)
{
goto EH_FreeResource;
}
switch(pTexRes->type)
{
case TEX_RGB:
fLoaded = ss_RGBImageLoad( pv, pTex );
break;
case TEX_BMP:
fLoaded = ss_DIBImageLoad( pv, pTex );
break;
case TEX_A8:
fLoaded = ss_A8ImageLoad( pv, pTex );
break;
}
EH_FreeResource:
FreeResource(hr);
EH_NotFound:
if( !fLoaded ) {
return 0;
}
return ProcessTexture( pTex );
}
/******************************Public*Routine******************************\
*
* ValidateTextureSize
*
* - Scales the texture to powers of 2
*
\**************************************************************************/
static BOOL
ValidateTextureSize( TEXTURE *pTex )
{
double xPow2, yPow2;
int ixPow2, iyPow2;
int xSize2, ySize2;
GLint glMaxTexDim;
if( (pTex->width <= 0) || (pTex->height <= 0) ) {
SS_WARNING( "ValidateTextureSize : invalid texture dimensions\n" );
return FALSE;
}
pTex->origAspectRatio = (float) pTex->height / (float) pTex->width;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTexDim);
if( glMaxTexDim <= 0 )
return FALSE;
if( pTex->format != GL_COLOR_INDEX ) {
// We limit the max dimension here for performance reasons
glMaxTexDim = min(256, glMaxTexDim);
if (pTex->width <= glMaxTexDim)
xPow2 = log((double)pTex->width) / log((double)2.0);
else
xPow2 = log((double)glMaxTexDim) / log((double)2.0);
if (pTex->height <= glMaxTexDim)
yPow2 = log((double)pTex->height) / log((double)2.0);
else
yPow2 = log((double)glMaxTexDim) / log((double)2.0);
ixPow2 = (int)xPow2;
iyPow2 = (int)yPow2;
// Always scale to higher nearest power
if (xPow2 != (double)ixPow2)
ixPow2++;
if (yPow2 != (double)iyPow2)
iyPow2++;
xSize2 = 1 << ixPow2;
ySize2 = 1 << iyPow2;
if (xSize2 != pTex->width ||
ySize2 != pTex->height)
{
BYTE *pData;
pData = (BYTE *) malloc(xSize2 * ySize2 * pTex->components * sizeof(BYTE));
if (!pData) {
SS_WARNING( "ValidateTextureSize : can't alloc pData\n" );
return FALSE;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if( gluScaleImage(pTex->format, pTex->width, pTex->height,
GL_UNSIGNED_BYTE, pTex->data,
xSize2, ySize2, GL_UNSIGNED_BYTE,
pData) )
{
// glu failure
SS_WARNING( "ValidateTextureSize : gluScaleImage failure\n" );
return FALSE;
}
// set the new width,height,data
pTex->width = xSize2;
pTex->height = ySize2;
free(pTex->data);
pTex->data = pData;
}
} else { // paletted texture case
// paletted texture: must be power of 2 - but might need to enforce
// here if not done in a8 load. Also have to check against
// GL_MAX_TEXTURE_SIZE. Could then clip it to power of 2 size
}
return TRUE;
}
/******************************Public*Routine******************************\
*
* SetDefaultTextureParams
*
\**************************************************************************/
static void
SetDefaultTextureParams()
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
/******************************Public*Routine******************************\
*
* ProcessTexture
*
* - Verifies texture size
* - Fills out TEXTURE structure with required data
* - Creates a texture object if extension exists
*
\**************************************************************************/
static int
ProcessTexture( TEXTURE *pTex )
{
// Enforce proper texture size (power of 2, etc.)
if( !ValidateTextureSize( pTex ) )
return 0;
// if texturing objects available, init the object
if( gbTextureObjects ) {
glGenTextures( 1, &pTex->texObj );
glBindTexture( GL_TEXTURE_2D, pTex->texObj );
// Default attributes for texObj
SetDefaultTextureParams();
glTexImage2D( GL_TEXTURE_2D, 0, pTex->components,
pTex->width, pTex->height, 0, pTex->format,
GL_UNSIGNED_BYTE, pTex->data );
if (gbPalettedTexture && pTex->pal != NULL)
{
pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTex->pal_size,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTex->pal);
}
} else
pTex->texObj = 0;
return 1;
}
/******************************Public*Routine******************************\
*
* ProcessTkTexture
*
* Simple wrapper for ProcessTexture which fills out a TEXTURE
* from a AUX_RGBImageRec
*
* Frees the ImageRec if ProcessTexture succeeds
*
\**************************************************************************/
static int
ProcessTkTexture( AUX_RGBImageRec *image, TEXTURE *pTex )
{
pTex->width = image->sizeX;
pTex->height = image->sizeY;
pTex->format = GL_RGB;
pTex->components = 3;
pTex->data = image->data;
pTex->pal_size = 0;
pTex->pal = NULL;
if( ProcessTexture( pTex ) )
{
free(image);
return 1;
}
else
{
return 0;
}
}
/******************************Public*Routine******************************\
*
* ss_SetTexture
*
\**************************************************************************/
void
ss_SetTexture( TEXTURE *pTex )
{
if( pTex == NULL )
return;
if( gbTextureObjects && pTex->texObj ) {
glBindTexture( GL_TEXTURE_2D, pTex->texObj );
return;
}
glTexImage2D( GL_TEXTURE_2D, 0, pTex->components,
pTex->width, pTex->height, 0, pTex->format,
GL_UNSIGNED_BYTE, pTex->data );
if (gbPalettedTexture && pTex->pal != NULL)
{
pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTex->pal_size,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTex->pal);
}
}
/******************************Public*Routine******************************\
*
* ss_CopyTexture
*
* Make a copy of a texture.
*
\**************************************************************************/
BOOL
ss_CopyTexture( TEXTURE *pTexDst, TEXTURE *pTexSrc )
{
int size;
if( (pTexDst == NULL) || (pTexSrc == NULL) )
return FALSE;
*pTexDst = *pTexSrc;
if( gbTextureObjects && pTexSrc->texObj ) {
glGenTextures( 1, &pTexDst->texObj );
}
// copy image data
size = pTexSrc->width * pTexSrc->height;
if( pTexSrc->components != GL_COLOR_INDEX8_EXT )
size *= pTexSrc->components; // since data format always UNSIGNED_BYTE
pTexDst->data = (unsigned char *) malloc( size );
if( pTexDst->pal == NULL )
return FALSE;
memcpy( pTexDst->data, pTexSrc->data, size );
// copy palette data
if( gbPalettedTexture && pTexSrc->pal != NULL )
{
size = pTexSrc->pal_size*sizeof(RGBQUAD);
pTexDst->pal = (RGBQUAD *) malloc(size);
if( pTexDst->pal == NULL )
{
free(pTexDst->data);
return FALSE;
}
memcpy( pTexDst->pal, pTexSrc->pal, size );
}
if( gbTextureObjects ) {
glBindTexture( GL_TEXTURE_2D, pTexDst->texObj );
// Default attributes for texObj
SetDefaultTextureParams();
glTexImage2D( GL_TEXTURE_2D, 0, pTexDst->components,
pTexDst->width, pTexDst->height, 0, pTexDst->format,
GL_UNSIGNED_BYTE, pTexDst->data );
if( gbPalettedTexture && (pTexDst->pal != NULL) )
{
pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTexDst->pal_size,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTexDst->pal);
}
}
return TRUE;
}
/******************************Public*Routine******************************\
*
* ss_SetTexturePalette
*
* Set a texture's palette according to the supplied index. This index
* indicates the start of the palette, which then wraps around if necessary.
* Of course this only works on paletted textures.
*
\**************************************************************************/
void
ss_SetTexturePalette( TEXTURE *pTex, int index )
{
if( pTex == NULL )
return;
if( gbTextureObjects )
ss_SetTexture( pTex );
if( gbPalettedTexture && pTex->pal != NULL )
{
int start, count;
start = index & (pTex->pal_size - 1);
count = pTex->pal_size - start;
pfnColorSubTableEXT(GL_TEXTURE_2D, 0, count, GL_BGRA_EXT,
GL_UNSIGNED_BYTE, pTex->pal + start);
if (start != 0)
{
pfnColorSubTableEXT(GL_TEXTURE_2D, count, start, GL_BGRA_EXT,
GL_UNSIGNED_BYTE, pTex->pal);
}
}
}
/******************************Public*Routine******************************\
*
* SetTextureAlpha
*
* Set a constant alpha value for the texture
* Again, don't overwrite any existing 0 alpha values, as explained in
* ss_SetTextureTransparency
*
\**************************************************************************/
static void
SetTextureAlpha( TEXTURE *pTex, float fAlpha )
{
int i;
unsigned char *pData = pTex->data;
RGBA8 *pColor = (RGBA8 *) pTex->data;
BYTE bAlpha = (BYTE) (fAlpha * 255.0f);
if( pTex->components != 4 )
return;
for( i = 0; i < pTex->width*pTex->height; i ++, pColor++ ) {
if( pColor->a != 0 )
pColor->a = bAlpha;
}
}
/******************************Public*Routine******************************\
*
* ConvertTextureToRGBA
*
* Convert RGB texture to RGBA
*
\**************************************************************************/
static void
ConvertTextureToRGBA( TEXTURE *pTex, float fAlpha )
{
unsigned char *pNewData;
int count = pTex->width * pTex->height;
unsigned char *src, *dst;
BYTE bAlpha = (BYTE) (fAlpha * 255.0f);
int i;
pNewData = (unsigned char *) LocalAlloc(LMEM_FIXED, count * sizeof(RGBA8));
if( !pNewData )
return;
src = pTex->data;
dst = pNewData;
// Note: the color ordering is ABGR, where R is lsb
for( i = 0; i < count; i ++ ) {
*((RGB8 *)dst) = *((RGB8 *)src);
dst += sizeof(RGB8);
src += sizeof(RGB8);
*dst++ = bAlpha;
}
LocalFree( pTex->data );
pTex->data = pNewData;
pTex->components = 4;
pTex->format = GL_RGBA;
}
/******************************Public*Routine******************************\
*
* ss_SetTextureTransparency
*
* Set transparency for a texture by adding or modifying the alpha data.
* Transparency value must be between 0.0 (opaque) and 1.0 (fully transparent)
* If the texture data previously had no alpha, add it in.
* If bSet is TRUE, make this the current texture.
*
* Note: Currently fully transparent pixels (alpha=0) will not be altered, since
* it is assumed these should be permanently transparent (could make this an
* option? - bPreserveTransparentPixels )
*
\**************************************************************************/
BOOL
ss_SetTextureTransparency( TEXTURE *pTex, float fTransp, BOOL bSet )
{
int i;
float fAlpha;
if( pTex == NULL )
return FALSE;
SS_CLAMP_TO_RANGE2( fTransp, 0.0f, 1.0f );
fAlpha = 1 - fTransp;
if( pTex->format == GL_COLOR_INDEX )
{
// just need to modify the palette
RGBQUAD *pPal = pTex->pal;
BYTE bAlpha = (BYTE) (fAlpha * 255.0f);
if( !pPal )
return FALSE;
for( i = 0; i < pTex->pal_size; i ++, pPal++ ) {
if( pPal->rgbReserved != 0 )
pPal->rgbReserved = bAlpha;
}
// need to send down the new palette for texture objects
if( gbTextureObjects && gbPalettedTexture )
{
glBindTexture( GL_TEXTURE_2D, pTex->texObj );
pfnColorTableEXT(GL_TEXTURE_2D, GL_RGBA, pTex->pal_size,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, pTex->pal);
}
}
else {
// Need to setup new texture data
if( pTex->components != 4 ) {
// Make room for alpha component
ConvertTextureToRGBA( pTex, fAlpha );
} else {
// Set alpha component
SetTextureAlpha( pTex, fAlpha );
}
// Send down new data if texture objects
if( gbTextureObjects )
{
glBindTexture( GL_TEXTURE_2D, pTex->texObj );
glTexImage2D( GL_TEXTURE_2D, 0, pTex->components,
pTex->width, pTex->height, 0, pTex->format,
GL_UNSIGNED_BYTE, pTex->data );
}
}
if( bSet )
ss_SetTexture( pTex );
return TRUE;
}
/******************************Public*Routine******************************\
*
* ss_DeleteTexture
*
\**************************************************************************/
void
ss_DeleteTexture( TEXTURE *pTex )
{
if( pTex == NULL )
return;
if( gbTextureObjects && pTex->texObj ) {
glDeleteTextures( 1, &pTex->texObj );
pTex->texObj = 0;
}
if (pTex->pal != NULL)
{
free(pTex->pal);
}
if( pTex->data )
free( pTex->data );
}
/******************************Public*Routine******************************\
*
* ss_TextureObjectsEnabled
*
* Returns BOOL set by ss_QueryGLVersion (Texture Objects only supported on
* GL v.1.1 or greater)
*
\**************************************************************************/
BOOL
ss_TextureObjectsEnabled( void )
{
return gbTextureObjects;
}
/******************************Public*Routine******************************\
*
* ss_PalettedTextureEnabled
*
* Returns result from ss_QueryPalettedTextureEXT
*
\**************************************************************************/
BOOL
ss_PalettedTextureEnabled( void )
{
return gbPalettedTexture;
}
/******************************Public*Routine******************************\
*
* ss_QueryPalettedTextureEXT
*
* Queries the OpenGL implementation to see if paletted texture is supported
* Typically called once at app startup.
*
\**************************************************************************/
BOOL
ss_QueryPalettedTextureEXT( void )
{
PFNGLGETCOLORTABLEPARAMETERIVEXTPROC pfnGetColorTableParameterivEXT;
int size;
pfnColorTableEXT = (PFNGLCOLORTABLEEXTPROC)
wglGetProcAddress("glColorTableEXT");
if (pfnColorTableEXT == NULL)
return FALSE;
pfnColorSubTableEXT = (PFNGLCOLORSUBTABLEEXTPROC)
wglGetProcAddress("glColorSubTableEXT");
if (pfnColorSubTableEXT == NULL)
return FALSE;
// Check color table size
pfnGetColorTableParameterivEXT = (PFNGLGETCOLORTABLEPARAMETERIVEXTPROC)
wglGetProcAddress("glGetColorTableParameterivEXT");
if (pfnGetColorTableParameterivEXT == NULL)
return FALSE;
// For now, the only paletted textures supported in this lib are TEX_A8,
// with 256 color table entries. Make sure the device supports this.
pfnColorTableEXT(GL_PROXY_TEXTURE_2D, GL_RGBA, 256,
GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL );
pfnGetColorTableParameterivEXT( GL_PROXY_TEXTURE_2D,
GL_COLOR_TABLE_WIDTH_EXT, &size );
if( size != 256 )
// The device does not support a color table size of 256, so we don't
// enable paletted textures in general.
return FALSE;
return gbPalettedTexture=TRUE;
}
/******************************Public*Routine******************************\
*
* ss_VerifyTextureFile
*
* Validates texture bmp or rgb file, by checking for valid pathname and
* correct format.
*
\**************************************************************************/
BOOL
ss_VerifyTextureFile( TEXFILE *ptf )
{
// Make sure the selected texture file is OK.
TCHAR szFileName[MAX_PATH];
PTSTR pszString;
TCHAR szString[MAX_PATH];
lstrcpy(szFileName, ptf->szPathName);
if ( SearchPath(NULL, szFileName, NULL, MAX_PATH,
ptf->szPathName, &pszString)
)
{
ptf->nOffset = pszString - ptf->szPathName;
return TRUE;
}
else
{
lstrcpy(ptf->szPathName, szFileName); // restore
if( !ss_fOnWin95() && gbEnableErrorMsgs )
{
wsprintf(szString, gts.szSelectAnotherBitmapMsg, ptf->szPathName);
MessageBox(NULL, szString, gts.szWarningMsg, MB_OK);
}
return FALSE;
}
}
/******************************Public*Routine******************************\
*
* ss_SelectTextureFile
*
* Use the common dialog GetOpenFileName to get the name of a bitmap file
* for use as a texture. This function will not return until the user
* either selects a valid bitmap or cancels. If a valid bitmap is selected
* by the user, the global array szPathName will have the full path
* to the bitmap file and the global value nOffset will have the
* offset from the beginning of szPathName to the pathless file name.
*
* If the user cancels, szPathName and nOffset will remain
* unchanged.
*
\**************************************************************************/
BOOL
ss_SelectTextureFile( HWND hDlg, TEXFILE *ptf )
{
OPENFILENAME ofn;
TCHAR dirName[MAX_PATH];
TEXFILE newTexFile;
LPTSTR pszFileName = newTexFile.szPathName;
TCHAR origPathName[MAX_PATH];
PTSTR pszString;
BOOL bTryAgain, bFileSelected;
gbEnableErrorMsgs = TRUE;
// Make a copy of the original file path name, so we can tell if
// it changed or not
lstrcpy( origPathName, ptf->szPathName );
// Make dialog look nice by parsing out the initial path and
// file name from the full pathname. If this isn't done, then
// dialog has a long ugly name in the file combo box and
// directory will end up with the default current directory.
if (ptf->nOffset) {
// Separate the directory and file names.
lstrcpy(dirName, ptf->szPathName);
dirName[ptf->nOffset-1] = L'\0';
lstrcpy(pszFileName, &ptf->szPathName[ptf->nOffset]);
}
else {
// If nOffset is zero, then szPathName is not a full path.
// Attempt to make it a full path by calling SearchPath.
if ( SearchPath(NULL, ptf->szPathName, NULL, MAX_PATH,
dirName, &pszString) )
{
// Successful. Go ahead a change szPathName to the full path
// and compute the filename offset.
lstrcpy(ptf->szPathName, dirName);
ptf->nOffset = pszString - dirName;
// Break the filename and directory paths apart.
dirName[ptf->nOffset-1] = TEXT('\0');
lstrcpy(pszFileName, pszString);
}
// Give up and use the Windows system directory.
else
{
GetWindowsDirectory(dirName, MAX_PATH);
lstrcpy(pszFileName, ptf->szPathName);
}
}
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hDlg;
ofn.hInstance = NULL;
ofn.lpstrFilter = gts.szTextureFilter;
ofn.lpstrCustomFilter = (LPTSTR) NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 1;
ofn.lpstrFile = pszFileName;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFileTitle = (LPTSTR) NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = dirName;
ofn.lpstrTitle = gts.szTextureDialogTitle;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = gts.szBmp;
ofn.lCustData = 0;
ofn.lpfnHook = (LPOFNHOOKPROC) NULL;
ofn.lpTemplateName = (LPTSTR) NULL;
do {
// Invoke the common file dialog. If it succeeds, then validate
// the bitmap file. If not valid, make user try again until either
// they pick a good one or cancel the dialog.
bTryAgain = FALSE;
if ( bFileSelected = GetOpenFileName(&ofn) ) {
newTexFile.nOffset = ofn.nFileOffset;
if( VerifyTextureFile( &newTexFile ) ) {
// copy in new file and offset
*ptf = newTexFile;
}
else {
bTryAgain = TRUE;
}
}
// If need to try again, recompute dir and file name so dialog
// still looks nice.
if (bTryAgain && ofn.nFileOffset) {
lstrcpy(dirName, pszFileName);
dirName[ofn.nFileOffset-1] = L'\0';
lstrcpy(pszFileName, &pszFileName[ofn.nFileOffset]);
}
} while (bTryAgain);
gbEnableErrorMsgs = FALSE;
if( bFileSelected ) {
if( lstrcmpi( origPathName, ptf->szPathName ) )
// a different file was selected
return TRUE;
}
return FALSE;
}
/******************************Public*Routine******************************\
*
* ss_GetDefaultBmpFile
*
* Determine a default bitmap file to use for texturing, if none
* exists yet in the registry.
*
* Put default in BmpFile parameter. DotBmp parameter is the bitmap
* extension (usually .bmp).
*
* We have to synthesise the name from the ProductType registry entry.
* Currently, this can be WinNT, LanmanNT, or Server. If it is
* WinNT, the bitmap is winnt.bmp. If it is LanmanNT or Server,
* the bitmap is lanmannt.bmp.
*
\**************************************************************************/
void
ss_GetDefaultBmpFile( LPTSTR pszBmpFile )
{
HKEY hkey;
LONG cjDefaultBitmap = MAX_PATH;
if( ss_fOnWin95() )
// There is no 'nice' bmp file on standard win95 installations
lstrcpy( pszBmpFile, TEXT("") );
else {
if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE,
(LPCTSTR) TEXT("System\\CurrentControlSet\\Control\\ProductOptions"),
0,
KEY_QUERY_VALUE,
&hkey) == ERROR_SUCCESS )
{
if ( RegQueryValueEx(hkey,
TEXT("ProductType"),
(LPDWORD) NULL,
(LPDWORD) NULL,
(LPBYTE) pszBmpFile,
(LPDWORD) &cjDefaultBitmap) == ERROR_SUCCESS
&& (cjDefaultBitmap / sizeof(TCHAR) + 4) <= MAX_PATH )
lstrcat( pszBmpFile, gts.szDotBmp );
else
lstrcpy( pszBmpFile, TEXT("winnt.bmp") );
RegCloseKey(hkey);
}
else
lstrcpy( pszBmpFile, TEXT("winnt.bmp") );
// If its not winnt.bmp, then its lanmannt.bmp. (This would be a lot
// cleaner both in the screen savers and for usersrv desktop bitmap
// initialization if the desktop bitmap name were stored in the
// registry).
if ( lstrcmpi( pszBmpFile, TEXT("winnt.bmp") ) != 0 )
lstrcpy( pszBmpFile, TEXT("lanmannt.bmp") );
}
}
/******************************Public*Routine******************************\
*
* VerifyTextureFile
*
* Verify that a bitmap or rgb file is valid
*
* Returns:
* File type (RGB or BMP) if valid file; otherwise, 0.
*
\**************************************************************************/
static int
VerifyTextureFile( TEXFILE *pTexFile )
{
int type;
ISIZE size;
BOOL bValid;
TCHAR szString[2 * MAX_PATH]; // May contain a pathname
// check for 0 offset and null strings
if( (pTexFile->nOffset == 0) || (*pTexFile->szPathName == 0) )
return 0;
type = GetTexFileType( pTexFile );
switch( type ) {
case TEX_BMP:
bValid = bVerifyDIB( pTexFile->szPathName, &size );
break;
case TEX_RGB:
bValid = bVerifyRGB( pTexFile->szPathName, &size );
break;
case TEX_UNKNOWN:
default:
bValid = FALSE;
}
if( !bValid ) {
if( gbEnableErrorMsgs ) {
wsprintf(szString, gts.szSelectAnotherBitmapMsg, pTexFile->szPathName);
MessageBox(NULL, szString, gts.szWarningMsg, MB_OK);
}
return 0;
}
// Check size ?
if ( (size.width > TEX_WIDTH_MAX) ||
(size.height > TEX_HEIGHT_MAX) )
{
if( gbEnableErrorMsgs )
{
wsprintf(szString, gts.szBitmapSizeMsg,
TEX_WIDTH_MAX, TEX_HEIGHT_MAX);
MessageBox(NULL, szString, gts.szWarningMsg, MB_OK);
}
return 0;
}
return type;
}
/******************************Public*Routine******************************\
*
* ss_InitAutoTexture
*
* Generate texture coordinates automatically.
* If pTexRep is not NULL, use it to set the repetition of the generated
* texture.
*
\**************************************************************************/
void
ss_InitAutoTexture( TEX_POINT2D *pTexRep )
{
GLfloat sgenparams[] = {1.0f, 0.0f, 0.0f, 0.0f};
GLfloat tgenparams[] = {0.0f, 1.0f, 0.0f, 0.0f};
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
if( pTexRep )
sgenparams[0] = pTexRep->s;
glTexGenfv(GL_S, GL_OBJECT_PLANE, sgenparams );
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
if( pTexRep )
tgenparams[0] = pTexRep->t;
glTexGenfv(GL_T, GL_OBJECT_PLANE, tgenparams );
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable( GL_TEXTURE_2D );
}
/******************************Public*Routine******************************\
*
* GetTexFileType
*
* Determine if a texture file is rgb or bmp, based on extension. This is
* good enough, as the open texture dialog only shows files with these
* extensions.
*
\**************************************************************************/
static int
GetTexFileType( TEXFILE *pTexFile )
{
LPTSTR pszStr;
#ifdef UNICODE
pszStr = wcsrchr( pTexFile->szPathName + pTexFile->nOffset,
(USHORT) L'.' );
#else
pszStr = strrchr( pTexFile->szPathName + pTexFile->nOffset,
(USHORT) L'.' );
#endif
if( !pszStr || (lstrlen(++pszStr) == 0) )
return TEX_UNKNOWN;
if( !lstrcmpi( pszStr, TEXT("bmp") ) )
return TEX_BMP;
else if( !lstrcmpi( pszStr, TEXT("rgb") ) )
return TEX_RGB;
else
return TEX_UNKNOWN;
}