mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
Allow more than 32 surfaces in skin files
Models don't have a surface limit; skins shouldn't either. Some player models require more than 32 surfaces since vanilla Quake 3 did not enforce the limit. Skins are now limited to 256 surfaces because having no limit would require parsing the skin file twice. The skin surfaces are dynamically allocated so it doesn't increase memory usage when less surfaces are used.
This commit is contained in:
parent
4dffc52c1d
commit
904bbc1a8f
10 changed files with 54 additions and 32 deletions
|
@ -263,9 +263,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) {
|
||||||
|
|
||||||
for(j = 0; j < skin->numSurfaces; j++)
|
for(j = 0; j < skin->numSurfaces; j++)
|
||||||
{
|
{
|
||||||
if (!strcmp(skin->surfaces[j]->name, surface->name))
|
if (!strcmp(skin->surfaces[j].name, surface->name))
|
||||||
{
|
{
|
||||||
shader = skin->surfaces[j]->shader;
|
shader = skin->surfaces[j].shader;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1508,6 +1508,7 @@ RE_RegisterSkin
|
||||||
===============
|
===============
|
||||||
*/
|
*/
|
||||||
qhandle_t RE_RegisterSkin( const char *name ) {
|
qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
|
skinSurface_t parseSurfaces[MAX_SKIN_SURFACES];
|
||||||
qhandle_t hSkin;
|
qhandle_t hSkin;
|
||||||
skin_t *skin;
|
skin_t *skin;
|
||||||
skinSurface_t *surf;
|
skinSurface_t *surf;
|
||||||
|
@ -1557,8 +1558,8 @@ qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
// If not a .skin file, load as a single shader
|
// If not a .skin file, load as a single shader
|
||||||
if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
|
if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
|
||||||
skin->numSurfaces = 1;
|
skin->numSurfaces = 1;
|
||||||
skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
|
skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low );
|
||||||
skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue );
|
skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue );
|
||||||
return hSkin;
|
return hSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1591,12 +1592,12 @@ qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
// parse the shader name
|
// parse the shader name
|
||||||
token = CommaParse( &text_p );
|
token = CommaParse( &text_p );
|
||||||
|
|
||||||
if ( skin->numSurfaces >= MD3_MAX_SURFACES ) {
|
if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) {
|
||||||
ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES );
|
ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
|
surf = &parseSurfaces[skin->numSurfaces];
|
||||||
Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
|
Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
|
||||||
surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue );
|
surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue );
|
||||||
skin->numSurfaces++;
|
skin->numSurfaces++;
|
||||||
|
@ -1610,6 +1611,10 @@ qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
return 0; // use default skin
|
return 0; // use default skin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy surfaces to skin
|
||||||
|
skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low );
|
||||||
|
memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) );
|
||||||
|
|
||||||
return hSkin;
|
return hSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1628,8 +1633,8 @@ void R_InitSkins( void ) {
|
||||||
skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
|
skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
|
||||||
Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
|
Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
|
||||||
skin->numSurfaces = 1;
|
skin->numSurfaces = 1;
|
||||||
skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
|
skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low );
|
||||||
skin->surfaces[0]->shader = tr.defaultShader;
|
skin->surfaces[0].shader = tr.defaultShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1658,10 +1663,10 @@ void R_SkinList_f( void ) {
|
||||||
for ( i = 0 ; i < tr.numSkins ; i++ ) {
|
for ( i = 0 ; i < tr.numSkins ; i++ ) {
|
||||||
skin = tr.skins[i];
|
skin = tr.skins[i];
|
||||||
|
|
||||||
ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name );
|
ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces );
|
||||||
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
||||||
ri.Printf( PRINT_ALL, " %s = %s\n",
|
ri.Printf( PRINT_ALL, " %s = %s\n",
|
||||||
skin->surfaces[j]->name, skin->surfaces[j]->shader->name );
|
skin->surfaces[j].name, skin->surfaces[j].shader->name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ri.Printf (PRINT_ALL, "------------------\n");
|
ri.Printf (PRINT_ALL, "------------------\n");
|
||||||
|
|
|
@ -411,6 +411,12 @@ typedef struct {
|
||||||
|
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
|
|
||||||
|
// max surfaces per-skin
|
||||||
|
// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to
|
||||||
|
// enforce the maximum limit when reading skin files. It was possile to use more than 32
|
||||||
|
// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block.
|
||||||
|
#define MAX_SKIN_SURFACES 256
|
||||||
|
|
||||||
// skins allow models to be retextured without modifying the model file
|
// skins allow models to be retextured without modifying the model file
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[MAX_QPATH];
|
char name[MAX_QPATH];
|
||||||
|
@ -420,7 +426,7 @@ typedef struct {
|
||||||
typedef struct skin_s {
|
typedef struct skin_s {
|
||||||
char name[MAX_QPATH]; // game path, including extension
|
char name[MAX_QPATH]; // game path, including extension
|
||||||
int numSurfaces;
|
int numSurfaces;
|
||||||
skinSurface_t *surfaces[MD3_MAX_SURFACES];
|
skinSurface_t *surfaces; // dynamically allocated array of surfaces
|
||||||
} skin_t;
|
} skin_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -361,8 +361,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) {
|
||||||
shader = tr.defaultShader;
|
shader = tr.defaultShader;
|
||||||
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
||||||
// the names have both been lowercased
|
// the names have both been lowercased
|
||||||
if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
|
if ( !strcmp( skin->surfaces[j].name, surface->name ) ) {
|
||||||
shader = skin->surfaces[j]->shader;
|
shader = skin->surfaces[j].shader;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -903,9 +903,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) {
|
||||||
|
|
||||||
for(j = 0; j < skin->numSurfaces; j++)
|
for(j = 0; j < skin->numSurfaces; j++)
|
||||||
{
|
{
|
||||||
if (!strcmp(skin->surfaces[j]->name, surface->name))
|
if (!strcmp(skin->surfaces[j].name, surface->name))
|
||||||
{
|
{
|
||||||
shader = skin->surfaces[j]->shader;
|
shader = skin->surfaces[j].shader;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,9 +267,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) {
|
||||||
|
|
||||||
for(j = 0; j < skin->numSurfaces; j++)
|
for(j = 0; j < skin->numSurfaces; j++)
|
||||||
{
|
{
|
||||||
if (!strcmp(skin->surfaces[j]->name, surface->name))
|
if (!strcmp(skin->surfaces[j].name, surface->name))
|
||||||
{
|
{
|
||||||
shader = skin->surfaces[j]->shader;
|
shader = skin->surfaces[j].shader;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3060,6 +3060,7 @@ RE_RegisterSkin
|
||||||
===============
|
===============
|
||||||
*/
|
*/
|
||||||
qhandle_t RE_RegisterSkin( const char *name ) {
|
qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
|
skinSurface_t parseSurfaces[MAX_SKIN_SURFACES];
|
||||||
qhandle_t hSkin;
|
qhandle_t hSkin;
|
||||||
skin_t *skin;
|
skin_t *skin;
|
||||||
skinSurface_t *surf;
|
skinSurface_t *surf;
|
||||||
|
@ -3109,8 +3110,8 @@ qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
// If not a .skin file, load as a single shader
|
// If not a .skin file, load as a single shader
|
||||||
if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
|
if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) {
|
||||||
skin->numSurfaces = 1;
|
skin->numSurfaces = 1;
|
||||||
skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
|
skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low );
|
||||||
skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue );
|
skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue );
|
||||||
return hSkin;
|
return hSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3143,12 +3144,12 @@ qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
// parse the shader name
|
// parse the shader name
|
||||||
token = CommaParse( &text_p );
|
token = CommaParse( &text_p );
|
||||||
|
|
||||||
if ( skin->numSurfaces >= MD3_MAX_SURFACES ) {
|
if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) {
|
||||||
ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES );
|
ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
|
surf = &parseSurfaces[skin->numSurfaces];
|
||||||
Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
|
Q_strncpyz( surf->name, surfName, sizeof( surf->name ) );
|
||||||
surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue );
|
surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue );
|
||||||
skin->numSurfaces++;
|
skin->numSurfaces++;
|
||||||
|
@ -3162,6 +3163,10 @@ qhandle_t RE_RegisterSkin( const char *name ) {
|
||||||
return 0; // use default skin
|
return 0; // use default skin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy surfaces to skin
|
||||||
|
skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low );
|
||||||
|
memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) );
|
||||||
|
|
||||||
return hSkin;
|
return hSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3180,8 +3185,8 @@ void R_InitSkins( void ) {
|
||||||
skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
|
skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low );
|
||||||
Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
|
Q_strncpyz( skin->name, "<default skin>", sizeof( skin->name ) );
|
||||||
skin->numSurfaces = 1;
|
skin->numSurfaces = 1;
|
||||||
skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low );
|
skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low );
|
||||||
skin->surfaces[0]->shader = tr.defaultShader;
|
skin->surfaces[0].shader = tr.defaultShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3210,10 +3215,10 @@ void R_SkinList_f( void ) {
|
||||||
for ( i = 0 ; i < tr.numSkins ; i++ ) {
|
for ( i = 0 ; i < tr.numSkins ; i++ ) {
|
||||||
skin = tr.skins[i];
|
skin = tr.skins[i];
|
||||||
|
|
||||||
ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name );
|
ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces );
|
||||||
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
||||||
ri.Printf( PRINT_ALL, " %s = %s\n",
|
ri.Printf( PRINT_ALL, " %s = %s\n",
|
||||||
skin->surfaces[j]->name, skin->surfaces[j]->shader->name );
|
skin->surfaces[j].name, skin->surfaces[j].shader->name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ri.Printf (PRINT_ALL, "------------------\n");
|
ri.Printf (PRINT_ALL, "------------------\n");
|
||||||
|
|
|
@ -766,6 +766,12 @@ typedef struct {
|
||||||
|
|
||||||
//=================================================================================
|
//=================================================================================
|
||||||
|
|
||||||
|
// max surfaces per-skin
|
||||||
|
// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to
|
||||||
|
// enforce the maximum limit when reading skin files. It was possile to use more than 32
|
||||||
|
// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block.
|
||||||
|
#define MAX_SKIN_SURFACES 256
|
||||||
|
|
||||||
// skins allow models to be retextured without modifying the model file
|
// skins allow models to be retextured without modifying the model file
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[MAX_QPATH];
|
char name[MAX_QPATH];
|
||||||
|
@ -775,7 +781,7 @@ typedef struct {
|
||||||
typedef struct skin_s {
|
typedef struct skin_s {
|
||||||
char name[MAX_QPATH]; // game path, including extension
|
char name[MAX_QPATH]; // game path, including extension
|
||||||
int numSurfaces;
|
int numSurfaces;
|
||||||
skinSurface_t *surfaces[MD3_MAX_SURFACES];
|
skinSurface_t *surfaces; // dynamically allocated array of surfaces
|
||||||
} skin_t;
|
} skin_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -365,8 +365,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) {
|
||||||
shader = tr.defaultShader;
|
shader = tr.defaultShader;
|
||||||
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
for ( j = 0 ; j < skin->numSurfaces ; j++ ) {
|
||||||
// the names have both been lowercased
|
// the names have both been lowercased
|
||||||
if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) {
|
if ( !strcmp( skin->surfaces[j].name, surface->name ) ) {
|
||||||
shader = skin->surfaces[j]->shader;
|
shader = skin->surfaces[j].shader;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -907,9 +907,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) {
|
||||||
|
|
||||||
for(j = 0; j < skin->numSurfaces; j++)
|
for(j = 0; j < skin->numSurfaces; j++)
|
||||||
{
|
{
|
||||||
if (!strcmp(skin->surfaces[j]->name, surface->name))
|
if (!strcmp(skin->surfaces[j].name, surface->name))
|
||||||
{
|
{
|
||||||
shader = skin->surfaces[j]->shader;
|
shader = skin->surfaces[j].shader;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue