From c50162e8b72cafec34f4a52aa20d105f2386abc0 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Sat, 12 Dec 2020 18:59:40 +0100 Subject: [PATCH] vmap: Read .mat files directly instead of relying on shaderlist.txt files and massive .shader dumps. --- tools/CMakeLists.txt | 1 + tools/common/matlib.c | 417 +++++++++++++++++ tools/common/matlib.h | 53 +++ tools/vmap/game_fte.h | 2 +- tools/vmap/shaders.c | 1008 ++++++++++++++++++++++++++++++++++++++++- tools/vmap/vmap.h | 1 + 6 files changed, 1480 insertions(+), 2 deletions(-) create mode 100644 tools/common/matlib.c create mode 100644 tools/common/matlib.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 245d551..dc82ec9 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -48,6 +48,7 @@ radiant_tool(vmap common/qfiles.h common/qthreads.h common/scriplib.c common/scriplib.h + common/matlib.c common/matlib.h common/surfaceflags.h common/threads.c common/vfs.c common/vfs.h diff --git a/tools/common/matlib.c b/tools/common/matlib.c new file mode 100644 index 0000000..730c187 --- /dev/null +++ b/tools/common/matlib.c @@ -0,0 +1,417 @@ +/* + Copyright (C) 1999-2007 id Software, Inc. and contributors. + For a list of contributors, see the accompanying CONTRIBUTORS file. + + This file is part of GtkRadiant. + + GtkRadiant 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 2 of the License, or + (at your option) any later version. + + GtkRadiant 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. + + You should have received a copy of the GNU General Public License + along with GtkRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// matlib.c + +#include "cmdlib.h" +#include "mathlib.h" +#include "inout.h" +#include "matlib.h" +#include "vfs.h" + +/* + ============================================================================= + + PARSING STUFF + + ============================================================================= + */ + +typedef struct +{ + char filename[1024]; + char *buffer,*script_p,*end_p; + int line; +} mat_t; + +#define MAX_INCLUDES 8 +mat_t matstack[MAX_INCLUDES]; +mat_t *mat; +int matline; + +char mattoken[MAXTOKEN]; +qboolean endofmat; +qboolean mattokenready; // only qtrue if UnGetMatToken was just called + +/* + ============== + AddMatToStack + ============== + */ +void AddMatToStack( const char *filename, int index ){ + int size; + void* buffer; + + mat++; + if ( mat == &matstack[MAX_INCLUDES] ) { + Error( "mat file exceeded MAX_INCLUDES" ); + } + strcpy( mat->filename, ExpandPath( filename ) ); + + size = vfsLoadFile( mat->filename, &buffer, index ); + + if ( size == -1 ) { + Sys_Printf( "Script file %s was not found\n", mat->filename ); + mat--; + } + else + { + if ( index > 0 ) { + Sys_Printf( "entering %s (%d)\n", mat->filename, index + 1 ); + } + else{ + Sys_Printf( "entering %s\n", mat->filename ); + } + + mat->buffer = buffer; + mat->line = 1; + mat->script_p = mat->buffer; + mat->end_p = mat->buffer + size; + } +} + + +/* + ============== + LoadMatFile + ============== + */ +void LoadMatFile( const char *filename, int index ){ + mat = matstack; + AddMatToStack( filename, index ); + + endofmat = qfalse; + mattokenready = qfalse; +} + + +/* + ============== + ParseMatMemory + ============== + */ +void ParseMatMemory( char *buffer, int size ){ + mat = matstack; + mat++; + if ( mat == &matstack[MAX_INCLUDES] ) { + Error( "mat file exceeded MAX_INCLUDES" ); + } + strcpy( mat->filename, "memory buffer" ); + + mat->buffer = buffer; + mat->line = 1; + mat->script_p = mat->buffer; + mat->end_p = mat->buffer + size; + + endofmat = qfalse; + mattokenready = qfalse; +} + + +/* + ============== + UnGetMatToken + + Signals that the current mattoken was not used, and should be reported + for the next GetMatToken. Note that + + GetMatToken (qtrue); + UnGetMatToken (); + GetMatToken (qfalse); + + could cross a line boundary. + ============== + */ +void UnGetMatToken( void ){ + mattokenready = qtrue; +} + + +qboolean EndOfMat( qboolean crossline ){ + if ( !crossline ) { + Error( "Line %i is incomplete\n",matline ); + } + + if ( !strcmp( mat->filename, "memory buffer" ) ) { + endofmat = qtrue; + return qfalse; + } + + if ( mat->buffer == NULL ) { + Sys_FPrintf( SYS_WRN, "WARNING: Attempt to free already freed mat buffer\n" ); + } + else{ + free( mat->buffer ); + } + mat->buffer = NULL; + if ( mat == matstack + 1 ) { + endofmat = qtrue; + return qfalse; + } + mat--; + matline = mat->line; + Sys_Printf( "returning to %s\n", mat->filename ); + return GetMatToken( crossline ); +} + +/* + ============== + GetMatToken + ============== + */ +qboolean GetMatToken( qboolean crossline ){ + char *mattoken_p; + + + /* ydnar: dummy testing */ + if ( mat == NULL || mat->buffer == NULL ) { + return qfalse; + } + + if ( mattokenready ) { // is a mattoken already waiting? + mattokenready = qfalse; + return qtrue; + } + + if ( ( mat->script_p >= mat->end_p ) || ( mat->script_p == NULL ) ) { + return EndOfMat( crossline ); + } + +// +// skip space +// +skipspace: + while ( *mat->script_p <= 32 ) + { + if ( mat->script_p >= mat->end_p ) { + return EndOfMat( crossline ); + } + if ( *mat->script_p++ == '\n' ) { + if ( !crossline ) { + Error( "Line %i is incomplete\n",matline ); + } + mat->line++; + matline = mat->line; + } + } + + if ( mat->script_p >= mat->end_p ) { + return EndOfMat( crossline ); + } + + // ; # // comments + if ( *mat->script_p == ';' || *mat->script_p == '#' + || ( mat->script_p[0] == '/' && mat->script_p[1] == '/' ) ) { + if ( !crossline ) { + Error( "Line %i is incomplete\n",matline ); + } + while ( *mat->script_p++ != '\n' ) + if ( mat->script_p >= mat->end_p ) { + return EndOfMat( crossline ); + } + mat->line++; + matline = mat->line; + goto skipspace; + } + + // /* */ comments + if ( mat->script_p[0] == '/' && mat->script_p[1] == '*' ) { + if ( !crossline ) { + Error( "Line %i is incomplete\n",matline ); + } + mat->script_p += 2; + while ( mat->script_p[0] != '*' && mat->script_p[1] != '/' ) + { + if ( *mat->script_p == '\n' ) { + mat->line++; + matline = mat->line; + } + mat->script_p++; + if ( mat->script_p >= mat->end_p ) { + return EndOfMat( crossline ); + } + } + mat->script_p += 2; + goto skipspace; + } + +// +// copy mattoken +// + mattoken_p = mattoken; + + if ( *mat->script_p == '"' ) { + // quoted mattoken + mat->script_p++; + while ( *mat->script_p != '"' ) + { + *mattoken_p++ = *mat->script_p++; + if ( mat->script_p == mat->end_p ) { + break; + } + if ( mattoken_p == &mattoken[MAXTOKEN] ) { + Error( "Token too large on line %i\n",matline ); + } + } + mat->script_p++; + } + else{ // regular mattoken + while ( *mat->script_p > 32 && *mat->script_p != ';' ) + { + *mattoken_p++ = *mat->script_p++; + if ( mat->script_p == mat->end_p ) { + break; + } + if ( mattoken_p == &mattoken[MAXTOKEN] ) { + Error( "Token too large on line %i\n",matline ); + } + } + } + + *mattoken_p = 0; + + if ( !strcmp( mattoken, "$include" ) ) { + GetMatToken( qfalse ); + AddMatToStack( mattoken, 0 ); + return GetMatToken( crossline ); + } + + return qtrue; +} + + +/* + ============== + MatTokenAvailable + + Returns qtrue if there is another mattoken on the line + ============== + */ +qboolean MatTokenAvailable( void ) { + int oldLine; + qboolean r; + + /* save */ + oldLine = matline; + + /* test */ + r = GetMatToken( qtrue ); + if ( !r ) { + return qfalse; + } + UnGetMatToken(); + if ( oldLine == matline ) { + return qtrue; + } + + /* restore */ + //% matline = oldLine; + //% script->line = oldScriptLine; + + return qfalse; +} + + +//===================================================================== + + +void MatchMatToken( char *match ) { + GetMatToken( qtrue ); + + if ( strcmp( mattoken, match ) ) { + Error( "MatchMatToken( \"%s\" ) failed at line %i in file %s", match, matline, mat->filename ); + } +} + +void Parse1DMatMatrix( int x, vec_t *m ) { + int i; + + MatchMatToken( "(" ); + + for ( i = 0 ; i < x ; i++ ) { + GetMatToken( qfalse ); + m[i] = atof( mattoken ); + } + + MatchMatToken( ")" ); +} + +void Parse2DMatMatrix( int y, int x, vec_t *m ) { + int i; + + MatchMatToken( "(" ); + + for ( i = 0 ; i < y ; i++ ) { + Parse1DMatMatrix( x, m + i * x ); + } + + MatchMatToken( ")" ); +} + +void Parse3DMatMatrix( int z, int y, int x, vec_t *m ) { + int i; + + MatchMatToken( "(" ); + + for ( i = 0 ; i < z ; i++ ) { + Parse2DMatMatrix( y, x, m + i * x * y ); + } + + MatchMatToken( ")" ); +} + + +void Write1DMatMatrix( FILE *f, int x, vec_t *m ) { + int i; + + fprintf( f, "( " ); + for ( i = 0 ; i < x ; i++ ) { + if ( m[i] == (int)m[i] ) { + fprintf( f, "%i ", (int)m[i] ); + } + else { + fprintf( f, "%f ", m[i] ); + } + } + fprintf( f, ")" ); +} + +void Write2DMatMatrix( FILE *f, int y, int x, vec_t *m ) { + int i; + + fprintf( f, "( " ); + for ( i = 0 ; i < y ; i++ ) { + Write1DMatMatrix( f, x, m + i * x ); + fprintf( f, " " ); + } + fprintf( f, ")\n" ); +} + + +void Write3DMatMatrix( FILE *f, int z, int y, int x, vec_t *m ) { + int i; + + fprintf( f, "(\n" ); + for ( i = 0 ; i < z ; i++ ) { + Write2DMatMatrix( f, y, x, m + i * ( x * y ) ); + } + fprintf( f, ")\n" ); +} diff --git a/tools/common/matlib.h b/tools/common/matlib.h new file mode 100644 index 0000000..03957c3 --- /dev/null +++ b/tools/common/matlib.h @@ -0,0 +1,53 @@ +/* + Copyright (C) 1999-2007 id Software, Inc. and contributors. + For a list of contributors, see the accompanying CONTRIBUTORS file. + + This file is part of GtkRadiant. + + GtkRadiant 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 2 of the License, or + (at your option) any later version. + + GtkRadiant 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. + + You should have received a copy of the GNU General Public License + along with GtkRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// scriplib.h + +#ifndef __CMDLIB__ +#include "../common/cmdlib.h" +#endif +#ifndef __MATHLIB__ +#include "mathlib.h" +#endif + +#define MAXTOKEN 1024 + +extern char mattoken[MAXTOKEN]; +extern int matline; +extern qboolean endofmat; + + +void LoadMatFile( const char *filename, int index ); +void ParseMatMemory( char *buffer, int size ); + +qboolean GetMatToken( qboolean crossline ); +void UnGetMatToken( void ); +qboolean MatTokenAvailable( void ); + +void MatchMatToken( char *match ); + +void Parse1DMatMatrix( int x, vec_t *m ); +void Parse2DMatMatrix( int y, int x, vec_t *m ); +void Parse3DMatMatrix( int z, int y, int x, vec_t *m ); + +void Write1DMatMatrix( FILE *f, int x, vec_t *m ); +void Write2DMatMatrix( FILE *f, int y, int x, vec_t *m ); +void Write3DMatMatrix( FILE *f, int z, int y, int x, vec_t *m ); diff --git a/tools/vmap/game_fte.h b/tools/vmap/game_fte.h index 8a72d0b..7f09780 100644 --- a/tools/vmap/game_fte.h +++ b/tools/vmap/game_fte.h @@ -96,7 +96,7 @@ { "fte", /* -game x */ - "wastes", /* default base game data dir */ + "platform", /* default base game data dir */ ".fte", /* unix home sub-dir */ "quake", /* magic path word */ "scripts", /* shader directory */ diff --git a/tools/vmap/shaders.c b/tools/vmap/shaders.c index 5d73c1c..15303f4 100644 --- a/tools/vmap/shaders.c +++ b/tools/vmap/shaders.c @@ -831,11 +831,83 @@ shaderInfo_t *ShaderInfoForShaderNull( const char *shaderName ){ return ShaderInfoForShader( shaderName ); } +/* + GetTokenAppend() - ydnar + gets a token and appends its text to the specified buffer + */ + +static int oldMatLine = 0; +static int mattabDepth = 0; + +qboolean GetMatTokenAppend( char *buffer, qboolean crossline ){ + qboolean r; + int i; + + + /* get the token */ + r = GetMatToken( crossline ); + if ( r == qfalse || buffer == NULL || mattoken[ 0 ] == '\0' ) { + return r; + } + + /* pre-tabstops */ + if ( mattoken[ 0 ] == '}' ) { + mattabDepth--; + } + + /* append? */ + if ( oldMatLine != matline ) { + strcat( buffer, "\n" ); + for ( i = 0; i < mattabDepth; i++ ) + strcat( buffer, "\t" ); + } + else{ + strcat( buffer, " " ); + } + oldMatLine = matline; + strcat( buffer, mattoken ); + + /* post-tabstops */ + if ( mattoken[ 0 ] == '{' ) { + mattabDepth++; + } + + /* return */ + return r; +} + +void Parse1DMatMatrixAppend( char *buffer, int x, vec_t *m ){ + int i; + + + if ( !GetMatTokenAppend( buffer, qtrue ) || strcmp( mattoken, "(" ) ) { + Error( "Parse1DMatrixAppend(): line %d: ( not found!", matline ); + } + for ( i = 0; i < x; i++ ) + { + if ( !GetMatTokenAppend( buffer, qfalse ) ) { + Error( "Parse1DMatrixAppend(): line %d: Number not found!", matline ); + } + m[ i ] = atof( mattoken ); + } + if ( !GetMatTokenAppend( buffer, qtrue ) || strcmp( mattoken, ")" ) ) { + Error( "Parse1DMatrixAppend(): line %d: ) not found!", matline ); + } +} + shaderInfo_t *ShaderInfoForShader( const char *shaderName ){ int i; int deprecationDepth; shaderInfo_t *si; char shader[ MAX_QPATH ]; + char filename[ MAX_QPATH ]; + char shaderText[ 8192 ], temp[ 1024 ]; + int val; + qboolean parsedContent; + + + /* init */ + si = NULL; /* dummy check */ if ( shaderName == NULL || shaderName[ 0 ] == '\0' ) { @@ -847,7 +919,7 @@ shaderInfo_t *ShaderInfoForShader( const char *shaderName ){ strcpy( shader, shaderName ); StripExtension( shader ); - /* search for it */ +#if 1 deprecationDepth = 0; for ( i = 0; i < numShaderInfo; i++ ) { @@ -878,10 +950,944 @@ shaderInfo_t *ShaderInfoForShader( const char *shaderName ){ return si; } } +#endif /* allocate a default shader */ si = AllocShaderInfo(); strcpy( si->shader, shader ); + +#if 1 + sprintf( filename, "%s.mat", shader ); + if ( vfsGetFileCount(filename) == 0 ) { + LoadShaderImages( si ); + FinishShader( si ); + + /* return it */ + return si; + } + + /* load the shader */ + LoadMatFile( filename, 0 ); + + /* copy shader text to the shaderinfo */ + if ( si != NULL && shaderText[ 0 ] != '\0' ) { + strcat( shaderText, "\n" ); + si->shaderText = safe_malloc( strlen( shaderText ) + 1 ); + strcpy( si->shaderText, shaderText ); + //% if( VectorLength( si->vecs[ 0 ] ) ) + //% Sys_Printf( "%s\n", shaderText ); + } + + /* ydnar: clear shader text buffer */ + shaderText[ 0 ] = '\0'; + + while ( 1 ) + { + /* get the next mattoken */ + if ( !GetMatTokenAppend( shaderText, qtrue ) ) { + break; + } + + if ( !strcmp( mattoken, "}" ) ) { + break; + } + + /* ----------------------------------------------------------------- + surfaceparm * directives + ----------------------------------------------------------------- */ + + /* match surfaceparm */ + if ( !Q_stricmp( mattoken, "surfaceparm" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( ApplySurfaceParm( mattoken, &si->contentFlags, &si->surfaceFlags, &si->compileFlags ) == qfalse ) { + Sys_FPrintf( SYS_WRN, "WARNING: Unknown surfaceparm: \"%s\"\n", mattoken ); + } + } + + + /* ----------------------------------------------------------------- + game-related shader directives + ----------------------------------------------------------------- */ + + /* ydnar: fogparms (for determining fog volumes) */ + else if ( !Q_stricmp( mattoken, "fogparms" ) ) { + si->fogParms = qtrue; + } + + /* ydnar: polygonoffset (for no culling) */ + else if ( !Q_stricmp( mattoken, "polygonoffset" ) ) { + si->polygonOffset = qtrue; + } + + /* tesssize is used to force liquid surfaces to subdivide */ + else if ( !Q_stricmp( mattoken, "tessSize" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->subdivisions = atof( mattoken ); + } + + /* cull none will set twoSided (ydnar: added disable too) */ + else if ( !Q_stricmp( mattoken, "cull" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( !Q_stricmp( mattoken, "none" ) || !Q_stricmp( mattoken, "disable" ) || !Q_stricmp( mattoken, "twosided" ) ) { + si->twoSided = qtrue; + } + } + + /* deformVertexes autosprite[ 2 ] + we catch this so autosprited surfaces become point + lights instead of area lights */ + else if ( !Q_stricmp( mattoken, "deformVertexes" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + + /* deformVertexes autosprite(2) */ + if ( !Q_strncasecmp( mattoken, "autosprite", 10 ) ) { + /* set it as autosprite and detail */ + si->autosprite = qtrue; + ApplySurfaceParm( "detail", &si->contentFlags, &si->surfaceFlags, &si->compileFlags ); + + /* ydnar: gs mods: added these useful things */ + si->noClip = qtrue; + si->notjunc = qtrue; + } + + /* deformVertexes move (ydnar: for particle studio support) */ + if ( !Q_stricmp( mattoken, "move" ) ) { + vec3_t amt, mins, maxs; + float base, amp; + + + /* get move amount */ + GetMatTokenAppend( shaderText, qfalse ); amt[ 0 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); amt[ 1 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); amt[ 2 ] = atof( mattoken ); + + /* skip func */ + GetMatTokenAppend( shaderText, qfalse ); + + /* get base and amplitude */ + GetMatTokenAppend( shaderText, qfalse ); base = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); amp = atof( mattoken ); + + /* calculate */ + VectorScale( amt, base, mins ); + VectorMA( mins, amp, amt, maxs ); + VectorAdd( si->mins, mins, si->mins ); + VectorAdd( si->maxs, maxs, si->maxs ); + } + } + + /* ydnar: damageShader (sof2 mods) */ + else if ( !Q_stricmp( mattoken, "damageShader" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] != '\0' ) { + si->damageShader = safe_malloc( strlen( mattoken ) + 1 ); + strcpy( si->damageShader, mattoken ); + } + GetMatTokenAppend( shaderText, qfalse ); /* don't do anything with health */ + } + + /* ydnar: enemy territory implicit shaders */ + else if ( !Q_stricmp( mattoken, "implicitMap" ) ) { + si->implicitMap = IM_OPAQUE; + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] == '-' && mattoken[ 1 ] == '\0' ) { + sprintf( si->implicitImagePath, "%s.tga", si->shader ); + } + else{ + strcpy( si->implicitImagePath, mattoken ); + } + } + + else if ( !Q_stricmp( mattoken, "implicitMask" ) ) { + si->implicitMap = IM_MASKED; + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] == '-' && mattoken[ 1 ] == '\0' ) { + sprintf( si->implicitImagePath, "%s.tga", si->shader ); + } + else{ + strcpy( si->implicitImagePath, mattoken ); + } + } + + else if ( !Q_stricmp( mattoken, "implicitBlend" ) ) { + si->implicitMap = IM_MASKED; + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] == '-' && mattoken[ 1 ] == '\0' ) { + sprintf( si->implicitImagePath, "%s.tga", si->shader ); + } + else{ + strcpy( si->implicitImagePath, mattoken ); + } + } + + + /* ----------------------------------------------------------------- + image directives + ----------------------------------------------------------------- */ + + /* qer_editorimage */ + else if ( !Q_stricmp( mattoken, "qer_editorImage" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + strcpy( si->editorImagePath, mattoken ); + DefaultExtension( si->editorImagePath, ".tga" ); + } + + /* diffusemap */ + else if ( !Q_stricmp( mattoken, "diffusemap" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + strcpy( si->editorImagePath, mattoken ); + DefaultExtension( si->editorImagePath, ".tga" ); + } + + /* ydnar: q3map_normalimage (bumpmapping normal map) */ + /*else if ( !Q_stricmp( mattoken, "q3map_normalImage" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + strcpy( si->normalImagePath, mattoken ); + DefaultExtension( si->normalImagePath, ".tga" ); + }*/ + + /* q3map_lightimage */ + else if ( !Q_stricmp( mattoken, "q3map_lightImage" ) || !Q_stricmp( mattoken, "vmap_lightImage" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + strcpy( si->lightImagePath, mattoken ); + DefaultExtension( si->lightImagePath, ".tga" ); + } + + /* ydnar: skyparms */ + else if ( !Q_stricmp( mattoken, "skyParms" ) ) { + /* get image base */ + GetMatTokenAppend( shaderText, qfalse ); + + /* ignore bogus paths */ + if ( Q_stricmp( mattoken, "-" ) && Q_stricmp( mattoken, "full" ) ) { + strcpy( si->skyParmsImageBase, mattoken ); + } + + /* skip rest of line */ + GetMatTokenAppend( shaderText, qfalse ); + GetMatTokenAppend( shaderText, qfalse ); + } + + /* ----------------------------------------------------------------- + q3map_* directives + ----------------------------------------------------------------- */ + + /* q3map_sun + color will be normalized, so it doesn't matter what range you use + intensity falls off with angle but not distance 100 is a fairly bright sun + degree of 0 = from the east, 90 = north, etc. altitude of 0 = sunrise/set, 90 = noon */ +#if 0 + else if ( !Q_stricmp( mattoken, "vmap_sun" ) || !Q_stricmp( mattoken, "q3map_sun" ) || !Q_stricmp( mattoken, "q3map_sunExt" ) ) { + float a, b; + sun_t *sun; + qboolean ext = qfalse; + + /* ydnar: extended sun directive? */ + if ( !Q_stricmp( mattoken, "q3map_sunext" ) || !Q_stricmp( mattoken, "vmap_sun" ) ) { + ext = qtrue; + } + + /* allocate sun */ + sun = safe_malloc( sizeof( *sun ) ); + memset( sun, 0, sizeof( *sun ) ); + + /* set style */ + sun->style = si->lightStyle; + + /* get color */ + GetTokenAppend( shaderText, qfalse ); + sun->color[ 0 ] = atof( mattoken ); + GetTokenAppend( shaderText, qfalse ); + sun->color[ 1 ] = atof( mattoken ); + GetTokenAppend( shaderText, qfalse ); + sun->color[ 2 ] = atof( mattoken ); + + if ( colorsRGB ) { + sun->color[0] = Image_LinearFloatFromsRGBFloat( sun->color[0] ); + sun->color[1] = Image_LinearFloatFromsRGBFloat( sun->color[1] ); + sun->color[2] = Image_LinearFloatFromsRGBFloat( sun->color[2] ); + } + + /* normalize it */ + ColorNormalize( sun->color, sun->color ); + + /* scale color by brightness */ + GetTokenAppend( shaderText, qfalse ); + sun->photons = atof( mattoken ); + + /* get sun angle/elevation */ + GetTokenAppend( shaderText, qfalse ); + a = atof( mattoken ); + a = a / 180.0f * Q_PI; + + GetTokenAppend( shaderText, qfalse ); + b = atof( mattoken ); + b = b / 180.0f * Q_PI; + + sun->direction[ 0 ] = cos( a ) * cos( b ); + sun->direction[ 1 ] = sin( a ) * cos( b ); + sun->direction[ 2 ] = sin( b ); + + /* get filter radius from shader */ + sun->filterRadius = si->lightFilterRadius; + + /* ydnar: get sun angular deviance/samples */ + if ( ext && TokenAvailable() ) { + GetTokenAppend( shaderText, qfalse ); + sun->deviance = atof( mattoken ); + sun->deviance = sun->deviance / 180.0f * Q_PI; + + GetTokenAppend( shaderText, qfalse ); + sun->numSamples = atoi( mattoken ); + } + + /* store sun */ + sun->next = si->sun; + si->sun = sun; + + /* apply sky surfaceparm */ + ApplySurfaceParm( "sky", &si->contentFlags, &si->surfaceFlags, &si->compileFlags ); + + /* don't process any more mattokens on this line */ + continue; + } +#endif + /* match q3map_ */ + else if ( !Q_strncasecmp( mattoken, "q3map_", 6 ) || !Q_strncasecmp( mattoken, "vmap_", 5 ) ) { + /* ydnar: q3map_baseShader (inherit this shader's parameters) */ + if ( !Q_stricmp( mattoken, "q3map_baseShader" ) || !Q_stricmp( mattoken, "vmap_baseShader" ) ) { + shaderInfo_t *si2; + qboolean oldWarnImage; + + + /* get shader */ + GetMatTokenAppend( shaderText, qfalse ); + //% Sys_FPrintf( SYS_VRB, "Shader %s has base shader %s\n", si->shader, mattoken ); + oldWarnImage = warnImage; + warnImage = qfalse; + si2 = ShaderInfoForShader( mattoken ); + warnImage = oldWarnImage; + + /* subclass it */ + if ( si2 != NULL ) { + /* preserve name */ + strcpy( temp, si->shader ); + + /* copy shader */ + memcpy( si, si2, sizeof( *si ) ); + + /* restore name and set to unfinished */ + strcpy( si->shader, temp ); + si->shaderWidth = 0; + si->shaderHeight = 0; + si->finished = qfalse; + } + } + + /* ydnar: q3map_surfacemodel */ + else if ( !Q_stricmp( mattoken, "q3map_surfacemodel" ) || !Q_stricmp( mattoken, "vmap_surfacemodel" ) ) { + surfaceModel_t *model; + + /* allocate new model and attach it */ + model = safe_malloc( sizeof( *model ) ); + memset( model, 0, sizeof( *model ) ); + model->next = si->surfaceModel; + si->surfaceModel = model; + + /* get parameters */ + GetMatTokenAppend( shaderText, qfalse ); + strcpy( model->model, mattoken ); + + GetMatTokenAppend( shaderText, qfalse ); + model->density = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + model->odds = atof( mattoken ); + + GetMatTokenAppend( shaderText, qfalse ); + model->minScale = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + model->maxScale = atof( mattoken ); + + GetMatTokenAppend( shaderText, qfalse ); + model->minAngle = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + model->maxAngle = atof( mattoken ); + + GetMatTokenAppend( shaderText, qfalse ); + model->oriented = ( mattoken[ 0 ] == '1' ? qtrue : qfalse ); + } + + /* ydnar/sd: q3map_foliage */ + else if ( !Q_stricmp( mattoken, "q3map_foliage" ) || !Q_stricmp( mattoken, "vmap_foliage" ) ) { + foliage_t *foliage; + + + /* allocate new foliage struct and attach it */ + foliage = safe_malloc( sizeof( *foliage ) ); + memset( foliage, 0, sizeof( *foliage ) ); + foliage->next = si->foliage; + si->foliage = foliage; + + /* get parameters */ + GetMatTokenAppend( shaderText, qfalse ); + strcpy( foliage->model, mattoken ); + + GetMatTokenAppend( shaderText, qfalse ); + foliage->scale = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + foliage->density = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + foliage->odds = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + foliage->inverseAlpha = atoi( mattoken ); + } + + /* ydnar: q3map_bounce (fraction of light to re-emit during radiosity passes) */ + else if ( !Q_stricmp( mattoken, "q3map_bounce" ) || !Q_stricmp( mattoken, "vmap_bounce" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->bounceScale = atof( mattoken ); + } +#if 0 + /* ydnar/splashdamage: q3map_skyLight */ + else if ( !Q_stricmp( mattoken, "q3map_skyLight" ) || !Q_stricmp( mattoken, "vmap_skyLight" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->skyLightValue = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->skyLightIterations = atoi( mattoken ); + + /* clamp */ + if ( si->skyLightValue < 0.0f ) { + si->skyLightValue = 0.0f; + } + if ( si->skyLightIterations < 2 ) { + si->skyLightIterations = 2; + } + } +#endif + /* q3map_surfacelight */ + else if ( !Q_stricmp( mattoken, "q3map_surfacelight" ) || !Q_stricmp( mattoken, "vmap_surfacelight" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->value = atof( mattoken ); + } + + /* q3map_lightStyle (sof2/jk2 lightstyle) */ + else if ( !Q_stricmp( mattoken, "q3map_lightStyle" ) || !Q_stricmp( mattoken, "vmap_lightStyle" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + val = atoi( mattoken ); + if ( val < 0 ) { + val = 0; + } + else if ( val > LS_NONE ) { + val = LS_NONE; + } + si->lightStyle = val; + } + + /* wolf: q3map_lightRGB */ + else if ( !Q_stricmp( mattoken, "q3map_lightRGB" ) || !Q_stricmp( mattoken, "vmap_lightRGB" )) { + VectorClear( si->color ); + GetMatTokenAppend( shaderText, qfalse ); + si->color[ 0 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->color[ 1 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->color[ 2 ] = atof( mattoken ); + if ( colorsRGB ) { + si->color[0] = Image_LinearFloatFromsRGBFloat( si->color[0] ); + si->color[1] = Image_LinearFloatFromsRGBFloat( si->color[1] ); + si->color[2] = Image_LinearFloatFromsRGBFloat( si->color[2] ); + } + ColorNormalize( si->color, si->color ); + } + + /* q3map_lightSubdivide */ + else if ( !Q_stricmp( mattoken, "q3map_lightSubdivide" ) || !Q_stricmp( mattoken, "vmap_lightSubdivide" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->lightSubdivide = atoi( mattoken ); + } + + /* q3map_backsplash */ + else if ( !Q_stricmp( mattoken, "q3map_backsplash" ) || !Q_stricmp( mattoken, "vmap_backsplash" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->backsplashFraction = atof( mattoken ) * 0.01f; + GetMatTokenAppend( shaderText, qfalse ); + si->backsplashDistance = atof( mattoken ); + } +#if 0 + /* q3map_floodLight */ + else if ( !Q_stricmp( mattoken, "q3map_floodLight" ) || !Q_stricmp( mattoken, "vmap_floodLight" ) ) { + /* get color */ + GetMatTokenAppend( shaderText, qfalse ); + si->floodlightRGB[ 0 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->floodlightRGB[ 1 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->floodlightRGB[ 2 ] = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->floodlightDistance = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->floodlightIntensity = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->floodlightDirectionScale = atof( mattoken ); + if ( colorsRGB ) { + si->floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( si->floodlightRGB[0] ); + si->floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( si->floodlightRGB[1] ); + si->floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( si->floodlightRGB[2] ); + } + ColorNormalize( si->floodlightRGB, si->floodlightRGB ); + } +#endif + /* jal: q3map_nodirty : skip dirty */ + else if ( !Q_stricmp( mattoken, "q3map_nodirty" ) || !Q_stricmp( mattoken, "vmap_nodirty" ) ) { + si->noDirty = qtrue; + } + + /* q3map_lightmapSampleSize */ + else if ( !Q_stricmp( mattoken, "q3map_lightmapSampleSize" ) || !Q_stricmp( mattoken, "vmap_lightmapSampleSize" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->lightmapSampleSize = atoi( mattoken ); + } + + /* q3map_lightmapSampleOffset */ + else if ( !Q_stricmp( mattoken, "q3map_lightmapSampleOffset" ) || !Q_stricmp( mattoken, "vmap_lightmapSampleOffset" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->lightmapSampleOffset = atof( mattoken ); + } + + /* ydnar: q3map_lightmapFilterRadius */ + else if ( !Q_stricmp( mattoken, "q3map_lightmapFilterRadius" ) || !Q_stricmp( mattoken, "vmap_lightmapFilterRadius" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->lmFilterRadius = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->lightFilterRadius = atof( mattoken ); + } + + /* ydnar: q3map_lightmapAxis [xyz] */ + else if ( !Q_stricmp( mattoken, "q3map_lightmapAxis" ) || !Q_stricmp( mattoken, "vmap_lightmapAxis" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( !Q_stricmp( mattoken, "x" ) ) { + VectorSet( si->lightmapAxis, 1, 0, 0 ); + } + else if ( !Q_stricmp( mattoken, "y" ) ) { + VectorSet( si->lightmapAxis, 0, 1, 0 ); + } + else if ( !Q_stricmp( mattoken, "z" ) ) { + VectorSet( si->lightmapAxis, 0, 0, 1 ); + } + else + { + Sys_FPrintf( SYS_WRN, "WARNING: Unknown value for lightmap axis: %s\n", mattoken ); + VectorClear( si->lightmapAxis ); + } + } + + /* ydnar: q3map_lightmapSize (for autogenerated shaders + external tga lightmaps) */ + else if ( !Q_stricmp( mattoken, "q3map_lightmapSize" ) || !Q_stricmp( mattoken, "vmap_lightmapSize" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->lmCustomWidth = atoi( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->lmCustomHeight = atoi( mattoken ); + + /* must be a power of 2 */ + if ( ( ( si->lmCustomWidth - 1 ) & si->lmCustomWidth ) || + ( ( si->lmCustomHeight - 1 ) & si->lmCustomHeight ) ) { + Sys_FPrintf( SYS_WRN, "WARNING: Non power-of-two lightmap size specified (%d, %d)\n", + si->lmCustomWidth, si->lmCustomHeight ); + si->lmCustomWidth = lmCustomSize; + si->lmCustomHeight = lmCustomSize; + } + } + + /* ydnar: q3map_lightmapBrightness N (for autogenerated shaders + external tga lightmaps) */ + else if ( !Q_stricmp( mattoken, "q3map_lightmapBrightness" ) || !Q_stricmp( mattoken, "q3map_lightmapGamma" ) || !Q_stricmp( mattoken, "vmap_lightmapBrightness" ) || !Q_stricmp( mattoken, "vmap_lightmapGamma" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->lmBrightness = atof( mattoken ); + if ( si->lmBrightness < 0 ) { + si->lmBrightness = 1.0; + } + } + + /* q3map_vertexScale (scale vertex lighting by this fraction) */ + else if ( !Q_stricmp( mattoken, "q3map_vertexScale" ) || !Q_stricmp( mattoken, "vmap_vertexScale" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->vertexScale = atof( mattoken ); + } + + /* q3map_noVertexLight */ + else if ( !Q_stricmp( mattoken, "q3map_noVertexLight" ) || !Q_stricmp( mattoken, "vmap_noVertexLight" ) ) { + si->noVertexLight = qtrue; + } + + /* q3map_backShader */ + else if ( !Q_stricmp( mattoken, "q3map_backShader" ) || !Q_stricmp( mattoken, "vmap_backShader" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] != '\0' ) { + si->backShader = safe_malloc( strlen( mattoken ) + 1 ); + strcpy( si->backShader, mattoken ); + } + } + + /* ydnar: q3map_cloneShader */ + else if ( !Q_stricmp( mattoken, "q3map_cloneShader" ) || !Q_stricmp( mattoken, "vmap_cloneShader" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] != '\0' ) { + si->cloneShader = safe_malloc( strlen( mattoken ) + 1 ); + strcpy( si->cloneShader, mattoken ); + } + } + + /* q3map_remapShader */ + else if ( !Q_stricmp( mattoken, "q3map_remapShader" ) || !Q_stricmp( mattoken, "vmap_remapShader" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] != '\0' ) { + si->remapShader = safe_malloc( strlen( mattoken ) + 1 ); + strcpy( si->remapShader, mattoken ); + } + } + + /* q3map_deprecateShader */ + else if ( !Q_stricmp( mattoken, "q3map_deprecateShader" ) || !Q_stricmp( mattoken, "vmap_deprecateShader" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + if ( mattoken[ 0 ] != '\0' ) { + + si->deprecateShader = safe_malloc( strlen( mattoken ) + 1 ); + strcpy( si->deprecateShader, mattoken ); + } + } + + /* ydnar: q3map_offset */ + else if ( !Q_stricmp( mattoken, "q3map_offset" ) || !Q_stricmp( mattoken, "vmap_offset" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->offset = atof( mattoken ); + } + + /* ydnar: q3map_fur */ + else if ( !Q_stricmp( mattoken, "q3map_fur" ) || !Q_stricmp( mattoken, "vmap_fur" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->furNumLayers = atoi( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->furOffset = atof( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->furFade = atof( mattoken ); + } + + /* ydnar: gs mods: legacy support for terrain/terrain2 shaders */ + else if ( !Q_stricmp( mattoken, "q3map_terrain" ) || !Q_stricmp( mattoken, "vmap_terrain" ) ) { + /* team arena terrain is assumed to be nonplanar, with full normal averaging, + passed through the metatriangle surface pipeline, with a lightmap axis on z */ + si->legacyTerrain = qtrue; + si->noClip = qtrue; + si->notjunc = qtrue; + si->indexed = qtrue; + si->nonplanar = qtrue; + si->forceMeta = qtrue; + si->shadeAngleDegrees = 179.0f; + //% VectorSet( si->lightmapAxis, 0, 0, 1 ); /* ydnar 2002-09-21: turning this off for better lightmapping of cliff faces */ + } + + /* ydnar: picomodel: q3map_forceMeta (forces brush faces and/or triangle models to go through the metasurface pipeline) */ + else if ( !Q_stricmp( mattoken, "q3map_forceMeta" ) || !Q_stricmp( mattoken, "vmap_forceMeta" ) ) { + si->forceMeta = qtrue; + } + + /* ydnar: gs mods: q3map_shadeAngle */ + else if ( !Q_stricmp( mattoken, "q3map_shadeAngle" ) || !Q_stricmp( mattoken, "vmap_shadeAngle" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->shadeAngleDegrees = atof( mattoken ); + } + + /* ydnar: q3map_textureSize (substitute for q3map_lightimage derivation for terrain) */ + else if ( !Q_stricmp( mattoken, "q3map_textureSize" ) || !Q_stricmp( mattoken, "vmap_textureSize" ) ) { + GetMatTokenAppend( shaderText, qfalse ); + si->shaderWidth = atoi( mattoken ); + GetMatTokenAppend( shaderText, qfalse ); + si->shaderHeight = atoi( mattoken ); + } + + /* ydnar: gs mods: q3map_tcGen