mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
623 lines
16 KiB
C
623 lines
16 KiB
C
/*
|
|
r_context.c -- dx2 renderer texture uploading and processing
|
|
Copyright (C) 2010 Uncle Mike
|
|
Copyright (C) 2025 lewa_j
|
|
|
|
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 3 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.
|
|
*/
|
|
|
|
#include "r_local.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "crtlib.h"
|
|
#include "crclib.h"
|
|
|
|
static dx_texture_t dx_textures[MAX_TEXTURES];
|
|
static dx_texture_t *dx_texturesHashTable[TEXTURES_HASH_SIZE];
|
|
static uint dx_numTextures;
|
|
|
|
void R_InitImages( void )
|
|
{
|
|
memset(dx_textures, 0, sizeof(dx_textures));
|
|
memset(dx_texturesHashTable, 0, sizeof(dx_texturesHashTable));
|
|
dx_numTextures = 0;
|
|
|
|
// create unused 0-entry
|
|
Q_strncpy(dx_textures->name, "*unused*", sizeof(dx_textures->name));
|
|
dx_textures->hashValue = COM_HashKey(dx_textures->name, TEXTURES_HASH_SIZE);
|
|
dx_textures->nextHash = dx_texturesHashTable[dx_textures->hashValue];
|
|
dx_texturesHashTable[dx_textures->hashValue] = dx_textures;
|
|
dx_numTextures = 1;
|
|
}
|
|
|
|
dx_texture_t *R_GetTexture( unsigned int texnum )
|
|
{
|
|
Assert(texnum < MAX_TEXTURES);
|
|
return &dx_textures[texnum];
|
|
}
|
|
|
|
void R_ShowTextures( void )
|
|
{
|
|
|
|
}
|
|
|
|
const byte *R_GetTextureOriginalBuffer( unsigned int idx )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static qboolean GL_CheckTexName( const char *name )
|
|
{
|
|
int len;
|
|
|
|
if( COM_StringEmptyOrNULL( name ))
|
|
return false;
|
|
|
|
len = Q_strlen( name );
|
|
|
|
// because multi-layered textures can exceed name string
|
|
if( len >= sizeof( dx_textures->name ))
|
|
{
|
|
gEngfuncs.Con_Printf( S_ERROR "LoadTexture: too long name %s (%d)\n", name, len );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static dx_texture_t *GL_TextureForName( const char *name )
|
|
{
|
|
dx_texture_t *tex;
|
|
uint hash;
|
|
|
|
// find the texture in array
|
|
hash = COM_HashKey(name, TEXTURES_HASH_SIZE);
|
|
|
|
for (tex = dx_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash)
|
|
{
|
|
if (!Q_stricmp(tex->name, name))
|
|
return tex;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static dx_texture_t *GL_AllocTexture( const char *name, texFlags_t flags )
|
|
{
|
|
dx_texture_t *tex = NULL;
|
|
|
|
// find a free texture_t slot
|
|
uint i;
|
|
|
|
//skip 0
|
|
for (i = 1; i < MAX_TEXTURES; i++)
|
|
{
|
|
if (dx_textures[i].used)
|
|
continue;
|
|
|
|
tex = &dx_textures[i];
|
|
break;
|
|
}
|
|
|
|
if (tex == NULL)
|
|
{
|
|
gEngfuncs.Host_Error("%s: MAX_TEXTURES limit exceeds\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
// copy initial params
|
|
Q_strncpy(tex->name, name, sizeof(tex->name));
|
|
tex->used = 1;
|
|
tex->flags = flags;
|
|
|
|
// increase counter
|
|
dx_numTextures = Q_max((tex - dx_textures) + 1, dx_numTextures);
|
|
|
|
// add to hash table
|
|
tex->hashValue = COM_HashKey(name, TEXTURES_HASH_SIZE);
|
|
tex->nextHash = dx_texturesHashTable[tex->hashValue];
|
|
dx_texturesHashTable[tex->hashValue] = tex;
|
|
|
|
return tex;
|
|
}
|
|
|
|
static void GL_ProcessImage( dx_texture_t *tex, rgbdata_t *pic )
|
|
{
|
|
uint img_flags = 0;
|
|
|
|
// force upload texture as RGB or RGBA (detail textures requires this)
|
|
if (tex->flags & TF_FORCE_COLOR) pic->flags |= IMAGE_HAS_COLOR;
|
|
if (pic->flags & IMAGE_HAS_ALPHA) tex->flags |= TF_HAS_ALPHA;
|
|
|
|
if (ImageCompressed(pic->type))
|
|
{
|
|
if (!pic->numMips)
|
|
tex->flags |= TF_NOMIPMAP; // disable mipmapping by user request
|
|
|
|
// clear all the unsupported flags
|
|
tex->flags &= ~TF_KEEP_SOURCE;
|
|
}
|
|
else
|
|
{
|
|
// copy flag about luma pixels
|
|
if (pic->flags & IMAGE_HAS_LUMA)
|
|
tex->flags |= TF_HAS_LUMA;
|
|
|
|
if (pic->flags & IMAGE_QUAKEPAL)
|
|
tex->flags |= TF_QUAKEPAL;
|
|
|
|
// create luma texture from quake texture
|
|
if (tex->flags & TF_MAKELUMA)
|
|
{
|
|
img_flags |= IMAGE_MAKE_LUMA;
|
|
tex->flags &= ~TF_MAKELUMA;
|
|
}
|
|
|
|
if (!FBitSet(tex->flags, TF_IMG_UPLOADED) && FBitSet(tex->flags, TF_KEEP_SOURCE))
|
|
tex->original = gEngfuncs.FS_CopyImage(pic); // because current pic will be expanded to rgba
|
|
|
|
// we need to expand image into RGBA buffer
|
|
if (pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32)
|
|
img_flags |= IMAGE_FORCE_RGBA;
|
|
|
|
// processing image before uploading (force to rgba, make luma etc)
|
|
if (pic->buffer) gEngfuncs.Image_Process(&pic, 0, 0, img_flags, 0);
|
|
|
|
if (FBitSet(tex->flags, TF_LUMINANCE))
|
|
ClearBits(pic->flags, IMAGE_HAS_COLOR);
|
|
}
|
|
}
|
|
|
|
static int GL_CalcMipmapCount( dx_texture_t *tex, qboolean haveBuffer )
|
|
{
|
|
int width, height;
|
|
int mipcount;
|
|
|
|
Assert(tex != NULL);
|
|
|
|
//TEMP
|
|
return 1;
|
|
|
|
if (!haveBuffer)
|
|
return 1;
|
|
|
|
// generate mip-levels by user request
|
|
if (FBitSet(tex->flags, TF_NOMIPMAP))
|
|
return 1;
|
|
|
|
// mip-maps can't exceeds 16
|
|
for (mipcount = 0; mipcount < 16; mipcount++)
|
|
{
|
|
width = Q_max(1, (tex->width >> mipcount));
|
|
height = Q_max(1, (tex->height >> mipcount));
|
|
if (width == 1 && height == 1)
|
|
break;
|
|
}
|
|
|
|
return mipcount + 1;
|
|
}
|
|
|
|
static void GL_TextureImageRAW( dx_texture_t *tex, int width, int height, int type, const char *data )
|
|
{
|
|
if (!data)
|
|
return;
|
|
|
|
DDSURFACEDESC ddsd = {};
|
|
ZeroMemory(&ddsd, sizeof(ddsd));
|
|
ddsd.dwSize = sizeof(ddsd);
|
|
ddsd.dwFlags = DDSD_LPSURFACE;
|
|
DXCheck(IDirectDrawSurface_Lock(tex->dds, NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR, NULL));
|
|
|
|
int pitch = ddsd.lPitch;
|
|
if (!pitch)
|
|
pitch = ddsd.dwWidth * 4;
|
|
char *dst = (char *)ddsd.lpSurface;
|
|
|
|
for (int j = 0; j < (int)ddsd.dwHeight; j++)
|
|
{
|
|
//RGBA to BGRA
|
|
if (type == PF_RGBA_32)
|
|
{
|
|
for (int i = 0; i < ddsd.dwWidth; i++)
|
|
{
|
|
dst[i * 4 + 0] = data[i * 4 + 2];
|
|
dst[i * 4 + 1] = data[i * 4 + 1];
|
|
dst[i * 4 + 2] = data[i * 4 + 0];
|
|
dst[i * 4 + 3] = data[i * 4 + 3];
|
|
}
|
|
}
|
|
else if (type == PF_BGRA_32)
|
|
{
|
|
memcpy(dst, data, ddsd.dwWidth * 4);
|
|
}
|
|
|
|
dst += pitch;
|
|
data += ddsd.dwWidth * 4;
|
|
}
|
|
|
|
DXCheck(IDirectDrawSurface_Unlock(tex->dds, ddsd.lpSurface));
|
|
}
|
|
|
|
static qboolean GL_UploadTexture( dx_texture_t *tex, rgbdata_t *pic )
|
|
{
|
|
byte *buf, *data;
|
|
size_t size;
|
|
uint width, height;
|
|
uint j;
|
|
qboolean normalMap;
|
|
const byte *bufend;
|
|
|
|
// dedicated server
|
|
if (!dxc.window)
|
|
return true;
|
|
|
|
Assert(pic != NULL);
|
|
Assert(tex != NULL);
|
|
|
|
qboolean supported = true;
|
|
|
|
if (FBitSet(pic->flags, IMAGE_CUBEMAP))
|
|
supported = false;
|
|
else if (FBitSet(pic->flags, IMAGE_MULTILAYER) && pic->depth >= 1)
|
|
supported = false;
|
|
else if (pic->width > 1 && pic->height > 1 && pic->depth > 1)
|
|
supported = false;
|
|
else if (FBitSet(tex->flags, TF_RECTANGLE))
|
|
supported = false;
|
|
else if (FBitSet(tex->flags, TF_MULTISAMPLE))
|
|
supported = false;
|
|
|
|
// make sure what target is correct
|
|
if (!supported)
|
|
{
|
|
gEngfuncs.Con_DPrintf(S_ERROR "%s: %s is not supported\n", __func__, tex->name);
|
|
return false;
|
|
}
|
|
|
|
//if (pic->type == PF_BC6H_SIGNED || pic->type == PF_BC6H_UNSIGNED || pic->type == PF_BC7_UNORM || pic->type == PF_BC7_SRGB)
|
|
if (ImageCompressed(pic->type))
|
|
{
|
|
gEngfuncs.Con_DPrintf(S_ERROR "%s: compressed textures are not supported\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
// store original sizes
|
|
tex->srcWidth = pic->width;
|
|
tex->srcHeight = pic->height;
|
|
|
|
// set the texture dimensions
|
|
{
|
|
int step = 0;// (int)gl_round_down.value;
|
|
int scaled_width, scaled_height;
|
|
|
|
for (scaled_width = 1; scaled_width < pic->width; scaled_width <<= 1);
|
|
|
|
if (step > 0 && pic->width < scaled_width && (step == 1 || (scaled_width - pic->width) > (scaled_width >> step)))
|
|
scaled_width >>= 1;
|
|
|
|
for (scaled_height = 1; scaled_height < pic->height; scaled_height <<= 1);
|
|
|
|
if (step > 0 && pic->height < scaled_height && (step == 1 || (scaled_height - pic->height) > (scaled_height >> step)))
|
|
scaled_height >>= 1;
|
|
|
|
tex->width = Q_max(1, scaled_width);
|
|
tex->height = Q_max(1, scaled_height);
|
|
}
|
|
|
|
DDSURFACEDESC ddsd = {};
|
|
ZeroMemory(&ddsd, sizeof(ddsd));
|
|
ddsd.dwSize = sizeof(ddsd);
|
|
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
|
|
ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
|
|
//if (pic->numMips > 1)
|
|
// ddsd.ddsCaps.dwCaps |= DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
|
|
ddsd.dwWidth = tex->width;
|
|
ddsd.dwHeight = tex->height;
|
|
|
|
#if 1
|
|
//BGRA8
|
|
ddsd.dwFlags |= DDSD_PIXELFORMAT;
|
|
ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
|
|
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
|
|
if(FBitSet(pic->flags, IMAGE_HAS_ALPHA))
|
|
ddsd.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS;
|
|
ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
|
|
ddsd.ddpfPixelFormat.dwBBitMask = 0xFF;
|
|
ddsd.ddpfPixelFormat.dwGBitMask = 0xFF00;
|
|
ddsd.ddpfPixelFormat.dwRBitMask = 0xFF0000;
|
|
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000;
|
|
#endif
|
|
|
|
DXCheck(IDirectDraw_CreateSurface(dxc.pdd, &ddsd, &tex->dds, NULL));
|
|
DXCheck(IDirectDrawSurface_QueryInterface(tex->dds, &IID_IDirect3DTexture, (void**)&tex->d3dTex));
|
|
DXCheck(IDirect3DTexture_GetHandle(tex->d3dTex, dxc.pd3dd, &tex->d3dHandle));
|
|
|
|
tex->fogParams[0] = pic->fogParams[0];
|
|
tex->fogParams[1] = pic->fogParams[1];
|
|
tex->fogParams[2] = pic->fogParams[2];
|
|
tex->fogParams[3] = pic->fogParams[3];
|
|
|
|
buf = pic->buffer;
|
|
bufend = pic->buffer + pic->size; // total image size include all the layers, cube sides, mipmaps
|
|
normalMap = FBitSet(tex->flags, TF_NORMALMAP) ? true : false;
|
|
|
|
if (Q_max(1, pic->numMips) > 1) // not-compressed DDS
|
|
{
|
|
//TEMP
|
|
j = 0;
|
|
//for (j = 0; j < Q_max(1, pic->numMips); j++)
|
|
{
|
|
// track the buffer bounds
|
|
if (buf != NULL && buf >= bufend)
|
|
gEngfuncs.Host_Error("%s: %s image buffer overflow\n", __func__, tex->name);
|
|
|
|
width = Q_max(1, (tex->width >> j));
|
|
height = Q_max(1, (tex->height >> j));
|
|
size = gEngfuncs.Image_CalcImageSize(pic->type, width, height, 1);
|
|
GL_TextureImageRAW(tex, width, height, pic->type, buf);
|
|
buf += size; // move pointer
|
|
tex->numMips++;
|
|
}
|
|
}
|
|
else // RGBA32
|
|
{
|
|
// track the buffer bounds
|
|
if (buf != NULL && buf >= bufend)
|
|
gEngfuncs.Host_Error("%s: %s image buffer overflow\n", __func__, tex->name);
|
|
|
|
int mipCount = GL_CalcMipmapCount(tex, (buf != NULL));
|
|
|
|
// NOTE: only single uncompressed textures can be resamples, no mips, no layers, no sides
|
|
if (((pic->width != tex->width) || (pic->height != tex->height)))
|
|
data = GL_ResampleTexture(buf, pic->width, pic->height, tex->width, tex->height, normalMap);
|
|
else data = buf;
|
|
|
|
//if (!ImageCompressed(pic->type) && !FBitSet(tex->flags, TF_NOMIPMAP) && FBitSet(pic->flags, IMAGE_ONEBIT_ALPHA))
|
|
// data = GL_ApplyFilter(data, tex->width, tex->height);
|
|
|
|
// mips will be auto-generated if desired
|
|
for (j = 0; j < mipCount; j++)
|
|
{
|
|
width = Q_max(1, (tex->width >> j));
|
|
height = Q_max(1, (tex->height >> j));
|
|
size = gEngfuncs.Image_CalcImageSize(pic->type, width, height, 1);
|
|
GL_TextureImageRAW(tex, width, height, pic->type, data);
|
|
//if (mipCount > 1)
|
|
// GL_BuildMipMap(data, width, height, tex->depth, tex->flags);
|
|
tex->numMips++;
|
|
}
|
|
}
|
|
|
|
SetBits(tex->flags, TF_IMG_UPLOADED); // done
|
|
|
|
return true;
|
|
}
|
|
|
|
int GL_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update )
|
|
{
|
|
dx_texture_t *tex;
|
|
|
|
if (!GL_CheckTexName(name))
|
|
return 0;
|
|
|
|
// see if already loaded
|
|
if ((tex = GL_TextureForName(name)) && !update)
|
|
return (tex - dx_textures);
|
|
|
|
// couldn't loading image
|
|
if (!pic) return 0;
|
|
|
|
if (update)
|
|
{
|
|
if (tex == NULL)
|
|
gEngfuncs.Host_Error("%s: couldn't find texture %s for update\n", __func__, name);
|
|
SetBits(tex->flags, flags);
|
|
}
|
|
else
|
|
{
|
|
// allocate the new one
|
|
tex = GL_AllocTexture(name, flags);
|
|
}
|
|
|
|
GL_ProcessImage(tex, pic);
|
|
|
|
if (!GL_UploadTexture(tex, pic))
|
|
{
|
|
memset(tex, 0, sizeof(dx_texture_t));
|
|
return 0;
|
|
}
|
|
|
|
return (tex - dx_textures);
|
|
}
|
|
|
|
int GL_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags )
|
|
{
|
|
qboolean update = FBitSet( flags, TF_UPDATE ) ? true : false;
|
|
int datasize = 1;
|
|
rgbdata_t r_empty;
|
|
|
|
if( FBitSet( flags, TF_ARB_16BIT ))
|
|
datasize = 2;
|
|
else if( FBitSet( flags, TF_ARB_FLOAT ))
|
|
datasize = 4;
|
|
|
|
ClearBits( flags, TF_UPDATE );
|
|
memset( &r_empty, 0, sizeof( r_empty ));
|
|
r_empty.width = width;
|
|
r_empty.height = height;
|
|
r_empty.type = PF_RGBA_32;
|
|
r_empty.size = r_empty.width * r_empty.height * datasize * 4;
|
|
r_empty.buffer = (byte *)buffer;
|
|
|
|
// clear invalid combinations
|
|
ClearBits( flags, TF_TEXTURE_3D );
|
|
|
|
// if image not luminance and not alphacontrast it will have color
|
|
if( !FBitSet( flags, TF_LUMINANCE ) && !FBitSet( flags, TF_ALPHACONTRAST ))
|
|
SetBits( r_empty.flags, IMAGE_HAS_COLOR );
|
|
|
|
if( FBitSet( flags, TF_HAS_ALPHA ))
|
|
SetBits( r_empty.flags, IMAGE_HAS_ALPHA );
|
|
|
|
if( FBitSet( flags, TF_CUBEMAP ))
|
|
{
|
|
// no cubemaps in dx2
|
|
return 0;
|
|
}
|
|
|
|
int texnum = GL_LoadTextureFromBuffer( name, &r_empty, flags, update );
|
|
|
|
if( !Q_strcmp( name, REF_DEFAULT_TEXTURE ))
|
|
tr.defaultTexture = texnum;
|
|
else if( !Q_strcmp( name, REF_PARTICLE_TEXTURE ))
|
|
tr.particleTexture = texnum;
|
|
else if( !Q_strcmp( name, REF_WHITE_TEXTURE ))
|
|
tr.whiteTexture = texnum;
|
|
else if( !Q_strcmp( name, REF_GRAY_TEXTURE ))
|
|
tr.grayTexture = texnum;
|
|
else if( !Q_strcmp( name, REF_BLACK_TEXTURE ))
|
|
tr.blackTexture = texnum;
|
|
|
|
return texnum;
|
|
}
|
|
|
|
void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor )
|
|
{
|
|
;
|
|
}
|
|
|
|
void R_GetDetailScaleForTexture( int texture, float *xScale, float *yScale )
|
|
{
|
|
*xScale = *yScale = 1.0f;
|
|
}
|
|
|
|
void R_SetDetailScaleForTexture( int texture, float xScale, float yScale )
|
|
{
|
|
;
|
|
}
|
|
|
|
int GL_FindTexture( const char *name )
|
|
{
|
|
gEngfuncs.Con_Printf("GL_FindTexture(%s)\n", name);
|
|
return 0;
|
|
}
|
|
|
|
const char *GL_TextureName( unsigned int texnum )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
const byte *GL_TextureData( unsigned int texnum )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
|
|
{
|
|
dx_texture_t *tex;
|
|
rgbdata_t *pic;
|
|
uint picFlags = 0;
|
|
|
|
if (!GL_CheckTexName(name))
|
|
return 0;
|
|
|
|
// see if already loaded
|
|
if ((tex = GL_TextureForName(name)))
|
|
return (tex - dx_textures);
|
|
|
|
gEngfuncs.Con_Printf("GL_LoadTexture(%s, %p, %zu, %X)\n", name, buf, size, flags);
|
|
|
|
if (FBitSet(flags, TF_NOFLIP_TGA))
|
|
SetBits(picFlags, IL_DONTFLIP_TGA);
|
|
|
|
if (FBitSet(flags, TF_KEEP_SOURCE) && !FBitSet(flags, TF_EXPAND_SOURCE))
|
|
SetBits(picFlags, IL_KEEP_8BIT);
|
|
|
|
// set some image flags
|
|
gEngfuncs.Image_SetForceFlags(picFlags);
|
|
|
|
pic = gEngfuncs.FS_LoadImage(name, buf, size);
|
|
if (!pic) return 0; // couldn't loading image
|
|
|
|
// allocate the new one
|
|
tex = GL_AllocTexture(name, flags);
|
|
GL_ProcessImage(tex, pic);
|
|
|
|
if (!GL_UploadTexture(tex, pic))
|
|
{
|
|
memset(tex, 0, sizeof(dx_texture_t));
|
|
gEngfuncs.FS_FreeImage(pic); // release source texture
|
|
return 0;
|
|
}
|
|
|
|
//GL_ApplyTextureParams(tex); // update texture filter, wrap etc
|
|
gEngfuncs.FS_FreeImage(pic); // release source texture
|
|
|
|
// NOTE: always return texnum as index in array or engine will stop work !!!
|
|
return tex - dx_textures;
|
|
}
|
|
|
|
void GL_FreeTexture( unsigned int texnum )
|
|
{
|
|
if (texnum <= 0 || texnum >= MAX_TEXTURES)
|
|
return;
|
|
|
|
gEngfuncs.Con_Printf("GL_FreeTexture(%d) %s\n", texnum, dx_textures[texnum].name);
|
|
|
|
dx_texture_t* tex = R_GetTexture(texnum);
|
|
if (!tex->dds)
|
|
return;
|
|
|
|
// debug
|
|
if (!tex->name[0])
|
|
{
|
|
gEngfuncs.Con_Printf(S_ERROR "%s: trying to free unnamed texture with handle %X\n", __func__, tex->d3dHandle);
|
|
return;
|
|
}
|
|
|
|
// remove from hash table
|
|
dx_texture_t **prev = &dx_texturesHashTable[tex->hashValue];
|
|
|
|
while (1)
|
|
{
|
|
dx_texture_t *cur = *prev;
|
|
if (!cur) break;
|
|
|
|
if (cur == tex)
|
|
{
|
|
*prev = cur->nextHash;
|
|
break;
|
|
}
|
|
prev = &cur->nextHash;
|
|
}
|
|
|
|
// TODO invalidate texture state
|
|
|
|
// release source
|
|
if (tex->original)
|
|
gEngfuncs.FS_FreeImage(tex->original);
|
|
|
|
if (tex->d3dTex)
|
|
IDirect3DTexture_Release(tex->d3dTex);
|
|
if (tex->dds)
|
|
IDirectDrawSurface_Release(tex->dds);
|
|
memset(tex, 0, sizeof(*tex));
|
|
}
|
|
|
|
void R_OverrideTextureSourceSize( unsigned int textnum, unsigned int srcWidth, unsigned int srcHeight )
|
|
{
|
|
|
|
}
|
|
|
|
void GL_UpdateTexture( int texnum, int cols, int rows, int width, int height, const byte *buffer, pixformat_t fmt )
|
|
{
|
|
;
|
|
}
|