18 Aug 2009

This commit is contained in:
g-cont 2009-08-18 00:00:00 +04:00 committed by Alibek Omarov
parent 623d6c2db1
commit 65fc6c3631
105 changed files with 41245 additions and 11609 deletions

View File

@ -10,7 +10,6 @@ backup.lst
backup.bat
release.bat
launchers.bat
history.log
todo.log
baserc\

View File

@ -296,7 +296,7 @@ void CL_TeleportParticles( const Vector org )
void CL_PlaceDecal( Vector pos, Vector dir, float scale, HSPRITE hDecal )
{
float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
int flags = DECAL_FADEALPHA;
int flags = 0;
g_engfuncs.pEfxAPI->R_SetDecal( pos, dir, rgba, RANDOM_LONG( 0, 360 ), scale, hDecal, flags );
}
@ -329,14 +329,14 @@ void HUD_ParseTempEntity( void )
READ_SHORT(); // FIXME: skip entindex
g_engfuncs.pEfxAPI->CL_FindExplosionPlane( pos, 10, dir );
CL_BulletParticles( pos, Vector( 0, 0, -1 ));
CL_PlaceDecal( pos, dir, 10, g_engfuncs.pEfxAPI->CL_DecalIndex( READ_BYTE() ));
CL_PlaceDecal( pos, dir, 2, g_engfuncs.pEfxAPI->CL_DecalIndex( READ_BYTE() ));
break;
case TE_DECAL:
pos.x = READ_COORD();
pos.y = READ_COORD();
pos.z = READ_COORD();
g_engfuncs.pEfxAPI->CL_FindExplosionPlane( pos, 10, dir );
CL_PlaceDecal( pos, dir, 10, g_engfuncs.pEfxAPI->CL_DecalIndex( READ_BYTE() ));
CL_PlaceDecal( pos, dir, 2, g_engfuncs.pEfxAPI->CL_DecalIndex( READ_BYTE() ));
READ_SHORT(); // FIXME: skip entindex
break;
case TE_EXPLOSION:

View File

@ -409,7 +409,8 @@ void CL_SpawnDecal( vec3_t org, vec3_t dir, float rot, float rad, float *col, fl
poly->numverts = fr->numverts;
poly->fognum = fr->fognum;
for( j = 0; j < fr->numverts; j++ ) {
for( j = 0; j < fr->numverts; j++ )
{
vec3_t v;
VectorCopy( verts[fr->firstvert+j], poly->verts[j] );

View File

@ -1,16 +0,0 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: engine - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
<h3>Results</h3>
engine.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

View File

@ -1256,8 +1256,7 @@ void SV_AirMove( sv_client_t *cl, usercmd_t *cmd )
if((int)cl->edict->v.movetype != MOVETYPE_WALK )
wishvel[2] += cmd->upmove;
VectorCopy( wishvel, wishdir );
wishspeed = VectorNormalizeLength( wishdir );
wishspeed = VectorNormalizeLength2( wishvel, wishdir );
if( wishspeed > sv_maxspeed->value )
{
temp = sv_maxspeed->value / wishspeed;

View File

@ -544,6 +544,7 @@ void SV_InitOperatorCommands( void )
Cmd_AddCommand( "clientinfo", SV_ClientInfo_f, "print user infostring (player num required)" );
Cmd_AddCommand( "map", SV_Map_f, "start new level" );
Cmd_AddCommand( "devmap", SV_Map_f, "start new level" );
Cmd_AddCommand( "newgame", SV_Newgame_f, "begin new game" );
Cmd_AddCommand( "changelevel", SV_ChangeLevel_f, "changing level" );
Cmd_AddCommand( "restart", SV_Restart_f, "restarting current level" );

View File

View File

@ -293,17 +293,10 @@ void Cmd_SystemCfg_f( void )
char *f;
size_t len;
// immitate loading normal PE-file
MsgDev(D_NOTE, "Sys_LoadLibrary: Loading %s", "config.dll" );
f = FS_LoadFile( "config.dll", &len );
if( !f )
{
MsgDev(D_NOTE, " - failed\n");
return;
}
MsgDev(D_NOTE, " - ok\n");
Cbuf_InsertText(f);
Mem_Free(f);
f = FS_LoadFile( "config.rc", &len );
if( !f ) return;
Cbuf_InsertText( f );
Mem_Free( f );
}
/*

View File

@ -1280,6 +1280,7 @@ FS_Rescan
*/
void FS_Rescan( void )
{
MsgDev( D_NOTE, "FS_Rescan( %s )\n", GI.title );
FS_ClearSearchPath();
FS_AddGameHierarchy( GI.basedir );
@ -1291,6 +1292,53 @@ void FS_Rescan_f( void )
FS_Rescan();
}
void FS_CreatePathesList( void )
{
file_t *f;
// make simple pathes.rc
f = FS_Open( "pathes.rc", "w" );
if( !f ) return; // not fatal for us
FS_Printf (f, "//=======================================================================\n");
FS_Printf (f, "//\t\t\tCopyright XashXT Group %s ©\n", com.timestamp( TIME_YEAR_ONLY ));
FS_Printf (f, "//\t\t pathes.rc - executable associations\n");
FS_Printf (f, "//=======================================================================\n");
FS_Printf (f, "\n");
FS_Printf (f, "// executable name\t\tlinked basedir\n");
FS_Printf (f, "quake.exe\t\t\tid1\n" );
FS_Printf (f, "quake2.exe\t\tbaseq2\n" );
FS_Printf (f, "quake3.exe\t\tbaseq3\n" );
FS_Printf (f, "hl.exe\t\t\tvalve\n" );
FS_Close (f);
}
void FS_ApplyBaseDir( void )
{
script_t *script;
token_t tok;
if(!FS_FileExists( "pathes.rc" )) FS_CreatePathesList();
script = PS_LoadScript( "pathes.rc", NULL, 0 );
if( !script ) return; // use standard methods
while( PS_ReadToken( script, SC_ALLOW_NEWLINES|SC_ALLOW_PATHNAMES, &tok ))
{
FS_StripExtension( tok.string ); // cutoff .exe if present
if( !com.stricmp( tok.string, gs_basedir ))
{
if( PS_ReadToken( script, SC_ALLOW_PATHNAMES, &tok ))
{
// install new basedir
com.strncpy( gs_basedir, tok.string, sizeof( gs_basedir ));
break;
}
else MsgDev( D_ERROR, "missing associated directory with %s.exe", gs_basedir );
}
}
PS_FreeScript( script );
}
void FS_ResetGameInfo( void )
{
com.strcpy( GI.title, gs_basedir );
@ -1317,7 +1365,7 @@ void FS_CreateGameInfo( const char *filename )
com.strncat( buffer, "\nsourcedir\t\t\"source\"", MAX_SYSPATH );
com.strncat( buffer, "\nsp_spawn\t\t\"info_player_start\"", MAX_SYSPATH );
com.strncat( buffer, "\ndm_spawn\t\t\"info_player_deathmatch\"", MAX_SYSPATH );
com.strncat( buffer, "\nteam_spawn\t\t\"info_player_coop\"", MAX_SYSPATH );
com.strncat( buffer, "\nteam_spawn\t\"info_player_coop\"", MAX_SYSPATH );
FS_WriteFile( filename, buffer, com.strlen( buffer ));
Mem_Free( buffer );
@ -1333,6 +1381,9 @@ void FS_LoadGameInfo( const char *filename )
// lock uplevel of gamedir for read\write
fs_ext_path = false;
Com_Assert( filename == NULL );
MsgDev( D_NOTE, "FS_LoadGameInfo: [%s]\n", filename );
// prepare to loading
FS_ClearSearchPath();
FS_AddGameHierarchy( gs_basedir );
@ -1455,6 +1506,8 @@ void FS_Init( void )
com.strcpy( gs_basedir, fs_defaultdir->string );
else if( Sys_GetModuleName( gs_basedir, MAX_SYSPATH ));
else com.strcpy( gs_basedir, fs_defaultdir->string ); // default dir
FS_ApplyBaseDir(); // check for associated folders
}
// checked nasty path: "bin" it's a reserved word
if( FS_CheckNastyPath( gs_basedir, true ) || !com.stricmp( "bin", gs_basedir ))
@ -1562,20 +1615,20 @@ void FS_UpdateConfig( void )
file_t *f;
if( Sys.app_state == SYS_ERROR ) return;
// only normal and bsplib instance can change config.dll
// only normal and bsplib instance can change config.rc
if( Sys.app_name != HOST_NORMAL && Sys.app_name != HOST_BSPLIB ) return;
com.strncpy( fs_gamedir, "bin", sizeof( fs_gamedir )); // set write directory for system config
f = FS_Open( "config.dll", "w" );
f = FS_Open( "config.rc", "w" );
if( f )
{
FS_Printf (f, "//=======================================================================\n");
FS_Printf (f, "//\t\t\tCopyright XashXT Group %s ©\n", com.timestamp( TIME_YEAR_ONLY ));
FS_Printf (f, "//\t\t system.rc - archive of system cvars\n");
FS_Printf (f, "//\t\t config.rc - archive of system cvars\n");
FS_Printf (f, "//=======================================================================\n");
FS_WriteVariables( f );
FS_Close (f);
}
else MsgDev( D_NOTE, "can't update config.dll.\n" );
else MsgDev( D_ERROR, "can't update config.rc.\n" );
}
/*

View File

@ -577,6 +577,14 @@ bool Image_SaveJPG( const char *name, rgbdata_t *pix )
if( FS_FileExists( name ) && !(image.cmd_flags & IL_ALLOW_OVERWRITE ))
return false; // already existed
if( pix->flags & IMAGE_HAS_ALPHA )
{
// save alpha if any
FS_StripExtension( (char *)name );
FS_DefaultExtension( (char *)name, ".png" );
return FS_SaveImage( name, pix );
}
// Open the file
file = FS_Open( name, "wb" );
if( !file ) return false;

View File

@ -19,7 +19,7 @@
#define LAUNCH_DLL // ignore alias names
#include "launch_api.h"
#define XASH_VERSION 0.56f // current version will be shared across gameinfo struct
#define XASH_VERSION 0.61f // current version will be shared across gameinfo struct
#define MAX_NUM_ARGVS 128
#define MAX_STRING_TOKENS 80

View File

@ -1,64 +0,0 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: launch - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP1F9D.tmp" with contents
[
/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "imagelib" /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\launch\!debug/" /Fo"..\temp\launch\!debug/" /Fd"..\temp\launch\!debug/" /FD /GZ /c
"D:\Xash3D\src_main\launch\filesystem.c"
]
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP1F9D.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP1F9E.tmp" with contents
[
zlib.lib png.lib jpg.lib user32.lib gdi32.lib advapi32.lib winmm.lib /nologo /dll /incremental:yes /pdb:"..\temp\launch\!debug/launch.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /out:"..\temp\launch\!debug/launch.dll" /implib:"..\temp\launch\!debug/launch.lib" /pdbtype:sept /libpath:"./imagelib"
"\Xash3D\src_main\temp\launch\!debug\cmd.obj"
"\Xash3D\src_main\temp\launch\!debug\console.obj"
"\Xash3D\src_main\temp\launch\!debug\cpuinfo.obj"
"\Xash3D\src_main\temp\launch\!debug\crclib.obj"
"\Xash3D\src_main\temp\launch\!debug\cvar.obj"
"\Xash3D\src_main\temp\launch\!debug\export.obj"
"\Xash3D\src_main\temp\launch\!debug\filesystem.obj"
"\Xash3D\src_main\temp\launch\!debug\img_bmp.obj"
"\Xash3D\src_main\temp\launch\!debug\img_dds.obj"
"\Xash3D\src_main\temp\launch\!debug\img_jpg.obj"
"\Xash3D\src_main\temp\launch\!debug\img_main.obj"
"\Xash3D\src_main\temp\launch\!debug\img_pcx.obj"
"\Xash3D\src_main\temp\launch\!debug\img_png.obj"
"\Xash3D\src_main\temp\launch\!debug\img_tga.obj"
"\Xash3D\src_main\temp\launch\!debug\img_utils.obj"
"\Xash3D\src_main\temp\launch\!debug\img_vtf.obj"
"\Xash3D\src_main\temp\launch\!debug\img_wad.obj"
"\Xash3D\src_main\temp\launch\!debug\memlib.obj"
"\Xash3D\src_main\temp\launch\!debug\network.obj"
"\Xash3D\src_main\temp\launch\!debug\parselib.obj"
"\Xash3D\src_main\temp\launch\!debug\patch.obj"
"\Xash3D\src_main\temp\launch\!debug\stdlib.obj"
"\Xash3D\src_main\temp\launch\!debug\system.obj"
"\Xash3D\src_main\temp\launch\!debug\utils.obj"
]
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP1F9E.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP1F9F.bat" with contents
[
@echo off
copy \Xash3D\src_main\temp\launch\!debug\launch.dll "D:\Xash3D\bin\launch.dll"
]
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP1F9F.bat"
Compiling...
filesystem.c
Linking...
Creating library ..\temp\launch\!debug/launch.lib and object ..\temp\launch\!debug/launch.exp
<h3>Output Window</h3>
Performing Custom Build Step on \Xash3D\src_main\temp\launch\!debug\launch.dll
‘Ş®Ż¨ŕ®˘ ­® ä ©«®˘: 1.
<h3>Results</h3>
launch.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

View File

@ -942,6 +942,9 @@ bool PS_ReadToken( script_t *script, scFlags_t flags, token_t *token )
{
token_t dummy;
// parselib requries token always
if( !token ) token = &dummy;
// if there is a token available (from PS_FreeToken)
if( script->tokenAvailable )
{
@ -950,9 +953,6 @@ bool PS_ReadToken( script_t *script, scFlags_t flags, token_t *token )
return true;
}
// parselib requries token always
if( !token ) token = &dummy;
// clear token
token->type = TT_EMPTY;
token->subType = 0;

View File

@ -1666,7 +1666,7 @@ bool CM_SpriteModel( byte *buffer, uint filesize )
bool CM_BrushModel( byte *buffer, uint filesize )
{
MsgDev( D_WARN, "CM_BrushModel: not implemented\n" );
Host_Error( "CM_BrushModel: not implemented\n" );
return false;
}
@ -1677,10 +1677,10 @@ cmodel_t *CM_RegisterModel( const char *name )
cmodel_t *mod;
if( !name[0] ) return NULL;
if(name[0] == '*')
if( name[0] == '*' )
{
i = com.atoi( name + 1);
if( i < 1 || !cms.loaded || i >= cms.numbmodels)
i = com.atoi( name + 1 );
if( i < 1 || !cms.loaded || i >= cms.numbmodels )
{
MsgDev(D_WARN, "CM_InlineModel: bad submodel number %d\n", i );
return NULL;
@ -1689,11 +1689,16 @@ cmodel_t *CM_RegisterModel( const char *name )
cms.bmodels[i].registration_sequence = registration_sequence;
return &cms.bmodels[i];
}
// FIXME: use registration_sequence for worldmodel
if( !com.strcmp( name, cm.name ))
return &cms.bmodels[0];
for( i = 0; i < cms.numcmodels; i++ )
{
mod = &cms.cmodels[i];
if(!mod->name[0]) continue;
if(!com.strcmp( name, mod->name ))
if( !mod->name[0] ) continue;
if( !com.strcmp( name, mod->name ))
{
// prolonge registration
mod->registration_sequence = registration_sequence;
@ -1703,9 +1708,7 @@ cmodel_t *CM_RegisterModel( const char *name )
// find a free model slot spot
for( i = 0, mod = cms.cmodels; i < cms.numcmodels; i++, mod++)
{
if( !mod->name[0] ) break; // free spot
}
if( i == cms.numcmodels )
{
if( cms.numcmodels == MAX_MODELS )
@ -1716,7 +1719,7 @@ cmodel_t *CM_RegisterModel( const char *name )
cms.numcmodels++;
}
com.strncpy( mod->name, name, sizeof(mod->name));
com.strncpy( mod->name, name, sizeof( mod->name ));
buf = FS_LoadFile( name, &size );
if( !buf )
{
@ -1726,7 +1729,7 @@ cmodel_t *CM_RegisterModel( const char *name )
}
MsgDev( D_NOTE, "CM_LoadModel: load %s\n", name );
MsgDev( D_NOTE, "CM_LoadModel: %s\n", name );
mod->mempool = Mem_AllocPool( va("^2%s^7", mod->name ));
loadmodel = mod;
@ -1740,7 +1743,7 @@ cmodel_t *CM_RegisterModel( const char *name )
CM_SpriteModel( buf, size );
break;
case IDBSPMODHEADER:
CM_BrushModel( buf, size );//FIXME
CM_BrushModel( buf, size );
break;
}
Mem_Free( buf );

View File

@ -1,53 +0,0 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: physic - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20DC.tmp" with contents
[
/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\physic\!debug/" /Fo"..\temp\physic\!debug/" /Fd"..\temp\physic\!debug/" /FD /GZ /c
"D:\Xash3D\src_main\physic\cm_model.c"
]
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20DC.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20DD.tmp" with contents
[
user32.lib msvcrtd.lib newton.lib opengl32.lib /nologo /dll /incremental:yes /pdb:"..\temp\physic\!debug/physic.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /out:"..\temp\physic\!debug/physic.dll" /implib:"..\temp\physic\!debug/physic.lib" /pdbtype:sept
"\Xash3D\src_main\temp\physic\!debug\cm_callback.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_collision.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_debug.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_materials.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_model.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_pmove.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_polygon.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_portals.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_rigidbody.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_test.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_trace.obj"
"\Xash3D\src_main\temp\physic\!debug\cm_utils.obj"
"\Xash3D\src_main\temp\physic\!debug\physic.obj"
]
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20DD.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20DE.bat" with contents
[
@echo off
copy \Xash3D\src_main\temp\physic\!debug\physic.dll "D:\Xash3D\bin\physic.dll"
]
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20DE.bat"
Compiling...
cm_model.c
Linking...
Creating library ..\temp\physic\!debug/physic.lib and object ..\temp\physic\!debug/physic.exp
<h3>Output Window</h3>
Performing Custom Build Step on \Xash3D\src_main\temp\physic\!debug\physic.dll
‘ª®¯¨à®¢ ­® ä ©«®¢: 1.
<h3>Results</h3>
physic.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

View File

@ -158,6 +158,22 @@ _inline void CategorizePlane( cplane_t *plane )
}
}
/*
=================
PlaneTypeForNormal
=================
*/
_inline int PlaneTypeForNormal( const vec3_t normal )
{
if( normal[0] == 1.0 || normal[0] == -1.0 )
return PLANE_X;
if( normal[1] == 1.0 || normal[1] == -1.0 )
return PLANE_Y;
if( normal[2] == 1.0 || normal[2] == -1.0 )
return PLANE_Z;
return PLANE_NONAXIAL;
}
/*
==============
BoxOnPlaneSide (engine fast version)

View File

@ -87,6 +87,7 @@ typedef void ( *cmdraw_t )( int color, int numpoints, const float *points, const
typedef void ( *setpair_t )( const char *key, const char *value, void *buffer, void *numpairs );
typedef enum { mod_bad, mod_world, mod_brush, mod_alias, mod_studio, mod_sprite } modtype_t;
typedef enum { NA_LOOPBACK, NA_BROADCAST, NA_IP } netadrtype_t;
typedef enum { eXYZ, eYZX, eZXY, eXZY, eYXZ, eZYX } euler_t;
typedef enum { NS_CLIENT, NS_SERVER } netsrc_t;
typedef void ( *xcommand_t )( void );
@ -114,7 +115,7 @@ typedef enum
CVAR_ARCHIVE = BIT(0), // set to cause it to be saved to vars.rc
CVAR_USERINFO = BIT(1), // added to userinfo when changed
CVAR_SERVERINFO = BIT(2), // added to serverinfo when changed
CVAR_SYSTEMINFO = BIT(3), // don't changed from console, saved into config.dll
CVAR_SYSTEMINFO = BIT(3), // don't changed from console, saved into config.rc
CVAR_INIT = BIT(4), // don't allow change from console at all, but can be set from the command line
CVAR_LATCH = BIT(5), // save changes until server restart
CVAR_READ_ONLY = BIT(6), // display only, cannot be set by user at all
@ -640,6 +641,9 @@ typedef struct stdilib_api_s
typedef void *(*launch_t)( stdlib_api_t*, void* );
typedef struct { size_t api_size; } generic_api_t;
// moved here to enable assertation feature in launch.dll
#define Com_Assert( x ) if( x ) com.abort( "assert failed at %s:%i\n", __FILE__, __LINE__ );
#ifndef LAUNCH_DLL
/*
==============================================================================
@ -897,7 +901,6 @@ misc utils
#define StringTable_GetName com.st_getname
#define StringTable_Load com.st_load
#define StringTable_Save com.st_save
#define Com_Assert( x ) if( x ) com.abort( "assert failed at %s:%i\n", __FILE__, __LINE__ );
/*
===========================================

View File

@ -29,6 +29,7 @@
#define STUDIO_TO_RAD (M_PI / 32768.0)
#define nanmask (255<<23)
#define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f)))
#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
#define RANDOM_LONG(MIN, MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
#define RANDOM_FLOAT(MIN,MAX) (((float)rand() / RAND_MAX) * ((MAX)-(MIN)) + (MIN))
@ -188,23 +189,25 @@ _inline void VectorBound(const float min, vec3_t v, const float max)
}
// FIXME: convert to #define
_inline float VectorNormalizeLength( vec3_t v )
_inline float VectorNormalizeLength2( const vec3_t v, vec3_t out )
{
float length, ilength;
float length, ilength;
length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
length = sqrt (length);
length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
length = rsqrt( length );
if( length )
{
ilength = 1/length;
v[0] *= ilength;
v[1] *= ilength;
v[2] *= ilength;
ilength = 1.0f / length;
out[0] = v[0] * ilength;
out[1] = v[1] * ilength;
out[2] = v[2] * ilength;
}
return length;
else out[0] = out[1] = out[2] = 0.0f;
return length;
}
#define VectorNormalizeLength( v ) VectorNormalizeLength2((v), (v))
_inline bool VectorIsNull( const vec3_t v )
{
@ -550,27 +553,6 @@ _inline float *UnpackRGBA( dword icolor )
return color;
}
/*
===============
ColorNormalize
===============
*/
_inline float ColorNormalize( const float *in, vec3_t out )
{
float f = max( max( in[0], in[1] ), in[2] );
if( f > 1.0f )
{
f = 1.0f / f;
out[0] = in[0] * f;
out[1] = in[1] * f;
out[2] = in[2] * f;
}
else VectorCopy( in, out );
return f;
}
/*
=================
BoundsIntersect

View File

@ -321,19 +321,6 @@ _inline void Matrix4x4_TransformPoint( const matrix4x4 in, vec3_t point )
{
float out1, out2, out3;
#ifdef OPENGL_STYLE
out1 = in[0][0] * point[0];
out2 = in[0][1] * point[0];
out3 = in[0][2] * point[0];
out1 += in[1][0] * point[1];
out2 += in[1][1] * point[1];
out3 += in[1][2] * point[1];
out1 += in[2][0] * point[2];
out2 += in[2][1] * point[2];
out3 += in[2][2] * point[2];
out1 += in[3][0];
out2 += in[3][1];
out3 += in[3][2];
#else
out1 = in[0][0] * point[0];
out2 = in[1][0] * point[0];
out3 = in[2][0] * point[0];
@ -346,12 +333,54 @@ _inline void Matrix4x4_TransformPoint( const matrix4x4 in, vec3_t point )
out1 += in[0][3];
out2 += in[1][3];
out3 += in[2][3];
#else
out1 = in[0][0] * point[0];
out2 = in[0][1] * point[0];
out3 = in[0][2] * point[0];
out1 += in[1][0] * point[1];
out2 += in[1][1] * point[1];
out3 += in[1][2] * point[1];
out1 += in[2][0] * point[2];
out2 += in[2][1] * point[2];
out3 += in[2][2] * point[2];
out1 += in[3][0];
out2 += in[3][1];
out3 += in[3][2];
#endif
point[0] = out1;
point[1] = out2;
point[2] = out3;
}
_inline void Matrix4x4_TransformNormal( const matrix4x4 matrix, vec3_t normal )
{
float out1, out2, out3;
#ifdef OPENGL_STYLE
out1 = matrix[0][0] * normal[0];
out2 = matrix[1][0] * normal[0];
out3 = matrix[2][0] * normal[0];
out1 += matrix[0][1] * normal[1];
out2 += matrix[1][1] * normal[1];
out3 += matrix[2][1] * normal[1];
out1 += matrix[0][2] * normal[2];
out2 += matrix[1][2] * normal[2];
out3 += matrix[2][2] * normal[2];
#else
out1 = matrix[0][0] * normal[0];
out2 = matrix[0][1] * normal[0];
out3 = matrix[0][2] * normal[0];
out1 += matrix[1][0] * normal[1];
out2 += matrix[1][1] * normal[1];
out3 += matrix[1][2] * normal[1];
out1 += matrix[2][0] * normal[2];
out2 += matrix[2][1] * normal[2];
out3 += matrix[2][2] * normal[2];
#endif
normal[0] = out1;
normal[1] = out2;
normal[2] = out3;
}
_inline void Matrix4x4_Transform3x3( const matrix4x4 in, const float v[3], float out[3] )
{
#ifdef OPENGL_STYLE
@ -1659,6 +1688,188 @@ _inline void Matrix4x4_Stretch2D( matrix4x4 out, vec_t s, vec_t t )
#endif
}
_inline void Matrix4x4_FromEulerAngles( matrix4x4 out, const vec3_t euler, euler_t order )
{
double cx, sx, cy, sy, cz, sz;
matrix4x4 temp, temp2;
cx = cos( DEG2RAD( euler[0] ));
sx = sin( DEG2RAD( euler[0] ));
cy = cos( DEG2RAD( euler[1] ));
sy = sin( DEG2RAD( euler[1] ));
cz = cos( DEG2RAD( euler[2] ));
sz = sin( DEG2RAD( euler[2] ));
switch( order )
{
case eXYZ:
#ifdef OPENGL_STYLE
out[1][0] = (float)(cy*sz);
out[2][0] = (float)-sy;
out[0][1] = (float)(sx*sy*cz + cx*-sz);
out[2][1] = (float)(sx*cy);
out[0][2] = (float)(cx*sy*cz + sx*sz);
out[1][2] = (float)(cx*sy*sz + -sx*cz);
#else
out[0][1] = (float)(cy*sz);
out[0][2] = (float)-sy;
out[1][0] = (float)(sx*sy*cz + cx*-sz);
out[1][2] = (float)(sx*cy);
out[2][0] = (float)(cx*sy*cz + sx*sz);
out[2][1] = (float)(cx*sy*sz + -sx*cz);
#endif
out[0][0] = (float)(cy*cz);
out[1][1] = (float)(sx*sy*sz + cx*cz);
out[2][2] = (float)(cx*cy);
out[0][3] = out[1][3] = out[2][3] = out[3][0] = out[3][1] = out[3][2] = 0.0f;
out[3][3] = 1.0f;
break;
case eYZX:
Matrix4x4_LoadIdentity( out );
out[0][0] = out[2][2] = (float)cy;
#ifdef OPENGL_STYLE
out[2][0] = (float)-sy;
out[0][2] = (float) sy;
#else
out[0][2] = (float)-sy;
out[2][0] = (float) sy;
#endif
Matrix4x4_LoadIdentity( temp );
temp[1][1] = temp[2][2] = (float)cx;
#ifdef OPENGL_STYLE
temp[2][1] = (float) sx;
temp[1][2] = (float)-sx;
#else
temp[1][2] = (float) sx;
temp[2][1] = (float)-sx;
#endif
Matrix4x4_Concat( temp2, out, temp );
Matrix4x4_Copy( out, temp2 );
Matrix4x4_LoadIdentity( temp );
temp[0][0] = temp[1][1] = (float)cz;
#ifdef OPENGL_STYLE
temp[1][0] = (float) sz;
temp[0][1] = (float)-sz;
#else
temp[0][1] = (float) sz;
temp[1][0] = (float)-sz;
#endif
Matrix4x4_Concat( temp2, out, temp );
Matrix4x4_Copy( out, temp2 );
break;
case eZXY:
Matrix4x4_LoadIdentity( out );
out[0][0] = out[1][1] = (float)cz;
#ifdef OPENGL_STYLE
out[1][0] = (float) sz;
out[0][1] = (float)-sz;
#else
out[0][1] = (float) sz;
out[1][0] = (float)-sz;
#endif
Matrix4x4_LoadIdentity( temp );
temp[1][1] = temp[2][2] = (float) cx;
#ifdef OPENGL_STYLE
temp[2][1] = (float) sx;
temp[1][2] = (float)-sx;
#else
temp[1][2] = (float) sx;
temp[2][1] = (float)-sx;
#endif
Matrix4x4_Concat( temp2, out, temp );
Matrix4x4_Copy( out, temp2 );
Matrix4x4_LoadIdentity( temp );
temp[0][0] = temp[2][2] = (float)cy;
#ifdef OPENGL_STYLE
temp[2][0] = (float)-sy;
temp[0][2] = (float) sy;
#else
temp[0][2] = (float)-sy;
temp[2][0] = (float) sy;
#endif
Matrix4x4_Concat( temp2, out, temp );
Matrix4x4_Copy( out, temp2 );
break;
case eXZY:
Matrix4x4_LoadIdentity( out );
out[1][1] = out[2][2] = (float)cx;
#ifdef OPENGL_STYLE
out[2][1] = (float) sx;
out[1][2] = (float)-sx;
#else
out[1][2] = (float) sx;
out[2][1] = (float)-sx;
#endif
Matrix4x4_LoadIdentity( temp );
temp[0][0] = temp[1][1] = (float)cz;
#ifdef OPENGL_STYLE
temp[1][0] = (float) sz;
temp[0][1] = (float)-sz;
#else
temp[0][1] = (float) sz;
temp[1][0] = (float)-sz;
#endif
Matrix4x4_Concat( temp2, out, temp );
Matrix4x4_Copy( out, temp2 );
Matrix4x4_LoadIdentity( temp );
temp[0][0] = temp[2][2] = (float) cy;
#ifdef OPENGL_STYLE
temp[2][0] = (float)-sy;
temp[0][2] = (float) sy;
#else
temp[0][2] = (float)-sy;
temp[2][0] = (float) sy;
#endif
Matrix4x4_Concat( temp2, out, temp );
Matrix4x4_Copy( out, temp2 );
break;
case eYXZ:
out[0][0] = (float)(cy*cz + sx*sy*-sz);
out[1][1] = (float)(cx*cz);
out[2][2] = (float)(cx*cy);
#ifdef OPENGL_STYLE
out[1][0] = (float)(cy*sz + sx*sy*cz);
out[2][0] = (float)(-cx*sy);
out[0][1] = (float)(cx*-sz);
out[2][1] = (float)(sx);
out[0][2] = (float)(sy*cz + -sx*cy*-sz);
out[1][2] = (float)(sy*sz + -sx*cy*cz);
#else
out[0][1] = (float)(cy*sz + sx*sy*cz);
out[0][2] = (float)(-cx*sy);
out[1][0] = (float)(cx*-sz);
out[1][2] = (float)(sx);
out[2][0] = (float)(sy*cz + -sx*cy*-sz);
out[2][1] = (float)(sy*sz + -sx*cy*cz);
#endif
out[0][3] = out[1][3] = out[2][3] = out[3][0] = out[3][1] = out[3][2] = 0.0f;
out[3][3] = 1.0f;
break;
case eZYX:
out[0][0] = (float)(cy*cz);
out[1][1] = (float)(sx*sy*-sz + cx*cz);
out[2][2] = (float)(cx*cy);
#ifdef OPENGL_STYLE
out[1][0] = (float)(sx*sy*cz + cx*sz);
out[2][0] = (float)(cx*-sy*cz + sx*sz);
out[0][1] = (float)(cy*-sz);
out[2][1] = (float)(cx*-sy*-sz + sx*cz);
out[0][2] = (float)sy;
out[1][2] = (float)(-sx*cy);
#else
out[0][1] = (float)(sx*sy*cz + cx*sz);
out[0][2] = (float)(cx*-sy*cz + sx*sz);
out[1][0] = (float)(cy*-sz);
out[1][2] = (float)(cx*-sy*-sz + sx*cz);
out[2][0] = (float)sy;
out[2][1] = (float)(-sx*cy);
#endif
out[0][3] = out[1][3] = out[2][3] = out[3][0] = out[3][1] = out[3][2] = 0.0f;
out[3][3] = 1.0f;
break;
}
}
_inline void Matrix4x4_ConcatScale( matrix4x4 out, float x )
{
matrix4x4 base, temp;
@ -1686,6 +1897,15 @@ _inline void Matrix4x4_ConcatRotate( matrix4x4 out, float angle, float x, float
Matrix4x4_Concat( out, base, temp );
}
_inline void Matrix4x4_ConcatEulerAngles( matrix4x4 out, const vec3_t euler, euler_t order )
{
matrix4x4 base, temp;
Matrix4x4_Copy( base, out );
Matrix4x4_FromEulerAngles( temp, euler, order );
Matrix4x4_Concat( out, base, temp );
}
_inline void Matrix4x4_ConcatScale3( matrix4x4 out, float x, float y, float z )
{
matrix4x4 base, temp;
@ -1695,18 +1915,15 @@ _inline void Matrix4x4_ConcatScale3( matrix4x4 out, float x, float y, float z )
Matrix4x4_Concat( out, base, temp );
}
_inline void Matrix4x4_Pivot( matrix4x4 m, const vec3_t org, const vec3_t ang, const vec3_t scale, const vec3_t pivot )
_inline void Matrix4x4_Pivot( matrix4x4 m, const vec3_t org, const vec3_t ang, euler_t order, const vec3_t scale, const vec3_t pv )
{
vec3_t temp;
VectorAdd( pivot, org, temp );
Matrix4x4_LoadIdentity( m );
VectorAdd( pv, org, temp );
Matrix4x4_ConcatTranslate( m, temp[0], temp[1], temp[2] );
Matrix4x4_ConcatRotate( m, ang[0], 1, 0, 0 );
Matrix4x4_ConcatRotate( m, ang[1], 0, 1, 0 );
Matrix4x4_ConcatRotate( m, ang[2], 0, 0, 1 );
Matrix4x4_ConcatEulerAngles( m, ang, order );
Matrix4x4_ConcatScale3( m, scale[0], scale[1], scale[2] );
VectorNegate( pivot, temp );
VectorNegate( pv, temp );
Matrix4x4_ConcatTranslate( m, temp[0], temp[1], temp[2] );
}

View File

@ -268,9 +268,9 @@ typedef enum
SURF_ALPHASHADOW = BIT(16), // do per-pixel light shadow casting in q3map
SURF_NODLIGHT = BIT(17), // never add dynamic lights
SURF_DUST = BIT(18), // REMOVE? leave a dust trail when walking on this surface
} surfaceType_t;
} surfaceFlags_t;
enum
typedef enum
{
MST_BAD = 0,
MST_PLANAR,
@ -278,7 +278,7 @@ enum
MST_TRISURF,
MST_FLARE,
MST_FOLIAGE
};
} bspSurfaceType_t;
typedef struct
{

View File

@ -1322,7 +1322,7 @@ static void R_ShaderpassRenderMode( ref_stage_t *pass )
case mod_studio:
break;
case mod_sprite:
pass->glState = (GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE|GLSTATE_NO_DEPTH_TEST);
pass->glState = (GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA|GLSTATE_DSTBLEND_ONE|GLSTATE_NO_DEPTH_TEST);
pass->rgbGen.type = RGBGEN_IDENTITY_LIGHTING; // sprites ignore color in 'add' mode
pass->alphaGen.type = ALPHAGEN_ENTITY;
break;
@ -1578,7 +1578,7 @@ void R_ModifyColor( const ref_stage_t *pass )
{
vec4_t diffuse;
if( RI.currententity->flags & EF_FULLBRIGHT )
if( RI.currententity->flags & EF_FULLBRIGHT || !r_worldbrushmodel->lightgrid )
VectorSet( diffuse, 1, 1, 1 );
else R_LightForOrigin( RI.currententity->lightingOrigin, t, NULL, diffuse, RI.currentmodel->radius * RI.currententity->scale );
@ -1595,8 +1595,8 @@ void R_ModifyColor( const ref_stage_t *pass )
{
vec4_t ambient;
if( RI.currententity->flags & EF_FULLBRIGHT )
VectorSet( ambient, 1, 1, 1 );
if( RI.currententity->flags & EF_FULLBRIGHT || !r_worldbrushmodel->lightgrid )
VectorSet( ambient, 1.0f, 1.0f, 1.0f );
else R_LightForOrigin( RI.currententity->lightingOrigin, t, ambient, NULL, RI.currentmodel->radius * RI.currententity->scale );
rgba[0] = R_FloatToByte( ambient[0] );

View File

@ -658,11 +658,13 @@ static byte *R_BuildMipMap( const byte *in, int width, int height, bool isNormal
vec3_t normal;
int x, y;
width <<= 2;
height >>= 1;
if( isNormalMap )
{
width <<= 2;
height >>= 1;
if( width == 1 && height == 1 ) return out; // e.g. blankbump
for( y = 0; y < height; y++, in += width )
{
for( x = 0; x < width; x += 8, in += 8, out += 4 )
@ -682,6 +684,21 @@ static byte *R_BuildMipMap( const byte *in, int width, int height, bool isNormal
}
else
{
width >>= 1;
height >>= 1;
if( width == 0 || height == 0 ) // some of last iterations
{
width += height; // get largest
for( x = 0; x < width; x++, in += 8, out += 4 )
{
out[0] = ( in[0] + in[4] )>>1;
out[1] = ( in[1] + in[5] )>>1;
out[2] = ( in[2] + in[6] )>>1;
out[3] = ( in[3] + in[7] )>>1;
}
return out;
}
for( y = 0; y < height; y++, in += width )
{
for( x = 0; x < width; x += 8, in += 8, out += 4 )
@ -1544,10 +1561,17 @@ static rgbdata_t *R_IncludeDepthmap( rgbdata_t *in1, rgbdata_t *in2 )
pic1 = in1->buffer;
pic2 = in2->buffer;
for( i = in1->width * in1->height - 1; i > 0; i--, pic1 += 4, pic2 += 4 )
pic1[3] = ((int)pic2[0] + (int)pic2[1] + (int)pic2[2]) / 3;
for( i = (in1->width * in1->height) - 1; i > 0; i--, pic1 += 4, pic2 += 4 )
{
if( in2->flags & IMAGE_HAS_COLOR )
pic1[3] = ((int)pic2[0] + (int)pic2[1] + (int)pic2[2]) / 3;
else if( in2->flags & IMAGE_HAS_ALPHA )
pic1[3] = pic2[3];
else pic1[3] = pic2[0];
}
FS_FreeImage( in2 );
in1->flags |= (IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA);
return in1;
}
@ -2595,8 +2619,6 @@ static rgbdata_t *R_ParseDepthmap( script_t *script, int *samples, texFlags_t *f
*samples = 4;
*flags &= ~TF_INTENSITY;
*flags |= TF_DEPTHMAP;
*flags |= TF_ALPHA; // store depthmap in alpha-channel
return R_IncludeDepthmap( pic1, pic2 );
}

View File

@ -299,21 +299,20 @@ R_LightForOrigin
*/
void R_LightForOrigin( const vec3_t origin, vec3_t dir, vec4_t ambient, vec4_t diffuse, float radius )
{
int i, j;
int k, s;
int vi[3], elem[4];
float dot, t[8];
vec3_t vf, vf2, tdir;
vec3_t ambientLocal, diffuseLocal;
vec_t *gridSize, *gridMins;
int *gridBounds;
mgridlight_t **lightarray;
int i, j;
int k, s;
int vi[3], elem[4];
float dot, t[8];
vec3_t vf, vf2, tdir;
vec3_t ambientLocal, diffuseLocal;
vec_t *gridSize, *gridMins;
int *gridBounds;
mgridlight_t **lightarray;
VectorSet( ambientLocal, 0, 0, 0 );
VectorSet( diffuseLocal, 0, 0, 0 );
if( !r_worldmodel /* || (RI.refdef.rdflags & RDF_NOWORLDMODEL)*/ ||
!r_worldbrushmodel->lightgrid || !r_worldbrushmodel->numlightgridelems )
if( !r_worldmodel || !r_worldbrushmodel->lightgrid || !r_worldbrushmodel->numlightgridelems )
{
VectorSet( dir, 0.5f, 0.2f, -1.0f );
goto dynamic;
@ -702,7 +701,7 @@ static int R_PackLightmaps( int num, int w, int h, int size, int stride, bool de
maxY = r_maxLightmapBlockSize / h;
max = min( maxX, maxY );
MsgDev( D_NOTE, "Packing %i lightmap(s) -> ", num );
MsgDev( D_NOTE, "Packing %i lightmap%s -> ", num, (num > 1) ? "s" : "" );
com.snprintf( uploadName, sizeof( uploadName ), "%s%i", name, r_numUploadedLightmaps );
if( !max || num == 1 || !mapConfig.lightmapsPacking /* || !r_lighting_packlightmaps->integer*/ )
@ -873,7 +872,7 @@ void R_BuildLightmaps( int numLightmaps, int w, int h, const byte *data, mlightm
if( r_lightmapBuffer )
Mem_Free( r_lightmapBuffer );
MsgDev( D_NOTE, "Packed %i lightmap blocks into %i texture(s)\n", numBlocks, r_numUploadedLightmaps );
MsgDev( D_NOTE, "Packed %i lightmap blocks into %i texture%s\n", numBlocks, r_numUploadedLightmaps, (r_numUploadedLightmaps > 1) ? "s" : "" );
}
/*

View File

@ -748,6 +748,7 @@ void R_ClearSkyBox( void );
void R_DrawSky( ref_shader_t *shader );
bool R_AddSkySurface( msurface_t *fa );
ref_shader_t *R_SetupSky( const char *name );
void R_FreeSky( void );
//====================================================================

View File

@ -740,10 +740,13 @@ static void R_AddSpriteModelToList( ref_entity_t *e )
if( e->rendermode == kRenderGlow )
{
trace_t tr;
trace_t tr;
msurface_t *surf;
surf = R_TraceLine( &tr, e->origin, RI.viewOrigin, 0 );
if( e->renderfx == kRenderFxNoDissipation );
else if( R_TraceLine( &tr, e->origin, RI.viewOrigin, SURF_NONSOLID ) == NULL )
else if( surf == NULL && tr.fraction == 1.0f )
{
float dist = VectorDistance( e->origin, RI.viewOrigin );
e->scale = bound( 1.0, dist * 0.005f, 10.0f );

View File

@ -86,21 +86,6 @@ int SignbitsForPlane( const cplane_t *out )
return bits;
}
/*
=================
PlaneTypeForNormal
=================
*/
int PlaneTypeForNormal( const vec3_t normal )
{
// NOTE: should these have an epsilon around 1.0?
if( normal[0] >= 1.0f ) return PLANE_X;
if( normal[1] >= 1.0f ) return PLANE_Y;
if( normal[2] >= 1.0f ) return PLANE_Z;
return PLANE_NONAXIAL;
}
/*
=================
PlaneFromPoints

View File

@ -24,6 +24,30 @@ float CalcFov( float fov_x, float width, float height );
void AdjustFov( float *fov_x, float *fov_y, float width, float height, bool lock_x );
void PlaneFromPoints( vec3_t verts[3], cplane_t *plane );
int SignbitsForPlane( const cplane_t *out );
int PlaneTypeForNormal( const vec3_t normal );
/*
===============
ColorNormalize
===============
*/
_inline float ColorNormalize( const float *in, vec3_t out )
{
float f = max( max( in[0], in[1] ), in[2] );
if( f > 1.0f )
{
f = 1.0f / f;
out[0] = in[0] * f;
out[1] = in[1] * f;
out[2] = in[2] * f;
}
else
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
return f;
}
#endif /*__R_MATH_H__*/

View File

@ -315,11 +315,32 @@ All 3D-geometry passes this function.
*/
meshbuffer_t *R_AddMeshToList( int type, mfog_t *fog, ref_shader_t *shader, int infokey )
{
meshlist_t *list;
meshbuffer_t *meshbuf;
meshlist_t *list;
meshbuffer_t *meshbuf;
if( !shader )
return NULL;
if( !shader ) return NULL;
if( RI.currententity && (shader->flags & SHADER_RENDERMODE))
{
switch( RI.currententity->rendermode )
{
case kRenderTransTexture:
case kRenderGlow:
case kRenderTransAdd:
shader->sort = SORT_ADDITIVE;
break;
case kRenderTransColor:
shader->sort = SORT_DECAL;
break;
case kRenderTransAlpha:
shader->sort = SORT_ALPHATEST;
break;
case kRenderNormal:
default: shader->sort = shader->realsort; // restore original
break;
}
shader->sortkey = Shader_Sortkey( shader, shader->sort ); // update sortkey too
}
list = RI.meshlist;
if( shader->sort > SORT_OPAQUE )

View File

@ -27,7 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "matrix_lib.h"
#include "byteorder.h"
#define Q_rint(x) ((x) < 0 ? ((int)((x)-0.5f)) : ((int)((x)+0.5f)))
#define Mod_CopyString( m, str ) com.stralloc( (m)->mempool, str, __FILE__, __LINE__ )
enum
@ -193,6 +192,7 @@ void Mod_FreeModel( ref_model_t *mod )
{
if( !mod || !mod->mempool ) return;
if( mod == r_worldmodel ) R_FreeSky();
Mem_FreePool( &mod->mempool );
Mem_Set( mod, 0, sizeof( *mod ));
}
@ -334,6 +334,9 @@ static void Mod_UpdateShaders( ref_model_t *mod )
if( !shader || !shader->name ) continue;
Shader_TouchImages( shader, false );
}
if( mod == r_worldmodel && tr.currentSkyShader )
Shader_TouchImages( tr.currentSkyShader, false );
}
/*
@ -416,6 +419,13 @@ ref_model_t *Mod_ForName( const char *name, bool crash )
descr->loader( mod, NULL, buf );
Mem_Free( buf );
if( mod->type == mod_bad )
{
// check for loading problems
Mod_FreeModel( mod );
return NULL;
}
if( !descr->maxLods )
return mod;
@ -1568,9 +1578,9 @@ Mod_LoadLightgrid
*/
static void Mod_LoadLightgrid( const lump_t *l )
{
int i, j, count;
dlightgridq_t *in;
mgridlight_t *out;
int i, j, count;
dlightgridq_t *in;
mgridlight_t *out;
in = ( void * )( mod_base + l->fileofs );
if( l->filelen % sizeof( *in ) )
@ -1628,11 +1638,11 @@ Mod_LoadLightArray
*/
static void Mod_LoadLightArray( void )
{
int i, count;
mgridlight_t **out;
int i, count;
mgridlight_t **out;
count = loadbmodel->numlightgridelems;
out = Mod_Malloc( loadmodel, sizeof( *out )*count );
out = Mod_Malloc( loadmodel, sizeof( *out ) * count );
loadbmodel->lightarray = out;
loadbmodel->numlightarrayelems = count;
@ -2069,7 +2079,6 @@ void R_BeginRegistration( const char *mapname, const dvis_t *visData )
// explicitly free the old map if different
if( com.strcmp( r_models[0].name, fullname ))
{
tr.currentSkyShader = NULL; // invalidate sky shader
Mod_FreeModel( &r_models[0] );
R_NewMap ();
}

View File

@ -67,7 +67,6 @@ static bool r_shaderHasDlightPass;
#define Shader_FreePassCinematics( s ) if((s)->cinHandle ) { R_FreeCinematics((s)->cinHandle ); (s)->cinHandle = 0; }
#define Shader_CopyString( str ) com.stralloc( r_shaderpool, str, __FILE__, __LINE__ )
#define Shader_Sortkey( shader, sort ) ((( sort )<<26 )|( shader - r_shaders ))
#define Shader_Malloc( size ) Mem_Alloc( r_shaderpool, size )
#define Shader_Free( data ) Mem_Free( data )
@ -2559,7 +2558,7 @@ void R_InitShaders( void )
}
// parse this file
MsgDev( D_LOAD, "loading shaderfile '%s'\n", t->filenames[i] );
MsgDev( D_LOAD, "%s\n", t->filenames[i] );
Shader_ParseFile( script, t->filenames[i] );
Com_CloseScript( script );
}
@ -2724,6 +2723,8 @@ void Shader_SetBlendmode( ref_stage_t *pass )
pass->flags |= SHADERSTAGE_BLEND_ADD;
else if( blendsrc == GLSTATE_SRCBLEND_SRC_ALPHA && blenddst == GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA )
pass->flags |= SHADERSTAGE_BLEND_DECAL;
else if( blendsrc == GLSTATE_DSTBLEND_SRC_COLOR && blenddst == GLSTATE_SRCBLEND_DST_COLOR )
pass->flags |= SHADERSTAGE_BLEND_DECAL; // for detail textures
}
static bool Shader_ParseCommand( ref_shader_t *shader, script_t *script, const char *command )
@ -3372,8 +3373,8 @@ static ref_shader_t *Shader_CreateDefault( ref_shader_t *shader, int type, int a
// normal transparency
pass->flags |= SHADERSTAGE_BLEND_MODULATE;
pass->glState = GLSTATE_SRCBLEND_SRC_ALPHA|GLSTATE_DSTBLEND_ONE_MINUS_SRC_ALPHA|GLSTATE_DEPTHWRITE;
pass->rgbGen.type = RGBGEN_LIGHTING_AMBIENT_ONLY;
pass->alphaGen.type = ALPHAGEN_ENTITY;
pass->rgbGen.type = RGBGEN_ENTITY;
shader->sort = SORT_ADDITIVE;
break;
case kRenderTransAdd:
@ -3386,8 +3387,8 @@ static ref_shader_t *Shader_CreateDefault( ref_shader_t *shader, int type, int a
case kRenderTransAlpha:
pass->flags |= SHADERSTAGE_BLEND_DECAL;
pass->glState = GLSTATE_AFUNC_GE128|GLSTATE_DEPTHWRITE;
pass->rgbGen.type = RGBGEN_LIGHTING_AMBIENT_ONLY;
pass->alphaGen.type = ALPHAGEN_ENTITY;
pass->rgbGen.type = RGBGEN_ENTITY;
shader->sort = SORT_ALPHATEST;
break;
default:
@ -3469,7 +3470,7 @@ static ref_shader_t *Shader_CreateDefault( ref_shader_t *shader, int type, int a
break;
case kRenderGlow:
pass->flags |= SHADERSTAGE_BLEND_ADD;
pass->glState = GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA|GLSTATE_DSTBLEND_ONE|GLSTATE_DEPTHWRITE;
pass->glState = GLSTATE_SRCBLEND_ONE_MINUS_SRC_ALPHA|GLSTATE_DSTBLEND_ONE|GLSTATE_NO_DEPTH_TEST;
pass->alphaGen.type = ALPHAGEN_ENTITY;
pass->rgbGen.type = RGBGEN_ENTITY;
shader->sort = SORT_ADDITIVE;
@ -4006,4 +4007,12 @@ ref_shader_t *R_SetupSky( const char *name )
MsgDev( D_ERROR, "R_SetupSky: mismatch shader indexes %i != %i\n", index, tr.currentSkyShader->shadernum );
return tr.currentSkyShader;
}
void R_FreeSky( void )
{
if( tr.currentSkyShader == NULL ) return; // already freed
Shader_FreeShader( tr.currentSkyShader );
tr.currentSkyShader = NULL;
}

View File

@ -340,6 +340,7 @@ typedef struct ref_shader_s
} ref_shader_t;
extern ref_shader_t r_shaders[MAX_SHADERS];
#define Shader_Sortkey( shader, sort ) ((( sort )<<26 )|( shader - r_shaders ))
void R_InitShaders( void );
void R_ShutdownShaders( void );

View File

@ -180,7 +180,7 @@ dstudiohdr_t *R_StudioLoadHeader( ref_model_t *mod, const uint *buffer )
void Mod_StudioLoadModel( ref_model_t *mod, ref_model_t *parent, const void *buffer )
{
dstudiohdr_t *phdr = R_StudioLoadHeader( mod, buffer );
dstudiohdr_t *thdr;
dstudiohdr_t *thdr = NULL;
void *texbuf;
if( !phdr ) return; // there were problems
@ -191,7 +191,7 @@ void Mod_StudioLoadModel( ref_model_t *mod, ref_model_t *parent, const void *buf
{
texbuf = FS_LoadFile( R_ExtName( mod ), NULL ); // use buffer again
if( texbuf ) thdr = R_StudioLoadHeader( mod, texbuf );
else MsgDev( D_WARN, "textures for %s not found!\n", mod->name );
else MsgDev( D_ERROR, "textures for %s not found!\n", mod->name );
if( !thdr ) return; // there were problems
mod->thdr = (dstudiohdr_t *)Mem_Alloc( mod->mempool, LittleLong( thdr->length ));
@ -1366,7 +1366,7 @@ void R_StudioDrawMesh( const meshbuffer_t *mb, int meshnum, dstudiotexture_t * p
if( RI.currententity->outlineHeight )
features |= MF_NORMALS|(GL_Support( R_SHADER_GLSL100_EXT ) ? MF_ENABLENORMALS : 0);
}
for( i = 0; i < pmesh[meshnum].numnorms; i++, lv += 3, pstudionorms++, pnormbone++)
{
R_StudioLighting (&lv_tmp, *pnormbone, flags, (float *)pstudionorms);
@ -1471,7 +1471,7 @@ void R_StudioDrawMesh( const meshbuffer_t *mb, int meshnum, dstudiotexture_t * p
studio_mesh.xyzArray = inVertsArray;
R_TranslateForEntity( RI.currententity );
R_PushMesh( &studio_mesh, features );
// FIXME! R_PushMesh( &studio_mesh, features );
R_RenderMeshBuffer( mb );
}
@ -2122,6 +2122,7 @@ void R_StudioDrawPoints( const meshbuffer_t *mb )
}
R_StudioSetupRender( e, Mod_ForHandle( mb->LODModelHandle ));
if( m_pStudioHeader->numbodyparts == 0 ) return; // nothing to draw
if( mb->LODModelHandle != old_model || old_entity != RI.currententity )
{

View File

@ -1,63 +0,0 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: render - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20C7.tmp" with contents
[
/nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "../public" /I "../common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"..\temp\render\!debug/" /Fo"..\temp\render\!debug/" /Fd"..\temp\render\!debug/" /FD /c
"D:\Xash3D\src_main\render\r_opengl.c"
]
Creating command line "cl.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20C7.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20C8.tmp" with contents
[
msvcrtd.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"..\temp\render\!debug/render.pdb" /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"..\temp\render\!debug/render.dll" /implib:"..\temp\render\!debug/render.lib" /pdbtype:sept
"\Xash3D\src_main\temp\render\!debug\cin.obj"
"\Xash3D\src_main\temp\render\!debug\r_alias.obj"
"\Xash3D\src_main\temp\render\!debug\r_backend.obj"
"\Xash3D\src_main\temp\render\!debug\r_bloom.obj"
"\Xash3D\src_main\temp\render\!debug\r_cin.obj"
"\Xash3D\src_main\temp\render\!debug\r_cull.obj"
"\Xash3D\src_main\temp\render\!debug\r_draw.obj"
"\Xash3D\src_main\temp\render\!debug\r_image.obj"
"\Xash3D\src_main\temp\render\!debug\r_light.obj"
"\Xash3D\src_main\temp\render\!debug\r_main.obj"
"\Xash3D\src_main\temp\render\!debug\r_math.obj"
"\Xash3D\src_main\temp\render\!debug\r_mesh.obj"
"\Xash3D\src_main\temp\render\!debug\r_model.obj"
"\Xash3D\src_main\temp\render\!debug\r_opengl.obj"
"\Xash3D\src_main\temp\render\!debug\r_poly.obj"
"\Xash3D\src_main\temp\render\!debug\r_program.obj"
"\Xash3D\src_main\temp\render\!debug\r_register.obj"
"\Xash3D\src_main\temp\render\!debug\r_shader.obj"
"\Xash3D\src_main\temp\render\!debug\r_shadow.obj"
"\Xash3D\src_main\temp\render\!debug\r_skin.obj"
"\Xash3D\src_main\temp\render\!debug\r_sky.obj"
"\Xash3D\src_main\temp\render\!debug\r_sprite.obj"
"\Xash3D\src_main\temp\render\!debug\r_studio.obj"
"\Xash3D\src_main\temp\render\!debug\r_surf.obj"
]
Creating command line "link.exe @C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20C8.tmp"
Creating temporary file "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20C9.bat" with contents
[
@echo off
copy \Xash3D\src_main\temp\render\!debug\render.dll "D:\Xash3D\bin\render.dll"
]
Creating command line "C:\DOCUME~1\MIKE~1.MIK\LOCALS~1\Temp\RSP20C9.bat"
Compiling...
r_opengl.c
Linking...
<h3>Output Window</h3>
Performing Custom Build Step on \Xash3D\src_main\temp\render\!debug\render.dll
‘ª®¯¨à®¢ ­® ä ©«®¢: 1.
<h3>Results</h3>
render.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

View File

@ -1,16 +0,0 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: server - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
<h3>Results</h3>
server.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

View File

@ -125,4 +125,8 @@ Beta 13.12.08
102. replace Matrix4_ with Matrix4x4_ OK
103. fixup studio culling OK
103. fixup player blending
104. sorting folder resources
104. sorting folder resources OK
105. sorting sources folder OK
106. implement q3map2 into xtools.dll OK
107. implement shader sorting for rendermodes OK
108. prepare QuArK to Xash-ready

View File

@ -1,134 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// ambient.c - calcualte ambient sounds level
//=======================================================================
#include "bsplib.h"
/*
Some textures (sky, water, slime, lava) are considered ambient sound emiters.
Find an aproximate distance to the nearest emiter of each class for each leaf.
*/
#define MIN_AMBIENT_DIST 128
#define MAX_AMBIENT_DIST 1024
/*
====================
SurfaceBBox
====================
*/
void SurfaceBBox( dsurface_t *s, vec3_t mins, vec3_t maxs )
{
int i, e, vi;
vec3_t v;
ClearBounds( mins, maxs );
for( i = 0; i < s->numedges; i++ )
{
e = dsurfedges[s->firstedge+i];
if( e >= 0 ) vi = dedges[e].v[0];
else vi = dedges[-e].v[1];
VectorCopy( dvertexes[vi].point, v );
AddPointToBounds( v, mins, maxs );
}
}
void AmbientForLeaf( dleaf_t *leaf, float *dists )
{
int j;
float vol;
for( j = 0; j < NUM_AMBIENTS; j++ )
{
if( dists[j] < MIN_AMBIENT_DIST ) vol = 1.0;
else if( dists[j] < MAX_AMBIENT_DIST )
{
vol = 1.0f - (dists[j] / MAX_AMBIENT_DIST);
vol = bound( 0.0f, vol, 1.0f );
}
else vol = 0.0f;
leaf->sounds[j] = (byte)(vol * 255);
}
}
/*
====================
CalcAmbientSounds
FIXME: make work
====================
*/
void CalcAmbientSounds( void )
{
int i, j, k, l;
dleaf_t *leaf, *hit;
byte *vis;
dsurface_t *surf;
vec3_t mins, maxs;
float d, maxd;
int ambient_type;
dtexinfo_t *info;
dshader_t *shader;
float dists[NUM_AMBIENTS];
Msg( "---- CalcAmbientSounds ----\n" );
if( !visdatasize )
Sys_Break( "can't create ambient sources - map not vised\n" );
for( i = 0; i < dvis->numclusters; i++ )
{
leaf = &dleafs[i+1];
// clear ambients
for( j = 0; j < NUM_AMBIENTS; j++ )
dists[j] = MAX_AMBIENT_DIST;
vis = PhsForCluster( i );
for( j = 0; j < dvis->numclusters; j++ )
{
if(!(vis[j>>3] & (1<<(j & 7))))
continue;
// check this leaf for sound textures
hit = &dleafs[j+1];
for( k = 0; k < hit->numleafsurfaces; k++ )
{
surf = &dsurfaces[dleafsurfaces[hit->firstleafsurface + k]];
info = &texinfo[surf->texinfo];
shader = &dshaders[info->shadernum];
if( shader->contentFlags & CONTENTS_SKY )
ambient_type = AMBIENT_SKY;
else if( shader->contentFlags & CONTENTS_SOLID )
continue;
else if( shader->contentFlags & CONTENTS_WATER )
ambient_type = AMBIENT_WATER;
else if( shader->contentFlags & CONTENTS_SLIME )
ambient_type = AMBIENT_SLIME;
else if( shader->contentFlags & CONTENTS_LAVA )
ambient_type = AMBIENT_LAVA;
else continue;
// find distance from source leaf to polygon
SurfaceBBox( surf, mins, maxs );
for( l = maxd = 0; l < 3; l++ )
{
if (maxs[l] > leaf->maxs[l] )
d = maxs[l] - leaf->maxs[l];
else if( mins[l] < leaf->mins[l] )
d = leaf->mins[l] - mins[l];
else d = 0;
if( d > maxd ) maxd = d;
}
if( maxd < dists[ambient_type] )
dists[ambient_type] = maxd;
}
}
AmbientForLeaf( leaf, dists );
}
}

969
xtools/bsplib/brush.c Normal file
View File

@ -0,0 +1,969 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BRUSH_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
AllocSideRef() - ydnar
allocates and assigns a brush side reference
*/
sideRef_t *AllocSideRef( side_t *side, sideRef_t *next )
{
sideRef_t *sideRef;
/* dummy check */
if( side == NULL )
return next;
/* allocate and return */
sideRef = Malloc( sizeof( *sideRef ) );
sideRef->side = side;
sideRef->next = next;
return sideRef;
}
/*
CountBrushList()
counts the number of brushes in a brush linked list
*/
int CountBrushList( brush_t *brushes )
{
int c = 0;
/* count brushes */
for( ; brushes != NULL; brushes = brushes->next )
c++;
return c;
}
/*
AllocBrush()
allocates a new brush
*/
brush_t *AllocBrush( int numSides )
{
brush_t *bb;
size_t c;
/* allocate and clear */
if( numSides <= 0 )
Sys_Break( "AllocBrush called with numsides = %d", numSides );
c = (size_t)&(((brush_t*) 0)->sides[ numSides ]);
bb = Malloc( c );
if( GetNumThreads() == 1 )
numActiveBrushes++;
/* return it */
return bb;
}
/*
FreeBrush()
frees a single brush and all sides/windings
*/
void FreeBrush( brush_t *b )
{
int i;
/* error check */
if( *((unsigned int*) b) == 0xFEFEFEFE )
{
MsgDev( D_WARN, "Attempt to free an already freed brush!\n" );
return;
}
/* free brush sides */
for( i = 0; i < b->numsides; i++ )
if( b->sides[i].winding != NULL )
FreeWinding( b->sides[ i ].winding );
/* ydnar: overwrite it */
Mem_Set( b, 0xFE, (size_t)&(((brush_t*) 0)->sides[ b->numsides ]) );
*((unsigned int*) b) = 0xFEFEFEFE;
/* free it */
Mem_Free( b );
if( GetNumThreads() == 1 )
numActiveBrushes--;
}
/*
FreeBrushList()
frees a linked list of brushes
*/
void FreeBrushList( brush_t *brushes )
{
brush_t *next;
/* walk brush list */
for( ; brushes != NULL; brushes = next )
{
next = brushes->next;
FreeBrush( brushes );
}
}
/*
CopyBrush()
duplicates the brush, sides, and windings
*/
brush_t *CopyBrush( brush_t *brush )
{
brush_t *newBrush;
size_t size;
int i;
/* copy brush */
size = (size_t)&(((brush_t*) 0)->sides[ brush->numsides ]);
newBrush = AllocBrush( brush->numsides );
Mem_Copy( newBrush, brush, size );
/* ydnar: nuke linked list */
newBrush->next = NULL;
/* copy sides */
for( i = 0; i < brush->numsides; i++ )
{
if( brush->sides[ i ].winding != NULL )
newBrush->sides[ i ].winding = CopyWinding( brush->sides[ i ].winding );
}
/* return it */
return newBrush;
}
/*
BoundBrush()
sets the mins/maxs based on the windings
returns false if the brush doesn't enclose a valid volume
*/
bool BoundBrush( brush_t *brush )
{
int i, j;
winding_t *w;
ClearBounds( brush->mins, brush->maxs );
for( i = 0; i < brush->numsides; i++ )
{
w = brush->sides[ i ].winding;
if( w == NULL )
continue;
for( j = 0; j < w->numpoints; j++ )
AddPointToBounds( w->p[ j ], brush->mins, brush->maxs );
}
for( i = 0; i < 3; i++ )
{
if( brush->mins[ i ] < MIN_WORLD_COORD || brush->maxs[ i ] > MAX_WORLD_COORD || brush->mins[i] >= brush->maxs[ i ] )
return false;
}
return true;
}
/*
SnapWeldVector() - ydnar
welds two vec3_t's into a third, taking into account nearest-to-integer
instead of averaging
*/
#define SNAP_EPSILON 0.01
void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out )
{
int i;
vec_t ai, bi, outi;
/* dummy check */
if( a == NULL || b == NULL || out == NULL )
return;
/* do each element */
for( i = 0; i < 3; i++ )
{
/* round to integer */
ai = Q_rint( a[ i ] );
bi = Q_rint( a[ i ] );
/* prefer exact integer */
if( ai == a[ i ] )
out[ i ] = a[ i ];
else if( bi == b[ i ] )
out[ i ] = b[ i ];
/* use nearest */
else if( fabs( ai - a[ i ] ) < fabs( bi < b[ i ] ) )
out[ i ] = a[ i ];
else
out[ i ] = b[ i ];
/* snap */
outi = Q_rint( out[ i ] );
if( fabs( outi - out[ i ] ) <= SNAP_EPSILON )
out[ i ] = outi;
}
}
/*
FixWinding() - ydnar
removes degenerate edges from a winding
returns true if the winding is valid
*/
#define DEGENERATE_EPSILON 0.1
bool FixWinding( winding_t *w )
{
bool valid = true;
int i, j, k;
vec3_t vec;
float dist;
/* dummy check */
if( !w )
return false;
/* check all verts */
for( i = 0; i < w->numpoints; i++ )
{
/* don't remove points if winding is a triangle */
if( w->numpoints == 3 )
return valid;
/* get second point index */
j = (i + 1) % w->numpoints;
/* degenerate edge? */
VectorSubtract( w->p[ i ], w->p[ j ], vec );
dist = VectorLength( vec );
if( dist < DEGENERATE_EPSILON )
{
valid = false;
//MsgDev( D_WARN, "Degenerate winding edge found, fixing...\n" );
/* create an average point (ydnar 2002-01-26: using nearest-integer weld preference) */
SnapWeldVector( w->p[ i ], w->p[ j ], vec );
VectorCopy( vec, w->p[ i ] );
//VectorAdd( w->p[ i ], w->p[ j ], vec );
//VectorScale( vec, 0.5, w->p[ i ] );
/* move the remaining verts */
for( k = i + 2; k < w->numpoints; k++ )
{
VectorCopy( w->p[ k ], w->p[ k - 1 ] );
}
w->numpoints--;
}
}
/* one last check and return */
if( w->numpoints < 3 )
valid = false;
return valid;
}
/*
CreateBrushWindings()
makes basewindigs for sides and mins/maxs for the brush
returns false if the brush doesn't enclose a valid volume
*/
bool CreateBrushWindings( brush_t *brush )
{
int i, j;
winding_t *w;
side_t *side;
plane_t *plane;
/* walk the list of brush sides */
for( i = 0; i < brush->numsides; i++ )
{
/* get side and plane */
side = &brush->sides[ i ];
plane = &mapplanes[ side->planenum ];
/* make huge winding */
w = BaseWindingForPlane( plane->normal, plane->dist );
/* walk the list of brush sides */
for( j = 0; j < brush->numsides && w != NULL; j++ )
{
if( i == j )
continue;
if( brush->sides[ j ].planenum == (brush->sides[ i ].planenum ^ 1) )
continue; /* back side clipaway */
if( brush->sides[ j ].bevel )
continue;
plane = &mapplanes[ brush->sides[ j ].planenum ^ 1 ];
ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); // CLIP_EPSILON );
/* ydnar: fix broken windings that would generate trifans */
FixWinding( w );
}
/* set side winding */
side->winding = w;
}
/* find brush bounds */
return BoundBrush( brush );
}
/*
==================
BrushFromBounds
Creates a new axial brush
==================
*/
brush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
{
brush_t *b;
int i;
vec3_t normal;
vec_t dist;
b = AllocBrush (6);
b->numsides = 6;
for (i=0 ; i<3 ; i++)
{
VectorClear (normal);
normal[i] = 1;
dist = maxs[i];
b->sides[i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &maxs );
normal[i] = -1;
dist = -mins[i];
b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins );
}
CreateBrushWindings (b);
return b;
}
/*
==================
BrushVolume
==================
*/
vec_t BrushVolume (brush_t *brush)
{
int i;
winding_t *w;
vec3_t corner;
vec_t d, area, volume;
plane_t *plane;
if (!brush)
return 0;
// grab the first valid point as the corner
w = NULL;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (w)
break;
}
if (!w)
return 0;
VectorCopy (w->p[0], corner);
// make tetrahedrons to all other faces
volume = 0;
for ( ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
plane = &mapplanes[brush->sides[i].planenum];
d = -(DotProduct (corner, plane->normal) - plane->dist);
area = WindingArea (w);
volume += d*area;
}
volume /= 3;
return volume;
}
/*
WriteBSPBrushMap()
writes a map with the split bsp brushes
*/
void WriteBSPBrushMap( char *name, brush_t *list )
{
file_t *f;
side_t *s;
int i;
winding_t *w;
/* note it */
Msg( "Writing %s\n", name );
/* open the map file */
f = FS_Open( name, "wb" );
if( f == NULL )
Sys_Break( "Can't write %s\b", name );
FS_Printf( f, "{\n\"classname\" \"worldspawn\"\n" );
for ( ; list ; list=list->next )
{
FS_Printf (f, "{\n");
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
{
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
FS_Printf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
FS_Printf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
FS_Printf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
FS_Printf (f, "notexture 0 0 0 1 1\n" );
FreeWinding (w);
}
FS_Printf (f, "}\n");
}
FS_Printf (f, "}\n");
FS_Close (f);
}
/*
FilterBrushIntoTree_r()
adds brush reference to any intersecting bsp leafnode
*/
int FilterBrushIntoTree_r( brush_t *b, node_t *node )
{
brush_t *front, *back;
int c;
/* dummy check */
if( b == NULL )
return 0;
/* add it to the leaf list */
if( node->planenum == PLANENUM_LEAF )
{
/* something somewhere is hammering brushlist */
b->next = node->brushlist;
node->brushlist = b;
/* classify the leaf by the structural brush */
if( !b->detail )
{
if( b->opaque )
{
node->opaque = true;
node->areaportal = false;
}
else if( b->compileFlags & C_AREAPORTAL )
{
if( !node->opaque )
node->areaportal = true;
}
}
return 1;
}
/* split it by the node plane */
c = b->numsides;
SplitBrush( b, node->planenum, &front, &back );
FreeBrush( b );
c = 0;
c += FilterBrushIntoTree_r( front, node->children[ 0 ] );
c += FilterBrushIntoTree_r( back, node->children[ 1 ] );
return c;
}
/*
FilterDetailBrushesIntoTree
fragment all the detail brushes into the structural leafs
*/
void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree )
{
brush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
/* note it */
MsgDev( D_NOTE, "--- FilterDetailBrushesIntoTree ---\n" );
/* walk the list of brushes */
c_unique = 0;
c_clusters = 0;
for( b = e->brushes; b; b = b->next )
{
if( !b->detail )
continue;
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, tree->headnode );
c_clusters += r;
/* mark all sides as visible so drawsurfs are created */
if( r )
{
for( i = 0; i < b->numsides; i++ )
{
if( b->sides[ i ].winding )
b->sides[ i ].visible = true;
}
}
}
/* emit some statistics */
MsgDev( D_INFO, "%9d detail brushes\n", c_unique );
MsgDev( D_INFO, "%9d cluster references\n", c_clusters );
}
/*
=====================
FilterStructuralBrushesIntoTree
Mark the leafs as opaque and areaportals
=====================
*/
void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
brush_t *b, *newb;
int r;
int c_unique, c_clusters;
int i;
MsgDev( D_NOTE, "--- FilterStructuralBrushesIntoTree ---\n");
c_unique = 0;
c_clusters = 0;
for ( b = e->brushes ; b ; b = b->next ) {
if ( b->detail ) {
continue;
}
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, tree->headnode );
c_clusters += r;
// mark all sides as visible so drawsurfs are created
if ( r ) {
for ( i = 0 ; i < b->numsides ; i++ ) {
if ( b->sides[i].winding ) {
b->sides[i].visible = true;
}
}
}
}
/* emit some statistics */
MsgDev( D_INFO, "%9d structural brushes\n", c_unique );
MsgDev( D_INFO, "%9d cluster references\n", c_clusters );
}
/*
================
AllocTree
================
*/
tree_t *AllocTree (void)
{
tree_t *tree;
tree = Malloc(sizeof(*tree));
Mem_Set (tree, 0, sizeof(*tree));
ClearBounds (tree->mins, tree->maxs);
return tree;
}
/*
================
AllocNode
================
*/
node_t *AllocNode (void)
{
node_t *node;
node = Malloc(sizeof(*node));
Mem_Set (node, 0, sizeof(*node));
return node;
}
/*
================
WindingIsTiny
Returns true if the winding would be crunched out of
existance by the vertex snapping.
================
*/
#define EDGE_LENGTH 0.2
bool WindingIsTiny (winding_t *w)
{
/*
if (WindingArea (w) < 1)
return true;
return false;
*/
int i, j;
vec_t len;
vec3_t delta;
int edges;
edges = 0;
for (i=0 ; i<w->numpoints ; i++)
{
j = i == w->numpoints - 1 ? 0 : i+1;
VectorSubtract (w->p[j], w->p[i], delta);
len = VectorLength (delta);
if (len > EDGE_LENGTH)
{
if (++edges == 3)
return false;
}
}
return true;
}
/*
================
WindingIsHuge
Returns true if the winding still has one of the points
from basewinding for plane
================
*/
bool WindingIsHuge (winding_t *w)
{
int i, j;
for (i=0 ; i<w->numpoints ; i++)
{
for (j=0 ; j<3 ; j++)
if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
return true;
}
return false;
}
//============================================================
/*
==================
BrushMostlyOnSide
==================
*/
int BrushMostlyOnSide (brush_t *brush, plane_t *plane)
{
int i, j;
winding_t *w;
vec_t d, max;
int side;
max = 0;
side = PSIDE_FRONT;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
{
d = DotProduct (w->p[j], plane->normal) - plane->dist;
if (d > max)
{
max = d;
side = PSIDE_FRONT;
}
if (-d > max)
{
max = -d;
side = PSIDE_BACK;
}
}
}
return side;
}
/*
SplitBrush()
generates two new brushes, leaving the original unchanged
*/
void SplitBrush( brush_t *brush, int planenum, brush_t **front, brush_t **back )
{
brush_t *b[2];
int i, j;
winding_t *w, *cw[2], *midwinding;
plane_t *plane, *plane2;
side_t *s, *cs;
float d, d_front, d_back;
*front = NULL;
*back = NULL;
plane = &mapplanes[planenum];
// check all points
d_front = d_back = 0;
for (i=0 ; i<brush->numsides ; i++)
{
w = brush->sides[i].winding;
if (!w)
continue;
for (j=0 ; j<w->numpoints ; j++)
{
d = DotProduct (w->p[j], plane->normal) - plane->dist;
if (d > 0 && d > d_front)
d_front = d;
if (d < 0 && d < d_back)
d_back = d;
}
}
if (d_front < 0.1) // PLANESIDE_EPSILON)
{ // only on back
*back = CopyBrush( brush );
return;
}
if (d_back > -0.1) // PLANESIDE_EPSILON)
{ // only on front
*front = CopyBrush( brush );
return;
}
// create a new winding from the split plane
w = BaseWindingForPlane (plane->normal, plane->dist);
for (i=0 ; i<brush->numsides && w ; i++)
{
plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
}
if (!w || WindingIsTiny (w) )
{ // the brush isn't really split
int side;
side = BrushMostlyOnSide (brush, plane);
if (side == PSIDE_FRONT)
*front = CopyBrush (brush);
if (side == PSIDE_BACK)
*back = CopyBrush (brush);
return;
}
if( WindingIsHuge( w ))
MsgDev( D_WARN, "huge winding\n" );
midwinding = w;
// split it for real
for (i=0 ; i<2 ; i++)
{
b[i] = AllocBrush (brush->numsides+1);
Mem_Copy( b[i], brush, sizeof( brush_t ) - sizeof( brush->sides ) );
b[i]->numsides = 0;
b[i]->next = NULL;
b[i]->original = brush->original;
}
// split all the current windings
for (i=0 ; i<brush->numsides ; i++)
{
s = &brush->sides[i];
w = s->winding;
if (!w)
continue;
ClipWindingEpsilon (w, plane->normal, plane->dist,
0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
for (j=0 ; j<2 ; j++)
{
if (!cw[j])
continue;
cs = &b[j]->sides[b[j]->numsides];
b[j]->numsides++;
*cs = *s;
cs->winding = cw[j];
}
}
// see if we have valid polygons on both sides
for (i=0 ; i<2 ; i++)
{
if (b[i]->numsides < 3 || !BoundBrush (b[i]))
{
if (b[i]->numsides >= 3)
MsgDev( D_ERROR, "bogus brush after clip\n");
FreeBrush (b[i]);
b[i] = NULL;
}
}
if ( !(b[0] && b[1]) )
{
if (!b[0] && !b[1])
MsgDev( D_NOTE, "split removed brush\n" );
else
MsgDev( D_NOTE, "split not on both sides\n" );
if (b[0])
{
FreeBrush (b[0]);
*front = CopyBrush (brush);
}
if (b[1])
{
FreeBrush (b[1]);
*back = CopyBrush (brush);
}
return;
}
// add the midwinding to both sides
for (i=0 ; i<2 ; i++)
{
cs = &b[i]->sides[b[i]->numsides];
b[i]->numsides++;
cs->planenum = planenum^i^1;
cs->shaderInfo = NULL;
if (i==0)
cs->winding = CopyWinding (midwinding);
else
cs->winding = midwinding;
}
{
vec_t v1;
int i;
for (i=0 ; i<2 ; i++)
{
v1 = BrushVolume (b[i]);
if (v1 < 1.0)
{
FreeBrush (b[i]);
b[i] = NULL;
// MsgDev( D_WARN,"tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}

View File

@ -0,0 +1,81 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BRUSH_PRIMIT_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
ComputeAxisBase()
computes the base texture axis for brush primitive texturing
note: ComputeAxisBase here and in editor code must always BE THE SAME!
warning: special case behaviour of atan2( y, x ) <-> atan( y / x ) might not be the same everywhere when x == 0
rotation by (0,RotY,RotZ) assigns X to normal
*/
void ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY )
{
vec_t RotY, RotZ;
/* do some cleaning */
if( fabs( normal[ 0 ] ) < 1e-6 )
normal[ 0 ]= 0.0f;
if( fabs( normal[ 1 ] ) < 1e-6 )
normal[ 1 ]=0.0f;
if( fabs( normal[ 2 ] ) < 1e-6 )
normal[ 2 ] = 0.0f;
/* compute the two rotations around y and z to rotate x to normal */
RotY = -atan2( normal[ 2 ], sqrt( normal[ 1 ] * normal[ 1 ] + normal[ 0 ] * normal[ 0 ]) );
RotZ = atan2( normal[ 1 ], normal[ 0 ] );
/* rotate (0,1,0) and (0,0,1) to compute texX and texY */
texX[ 0 ] = -sin( RotZ );
texX[ 1 ] = cos( RotZ );
texX[ 2 ] = 0;
/* the texY vector is along -z (t texture coorinates axis) */
texY[ 0 ] = -sin( RotY ) * cos( RotZ );
texY[ 1 ] = -sin( RotY ) * sin( RotZ );
texY[ 2 ] = -cos( RotY );
}

File diff suppressed because it is too large Load Diff

905
xtools/bsplib/bsp.c Normal file
View File

@ -0,0 +1,905 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSP_C
/* dependencies */
#include "q3map2.h"
#include "stdio.h" // sscanf
/* -------------------------------------------------------------------------------
functions
------------------------------------------------------------------------------- */
/*
ProcessAdvertisements()
copies advertisement info into the BSP structures
*/
static void ProcessAdvertisements( void ) {
int i;
const char* className;
const char* modelKey;
int modelNum;
bspModel_t* adModel;
bspDrawSurface_t* adSurface;
MsgDev( D_NOTE, "--- ProcessAdvertisements ---\n" );
for( i = 0; i < numEntities; i++ ) {
/* is an advertisement? */
className = ValueForKey( &entities[ i ], "classname" );
if( !com.stricmp( "advertisement", className ) ) {
modelKey = ValueForKey( &entities[ i ], "model" );
if( strlen( modelKey ) > MAX_QPATH - 1 ) {
Sys_Break( "Model Key for entity exceeds ad struct string length." );
} else {
if( numBSPAds < MAX_MAP_ADVERTISEMENTS ) {
bspAds[numBSPAds].cellId = IntForKey( &entities[ i ], "cellId" );
strncpy( bspAds[numBSPAds].model, modelKey, sizeof( bspAds[numBSPAds].model ) );
modelKey++;
modelNum = atoi( modelKey );
adModel = &bspModels[modelNum];
if( adModel->numBSPSurfaces != 1 ) {
Sys_Break( "Ad cell id %d has more than one surface.", bspAds[numBSPAds].cellId );
}
adSurface = &bspDrawSurfaces[adModel->firstBSPSurface];
// store the normal for use at run time.. all ad verts are assumed to
// have identical normals (because they should be a simple rectangle)
// so just use the first vert's normal
VectorCopy( bspDrawVerts[adSurface->firstVert].normal, bspAds[numBSPAds].normal );
// store the ad quad for quick use at run time
if( adSurface->surfaceType == MST_PATCH ) {
int v0 = adSurface->firstVert + adSurface->patchHeight - 1;
int v1 = adSurface->firstVert + adSurface->numVerts - 1;
int v2 = adSurface->firstVert + adSurface->numVerts - adSurface->patchWidth;
int v3 = adSurface->firstVert;
VectorCopy( bspDrawVerts[v0].xyz, bspAds[numBSPAds].rect[0] );
VectorCopy( bspDrawVerts[v1].xyz, bspAds[numBSPAds].rect[1] );
VectorCopy( bspDrawVerts[v2].xyz, bspAds[numBSPAds].rect[2] );
VectorCopy( bspDrawVerts[v3].xyz, bspAds[numBSPAds].rect[3] );
} else {
Sys_Break( "Ad cell %d has an unsupported Ad Surface type.", bspAds[numBSPAds].cellId );
}
numBSPAds++;
} else {
Sys_Break( "Maximum number of map advertisements exceeded." );
}
}
}
}
MsgDev( D_INFO, "%9d in-game advertisements\n", numBSPAds );
}
/*
SetCloneModelNumbers() - ydnar
sets the model numbers for brush entities
*/
static void SetCloneModelNumbers( void )
{
int i, j;
int models;
char modelValue[ 10 ];
const char *value, *value2, *value3;
/* start with 1 (worldspawn is model 0) */
models = 1;
for( i = 1; i < numEntities; i++ )
{
/* only entities with brushes or patches get a model number */
if( entities[ i ].brushes == NULL && entities[ i ].patches == NULL )
continue;
/* is this a clone? */
value = ValueForKey( &entities[ i ], "_clone" );
if( value[ 0 ] != '\0' )
continue;
/* add the model key */
com.sprintf( modelValue, "*%d", models );
SetKeyValue( &entities[ i ], "model", modelValue );
/* increment model count */
models++;
}
/* fix up clones */
for( i = 1; i < numEntities; i++ )
{
/* only entities with brushes or patches get a model number */
if( entities[ i ].brushes == NULL && entities[ i ].patches == NULL )
continue;
/* is this a clone? */
value = ValueForKey( &entities[ i ], "_ins" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ i ], "_instance" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ i ], "_clone" );
if( value[ 0 ] == '\0' )
continue;
/* find an entity with matching clone name */
for( j = 0; j < numEntities; j++ )
{
/* is this a clone parent? */
value2 = ValueForKey( &entities[ j ], "_clonename" );
if( value2[ 0 ] == '\0' )
continue;
/* do they match? */
if( strcmp( value, value2 ) == 0 )
{
/* get the model num */
value3 = ValueForKey( &entities[ j ], "model" );
if( value3[ 0 ] == '\0' )
{
MsgDev( D_WARN, "Cloned entity %s referenced entity without model\n", value2 );
continue;
}
models = atoi( &value2[ 1 ] );
/* add the model key */
com.sprintf( modelValue, "*%d", models );
SetKeyValue( &entities[ i ], "model", modelValue );
/* nuke the brushes/patches for this entity (fixme: leak!) */
entities[ i ].brushes = NULL;
entities[ i ].patches = NULL;
}
}
}
}
/*
FixBrushSides() - ydnar
matches brushsides back to their appropriate drawsurface and shader
*/
static void FixBrushSides( entity_t *e )
{
int i;
mapDrawSurface_t *ds;
sideRef_t *sideRef;
bspBrushSide_t *side;
/* note it */
MsgDev( D_NOTE, "--- FixBrushSides ---\n" );
/* walk list of drawsurfaces */
for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
{
/* get surface and try to early out */
ds = &mapDrawSurfs[ i ];
if( ds->outputNum < 0 )
continue;
/* walk sideref list */
for( sideRef = ds->sideRef; sideRef != NULL; sideRef = sideRef->next )
{
/* get bsp brush side */
if( sideRef->side == NULL || sideRef->side->outputNum < 0 )
continue;
side = &bspBrushSides[ sideRef->side->outputNum ];
/* set drawsurface */
side->surfaceNum = ds->outputNum;
//% MsgDev( D_NOTE, "DS: %7d Side: %7d ", ds->outputNum, sideRef->side->outputNum );
/* set shader */
if( strcmp( bspShaders[ side->shaderNum ].shader, ds->shaderInfo->shader ) )
{
//% MsgDev( D_NOTE, "Remapping %s to %s\n", bspShaders[ side->shaderNum ].shader, ds->shaderInfo->shader );
side->shaderNum = EmitShader( ds->shaderInfo->shader, &ds->shaderInfo->contentFlags, &ds->shaderInfo->surfaceFlags );
}
}
}
}
/*
ProcessWorldModel()
creates a full bsp + surfaces for the worldspawn entity
*/
void ProcessWorldModel( void )
{
int i, s;
entity_t *e;
tree_t *tree;
face_t *faces;
bool ignoreLeaks, leaked;
char shader[MAX_SYSPATH];
const char *value;
/* sets integer blockSize from worldspawn "_blocksize" key if it exists */
value = ValueForKey( &entities[ 0 ], "_blocksize" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "blocksize" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "chopsize" ); /* sof2 */
if( value[ 0 ] != '\0' )
{
/* scan 3 numbers */
s = sscanf( value, "%d %d %d", &blockSize[ 0 ], &blockSize[ 1 ], &blockSize[ 2 ] );
/* handle legacy case */
if( s == 1 )
{
blockSize[ 1 ] = blockSize[ 0 ];
blockSize[ 2 ] = blockSize[ 0 ];
}
}
MsgDev( D_INFO, "block size = { %d %d %d }\n", blockSize[ 0 ], blockSize[ 1 ], blockSize[ 2 ] );
/* sof2: ignore leaks? */
value = ValueForKey( &entities[ 0 ], "_ignoreleaks" ); /* ydnar */
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "ignoreleaks" );
if( value[ 0 ] == '1' )
ignoreLeaks = true;
else
ignoreLeaks = false;
/* begin worldspawn model */
BeginModel();
e = &entities[ 0 ];
e->firstDrawSurf = 0;
/* ydnar: gs mods */
ClearMetaTriangles();
/* check for patches with adjacent edges that need to lod together */
PatchMapDrawSurfs( e );
/* build an initial bsp tree using all of the sides of all of the structural brushes */
faces = MakeStructuralBSPFaceList( entities[ 0 ].brushes );
tree = FaceBSP( faces );
MakeTreePortals( tree );
FilterStructuralBrushesIntoTree( e, tree );
/* see if the bsp is completely enclosed */
if( FloodEntities( tree ) || ignoreLeaks )
{
/* rebuild a better bsp tree using only the sides that are visible from the inside */
FillOutside( tree->headnode );
/* chop the sides to the convex hull of their visible fragments, giving us the smallest polygons */
ClipSidesIntoTree( e, tree );
/* build a visible face tree */
faces = MakeVisibleBSPFaceList( entities[ 0 ].brushes );
FreeTree( tree );
tree = FaceBSP( faces );
MakeTreePortals( tree );
FilterStructuralBrushesIntoTree( e, tree );
leaked = false;
/* ydnar: flood again for skybox */
if( skyboxPresent )
FloodEntities( tree );
}
else
{
Msg( "**********************\n" );
Msg( "******* leaked *******\n" );
Msg( "**********************\n" );
LeakFile( tree );
if( leaktest )
{
Sys_Break( "--- MAP LEAKED, ABORTING LEAKTEST ---\n" );
}
leaked = true;
/* chop the sides to the convex hull of their visible fragments, giving us the smallest polygons */
ClipSidesIntoTree( e, tree );
}
/* save out information for visibility processing */
NumberClusters( tree );
if( !leaked )
WritePortalFile( tree );
/* flood from entities */
FloodAreas( tree );
/* create drawsurfs for triangle models */
AddTriangleModels( e );
/* create drawsurfs for surface models */
AddEntitySurfaceModels( e );
/* generate bsp brushes from map brushes */
EmitBrushes( e->brushes, &e->firstBrush, &e->numBrushes );
/* add references to the detail brushes */
FilterDetailBrushesIntoTree( e, tree );
/* drawsurfs that cross fog boundaries will need to be split along the fog boundary */
if( !nofog )
FogDrawSurfaces( e );
/* subdivide each drawsurf as required by shader tesselation */
if( !nosubdivide )
SubdivideFaceSurfaces( e, tree );
/* add in any vertexes required to fix t-junctions */
if( !notjunc )
FixTJunctions( e );
/* ydnar: classify the surfaces */
ClassifyEntitySurfaces( e );
/* ydnar: project decals */
MakeEntityDecals( e );
/* ydnar: meta surfaces */
MakeEntityMetaTriangles( e );
SmoothMetaTriangles();
FixMetaTJunctions();
MergeMetaTriangles();
/* ydnar: debug portals */
if( debugPortals )
MakeDebugPortalSurfs( tree );
/* ydnar: fog hull */
value = ValueForKey( &entities[ 0 ], "_foghull" );
if( value[ 0 ] != '\0' )
{
com.sprintf( shader, "textures/%s", value );
MakeFogHullSurfs( e, tree, shader );
}
/* ydnar: bug 645: do flares for lights */
for( i = 0; i < numEntities && emitFlares; i++ )
{
entity_t *light, *target;
const char *value, *flareShader;
vec3_t origin, targetOrigin, normal, color;
int lightStyle;
/* get light */
light = &entities[ i ];
value = ValueForKey( light, "classname" );
if( !strcmp( value, "light" ) )
{
/* get flare shader */
flareShader = ValueForKey( light, "_flareshader" );
value = ValueForKey( light, "_flare" );
if( flareShader[ 0 ] != '\0' || value[ 0 ] != '\0' )
{
/* get specifics */
GetVectorForKey( light, "origin", origin );
GetVectorForKey( light, "_color", color );
lightStyle = IntForKey( light, "_style" );
if( lightStyle == 0 )
lightStyle = IntForKey( light, "style" );
/* handle directional spotlights */
value = ValueForKey( light, "target" );
if( value[ 0 ] != '\0' )
{
/* get target light */
target = FindTargetEntity( value );
if( target != NULL )
{
GetVectorForKey( target, "origin", targetOrigin );
VectorSubtract( targetOrigin, origin, normal );
VectorNormalize( normal );
}
}
else
//% VectorClear( normal );
VectorSet( normal, 0, 0, -1 );
/* create the flare surface (note shader defaults automatically) */
DrawSurfaceForFlare( mapEntityNum, origin, normal, color, (char*) flareShader, lightStyle );
}
}
}
/* add references to the final drawsurfs in the apropriate clusters */
FilterDrawsurfsIntoTree( e, tree );
/* match drawsurfaces back to original brushsides (sof2) */
FixBrushSides( e );
/* finish */
EndModel( e, tree->headnode );
FreeTree( tree );
}
/*
ProcessSubModel()
creates bsp + surfaces for other brush models
*/
void ProcessSubModel( void )
{
entity_t *e;
tree_t *tree;
brush_t *b, *bc;
node_t *node;
/* start a brush model */
BeginModel();
e = &entities[ mapEntityNum ];
e->firstDrawSurf = numMapDrawSurfs;
/* ydnar: gs mods */
ClearMetaTriangles();
/* check for patches with adjacent edges that need to lod together */
PatchMapDrawSurfs( e );
/* allocate a tree */
node = AllocNode();
node->planenum = PLANENUM_LEAF;
tree = AllocTree();
tree->headnode = node;
/* add the sides to the tree */
ClipSidesIntoTree( e, tree );
/* ydnar: create drawsurfs for triangle models */
AddTriangleModels( e );
/* create drawsurfs for surface models */
AddEntitySurfaceModels( e );
/* generate bsp brushes from map brushes */
EmitBrushes( e->brushes, &e->firstBrush, &e->numBrushes );
/* just put all the brushes in headnode */
for( b = e->brushes; b; b = b->next )
{
bc = CopyBrush( b );
bc->next = node->brushlist;
node->brushlist = bc;
}
/* subdivide each drawsurf as required by shader tesselation */
if( !nosubdivide )
SubdivideFaceSurfaces( e, tree );
/* add in any vertexes required to fix t-junctions */
if( !notjunc )
FixTJunctions( e );
/* ydnar: classify the surfaces and project lightmaps */
ClassifyEntitySurfaces( e );
/* ydnar: project decals */
MakeEntityDecals( e );
/* ydnar: meta surfaces */
MakeEntityMetaTriangles( e );
SmoothMetaTriangles();
FixMetaTJunctions();
MergeMetaTriangles();
/* add references to the final drawsurfs in the apropriate clusters */
FilterDrawsurfsIntoTree( e, tree );
/* match drawsurfaces back to original brushsides (sof2) */
FixBrushSides( e );
/* finish */
EndModel( e, node );
FreeTree( tree );
}
/*
ProcessModels()
process world + other models into the bsp
*/
void ProcessModels( void )
{
entity_t *entity;
/* start a new bsp */
BeginBSPFile();
/* create map fogs */
CreateMapFogs();
/* walk entity list */
for( mapEntityNum = 0; mapEntityNum < numEntities; mapEntityNum++ )
{
/* get entity */
entity = &entities[ mapEntityNum ];
if( entity->brushes == NULL && entity->patches == NULL )
continue;
/* process the model */
MsgDev( D_INFO, "############### model %i ###############\n", numBSPModels );
if( mapEntityNum == 0 )
ProcessWorldModel();
else
ProcessSubModel();
}
/* write fogs */
EmitFogs();
}
/*
OnlyEnts()
this is probably broken unless teamed with a radiant version that preserves entity order
*/
void OnlyEnts( void )
{
char out[ 1024 ];
/* note it */
MsgDev( D_INFO, "--- OnlyEnts ---\n" );
com.sprintf( out, "%s.bsp", source );
LoadBSPFile( out );
numEntities = 0;
LoadShaderInfo();
LoadMapFile( name, false );
SetModelNumbers();
SetLightStyles();
numBSPEntities = numEntities;
UnparseEntities();
WriteBSPFile( out );
}
/*
BSPMain() - ydnar
handles creation of a bsp from a map file
*/
int BSPMain( int argc, char **argv )
{
int i;
char path[ 1024 ], tempSource[ 1024 ];
bool onlyents = false;
/* note it */
MsgDev( D_INFO, "--- BSP ---\n" );
SetDrawSurfacesBuffer();
mapDrawSurfs = Malloc( sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
numMapDrawSurfs = 0;
tempSource[ 0 ] = '\0';
/* set standard game flags */
maxSurfaceVerts = game->maxSurfaceVerts;
maxSurfaceIndexes = game->maxSurfaceIndexes;
emitFlares = game->emitFlares;
/* process arguments */
for( i = 1; i < (argc - 1); i++ )
{
if( !strcmp( argv[ i ], "-onlyents" ) )
{
MsgDev( D_INFO, "Running entity-only compile\n" );
onlyents = true;
}
else if( !strcmp( argv[ i ], "-tempname" ) )
strcpy( tempSource, argv[ ++i ] );
else if( !strcmp( argv[ i ], "-tmpout" ) )
strcpy( outbase, "/tmp" );
else if( !strcmp( argv[ i ], "-nowater" ) )
{
MsgDev( D_INFO, "Disabling water\n" );
nowater = true;
}
else if( !strcmp( argv[ i ], "-nodetail" ) )
{
MsgDev( D_INFO, "Ignoring detail brushes\n") ;
nodetail = true;
}
else if( !strcmp( argv[ i ], "-fulldetail" ) )
{
MsgDev( D_INFO, "Turning detail brushes into structural brushes\n" );
fulldetail = true;
}
else if( !strcmp( argv[ i ], "-nofog" ) )
{
MsgDev( D_INFO, "Fog volumes disabled\n" );
nofog = true;
}
else if( !strcmp( argv[ i ], "-nosubdivide" ) )
{
MsgDev( D_INFO, "Disabling brush face subdivision\n" );
nosubdivide = true;
}
else if( !strcmp( argv[ i ], "-leaktest" ) )
{
MsgDev( D_INFO, "Leaktest enabled\n" );
leaktest = true;
}
else if( !strcmp( argv[ i ], "-nocurves" ) )
{
MsgDev( D_INFO, "Ignoring curved surfaces (patches)\n" );
noCurveBrushes = true;
}
else if( !strcmp( argv[ i ], "-notjunc" ) )
{
MsgDev( D_INFO, "T-junction fixing disabled\n" );
notjunc = true;
}
else if( !strcmp( argv[ i ], "-fakemap" ) )
{
MsgDev( D_INFO, "Generating fakemap.map\n" );
fakemap = true;
}
else if( !strcmp( argv[ i ], "-samplesize" ) )
{
sampleSize = atoi( argv[ i + 1 ] );
if( sampleSize < 1 )
sampleSize = 1;
i++;
MsgDev( D_INFO, "Lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
}
else if( !strcmp( argv[ i ], "-custinfoparms") )
{
MsgDev( D_INFO, "Custom info parms enabled\n" );
useCustomInfoParms = true;
}
/* sof2 args */
else if( !strcmp( argv[ i ], "-rename" ) )
{
MsgDev( D_INFO, "Appending _bsp suffix to misc_model shaders (SOF2)\n" );
renameModelShaders = true;
}
/* ydnar args */
else if( !strcmp( argv[ i ], "-ne" ) )
{
normalEpsilon = atof( argv[ i + 1 ] );
i++;
MsgDev( D_INFO, "Normal epsilon set to %f\n", normalEpsilon );
}
else if( !strcmp( argv[ i ], "-de" ) )
{
distanceEpsilon = atof( argv[ i + 1 ] );
i++;
MsgDev( D_INFO, "Distance epsilon set to %f\n", distanceEpsilon );
}
else if( !strcmp( argv[ i ], "-mv" ) )
{
maxLMSurfaceVerts = atoi( argv[ i + 1 ] );
if( maxLMSurfaceVerts < 3 )
maxLMSurfaceVerts = 3;
if( maxLMSurfaceVerts > maxSurfaceVerts )
maxSurfaceVerts = maxLMSurfaceVerts;
i++;
MsgDev( D_INFO, "Maximum lightmapped surface vertex count set to %d\n", maxLMSurfaceVerts );
}
else if( !strcmp( argv[ i ], "-mi" ) )
{
maxSurfaceIndexes = atoi( argv[ i + 1 ] );
if( maxSurfaceIndexes < 3 )
maxSurfaceIndexes = 3;
i++;
MsgDev( D_INFO, "Maximum per-surface index count set to %d\n", maxSurfaceIndexes );
}
else if( !strcmp( argv[ i ], "-np" ) )
{
npDegrees = atof( argv[ i + 1 ] );
if( npDegrees < 0.0f )
shadeAngleDegrees = 0.0f;
else if( npDegrees > 0.0f )
MsgDev( D_INFO, "Forcing nonplanar surfaces with a breaking angle of %f degrees\n", npDegrees );
i++;
}
else if( !strcmp( argv[ i ], "-snap" ) )
{
bevelSnap = atoi( argv[ i + 1 ]);
if( bevelSnap < 0 )
bevelSnap = 0;
i++;
if( bevelSnap > 0 )
MsgDev( D_INFO, "Snapping brush bevel planes to %d units\n", bevelSnap );
}
else if( !strcmp( argv[ i ], "-texrange" ) )
{
texRange = atoi( argv[ i + 1 ]);
if( texRange < 0 )
texRange = 0;
i++;
MsgDev( D_INFO, "Limiting per-surface texture range to %d texels\n", texRange );
}
else if( !strcmp( argv[ i ], "-nohint" ) )
{
MsgDev( D_INFO, "Hint brushes disabled\n" );
noHint = true;
}
else if( !strcmp( argv[ i ], "-flat" ) )
{
MsgDev( D_INFO, "Flatshading enabled\n" );
flat = true;
}
else if( !strcmp( argv[ i ], "-meta" ) )
{
MsgDev( D_INFO, "Creating meta surfaces from brush faces\n" );
meta = true;
}
else if( !strcmp( argv[ i ], "-patchmeta" ) )
{
MsgDev( D_INFO, "Creating meta surfaces from patches\n" );
patchMeta = true;
}
else if( !strcmp( argv[ i ], "-flares" ) )
{
MsgDev( D_INFO, "Flare surfaces enabled\n" );
emitFlares = true;
}
else if( !strcmp( argv[ i ], "-noflares" ) )
{
MsgDev( D_INFO, "Flare surfaces disabled\n" );
emitFlares = false;
}
else if( !strcmp( argv[ i ], "-skyfix" ) )
{
MsgDev( D_INFO, "GL_CLAMP sky fix/hack/workaround enabled\n" );
skyFixHack = true;
}
else if( !strcmp( argv[ i ], "-debugsurfaces" ) )
{
MsgDev( D_INFO, "emitting debug surfaces\n" );
debugSurfaces = true;
}
else if( !strcmp( argv[ i ], "-debuginset" ) )
{
MsgDev( D_INFO, "Debug surface triangle insetting enabled\n" );
debugInset = true;
}
else if( !strcmp( argv[ i ], "-debugportals" ) )
{
MsgDev( D_INFO, "Debug portal surfaces enabled\n" );
debugPortals = true;
}
else if( !strcmp( argv[ i ], "-bsp" ) )
MsgDev( D_INFO, "-bsp argument unnecessary\n" );
else
MsgDev( D_INFO, "WARNING: Unknown option \"%s\"\n", argv[ i ] );
}
/* fixme: print more useful usage here */
if( i != (argc - 1) )
Sys_Break( "usage: q3map [options] mapfile" );
/* copy source name */
strcpy( source, argv[i] );
FS_StripExtension( source );
/* ydnar: set default sample size */
SetDefaultSampleSize( sampleSize );
/* delete portal, line and surface files */
com.sprintf( path, "%s.prt", source );
remove( path );
com.sprintf( path, "%s.lin", source );
remove( path );
//% com.sprintf( path, "%s.srf", source ); /* ydnar */
//% remove( path );
/* expand mapname */
strcpy( name, argv[i] );
if( strcmp( name + strlen( name ) - 4, ".reg" ) )
{
/* if we are doing a full map, delete the last saved region map */
com.sprintf( path, "%s.reg", source );
remove( path );
FS_DefaultExtension( name, ".map" ); /* might be .reg */
}
/* if onlyents, just grab the entites and resave */
if( onlyents )
{
OnlyEnts();
return 0;
}
/* load shaders */
LoadShaderInfo();
/* load original file from temp spot in case it was renamed by the editor on the way in */
if( strlen( tempSource ) > 0 )
LoadMapFile( tempSource, false );
else
LoadMapFile( name, false );
/* ydnar: decal setup */
ProcessDecals();
/* ydnar: cloned brush model entities */
SetCloneModelNumbers();
/* process world and submodels */
ProcessModels();
/* set light styles from targetted light entities */
SetLightStyles();
/* process in game advertisements */
ProcessAdvertisements();
/* finish and write bsp */
EndBSPFile();
/* remove temp map source file if appropriate */
if( strlen( tempSource ) > 0)
remove( tempSource );
/* return to sender */
return 0;
}

View File

@ -1,666 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2007 ©
// bspfile.c - read\save bsp file
//=======================================================================
#include <stdio.h> // sscanf support
#include "bsplib.h"
#include "entity_def.h"
#include "physic_api.h"
#include "byteorder.h"
#include "const.h"
#define ENTRIES(a) (sizeof(a)/sizeof(*(a)))
#define ENTRYSIZE(a) (sizeof(*(a)))
extern physic_exp_t *pe;
//=============================================================================
wfile_t *wadfile;
dheader_t *header;
int num_entities;
bsp_entity_t entities[MAX_MAP_ENTITIES];
int nummodels;
dmodel_t dmodels[MAX_MAP_MODELS];
int visdatasize;
byte dvisdata[MAX_MAP_VISIBILITY];
dvis_t *dvis = (dvis_t *)dvisdata;
int lightdatasize;
byte dlightdata[MAX_MAP_LIGHTING];
int entdatasize;
char dentdata[MAX_MAP_ENTSTRING];
int numleafs;
dleaf_t dleafs[MAX_MAP_LEAFS];
int numplanes;
dplane_t dplanes[MAX_MAP_PLANES];
int numvertexes;
dvertex_t dvertexes[MAX_MAP_VERTS];
int numnodes;
dnode_t dnodes[MAX_MAP_NODES];
int numtexinfo;
dtexinfo_t texinfo[MAX_MAP_TEXINFO];
int numsurfaces;
dsurface_t dsurfaces[MAX_MAP_SURFACES];
int numedges;
dedge_t dedges[MAX_MAP_EDGES];
int numleafsurfaces;
dleafface_t dleafsurfaces[MAX_MAP_LEAFFACES];
int numleafbrushes;
dleafbrush_t dleafbrushes[MAX_MAP_LEAFBRUSHES];
int numsurfedges;
dsurfedge_t dsurfedges[MAX_MAP_SURFEDGES];
int numbrushes;
dbrush_t dbrushes[MAX_MAP_BRUSHES];
int numbrushsides;
dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
int numshaders;
dshader_t dshaders[MAX_MAP_SHADERS];
int numareas;
darea_t dareas[MAX_MAP_AREAS];
int numareaportals;
dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
byte dcollision[MAX_MAP_COLLISION];
int dcollisiondatasize;
//=============================================================================
size_t ArrayUsage( char *szItem, int items, int maxitems, int itemsize )
{
float percentage = maxitems ? items * 100.0 / maxitems : 0.0;
MsgDev( D_INFO, "%-12s %7i/%-7i %7i/%-7i (%4.1f%%)", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage );
if( percentage > 80.0 ) MsgDev( D_INFO, "VERY FULL!\n" );
else if( percentage > 95.0 ) MsgDev( D_INFO, "SIZE DANGER!\n" );
else if( percentage > 99.9 ) MsgDev( D_INFO, "SIZE OVERFLOW!!!\n" );
else MsgDev( D_INFO, "\n" );
return items * itemsize;
}
size_t GlobUsage( char *szItem, int itemstorage, int maxstorage )
{
float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0;
MsgDev( D_INFO, "%-12s [variable] %7i/%-7i (%4.1f%%)", szItem, itemstorage, maxstorage, percentage );
if( percentage > 80.0 ) MsgDev( D_INFO, "VERY FULL!\n" );
else if( percentage > 95.0 ) MsgDev( D_INFO, "SIZE DANGER!\n" );
else if( percentage > 99.9 ) MsgDev( D_INFO, "SIZE OVERFLOW!!!\n" );
else MsgDev( D_INFO, "\n" );
return itemstorage;
}
/*
=============
PrintBSPFileSizes
Dumps info about current file
=============
*/
void PrintBSPFileSizes( void )
{
int totalmemory = 0;
MsgDev( D_INFO, "\n" );
MsgDev( D_INFO, "Object names Objects/Maxobjs Memory / Maxmem Fullness\n" );
MsgDev( D_INFO, "------------ --------------- --------------- --------\n" );
// struct arrays
totalmemory += ArrayUsage( "shaders", numshaders, ENTRIES( dshaders ), ENTRYSIZE( dshaders ));
totalmemory += ArrayUsage( "planes", numplanes, ENTRIES( dplanes ), ENTRYSIZE( dplanes ));
totalmemory += ArrayUsage( "leafs", numleafs, ENTRIES( dleafs ), ENTRYSIZE( dleafs ));
totalmemory += ArrayUsage( "leaffaces", numleafsurfaces, ENTRIES( dleafsurfaces ), ENTRYSIZE( dleafsurfaces ));
totalmemory += ArrayUsage( "leafbrushes", numleafbrushes, ENTRIES( dleafbrushes ), ENTRYSIZE( dleafbrushes ));
totalmemory += ArrayUsage( "nodes", numnodes, ENTRIES( dnodes ), ENTRYSIZE( dnodes ));
totalmemory += ArrayUsage( "vertexes", numvertexes, ENTRIES( dvertexes ), ENTRYSIZE( dvertexes ));
totalmemory += ArrayUsage( "edges", numedges, ENTRIES( dedges ), ENTRYSIZE( dedges ));
totalmemory += ArrayUsage( "surfedges", numsurfedges, ENTRIES( dsurfedges ), ENTRYSIZE( dsurfedges ));
totalmemory += ArrayUsage( "texinfos", numtexinfo, ENTRIES( texinfo ), ENTRYSIZE( texinfo ));
totalmemory += ArrayUsage( "surfaces", numsurfaces, ENTRIES( dsurfaces ), ENTRYSIZE( dsurfaces ));
totalmemory += ArrayUsage( "models", nummodels, ENTRIES( dmodels ), ENTRYSIZE( dmodels ));
totalmemory += ArrayUsage( "brushes", numbrushes, ENTRIES( dbrushes ), ENTRYSIZE( dbrushes ));
totalmemory += ArrayUsage( "brushsides", numbrushsides, ENTRIES( dbrushsides ), ENTRYSIZE( dbrushsides ));
totalmemory += ArrayUsage( "areas", numareas, ENTRIES( dareas ), ENTRYSIZE( dareas ));
totalmemory += ArrayUsage( "areaportals", numareaportals, ENTRIES( dareaportals ), ENTRYSIZE( dareaportals ));
// byte arrays
totalmemory += GlobUsage( "entities", entdatasize, sizeof( dentdata ));
totalmemory += GlobUsage( "lightdata", lightdatasize, sizeof( dlightdata ));
totalmemory += GlobUsage( "visdata", visdatasize, sizeof( dvisdata ));
totalmemory += GlobUsage( "collision", dcollisiondatasize, sizeof( dcollision ));
MsgDev( D_INFO, "=== Total BSP file data space used: %d bytes ===\n", totalmemory );
}
/*
=============
SwapBSPFile
Byte swaps all data in a bsp file.
=============
*/
void SwapBSPFile( bool todisk )
{
int i, j;
// models
SwapBlock((int *)dmodels, nummodels * sizeof( dmodels[0] ));
// vertexes
SwapBlock( (int *)dvertexes, numvertexes * sizeof( dvertexes[0] ));
// planes
SwapBlock( (int *)dplanes, numplanes * sizeof( dplanes[0] ));
// texinfos
SwapBlock( (int *)texinfo, numtexinfo * sizeof( texinfo[0] ));
// nodes
SwapBlock( (int *)dnodes, numnodes * sizeof( dnodes[0] ));
// leafs
SwapBlock( (int *)dleafs, numleafs * sizeof( dleafs[0] ));
// leaffaces
SwapBlock( (int *)dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ));
// leafbrushes
SwapBlock( (int *)dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ));
// surfedges
SwapBlock( (int *)dsurfedges, numsurfedges * sizeof( dsurfedges[0] ));
// edges
SwapBlock( (int *)dedges, numedges * sizeof( dedges[0] ));
// brushes
SwapBlock( (int *)dbrushes, numbrushes * sizeof( dbrushes[0] ));
// areas
SwapBlock( (int *)dareas, numareas * sizeof( dareas[0] ));
// areasportals
SwapBlock( (int *)dareaportals, numareaportals * sizeof( dareaportals[0] ));
// brushsides
SwapBlock( (int *)dbrushsides, numbrushsides * sizeof( dbrushsides[0] ));
// shaders
for( i = 0; i < numshaders; i++ )
{
dshaders[i].size[0] = LittleLong( dshaders[i].size[0] );
dshaders[i].size[1] = LittleLong( dshaders[i].size[1] );
dshaders[i].surfaceFlags = LittleLong( dshaders[i].surfaceFlags );
dshaders[i].contentFlags = LittleLong( dshaders[i].contentFlags );
}
// faces
for( i = 0; i < numsurfaces; i++ )
{
dsurfaces[i].planenum = LittleLong( dsurfaces[i].planenum );
dsurfaces[i].side = LittleLong (dsurfaces[i].side);
dsurfaces[i].firstedge = LittleLong( dsurfaces[i].firstedge );
dsurfaces[i].numedges = LittleLong( dsurfaces[i].numedges );
dsurfaces[i].texinfo = LittleLong( dsurfaces[i].texinfo );
dsurfaces[i].lightofs = LittleLong( dsurfaces[i].lightofs );
}
// visibility
if( todisk ) j = dvis->numclusters;
else j = LittleLong( dvis->numclusters );
dvis->numclusters = LittleLong( dvis->numclusters );
for( i = 0; i < j; i++ )
{
dvis->bitofs[i][0] = LittleLong( dvis->bitofs[i][0] );
dvis->bitofs[i][1] = LittleLong( dvis->bitofs[i][1] );
}
}
bool CompressLump( const char *lumpname, size_t length )
{
if( !bsplib_compress_bsp->integer )
return false;
if( !com.strcmp( lumpname, LUMP_MAPINFO ))
return false;
if( !com.strcmp( lumpname, LUMP_ENTITIES ))
return false; // never compress entities
if( !com.strcmp( lumpname, LUMP_COLLISION ))
return true;
if( !com.strcmp( lumpname, LUMP_VISIBILITY ))
return true;
if( !com.strcmp( lumpname, LUMP_LIGHTING ))
return true;
// other lumps can be compressed if their size more than 32 kBytes
if( length > 0x8000 )
return true;
return false;
}
char TypeForLump( const char *lumpname )
{
if( !com.strcmp( lumpname, LUMP_ENTITIES ))
return TYPE_SCRIPT;
return TYPE_BINDATA;
}
size_t CopyLump( const char *lumpname, void *dest, size_t block_size )
{
size_t length;
byte *in;
if( !wadfile ) return 0;
in = WAD_Read( wadfile, lumpname, &length, TypeForLump( lumpname ));
if( !in ) return 0; // empty lump
if( length % block_size ) Sys_Break( "LoadBSPFile: %s funny lump size\n", lumpname );
Mem_Copy( dest, in, length );
return length / block_size;
}
void AddLump( const char *lumpname, const void *data, size_t length )
{
int compress = CMP_NONE;
if( !wadfile || !length ) return;
compress = CompressLump( lumpname, length ) ? CMP_ZLIB : CMP_NONE;
WAD_Write( wadfile, lumpname, data, (length + 3) & ~3, TypeForLump( lumpname ), compress );
}
/*
=============
LoadBSPFile
=============
*/
bool LoadBSPFile( void )
{
static dheader_t inheader;
header = &inheader;
wadfile = WAD_Open( va("maps/%s.bsp", gs_filename ), "rb" );
if( !wadfile ) return false;
CopyLump( LUMP_MAPINFO, header, 1 );
if( pe ) pe->LoadBSP( wadfile );
MsgDev( D_NOTE, "reading %s.bsp\n", gs_filename );
// swap the header
header->ident = LittleLong( header->ident );
header->version = LittleLong( header->version );
if( header->ident != IDBSPMODHEADER )
Sys_Break( "%s.bsp is not a IBSP file\n", gs_filename );
if( header->version != BSPMOD_VERSION )
Sys_Break( "%s.bsp is version %i, not %i\n", gs_filename, header->version, BSPMOD_VERSION );
entdatasize = CopyLump( LUMP_ENTITIES, dentdata, 1 );
numplanes = CopyLump( LUMP_PLANES, dplanes, sizeof( dplanes[0] ));
numvertexes = CopyLump( LUMP_VERTEXES, dvertexes, sizeof( dvertexes[0] ));
visdatasize = CopyLump( LUMP_VISIBILITY, dvisdata, 1 );
numnodes = CopyLump( LUMP_NODES, dnodes, sizeof( dnodes[0] ));
numtexinfo = CopyLump( LUMP_TEXINFO, texinfo, sizeof( texinfo[0] ));
numsurfaces = CopyLump( LUMP_SURFACES, dsurfaces, sizeof( dsurfaces[0] ));
lightdatasize = CopyLump( LUMP_LIGHTING, dlightdata, 1 );
numleafs = CopyLump( LUMP_LEAFS, dleafs, sizeof( dleafs[0] ));
numleafsurfaces = CopyLump( LUMP_LEAFFACES, dleafsurfaces, sizeof( dleafsurfaces[0] ));
numleafbrushes = CopyLump( LUMP_LEAFBRUSHES, dleafbrushes, sizeof( dleafbrushes[0] ));
numedges = CopyLump( LUMP_EDGES, dedges, sizeof( dedges[0] ));
numsurfedges = CopyLump( LUMP_SURFEDGES, dsurfedges, sizeof( dsurfedges[0] ));
nummodels = CopyLump( LUMP_MODELS, dmodels, sizeof( dmodels[0] ));
numbrushes = CopyLump( LUMP_BRUSHES, dbrushes, sizeof( dbrushes[0] ));
numbrushsides = CopyLump( LUMP_BRUSHSIDES, dbrushsides, sizeof( dbrushsides[0] ));
dcollisiondatasize = CopyLump( LUMP_COLLISION, dcollision, 1 );
numshaders = CopyLump( LUMP_SHADERS, dshaders, sizeof( dshaders[0] ));
numareas = CopyLump ( LUMP_AREAS, dareas, sizeof( dareas[0] ));
numareaportals = CopyLump( LUMP_AREAPORTALS, dareaportals, sizeof( dareaportals[0] ));
WAD_Close( wadfile ); // release memory
wadfile = NULL;
// swap everything
SwapBSPFile( false );
return true;
}
//============================================================================
/*
=============
WriteBSPFile
Swaps the bsp file in place, so it should not be referenced again
=============
*/
void WriteBSPFile( void )
{
static dheader_t outheader;
header = &outheader;
Mem_Set( header, 0, sizeof( dheader_t ));
SwapBSPFile( true );
header->ident = LittleLong( IDBSPMODHEADER );
header->version = LittleLong( BSPMOD_VERSION );
FindMapMessage( header->message );
MsgDev( D_NOTE, "\n\nwriting %s.bsp\n", gs_filename );
if( pe ) pe->FreeBSP();
wadfile = WAD_Open( va( "maps/%s.bsp", gs_filename ), "wb" );
AddLump( LUMP_MAPINFO, header, sizeof( dheader_t ));
AddLump( LUMP_ENTITIES, dentdata, entdatasize );
AddLump( LUMP_PLANES, dplanes, numplanes * sizeof( dplanes[0] ));
AddLump( LUMP_VERTEXES, dvertexes, numvertexes * sizeof( dvertexes[0] ));
AddLump( LUMP_VISIBILITY, dvisdata, visdatasize );
AddLump( LUMP_NODES, dnodes, numnodes * sizeof( dnodes[0] ));
AddLump( LUMP_TEXINFO, texinfo, numtexinfo * sizeof( texinfo[0] ));
AddLump( LUMP_SURFACES, dsurfaces, numsurfaces * sizeof( dsurfaces[0] ));
AddLump( LUMP_LIGHTING, dlightdata, lightdatasize );
AddLump( LUMP_LEAFS, dleafs, numleafs * sizeof( dleafs[0] ));
AddLump( LUMP_LEAFFACES, dleafsurfaces, numleafsurfaces * sizeof( dleafsurfaces[0] ));
AddLump( LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes * sizeof( dleafbrushes[0] ));
AddLump( LUMP_EDGES, dedges, numedges * sizeof( dedges[0] ));
AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges * sizeof( dsurfedges[0] ));
AddLump( LUMP_MODELS, dmodels, nummodels * sizeof( dmodels[0] ));
AddLump( LUMP_BRUSHES, dbrushes, numbrushes * sizeof( dbrushes[0] ));
AddLump( LUMP_BRUSHSIDES, dbrushsides, numbrushsides * sizeof( dbrushsides[0] ));
AddLump( LUMP_COLLISION, dcollision, dcollisiondatasize );
AddLump( LUMP_SHADERS, dshaders, numshaders * sizeof( dshaders[0] ));
AddLump( LUMP_AREAS, dareas, numareas * sizeof( dareas[0] ));
AddLump( LUMP_AREAPORTALS, dareaportals, numareaportals * sizeof( dareaportals[0] ));
WAD_Close( wadfile );
wadfile = NULL;
}
//============================================
// misc parse functions
//============================================
void StripTrailing( char *e )
{
char *s;
s = e + com.strlen( e ) - 1;
while( s >= e && *s <= 32 )
{
*s = 0;
s--;
}
}
/*
=================
ParseEpair
=================
*/
epair_t *ParseEpair( script_t *script, token_t *token )
{
epair_t *e;
e = Malloc( sizeof( epair_t ));
if( com.strlen( token->string ) >= MAX_KEY - 1 )
Sys_Break( "ParseEpair: token too long\n" );
e->key = copystring( token->string );
Com_ReadToken( script, SC_PARSE_GENERIC, token );
if( com.strlen( token->string ) >= MAX_VALUE - 1 )
Sys_Break( "ParseEpair: token too long\n" );
e->value = copystring( token->string );
// strip trailing spaces
StripTrailing( e->key );
StripTrailing( e->value );
return e;
}
/*
================
ParseEntity
================
*/
bool ParseEntity( void )
{
epair_t *e;
token_t token;
bsp_entity_t *mapent;
if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES, &token ))
return false;
if( com.stricmp( token.string, "{" )) Sys_Break( "ParseEntity: '{' not found\n" );
if( num_entities == MAX_MAP_ENTITIES ) Sys_Break( "MAX_MAP_ENTITIES limit excceded\n" );
mapent = &entities[num_entities];
num_entities++;
while( 1 )
{
if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token ))
Sys_Break( "ParseEntity: EOF without closing brace\n" );
if( !com.stricmp( token.string, "}" )) break;
e = ParseEpair( mapfile, &token );
e->next = mapent->epairs;
mapent->epairs = e;
}
return true;
}
/*
================
ParseEntities
Parses the dentdata string into entities
================
*/
void ParseEntities( void )
{
num_entities = 0;
mapfile = Com_OpenScript( "entities", dentdata, entdatasize );
if( mapfile )
{
while( ParseEntity( ));
Com_CloseScript( mapfile );
}
}
/*
================
UnparseEntities
Generates the dentdata string from all the entities
================
*/
void UnparseEntities( void )
{
epair_t *ep;
char *buf, *end;
char line[2048];
char key[MAX_KEY], value[MAX_VALUE];
int i;
buf = dentdata;
end = buf;
*end = 0;
for( i = 0; i < num_entities; i++ )
{
ep = entities[i].epairs;
if( !ep ) continue; // ent got removed
com.strcat( end, "{\n" );
end += 2;
for( ep = entities[i].epairs; ep; ep = ep->next )
{
com.strncpy( key, ep->key, MAX_KEY );
StripTrailing( key );
com.strncpy( value, ep->value, MAX_VALUE );
StripTrailing( value );
com.snprintf( line, 2048, "\"%s\" \"%s\"\n", key, value );
com.strcat( end, line );
end += com.strlen( line );
}
com.strcat( end, "}\n" );
end += 2;
if( end > buf + MAX_MAP_ENTSTRING )
Sys_Break( "Entity text too long\n" );
}
entdatasize = end - buf + 1;
}
void SetKeyValue( bsp_entity_t *ent, const char *key, const char *value )
{
epair_t *ep;
for( ep = ent->epairs; ep; ep = ep->next )
{
if(!com.strcmp( ep->key, key ))
{
Mem_Free( ep->value );
ep->value = copystring( value );
return;
}
}
ep = Malloc( sizeof( *ep ));
ep->next = ent->epairs;
ent->epairs = ep;
ep->key = copystring( key );
ep->value = copystring( value );
}
char *ValueForKey( const bsp_entity_t *ent, const char *key )
{
epair_t *ep;
if( !ent ) return "";
for( ep = ent->epairs; ep; ep = ep->next )
{
if(!com.strcmp( ep->key, key ))
return ep->value;
}
return "";
}
long IntForKey( const bsp_entity_t *ent, const char *key )
{
char *k;
k = ValueForKey( ent, key );
return com.atoi( k );
}
vec_t FloatForKey( const bsp_entity_t *ent, const char *key )
{
char *k;
k = ValueForKey( ent, key );
return com.atof( k );
}
void GetVectorForKey( const bsp_entity_t *ent, const char *key, vec3_t vec )
{
char *k;
double v1, v2, v3;
k = ValueForKey( ent, key );
// scanf into doubles, then assign, so it is vec_t size independent
v1 = v2 = v3 = 0;
sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
VectorSet( vec, v1, v2, v3 );
}
/*
================
FindTargetEntity
finds an entity target
================
*/
bsp_entity_t *FindTargetEntity( const char *target )
{
int i;
const char *n;
// walk entity list
for( i = 0; i < num_entities; i++ )
{
n = ValueForKey( &entities[i], "targetname" );
if( !com.strcmp( n, target ))
return &entities[i];
}
return NULL;
}
/*
================
FindTargetEntity
finds an entity target
================
*/
void FindMapMessage( char *message )
{
int i;
const char *value;
bsp_entity_t *e;
if( !message ) return;
// walk entity list
for( i = 0; i < num_entities; i++ )
{
e = &entities[i];
value = ValueForKey( e, "classname" );
if( !com.stricmp( value, "worldspawn" ))
{
value = ValueForKey( e, "message" );
com.strncpy( message, value, MAX_SHADERPATH );
return;
}
}
com.strncpy( message, "", MAX_SHADERPATH );
}
void Com_CheckToken( script_t *script, const char *match )
{
token_t token;
Com_ReadToken( script, SC_ALLOW_NEWLINES, &token );
if( com.stricmp( token.string, match ))
{
Sys_Break( "Com_CheckToken: \"%s\" not found\n", match );
}
}
void Com_Parse1DMatrix( script_t *script, int x, vec_t *m )
{
int i;
Com_CheckToken( script, "(" );
for( i = 0; i < x; i++ )
Com_ReadFloat( script, false, &m[i] );
Com_CheckToken( script, ")" );
}
void Com_Parse2DMatrix( script_t *script, int y, int x, vec_t *m )
{
int i;
Com_CheckToken( script, "(" );
for( i = 0; i < y; i++ )
Com_Parse1DMatrix( script, x, m+i*x );
Com_CheckToken( script, ")" );
}

View File

@ -0,0 +1,794 @@
/* -------------------------------------------------------------------------------
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 Mem_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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSPFILE_ABSTRACT_C
/* dependencies */
#include "q3map2.h"
#include "byteorder.h"
#include "stdio.h" // rename support, FIXME, implement rename into launch.dll
/* -------------------------------------------------------------------------------
this file was copied out of the common directory in order to not break
compatibility with the q3map 1.x tree. it was moved out in order to support
the raven bsp format (RBSP) used in soldier of fortune 2 and jedi knight 2.
since each game has its own set of particular features, the data structures
below no longer directly correspond to the binary format of a particular game.
the translation will be done at bsp load/save time to keep any sort of
special-case code messiness out of the rest of the program.
------------------------------------------------------------------------------- */
/* FIXME: remove the functions below that handle memory management of bsp file chunks */
int numBSPDrawVertsBuffer = 0;
void IncDrawVerts( void )
{
numBSPDrawVerts++;
if( bspDrawVerts == 0 )
{
numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS / 37;
bspDrawVerts = Malloc( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer );
}
else if( numBSPDrawVerts > numBSPDrawVertsBuffer )
{
numBSPDrawVertsBuffer *= 3; // multiply by 1.5
numBSPDrawVertsBuffer /= 2;
if(numBSPDrawVertsBuffer > MAX_MAP_DRAW_VERTS)
numBSPDrawVertsBuffer = MAX_MAP_DRAW_VERTS;
bspDrawVerts = Realloc( bspDrawVerts, sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer );
}
}
void SetDrawVerts(int n)
{
if(bspDrawVerts != 0)
Mem_Free(bspDrawVerts);
numBSPDrawVerts = n;
numBSPDrawVertsBuffer = numBSPDrawVerts;
bspDrawVerts = Malloc( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer );
}
int numBSPDrawSurfacesBuffer = 0;
void SetDrawSurfacesBuffer()
{
if(bspDrawSurfaces != 0)
Mem_Free(bspDrawSurfaces);
numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
bspDrawSurfaces = Malloc( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer );
}
void SetDrawSurfaces(int n)
{
if( bspDrawSurfaces != 0 )
Mem_Free( bspDrawSurfaces );
numBSPDrawSurfaces = n;
numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
bspDrawSurfaces = Malloc( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer );
}
void BSPFilesCleanup()
{
if(bspDrawVerts != 0)
Mem_Free(bspDrawVerts);
if(bspDrawSurfaces != 0)
Mem_Free(bspDrawSurfaces);
if(bspLightBytes != 0)
Mem_Free(bspLightBytes);
if(bspGridPoints != 0)
Mem_Free(bspGridPoints);
}
/*
SwapBSPFile()
byte swaps all data in the abstract bsp
*/
void SwapBSPFile( void )
{
int i, j;
/* models */
SwapBlock( (int*) bspModels, numBSPModels * sizeof( bspModels[ 0 ] ) );
/* shaders (don't swap the name) */
for( i = 0; i < numBSPShaders ; i++ )
{
bspShaders[ i ].contentFlags = LittleLong( bspShaders[ i ].contentFlags );
bspShaders[ i ].surfaceFlags = LittleLong( bspShaders[ i ].surfaceFlags );
}
/* planes */
SwapBlock( (int*) bspPlanes, numBSPPlanes * sizeof( bspPlanes[ 0 ] ) );
/* nodes */
SwapBlock( (int*) bspNodes, numBSPNodes * sizeof( bspNodes[ 0 ] ) );
/* leafs */
SwapBlock( (int*) bspLeafs, numBSPLeafs * sizeof( bspLeafs[ 0 ] ) );
/* leaffaces */
SwapBlock( (int*) bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
/* leafbrushes */
SwapBlock( (int*) bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
// brushes
SwapBlock( (int*) bspBrushes, numBSPBrushes * sizeof( bspBrushes[ 0 ] ) );
// brushsides
SwapBlock( (int*) bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
// vis
((int*) &bspVisBytes)[ 0 ] = LittleLong( ((int*) &bspVisBytes)[ 0 ] );
((int*) &bspVisBytes)[ 1 ] = LittleLong( ((int*) &bspVisBytes)[ 1 ] );
/* drawverts (don't swap colors) */
for( i = 0; i < numBSPDrawVerts; i++ )
{
bspDrawVerts[ i ].xyz[ 0 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 0 ] );
bspDrawVerts[ i ].xyz[ 1 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 1 ] );
bspDrawVerts[ i ].xyz[ 2 ] = LittleFloat( bspDrawVerts[ i ].xyz[ 2 ] );
bspDrawVerts[ i ].normal[ 0 ] = LittleFloat( bspDrawVerts[ i ].normal[ 0 ] );
bspDrawVerts[ i ].normal[ 1 ] = LittleFloat( bspDrawVerts[ i ].normal[ 1 ] );
bspDrawVerts[ i ].normal[ 2 ] = LittleFloat( bspDrawVerts[ i ].normal[ 2 ] );
bspDrawVerts[ i ].st[ 0 ] = LittleFloat( bspDrawVerts[ i ].st[ 0 ] );
bspDrawVerts[ i ].st[ 1 ] = LittleFloat( bspDrawVerts[ i ].st[ 1 ] );
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
bspDrawVerts[ i ].lightmap[ j ][ 0 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 0 ] );
bspDrawVerts[ i ].lightmap[ j ][ 1 ] = LittleFloat( bspDrawVerts[ i ].lightmap[ j ][ 1 ] );
}
}
/* drawindexes */
SwapBlock( (int*) bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[0] ) );
/* drawsurfs */
/* note: rbsp files (and hence q3map2 abstract bsp) have byte lightstyles index arrays, this follows sof2map convention */
SwapBlock( (int*) bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
/* fogs */
for( i = 0; i < numBSPFogs; i++ )
{
bspFogs[ i ].brushNum = LittleLong( bspFogs[ i ].brushNum );
bspFogs[ i ].visibleSide = LittleLong( bspFogs[ i ].visibleSide );
}
/* advertisements */
for( i = 0; i < numBSPAds; i++ )
{
bspAds[ i ].cellId = LittleLong( bspAds[ i ].cellId );
bspAds[ i ].normal[ 0 ] = LittleFloat( bspAds[ i ].normal[ 0 ] );
bspAds[ i ].normal[ 1 ] = LittleFloat( bspAds[ i ].normal[ 1 ] );
bspAds[ i ].normal[ 2 ] = LittleFloat( bspAds[ i ].normal[ 2 ] );
for( j = 0; j < 4; j++ )
{
bspAds[ i ].rect[j][ 0 ] = LittleFloat( bspAds[ i ].rect[j][ 0 ] );
bspAds[ i ].rect[j][ 1 ] = LittleFloat( bspAds[ i ].rect[j][ 1 ] );
bspAds[ i ].rect[j][ 2 ] = LittleFloat( bspAds[ i ].rect[j][ 2 ] );
}
//bspAds[ i ].model[ MAX_QPATH ];
}
}
/*
GetLumpElements()
gets the number of elements in a bsp lump
*/
int GetLumpElements( bspHeader_t *header, int lump, int size )
{
/* check for odd size */
if( header->lumps[ lump ].length % size )
{
if( force )
{
MsgDev( D_WARN, "GetLumpElements: odd lump size (%d) in lump %d\n", header->lumps[ lump ].length, lump );
return 0;
}
else Sys_Break( "GetLumpElements: odd lump size (%d) in lump %d", header->lumps[ lump ].length, lump );
}
/* return element count */
return header->lumps[ lump ].length / size;
}
/*
GetLump()
returns a pointer to the specified lump
*/
void *GetLump( bspHeader_t *header, int lump )
{
return (void*)( (byte*) header + header->lumps[ lump ].offset);
}
/*
CopyLump()
copies a bsp file lump into a destination buffer
*/
int CopyLump( bspHeader_t *header, int lump, void *dest, int size )
{
int length, offset;
/* get lump length and offset */
length = header->lumps[ lump ].length;
offset = header->lumps[ lump ].offset;
/* handle erroneous cases */
if( length == 0 )
return 0;
if( length % size )
{
if( force )
{
MsgDev( D_WARN, "CopyLump: odd lump size (%d) in lump %d\n", length, lump );
return 0;
}
else Sys_Break( "CopyLump: odd lump size (%d) in lump %d", length, lump );
}
/* copy block of memory and return */
memcpy( dest, (byte*) header + offset, length );
return length / size;
}
/*
AddLump()
adds a lump to an outgoing bsp file
*/
void AddLump( file_t *file, bspHeader_t *header, int lumpNum, const void *data, int length )
{
bspLump_t *lump;
/* add lump to bsp file header */
lump = &header->lumps[ lumpNum ];
lump->offset = LittleLong( FS_Tell( file ) );
lump->length = LittleLong( length );
/* write lump to file */
FS_Write( file, data, (length + 3) & ~3 );
}
/*
LoadBSPFile()
loads a bsp file into memory
*/
void LoadBSPFile( const char *filename )
{
/* dummy check */
if( game == NULL || game->load == NULL )
Sys_Break( "LoadBSPFile: unsupported BSP file format" );
/* load it, then byte swap the in-memory version */
game->load( filename );
SwapBSPFile();
}
/*
WriteBSPFile()
writes a bsp file
*/
void WriteBSPFile( const char *filename )
{
char tempname[ 1024 ];
time_t tm;
/* dummy check */
if( game == NULL || game->write == NULL )
Sys_Break( "WriteBSPFile: unsupported BSP file format" );
/* make fake temp name so existing bsp file isn't damaged in case write process fails */
time( &tm );
com.sprintf( tempname, "%s.%08X", filename, (int) tm );
/* byteswap, write the bsp, then swap back so it can be manipulated further */
SwapBSPFile();
game->write( tempname );
SwapBSPFile();
/* replace existing bsp file */
FS_Delete( filename );
rename( tempname, filename );
}
/*
PrintBSPFileSizes()
dumps info about current file
*/
void PrintBSPFileSizes( void )
{
/* parse entities first */
if( numEntities <= 0 )
ParseEntities();
/* note that this is abstracted */
Msg( "Abstracted BSP file components (*actual sizes may differ)\n" );
/* print various and sundry bits */
Msg( "%9d models %9d\n", numBSPModels, (int) (numBSPModels * sizeof( bspModel_t )) );
Msg( "%9d shaders %9d\n", numBSPShaders, (int) (numBSPShaders * sizeof( bspShader_t )) );
Msg( "%9d brushes %9d\n", numBSPBrushes, (int) (numBSPBrushes * sizeof( bspBrush_t )) );
Msg( "%9d brushsides %9d *\n", numBSPBrushSides, (int) (numBSPBrushSides * sizeof( bspBrushSide_t )) );
Msg( "%9d fogs %9d\n", numBSPFogs, (int) (numBSPFogs * sizeof( bspFog_t ) ) );
Msg( "%9d planes %9d\n", numBSPPlanes, (int) (numBSPPlanes * sizeof( bspPlane_t )) );
Msg( "%9d entdata %9d\n", numEntities, bspEntDataSize );
Msg( "\n");
Msg( "%9d nodes %9d\n", numBSPNodes, (int) (numBSPNodes * sizeof( bspNode_t)) );
Msg( "%9d leafs %9d\n", numBSPLeafs, (int) (numBSPLeafs * sizeof( bspLeaf_t )) );
Msg( "%9d leafsurfaces %9d\n", numBSPLeafSurfaces, (int) (numBSPLeafSurfaces * sizeof( *bspLeafSurfaces )) );
Msg( "%9d leafbrushes %9d\n", numBSPLeafBrushes, (int) (numBSPLeafBrushes * sizeof( *bspLeafBrushes )) );
Msg( "\n");
Msg( "%9d drawsurfaces %9d *\n", numBSPDrawSurfaces, (int) (numBSPDrawSurfaces * sizeof( *bspDrawSurfaces )) );
Msg( "%9d drawverts %9d *\n", numBSPDrawVerts, (int) (numBSPDrawVerts * sizeof( *bspDrawVerts )) );
Msg( "%9d drawindexes %9d\n", numBSPDrawIndexes, (int) (numBSPDrawIndexes * sizeof( *bspDrawIndexes )) );
Msg( "\n");
Msg( "%9d lightmaps %9d\n", numBSPLightBytes / (game->lightmapSize * game->lightmapSize * 3), numBSPLightBytes );
Msg( "%9d lightgrid %9d *\n", numBSPGridPoints, (int) (numBSPGridPoints * sizeof( *bspGridPoints )) );
Msg( " visibility %9d\n", numBSPVisBytes );
}
/* -------------------------------------------------------------------------------
entity data handling
------------------------------------------------------------------------------- */
/*
StripTrailing()
strips low byte chars off the end of a string
*/
void StripTrailing( char *e )
{
char *s;
s = e + strlen( e ) - 1;
while( s >= e && *s <= 32 )
{
*s = 0;
s--;
}
}
/*
ParseEpair()
parses a single quoted "key" "value" pair into an epair struct
*/
epair_t *ParseEPair( void )
{
epair_t *e;
/* allocate and clear new epair */
e = Malloc( sizeof( epair_t ) );
/* handle key */
if( strlen( token ) >= (MAX_KEY - 1) )
Sys_Break( "ParseEPair: token too long" );
e->key = copystring( token );
GetToken( false );
/* handle value */
if( strlen( token ) >= MAX_VALUE - 1 )
Sys_Break( "ParseEpar: token too long" );
e->value = copystring( token );
/* strip trailing spaces that sometimes get accidentally added in the editor */
StripTrailing( e->key );
StripTrailing( e->value );
/* return it */
return e;
}
/*
ParseEntity()
parses an entity's epairs
*/
bool ParseEntity( void )
{
epair_t *e;
/* dummy check */
if( !GetToken( true ) )
return false;
if( strcmp( token, "{" ) )
Sys_Break( "ParseEntity: { not found" );
if( numEntities == MAX_MAP_ENTITIES )
Sys_Break( "numEntities == MAX_MAP_ENTITIES" );
/* create new entity */
mapEnt = &entities[ numEntities ];
numEntities++;
/* parse */
while( 1 )
{
if( !GetToken( true ) )
Sys_Break( "ParseEntity: EOF without closing brace" );
if( !EPAIR_STRCMP( token, "}" ) )
break;
e = ParseEPair();
e->next = mapEnt->epairs;
mapEnt->epairs = e;
}
/* return to sender */
return true;
}
/*
ParseEntities()
parses the bsp entity data string into entities
*/
void ParseEntities( void )
{
numEntities = 0;
ParseFromMemory( bspEntData, bspEntDataSize );
while( ParseEntity() );
/* ydnar: set number of bsp entities in case a map is loaded on top */
numBSPEntities = numEntities;
}
/*
UnparseEntities()
generates the dentdata string from all the entities.
this allows the utilities to add or remove key/value
pairs to the data created by the map editor
*/
void UnparseEntities( void )
{
int i;
char *buf, *end;
epair_t *ep;
char line[ 2048 ];
char key[ 1024 ], value[ 1024 ];
const char *value2;
/* setup */
buf = bspEntData;
end = buf;
*end = 0;
/* run through entity list */
for( i = 0; i < numBSPEntities && i < numEntities; i++ )
{
/* get epair */
ep = entities[ i ].epairs;
if( ep == NULL )
continue; /* ent got removed */
/* ydnar: certain entities get stripped from bsp file */
value2 = ValueForKey( &entities[ i ], "classname" );
if( !com.stricmp( value2, "misc_model" ) ||
!com.stricmp( value2, "_decal" ) ||
!com.stricmp( value2, "_skybox" ) )
continue;
/* add beginning brace */
strcat( end, "{\n" );
end += 2;
/* walk epair list */
for( ep = entities[ i ].epairs; ep != NULL; ep = ep->next )
{
/* copy and clean */
strcpy( key, ep->key );
StripTrailing( key );
strcpy( value, ep->value );
StripTrailing( value );
/* add to buffer */
com.sprintf( line, "\"%s\" \"%s\"\n", key, value );
strcat( end, line );
end += strlen( line );
}
/* add trailing brace */
strcat( end,"}\n" );
end += 2;
/* check for overflow */
if( end > buf + MAX_MAP_ENTSTRING )
Sys_Break( "Entity text too long" );
}
/* set size */
bspEntDataSize = end - buf + 1;
}
/*
PrintEntity()
prints an entity's epairs to the console
*/
void PrintEntity( const entity_t *ent )
{
epair_t *ep;
Msg( "------- entity %p -------\n", ent );
for( ep = ent->epairs; ep != NULL; ep = ep->next )
Msg( "%s = %s\n", ep->key, ep->value );
}
/*
SetKeyValue()
sets an epair in an entity
*/
void SetKeyValue( entity_t *ent, const char *key, const char *value )
{
epair_t *ep;
/* check for existing epair */
for( ep = ent->epairs; ep != NULL; ep = ep->next )
{
if( !EPAIR_STRCMP( ep->key, key ) )
{
Mem_Free( ep->value );
ep->value = copystring( value );
return;
}
}
/* create new epair */
ep = Malloc( sizeof( *ep ) );
ep->next = ent->epairs;
ent->epairs = ep;
ep->key = copystring( key );
ep->value = copystring( value );
}
/*
ValueForKey()
gets the value for an entity key
*/
const char *ValueForKey( const entity_t *ent, const char *key )
{
epair_t *ep;
/* dummy check */
if( ent == NULL )
return "";
/* walk epair list */
for( ep = ent->epairs; ep != NULL; ep = ep->next )
{
if( !EPAIR_STRCMP( ep->key, key ) )
return ep->value;
}
/* if no match, return empty string */
return "";
}
/*
IntForKey()
gets the integer point value for an entity key
*/
int IntForKey( const entity_t *ent, const char *key )
{
const char *k;
k = ValueForKey( ent, key );
return atoi( k );
}
/*
FloatForKey()
gets the floating point value for an entity key
*/
vec_t FloatForKey( const entity_t *ent, const char *key )
{
const char *k;
k = ValueForKey( ent, key );
return atof( k );
}
/*
GetVectorForKey()
gets a 3-element vector value for an entity key
*/
void GetVectorForKey( const entity_t *ent, const char *key, vec3_t vec )
{
const char *k;
double v1, v2, v3;
/* get value */
k = ValueForKey( ent, key );
/* scanf into doubles, then assign, so it is vec_t size independent */
v1 = v2 = v3 = 0.0;
sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
vec[ 0 ] = v1;
vec[ 1 ] = v2;
vec[ 2 ] = v3;
}
/*
FindTargetEntity()
finds an entity target
*/
entity_t *FindTargetEntity( const char *target )
{
int i;
const char *n;
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
n = ValueForKey( &entities[ i ], "targetname" );
if ( !strcmp( n, target ) )
return &entities[ i ];
}
/* nada */
return NULL;
}
/*
GetEntityShadowFlags() - ydnar
gets an entity's shadow flags
note: does not set them to defaults if the keys are not found!
*/
void GetEntityShadowFlags( const entity_t *ent, const entity_t *ent2, int *castShadows, int *recvShadows )
{
const char *value;
/* get cast shadows */
if( castShadows != NULL )
{
value = ValueForKey( ent, "_castShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent, "_cs" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_castShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_cs" );
if( value[ 0 ] != '\0' )
*castShadows = atoi( value );
}
/* receive */
if( recvShadows != NULL )
{
value = ValueForKey( ent, "_receiveShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent, "_rs" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_receiveShadows" );
if( value[ 0 ] == '\0' )
value = ValueForKey( ent2, "_rs" );
if( value[ 0 ] != '\0' )
*recvShadows = atoi( value );
}
}

View File

@ -0,0 +1,586 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSPFILE_IBSP_C
/* dependencies */
#include "q3map2.h"
#include "byteorder.h"
/* -------------------------------------------------------------------------------
this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.
------------------------------------------------------------------------------- */
/* constants */
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define LUMP_ADVERTISEMENTS 17
#define HEADER_LUMPS 18
/* types */
typedef struct
{
char ident[ 4 ];
int version;
bspLump_t lumps[ HEADER_LUMPS ];
}
ibspHeader_t;
/* brush sides */
typedef struct
{
int planeNum;
int shaderNum;
}
ibspBrushSide_t;
static void CopyBrushSidesLump( ibspHeader_t *header )
{
int i;
ibspBrushSide_t *in;
bspBrushSide_t *out;
/* get count */
numBSPBrushSides = GetLumpElements( (bspHeader_t*) header, LUMP_BRUSHSIDES, sizeof( *in ) );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_BRUSHSIDES );
out = bspBrushSides;
for( i = 0; i < numBSPBrushSides; i++ )
{
out->planeNum = in->planeNum;
out->shaderNum = in->shaderNum;
out->surfaceNum = -1;
in++;
out++;
}
}
static void AddBrushSidesLump( file_t *file, ibspHeader_t *header )
{
int i, size;
bspBrushSide_t *in;
ibspBrushSide_t *buffer, *out;
/* allocate output buffer */
size = numBSPBrushSides * sizeof( *buffer );
buffer = Malloc( size );
/* convert */
in = bspBrushSides;
out = buffer;
for( i = 0; i < numBSPBrushSides; i++ )
{
out->planeNum = in->planeNum;
out->shaderNum = in->shaderNum;
in++;
out++;
}
/* write lump */
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHSIDES, buffer, size );
/* free buffer */
Mem_Free( buffer );
}
/* drawsurfaces */
typedef struct ibspDrawSurface_s
{
int shaderNum;
int fogNum;
int surfaceType;
int firstVert;
int numVerts;
int firstIndex;
int numIndexes;
int lightmapNum;
int lightmapX, lightmapY;
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[ 3 ];
int patchWidth;
int patchHeight;
}
ibspDrawSurface_t;
static void CopyDrawSurfacesLump( ibspHeader_t *header )
{
int i, j;
ibspDrawSurface_t *in;
bspDrawSurface_t *out;
/* get count */
numBSPDrawSurfaces = GetLumpElements( (bspHeader_t*) header, LUMP_SURFACES, sizeof( *in ) );
SetDrawSurfaces( numBSPDrawSurfaces );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_SURFACES );
out = bspDrawSurfaces;
for( i = 0; i < numBSPDrawSurfaces; i++ )
{
out->shaderNum = in->shaderNum;
out->fogNum = in->fogNum;
out->surfaceType = in->surfaceType;
out->firstVert = in->firstVert;
out->numVerts = in->numVerts;
out->firstIndex = in->firstIndex;
out->numIndexes = in->numIndexes;
out->lightmapStyles[ 0 ] = LS_NORMAL;
out->vertexStyles[ 0 ] = LS_NORMAL;
out->lightmapNum[ 0 ] = in->lightmapNum;
out->lightmapX[ 0 ] = in->lightmapX;
out->lightmapY[ 0 ] = in->lightmapY;
for( j = 1; j < MAX_LIGHTMAPS; j++ )
{
out->lightmapStyles[ j ] = LS_NONE;
out->vertexStyles[ j ] = LS_NONE;
out->lightmapNum[ j ] = -3;
out->lightmapX[ j ] = 0;
out->lightmapY[ j ] = 0;
}
out->lightmapWidth = in->lightmapWidth;
out->lightmapHeight = in->lightmapHeight;
VectorCopy( in->lightmapOrigin, out->lightmapOrigin );
VectorCopy( in->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );
VectorCopy( in->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );
VectorCopy( in->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );
out->patchWidth = in->patchWidth;
out->patchHeight = in->patchHeight;
in++;
out++;
}
}
static void AddDrawSurfacesLump( file_t *file, ibspHeader_t *header )
{
int i, size;
bspDrawSurface_t *in;
ibspDrawSurface_t *buffer, *out;
/* allocate output buffer */
size = numBSPDrawSurfaces * sizeof( *buffer );
buffer = Malloc( size );
/* convert */
in = bspDrawSurfaces;
out = buffer;
for( i = 0; i < numBSPDrawSurfaces; i++ )
{
out->shaderNum = in->shaderNum;
out->fogNum = in->fogNum;
out->surfaceType = in->surfaceType;
out->firstVert = in->firstVert;
out->numVerts = in->numVerts;
out->firstIndex = in->firstIndex;
out->numIndexes = in->numIndexes;
out->lightmapNum = in->lightmapNum[ 0 ];
out->lightmapX = in->lightmapX[ 0 ];
out->lightmapY = in->lightmapY[ 0 ];
out->lightmapWidth = in->lightmapWidth;
out->lightmapHeight = in->lightmapHeight;
VectorCopy( in->lightmapOrigin, out->lightmapOrigin );
VectorCopy( in->lightmapVecs[ 0 ], out->lightmapVecs[ 0 ] );
VectorCopy( in->lightmapVecs[ 1 ], out->lightmapVecs[ 1 ] );
VectorCopy( in->lightmapVecs[ 2 ], out->lightmapVecs[ 2 ] );
out->patchWidth = in->patchWidth;
out->patchHeight = in->patchHeight;
in++;
out++;
}
/* write lump */
AddLump( file, (bspHeader_t*) header, LUMP_SURFACES, buffer, size );
/* free buffer */
Mem_Free( buffer );
}
/* drawverts */
typedef struct
{
vec3_t xyz;
float st[ 2 ];
float lightmap[ 2 ];
vec3_t normal;
byte color[ 4 ];
}
ibspDrawVert_t;
static void CopyDrawVertsLump( ibspHeader_t *header )
{
int i;
ibspDrawVert_t *in;
bspDrawVert_t *out;
/* get count */
numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( *in ) );
SetDrawVerts( numBSPDrawVerts );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_DRAWVERTS );
out = bspDrawVerts;
for( i = 0; i < numBSPDrawVerts; i++ )
{
VectorCopy( in->xyz, out->xyz );
out->st[ 0 ] = in->st[ 0 ];
out->st[ 1 ] = in->st[ 1 ];
out->lightmap[ 0 ][ 0 ] = in->lightmap[ 0 ];
out->lightmap[ 0 ][ 1 ] = in->lightmap[ 1 ];
VectorCopy( in->normal, out->normal );
out->color[ 0 ][ 0 ] = in->color[ 0 ];
out->color[ 0 ][ 1 ] = in->color[ 1 ];
out->color[ 0 ][ 2 ] = in->color[ 2 ];
out->color[ 0 ][ 3 ] = in->color[ 3 ];
in++;
out++;
}
}
static void AddDrawVertsLump( file_t *file, ibspHeader_t *header )
{
int i, size;
bspDrawVert_t *in;
ibspDrawVert_t *buffer, *out;
/* allocate output buffer */
size = numBSPDrawVerts * sizeof( *buffer );
buffer = Malloc( size );
/* convert */
in = bspDrawVerts;
out = buffer;
for( i = 0; i < numBSPDrawVerts; i++ )
{
VectorCopy( in->xyz, out->xyz );
out->st[ 0 ] = in->st[ 0 ];
out->st[ 1 ] = in->st[ 1 ];
out->lightmap[ 0 ] = in->lightmap[ 0 ][ 0 ];
out->lightmap[ 1 ] = in->lightmap[ 0 ][ 1 ];
VectorCopy( in->normal, out->normal );
out->color[ 0 ] = in->color[ 0 ][ 0 ];
out->color[ 1 ] = in->color[ 0 ][ 1 ];
out->color[ 2 ] = in->color[ 0 ][ 2 ];
out->color[ 3 ] = in->color[ 0 ][ 3 ];
in++;
out++;
}
/* write lump */
AddLump( file, (bspHeader_t*) header, LUMP_DRAWVERTS, buffer, size );
/* free buffer */
Mem_Free( buffer );
}
/* light grid */
typedef struct
{
byte ambient[ 3 ];
byte directed[ 3 ];
byte latLong[ 2 ];
}
ibspGridPoint_t;
static void CopyLightGridLumps( ibspHeader_t *header )
{
int i, j;
ibspGridPoint_t *in;
bspGridPoint_t *out;
/* get count */
numBSPGridPoints = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTGRID, sizeof( *in ) );
/* allocate buffer */
bspGridPoints = Malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
/* copy */
in = GetLump( (bspHeader_t*) header, LUMP_LIGHTGRID );
out = bspGridPoints;
for( i = 0; i < numBSPGridPoints; i++ )
{
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
VectorCopy( in->ambient, out->ambient[ j ] );
VectorCopy( in->directed, out->directed[ j ] );
out->styles[ j ] = LS_NONE;
}
out->styles[ 0 ] = LS_NORMAL;
out->latLong[ 0 ] = in->latLong[ 0 ];
out->latLong[ 1 ] = in->latLong[ 1 ];
in++;
out++;
}
}
static void AddLightGridLumps( file_t *file, ibspHeader_t *header )
{
int i;
bspGridPoint_t *in;
ibspGridPoint_t *buffer, *out;
/* dummy check */
if( bspGridPoints == NULL )
return;
/* allocate temporary buffer */
buffer = Malloc( numBSPGridPoints * sizeof( *out ) );
/* convert */
in = bspGridPoints;
out = buffer;
for( i = 0; i < numBSPGridPoints; i++ )
{
VectorCopy( in->ambient[ 0 ], out->ambient );
VectorCopy( in->directed[ 0 ], out->directed );
out->latLong[ 0 ] = in->latLong[ 0 ];
out->latLong[ 1 ] = in->latLong[ 1 ];
in++;
out++;
}
/* write lumps */
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTGRID, buffer, (numBSPGridPoints * sizeof( *out )) );
/* free buffer (ydnar 2002-10-22: [bug 641] thanks Rap70r! */
Mem_Free( buffer );
}
/*
LoadIBSPFile()
loads a quake 3 bsp file into memory
*/
void LoadIBSPFile( const char *filename )
{
ibspHeader_t *header;
/* load the file header */
header = (ibspHeader_t *)FS_LoadFile( filename, NULL );
/* swap the header (except the first 4 bytes) */
SwapBlock( (int*) ((byte*) header + sizeof( int )), sizeof( *header ) - sizeof( int ));
/* make sure it matches the format we're trying to load */
if( force == false && *((int*) header->ident) != *((int*) game->bspIdent) )
Sys_Break( "%s is not a %s file", filename, game->bspIdent );
if( force == false && header->version != game->bspVersion )
Sys_Break( "%s is version %d, not %d", filename, header->version, game->bspVersion );
/* load/convert lumps */
numBSPShaders = CopyLump( (bspHeader_t*) header, LUMP_SHADERS, bspShaders, sizeof( bspShader_t ) );
numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
CopyBrushSidesLump( header );
CopyDrawVertsLump( header );
CopyDrawSurfacesLump( header );
numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFog_t ) );
numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
bspLightBytes = Malloc( numBSPLightBytes );
CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
CopyLightGridLumps( header );
/* advertisements */
numBSPAds = CopyLump( (bspHeader_t*) header, LUMP_ADVERTISEMENTS, bspAds, sizeof( bspAdvertisement_t ) );
/* free the file buffer */
Mem_Free( header );
}
/*
WriteIBSPFile()
writes an id bsp file
*/
void WriteIBSPFile( const char *filename )
{
ibspHeader_t outheader, *header;
file_t *file;
time_t t;
char marker[ 1024 ];
int size;
/* set header */
header = &outheader;
memset( header, 0, sizeof( *header ));
//% Swapfile();
/* set up header */
*((int*) (bspHeader_t*) header->ident) = *((int*) game->bspIdent);
header->version = LittleLong( game->bspVersion );
/* write initial header */
file = FS_Open( filename, "wb" );
FS_Write( file, (bspHeader_t*)header, sizeof( *header )); /* overwritten later */
/* add marker lump */
time( &t );
com.sprintf( marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime( localtime( &t )));
AddLump( file, (bspHeader_t*) header, 0, marker, strlen( marker ) + 1 );
/* add lumps */
AddLump( file, (bspHeader_t*) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof( bspShader_t ));
AddLump( file, (bspHeader_t*) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof( bspPlane_t ));
AddLump( file, (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof( bspLeaf_t ));
AddLump( file, (bspHeader_t*) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof( bspNode_t ));
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes*sizeof( bspBrush_t ));
AddBrushSidesLump( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_MODELS, bspModels, numBSPModels * sizeof( bspModel_t ) );
AddDrawVertsLump( file, header );
AddDrawSurfacesLump( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes );
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes );
AddLightGridLumps( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, bspEntDataSize );
AddLump( file, (bspHeader_t*) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof( bspFog_t ));
AddLump( file, (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[ 0 ] ));
/* advertisements */
AddLump( file, (bspHeader_t*) header, LUMP_ADVERTISEMENTS, bspAds, numBSPAds * sizeof( bspAdvertisement_t ));
/* emit bsp size */
size = FS_Tell( file );
Msg( "Wrote %.1f MB (%d bytes)\n", (float) size / (1024 * 1024), size );
/* write the completed header */
FS_Seek( file, 0, SEEK_SET );
FS_Write( file, header, sizeof( *header ));
/* close the file */
FS_Close( file );
}

View File

@ -0,0 +1,327 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define BSPFILE_RBSP_C
/* dependencies */
#include "q3map2.h"
#include "byteorder.h"
/* -------------------------------------------------------------------------------
this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.
------------------------------------------------------------------------------- */
/* constants */
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define LUMP_LIGHTARRAY 17
#define HEADER_LUMPS 18
/* types */
typedef struct
{
char ident[4];
int version;
bspLump_t lumps[HEADER_LUMPS];
} rbspHeader_t;
/* light grid */
#define MAX_MAP_GRID 0xffff
#define MAX_MAP_GRIDARRAY 0x100000
#define LG_EPSILON 4
static void CopyLightGridLumps( rbspHeader_t *header )
{
int i;
unsigned short *inArray;
bspGridPoint_t *in, *out;
/* get count */
numBSPGridPoints = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTARRAY, sizeof( *inArray ) );
/* allocate buffer */
bspGridPoints = Malloc( numBSPGridPoints * sizeof( *bspGridPoints ));
/* copy */
inArray = GetLump( (bspHeader_t*) header, LUMP_LIGHTARRAY );
in = GetLump( (bspHeader_t*) header, LUMP_LIGHTGRID );
out = bspGridPoints;
for( i = 0; i < numBSPGridPoints; i++ )
{
Mem_Copy( out, &in[ *inArray ], sizeof( *in ) );
inArray++;
out++;
}
}
static void AddLightGridLumps( file_t *file, rbspHeader_t *header )
{
int i, j, k, c, d;
int numGridPoints, maxGridPoints;
bspGridPoint_t *gridPoints, *in, *out;
int numGridArray;
unsigned short *gridArray;
bool bad;
/* allocate temporary buffers */
maxGridPoints = (numBSPGridPoints < MAX_MAP_GRID) ? numBSPGridPoints : MAX_MAP_GRID;
gridPoints = Malloc( maxGridPoints * sizeof( *gridPoints ) );
gridArray = Malloc( numBSPGridPoints * sizeof( *gridArray ) );
/* zero out */
numGridPoints = 0;
numGridArray = numBSPGridPoints;
/* for each bsp grid point, find an approximate twin */
MsgDev( D_INFO, "Storing lightgrid: %d points\n", numBSPGridPoints );
for( i = 0; i < numGridArray; i++ )
{
/* get points */
in = &bspGridPoints[ i ];
/* walk existing list */
for( j = 0; j < numGridPoints; j++ )
{
/* get point */
out = &gridPoints[ j ];
/* compare styles */
if( *((unsigned int*) in->styles) != *((unsigned int*) out->styles) )
continue;
/* compare direction */
d = abs( in->latLong[ 0 ] - out->latLong[ 0 ] );
if( d < (255 - LG_EPSILON) && d > LG_EPSILON )
continue;
d = abs( in->latLong[ 1 ] - out->latLong[ 1 ] );
if( d < 255 - LG_EPSILON && d > LG_EPSILON )
continue;
/* compare light */
bad = false;
for( k = 0; (k < MAX_LIGHTMAPS && bad == false); k++ )
{
for( c = 0; c < 3; c++ )
{
if( abs( (int) in->ambient[ k ][ c ] - (int) out->ambient[ k ][ c ]) > LG_EPSILON ||
abs( (int) in->directed[ k ][ c ] - (int) out->directed[ k ][ c ]) > LG_EPSILON )
{
bad = true;
break;
}
}
}
/* failure */
if( bad )
continue;
/* this sample is ok */
break;
}
/* set sample index */
gridArray[ i ] = (unsigned short) j;
/* if no sample found, add a new one */
if( j >= numGridPoints && numGridPoints < maxGridPoints )
{
out = &gridPoints[ numGridPoints++ ];
Mem_Copy( out, in, sizeof( *in ) );
}
}
/* swap array */
for( i = 0; i < numGridArray; i++ )
gridArray[ i ] = LittleShort( gridArray[ i ] );
/* write lumps */
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTGRID, gridPoints, (numGridPoints * sizeof( *gridPoints )) );
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTARRAY, gridArray, (numGridArray * sizeof( *gridArray )) );
/* free buffers */
Mem_Free( gridPoints );
Mem_Free( gridArray );
}
/*
LoadRBSPFile()
loads a raven bsp file into memory
*/
void LoadRBSPFile( const char *filename )
{
rbspHeader_t *header;
/* load the file header */
header = (rbspHeader_t *)FS_LoadFile( filename, NULL );
/* swap the header (except the first 4 bytes) */
SwapBlock( (int*) ((byte*) header + sizeof( int )), sizeof( *header ) - sizeof( int ) );
/* make sure it matches the format we're trying to load */
if( force == false && *((int*) header->ident) != *((int*) game->bspIdent) )
Sys_Break( "%s is not a %s file", filename, game->bspIdent );
if( force == false && header->version != game->bspVersion )
Sys_Break( "%s is version %d, not %d", filename, header->version, game->bspVersion );
/* load/convert lumps */
numBSPShaders = CopyLump( (bspHeader_t*) header, LUMP_SHADERS, bspShaders, sizeof( bspShader_t ) );
numBSPModels = CopyLump( (bspHeader_t*) header, LUMP_MODELS, bspModels, sizeof( bspModel_t ) );
numBSPPlanes = CopyLump( (bspHeader_t*) header, LUMP_PLANES, bspPlanes, sizeof( bspPlane_t ) );
numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
numBSPNodes = CopyLump( (bspHeader_t*) header, LUMP_NODES, bspNodes, sizeof( bspNode_t ) );
numBSPLeafSurfaces = CopyLump( (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ) );
numBSPLeafBrushes = CopyLump( (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ) );
numBSPBrushes = CopyLump( (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, sizeof( bspBrush_t ) );
numBSPBrushSides = CopyLump( (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, sizeof( bspBrushSide_t ) );
numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( bspDrawVerts[ 0 ] ) );
SetDrawVerts( numBSPDrawVerts );
CopyLump( (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, sizeof( bspDrawVerts[ 0 ] ) );
numBSPDrawSurfaces = GetLumpElements( (bspHeader_t*) header, LUMP_SURFACES, sizeof( bspDrawSurfaces[ 0 ] ) );
SetDrawSurfaces( numBSPDrawSurfaces );
CopyLump( (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, sizeof( bspDrawSurfaces[ 0 ] ) );
numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFogs[ 0 ] ) );
numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
bspLightBytes = Malloc( numBSPLightBytes );
CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
bspEntDataSize = CopyLump( (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, 1);
CopyLightGridLumps( header );
/* free the file buffer */
Mem_Free( header );
}
/*
WriteRBSPFile()
writes a raven bsp file
*/
void WriteRBSPFile( const char *filename )
{
rbspHeader_t outheader, *header;
file_t *file;
time_t t;
char marker[MAX_SYSPATH];
int size;
/* set header */
header = &outheader;
memset( header, 0, sizeof( *header ) );
//% Swapfile();
/* set up header */
*((int*) (bspHeader_t*) header->ident) = *((int*) game->bspIdent);
header->version = LittleLong( game->bspVersion );
/* write initial header */
file = FS_Open( filename, "wb" );
FS_Write( file, (bspHeader_t *)header, sizeof( *header )); /* overwritten later */
/* add marker lump */
time( &t );
com.sprintf( marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime( localtime( &t )));
AddLump( file, (bspHeader_t *) header, 0, marker, com.strlen( marker ) + 1 );
/* add lumps */
AddLump( file, (bspHeader_t*) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof( bspShader_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof( bspPlane_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof( bspLeaf_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof( bspNode_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes*sizeof( bspBrush_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_MODELS, bspModels, numBSPModels * sizeof( bspModel_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVerts[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
AddLump( file, (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes );
AddLump( file, (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes );
AddLightGridLumps( file, header );
AddLump( file, (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, bspEntDataSize );
AddLump( file, (bspHeader_t*) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof( bspFog_t ) );
AddLump( file, (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[ 0 ] ) );
/* emit bsp size */
size = FS_Tell( file );
MsgDev( D_INFO, "Wrote %.1f MB (%d bytes)\n", (float) size / (1024 * 1024), size );
/* write the completed header */
FS_Seek( file, 0, SEEK_SET );
FS_Write( file, header, sizeof( *header ));
/* close the file */
FS_Close( file );
}

View File

@ -1,165 +1,655 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// bsplib.c - bsp level creator
//=======================================================================
/* -------------------------------------------------------------------------------
#include "bsplib.h"
#include "entity_def.h"
#include "physic_api.h"
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
byte *checkermate_dds;
size_t checkermate_dds_size;
char path[MAX_SYSPATH];
uint bsp_parms;
file_t *bsplog;
cvar_t *bsplib_compress_bsp;
This file is part of GtkRadiant.
dll_info_t physic_dll = { "physic.dll", NULL, "CreateAPI", NULL, NULL, false, sizeof(physic_exp_t) };
physic_exp_t *pe;
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.
void BSP_PrintLog( const char *pMsg )
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
-------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MAIN_C
/* dependencies */
#include "q3map2.h"
#include "byteorder.h"
/*
Random()
returns a pseudorandom number between 0 and 1
*/
vec_t Random( void )
{
if( !enable_log ) return;
if( !bsplog ) bsplog = FS_Open( va("maps/%s.log", gs_filename ), "wb" );
FS_Print( bsplog, pMsg );
return (vec_t) rand() / RAND_MAX;
}
static void AddCollision( void* handle, const void* buffer, size_t size )
/*
ExitQ3Map()
cleanup routine
*/
static void ExitQ3Map( void )
{
if((dcollisiondatasize + size) > MAX_MAP_COLLISION )
Sys_Error( "MAX_MAP_COLLISION limit exceeded\n" );
Mem_Copy( dcollision + dcollisiondatasize, (void *)buffer, size );
dcollisiondatasize += size;
BSPFilesCleanup();
if( mapDrawSurfs != NULL )
Mem_Free( mapDrawSurfs );
}
void ProcessCollisionTree( void )
/*
FixClipMap()
resets an clipmap checksum to match the given BSP
*/
int FixClipMap( int argc, char **argv )
{
if( !physic_dll.link ) return;
dcollisiondatasize = 0;
pe->WriteCollisionLump( NULL, AddCollision );
}
void Init_PhysicsLibrary( void )
{
static physic_imp_t pi;
launch_t CreatePhysic;
pi.api_size = sizeof(physic_imp_t);
Sys_LoadLibrary( &physic_dll );
if( physic_dll.link )
int length, checksum;
void *buffer;
file_t *file;
char clipmap[MAX_SYSPATH];
/* arg checking */
if( argc < 2 )
{
CreatePhysic = (void *)physic_dll.main;
pe = CreatePhysic( &com, &pi ); // sys_error not overrided
pe->Init(); // initialize phys callback
Msg( "Usage: q3map -fixaas [-v] <mapname>\n" );
return 0;
}
else Mem_Set( &pe, 0, sizeof( pe ));
/* do some path mangling */
strcpy( source, argv[argc-1] );
FS_StripExtension( source );
FS_DefaultExtension( source, ".bsp" );
Msg( "--- FixClipMap ---\n" );
Msg( "Loading %s\n", source );
buffer = FS_LoadFile( source, &length );
if( !buffer ) Sys_Break( "can't load %s\n", source );
/* create bsp checksum */
Msg( "Creating checksum...\n" );
checksum = LittleLong( Com_BlockChecksum( buffer, length ));
Mem_Free( buffer );
/* mangle name */
FS_FileBase( source, source );
com.sprintf( clipmap, "maps/clipmaps/%s.bin", source );
/* fix it */
file = FS_Open( clipmap, "r+b" );
if( !file ) return 1;
FS_Seek( file, 0, SEEK_SET );
if( FS_Write( file, &checksum, sizeof( int )) != sizeof( int ))
Sys_Break( "Error writing checksum to %s\n", clipmap );
FS_Close( file );
return 0;
}
void Free_PhysicLibrary( void )
/*
AnalyzeBSP() - ydnar
analyzes a Quake engine BSP file
*/
typedef struct abspHeader_s
{
if( physic_dll.link )
{
pe->Shutdown();
Mem_Set( &pe, 0, sizeof( pe ));
}
Sys_FreeLibrary( &physic_dll );
}
char ident[4];
int version;
bspLump_t lumps[1]; // unknown size
} abspHeader_t;
bool PrepareBSPModel( const char *dir, const char *name )
typedef struct abspLumpTest_s
{
int numshaders;
int radix, minCount;
char *name;
} abspLumpTest_t;
bsp_parms = 0;
maxdist = 0.0;
bsplog = NULL;
// get global parms
if( FS_CheckParm( "-vis" )) bsp_parms |= BSPLIB_MAKEVIS;
if( FS_CheckParm( "-qrad" )) bsp_parms |= BSPLIB_MAKEQ2RAD;
if( FS_CheckParm( "-hlrad" )) bsp_parms |= BSPLIB_MAKEHLRAD;
if( FS_CheckParm( "-full" )) bsp_parms |= BSPLIB_FULLCOMPILE;
if( FS_CheckParm( "-onlyents" )) bsp_parms |= BSPLIB_ONLYENTS;
if( FS_CheckParm( "-info" )) bsp_parms |= BSPLIB_SHOWINFO;
if( FS_CheckParm( "-cullerror" )) bsp_parms |= BSPLIB_CULLERROR;
if( FS_CheckParm( "-sound" )) bsp_parms |= BSPLIB_MAKESOUND;
if( FS_CheckParm( "-deltemp" )) bsp_parms |= BSPLIB_DELETE_TEMP;
// famous q1 "notexture" image: purple-black checkerboard
checkermate_dds = FS_LoadInternal( "checkerboard.dds", &checkermate_dds_size );
Image_Init( NULL, IL_ALLOW_OVERWRITE|IL_IGNORE_MIPS );
bsplib_compress_bsp = Cvar_Get( "bsp_compress", "0", CVAR_SYSTEMINFO, "compress bsp lumps" );
// merge parms
if( bsp_parms & BSPLIB_ONLYENTS )
static abspLumpTest_t lumpTests[] =
{
{ sizeof( bspPlane_t ), 6, "IBSP LUMP_PLANES" },
{ sizeof( bspBrush_t ), 1, "IBSP LUMP_BRUSHES" },
{ 8, 6, "IBSP LUMP_BRUSHSIDES" },
{ sizeof( bspBrushSide_t ), 6, "RBSP LUMP_BRUSHSIDES" },
{ sizeof( bspModel_t ), 1, "IBSP LUMP_MODELS" },
{ sizeof( bspNode_t ), 2, "IBSP LUMP_NODES" },
{ sizeof( bspLeaf_t ), 1, "IBSP LUMP_LEAFS" },
{ 104, 3, "IBSP LUMP_DRAWSURFS" },
{ 44, 3, "IBSP LUMP_DRAWVERTS" },
{ 4, 6, "IBSP LUMP_DRAWINDEXES" },
{ 128 * 128 * 3, 1, "IBSP LUMP_LIGHTMAPS" },
{ 256 * 256 * 3, 1, "IBSP LUMP_LIGHTMAPS (256 x 256)" },
{ 512 * 512 * 3, 1, "IBSP LUMP_LIGHTMAPS (512 x 512)" },
{ 0, 0, NULL }
};
int AnalyzeBSP( int argc, char **argv )
{
abspHeader_t *header;
int size, i, version, offset, length, lumpInt, count;
char ident[5];
void *lump;
float lumpFloat;
char lumpString[MAX_SYSPATH], source[MAX_SYSPATH];
bool lumpSwap = false;
abspLumpTest_t *lumpTest;
/* arg checking */
if( argc < 1 )
{
bsp_parms = (BSPLIB_ONLYENTS|BSPLIB_MAKEBSP);
Msg( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
return 0;
}
else
/* process arguments */
for( i = 1; i < (argc - 1); i++ )
{
if( bsp_parms & BSPLIB_MAKEQ2RAD && bsp_parms & BSPLIB_MAKEHLRAD )
/* -format map|ase|... */
if( !strcmp( argv[i], "-lumpswap" ) )
{
MsgDev( D_WARN, "both type 'hlrad' and 'qrad' specified\ndefaulting to 'qrad'\n" );
bsp_parms &= ~BSPLIB_MAKEHLRAD;
Msg( "Swapped lump structs enabled\n" );
lumpSwap = true;
}
}
/* clean up map name */
com.strcpy( source, argv[i] );
Msg( "Loading %s\n", source );
/* load the file */
header = (void *)FS_LoadFile( source, &size );
if( size == 0 || header == NULL )
{
Msg( "Unable to load %s.\n", source );
return -1;
}
/* analyze ident/version */
Mem_Copy( ident, header->ident, 4 );
ident[4] = '\0';
version = LittleLong( header->version );
Msg( "Identity: %s\n", ident );
Msg( "Version: %d\n", version );
Msg( "---------------------------------------\n" );
// analyze each lump
for( i = 0; i < 100; i++ )
{
/* call of duty swapped lump pairs */
if( lumpSwap )
{
offset = LittleLong( header->lumps[ i ].length );
length = LittleLong( header->lumps[ i ].offset );
}
if( bsp_parms & BSPLIB_FULLCOMPILE )
/* standard lump pairs */
else
{
if((bsp_parms & BSPLIB_MAKEVIS) && (bsp_parms & (BSPLIB_MAKEQ2RAD|BSPLIB_MAKEHLRAD)))
offset = LittleLong( header->lumps[ i ].offset );
length = LittleLong( header->lumps[ i ].length );
}
/* extract data */
lump = (byte*) header + offset;
lumpInt = LittleLong( (int) *((int*) lump) );
lumpFloat = LittleFloat( (float) *((float*) lump) );
memcpy( lumpString, (char*) lump, (length < 1024 ? length : 1024) );
lumpString[ 1024 ] = '\0';
/* print basic lump info */
Msg( "Lump: %d\n", i );
Msg( "Offset: %d bytes\n", offset );
Msg( "Length: %d bytes\n", length );
/* only operate on valid lumps */
if( length > 0 )
{
/* print data in 4 formats */
Msg( "As hex: %08X\n", lumpInt );
Msg( "As int: %d\n", lumpInt );
Msg( "As float: %f\n", lumpFloat );
Msg( "As string: %s\n", lumpString );
/* guess lump type */
if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
Msg( "Type guess: IBSP LUMP_ENTITIES\n" );
else if( strstr( lumpString, "textures/" ) )
Msg( "Type guess: IBSP LUMP_SHADERS\n" );
else
{
bsp_parms |= BSPLIB_MAKEBSP; // rebuild bsp file for final compile
bsp_parms |= BSPLIB_MAKESOUND;
bsp_parms |= BSPLIB_DELETE_TEMP;
/* guess based on size/count */
for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
{
if( (length % lumpTest->radix) != 0 )
continue;
count = length / lumpTest->radix;
if( count < lumpTest->minCount )
continue;
Msg( "Type guess: %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
}
}
}
if(!(bsp_parms & (BSPLIB_MAKEVIS|BSPLIB_MAKEQ2RAD|BSPLIB_MAKEHLRAD)))
{
// -vis -light or -rad not specified, just create a .bsp
bsp_parms |= BSPLIB_MAKEBSP;
}
Msg( "---------------------------------------\n" );
/* end of file */
if( offset + length >= size )
break;
}
FS_LoadGameInfo( "gameinfo.txt" ); // same as normal gamemode
Init_PhysicsLibrary();
enable_log = true;
numshaders = LoadShaderInfo();
Msg( "%5i shaderInfo\n", numshaders );
return true;
/* last stats */
Msg( "Lump count: %d\n", i + 1 );
Msg( "File size: %d bytes\n", size );
/* return to caller */
return 0;
}
bool CompileBSPModel ( void )
/*
BSPInfo()
emits statistics about the bsp file
*/
int BSPInfo( int count, char **fileNames )
{
// now run specified utils
if( bsp_parms & BSPLIB_MAKEBSP )
WbspMain();
if( bsp_parms & (BSPLIB_MAKEVIS|BSPLIB_MAKESOUND))
WvisMain();
if( bsp_parms & (BSPLIB_MAKEQ2RAD|BSPLIB_MAKEHLRAD))
WradMain();
if( bsp_parms & BSPLIB_SHOWINFO )
int i;
char source[MAX_SYSPATH];
const char *ext;
int size;
file_t *f;
/* dummy check */
if( count < 1 )
{
Msg( "No files to dump info for.\n");
return -1;
}
/* enable info mode */
infoMode = true;
/* walk file list */
for( i = 0; i < count; i++ )
{
Msg( "---------------------------------\n" );
/* mangle filename and get size */
strcpy( source, fileNames[ i ] );
ext = FS_FileExtension( source );
if( !com.stricmp( ext, "map" ))
FS_StripExtension( source );
FS_DefaultExtension( source, ".bsp" );
f = FS_Open( source, "rb" );
if( f )
{
FS_Seek( f, 0, SEEK_END );
size = FS_Tell( f );
FS_Close( f );
}
else size = 0;
/* load the bsp file and print lump sizes */
Msg( "%s\n", source );
LoadBSPFile( source );
PrintBSPFileSizes();
Free_PhysicLibrary();
// close log before deleting temporaries
enable_log = false;
if( bsplog ) FS_Close( bsplog );
bsplog = NULL;
if( bsp_parms & BSPLIB_DELETE_TEMP )
{
// delete all temporary files after final compile
com.sprintf( path, "%s/maps/%s.prt", com.GameInfo->gamedir, gs_filename );
FS_Delete( path );
com.sprintf( path, "%s/maps/%s.lin", com.GameInfo->gamedir, gs_filename );
FS_Delete( path );
com.sprintf( path, "%s/maps/%s.log", com.GameInfo->gamedir, gs_filename );
FS_Delete( path );
/* print sizes */
Msg( "\n" );
Msg( " total %9d\n", size );
Msg( " %9d KB\n", size / 1024 );
Msg( " %9d MB\n", size / (1024 * 1024) );
Msg( "---------------------------------\n" );
}
return true;
}
/* return count */
return i;
}
/*
ScaleBSPMain()
amaze and confuse your enemies with wierd scaled maps!
*/
int ScaleBSPMain( int argc, char **argv )
{
int i;
float f, scale;
vec3_t vec;
char str[MAX_SYSPATH];
/* arg checking */
if( argc < 2 )
{
Msg( "Usage: q3map -scale <value> [-v] <mapname>\n" );
return 0;
}
/* get scale */
scale = com.atof( argv[argc - 2] );
if( scale == 0.0f )
{
Msg( "Usage: q3map -scale <value> [-v] <mapname>\n" );
Msg( "Non-zero scale value required.\n" );
return 0;
}
/* do some path mangling */
strcpy( source, argv[ argc - 1 ] );
FS_StripExtension( source );
FS_DefaultExtension( source, ".bsp" );
/* load the bsp */
Msg( "Loading %s\n", source );
LoadBSPFile( source );
ParseEntities();
/* note it */
Msg( "--- ScaleBSP ---\n" );
MsgDev( D_NOTE, "%9d entities\n", numEntities );
/* scale entity keys */
for( i = 0; i < numBSPEntities && i < numEntities; i++ )
{
// scale origin
GetVectorForKey( &entities[ i ], "origin", vec );
if( (vec[0] + vec[1] + vec[2]) )
{
VectorScale( vec, scale, vec );
com.sprintf( str, "%f %f %f", vec[0], vec[1], vec[2] );
SetKeyValue( &entities[ i ], "origin", str );
}
// scale door lip
f = FloatForKey( &entities[ i ], "lip" );
if( f )
{
f *= scale;
com.sprintf( str, "%f", f );
SetKeyValue( &entities[ i ], "lip", str );
}
}
/* scale models */
for( i = 0; i < numBSPModels; i++ )
{
VectorScale( bspModels[ i ].mins, scale, bspModels[ i ].mins );
VectorScale( bspModels[ i ].maxs, scale, bspModels[ i ].maxs );
}
/* scale nodes */
for( i = 0; i < numBSPNodes; i++ )
{
VectorScale( bspNodes[ i ].mins, scale, bspNodes[ i ].mins );
VectorScale( bspNodes[ i ].maxs, scale, bspNodes[ i ].maxs );
}
/* scale leafs */
for( i = 0; i < numBSPLeafs; i++ )
{
VectorScale( bspLeafs[ i ].mins, scale, bspLeafs[ i ].mins );
VectorScale( bspLeafs[ i ].maxs, scale, bspLeafs[ i ].maxs );
}
/* scale drawverts */
for( i = 0; i < numBSPDrawVerts; i++ )
VectorScale( bspDrawVerts[ i ].xyz, scale, bspDrawVerts[ i ].xyz );
/* scale planes */
for( i = 0; i < numBSPPlanes; i++ )
bspPlanes[ i ].dist *= scale;
/* scale gridsize */
GetVectorForKey( &entities[ 0 ], "gridsize", vec );
if((vec[0] + vec[1] + vec[2]) == 0.0f ) VectorCopy( gridSize, vec );
VectorScale( vec, scale, vec );
com.sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
SetKeyValue( &entities[ 0 ], "gridsize", str );
/* write the bsp */
UnparseEntities();
FS_StripExtension( source );
FS_DefaultExtension( source, "_s.bsp" );
Msg( "Writing %s\n", source );
WriteBSPFile( source );
return 0;
}
/*
ConvertBSPMain()
main argument processing function for bsp conversion
*/
int ConvertBSPMain( int argc, char **argv )
{
int i;
int (*convertFunc)( char * );
game_t *convertGame;
convertFunc = ConvertBSPToASE;
convertGame = NULL;
/* arg checking */
if( argc < 1 )
{
Msg( "Usage: q3map -scale <value> [-v] <mapname>\n" );
return 0;
}
/* process arguments */
for( i = 1; i < (argc - 1); i++ )
{
/* -format map|ase|... */
if( !strcmp( argv[ i ], "-format" ) )
{
i++;
if( !com.stricmp( argv[ i ], "ase" ) )
convertFunc = ConvertBSPToASE;
else if( !com.stricmp( argv[ i ], "map" ) )
convertFunc = ConvertBSPToMap;
else
{
convertGame = GetGame( argv[ i ] );
if( convertGame == NULL )
Msg( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
}
}
}
/* clean up map name */
strcpy( source, argv[ i ] );
FS_StripExtension( source );
FS_DefaultExtension( source, ".bsp" );
LoadShaderInfo();
Msg( "Loading %s\n", source );
/* ydnar: load surface file */
//% LoadSurfaceExtraFile( source );
LoadBSPFile( source );
/* parse bsp entities */
ParseEntities();
/* bsp format convert? */
if( convertGame != NULL )
{
/* set global game */
game = convertGame;
/* write bsp */
FS_StripExtension( source );
FS_DefaultExtension( source, "_c.bsp" );
Msg( "Writing %s\n", source );
WriteBSPFile( source );
/* return to sender */
return 0;
}
return convertFunc( source );
}
/*
main()
q3map mojo...
*/
bool Q3MapMain( int argc, char **argv )
{
int i, r;
double start, end;
/* we want consistent 'randomness' */
srand( 0 );
/* start timer */
start = Sys_DoubleTime();
/* this was changed to emit version number over the network */
Msg( Q3MAP_VERSION "\n" );
/* set exit call */
atexit( ExitQ3Map );
/* read general options first */
for( i = 1; i < argc; i++ )
{
/* force */
if( !strcmp( argv[ i ], "-force" ) )
{
force = true;
argv[ i ] = NULL;
}
/* patch subdivisions */
else if( !strcmp( argv[ i ], "-subdivisions" ) )
{
argv[ i ] = NULL;
i++;
patchSubdivisions = atoi( argv[ i ] );
argv[ i ] = NULL;
if( patchSubdivisions <= 0 )
patchSubdivisions = 1;
}
}
/* init model library */
PicoInit();
PicoSetMallocFunc( NULL );
PicoSetFreeFunc( NULL );
PicoSetPrintFunc( PicoPrintFunc );
PicoSetLoadFileFunc( PicoLoadFileFunc );
PicoSetFreeFileFunc( NULL );
/* generate sinusoid jitter table */
for( i = 0; i < MAX_JITTERS; i++ )
jitters[ i ] = sin( i * 139.54152147 );
/* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
Msg( "Q3Map - v1.0r (c) 1999 Id Software Inc.\n" );
Msg( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
Msg( "%s\n", Q3MAP_MOTD );
/* ydnar: new path initialization */
InitPaths( &argc, argv );
/* check if we have enough options left to attempt something */
if( argc < 2 ) Sys_Break( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
/* fixaas */
if( !strcmp( argv[ 1 ], "-fixclip" ) )
r = FixClipMap( argc - 1, argv + 1 );
/* analyze */
else if( !strcmp( argv[ 1 ], "-analyze" ) )
r = AnalyzeBSP( argc - 1, argv + 1 );
/* info */
else if( !strcmp( argv[ 1 ], "-info" ) )
r = BSPInfo( argc - 2, argv + 2 );
/* vis */
else if( !strcmp( argv[ 1 ], "-vis" ) )
r = VisMain( argc - 1, argv + 1 );
/* light */
else if( !strcmp( argv[ 1 ], "-light" ) )
r = LightMain( argc - 1, argv + 1 );
/* vlight */
else if( !strcmp( argv[ 1 ], "-vlight" ) )
{
Msg( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
argv[ 1 ] = "-fast"; /* eek a hack */
r = LightMain( argc, argv );
}
/* ydnar: lightmap export */
else if( !strcmp( argv[ 1 ], "-export" ) )
r = ExportLightmapsMain( argc - 1, argv + 1 );
/* ydnar: lightmap import */
else if( !strcmp( argv[ 1 ], "-import" ) )
r = ImportLightmapsMain( argc - 1, argv + 1 );
/* ydnar: bsp scaling */
else if( !strcmp( argv[ 1 ], "-scale" ) )
r = ScaleBSPMain( argc - 1, argv + 1 );
/* ydnar: bsp conversion */
else if( !strcmp( argv[ 1 ], "-convert" ) )
r = ConvertBSPMain( argc - 1, argv + 1 );
/* ydnar: otherwise create a bsp */
else
r = BSPMain( argc, argv );
/* emit time */
end = Sys_DoubleTime();
Msg( "%9.0f seconds elapsed\n", end - start );
return r;
}

View File

@ -1,694 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2007 ©
// bsplib.h - bsplib header
//=======================================================================
#ifndef BSPLIB_H
#define BSPLIB_H
#include "xtools.h"
#include "engine_api.h"
#include "utils.h"
#include "mathlib.h"
#include "trace_def.h"
// supported map formats
enum
{
BRUSH_UNKNOWN = 0,
BRUSH_WORLDCRAFT_21, // quake worldcraft <= 2.1
BRUSH_WORLDCRAFT_22, // half-life worldcraft >= 2.2
BRUSH_RADIANT,
BRUSH_QUARK,
BRUSH_COUNT
};
#define VALVE_FORMAT 220
#define MAX_BRUSH_SIDES 128
#define BOGUS_RANGE MAX_WORLD_COORD
#define TEXINFO_NODE -1 // side is allready on a node
#define MAX_PORTALS 32768
#define PORTALFILE "PRT1"
#define MAX_POINTS_ON_WINDING 64
#define PLANENUM_LEAF -1
#define MAXEDGES 20
#define MAX_NODE_BRUSHES 8
#define MAX_PORTALS_ON_LEAF 128
#define MAX_MAP_SIDES (MAX_MAP_BRUSHES*6)
#define MAX_TEXTURE_FRAMES 256
#define MAX_PATCHES 65000 // larger will cause 32 bit overflows
// compile parms
typedef enum
{
BSPLIB_MAKEBSP = BIT(0), // create a bsp file
BSPLIB_MAKEVIS = BIT(1), // do visibility
BSPLIB_MAKEHLRAD = BIT(2), // do half-life radiosity
BSPLIB_MAKEQ2RAD = BIT(3), // do quake2 radiosity
BSPLIB_FULLCOMPILE = BIT(4), // equals -full for vis, -extra for rad or light
BSPLIB_ONLYENTS = BIT(5), // update only ents lump
BSPLIB_RAD_NOPVS = BIT(6), // ignore pvs while processing radiocity (kill smooth light)
BSPLIB_RAD_NOBLOCK = BIT(7),
BSPLIB_RAD_NOCOLOR = BIT(8),
BSPLIB_DELETE_TEMP = BIT(9), // delete itermediate files
BSPLIB_SHOWINFO = BIT(10),
BSPLIB_CULLERROR = BIT(11),
BSPLIB_MAKESOUND = BIT(12), // calculate ambient sounds
} bsplibFlags_t;
extern uint bsp_parms;
extern char path[MAX_SYSPATH];
extern cvar_t *bsplib_compress_bsp;
extern float maxdist;
// bsplib export functions
void WradMain( void );
void WvisMain( void );
void WbspMain( void );
typedef struct plane_s
{
vec3_t normal;
vec_t dist;
int type;
struct plane_s *hash_chain;
} plane_t;
typedef struct
{
int numpoints;
vec3_t p[4]; // variable sized
} winding_t;
typedef struct epair_s
{
struct epair_s *next;
char *key;
char *value;
} epair_t;
typedef struct
{
vec3_t origin;
int firstbrush;
int numbrushes;
epair_t *epairs;
// only valid for func_areaportals
int areaportalnum;
int portalareas[2];
} bsp_entity_t;
typedef struct
{
vec3_t UAxis;
vec3_t VAxis;
float shift[2];
float rotate;
float scale[2];
} wrl_vecs;
typedef struct
{
float vecs[2][4];
} qrk_vecs;
typedef struct
{
float matrix[2][3];
} q3a_vecs;
typedef struct
{
string name;
int surfaceFlags;
int contents;
vec3_t color;
int intensity;
bool hasPasses;
} bsp_shader_t;
typedef union
{
qrk_vecs quark;
wrl_vecs hammer;
q3a_vecs radiant;
} vects_u;
typedef struct
{
vects_u vects;
string name;
int size[2];
int brush_type;
int contents;
int flags;
int value;
} brush_texture_t;
typedef struct side_s
{
int planenum;
int texinfo;
winding_t *winding;
struct side_s *original; // bspbrush_t sides will reference the mapbrush_t sides
int contents; // from miptex
int surf; // from miptex
bool visible; // choose visble planes first
bool tested; // this plane allready checked as a split
bool bevel; // don't ever use for bsp splitting
} side_t;
typedef struct mapbrush_s
{
int entitynum;
int brushnum;
int contents;
int shadernum;
vec3_t mins, maxs;
int numsides;
side_t *original_sides;
} mapbrush_t;
typedef struct face_s
{
struct face_s *next; // on node
// the chain of faces off of a node can be merged or split,
// but each face_t along the way will remain in the chain
// until the entire tree is freed
struct face_s *merged; // if set, this face isn't valid anymore
struct face_s *split[2]; // if set, this face isn't valid anymore
struct portal_s *portal;
int texinfo;
int planenum;
int contents; // faces in different contents can't merge
int outputnumber;
winding_t *w;
int numpoints;
bool badstartvert; // tjunctions cannot be fixed without a midpoint vertex
int vertexnums[MAXEDGES];
} face_t;
typedef struct bspbrush_s
{
struct bspbrush_s *next;
vec3_t mins, maxs;
int side, testside; // side of node during construction
mapbrush_t *original;
int numsides;
side_t sides[6]; // variably sized
} bspbrush_t;
typedef struct node_s
{
// both leafs and nodes
int planenum; // -1 = leaf node
struct node_s *parent;
vec3_t mins, maxs; // valid after portalization
bspbrush_t *volume; // one for each leaf/node
// nodes only
bool detail_seperator; // a detail brush caused the split
side_t *side; // the side that created the node
struct node_s *children[2];
face_t *faces;
// leafs only
bspbrush_t *brushlist; // fragments of all brushes in this leaf
int contents; // OR of all brush contents
int occupied; // 1 or greater can reach entity
bsp_entity_t *occupant; // for leak file testing
int cluster; // for portalfile writing
int area; // for areaportals
struct portal_s *portals; // also on nodes during construction
} node_t;
typedef struct portal_s
{
plane_t plane;
node_t *onnode; // NULL = outside box
node_t *nodes[2]; // [0] = front side of plane
struct portal_s *next[2];
winding_t *winding;
bool sidefound; // false if ->side hasn't been checked
side_t *side; // NULL = non-visible
face_t *face[2]; // output face in bsp file
} portal_t;
typedef struct
{
node_t *headnode;
node_t outside_node;
vec3_t mins, maxs;
} tree_t;
typedef struct
{
vec3_t normal;
float dist;
} visplane_t;
typedef struct
{
bool original; // don't free, it's part of the portal
int numpoints;
vec3_t points[12]; // variable sized
} viswinding_t;
typedef enum {stat_none, stat_working, stat_done} vstatus_t;
typedef struct
{
visplane_t plane; // normal pointing into neighbor
int leaf; // neighbor
int owner_leaf; // neighbor
vec3_t origin; // for fast clip testing
float radius;
viswinding_t *winding;
vstatus_t status;
byte *portalfront; // [portals], preliminary
byte *portalflood; // [portals], intermediate
byte *portalvis; // [portals], final
int nummightsee; // bit count on portalflood for sort
} visportal_t;
typedef struct seperating_plane_s
{
struct seperating_plane_s *next;
visplane_t plane; // from portal is on positive side
} sep_t;
typedef struct passage_s
{
struct passage_s *next;
int from, to; // leaf numbers
sep_t *planes;
} passage_t;
typedef struct leaf_s
{
int numportals;
passage_t *passages;
visportal_t *portals[MAX_PORTALS_ON_LEAF];
} leaf_t;
typedef struct pstack_s
{
byte mightsee[MAX_PORTALS/8]; // bit string
struct pstack_s *next;
leaf_t *leaf;
visportal_t *portal; // portal exiting
viswinding_t *source;
viswinding_t *pass;
viswinding_t windings[3]; // source, pass, temp in any order
int freewindings[3];
visplane_t portalplane;
} pstack_t;
typedef struct
{
visportal_t *base;
int c_chains;
pstack_t pstack_head;
} threaddata_t;
extern int num_entities;
extern bsp_entity_t entities[MAX_MAP_ENTITIES];
extern file_t *bsplog;
extern script_t *mapfile;
void ParseEntities( void );
void UnparseEntities( void );
void Com_CheckToken( script_t *script, const char *match );
void Com_Parse1DMatrix( script_t *script, int x, vec_t *m );
void Com_Parse2DMatrix( script_t *script, int y, int x, vec_t *m );
void SetKeyValue( bsp_entity_t *ent, const char *key, const char *value );
char *ValueForKey( const bsp_entity_t *ent, const char *key ); // will return "" if not present
vec_t FloatForKey( const bsp_entity_t *ent, const char *key );
long IntForKey( const bsp_entity_t *ent, const char *key );
void GetVectorForKey( const bsp_entity_t *ent, const char *key, vec3_t vec );
bsp_entity_t *FindTargetEntity( const char *target );
void BSP_PrintLog( const char *pMsg );
void FindMapMessage( char *message );
epair_t *ParseEpair( script_t *script, token_t *token );
void PrintBSPFileSizes( void );
extern int entity_num;
extern int g_mapversion;
extern plane_t mapplanes[MAX_MAP_PLANES];
extern int nummapplanes;
extern int nummapbrushes;
extern mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
extern vec3_t map_mins, map_maxs;
extern int nummapbrushsides;
extern side_t brushsides[MAX_MAP_SIDES];
extern int nummodels;
extern dmodel_t dmodels[MAX_MAP_MODELS];
extern int visdatasize;
extern byte dvisdata[MAX_MAP_VISIBILITY];
extern dvis_t *dvis;
extern int lightdatasize;
extern byte dlightdata[MAX_MAP_LIGHTING];
extern int entdatasize;
extern char dentdata[MAX_MAP_ENTSTRING];
extern int numleafs;
extern dleaf_t dleafs[MAX_MAP_LEAFS];
extern int numplanes;
extern dplane_t dplanes[MAX_MAP_PLANES];
extern int numvertexes;
extern dvertex_t dvertexes[MAX_MAP_VERTS];
extern int numnodes;
extern dnode_t dnodes[MAX_MAP_NODES];
extern int numtexinfo;
extern dtexinfo_t texinfo[MAX_MAP_TEXINFO];
extern int numsurfaces;
extern dsurface_t dsurfaces[MAX_MAP_SURFACES];
extern int numedges;
extern dedge_t dedges[MAX_MAP_EDGES];
extern int numleafsurfaces;
extern dleafface_t dleafsurfaces[MAX_MAP_LEAFFACES];
extern int numleafbrushes;
extern dleafbrush_t dleafbrushes[MAX_MAP_LEAFBRUSHES];
extern int numsurfedges;
extern dsurfedge_t dsurfedges[MAX_MAP_SURFEDGES];
extern int numareas;
extern darea_t dareas[MAX_MAP_AREAS];
extern int numareaportals;
extern dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
extern int numbrushes;
extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
extern int numbrushsides;
extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
extern int dcollisiondatasize;
extern byte dcollision[MAX_MAP_COLLISION];
extern dshader_t dshaders[MAX_MAP_SHADERS];
extern int numshaders;
void LoadMapFile ( void );
int FindFloatPlane (vec3_t normal, vec_t dist);
bool LoadBSPFile ( void );
void WriteBSPFile ( void );
void DecompressVis (byte *in, byte *decompressed);
int CompressVis (byte *vis, byte *dest);
//=============================================================================
// bsplib.c
void ProcessCollisionTree( void );
//=============================================================================
// textures.c
int FindMiptex( const char *name );
int TexinfoForBrushTexture( plane_t *plane, brush_texture_t *bt, vec3_t origin );
//=============================================================================
mapbrush_t *Brush_LoadEntity (bsp_entity_t *ent);
int PlaneTypeForNormal (vec3_t normal);
bool MakeBrushPlanes (mapbrush_t *b);
int FindIntPlane (int *inormal, int *iorigin);
void CreateBrush (int brushnum);
//=============================================================================
// csg
bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
vec3_t clipmins, vec3_t clipmaxs);
bspbrush_t *ChopBrushes (bspbrush_t *head);
bspbrush_t *InitialBrushList (bspbrush_t *list);
bspbrush_t *OptimizedBrushList (bspbrush_t *list);
//=============================================================================
// brushbsp
void WriteBrushList (char *name, bspbrush_t *brush, bool onlyvis);
bspbrush_t *CopyBrush (bspbrush_t *brush);
void SplitBrush (bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back);
tree_t *AllocTree (void);
node_t *AllocNode (void);
bspbrush_t *AllocBrush (int numsides);
int CountBrushList (bspbrush_t *brushes);
void FreeBrush (bspbrush_t *brushes);
vec_t BrushVolume (bspbrush_t *brush);
void BoundBrush (bspbrush_t *brush);
void FreeBrushList (bspbrush_t *brushes);
tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
//=============================================================================
// winding.c
winding_t *AllocWinding (int points);
vec_t WindingArea (winding_t *w);
void WindingCenter (winding_t *w, vec3_t center);
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back);
winding_t *CopyWinding (winding_t *w);
winding_t *ReverseWinding (winding_t *w);
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
void CheckWinding (winding_t *w);
void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
void RemoveColinearPoints (winding_t *w);
void FreeWinding (winding_t *w);
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
//=============================================================================
// portals.c
int VisibleContents (int contents);
void MakeHeadnodePortals (tree_t *tree);
void MakeNodePortal (node_t *node);
void SplitNodePortals (node_t *node);
bool Portal_VisFlood (portal_t *p);
bool FloodEntities (tree_t *tree);
void FillOutside (node_t *headnode);
void FloodAreas (tree_t *tree);
void MarkVisibleSides (tree_t *tree, int start, int end);
void FreePortal (portal_t *p);
void EmitAreaPortals (node_t *headnode);
void MakeTreePortals (tree_t *tree);
//=============================================================================
// shaders.c
int LoadShaderInfo( void );
bsp_shader_t *FindShader( const char *texture );
//=============================================================================
// leakfile.c
void LeakFile (tree_t *tree);
//=============================================================================
// prtfile.c
void NumberClusters( tree_t *tree );
void WritePortalFile( tree_t *tree );
//=============================================================================
// writebsp.c
void SetModelNumbers (void);
void SetLightStyles (void);
void BeginBSPFile (void);
void WriteBSP (node_t *headnode);
void EndBSPFile (void);
void BeginModel (void);
void EndModel (void);
//=============================================================================
// faces.c
void MakeFaces (node_t *headnode);
void FixTjuncs (node_t *headnode);
int GetEdge2 (int v1, int v2, face_t *f);
face_t *AllocFace (void);
void FreeFace (face_t *f);
void MergeNodeFaces (node_t *node);
//=============================================================================
// tree.c
void FreeTree (tree_t *tree);
void FreeTree_r (node_t *node);
void PrintTree_r (node_t *node, int depth);
void FreeTreePortals_r (node_t *node);
void PruneNodes_r (node_t *node);
void PruneNodes (node_t *node);
//=============================================================================
// vis.c
viswinding_t *NewVisWinding (int points);
void FreeVisWinding (viswinding_t *w);
viswinding_t *CopyVisWinding (viswinding_t *w);
extern int numportals;
extern int portalclusters;
extern visportal_t *portals;
extern leaf_t *leafs;
extern int c_portaltest, c_portalpass, c_portalcheck;
extern int c_portalskip, c_leafskip;
extern int c_vistest, c_mighttest;
extern int c_chains;
extern byte *vismap, *vismap_p, *vismap_end; // past visfile
extern int testlevel;
extern byte *uncompressedvis;
extern int leafbytes, leaflongs;
extern int portalbytes, portallongs;
void LeafFlow (int leafnum);
void BasePortalVis (int portalnum);
void BetterPortalVis (int portalnum);
void PortalFlow (int portalnum);
extern visportal_t *sorted_portals[MAX_MAP_PORTALS*2];
int CountBits( byte *bits, int numbits );
int PointInLeafnum( vec3_t point );
dleaf_t *PointInLeaf( vec3_t point );
bool PvsForOrigin( vec3_t org, byte *pvs );
byte *PhsForCluster( int cluster );
void CalcAmbientSounds( void );
//=============================================================================
// rad.c
#define LIGHTDISTBIAS 6800.0
typedef enum
{
emit_surface,
emit_point,
emit_spotlight,
emit_skylight
} emittype_t;
typedef struct tnode_s
{
int type;
vec3_t normal;
float dist;
int children[2];
int pad;
} tnode_t;
// the sum of all tranfer->transfer values for a given patch
// should equal exactly 0x10000, showing that all radiance
// reaches other patches
typedef struct
{
word patch;
word transfer;
} transfer_t;
typedef struct patch_s
{
winding_t *winding;
struct patch_s *next; // next in face
int numtransfers;
transfer_t *transfers;
int cluster; // for pvs checking
vec3_t origin;
dplane_t *plane;
bool sky;
vec3_t totallight; // accumulated by radiosity
// does NOT include light
// accounted for by direct lighting
float area;
// illuminance * reflectivity = radiosity
vec3_t reflectivity;
vec3_t baselight; // emissivity only
// each style 0 lightmap sample in the patch will be
// added up to get the average illuminance of the entire patch
vec3_t samplelight;
int samples; // for averaging direct light
} patch_t;
extern patch_t *face_patches[MAX_MAP_SURFACES];
extern bsp_entity_t *face_entity[MAX_MAP_SURFACES];
extern vec3_t face_offset[MAX_MAP_SURFACES]; // for rotating bmodels
extern patch_t patches[MAX_PATCHES];
extern tnode_t *tnodes;
extern uint num_patches;
extern int leafparents[MAX_MAP_LEAFS];
extern int nodeparents[MAX_MAP_NODES];
extern float lightscale;
extern float ambient;
void MakeShadowSplits (void);
//==============================================
extern float ambient, maxlight;
void LinkPlaneFaces (void);
extern int numbounce;
extern byte nodehit[MAX_MAP_NODES];
void BuildLightmaps (void);
void BuildFacelights (int facenum);
void FinalLightFace (int facenum);
int TestLine_r (int node, vec3_t start, vec3_t stop);
void CreateDirectLights (void);
extern dplane_t backplanes[MAX_MAP_PLANES];
extern int fakeplanes;// created planes for origin offset
extern float subdiv;
extern float direct_scale;
extern float entity_scale;
void MakeTnodes (dmodel_t *bm);
void MakePatches (void);
void SubdividePatches (void);
void PairEdges (void);
void CalcTextureReflectivity (void);
#endif//BSPLIB_H

370
xtools/bsplib/convert_ase.c Normal file
View File

@ -0,0 +1,370 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define CONVERT_ASE_C
/* dependencies */
#include "q3map2.h"
/*
ConvertSurface()
converts a bsp drawsurface to an ase chunk
*/
static void ConvertSurface( file_t *f, bspModel_t *model, int modelNum, bspDrawSurface_t *ds, int surfaceNum, vec3_t origin )
{
int i, v, face, a, b, c;
bspDrawVert_t *dv;
vec3_t normal;
char name[ 1024 ];
/* ignore patches for now */
if( ds->surfaceType != MST_PLANAR && ds->surfaceType != MST_TRISURF )
return;
/* print object header for each dsurf */
com.sprintf( name, "mat%dmodel%dsurf%d", ds->shaderNum, modelNum, surfaceNum );
FS_Printf( f, "*GEOMOBJECT\t{\r\n" );
FS_Printf( f, "\t*NODE_NAME\t\"%s\"\r\n", name );
FS_Printf( f, "\t*NODE_TM\t{\r\n" );
FS_Printf( f, "\t\t*NODE_NAME\t\"%s\"\r\n", name );
FS_Printf( f, "\t\t*INHERIT_POS\t0\t0\t0\r\n" );
FS_Printf( f, "\t\t*INHERIT_ROT\t0\t0\t0\r\n" );
FS_Printf( f, "\t\t*INHERIT_SCL\t0\t0\t0\r\n" );
FS_Printf( f, "\t\t*TM_ROW0\t1.0\t0\t0\r\n" );
FS_Printf( f, "\t\t*TM_ROW1\t0\t1.0\t0\r\n" );
FS_Printf( f, "\t\t*TM_ROW2\t0\t0\t1.0\r\n" );
FS_Printf( f, "\t\t*TM_ROW3\t0\t0\t0\r\n" );
FS_Printf( f, "\t\t*TM_POS\t%f\t%f\t%f\r\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
FS_Printf( f, "\t}\r\n" );
/* print mesh header */
FS_Printf( f, "\t*MESH\t{\r\n" );
FS_Printf( f, "\t\t*TIMEVALUE\t0\r\n" );
FS_Printf( f, "\t\t*MESH_NUMVERTEX\t%d\r\n", ds->numVerts );
FS_Printf( f, "\t\t*MESH_NUMFACES\t%d\r\n", ds->numIndexes / 3 );
switch( ds->surfaceType )
{
case MST_PLANAR:
FS_Printf( f, "\t\t*COMMENT\t\"SURFACETYPE\tMST_PLANAR\"\r\n" );
break;
case MST_TRISURF:
FS_Printf( f, "\t\t*COMMENT\t\"SURFACETYPE\tMST_TRISURF\"\r\n" );
break;
}
/* export vertex xyz */
FS_Printf( f, "\t\t*MESH_VERTEX_LIST\t{\r\n" );
for( i = 0; i < ds->numVerts; i++ )
{
v = i + ds->firstVert;
dv = &bspDrawVerts[ v ];
FS_Printf( f, "\t\t\t*MESH_VERTEX\t%d\t%f\t%f\t%f\r\n", i, dv->xyz[ 0 ], dv->xyz[ 1 ], dv->xyz[ 2 ] );
}
FS_Printf( f, "\t\t}\r\n" );
/* export vertex normals */
FS_Printf( f, "\t\t*MESH_NORMALS\t{\r\n" );
for( i = 0; i < ds->numIndexes; i += 3 )
{
face = (i / 3);
a = bspDrawIndexes[ i + ds->firstIndex ];
b = bspDrawIndexes[ i + ds->firstIndex + 1 ];
c = bspDrawIndexes[ i + ds->firstIndex + 2 ];
VectorCopy( bspDrawVerts[ a ].normal, normal );
VectorAdd( normal, bspDrawVerts[ b ].normal, normal );
VectorAdd( normal, bspDrawVerts[ c ].normal, normal );
if( VectorNormalizeLength( normal ))
FS_Printf( f, "\t\t\t*MESH_FACENORMAL\t%d\t%f\t%f\t%f\r\n", face, normal[ 0 ], normal[ 1 ], normal[ 2 ] );
}
for( i = 0; i < ds->numVerts; i++ )
{
v = i + ds->firstVert;
dv = &bspDrawVerts[ v ];
FS_Printf( f, "\t\t\t*MESH_VERTEXNORMAL\t%d\t%f\t%f\t%f\r\n", i, dv->normal[ 0 ], dv->normal[ 1 ], dv->normal[ 2 ] );
}
FS_Printf( f, "\t\t}\r\n" );
/* export faces */
FS_Printf( f, "\t\t*MESH_FACE_LIST\t{\r\n" );
for( i = 0; i < ds->numIndexes; i += 3 )
{
face = (i / 3);
a = bspDrawIndexes[ i + ds->firstIndex ];
c = bspDrawIndexes[ i + ds->firstIndex + 1 ];
b = bspDrawIndexes[ i + ds->firstIndex + 2 ];
FS_Printf( f, "\t\t\t*MESH_FACE\t%d\tA:\t%d\tB:\t%d\tC:\t%d\tAB:\t1\tBC:\t1\tCA:\t1\t*MESH_SMOOTHING\t0\t*MESH_MTLID\t0\r\n",
face, a, b, c );
}
FS_Printf( f, "\t\t}\r\n" );
/* export vertex st */
FS_Printf( f, "\t\t*MESH_NUMTVERTEX\t%d\r\n", ds->numVerts );
FS_Printf( f, "\t\t*MESH_TVERTLIST\t{\r\n" );
for( i = 0; i < ds->numVerts; i++ )
{
v = i + ds->firstVert;
dv = &bspDrawVerts[ v ];
FS_Printf( f, "\t\t\t*MESH_TVERT\t%d\t%f\t%f\t%f\r\n", i, dv->st[ 0 ], (1.0 - dv->st[ 1 ]), 1.0f );
}
FS_Printf( f, "\t\t}\r\n" );
/* export texture faces */
FS_Printf( f, "\t\t*MESH_NUMTVFACES\t%d\r\n", ds->numIndexes / 3 );
FS_Printf( f, "\t\t*MESH_TFACELIST\t{\r\n" );
for( i = 0; i < ds->numIndexes; i += 3 )
{
face = (i / 3);
a = bspDrawIndexes[ i + ds->firstIndex ];
c = bspDrawIndexes[ i + ds->firstIndex + 1 ];
b = bspDrawIndexes[ i + ds->firstIndex + 2 ];
FS_Printf( f, "\t\t\t*MESH_TFACE\t%d\t%d\t%d\t%d\r\n", face, a, b, c );
}
FS_Printf( f, "\t\t}\r\n" );
/* print mesh footer */
FS_Printf( f, "\t}\r\n" );
/* print object footer */
FS_Printf( f, "\t*PROP_MOTIONBLUR\t0\r\n" );
FS_Printf( f, "\t*PROP_CASTSHADOW\t1\r\n" );
FS_Printf( f, "\t*PROP_RECVSHADOW\t1\r\n" );
FS_Printf( f, "\t*MATERIAL_REF\t%d\r\n", ds->shaderNum );
FS_Printf( f, "}\r\n" );
}
/*
ConvertModel()
exports a bsp model to an ase chunk
*/
static void ConvertModel( file_t *f, bspModel_t *model, int modelNum, vec3_t origin )
{
int i, s;
bspDrawSurface_t *ds;
/* go through each drawsurf in the model */
for( i = 0; i < model->numBSPSurfaces; i++ )
{
s = i + model->firstBSPSurface;
ds = &bspDrawSurfaces[ s ];
ConvertSurface( f, model, modelNum, ds, s, origin );
}
}
/*
ConvertShader()
exports a bsp shader to an ase chunk
*/
/*
*MATERIAL 0 {
*MATERIAL_NAME "models/test/rock16l"
*MATERIAL_CLASS "Standard"
*MATERIAL_AMBIENT 0.5882 0.5882 0.5882
*MATERIAL_DIFFUSE 0.5882 0.5882 0.5882
*MATERIAL_SPECULAR 0.5882 0.5882 0.5882
*MATERIAL_SHINE 0.0000
*MATERIAL_SHINESTRENGTH 0.0000
*MATERIAL_TRANSPARENCY 0.0000
*MATERIAL_WIRESIZE 1.0000
*MATERIAL_SHADING Phong
*MATERIAL_XP_FALLOFF 0.0000
*MATERIAL_SELFILLUM 0.0000
*MATERIAL_FALLOFF In
*MATERIAL_XP_TYPE Filter
*MAP_DIFFUSE {
*MAP_NAME "Map #2"
*MAP_CLASS "Bitmap"
*MAP_SUBNO 1
*MAP_AMOUNT 1.0000
*BITMAP "models/test/rock16l"
*MAP_TYPE Screen
*UVW_U_OFFSET 0.0000
*UVW_V_OFFSET 0.0000
*UVW_U_TILING 1.0000
*UVW_V_TILING 1.0000
*UVW_ANGLE 0.0000
*UVW_BLUR 1.0000
*UVW_BLUR_OFFSET 0.0000
*UVW_NOUSE_AMT 1.0000
*UVW_NOISE_SIZE 1.0000
*UVW_NOISE_LEVEL 1
*UVW_NOISE_PHASE 0.0000
*BITMAP_FILTER Pyramidal
}
}
*/
static void ConvertShader( file_t *f, bspShader_t *shader, int shaderNum )
{
shaderInfo_t *si;
char *c, filename[ 1024 ];
/* get shader */
si = ShaderInfoForShader( shader->shader );
if( si == NULL )
{
MsgDev( D_WARN, "NULL shader in BSP\n" );
return;
}
/* set bitmap filename */
if( si->shaderImage->name[0] != '*' )
com.strcpy( filename, si->shaderImage->name );
else com.sprintf( filename, "%s.tga", si->shader );
for( c = filename; *c != '\0'; c++ )
if( *c == '/' )
*c = '\\';
/* print shader info */
FS_Printf( f, "\t*MATERIAL\t%d\t{\r\n", shaderNum );
FS_Printf( f, "\t\t*MATERIAL_NAME\t\"%s\"\r\n", shader->shader );
FS_Printf( f, "\t\t*MATERIAL_CLASS\t\"Standard\"\r\n" );
FS_Printf( f, "\t\t*MATERIAL_DIFFUSE\t%f\t%f\t%f\r\n", si->color[ 0 ], si->color[ 1 ], si->color[ 2 ] );
FS_Printf( f, "\t\t*MATERIAL_SHADING Phong\r\n" );
/* print map info */
FS_Printf( f, "\t\t*MAP_DIFFUSE\t{\r\n" );
FS_Printf( f, "\t\t\t*MAP_NAME\t\"%s\"\r\n", shader->shader );
FS_Printf( f, "\t\t\t*MAP_CLASS\t\"Bitmap\"\r\n");
FS_Printf( f, "\t\t\t*MAP_SUBNO\t1\r\n" );
FS_Printf( f, "\t\t\t*MAP_AMOUNT\t1.0\r\n" );
FS_Printf( f, "\t\t\t*MAP_TYPE\tScreen\r\n" );
FS_Printf( f, "\t\t\t*BITMAP\t\"..\\%s\"\r\n", filename );
FS_Printf( f, "\t\t\t*BITMAP_FILTER\tPyramidal\r\n" );
FS_Printf( f, "\t\t}\r\n" );
FS_Printf( f, "\t}\r\n" );
}
/*
ConvertBSPToASE()
exports an 3d studio ase file from the bsp
*/
int ConvertBSPToASE( char *bspName )
{
int i, modelNum;
file_t *f;
bspShader_t *shader;
bspModel_t *model;
entity_t *e;
vec3_t origin;
const char *key;
char name[ 1024 ], base[ 1024 ];
/* note it */
MsgDev( D_NOTE, "--- Convert BSP to ASE ---\n" );
/* create the ase filename from the bsp name */
com.strcpy( name, bspName );
FS_StripExtension( name );
com.strcat( name, ".ase" );
Msg( "writing %s\n", name );
FS_FileBase( bspName, base );
com.strcat( base, ".bsp" );
/* open it */
f = FS_Open( name, "wb" );
if( f == NULL ) Sys_Break( "Open failed on %s\n", name );
/* print header */
FS_Printf( f, "*3DSMAX_ASCIIEXPORT\t200\r\n" );
FS_Printf( f, "*COMMENT\t\"Generated by Q3Map2 (ydnar) -convert -format ase\"\r\n" );
FS_Printf( f, "*SCENE\t{\r\n" );
FS_Printf( f, "\t*SCENE_file_tNAME\t\"%s\"\r\n", base );
FS_Printf( f, "\t*SCENE_FIRSTFRAME\t0\r\n" );
FS_Printf( f, "\t*SCENE_LASTFRAME\t100\r\n" );
FS_Printf( f, "\t*SCENE_FRAMESPEED\t30\r\n" );
FS_Printf( f, "\t*SCENE_TICKSPERFRAME\t160\r\n" );
FS_Printf( f, "\t*SCENE_BACKGROUND_STATIC\t0.0000\t0.0000\t0.0000\r\n" );
FS_Printf( f, "\t*SCENE_AMBIENT_STATIC\t0.0000\t0.0000\t0.0000\r\n" );
FS_Printf( f, "}\r\n" );
/* print materials */
FS_Printf( f, "*MATERIAL_LIST\t{\r\n" );
FS_Printf( f, "\t*MATERIAL_COUNT\t%d\r\n", numBSPShaders );
for( i = 0; i < numBSPShaders; i++ )
{
shader = &bspShaders[ i ];
ConvertShader( f, shader, i );
}
FS_Printf( f, "}\r\n" );
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
/* get entity and model */
e = &entities[ i ];
if( i == 0 )
modelNum = 0;
else
{
key = ValueForKey( e, "model" );
if( key[ 0 ] != '*' )
continue;
modelNum = atoi( key + 1 );
}
model = &bspModels[ modelNum ];
/* get entity origin */
key = ValueForKey( e, "origin" );
if( key[ 0 ] == '\0' )
VectorClear( origin );
else
GetVectorForKey( e, "origin", origin );
/* convert model */
ConvertModel( f, model, modelNum, origin );
}
/* close the file and return */
FS_Close( f );
/* return to sender */
return 0;
}

440
xtools/bsplib/convert_map.c Normal file
View File

@ -0,0 +1,440 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define CONVERT_MAP_C
/* dependencies */
#include "q3map2.h"
/*
ConvertBrush()
exports a map brush
*/
#define SNAP_FLOAT_TO_INT 4
#define SNAP_INT_TO_FLOAT (1.0 / SNAP_FLOAT_TO_INT)
static void ConvertBrush( file_t *f, int num, bspBrush_t *brush, vec3_t origin )
{
int i, j;
bspBrushSide_t *side;
side_t *buildSide;
bspShader_t *shader;
char *texture;
bspPlane_t *plane;
vec3_t pts[3];
/* start brush */
FS_Printf( f, "\t// brush %d\n", num );
FS_Printf( f, "\t{\n" );
/* clear out build brush */
for( i = 0; i < buildBrush->numsides; i++ )
{
buildSide = &buildBrush->sides[ i ];
if( buildSide->winding != NULL )
{
FreeWinding( buildSide->winding );
buildSide->winding = NULL;
}
}
buildBrush->numsides = 0;
/* iterate through bsp brush sides */
for( i = 0; i < brush->numSides; i++ )
{
/* get side */
side = &bspBrushSides[ brush->firstSide + i ];
/* get shader */
if( side->shaderNum < 0 || side->shaderNum >= numBSPShaders )
continue;
shader = &bspShaders[ side->shaderNum ];
if( !com.stricmp( shader->shader, "default" ) || !com.stricmp( shader->shader, "noshader" ) )
continue;
/* get plane */
plane = &bspPlanes[ side->planeNum ];
/* add build side */
buildSide = &buildBrush->sides[ buildBrush->numsides ];
buildBrush->numsides++;
/* tag it */
buildSide->shaderInfo = ShaderInfoForShader( shader->shader );
buildSide->planenum = side->planeNum;
buildSide->winding = NULL;
}
/* make brush windings */
if( !CreateBrushWindings( buildBrush ) )
return;
/* iterate through build brush sides */
for( i = 0; i < buildBrush->numsides; i++ )
{
/* get build side */
buildSide = &buildBrush->sides[ i ];
/* dummy check */
if( buildSide->shaderInfo == NULL || buildSide->winding == NULL )
continue;
/* get texture name */
if( !com.strnicmp( buildSide->shaderInfo->shader, "textures/", 9 ))
texture = buildSide->shaderInfo->shader + 9;
else texture = buildSide->shaderInfo->shader;
/* get plane points and offset by origin */
for( j = 0; j < 3; j++ )
{
VectorAdd( buildSide->winding->p[ j ], origin, pts[ j ] );
//% pts[ j ][ 0 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 0 ] * SNAP_FLOAT_TO_INT + 0.5f );
//% pts[ j ][ 1 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 1 ] * SNAP_FLOAT_TO_INT + 0.5f );
//% pts[ j ][ 2 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 2 ] * SNAP_FLOAT_TO_INT + 0.5f );
}
/* print brush side */
/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
FS_Printf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) %s 0 0 0 0.5 0.5 0 0 0\n",
pts[ 0 ][ 0 ], pts[ 0 ][ 1 ], pts[ 0 ][ 2 ],
pts[ 1 ][ 0 ], pts[ 1 ][ 1 ], pts[ 1 ][ 2 ],
pts[ 2 ][ 0 ], pts[ 2 ][ 1 ], pts[ 2 ][ 2 ],
texture );
}
/* end brush */
FS_Printf( f, "\t}\n\n" );
}
#if 0
/* iterate through the brush sides (ignore the first 6 bevel planes) */
for( i = 0; i < brush->numSides; i++ )
{
/* get side */
side = &bspBrushSides[ brush->firstSide + i ];
/* get shader */
if( side->shaderNum < 0 || side->shaderNum >= numBSPShaders )
continue;
shader = &bspShaders[ side->shaderNum ];
if( !com.stricmp( shader->shader, "default" ) || !com.stricmp( shader->shader, "noshader" ))
continue;
/* get texture name */
if( !com.strnicmp( shader->shader, "textures/", 9 ))
texture = shader->shader + 9;
else texture = shader->shader;
/* get plane */
plane = &bspPlanes[ side->planeNum ];
/* make plane points */
{
vec3_t vecs[ 2 ];
VectorVectors( plane->normal, vecs[ 0 ], vecs[ 1 ] );
VectorMA( vec3_origin, plane->dist, plane->normal, pts[ 0 ] );
VectorMA( pts[ 0 ], 256.0f, vecs[ 0 ], pts[ 1 ] );
VectorMA( pts[ 0 ], 256.0f, vecs[ 1 ], pts[ 2 ] );
}
/* offset by origin */
for( j = 0; j < 3; j++ )
VectorAdd( pts[ j ], origin, pts[ j ] );
/* print brush side */
/* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */
FS_Printf( f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) %s 0 0 0 0.5 0.5 0 0 0\n",
pts[ 0 ][ 0 ], pts[ 0 ][ 1 ], pts[ 0 ][ 2 ],
pts[ 1 ][ 0 ], pts[ 1 ][ 1 ], pts[ 1 ][ 2 ],
pts[ 2 ][ 0 ], pts[ 2 ][ 1 ], pts[ 2 ][ 2 ],
texture );
}
#endif
/*
ConvertPatch()
converts a bsp patch to a map patch
{
patchDef2
{
base_wall/concrete
( 9 3 0 0 0 )
(
( ( 168 168 -192 0 2 ) ( 168 168 -64 0 1 ) ( 168 168 64 0 0 ) ... )
...
)
}
}
*/
static void ConvertPatch( file_t *f, int num, bspDrawSurface_t *ds, vec3_t origin )
{
int x, y;
bspShader_t *shader;
char *texture;
bspDrawVert_t *dv;
vec3_t xyz;
/* only patches */
if( ds->surfaceType != MST_PATCH )
return;
/* get shader */
if( ds->shaderNum < 0 || ds->shaderNum >= numBSPShaders )
return;
shader = &bspShaders[ ds->shaderNum ];
/* get texture name */
if( !com.strnicmp( shader->shader, "textures/", 9 ))
texture = shader->shader + 9;
else texture = shader->shader;
/* start patch */
FS_Printf( f, "\t// patch %d\n", num );
FS_Printf( f, "\t{\n" );
FS_Printf( f, "\t\tpatchDef2\n" );
FS_Printf( f, "\t\t{\n" );
FS_Printf( f, "\t\t\t%s\n", texture );
FS_Printf( f, "\t\t\t( %d %d 0 0 0 )\n", ds->patchWidth, ds->patchHeight );
FS_Printf( f, "\t\t\t(\n" );
/* iterate through the verts */
for( x = 0; x < ds->patchWidth; x++ )
{
/* start row */
FS_Printf( f, "\t\t\t\t(" );
/* iterate through the row */
for( y = 0; y < ds->patchHeight; y++ )
{
/* get vert */
dv = &bspDrawVerts[ ds->firstVert + (y * ds->patchWidth) + x ];
/* offset it */
VectorAdd( origin, dv->xyz, xyz );
/* print vertex */
FS_Printf( f, " ( %f %f %f %f %f )", xyz[ 0 ], xyz[ 1 ], xyz[ 2 ], dv->st[ 0 ], dv->st[ 1 ] );
}
/* end row */
FS_Printf( f, " )\n" );
}
/* end patch */
FS_Printf( f, "\t\t\t)\n" );
FS_Printf( f, "\t\t}\n" );
FS_Printf( f, "\t}\n\n" );
}
/*
ConvertModel()
exports a bsp model to a map file
*/
static void ConvertModel( file_t *f, bspModel_t *model, int modelNum, vec3_t origin )
{
int i, num;
bspBrush_t *brush;
bspDrawSurface_t *ds;
/* convert bsp planes to map planes */
nummapplanes = numBSPPlanes;
for( i = 0; i < numBSPPlanes; i++ )
{
VectorCopy( bspPlanes[ i ].normal, mapplanes[ i ].normal );
mapplanes[ i ].dist = bspPlanes[ i ].dist;
mapplanes[ i ].type = PlaneTypeForNormal( mapplanes[ i ].normal );
mapplanes[ i ].hash_chain = NULL;
}
/* allocate a build brush */
buildBrush = AllocBrush( 512 );
buildBrush->entityNum = 0;
buildBrush->original = buildBrush;
/* go through each brush in the model */
for( i = 0; i < model->numBSPBrushes; i++ )
{
num = i + model->firstBSPBrush;
brush = &bspBrushes[ num ];
ConvertBrush( f, num, brush, origin );
}
/* free the build brush */
Mem_Free( buildBrush );
/* go through each drawsurf in the model */
for( i = 0; i < model->numBSPSurfaces; i++ )
{
num = i + model->firstBSPSurface;
ds = &bspDrawSurfaces[ num ];
/* we only love patches */
if( ds->surfaceType == MST_PATCH )
ConvertPatch( f, num, ds, origin );
}
}
/*
ConvertEPairs()
exports entity key/value pairs to a map file
*/
static void ConvertEPairs( file_t *f, entity_t *e )
{
epair_t *ep;
/* walk epairs */
for( ep = e->epairs; ep != NULL; ep = ep->next )
{
/* ignore empty keys/values */
if( ep->key[ 0 ] == '\0' || ep->value[ 0 ] == '\0' )
continue;
/* ignore model keys with * prefixed values */
if( !com.stricmp( ep->key, "model" ) && ep->value[ 0 ] == '*' )
continue;
/* emit the epair */
FS_Printf( f, "\t\"%s\" \"%s\"\n", ep->key, ep->value );
}
}
/*
ConvertBSPToMap()
exports an quake map file from the bsp
*/
int ConvertBSPToMap( char *bspName )
{
int i, modelNum;
file_t *f;
bspModel_t *model;
entity_t *e;
vec3_t origin;
const char *value;
char name[MAX_SYSPATH], base[MAX_SYSPATH];
/* note it */
Msg( "--- Convert BSP to MAP ---\n" );
/* create the bsp filename from the bsp name */
com.strcpy( name, bspName );
FS_StripExtension( name );
com.strcat( name, "_converted.map" );
Msg( "writing %s\n", name );
FS_FileBase( bspName, base );
com.strcat( base, ".bsp" );
/* open it */
f = FS_Open( name, "wb" );
if( f == NULL ) Sys_Break( "Open failed on %s\n", name );
/* print header */
FS_Printf( f, "// Generated by Q3Map2 (ydnar) -convert -format map\n" );
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
/* get entity */
e = &entities[ i ];
/* start entity */
FS_Printf( f, "// entity %d\n", i );
FS_Printf( f, "{\n" );
/* export keys */
ConvertEPairs( f, e );
FS_Printf( f, "\n" );
/* get model num */
if( i == 0 )
modelNum = 0;
else
{
value = ValueForKey( e, "model" );
if( value[ 0 ] == '*' )
modelNum = atoi( value + 1 );
else
modelNum = -1;
}
/* only handle bsp models */
if( modelNum >= 0 )
{
/* get model */
model = &bspModels[ modelNum ];
/* get entity origin */
value = ValueForKey( e, "origin" );
if( value[ 0 ] == '\0' )
VectorClear( origin );
else
GetVectorForKey( e, "origin", origin );
/* convert model */
ConvertModel( f, model, modelNum, origin );
}
/* end entity */
FS_Printf( f, "}\n\n" );
}
/* close the file and return */
FS_Close( f );
/* return to sender */
return 0;
}

View File

@ -1,533 +0,0 @@
#include "bsplib.h"
#include "const.h"
/*
tag all brushes with original contents
brushes may contain multiple contents
there will be no brush overlap after csg phase
each side has a count of the other sides it splits
the best split will be the one that minimizes the total split counts
of all remaining sides
precalc side on plane table
evaluate split side
{
cost = 0
for all sides
for all sides
get
if side splits side and splitside is on same child
cost++;
}
*/
void SplitBrush2 (bspbrush_t *brush, int planenum,
bspbrush_t **front, bspbrush_t **back)
{
SplitBrush (brush, planenum, front, back);
#if 0
if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
(*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
(*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
#endif
}
/*
===============
SubtractBrush
Returns a list of brushes that remain after B is subtracted from A.
May by empty if A is contained inside B.
The originals are undisturbed.
===============
*/
bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
{ // a - b = out (list)
int i;
bspbrush_t *front, *back;
bspbrush_t *out, *in;
in = a;
out = NULL;
for (i=0 ; i<b->numsides && in ; i++)
{
SplitBrush2 (in, b->sides[i].planenum, &front, &back);
if (in != a)
FreeBrush (in);
if (front)
{ // add to list
front->next = out;
out = front;
}
in = back;
}
if (in)
FreeBrush (in);
else
{ // didn't really intersect
FreeBrushList (out);
return a;
}
return out;
}
/*
===============
IntersectBrush
Returns a single brush made up by the intersection of the
two provided brushes, or NULL if they are disjoint.
The originals are undisturbed.
===============
*/
bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
{
int i;
bspbrush_t *front, *back;
bspbrush_t *in;
in = a;
for (i=0 ; i<b->numsides && in ; i++)
{
SplitBrush2 (in, b->sides[i].planenum, &front, &back);
if (in != a)
FreeBrush (in);
if (front)
FreeBrush (front);
in = back;
}
if (in == a)
return NULL;
in->next = NULL;
return in;
}
/*
===============
BrushesDisjoint
Returns true if the two brushes definately do not intersect.
There will be false negatives for some non-axial combinations.
===============
*/
bool BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
{
int i, j;
// check bounding boxes
for (i=0 ; i<3 ; i++)
if (a->mins[i] >= b->maxs[i]
|| a->maxs[i] <= b->mins[i])
return true; // bounding boxes don't overlap
// check for opposing planes
for (i=0 ; i<a->numsides ; i++)
{
for (j=0 ; j<b->numsides ; j++)
{
if (a->sides[i].planenum ==
(b->sides[j].planenum^1) )
return true; // opposite planes, so not touching
}
}
return false; // might intersect
}
int minplanenums[3];
int maxplanenums[3];
/*
===============
ClipBrushToBox
Any planes shared with the box edge will be set to no texinfo
===============
*/
bspbrush_t *ClipBrushToBox (bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs)
{
int i, j;
bspbrush_t *front, *back;
int p;
for (j=0 ; j<2 ; j++)
{
if (brush->maxs[j] > clipmaxs[j])
{
SplitBrush (brush, maxplanenums[j], &front, &back);
if (front)
FreeBrush (front);
brush = back;
if (!brush)
return NULL;
}
if (brush->mins[j] < clipmins[j])
{
SplitBrush (brush, minplanenums[j], &front, &back);
if (back)
FreeBrush (back);
brush = front;
if (!brush)
return NULL;
}
}
// remove any colinear faces
for (i=0 ; i<brush->numsides ; i++)
{
p = brush->sides[i].planenum & ~1;
if (p == maxplanenums[0] || p == maxplanenums[1]
|| p == minplanenums[0] || p == minplanenums[1])
{
brush->sides[i].texinfo = TEXINFO_NODE;
brush->sides[i].visible = false;
}
}
return brush;
}
/*
===============
MakeBspBrushList
===============
*/
bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
vec3_t clipmins, vec3_t clipmaxs)
{
mapbrush_t *mb;
bspbrush_t *brushlist, *newbrush;
int i, j;
int c_faces;
int c_brushes;
int numsides;
int vis;
vec3_t normal;
float dist;
for (i=0 ; i<2 ; i++)
{
VectorClear (normal);
normal[i] = 1;
dist = clipmaxs[i];
maxplanenums[i] = FindFloatPlane (normal, dist);
dist = clipmins[i];
minplanenums[i] = FindFloatPlane (normal, dist);
}
brushlist = NULL;
c_faces = 0;
c_brushes = 0;
for (i=startbrush ; i<endbrush ; i++)
{
mb = &mapbrushes[i];
numsides = mb->numsides;
if (!numsides)
continue;
// make sure the brush has at least one face showing
vis = 0;
for (j=0 ; j<numsides ; j++)
if (mb->original_sides[j].visible && mb->original_sides[j].winding)
vis++;
#if 0
if (!vis)
continue; // no faces at all
#endif
// if the brush is outside the clip area, skip it
for (j=0 ; j<3 ; j++)
if (mb->mins[j] >= clipmaxs[j]
|| mb->maxs[j] <= clipmins[j])
break;
if (j != 3)
continue;
//
// make a copy of the brush
//
newbrush = AllocBrush (mb->numsides);
newbrush->original = mb;
newbrush->numsides = mb->numsides;
memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t));
for (j=0 ; j<numsides ; j++)
{
if (newbrush->sides[j].winding)
newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
if (newbrush->sides[j].surf & SURF_HINT)
newbrush->sides[j].visible = true; // hints are always visible
}
VectorCopy (mb->mins, newbrush->mins);
VectorCopy (mb->maxs, newbrush->maxs);
//
// carve off anything outside the clip box
//
newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
if (!newbrush)
continue;
c_faces += vis;
c_brushes++;
newbrush->next = brushlist;
brushlist = newbrush;
}
return brushlist;
}
/*
===============
AddBspBrushListToTail
===============
*/
bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
{
bspbrush_t *walk, *next;
for (walk=list ; walk ; walk=next)
{ // add to end of list
next = walk->next;
walk->next = NULL;
tail->next = walk;
tail = walk;
}
return tail;
}
/*
===========
CullList
Builds a new list that doesn't hold the given brush
===========
*/
bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
{
bspbrush_t *newlist;
bspbrush_t *next;
newlist = NULL;
for ( ; list ; list = next)
{
next = list->next;
if (list == skip1)
{
FreeBrush (list);
continue;
}
list->next = newlist;
newlist = list;
}
return newlist;
}
/*
==================
BrushGE
Returns true if b1 is allowed to bite b2
==================
*/
bool BrushGE (bspbrush_t *b1, bspbrush_t *b2)
{
// detail brushes never bite structural brushes
if ( (b1->original->contents & CONTENTS_DETAIL)
&& !(b2->original->contents & CONTENTS_DETAIL) )
return false;
if (b1->original->contents & CONTENTS_SOLID)
return true;
return false;
}
/*
=================
ChopBrushes
Carves any intersecting solid brushes into the minimum number
of non-intersecting brushes.
=================
*/
bspbrush_t *ChopBrushes (bspbrush_t *head)
{
bspbrush_t *b1, *b2, *next;
bspbrush_t *tail;
bspbrush_t *keep;
bspbrush_t *sub, *sub2;
int c1, c2;
keep = NULL;
newlist:
// find tail
if (!head)
return NULL;
for (tail=head ; tail->next ; tail=tail->next)
;
for (b1=head ; b1 ; b1=next)
{
next = b1->next;
for (b2=b1->next ; b2 ; b2 = b2->next)
{
if (BrushesDisjoint (b1, b2))
continue;
sub = NULL;
sub2 = NULL;
c1 = 999999;
c2 = 999999;
if ( BrushGE (b2, b1) )
{
sub = SubtractBrush (b1, b2);
if (sub == b1)
continue; // didn't really intersect
if (!sub)
{ // b1 is swallowed by b2
head = CullList (b1, b1);
goto newlist;
}
c1 = CountBrushList (sub);
}
if ( BrushGE (b1, b2) )
{
sub2 = SubtractBrush (b2, b1);
if (sub2 == b2)
continue; // didn't really intersect
if (!sub2)
{ // b2 is swallowed by b1
FreeBrushList (sub);
head = CullList (b1, b2);
goto newlist;
}
c2 = CountBrushList (sub2);
}
if (!sub && !sub2)
continue; // neither one can bite
// only accept if it didn't fragment
// (commening this out allows full fragmentation)
if (c1 > 1 && c2 > 1)
{
if (sub2)
FreeBrushList (sub2);
if (sub)
FreeBrushList (sub);
continue;
}
if (c1 < c2)
{
if (sub2)
FreeBrushList (sub2);
tail = AddBrushListToTail (sub, tail);
head = CullList (b1, b1);
goto newlist;
}
else
{
if (sub)
FreeBrushList (sub);
tail = AddBrushListToTail (sub2, tail);
head = CullList (b1, b2);
goto newlist;
}
}
if (!b2)
{ // b1 is no longer intersecting anything, so keep it
b1->next = keep;
keep = b1;
}
}
return keep;
}
/*
=================
InitialBrushList
=================
*/
bspbrush_t *InitialBrushList (bspbrush_t *list)
{
bspbrush_t *b;
bspbrush_t *out, *newb;
int i;
// only return brushes that have visible faces
out = NULL;
for (b=list ; b ; b=b->next)
{
#if 0
for (i=0 ; i<b->numsides ; i++)
if (b->sides[i].visible)
break;
if (i == b->numsides)
continue;
#endif
newb = CopyBrush (b);
newb->next = out;
out = newb;
// clear visible, so it must be set by MarkVisibleFaces_r
// to be used in the optimized list
for (i=0 ; i<b->numsides ; i++)
{
newb->sides[i].original = &b->sides[i];
// newb->sides[i].visible = true;
b->sides[i].visible = false;
}
}
return out;
}
/*
=================
OptimizedBrushList
=================
*/
bspbrush_t *OptimizedBrushList (bspbrush_t *list)
{
bspbrush_t *b;
bspbrush_t *out, *newb;
int i;
// only return brushes that have visible faces
out = NULL;
for (b=list ; b ; b=b->next)
{
for (i=0 ; i<b->numsides ; i++)
if (b->sides[i].visible)
break;
if (i == b->numsides)
continue;
newb = CopyBrush (b);
newb->next = out;
out = newb;
}
return out;
}

900
xtools/bsplib/decals.c Normal file
View File

@ -0,0 +1,900 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define DECALS_C
/* dependencies */
#include "q3map2.h"
#define MAX_PROJECTORS 1024
typedef struct decalProjector_s
{
shaderInfo_t *si;
vec3_t mins, maxs;
vec3_t center;
float radius, radius2;
int numPlanes; /* either 5 or 6, for quad or triangle projectors */
vec4_t planes[6];
vec4_t texMat[2];
} decalProjector_t;
static int numProjectors = 0;
static decalProjector_t projectors[ MAX_PROJECTORS ];
static int numDecalSurfaces = 0;
static vec3_t entityOrigin;
/*
DVectorNormalize()
normalizes a vector, returns the length, operates using doubles
*/
typedef double dvec_t;
typedef dvec_t dvec3_t[ 3 ];
dvec_t DVectorNormalize( dvec3_t in, dvec3_t out )
{
dvec_t len, ilen;
len = (dvec_t) sqrt( in[ 0 ] * in[ 0 ] + in[ 1 ] * in[ 1 ] + in[ 2 ] * in[ 2 ] );
if( len == 0.0 )
{
VectorClear( out );
return 0.0;
}
ilen = 1.0 / len;
out[ 0 ] = in[ 0 ] * ilen;
out[ 1 ] = in[ 1 ] * ilen;
out[ 2 ] = in[ 2 ] * ilen;
return len;
}
/*
MakeTextureMatrix()
generates a texture projection matrix for a triangle
returns false if a texture matrix cannot be created
*/
#define Vector2Subtract(a,b,c) ((c)[ 0 ] = (a)[ 0 ] - (b)[ 0 ], (c)[ 1 ] = (a)[ 1 ] - (b)[ 1 ])
static boolean MakeTextureMatrix( decalProjector_t *dp, vec4_t projection, bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *c )
{
int i, j;
double bb, s, t, d;
dvec3_t pa, pb, pc;
dvec3_t bary, xyz;
dvec3_t vecs[ 3 ], axis[ 3 ], lengths;
/* project triangle onto plane of projection */
d = DotProduct( a->xyz, projection ) - projection[ 3 ];
VectorMA( a->xyz, -d, projection, pa );
d = DotProduct( b->xyz, projection ) - projection[ 3 ];
VectorMA( b->xyz, -d, projection, pb );
d = DotProduct( c->xyz, projection ) - projection[ 3 ];
VectorMA( c->xyz, -d, projection, pc );
/* two methods */
#if 1
{
/* old code */
/* calculate barycentric basis for the triangle */
bb = (b->st[ 0 ] - a->st[ 0 ]) * (c->st[ 1 ] - a->st[ 1 ]) - (c->st[ 0 ] - a->st[ 0 ]) * (b->st[ 1 ] - a->st[ 1 ]);
if( fabs( bb ) < 0.00000001 )
return false;
/* calculate texture origin */
#if 0
s = 0.0;
t = 0.0;
bary[ 0 ] = ((b->st[ 0 ] - s) * (c->st[ 1 ] - t) - (c->st[ 0 ] - s) * (b->st[ 1 ] - t)) / bb;
bary[ 1 ] = ((c->st[ 0 ] - s) * (a->st[ 1 ] - t) - (a->st[ 0 ] - s) * (c->st[ 1 ] - t)) / bb;
bary[ 2 ] = ((a->st[ 0 ] - s) * (b->st[ 1 ] - t) - (b->st[ 0 ] - s) * (a->st[ 1 ] - t)) / bb;
origin[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
origin[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
origin[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
#endif
/* calculate s vector */
s = a->st[ 0 ] + 1.0;
t = a->st[ 1 ] + 0.0;
bary[ 0 ] = ((b->st[ 0 ] - s) * (c->st[ 1 ] - t) - (c->st[ 0 ] - s) * (b->st[ 1 ] - t)) / bb;
bary[ 1 ] = ((c->st[ 0 ] - s) * (a->st[ 1 ] - t) - (a->st[ 0 ] - s) * (c->st[ 1 ] - t)) / bb;
bary[ 2 ] = ((a->st[ 0 ] - s) * (b->st[ 1 ] - t) - (b->st[ 0 ] - s) * (a->st[ 1 ] - t)) / bb;
xyz[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
xyz[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
xyz[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
//% VectorSubtract( xyz, origin, vecs[ 0 ] );
VectorSubtract( xyz, pa, vecs[ 0 ] );
/* calculate t vector */
s = a->st[ 0 ] + 0.0;
t = a->st[ 1 ] + 1.0;
bary[ 0 ] = ((b->st[ 0 ] - s) * (c->st[ 1 ] - t) - (c->st[ 0 ] - s) * (b->st[ 1 ] - t)) / bb;
bary[ 1 ] = ((c->st[ 0 ] - s) * (a->st[ 1 ] - t) - (a->st[ 0 ] - s) * (c->st[ 1 ] - t)) / bb;
bary[ 2 ] = ((a->st[ 0 ] - s) * (b->st[ 1 ] - t) - (b->st[ 0 ] - s) * (a->st[ 1 ] - t)) / bb;
xyz[ 0 ] = bary[ 0 ] * pa[ 0 ] + bary[ 1 ] * pb[ 0 ] + bary[ 2 ] * pc[ 0 ];
xyz[ 1 ] = bary[ 0 ] * pa[ 1 ] + bary[ 1 ] * pb[ 1 ] + bary[ 2 ] * pc[ 1 ];
xyz[ 2 ] = bary[ 0 ] * pa[ 2 ] + bary[ 1 ] * pb[ 2 ] + bary[ 2 ] * pc[ 2 ];
//% VectorSubtract( xyz, origin, vecs[ 1 ] );
VectorSubtract( xyz, pa, vecs[ 1 ] );
/* calcuate r vector */
VectorScale( projection, -1.0, vecs[ 2 ] );
/* calculate transform axis */
for( i = 0; i < 3; i++ )
lengths[ i ] = DVectorNormalize( vecs[ i ], axis[ i ] );
for( i = 0; i < 2; i++ )
for( j = 0; j < 3; j++ )
dp->texMat[ i ][ j ] = lengths[ i ] > 0.0 ? (axis[ i ][ j ] / lengths[ i ]) : 0.0;
//% dp->texMat[ i ][ j ] = fabs( vecs[ i ][ j ] ) > 0.0 ? (1.0 / vecs[ i ][ j ]) : 0.0;
//% dp->texMat[ i ][ j ] = axis[ i ][ j ] > 0.0 ? (1.0 / axis[ i ][ j ]) : 0.0;
/* calculalate translation component */
dp->texMat[ 0 ][ 3 ] = a->st[ 0 ] - DotProduct( a->xyz, dp->texMat[ 0 ] );
dp->texMat[ 1 ][ 3 ] = a->st[ 1 ] - DotProduct( a->xyz, dp->texMat[ 1 ] );
}
#else
{
int k;
dvec3_t origin, deltas[ 3 ];
double texDeltas[ 3 ][ 2 ];
double delta, texDelta;
/* new code */
/* calculate deltas */
VectorSubtract( pa, pb, deltas[ 0 ] );
VectorSubtract( pa, pc, deltas[ 1 ] );
VectorSubtract( pb, pc, deltas[ 2 ] );
Vector2Subtract( a->st, b->st, texDeltas[ 0 ] );
Vector2Subtract( a->st, c->st, texDeltas[ 1 ] );
Vector2Subtract( b->st, c->st, texDeltas[ 2 ] );
/* walk st */
for( i = 0; i < 2; i++ )
{
/* walk xyz */
for( j = 0; j < 3; j++ )
{
/* clear deltas */
delta = 0.0;
texDelta = 0.0;
/* walk deltas */
for( k = 0; k < 3; k++ )
{
if( fabs( deltas[ k ][ j ] ) > delta &&
fabs( texDeltas[ k ][ i ] ) > texDelta )
{
delta = deltas[ k ][ j ];
texDelta = texDeltas[ k ][ i ];
}
}
/* set texture matrix component */
if( fabs( delta ) > 0.0 )
dp->texMat[ i ][ j ] = texDelta / delta;
else dp->texMat[ i ][ j ] = 0.0;
}
/* set translation component */
dp->texMat[ i ][ 3 ] = a->st[ i ] - DotProduct( pa, dp->texMat[ i ] );
}
}
#endif
/* debug code */
#if 1
MsgDev( D_NOTE, "Mat: [ %f %f %f %f ] [ %f %f %f %f ] Theta: %f (%f)\n",
dp->texMat[ 0 ][ 0 ], dp->texMat[ 0 ][ 1 ], dp->texMat[ 0 ][ 2 ], dp->texMat[ 0 ][ 3 ],
dp->texMat[ 1 ][ 0 ], dp->texMat[ 1 ][ 1 ], dp->texMat[ 1 ][ 2 ], dp->texMat[ 1 ][ 3 ],
RAD2DEG( acos( DotProduct( dp->texMat[ 0 ], dp->texMat[ 1 ] ) ) ),
RAD2DEG( acos( DotProduct( axis[ 0 ], axis[ 1 ] ) ) ) );
MsgDev( D_NOTE, "XYZ: %f %f %f ST: %f %f ST(t): %f %f\n",
a->xyz[ 0 ], a->xyz[ 1 ], a->xyz[ 2 ],
a->st[ 0 ], a->st[ 1 ],
DotProduct( a->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ], DotProduct( a->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ] );
#endif
/* test texture matrix */
s = DotProduct( a->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
t = DotProduct( a->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
if( fabs( s - a->st[ 0 ] ) > 0.01 || fabs( t - a->st[ 1 ] ) > 0.01 )
{
MsgDev( D_ERROR, "Bad texture matrix! (A) (%f, %f) != (%f, %f)\n",
s, t, a->st[ 0 ], a->st[ 1 ] );
//% return false;
}
s = DotProduct( b->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
t = DotProduct( b->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
if( fabs( s - b->st[ 0 ] ) > 0.01 || fabs( t - b->st[ 1 ] ) > 0.01 )
{
MsgDev( D_ERROR, "Bad texture matrix! (B) (%f, %f) != (%f, %f)\n",
s, t, b->st[ 0 ], b->st[ 1 ] );
//% return false;
}
s = DotProduct( c->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
t = DotProduct( c->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
if( fabs( s - c->st[ 0 ] ) > 0.01 || fabs( t - c->st[ 1 ] ) > 0.01 )
{
MsgDev( D_ERROR, "Bad texture matrix! (C) (%f, %f) != (%f, %f)\n",
s, t, c->st[ 0 ], c->st[ 1 ] );
//% return false;
}
/* disco */
return true;
}
/*
TransformDecalProjector()
transforms a decal projector
note: non-normalized axes will screw up the plane transform
*/
static void TransformDecalProjector( decalProjector_t *in, vec3_t axis[ 3 ], vec3_t origin, decalProjector_t *out )
{
int i;
/* copy misc stuff */
out->si = in->si;
out->numPlanes = in->numPlanes;
/* translate bounding box and sphere (note: rotated projector bounding box will be invalid!) */
VectorSubtract( in->mins, origin, out->mins );
VectorSubtract( in->maxs, origin, out->maxs );
VectorSubtract( in->center, origin, out->center );
out->radius = in->radius;
out->radius2 = in->radius2;
/* translate planes */
for( i = 0; i < in->numPlanes; i++ )
{
out->planes[ i ][ 0 ] = DotProduct( in->planes[ i ], axis[ 0 ] );
out->planes[ i ][ 1 ] = DotProduct( in->planes[ i ], axis[ 1 ] );
out->planes[ i ][ 2 ] = DotProduct( in->planes[ i ], axis[ 2 ] );
out->planes[ i ][ 3 ] = in->planes[ i ][ 3 ] - DotProduct( out->planes[ i ], origin );
}
/* translate texture matrix */
for( i = 0; i < 2; i++ )
{
out->texMat[ i ][ 0 ] = DotProduct( in->texMat[ i ], axis[ 0 ] );
out->texMat[ i ][ 1 ] = DotProduct( in->texMat[ i ], axis[ 1 ] );
out->texMat[ i ][ 2 ] = DotProduct( in->texMat[ i ], axis[ 2 ] );
out->texMat[ i ][ 3 ] = in->texMat[ i ][ 3 ] + DotProduct( out->texMat[ i ], origin );
}
}
/*
MakeDecalProjector()
creates a new decal projector from a triangle
*/
static int MakeDecalProjector( shaderInfo_t *si, vec4_t projection, float distance, int numVerts, bspDrawVert_t **dv )
{
int i, j;
decalProjector_t *dp;
vec3_t xyz;
/* dummy check */
if( numVerts != 3 && numVerts != 4 )
return -1;
/* limit check */
if( numProjectors >= MAX_PROJECTORS )
{
MsgDev( D_WARN, "MAX_PROJECTORS (%d) exceeded, no more decal projectors available.\n", MAX_PROJECTORS );
return -2;
}
/* create a new projector */
dp = &projectors[ numProjectors ];
Mem_Set( dp, 0, sizeof( *dp ));
/* basic setup */
dp->si = si;
dp->numPlanes = numVerts + 2;
/* make texture matrix */
if( !MakeTextureMatrix( dp, projection, dv[ 0 ], dv[ 1 ], dv[ 2 ] ) )
return -1;
/* bound the projector */
ClearBounds( dp->mins, dp->maxs );
for( i = 0; i < numVerts; i++ )
{
AddPointToBounds( dv[ i ]->xyz, dp->mins, dp->maxs );
VectorMA( dv[ i ]->xyz, distance, projection, xyz );
AddPointToBounds( xyz, dp->mins, dp->maxs );
}
/* make bouding sphere */
VectorAdd( dp->mins, dp->maxs, dp->center );
VectorScale( dp->center, 0.5f, dp->center );
VectorSubtract( dp->maxs, dp->center, xyz );
dp->radius = VectorLength( xyz );
dp->radius2 = dp->radius * dp->radius;
/* make the front plane */
if( !PlaneFromPoints( dp->planes[ 0 ], dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) )
return -1;
/* make the back plane */
VectorSubtract( vec3_origin, dp->planes[ 0 ], dp->planes[ 1 ] );
VectorMA( dv[ 0 ]->xyz, distance, projection, xyz );
dp->planes[ 1 ][ 3 ] = DotProduct( xyz, dp->planes[ 1 ] );
/* make the side planes */
for( i = 0; i < numVerts; i++ )
{
j = (i + 1) % numVerts;
VectorMA( dv[ i ]->xyz, distance, projection, xyz );
if( !PlaneFromPoints( dp->planes[ i + 2 ], dv[ j ]->xyz, dv[ i ]->xyz, xyz ) )
return -1;
}
/* return ok */
numProjectors++;
return numProjectors - 1;
}
/*
ProcessDecals()
finds all decal entities and creates decal projectors
*/
#define PLANAR_EPSILON 0.5f
void ProcessDecals( void )
{
int i, j, x, y, pw[ 5 ], r, iterations;
float distance;
vec4_t projection, plane;
vec3_t origin, target, delta;
entity_t *e, *e2;
parseMesh_t *p;
mesh_t *mesh, *subdivided;
bspDrawVert_t *dv[4];
const char *value;
/* note it */
MsgDev( D_NOTE, "--- ProcessDecals ---\n" );
/* walk entity list */
for( i = 0; i < numEntities; i++ )
{
/* get entity */
e = &entities[ i ];
value = ValueForKey( e, "classname" );
if( com.stricmp( value, "_decal" ) )
continue;
/* any patches? */
if( e->patches == NULL )
{
MsgDev( D_WARN, "Decal entity without any patch meshes, ignoring.\n" );
e->epairs = NULL; /* fixme: leak! */
continue;
}
/* find target */
value = ValueForKey( e, "target" );
e2 = FindTargetEntity( value );
/* no target? */
if( e2 == NULL )
{
MsgDev( D_WARN, "Decal entity without a valid target, ignoring.\n" );
continue;
}
/* walk entity patches */
for( p = e->patches; p != NULL; p = e->patches )
{
/* setup projector */
if( VectorCompare( e->origin, vec3_origin ) )
{
VectorAdd( p->eMins, p->eMaxs, origin );
VectorScale( origin, 0.5f, origin );
}
else VectorCopy( e->origin, origin );
VectorCopy( e2->origin, target );
VectorSubtract( target, origin, delta );
/* setup projection plane */
distance = VectorNormalizeLength2( delta, projection );
projection[ 3 ] = DotProduct( origin, projection );
/* create projectors */
if( distance > 0.125f )
{
/* tesselate the patch */
iterations = IterationsForCurve( p->longestCurve, patchSubdivisions );
subdivided = SubdivideMesh2( p->mesh, iterations );
/* fit it to the curve and remove colinear verts on rows/columns */
PutMeshOnCurve( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
/* offset by projector origin */
for( j = 0; j < (mesh->width * mesh->height); j++ )
VectorAdd( mesh->verts[ j ].xyz, e->origin, mesh->verts[ j ].xyz );
/* iterate through the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* get drawverts */
dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];
/* planar? (nuking this optimization as it doesn't work on non-rectangular quads) */
plane[ 0 ] = 0.0f; /* stupid msvc */
if( 0 && PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) &&
fabs( DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ] ) <= PLANAR_EPSILON )
{
/* make a quad projector */
MakeDecalProjector( p->shaderInfo, projection, distance, 4, dv );
}
else
{
/* make first triangle */
MakeDecalProjector( p->shaderInfo, projection, distance, 3, dv );
/* make second triangle */
dv[ 1 ] = dv[ 2 ];
dv[ 2 ] = dv[ 3 ];
MakeDecalProjector( p->shaderInfo, projection, distance, 3, dv );
}
}
}
/* clean up */
Mem_Free( mesh );
}
/* remove patch from entity (fixme: leak!) */
e->patches = p->next;
/* push patch to worldspawn (enable this to debug projectors) */
#if 0
p->next = entities[ 0 ].patches;
entities[ 0 ].patches = p;
#endif
}
}
/* emit some stats */
MsgDev( D_INFO, "%9d decal projectors\n", numProjectors );
}
/*
ProjectDecalOntoWinding()
projects a decal onto a winding
*/
static void ProjectDecalOntoWinding( decalProjector_t *dp, mapDrawSurface_t *ds, winding_t *w )
{
int i, j;
float d, d2, alpha;
winding_t *front, *back;
mapDrawSurface_t *ds2;
bspDrawVert_t *dv;
vec4_t plane;
/* dummy check */
if( w->numpoints < 3 )
{
FreeWinding( w );
return;
}
/* offset by entity origin */
for( i = 0; i < w->numpoints; i++ )
VectorAdd( w->p[ i ], entityOrigin, w->p[ i ] );
/* make a plane from the winding */
if( !PlaneFromPoints( plane, w->p[ 0 ], w->p[ 1 ], w->p[ 2 ] ) )
{
FreeWinding( w );
return;
}
/* backface check */
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
{
FreeWinding( w );
return;
}
/* walk list of planes */
for( i = 0; i < dp->numPlanes; i++ )
{
/* chop winding by the plane */
ClipWindingEpsilon( w, dp->planes[ i ], dp->planes[ i ][ 3 ], 0.0625f, &front, &back );
FreeWinding( w );
/* lose the front fragment */
if( front != NULL )
FreeWinding( front );
/* if nothing left in back, then bail */
if( back == NULL )
return;
/* reset winding */
w = back;
}
/* nothing left? */
if( w == NULL || w->numpoints < 3 )
return;
/* add to counts */
numDecalSurfaces++;
/* make a new surface */
ds2 = AllocDrawSurface( SURFACE_DECAL );
/* set it up */
ds2->entityNum = ds->entityNum;
ds2->castShadows = ds->castShadows;
ds2->recvShadows = ds->recvShadows;
ds2->shaderInfo = dp->si;
ds2->fogNum = ds->fogNum; /* why was this -1? */
ds2->lightmapScale = ds->lightmapScale;
ds2->numVerts = w->numpoints;
ds2->verts = Malloc( ds2->numVerts * sizeof( *ds2->verts ) );
Mem_Set( ds2->verts, 0, ds2->numVerts * sizeof( *ds2->verts ) );
/* set vertexes */
for( i = 0; i < ds2->numVerts; i++ )
{
/* get vertex */
dv = &ds2->verts[ i ];
/* set alpha */
d = DotProduct( w->p[ i ], dp->planes[ 0 ] ) - dp->planes[ 0 ][ 3 ];
d2 = DotProduct( w->p[ i ], dp->planes[ 1 ] ) - dp->planes[ 1 ][ 3 ];
alpha = 255.0f * d2 / (d + d2);
if( alpha > 255 )
alpha = 255;
else if( alpha < 0 )
alpha = 0;
/* set misc */
VectorSubtract( w->p[ i ], entityOrigin, dv->xyz );
VectorCopy( plane, dv->normal );
dv->st[ 0 ] = DotProduct( dv->xyz, dp->texMat[ 0 ] ) + dp->texMat[ 0 ][ 3 ];
dv->st[ 1 ] = DotProduct( dv->xyz, dp->texMat[ 1 ] ) + dp->texMat[ 1 ][ 3 ];
/* set color */
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
dv->color[ j ][ 0 ] = 255;
dv->color[ j ][ 1 ] = 255;
dv->color[ j ][ 2 ] = 255;
dv->color[ j ][ 3 ] = alpha;
}
}
}
/*
ProjectDecalOntoFace()
projects a decal onto a brushface surface
*/
static void ProjectDecalOntoFace( decalProjector_t *dp, mapDrawSurface_t *ds )
{
vec4_t plane;
float d;
winding_t *w;
/* dummy check */
if( ds->sideRef == NULL || ds->sideRef->side == NULL )
return;
/* backface check */
if( ds->planar )
{
VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
plane[ 3 ] = mapplanes[ ds->planeNum ].dist + DotProduct( plane, entityOrigin );
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
return;
}
/* generate decal */
w = WindingFromDrawSurf( ds );
ProjectDecalOntoWinding( dp, ds, w );
}
/*
ProjectDecalOntoPatch()
projects a decal onto a patch surface
*/
static void ProjectDecalOntoPatch( decalProjector_t *dp, mapDrawSurface_t *ds )
{
int x, y, pw[ 5 ], r, iterations;
vec4_t plane;
float d;
mesh_t src, *mesh, *subdivided;
winding_t *w;
/* backface check */
if( ds->planar )
{
VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
plane[ 3 ] = mapplanes[ ds->planeNum ].dist + DotProduct( plane, entityOrigin );
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
return;
}
/* tesselate the patch */
src.width = ds->patchWidth;
src.height = ds->patchHeight;
src.verts = ds->verts;
iterations = IterationsForCurve( ds->longestCurve, patchSubdivisions );
subdivided = SubdivideMesh2( src, iterations );
/* fit it to the curve and remove colinear verts on rows/columns */
PutMeshOnCurve( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
/* iterate through the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* generate decal for first triangle */
w = AllocWinding( 3 );
w->numpoints = 3;
VectorCopy( mesh->verts[ pw[ r + 0 ] ].xyz, w->p[ 0 ] );
VectorCopy( mesh->verts[ pw[ r + 1 ] ].xyz, w->p[ 1 ] );
VectorCopy( mesh->verts[ pw[ r + 2 ] ].xyz, w->p[ 2 ] );
ProjectDecalOntoWinding( dp, ds, w );
/* generate decal for second triangle */
w = AllocWinding( 3 );
w->numpoints = 3;
VectorCopy( mesh->verts[ pw[ r + 0 ] ].xyz, w->p[ 0 ] );
VectorCopy( mesh->verts[ pw[ r + 2 ] ].xyz, w->p[ 1 ] );
VectorCopy( mesh->verts[ pw[ r + 3 ] ].xyz, w->p[ 2 ] );
ProjectDecalOntoWinding( dp, ds, w );
}
}
/* clean up */
Mem_Free( mesh );
}
/*
ProjectDecalOntoTriangles()
projects a decal onto a triangle surface
*/
static void ProjectDecalOntoTriangles( decalProjector_t *dp, mapDrawSurface_t *ds )
{
int i;
vec4_t plane;
float d;
winding_t *w;
/* triangle surfaces without shaders don't get marks by default */
if( ds->type == SURFACE_TRIANGLES && ds->shaderInfo->shaderText == NULL )
return;
/* backface check */
if( ds->planar )
{
VectorCopy( mapplanes[ ds->planeNum ].normal, plane );
plane[ 3 ] = mapplanes[ ds->planeNum ].dist + DotProduct( plane, entityOrigin );
d = DotProduct( dp->planes[ 0 ], plane );
if( d < -0.0001f )
return;
}
/* iterate through triangles */
for( i = 0; i < ds->numIndexes; i += 3 )
{
/* generate decal */
w = AllocWinding( 3 );
w->numpoints = 3;
VectorCopy( ds->verts[ ds->indexes[ i ] ].xyz, w->p[ 0 ] );
VectorCopy( ds->verts[ ds->indexes[ i + 1 ] ].xyz, w->p[ 1 ] );
VectorCopy( ds->verts[ ds->indexes[ i + 2 ] ].xyz, w->p[ 2 ] );
ProjectDecalOntoWinding( dp, ds, w );
}
}
/*
MakeEntityDecals()
projects decals onto world surfaces
*/
void MakeEntityDecals( entity_t *e )
{
int i, j, k, f, fOld;
double start;
decalProjector_t dp;
mapDrawSurface_t *ds;
vec3_t identityAxis[ 3 ] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
/* note it */
MsgDev( D_NOTE, "--- MakeEntityDecals ---\n" );
/* set entity origin */
VectorCopy( e->origin, entityOrigin );
/* transform projector instead of geometry */
VectorClear( entityOrigin );
/* init pacifier */
fOld = -1;
start = Sys_DoubleTime();
/* walk the list of decal projectors */
for( i = 0; i < numProjectors; i++ )
{
/* print pacifier */
f = 10 * i / numProjectors;
if( f != fOld )
{
fOld = f;
MsgDev( D_NOTE, "%d...", f );
}
/* get projector */
TransformDecalProjector( &projectors[ i ], identityAxis, e->origin, &dp );
/* walk the list of surfaces in the entity */
for( j = e->firstDrawSurf; j < numMapDrawSurfs; j++ )
{
/* get surface */
ds = &mapDrawSurfs[ j ];
if( ds->numVerts <= 0 )
continue;
/* ignore autosprite or nomarks */
if( ds->shaderInfo->autosprite || (ds->shaderInfo->compileFlags & C_NOMARKS) )
continue;
/* bounds check */
for( k = 0; k < 3; k++ )
if( ds->mins[ k ] >= (dp.center[ k ] + dp.radius) ||
ds->maxs[ k ] <= (dp.center[ k ] - dp.radius) )
break;
if( k < 3 )
continue;
/* switch on type */
switch( ds->type )
{
case SURFACE_FACE:
ProjectDecalOntoFace( &dp, ds );
break;
case SURFACE_PATCH:
ProjectDecalOntoPatch( &dp, ds );
break;
case SURFACE_TRIANGLES:
case SURFACE_FORCED_META:
case SURFACE_META:
ProjectDecalOntoTriangles( &dp, ds );
break;
default:
break;
}
}
}
/* print time */
MsgDev( D_INFO, " (%g)\n", (Sys_DoubleTime() - start ));
/* emit some stats */
MsgDev( D_INFO, "%9d decal surfaces\n", numDecalSurfaces );
}

458
xtools/bsplib/facebsp.c Normal file
View File

@ -0,0 +1,458 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define FACEBSP_C
/* dependencies */
#include "q3map2.h"
int c_faceLeafs;
/*
================
AllocBspFace
================
*/
face_t *AllocBspFace( void )
{
face_t *f;
f = Malloc( sizeof( *f ));
return f;
}
/*
================
FreeBspFace
================
*/
void FreeBspFace( face_t *f )
{
if( f->w ) FreeWinding( f->w );
Mem_Free( f );
}
/*
SelectSplitPlaneNum()
finds the best split plane for this node
*/
static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum, int *compileFlags )
{
face_t *split;
face_t *check;
face_t *bestSplit;
int splits, facing, front, back;
int side;
plane_t *plane;
int value, bestValue;
int i;
vec3_t normal;
float dist;
int planenum;
/* ydnar: set some defaults */
*splitPlaneNum = -1; /* leaf */
*compileFlags = 0;
/* ydnar 2002-06-24: changed this to split on z-axis as well */
/* ydnar 2002-09-21: changed blocksize to be a vector, so mappers can specify a 3 element value */
/* if it is crossing a block boundary, force a split */
for( i = 0; i < 3; i++ )
{
if( blockSize[ i ] <= 0 )
continue;
dist = blockSize[ i ] * (floor( node->mins[ i ] / blockSize[ i ] ) + 1);
if( node->maxs[ i ] > dist )
{
VectorClear( normal );
normal[ i ] = 1;
planenum = FindFloatPlane( normal, dist, 0, NULL );
*splitPlaneNum = planenum;
return;
}
}
/* pick one of the face planes */
bestValue = -99999;
bestSplit = list;
for( split = list; split; split = split->next )
split->checked = false;
for( split = list; split; split = split->next )
{
if ( split->checked )
continue;
plane = &mapplanes[ split->planenum ];
splits = 0;
facing = 0;
front = 0;
back = 0;
for ( check = list ; check ; check = check->next )
{
if( check->planenum == split->planenum )
{
facing++;
check->checked = true; // won't need to test this plane again
continue;
}
side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
if( side == SIDE_CROSS )
{
splits++;
}
else if( side == SIDE_FRONT )
{
front++;
}
else if( side == SIDE_BACK )
{
back++;
}
}
value = 5*facing - 5*splits; // - abs(front-back);
if ( plane->type < 3 ) {
value+=5; // axial is better
}
value += split->priority; // prioritize hints higher
if ( value > bestValue ) {
bestValue = value;
bestSplit = split;
}
}
/* nothing, we have a leaf */
if( bestValue == -99999 )
return;
/* set best split data */
*splitPlaneNum = bestSplit->planenum;
*compileFlags = bestSplit->compileFlags;
}
/*
CountFaceList()
counts bsp faces in the linked list
*/
int CountFaceList( face_t *list )
{
int c;
c = 0;
for( ; list != NULL; list = list->next )
c++;
return c;
}
/*
BuildFaceTree_r()
recursively builds the bsp, splitting on face planes
*/
void BuildFaceTree_r( node_t *node, face_t *list )
{
face_t *split;
face_t *next;
int side;
plane_t *plane;
face_t *newFace;
face_t *childLists[2];
winding_t *frontWinding, *backWinding;
int i;
int splitPlaneNum, compileFlags;
/* count faces left */
i = CountFaceList( list );
/* select the best split plane */
SelectSplitPlaneNum( node, list, &splitPlaneNum, &compileFlags );
/* if we don't have any more faces, this is a node */
if ( splitPlaneNum == -1 )
{
node->planenum = PLANENUM_LEAF;
c_faceLeafs++;
return;
}
/* partition the list */
node->planenum = splitPlaneNum;
node->compileFlags = compileFlags;
plane = &mapplanes[ splitPlaneNum ];
childLists[0] = NULL;
childLists[1] = NULL;
for( split = list; split; split = next )
{
/* set next */
next = split->next;
/* don't split by identical plane */
if( split->planenum == node->planenum )
{
FreeBspFace( split );
continue;
}
/* determine which side the face falls on */
side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
/* switch on side */
if( side == SIDE_CROSS )
{
ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2,
&frontWinding, &backWinding );
if( frontWinding ) {
newFace = AllocBspFace();
newFace->w = frontWinding;
newFace->next = childLists[0];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->compileFlags = split->compileFlags;
childLists[0] = newFace;
}
if( backWinding ) {
newFace = AllocBspFace();
newFace->w = backWinding;
newFace->next = childLists[1];
newFace->planenum = split->planenum;
newFace->priority = split->priority;
newFace->compileFlags = split->compileFlags;
childLists[1] = newFace;
}
FreeBspFace( split );
} else if ( side == SIDE_FRONT ) {
split->next = childLists[0];
childLists[0] = split;
} else if ( side == SIDE_BACK ) {
split->next = childLists[1];
childLists[1] = split;
}
}
// recursively process children
for ( i = 0 ; i < 2 ; i++ ) {
node->children[i] = AllocNode();
node->children[i]->parent = node;
VectorCopy( node->mins, node->children[i]->mins );
VectorCopy( node->maxs, node->children[i]->maxs );
}
for ( i = 0 ; i < 3 ; i++ ) {
if ( plane->normal[i] == 1 ) {
node->children[0]->mins[i] = plane->dist;
node->children[1]->maxs[i] = plane->dist;
break;
}
}
for ( i = 0 ; i < 2 ; i++ ) {
BuildFaceTree_r ( node->children[i], childLists[i]);
}
}
/*
================
FaceBSP
List will be freed before returning
================
*/
tree_t *FaceBSP( face_t *list )
{
tree_t *tree;
face_t *face;
int i, count;
MsgDev( D_NOTE, "--- FaceBSP ---\n" );
tree = AllocTree ();
count = 0;
for( face = list; face != NULL; face = face->next )
{
count++;
for( i = 0; i < face->w->numpoints; i++ )
{
AddPointToBounds( face->w->p[ i ], tree->mins, tree->maxs );
}
}
MsgDev( D_INFO, "%9d faces\n", count );
tree->headnode = AllocNode();
VectorCopy( tree->mins, tree->headnode->mins );
VectorCopy( tree->maxs, tree->headnode->maxs );
c_faceLeafs = 0;
BuildFaceTree_r ( tree->headnode, list );
MsgDev( D_INFO, "%9d leafs\n", c_faceLeafs );
return tree;
}
/*
MakeStructuralBSPFaceList()
get structural brush faces
*/
face_t *MakeStructuralBSPFaceList( brush_t *list )
{
brush_t *b;
int i;
side_t *s;
winding_t *w;
face_t *f, *flist;
flist = NULL;
for( b = list; b != NULL; b = b->next )
{
if( b->detail )
continue;
for( i = 0; i < b->numsides; i++ )
{
/* get side and winding */
s = &b->sides[ i ];
w = s->winding;
if( w == NULL )
continue;
/* ydnar: skip certain faces */
if( s->compileFlags & C_SKIP )
continue;
/* allocate a face */
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->compileFlags = s->compileFlags; /* ydnar */
/* ydnar: set priority */
f->priority = 0;
if( f->compileFlags & C_HINT )
f->priority += HINT_PRIORITY;
if( f->compileFlags & C_ANTIPORTAL )
f->priority += ANTIPORTAL_PRIORITY;
if( f->compileFlags & C_AREAPORTAL )
f->priority += AREAPORTAL_PRIORITY;
/* get next face */
f->next = flist;
flist = f;
}
}
return flist;
}
/*
MakeVisibleBSPFaceList()
get visible brush faces
*/
face_t *MakeVisibleBSPFaceList( brush_t *list )
{
brush_t *b;
int i;
side_t *s;
winding_t *w;
face_t *f, *flist;
flist = NULL;
for( b = list; b != NULL; b = b->next )
{
if( b->detail )
continue;
for( i = 0; i < b->numsides; i++ )
{
/* get side and winding */
s = &b->sides[ i ];
w = s->visibleHull;
if( w == NULL )
continue;
/* ydnar: skip certain faces */
if( s->compileFlags & C_SKIP )
continue;
/* allocate a face */
f = AllocBspFace();
f->w = CopyWinding( w );
f->planenum = s->planenum & ~1;
f->compileFlags = s->compileFlags; /* ydnar */
/* ydnar: set priority */
f->priority = 0;
if( f->compileFlags & C_HINT )
f->priority += HINT_PRIORITY;
if( f->compileFlags & C_ANTIPORTAL )
f->priority += ANTIPORTAL_PRIORITY;
if( f->compileFlags & C_AREAPORTAL )
f->priority += AREAPORTAL_PRIORITY;
/* get next face */
f->next = flist;
flist = f;
}
}
return flist;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,803 +0,0 @@
#include "bsplib.h"
/*
each portal will have a list of all possible to see from first portal
if (!thread->portalmightsee[portalnum])
portal mightsee
for p2 = all other portals in leaf
get sperating planes
for all portals that might be seen by p2
mark as unseen if not present in seperating plane
flood fill a new mightsee
save as passagemightsee
void CalcMightSee (leaf_t *leaf,
*/
int CountBits (byte *bits, int numbits)
{
int i;
int c;
c = 0;
for (i=0 ; i<numbits ; i++)
if (bits[i>>3] & (1<<(i&7)) )
c++;
return c;
}
int c_fullskip;
int c_portalskip, c_leafskip;
int c_vistest, c_mighttest;
int c_chop, c_nochop;
int active;
void CheckStack (leaf_t *leaf, threaddata_t *thread)
{
pstack_t *p, *p2;
for (p=thread->pstack_head.next ; p ; p=p->next)
{
if (p->leaf == leaf)
Sys_Error ("CheckStack: leaf recursion");
for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
if (p2->leaf == p->leaf)
Sys_Error ("CheckStack: late leaf recursion");
}
}
viswinding_t *AllocStackWinding (pstack_t *stack)
{
int i;
for (i=0 ; i<3 ; i++)
{
if (stack->freewindings[i])
{
stack->freewindings[i] = 0;
return &stack->windings[i];
}
}
Sys_Error ("AllocStackWinding: failed");
return NULL;
}
void FreeStackWinding (viswinding_t *w, pstack_t *stack)
{
int i;
i = w - stack->windings;
if (i<0 || i>2)
return; // not from local
if (stack->freewindings[i])
Sys_Error ("FreeStackWinding: allready free");
stack->freewindings[i] = 1;
}
/*
==============
ChopWinding
==============
*/
viswinding_t *ChopWinding (viswinding_t *in, pstack_t *stack, visplane_t *split)
{
vec_t dists[128];
int sides[128];
int counts[3];
vec_t dot;
int i, j;
vec_t *p1, *p2;
vec3_t mid;
viswinding_t *neww;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->points[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > ON_EPSILON)
sides[i] = SIDE_FRONT;
else if (dot < -ON_EPSILON)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
if (!counts[1])
return in; // completely on front side
if (!counts[0])
{
FreeStackWinding (in, stack);
return NULL;
}
sides[i] = sides[0];
dists[i] = dists[0];
neww = AllocStackWinding (stack);
neww->numpoints = 0;
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->points[i];
if (neww->numpoints == 12)
{
FreeStackWinding (neww, stack);
return in; // can't chop -- fall back to original
}
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
if (neww->numpoints == 12)
{
FreeStackWinding (neww, stack);
return in; // can't chop -- fall back to original
}
// generate a split point
p2 = in->points[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (split->normal[j] == 1)
mid[j] = split->dist;
else if (split->normal[j] == -1)
mid[j] = -split->dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, neww->points[neww->numpoints]);
neww->numpoints++;
}
// free the original winding
FreeStackWinding (in, stack);
return neww;
}
/*
==============
ClipToSeperators
Source, pass, and target are an ordering of portals.
Generates seperating planes canidates by taking two points from source and one
point from pass, and clips target by them.
If target is totally clipped away, that portal can not be seen through.
Normal clip keeps target on the same side as pass, which is correct if the
order goes source, pass, target. If the order goes pass, source, target then
flipclip should be set.
==============
*/
viswinding_t *ClipToSeperators (viswinding_t *source, viswinding_t *pass, viswinding_t *target, bool flipclip, pstack_t *stack)
{
int i, j, k, l;
visplane_t plane;
vec3_t v1, v2;
float d;
vec_t length;
int counts[3];
bool fliptest;
// check all combinations
for (i=0 ; i<source->numpoints ; i++)
{
l = (i+1)%source->numpoints;
VectorSubtract (source->points[l] , source->points[i], v1);
// fing a vertex of pass that makes a plane that puts all of the
// vertexes of pass on the front side and all of the vertexes of
// source on the back side
for (j=0 ; j<pass->numpoints ; j++)
{
VectorSubtract (pass->points[j], source->points[i], v2);
plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
// if points don't make a valid plane, skip it
length = plane.normal[0] * plane.normal[0]
+ plane.normal[1] * plane.normal[1]
+ plane.normal[2] * plane.normal[2];
if (length < ON_EPSILON)
continue;
length = 1/sqrt(length);
plane.normal[0] *= length;
plane.normal[1] *= length;
plane.normal[2] *= length;
plane.dist = DotProduct (pass->points[j], plane.normal);
//
// find out which side of the generated seperating plane has the
// source portal
//
#if 1
fliptest = false;
for (k=0 ; k<source->numpoints ; k++)
{
if (k == i || k == l)
continue;
d = DotProduct (source->points[k], plane.normal) - plane.dist;
if (d < -ON_EPSILON)
{ // source is on the negative side, so we want all
// pass and target on the positive side
fliptest = false;
break;
}
else if (d > ON_EPSILON)
{ // source is on the positive side, so we want all
// pass and target on the negative side
fliptest = true;
break;
}
}
if (k == source->numpoints)
continue; // planar with source portal
#else
fliptest = flipclip;
#endif
//
// flip the normal if the source portal is backwards
//
if (fliptest)
{
VectorSubtract (vec3_origin, plane.normal, plane.normal);
plane.dist = -plane.dist;
}
#if 1
//
// if all of the pass portal points are now on the positive side,
// this is the seperating plane
//
counts[0] = counts[1] = counts[2] = 0;
for (k=0 ; k<pass->numpoints ; k++)
{
if (k==j)
continue;
d = DotProduct (pass->points[k], plane.normal) - plane.dist;
if (d < -ON_EPSILON)
break;
else if (d > ON_EPSILON)
counts[0]++;
else
counts[2]++;
}
if (k != pass->numpoints)
continue; // points on negative side, not a seperating plane
if (!counts[0])
continue; // planar with seperating plane
#else
k = (j+1)%pass->numpoints;
d = DotProduct (pass->points[k], plane.normal) - plane.dist;
if (d < -ON_EPSILON)
continue;
k = (j+pass->numpoints-1)%pass->numpoints;
d = DotProduct (pass->points[k], plane.normal) - plane.dist;
if (d < -ON_EPSILON)
continue;
#endif
//
// flip the normal if we want the back side
//
if (flipclip)
{
VectorSubtract (vec3_origin, plane.normal, plane.normal);
plane.dist = -plane.dist;
}
//
// clip target by the seperating plane
//
target = ChopWinding (target, stack, &plane);
if (!target)
return NULL; // target is not visible
}
}
return target;
}
/*
==================
RecursiveLeafFlow
Flood fill through the leafs
If src_portal is NULL, this is the originating leaf
==================
*/
void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
{
pstack_t stack;
visportal_t *p;
visplane_t backplane;
leaf_t *leaf;
int i, j;
long *test, *might, *vis, more;
int pnum;
thread->c_chains++;
leaf = &leafs[leafnum];
// CheckStack (leaf, thread);
prevstack->next = &stack;
stack.next = NULL;
stack.leaf = leaf;
stack.portal = NULL;
might = (long *)stack.mightsee;
vis = (long *)thread->base->portalvis;
// check all portals for flowing into other leafs
for (i=0 ; i<leaf->numportals ; i++)
{
p = leaf->portals[i];
pnum = p - portals;
if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
{
continue; // can't possibly see it
}
// if the portal can't see anything we haven't allready seen, skip it
if (p->status == stat_done)
{
test = (long *)p->portalvis;
}
else
{
test = (long *)p->portalflood;
}
more = 0;
for (j=0 ; j<portallongs ; j++)
{
might[j] = ((long *)prevstack->mightsee)[j] & test[j];
more |= (might[j] & ~vis[j]);
}
if (!more &&
(thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
{ // can't see anything new
continue;
}
// get plane of portal, point normal into the neighbor leaf
stack.portalplane = p->plane;
VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
backplane.dist = -p->plane.dist;
// c_portalcheck++;
stack.portal = p;
stack.next = NULL;
stack.freewindings[0] = 1;
stack.freewindings[1] = 1;
stack.freewindings[2] = 1;
#if 1
{
float d;
d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
d -= thread->pstack_head.portalplane.dist;
if (d < -p->radius)
{
continue;
}
else if (d > p->radius)
{
stack.pass = p->winding;
}
else
{
stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
if (!stack.pass)
continue;
}
}
#else
stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
if (!stack.pass)
continue;
#endif
#if 1
{
float d;
d = DotProduct (thread->base->origin, p->plane.normal);
d -= p->plane.dist;
if (d > thread->base->radius)
// if (d > p->radius)
{
continue;
}
// else if (d < -p->radius)
else if (d < -thread->base->radius)
{
stack.source = prevstack->source;
}
else
{
stack.source = ChopWinding (prevstack->source, &stack, &backplane);
if (!stack.source)
continue;
}
}
#else
stack.source = ChopWinding (prevstack->source, &stack, &backplane);
if (!stack.source)
continue;
#endif
if (!prevstack->pass)
{ // the second leaf can only be blocked if coplanar
// mark the portal as visible
thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
RecursiveLeafFlow (p->leaf, thread, &stack);
continue;
}
stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
if (!stack.pass)
continue;
stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
if (!stack.pass)
continue;
// mark the portal as visible
thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
// flow through it for real
RecursiveLeafFlow (p->leaf, thread, &stack);
}
}
/*
===============
PortalFlow
generates the portalvis bit vector
===============
*/
void PortalFlow (int portalnum)
{
threaddata_t data;
int i;
visportal_t *p;
int c_might, c_can;
p = sorted_portals[portalnum];
p->status = stat_working;
c_might = CountBits (p->portalflood, numportals*2);
memset (&data, 0, sizeof(data));
data.base = p;
data.pstack_head.portal = p;
data.pstack_head.source = p->winding;
data.pstack_head.portalplane = p->plane;
for (i=0 ; i<portallongs ; i++)
((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
p->status = stat_done;
c_can = CountBits (p->portalvis, numportals*2);
}
/*
===============================================================================
This is a rough first-order aproximation that is used to trivially reject some
of the final calculations.
Calculates portalfront and portalflood bit vectors
thinking about:
typedef struct passage_s
{
struct passage_s *next;
struct portal_s *to;
stryct sep_s *seperators;
byte *mightsee;
} passage_t;
typedef struct portal_s
{
struct passage_s *passages;
int leaf; // leaf portal faces into
} portal_s;
leaf = portal->leaf
clear
for all portals
calc portal visibility
clear bit vector
for all passages
passage visibility
for a portal to be visible to a passage, it must be on the front of
all seperating planes, and both portals must be behind the mew portal
===============================================================================
*/
int c_flood, c_vis;
char test_leaf[MAX_MAP_LEAFS];
/*
==================
SimpleFlood
==================
*/
void SimpleFlood (visportal_t *srcportal, int leafnum)
{
int i;
leaf_t *leaf;
visportal_t *p;
int pnum;
if( bsp_parms & BSPLIB_CULLERROR && !test_leaf[leafnum])
return;
test_leaf[leafnum] = 0;
leaf = &leafs[leafnum];
for (i=0 ; i<leaf->numportals ; i++)
{
p = leaf->portals[i];
pnum = p - portals;
if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
continue;
if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
continue;
srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
SimpleFlood (srcportal, p->leaf);
}
}
/*
==============
BasePortalVis
==============
*/
void BasePortalVis (int portalnum)
{
int j, k;
visportal_t *tp, *p;
float d;
viswinding_t *w;
vec3_t prenormal, normal;
float p_dot, tp_dot, p_rad, tp_rad;
p = portals+portalnum;
p->portalfront = Malloc (portalbytes);
p->portalflood = Malloc (portalbytes);
p->portalvis = Malloc (portalbytes);
Mem_Set( test_leaf, 0, MAX_MAP_LEAFS );
for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
{
if (j == portalnum)
continue;
else if( bsp_parms & BSPLIB_CULLERROR && tp->leaf == p->owner_leaf)
continue;
test_leaf[tp->leaf] = 1;
w = tp->winding;
for (k=0 ; k<w->numpoints ; k++)
{
d = DotProduct (w->points[k], p->plane.normal)
- p->plane.dist;
if (d > ON_EPSILON)
break;
}
if (k == w->numpoints)
continue; // no points on front
w = p->winding;
for (k=0 ; k<w->numpoints ; k++)
{
d = DotProduct (w->points[k], tp->plane.normal)
- tp->plane.dist;
if (d < -ON_EPSILON)
break;
}
if (k == w->numpoints)
continue; // no points on front
if( maxdist > 0.0 )
{
// This approximation will consider 2 circles in 3d space with the centeer
// and radius of the polygons on the planes of the polygons.
prenormal[0] = p->origin[0] - tp->origin[0];
prenormal[1] = p->origin[1] - tp->origin[1];
prenormal[2] = p->origin[2] - tp->origin[2];
VectorCopy( prenormal, normal );
d = VectorNormalizeLength( normal );
p_dot = DotProduct(p->plane.normal, normal);
if(p_dot < 0.0)
p_rad = -p_dot * p->radius;
else
p_rad = p_dot * p->radius;
tp_dot = DotProduct(tp->plane.normal, normal);
if(tp_dot < 0.0)
tp_rad = -tp_dot * tp->radius;
else
tp_rad = tp_dot * tp->radius;
if(d > (maxdist + tp_rad + p_rad))
continue;
}
p->portalfront[j>>3] |= (1<<(j&7));
}
SimpleFlood (p, p->leaf);
p->nummightsee = CountBits (p->portalflood, numportals*2);
c_flood += p->nummightsee;
}
/*
===============================================================================
This is a second order aproximation
Calculates portalvis bit vector
WAAAAAAY too slow.
===============================================================================
*/
/*
==================
RecursiveLeafBitFlow
==================
*/
void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
{
visportal_t *p;
leaf_t *leaf;
int i, j;
long more;
int pnum;
byte newmight[MAX_PORTALS/8];
leaf = &leafs[leafnum];
// check all portals for flowing into other leafs
for (i=0 ; i<leaf->numportals ; i++)
{
p = leaf->portals[i];
pnum = p - portals;
// if some previous portal can't see it, skip
if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
continue;
// if this portal can see some portals we mightsee, recurse
more = 0;
for (j=0 ; j<portallongs ; j++)
{
((long *)newmight)[j] = ((long *)mightsee)[j]
& ((long *)p->portalflood)[j];
more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
}
if (!more)
continue; // can't see anything new
cansee[pnum>>3] |= (1<<(pnum&7));
RecursiveLeafBitFlow (p->leaf, newmight, cansee);
}
}
/*
==============
BetterPortalVis
==============
*/
void BetterPortalVis (int portalnum)
{
visportal_t *p;
p = portals+portalnum;
RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
// build leaf vis information
p->nummightsee = CountBits (p->portalvis, numportals*2);
c_vis += p->nummightsee;
}

800
xtools/bsplib/fog.c Normal file
View File

@ -0,0 +1,800 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define FOG_C
/* dependencies */
#include "q3map2.h"
int numFogFragments;
int numFogPatchFragments;
/*
DrawSurfToMesh()
converts a patch drawsurface to a mesh_t
*/
mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds )
{
mesh_t *m;
m = Malloc( sizeof( *m ) );
m->width = ds->patchWidth;
m->height = ds->patchHeight;
m->verts = Malloc( sizeof(m->verts[ 0 ]) * m->width * m->height );
Mem_Copy( m->verts, ds->verts, sizeof(m->verts[ 0 ]) * m->width * m->height );
return m;
}
/*
SplitMeshByPlane()
chops a mesh by a plane
*/
void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back )
{
int w, h, split;
float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
bspDrawVert_t *dv, *v1, *v2;
int c_front, c_back, c_on;
mesh_t *f, *b;
int i;
float frac;
int frontAprox, backAprox;
for ( i = 0 ; i < 2 ; i++ ) {
dv = in->verts;
c_front = 0;
c_back = 0;
c_on = 0;
for ( h = 0 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width ; w++, dv++ ) {
d[h][w] = DotProduct( dv->xyz, normal ) - dist;
if ( d[h][w] > ON_EPSILON ) {
c_front++;
} else if ( d[h][w] < -ON_EPSILON ) {
c_back++;
} else {
c_on++;
}
}
}
*front = NULL;
*back = NULL;
if ( !c_front ) {
*back = in;
return;
}
if ( !c_back ) {
*front = in;
return;
}
// find a split point
split = -1;
for ( w = 0 ; w < in->width -1 ; w++ ) {
if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
if ( split == -1 ) {
split = w;
break;
}
}
}
if ( split == -1 )
{
if( i == 1 )
{
MsgDev( D_ERROR, "No crossing points in patch\n" );
*front = in;
return;
}
in = TransposeMesh( in );
InvertMesh( in );
continue;
}
// make sure the split point stays the same for all other rows
for ( h = 1 ; h < in->height ; h++ ) {
for ( w = 0 ; w < in->width -1 ; w++ ) {
if ( ( d[h][w] < 0 ) != ( d[h][w+1] < 0 ) ) {
if ( w != split )
{
MsgDev( D_ERROR, "multiple crossing points for patch -- can't clip\n" );
*front = in;
return;
}
}
}
if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ))
{
MsgDev( D_ERROR, "differing crossing points for patch -- can't clip\n" );
*front = in;
return;
}
}
break;
}
// create two new meshes
f = Malloc( sizeof( *f ) );
f->width = split + 2;
if ( ! (f->width & 1))
{
f->width++;
frontAprox = 1;
} else {
frontAprox = 0;
}
if( f->width > MAX_PATCH_SIZE ) Sys_Error( "MAX_PATCH_SIZE after split\n" );
f->height = in->height;
f->verts = Malloc( sizeof(f->verts[0]) * f->width * f->height );
b = Malloc( sizeof( *b ) );
b->width = in->width - split;
if ( ! (b->width & 1) ) {
b->width++;
backAprox = 1;
} else {
backAprox = 0;
}
if( b->width > MAX_PATCH_SIZE ) Sys_Error( "MAX_PATCH_SIZE after split\n" );
b->height = in->height;
b->verts = Malloc( sizeof(b->verts[0]) * b->width * b->height );
if ( d[0][0] > 0 ) {
*front = f;
*back = b;
} else {
*front = b;
*back = f;
}
// distribute the points
for ( w = 0 ; w < in->width ; w++ ) {
for ( h = 0 ; h < in->height ; h++ ) {
if ( w <= split ) {
f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
} else {
b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
}
}
}
// clip the crossing line
for ( h = 0; h < in->height; h++ )
{
dv = &f->verts[ h * f->width + split + 1 ];
v1 = &in->verts[ h * in->width + split ];
v2 = &in->verts[ h * in->width + split + 1 ];
frac = d[h][split] / ( d[h][split] - d[h][split+1] );
/* interpolate */
//% for( i = 0; i < 10; i++ )
//% dv->xyz[ i ] = v1->xyz[ i ] + frac * (v2->xyz[ i ] - v1->xyz[ i ]);
//% dv->xyz[10] = 0; // set all 4 colors to 0
LerpDrawVertAmount( v1, v2, frac, dv );
if ( frontAprox ) {
f->verts[ h * f->width + split + 2 ] = *dv;
}
b->verts[ h * b->width ] = *dv;
if ( backAprox ) {
b->verts[ h * b->width + 1 ] = *dv;
}
}
#if 0
PrintMesh( in );
Msg( "\n" );
PrintMesh( f );
Msg( "\n" );
PrintMesh( b );
Msg( "\n" );
#endif
FreeMesh( in );
}
/*
ChopPatchSurfaceByBrush()
chops a patch up by a fog brush
*/
bool ChopPatchSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
{
int i, j;
side_t *s;
plane_t *plane;
mesh_t *outside[MAX_BRUSH_SIDES];
int numOutside;
mesh_t *m, *front, *back;
mapDrawSurface_t *newds;
m = DrawSurfToMesh( ds );
numOutside = 0;
// only split by the top and bottom planes to avoid
// some messy patch clipping issues
for ( i = 4 ; i <= 5 ; i++ ) {
s = &b->sides[ i ];
plane = &mapplanes[ s->planenum ];
SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
if( !back )
{
// nothing actually contained inside
for( j = 0; j < numOutside; j++ )
{
FreeMesh( outside[j] );
}
return false;
}
m = back;
if( front )
{
if( numOutside == MAX_BRUSH_SIDES ) Sys_Error( "MAX_BRUSH_SIDES limit exceeded\n" );
outside[ numOutside ] = front;
numOutside++;
}
}
/* all of outside fragments become seperate drawsurfs */
numFogPatchFragments += numOutside;
for( i = 0; i < numOutside; i++ )
{
/* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
outside[ i ] = TransposeMesh( outside[ i ] );
InvertMesh( outside[ i ] );
/* ydnar: do this the hacky right way */
newds = AllocDrawSurface( SURFACE_PATCH );
Mem_Copy( newds, ds, sizeof( *ds ) );
newds->patchWidth = outside[ i ]->width;
newds->patchHeight = outside[ i ]->height;
newds->numVerts = outside[ i ]->width * outside[ i ]->height;
newds->verts = Malloc( newds->numVerts * sizeof( *newds->verts ) );
Mem_Copy( newds->verts, outside[ i ]->verts, newds->numVerts * sizeof( *newds->verts ) );
/* free the source mesh */
FreeMesh( outside[ i ] );
}
/* only rejigger this patch if it was chopped */
//% Msg( "Inside: %d x %d\n", m->width, m->height );
if( numOutside > 0 )
{
/* transpose and invert the chopped patch (fixes potential crash. fixme: why?) */
m = TransposeMesh( m );
InvertMesh( m );
/* replace ds with m */
ds->patchWidth = m->width;
ds->patchHeight = m->height;
ds->numVerts = m->width * m->height;
Mem_Free( ds->verts );
ds->verts = Malloc( ds->numVerts * sizeof( *ds->verts ) );
Mem_Copy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
}
/* free the source mesh and return */
FreeMesh( m );
return true;
}
/*
WindingFromDrawSurf()
creates a winding from a surface's verts
*/
winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds )
{
winding_t *w;
int i;
// we use the first point of the surface, maybe something more clever would be useful
// (actually send the whole draw surface would be cool?)
if( ds->numVerts >= MAX_POINTS_ON_WINDING )
{
int max = ds->numVerts;
vec3_t p[256];
if(max > 256)
max = 256;
for ( i = 0 ; i < max ; i++ ) {
VectorCopy( ds->verts[i].xyz, p[i] );
}
Sys_Break( "WindingFromDrawSurf failed: MAX_POINTS_ON_WINDING limit exceeded\n" );
}
w = AllocWinding( ds->numVerts );
w->numpoints = ds->numVerts;
for ( i = 0 ; i < ds->numVerts ; i++ ) {
VectorCopy( ds->verts[i].xyz, w->p[i] );
}
return w;
}
/*
ChopFaceSurfaceByBrush()
chops up a face drawsurface by a fog brush, with a potential fragment left inside
*/
bool ChopFaceSurfaceByBrush( entity_t *e, mapDrawSurface_t *ds, brush_t *b )
{
int i, j;
side_t *s;
plane_t *plane;
winding_t *w;
winding_t *front, *back;
winding_t *outside[ MAX_BRUSH_SIDES ];
int numOutside;
mapDrawSurface_t *newds;
/* dummy check */
if( ds->sideRef == NULL || ds->sideRef->side == NULL )
return false;
/* initial setup */
w = WindingFromDrawSurf( ds );
numOutside = 0;
/* chop by each brush side */
for( i = 0; i < b->numsides; i++ )
{
/* get brush side and plane */
s = &b->sides[ i ];
plane = &mapplanes[ s->planenum ];
/* handle coplanar outfacing (don't fog) */
if( ds->sideRef->side->planenum == s->planenum )
return false;
/* handle coplanar infacing (keep inside) */
if( (ds->sideRef->side->planenum ^ 1) == s->planenum )
continue;
/* general case */
ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON, &front, &back );
FreeWinding( w );
if( back == NULL )
{
/* nothing actually contained inside */
for( j = 0; j < numOutside; j++ )
FreeWinding( outside[ j ] );
return false;
}
if( front != NULL )
{
if( numOutside == MAX_BRUSH_SIDES ) Sys_Error( "MAX_BRUSH_SIDES limit exceeded\n" );
outside[numOutside] = front;
numOutside++;
}
w = back;
}
/* fixme: celshaded surface fragment errata */
/* all of outside fragments become seperate drawsurfs */
numFogFragments += numOutside;
s = ds->sideRef->side;
for( i = 0; i < numOutside; i++ )
{
newds = DrawSurfaceForSide( e, ds->mapBrush, s, outside[ i ] );
newds->fogNum = ds->fogNum;
FreeWinding( outside[ i ] );
}
/* ydnar: the old code neglected to snap to 0.125 for the fragment
inside the fog brush, leading to sparklies. this new code does
the right thing and uses the original surface's brush side */
/* build a drawsurf for it */
newds = DrawSurfaceForSide( e, ds->mapBrush, s, w );
if( newds == NULL )
return false;
/* copy new to original */
ClearSurface( ds );
Mem_Copy( ds, newds, sizeof( mapDrawSurface_t ) );
/* didn't really add a new drawsurface... :) */
numMapDrawSurfs--;
/* return ok */
return true;
}
/*
FogDrawSurfaces()
call after the surface list has been pruned, before tjunction fixing
*/
void FogDrawSurfaces( entity_t *e )
{
int i, j, k, fogNum;
fog_t *fog;
mapDrawSurface_t *ds;
vec3_t mins, maxs;
int fogged, numFogged;
int numBaseDrawSurfs;
/* note it */
MsgDev( D_NOTE, "----- FogDrawSurfs -----\n" );
/* reset counters */
numFogged = 0;
numFogFragments = 0;
/* walk fog list */
for( fogNum = 0; fogNum < numMapFogs; fogNum++ )
{
/* get fog */
fog = &mapFogs[ fogNum ];
/* clip each surface into this, but don't clip any of the resulting fragments to the same brush */
numBaseDrawSurfs = numMapDrawSurfs;
for( i = 0; i < numBaseDrawSurfs; i++ )
{
/* get the drawsurface */
ds = &mapDrawSurfs[ i ];
/* no fog? */
if( ds->shaderInfo->noFog )
continue;
/* global fog doesn't have a brush */
if( fog->brush == NULL )
{
/* don't re-fog already fogged surfaces */
if( ds->fogNum >= 0 )
continue;
fogged = 1;
}
else
{
/* find drawsurface bounds */
ClearBounds( mins, maxs );
for( j = 0; j < ds->numVerts; j++ )
AddPointToBounds( ds->verts[ j ].xyz, mins, maxs );
/* check against the fog brush */
for( k = 0; k < 3; k++ )
{
if( mins[ k ] > fog->brush->maxs[ k ] )
break;
if( maxs[ k ] < fog->brush->mins[ k ] )
break;
}
/* no intersection? */
if( k < 3 )
continue;
/* ydnar: gs mods: handle the various types of surfaces */
switch( ds->type )
{
/* handle brush faces */
case SURFACE_FACE:
fogged = ChopFaceSurfaceByBrush( e, ds, fog->brush );
break;
/* handle patches */
case SURFACE_PATCH:
fogged = ChopPatchSurfaceByBrush( e, ds, fog->brush );
break;
/* handle triangle surfaces (fixme: split triangle surfaces) */
case SURFACE_TRIANGLES:
case SURFACE_FORCED_META:
case SURFACE_META:
fogged = 1;
break;
/* no fogging */
default:
fogged = 0;
break;
}
}
/* is this surface fogged? */
if( fogged )
{
numFogged += fogged;
ds->fogNum = fogNum;
}
}
}
/* emit some statistics */
MsgDev( D_INFO, "%9d fog polygon fragments\n", numFogFragments );
MsgDev( D_INFO, "%9d fog patch fragments\n", numFogPatchFragments );
MsgDev( D_INFO, "%9d fogged drawsurfs\n", numFogged );
}
/*
FogForPoint() - ydnar
gets the fog number for a point in space
*/
int FogForPoint( vec3_t point, float epsilon )
{
int fogNum, i, j;
float dot;
bool inside;
brush_t *brush;
plane_t *plane;
/* start with bogus fog num */
fogNum = defaultFogNum;
/* walk the list of fog volumes */
for( i = 0; i < numMapFogs; i++ )
{
/* sof2: global fog doesn't reference a brush */
if( mapFogs[ i ].brush == NULL )
{
fogNum = i;
continue;
}
/* get fog brush */
brush = mapFogs[ i ].brush;
/* check point against all planes */
inside = true;
for( j = 0; j < brush->numsides && inside; j++ )
{
plane = &mapplanes[ brush->sides[ j ].planenum ]; /* note usage of map planes here */
dot = DotProduct( point, plane->normal );
dot -= plane->dist;
if( dot > epsilon )
inside = false;
}
/* if inside, return the fog num */
if( inside )
{
//% Msg( "FogForPoint: %f, %f, %f in fog %d\n", point[ 0 ], point[ 1 ], point[ 2 ], i );
return i;
}
}
/* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
return fogNum;
}
/*
FogForBounds() - ydnar
gets the fog number for a bounding box
*/
int FogForBounds( vec3_t mins, vec3_t maxs, float epsilon )
{
int fogNum, i, j;
float highMin, lowMax, volume, bestVolume;
vec3_t fogMins, fogMaxs, overlap;
brush_t *brush;
/* start with bogus fog num */
fogNum = defaultFogNum;
/* init */
bestVolume = 0.0f;
/* walk the list of fog volumes */
for( i = 0; i < numMapFogs; i++ )
{
/* sof2: global fog doesn't reference a brush */
if( mapFogs[ i ].brush == NULL )
{
fogNum = i;
continue;
}
/* get fog brush */
brush = mapFogs[ i ].brush;
/* get bounds */
fogMins[ 0 ] = brush->mins[ 0 ] - epsilon;
fogMins[ 1 ] = brush->mins[ 1 ] - epsilon;
fogMins[ 2 ] = brush->mins[ 2 ] - epsilon;
fogMaxs[ 0 ] = brush->maxs[ 0 ] + epsilon;
fogMaxs[ 1 ] = brush->maxs[ 1 ] + epsilon;
fogMaxs[ 2 ] = brush->maxs[ 2 ] + epsilon;
/* check against bounds */
for( j = 0; j < 3; j++ )
{
if( mins[ j ] > fogMaxs[ j ] || maxs[ j ] < fogMins[ j ] )
break;
highMin = mins[ j ] > fogMins[ j ] ? mins[ j ] : fogMins[ j ];
lowMax = maxs[ j ] < fogMaxs[ j ] ? maxs[ j ] : fogMaxs[ j ];
overlap[ j ] = lowMax - highMin;
if( overlap[ j ] < 1.0f )
overlap[ j ] = 1.0f;
}
/* no overlap */
if( j < 3 )
continue;
/* get volume */
volume = overlap[ 0 ] * overlap[ 1 ] * overlap[ 2 ];
/* test against best volume */
if( volume > bestVolume )
{
bestVolume = volume;
fogNum = i;
}
}
/* if the point made it this far, it's not inside any fog volumes (or inside global fog) */
return fogNum;
}
/*
CreateMapFogs() - ydnar
generates a list of map fogs
*/
void CreateMapFogs( void )
{
int i;
entity_t *entity;
brush_t *brush;
fog_t *fog;
vec3_t invFogDir;
const char *globalFog;
/* skip? */
if( nofog ) return;
/* note it */
MsgDev( D_NOTE, "--- CreateMapFogs ---\n" );
/* walk entities */
for( i = 0; i < numEntities; i++ )
{
/* get entity */
entity = &entities[ i ];
/* walk entity brushes */
for( brush = entity->brushes; brush != NULL; brush = brush->next )
{
/* ignore non-fog brushes */
if( brush->contentShader->fogParms == false )
continue;
/* test limit */
if( numMapFogs >= MAX_MAP_FOGS ) Sys_Error( "MAX_MAP_FOGS limit exceeded\n" );
/* set up fog */
fog = &mapFogs[ numMapFogs++ ];
fog->si = brush->contentShader;
fog->brush = brush;
fog->visibleSide = -1;
/* if shader specifies an explicit direction, then find a matching brush side with an opposed normal */
if( VectorLength( fog->si->fogDir ))
{
/* flip it */
VectorScale( fog->si->fogDir, -1.0f, invFogDir );
/* find the brush side */
for( i = 0; i < brush->numsides; i++ )
{
if( VectorCompare( invFogDir, mapplanes[ brush->sides[ i ].planenum ].normal ) )
{
fog->visibleSide = i;
//% Msg( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide );
break;
}
}
}
}
}
/* ydnar: global fog */
globalFog = ValueForKey( &entities[0], "_fog" );
if( globalFog[0] == '\0' ) globalFog = ValueForKey( &entities[0], "fog" );
if( globalFog[0] != '\0' )
{
/* test limit */
if( numMapFogs >= MAX_MAP_FOGS )
Sys_Break( "MAX_MAP_FOGS limit exceeded, trying to add global fog\n" );
/* note it */
MsgDev( D_INFO, "Map has global fog shader %s\n", globalFog );
/* set up fog */
fog = &mapFogs[ numMapFogs++ ];
fog->si = ShaderInfoForShader( globalFog );
if( fog->si == NULL ) Sys_Break( "Invalid shader \"%s\" referenced trying to add global fog\n", globalFog );
fog->brush = NULL;
fog->visibleSide = -1;
/* set as default fog */
defaultFogNum = numMapFogs - 1;
/* mark all worldspawn brushes as fogged */
for( brush = entities[ 0 ].brushes; brush != NULL; brush = brush->next )
ApplySurfaceParm( "fog", &brush->contentFlags, NULL, &brush->compileFlags );
}
/* emit some stats */
MsgDev( D_INFO, "%9d fogs\n", numMapFogs );
}

264
xtools/bsplib/game_etut.h Normal file
View File

@ -0,0 +1,264 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_ETUT_H
#define GAME_ETUT_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define U_CONT_SOLID 1 /* an eye is never valid in a solid */
#define U_CONT_LAVA 8
#define U_CONT_SLIME 16
#define U_CONT_WATER 32
#define U_CONT_FOG 64
#define U_CONT_AREAPORTAL 0x8000
#define U_CONT_PLAYERCLIP 0x10000
#define U_CONT_MONSTERCLIP 0x20000
#define U_CONT_TELEPORTER 0x40000
#define U_CONT_JUMPPAD 0x80000
#define U_CONT_CLUSTERPORTAL 0x100000
#define U_CONT_DONOTENTER 0x200000
#define U_CONT_BOTCLIP 0x400000
#define U_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define U_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define U_CONT_CORPSE 0x4000000
#define U_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define U_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define U_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define U_CONT_TRIGGER 0x40000000
#define U_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define U_SURF_NODAMAGE 0x1 /* never give falling damage */
#define U_SURF_SLICK 0x2 /* effects game physics */
#define U_SURF_SKY 0x4 /* lighting from environment map */
#define U_SURF_LADDER 0x8
#define U_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define U_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define U_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define U_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define U_SURF_HINT 0x100 /* make a primary bsp splitter */
#define U_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define U_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define U_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define U_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define U_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define U_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define U_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define U_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define U_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define U_SURF_DUST 0x40000 /* leave a dust trail when walking on this surface */
/* ydnar flags */
#define U_SURF_VERTEXLIT (U_SURF_POINTLIGHT | U_SURF_NOLIGHTMAP)
/* materials */
#define U_MAT_MASK 0xFFF00000 /* mask to get the material type */
#define U_MAT_NONE 0x00000000
#define U_MAT_TIN 0x00100000
#define U_MAT_ALUMINUM 0x00200000
#define U_MAT_IRON 0x00300000
#define U_MAT_TITANIUM 0x00400000
#define U_MAT_STEEL 0x00500000
#define U_MAT_BRASS 0x00600000
#define U_MAT_COPPER 0x00700000
#define U_MAT_CEMENT 0x00800000
#define U_MAT_ROCK 0x00900000
#define U_MAT_GRAVEL 0x00A00000
#define U_MAT_PAVEMENT 0x00B00000
#define U_MAT_BRICK 0x00C00000
#define U_MAT_CLAY 0x00D00000
#define U_MAT_GRASS 0x00E00000
#define U_MAT_DIRT 0x00F00000
#define U_MAT_MUD 0x01000000
#define U_MAT_SNOW 0x01100000
#define U_MAT_ICE 0x01200000
#define U_MAT_SAND 0x01300000
#define U_MAT_CERAMICTILE 0x01400000
#define U_MAT_LINOLEUM 0x01500000
#define U_MAT_RUG 0x01600000
#define U_MAT_PLASTER 0x01700000
#define U_MAT_PLASTIC 0x01800000
#define U_MAT_CARDBOARD 0x01900000
#define U_MAT_HARDWOOD 0x01A00000
#define U_MAT_SOFTWOOD 0x01B00000
#define U_MAT_PLANK 0x01C00000
#define U_MAT_GLASS 0x01D00000
#define U_MAT_WATER 0x01E00000
#define U_MAT_STUCCO 0x01F00000
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"etut", /* -game x */
"etut", /* default base game data dir */
".etwolf", /* unix home sub-dir */
"et", /* magic path word */
"scripts", /* shader directory */
1024, /* max lightmapped surface verts */
1024, /* max surface verts */
6144, /* max surface indexes */
false, /* flares */
"flareshader", /* default flare shader */
false, /* wolf lighting model? */
128, /* lightmap width/height */
2.2f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
false, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", U_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", U_CONT_ORIGIN, U_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", U_CONT_AREAPORTAL, U_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", U_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", U_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", U_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, U_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, U_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, U_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, U_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, U_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, U_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, U_CONT_SOLID, U_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", U_CONT_TRIGGER, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", U_CONT_WATER, U_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", U_CONT_SLIME, U_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", U_CONT_LAVA, U_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", U_CONT_PLAYERCLIP, U_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", U_CONT_MONSTERCLIP, U_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", U_CONT_NODROP, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", U_CONT_CLUSTERPORTAL, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", U_CONT_DONOTENTER, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", U_CONT_BOTCLIP, U_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", U_CONT_FOG, U_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, U_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, U_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, U_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, U_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, U_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, U_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, U_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, U_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, U_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, U_SURF_NODLIGHT, 0, 0, 0 },
{ "dust", 0, 0, U_SURF_DUST, 0, 0, 0 },
/* materials */
{ "*mat_none", 0, 0, U_MAT_NONE, U_MAT_MASK, 0, 0 },
{ "*mat_tin", 0, 0, U_MAT_TIN, U_MAT_MASK, 0, 0 },
{ "*mat_aluminum", 0, 0, U_MAT_ALUMINUM, U_MAT_MASK, 0, 0 },
{ "*mat_iron", 0, 0, U_MAT_IRON, U_MAT_MASK, 0, 0 },
{ "*mat_titanium", 0, 0, U_MAT_TITANIUM, U_MAT_MASK, 0, 0 },
{ "*mat_steel", 0, 0, U_MAT_STEEL, U_MAT_MASK, 0, 0 },
{ "*mat_brass", 0, 0, U_MAT_BRASS, U_MAT_MASK, 0, 0 },
{ "*mat_copper", 0, 0, U_MAT_COPPER, U_MAT_MASK, 0, 0 },
{ "*mat_cement", 0, 0, U_MAT_CEMENT, U_MAT_MASK, 0, 0 },
{ "*mat_rock", 0, 0, U_MAT_ROCK, U_MAT_MASK, 0, 0 },
{ "*mat_gravel", 0, 0, U_MAT_GRAVEL, U_MAT_MASK, 0, 0 },
{ "*mat_pavement", 0, 0, U_MAT_PAVEMENT, U_MAT_MASK, 0, 0 },
{ "*mat_brick", 0, 0, U_MAT_BRICK, U_MAT_MASK, 0, 0 },
{ "*mat_clay", 0, 0, U_MAT_CLAY, U_MAT_MASK, 0, 0 },
{ "*mat_grass", 0, 0, U_MAT_GRASS, U_MAT_MASK, 0, 0 },
{ "*mat_dirt", 0, 0, U_MAT_DIRT, U_MAT_MASK, 0, 0 },
{ "*mat_mud", 0, 0, U_MAT_MUD, U_MAT_MASK, 0, 0 },
{ "*mat_snow", 0, 0, U_MAT_SNOW, U_MAT_MASK, 0, 0 },
{ "*mat_ice", 0, 0, U_MAT_ICE, U_MAT_MASK, 0, 0 },
{ "*mat_sand", 0, 0, U_MAT_SAND, U_MAT_MASK, 0, 0 },
{ "*mat_ceramic", 0, 0, U_MAT_CERAMICTILE, U_MAT_MASK, 0, 0 },
{ "*mat_ceramictile", 0, 0, U_MAT_CERAMICTILE, U_MAT_MASK, 0, 0 },
{ "*mat_linoleum", 0, 0, U_MAT_LINOLEUM, U_MAT_MASK, 0, 0 },
{ "*mat_rug", 0, 0, U_MAT_RUG, U_MAT_MASK, 0, 0 },
{ "*mat_plaster", 0, 0, U_MAT_PLASTER, U_MAT_MASK, 0, 0 },
{ "*mat_plastic", 0, 0, U_MAT_PLASTIC, U_MAT_MASK, 0, 0 },
{ "*mat_cardboard", 0, 0, U_MAT_CARDBOARD, U_MAT_MASK, 0, 0 },
{ "*mat_hardwood", 0, 0, U_MAT_HARDWOOD, U_MAT_MASK, 0, 0 },
{ "*mat_softwood", 0, 0, U_MAT_SOFTWOOD, U_MAT_MASK, 0, 0 },
{ "*mat_plank", 0, 0, U_MAT_PLANK, U_MAT_MASK, 0, 0 },
{ "*mat_glass", 0, 0, U_MAT_GLASS, U_MAT_MASK, 0, 0 },
{ "*mat_water", 0, 0, U_MAT_WATER, U_MAT_MASK, 0, 0 },
{ "*mat_stucco", 0, 0, U_MAT_STUCCO, U_MAT_MASK, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

View File

@ -0,0 +1,195 @@
/* -------------------------------------------------------------------------------
This code is based on source provided under the terms of the Id Software
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.
All changes and additions to the original source which have been developed by
other contributors (see CONTRIBUTORS) are provided under the terms of the
license the contributors choose (see LICENSE), to the extent permitted by the
LICENSE_ID. If you did not receive a copy of the contributor license,
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_QFUSION_H
#define GAME_QFUSION_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define F_CONT_SOLID 1 /* an eye is never valid in a solid */
#define F_CONT_LAVA 8
#define F_CONT_SLIME 16
#define F_CONT_WATER 32
#define F_CONT_FOG 64
#define F_CONT_AREAPORTAL 0x8000
#define F_CONT_PLAYERCLIP 0x10000
#define F_CONT_MONSTERCLIP 0x20000
#define F_CONT_TELEPORTER 0x40000
#define F_CONT_JUMPPAD 0x80000
#define F_CONT_CLUSTERPORTAL 0x100000
#define F_CONT_DONOTENTER 0x200000
#define F_CONT_BOTCLIP 0x400000
#define F_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define F_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define F_CONT_CORPSE 0x4000000
#define F_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define F_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define F_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define F_CONT_TRIGGER 0x40000000
#define F_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define F_SURF_NODAMAGE 0x1 /* never give falling damage */
#define F_SURF_SLICK 0x2 /* effects game physics */
#define F_SURF_SKY 0x4 /* lighting from environment map */
#define F_SURF_LADDER 0x8
#define F_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define F_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define F_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define F_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define F_SURF_HINT 0x100 /* make a primary bsp splitter */
#define F_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define F_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define F_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define F_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define F_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define F_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define F_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define F_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define F_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define F_SURF_DUST 0x40000 /* leave a dust trail when walking on this surface */
/* ydnar flags */
#define F_SURF_VERTEXLIT (F_SURF_POINTLIGHT | F_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"qfusion", /* -game x */
"baseq3", /* default base game data dir */
".q3a", /* unix home sub-dir */
"quake", /* magic path word */
"scripts", /* shader directory */
2048, /* max lightmapped surface verts */
2048, /* max surface verts */
12288, /* max surface indexes */
true, /* flares */
"flareshader", /* default flare shader */
false, /* wolf lighting model? */
512, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"FBSP", /* bsp file prefix */
1, /* bsp file version */
false, /* cod-style lump len/ofs order */
LoadRBSPFile, /* bsp load function */
WriteRBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", F_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", F_CONT_ORIGIN, F_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", F_CONT_AREAPORTAL, F_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", F_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", F_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", F_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, F_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, F_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, F_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, F_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, F_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, F_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, F_CONT_SOLID, F_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", F_CONT_TRIGGER, F_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", F_CONT_WATER, F_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", F_CONT_SLIME, F_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", F_CONT_LAVA, F_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", F_CONT_PLAYERCLIP, F_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", F_CONT_MONSTERCLIP, F_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", F_CONT_NODROP, F_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", F_CONT_CLUSTERPORTAL, F_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", F_CONT_DONOTENTER, F_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", F_CONT_BOTCLIP, F_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", F_CONT_FOG, F_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, F_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, F_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, F_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, F_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, F_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, F_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, F_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, F_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, F_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, F_SURF_NODLIGHT, 0, 0, 0 },
{ "dust", 0, 0, F_SURF_DUST, 0, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

191
xtools/bsplib/game_quake3.h Normal file
View File

@ -0,0 +1,191 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_QUAKE3_H
#define GAME_QUAKE3_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define Q_CONT_SOLID 1 /* an eye is never valid in a solid */
#define Q_CONT_LAVA 8
#define Q_CONT_SLIME 16
#define Q_CONT_WATER 32
#define Q_CONT_FOG 64
#define Q_CONT_AREAPORTAL 0x8000
#define Q_CONT_PLAYERCLIP 0x10000
#define Q_CONT_MONSTERCLIP 0x20000
#define Q_CONT_TELEPORTER 0x40000
#define Q_CONT_JUMPPAD 0x80000
#define Q_CONT_CLUSTERPORTAL 0x100000
#define Q_CONT_DONOTENTER 0x200000
#define Q_CONT_BOTCLIP 0x400000
#define Q_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define Q_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define Q_CONT_CORPSE 0x4000000
#define Q_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define Q_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define Q_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define Q_CONT_TRIGGER 0x40000000
#define Q_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define Q_SURF_NODAMAGE 0x1 /* never give falling damage */
#define Q_SURF_SLICK 0x2 /* effects game physics */
#define Q_SURF_SKY 0x4 /* lighting from environment map */
#define Q_SURF_LADDER 0x8
#define Q_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define Q_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define Q_SURF_FLESH 0x40 /* make flesh sounds and effects */
#define Q_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define Q_SURF_HINT 0x100 /* make a primary bsp splitter */
#define Q_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define Q_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define Q_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define Q_SURF_METALSTEPS 0x1000 /* clanking footsteps */
#define Q_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define Q_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define Q_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define Q_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define Q_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define Q_SURF_DUST 0x40000 /* leave a dust trail when walking on this surface */
/* ydnar flags */
#define Q_SURF_VERTEXLIT (Q_SURF_POINTLIGHT | Q_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"quake3", /* -game x */
"baseq3", /* default base game data dir */
".q3a", /* unix home sub-dir */
"quake", /* magic path word */
"scripts", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
false, /* flares */
"flareshader", /* default flare shader */
false, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
false, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", Q_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", Q_CONT_ORIGIN, Q_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", Q_CONT_AREAPORTAL, Q_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", Q_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", Q_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", Q_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, Q_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, Q_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, Q_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, Q_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, Q_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, Q_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, Q_CONT_SOLID, Q_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", Q_CONT_TRIGGER, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", Q_CONT_WATER, Q_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", Q_CONT_SLIME, Q_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", Q_CONT_LAVA, Q_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", Q_CONT_PLAYERCLIP, Q_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", Q_CONT_MONSTERCLIP, Q_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", Q_CONT_NODROP, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", Q_CONT_CLUSTERPORTAL, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", Q_CONT_DONOTENTER, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "botclip", Q_CONT_BOTCLIP, Q_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", Q_CONT_FOG, Q_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, Q_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, Q_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, Q_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, Q_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, Q_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, Q_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, Q_SURF_METALSTEPS, 0, 0, 0 },
{ "flesh", 0, 0, Q_SURF_FLESH, 0, 0, 0 },
{ "nosteps", 0, 0, Q_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, Q_SURF_NODLIGHT, 0, 0, 0 },
{ "dust", 0, 0, Q_SURF_DUST, 0, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

257
xtools/bsplib/game_sof2.h Normal file
View File

@ -0,0 +1,257 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_SOF2_H
#define GAME_SOF2_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* thanks to the gracious fellows at raven */
#define S_CONT_SOLID 0x00000001 /* Default setting. An eye is never valid in a solid */
#define S_CONT_LAVA 0x00000002
#define S_CONT_WATER 0x00000004
#define S_CONT_FOG 0x00000008
#define S_CONT_PLAYERCLIP 0x00000010
#define S_CONT_MONSTERCLIP 0x00000020
#define S_CONT_BOTCLIP 0x00000040
#define S_CONT_SHOTCLIP 0x00000080
#define S_CONT_BODY 0x00000100 /* should never be on a brush, only in game */
#define S_CONT_CORPSE 0x00000200 /* should never be on a brush, only in game */
#define S_CONT_TRIGGER 0x00000400
#define S_CONT_NODROP 0x00000800 /* don't leave bodies or items (death fog, lava) */
#define S_CONT_TERRAIN 0x00001000 /* volume contains terrain data */
#define S_CONT_LADDER 0x00002000
#define S_CONT_ABSEIL 0x00004000 /* used like ladder to define where an NPC can abseil */
#define S_CONT_OPAQUE 0x00008000 /* defaults to on, when off, solid can be seen through */
#define S_CONT_OUTSIDE 0x00010000 /* volume is considered to be in the outside (i.e. not indoors) */
#define S_CONT_SLIME 0x00020000 /* don't be fooled. it may SAY "slime" but it really means "projectileclip" */
#define S_CONT_LIGHTSABER 0x00040000
#define S_CONT_TELEPORTER 0x00080000
#define S_CONT_ITEM 0x00100000
#define S_CONT_DETAIL 0x08000000 /* brushes not used for the bsp */
#define S_CONT_TRANSLUCENT 0x80000000 /* don't consume surface fragments inside */
#define S_SURF_SKY 0x00002000 /* lighting from environment map */
#define S_SURF_SLICK 0x00004000 /* affects game physics */
#define S_SURF_METALSTEPS 0x00008000 /* chc needs this since we use same tools */
#define S_SURF_FORCEFIELD 0x00010000 /* chc */
#define S_SURF_NODAMAGE 0x00040000 /* never give falling damage */
#define S_SURF_NOIMPACT 0x00080000 /* don't make missile explosions */
#define S_SURF_NOMARKS 0x00100000 /* don't leave missile marks */
#define S_SURF_NODRAW 0x00200000 /* don't generate a drawsurface at all */
#define S_SURF_NOSTEPS 0x00400000 /* no footstep sounds */
#define S_SURF_NODLIGHT 0x00800000 /* don't dlight even if solid (solid lava, skies) */
#define S_SURF_NOMISCENTS 0x01000000 /* no client models allowed on this surface */
#define S_SURF_PATCH 0x80000000 /* mark this face as a patch(editor only) */
/* materials */
#define S_MAT_BITS 5
#define S_MAT_MASK 0x1f /* mask to get the material type */
#define S_MAT_NONE 0 /* for when the artist hasn't set anything up =) */
#define S_MAT_SOLIDWOOD 1 /* freshly cut timber */
#define S_MAT_HOLLOWWOOD 2 /* termite infested creaky wood */
#define S_MAT_SOLIDMETAL 3 /* solid girders */
#define S_MAT_HOLLOWMETAL 4 /* hollow metal machines */
#define S_MAT_SHORTGRASS 5 /* manicured lawn */
#define S_MAT_LONGGRASS 6 /* long jungle grass */
#define S_MAT_DIRT 7 /* hard mud */
#define S_MAT_SAND 8 /* sandy beach */
#define S_MAT_GRAVEL 9 /* lots of small stones */
#define S_MAT_GLASS 10
#define S_MAT_CONCRETE 11 /* hardened concrete pavement */
#define S_MAT_MARBLE 12 /* marble floors */
#define S_MAT_WATER 13 /* light covering of water on a surface */
#define S_MAT_SNOW 14 /* freshly laid snow */
#define S_MAT_ICE 15 /* packed snow/solid ice */
#define S_MAT_FLESH 16 /* hung meat, corpses in the world */
#define S_MAT_MUD 17 /* wet soil */
#define S_MAT_BPGLASS 18 /* bulletproof glass */
#define S_MAT_DRYLEAVES 19 /* dried up leaves on the floor */
#define S_MAT_GREENLEAVES 20 /* fresh leaves still on a tree */
#define S_MAT_FABRIC 21 /* Cotton sheets */
#define S_MAT_CANVAS 22 /* tent material */
#define S_MAT_ROCK 23
#define S_MAT_RUBBER 24 /* hard tire like rubber */
#define S_MAT_PLASTIC 25
#define S_MAT_TILES 26 /* tiled floor */
#define S_MAT_CARPET 27 /* lush carpet */
#define S_MAT_PLASTER 28 /* drywall style plaster */
#define S_MAT_SHATTERGLASS 29 /* glass with the Crisis Zone style shattering */
#define S_MAT_ARMOR 30 /* body armor */
#define S_MAT_COMPUTER 31 /* computers/electronic equipment */
#define S_MAT_LAST 32 /* number of materials */
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"sof2", /* -game x */
"base", /* default base game data dir */
".sof2", /* unix home sub-dir */
"soldier", /* magic path word */
"shaders", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
true, /* flares */
"gfx/misc/lens_flare", /* default flare shader */
false, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"RBSP", /* bsp file prefix */
1, /* bsp file version */
false, /* cod-style lump len/ofs order */
LoadRBSPFile, /* bsp load function */
WriteRBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", S_CONT_SOLID | S_CONT_OPAQUE, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", 0, S_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", S_CONT_TRANSLUCENT, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", S_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", S_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", 0, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, 0, 0, C_HINT, 0 },
{ "nodraw", 0, 0, S_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, 0, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, 0, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, 0, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, 0, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, S_CONT_SOLID, 0, 0, 0, C_SOLID },
{ "nonopaque", 0, S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, 0 }, /* setting trans ok? */
{ "trigger", S_CONT_TRIGGER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", S_CONT_WATER, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slime", S_CONT_SLIME, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", S_CONT_LAVA, S_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "shotclip", S_CONT_SHOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* setting trans/detail ok? */
{ "playerclip", S_CONT_PLAYERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", S_CONT_MONSTERCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", S_CONT_NODROP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "terrain", S_CONT_TERRAIN, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "ladder", S_CONT_LADDER, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "abseil", S_CONT_ABSEIL, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "outside", S_CONT_OUTSIDE, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "botclip", S_CONT_BOTCLIP, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "fog", S_CONT_FOG, S_CONT_SOLID | S_CONT_OPAQUE, 0, 0, C_FOG | C_DETAIL | C_TRANSLUCENT, C_SOLID }, /* nonopaque? */
{ "sky", 0, 0, S_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, S_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, S_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, S_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "nodamage", 0, 0, S_SURF_NODAMAGE, 0, 0, 0 },
{ "metalsteps", 0, 0, S_SURF_METALSTEPS, 0, 0, 0 },
{ "nosteps", 0, 0, S_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, S_SURF_NODLIGHT, 0, 0, 0 },
{ "nomiscents", 0, 0, S_SURF_NOMISCENTS, 0, 0, 0 },
{ "forcefield", 0, 0, S_SURF_FORCEFIELD, 0, 0, 0 },
/* materials */
{ "*mat_none", 0, 0, S_MAT_NONE, S_MAT_MASK, 0, 0 },
{ "*mat_solidwood", 0, 0, S_MAT_SOLIDWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_hollowwood", 0, 0, S_MAT_HOLLOWWOOD, S_MAT_MASK, 0, 0 },
{ "*mat_solidmetal", 0, 0, S_MAT_SOLIDMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_hollowmetal", 0, 0, S_MAT_HOLLOWMETAL, S_MAT_MASK, 0, 0 },
{ "*mat_shortgrass", 0, 0, S_MAT_SHORTGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_longgrass", 0, 0, S_MAT_LONGGRASS, S_MAT_MASK, 0, 0 },
{ "*mat_dirt", 0, 0, S_MAT_DIRT, S_MAT_MASK, 0, 0 },
{ "*mat_sand", 0, 0, S_MAT_SAND, S_MAT_MASK, 0, 0 },
{ "*mat_gravel", 0, 0, S_MAT_GRAVEL, S_MAT_MASK, 0, 0 },
{ "*mat_glass", 0, 0, S_MAT_GLASS, S_MAT_MASK, 0, 0 },
{ "*mat_concrete", 0, 0, S_MAT_CONCRETE, S_MAT_MASK, 0, 0 },
{ "*mat_marble", 0, 0, S_MAT_MARBLE, S_MAT_MASK, 0, 0 },
{ "*mat_water", 0, 0, S_MAT_WATER, S_MAT_MASK, 0, 0 },
{ "*mat_snow", 0, 0, S_MAT_SNOW, S_MAT_MASK, 0, 0 },
{ "*mat_ice", 0, 0, S_MAT_ICE, S_MAT_MASK, 0, 0 },
{ "*mat_flesh", 0, 0, S_MAT_FLESH, S_MAT_MASK, 0, 0 },
{ "*mat_mud", 0, 0, S_MAT_MUD, S_MAT_MASK, 0, 0 },
{ "*mat_bpglass", 0, 0, S_MAT_BPGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_dryleaves", 0, 0, S_MAT_DRYLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_greenleaves", 0, 0, S_MAT_GREENLEAVES, S_MAT_MASK, 0, 0 },
{ "*mat_fabric", 0, 0, S_MAT_FABRIC, S_MAT_MASK, 0, 0 },
{ "*mat_canvas", 0, 0, S_MAT_CANVAS, S_MAT_MASK, 0, 0 },
{ "*mat_rock", 0, 0, S_MAT_ROCK, S_MAT_MASK, 0, 0 },
{ "*mat_rubber", 0, 0, S_MAT_RUBBER, S_MAT_MASK, 0, 0 },
{ "*mat_plastic", 0, 0, S_MAT_PLASTIC, S_MAT_MASK, 0, 0 },
{ "*mat_tiles", 0, 0, S_MAT_TILES, S_MAT_MASK, 0, 0 },
{ "*mat_carpet", 0, 0, S_MAT_CARPET, S_MAT_MASK, 0, 0 },
{ "*mat_plaster", 0, 0, S_MAT_PLASTER, S_MAT_MASK, 0, 0 },
{ "*mat_shatterglass", 0, 0, S_MAT_SHATTERGLASS, S_MAT_MASK, 0, 0 },
{ "*mat_armor", 0, 0, S_MAT_ARMOR, S_MAT_MASK, 0, 0 },
{ "*mat_computer", 0, 0, S_MAT_COMPUTER, S_MAT_MASK, 0, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

238
xtools/bsplib/game_wolf.h Normal file
View File

@ -0,0 +1,238 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_WOLF_H
#define GAME_WOLF_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* game flags */
#define W_CONT_SOLID 1 /* an eye is never valid in a solid */
#define W_CONT_LAVA 8
#define W_CONT_SLIME 16
#define W_CONT_WATER 32
#define W_CONT_FOG 64
#define W_CONT_MISSILECLIP 0x80 /* wolf ranged missile blocking */
#define W_CONT_ITEM 0x100 /* wolf item contents */
#define W_CONT_AI_NOSIGHT 0x1000 /* wolf ai sight blocking */
#define W_CONT_CLIPSHOT 0x2000 /* wolf shot clip */
#define W_CONT_AREAPORTAL 0x8000
#define W_CONT_PLAYERCLIP 0x10000
#define W_CONT_MONSTERCLIP 0x20000
#define W_CONT_TELEPORTER 0x40000
#define W_CONT_JUMPPAD 0x80000
#define W_CONT_CLUSTERPORTAL 0x100000
#define W_CONT_DONOTENTER 0x200000
#define W_CONT_DONOTENTER_LARGE 0x400000 /* wolf dne */
#define W_CONT_ORIGIN 0x1000000 /* removed before bsping an entity */
#define W_CONT_BODY 0x2000000 /* should never be on a brush, only in game */
#define W_CONT_CORPSE 0x4000000
#define W_CONT_DETAIL 0x8000000 /* brushes not used for the bsp */
#define W_CONT_STRUCTURAL 0x10000000 /* brushes used for the bsp */
#define W_CONT_TRANSLUCENT 0x20000000 /* don't consume surface fragments inside */
#define W_CONT_TRIGGER 0x40000000
#define W_CONT_NODROP 0x80000000 /* don't leave bodies or items (death fog, lava) */
#define W_SURF_NODAMAGE 0x1 /* never give falling damage */
#define W_SURF_SLICK 0x2 /* effects game physics */
#define W_SURF_SKY 0x4 /* lighting from environment map */
#define W_SURF_LADDER 0x8
#define W_SURF_NOIMPACT 0x10 /* don't make missile explosions */
#define W_SURF_NOMARKS 0x20 /* don't leave missile marks */
#define W_SURF_CERAMIC 0x40 /* wolf ceramic material */
#define W_SURF_NODRAW 0x80 /* don't generate a drawsurface at all */
#define W_SURF_HINT 0x100 /* make a primary bsp splitter */
#define W_SURF_SKIP 0x200 /* completely ignore, allowing non-closed brushes */
#define W_SURF_NOLIGHTMAP 0x400 /* surface doesn't need a lightmap */
#define W_SURF_POINTLIGHT 0x800 /* generate lighting info at vertexes */
#define W_SURF_METAL 0x1000 /* wolf metal material */
#define W_SURF_NOSTEPS 0x2000 /* no footstep sounds */
#define W_SURF_NONSOLID 0x4000 /* don't collide against curves with this set */
#define W_SURF_LIGHTFILTER 0x8000 /* act as a light filter during q3map -light */
#define W_SURF_ALPHASHADOW 0x10000 /* do per-pixel light shadow casting in q3map */
#define W_SURF_NODLIGHT 0x20000 /* don't dlight even if solid (solid lava, skies) */
#define W_SURF_WOOD 0x40000 /* wolf wood material */
#define W_SURF_GRASS 0x80000 /* wolf grass material */
#define W_SURF_GRAVEL 0x100000 /* wolf gravel material */
#define W_SURF_GLASS 0x200000 /* wolf glass material */
#define W_SURF_SNOW 0x400000 /* wolf snow material */
#define W_SURF_ROOF 0x800000 /* wolf roof material */
#define W_SURF_RUBBLE 0x1000000 /* wolf rubble material */
#define W_SURF_CARPET 0x2000000 /* wolf carpet material */
#define W_SURF_MONSTERSLICK 0x4000000 /* wolf npc slick surface */
#define W_SURF_MONSLICK_W 0x8000000 /* wolf slide bodies west */
#define W_SURF_MONSLICK_N 0x10000000 /* wolf slide bodies north */
#define W_SURF_MONSLICK_E 0x20000000 /* wolf slide bodies east */
#define W_SURF_MONSLICK_S 0x40000000 /* wolf slide bodies south */
/* ydnar flags */
#define W_SURF_VERTEXLIT (W_SURF_POINTLIGHT | W_SURF_NOLIGHTMAP)
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"wolf", /* -game x */
"main", /* default base game data dir */
".wolf", /* unix home sub-dir */
"wolf", /* magic path word */
"scripts", /* shader directory */
64, /* max lightmapped surface verts */
999, /* max surface verts */
6000, /* max surface indexes */
false, /* flares */
"flareshader", /* default flare shader */
true, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
false, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", W_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", W_CONT_ORIGIN, W_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", W_CONT_AREAPORTAL, W_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", W_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", W_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", W_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, W_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, W_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, W_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, W_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, W_CONT_SOLID, W_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", W_CONT_TRIGGER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", W_CONT_WATER, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slag", W_CONT_SLIME, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", W_CONT_LAVA, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", W_CONT_PLAYERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", W_CONT_MONSTERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipmissile", W_CONT_MISSILECLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipshot", W_CONT_CLIPSHOT, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", W_CONT_NODROP, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", W_CONT_CLUSTERPORTAL, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "nonotenterlarge",W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", W_CONT_FOG, W_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, W_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, W_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, W_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, W_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, W_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, W_SURF_NODAMAGE, 0, 0, 0 },
{ "nosteps", 0, 0, W_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, W_SURF_NODLIGHT, 0, 0, 0 },
/* materials */
{ "metal", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "metalsteps", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "glass", 0, 0, W_SURF_GLASS, 0, 0, 0 },
{ "ceramic", 0, 0, W_SURF_CERAMIC, 0, 0, 0 },
{ "woodsteps", 0, 0, W_SURF_WOOD, 0, 0, 0 },
{ "grasssteps", 0, 0, W_SURF_GRASS, 0, 0, 0 },
{ "gravelsteps", 0, 0, W_SURF_GRAVEL, 0, 0, 0 },
{ "rubble", 0, 0, W_SURF_RUBBLE, 0, 0, 0 },
{ "carpetsteps", 0, 0, W_SURF_CARPET, 0, 0, 0 },
{ "snowsteps", 0, 0, W_SURF_SNOW, 0, 0, 0 },
{ "roofsteps", 0, 0, W_SURF_ROOF, 0, 0, 0 },
/* ai */
{ "ai_nosight", W_CONT_AI_NOSIGHT, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* ydnar: experimental until bits are confirmed! */
{ "ai_nopass", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "ai_nopasslarge", W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* sliding bodies */
{ "monsterslick", 0, 0, W_SURF_MONSTERSLICK, 0, C_TRANSLUCENT, 0 },
{ "monsterslicknorth", 0, 0, W_SURF_MONSLICK_N, 0, C_TRANSLUCENT, 0 },
{ "monsterslickeast", 0, 0, W_SURF_MONSLICK_E, 0, C_TRANSLUCENT, 0 },
{ "monsterslicksouth", 0, 0, W_SURF_MONSLICK_S, 0, C_TRANSLUCENT, 0 },
{ "monsterslickwest", 0, 0, W_SURF_MONSLICK_W, 0, C_TRANSLUCENT, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

177
xtools/bsplib/game_wolfet.h Normal file
View File

@ -0,0 +1,177 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
Support for Wolfenstein: Enemy Territory by ydnar@splashdamage.com
------------------------------------------------------------------------------- */
/* marker */
#ifndef GAME_WOLFET_H
#define GAME_WOLFET_H
/* -------------------------------------------------------------------------------
content and surface flags
------------------------------------------------------------------------------- */
/* this file must be included *after* game_wolf.h because it shares defines! */
#define W_SURF_SPLASH 0x00000040 /* enemy territory water splash surface */
#define W_SURF_LANDMINE 0x80000000 /* enemy territory 'landminable' surface */
/* -------------------------------------------------------------------------------
game_t struct
------------------------------------------------------------------------------- */
{
"et", /* -game x */
"etmain", /* default base game data dir */
".etwolf", /* unix home sub-dir */
"et", /* magic path word */
"scripts", /* shader directory */
1024, /* max lightmapped surface verts */
1024, /* max surface verts */
6144, /* max surface indexes */
false, /* flares */
"flareshader", /* default flare shader */
true, /* wolf lighting model? */
128, /* lightmap width/height */
1.0f, /* lightmap gamma */
1.0f, /* lightmap compensate */
"IBSP", /* bsp file prefix */
47, /* bsp file version */
false, /* cod-style lump len/ofs order */
LoadIBSPFile, /* bsp load function */
WriteIBSPFile, /* bsp write function */
{
/* name contentFlags contentFlagsClear surfaceFlags surfaceFlagsClear compileFlags compileFlagsClear */
/* default */
{ "default", W_CONT_SOLID, -1, 0, -1, C_SOLID, -1 },
/* ydnar */
{ "lightgrid", 0, 0, 0, 0, C_LIGHTGRID, 0 },
{ "antiportal", 0, 0, 0, 0, C_ANTIPORTAL, 0 },
{ "skip", 0, 0, 0, 0, C_SKIP, 0 },
/* compiler */
{ "origin", W_CONT_ORIGIN, W_CONT_SOLID, 0, 0, C_ORIGIN | C_TRANSLUCENT, C_SOLID },
{ "areaportal", W_CONT_AREAPORTAL, W_CONT_SOLID, 0, 0, C_AREAPORTAL | C_TRANSLUCENT, C_SOLID },
{ "trans", W_CONT_TRANSLUCENT, 0, 0, 0, C_TRANSLUCENT, 0 },
{ "detail", W_CONT_DETAIL, 0, 0, 0, C_DETAIL, 0 },
{ "structural", W_CONT_STRUCTURAL, 0, 0, 0, C_STRUCTURAL, 0 },
{ "hint", 0, 0, W_SURF_HINT, 0, C_HINT, 0 },
{ "nodraw", 0, 0, W_SURF_NODRAW, 0, C_NODRAW, 0 },
{ "alphashadow", 0, 0, W_SURF_ALPHASHADOW, 0, C_ALPHASHADOW | C_TRANSLUCENT, 0 },
{ "lightfilter", 0, 0, W_SURF_LIGHTFILTER, 0, C_LIGHTFILTER | C_TRANSLUCENT, 0 },
{ "nolightmap", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
{ "pointlight", 0, 0, W_SURF_VERTEXLIT, 0, C_VERTEXLIT, 0 },
/* game */
{ "nonsolid", 0, W_CONT_SOLID, W_SURF_NONSOLID, 0, 0, C_SOLID },
{ "trigger", W_CONT_TRIGGER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "water", W_CONT_WATER, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "slag", W_CONT_SLIME, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "lava", W_CONT_LAVA, W_CONT_SOLID, 0, 0, C_LIQUID | C_TRANSLUCENT, C_SOLID },
{ "playerclip", W_CONT_PLAYERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "monsterclip", W_CONT_MONSTERCLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipmissile", W_CONT_MISSILECLIP, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "clipshot", W_CONT_CLIPSHOT, W_CONT_SOLID, 0, 0, C_DETAIL | C_TRANSLUCENT, C_SOLID },
{ "nodrop", W_CONT_NODROP, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "clusterportal", W_CONT_CLUSTERPORTAL, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "donotenter", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "nonotenterlarge",W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "fog", W_CONT_FOG, W_CONT_SOLID, 0, 0, C_FOG, C_SOLID },
{ "sky", 0, 0, W_SURF_SKY, 0, C_SKY, 0 },
{ "slick", 0, 0, W_SURF_SLICK, 0, 0, 0 },
{ "noimpact", 0, 0, W_SURF_NOIMPACT, 0, 0, 0 },
{ "nomarks", 0, 0, W_SURF_NOMARKS, 0, C_NOMARKS, 0 },
{ "ladder", 0, 0, W_SURF_LADDER, 0, 0, 0 },
{ "nodamage", 0, 0, W_SURF_NODAMAGE, 0, 0, 0 },
{ "nosteps", 0, 0, W_SURF_NOSTEPS, 0, 0, 0 },
{ "nodlight", 0, 0, W_SURF_NODLIGHT, 0, 0, 0 },
/* wolf et landmine-able surface */
{ "landmine", 0, 0, W_SURF_LANDMINE, 0, 0, 0 },
/* materials */
{ "metal", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "metalsteps", 0, 0, W_SURF_METAL, 0, 0, 0 },
{ "glass", 0, 0, W_SURF_GLASS, 0, 0, 0 },
{ "splash", 0, 0, W_SURF_SPLASH, 0, 0, 0 },
{ "woodsteps", 0, 0, W_SURF_WOOD, 0, 0, 0 },
{ "grasssteps", 0, 0, W_SURF_GRASS, 0, 0, 0 },
{ "gravelsteps", 0, 0, W_SURF_GRAVEL, 0, 0, 0 },
{ "rubble", 0, 0, W_SURF_RUBBLE, 0, 0, 0 },
{ "carpetsteps", 0, 0, W_SURF_CARPET, 0, 0, 0 },
{ "snowsteps", 0, 0, W_SURF_SNOW, 0, 0, 0 },
{ "roofsteps", 0, 0, W_SURF_ROOF, 0, 0, 0 },
/* ai */
{ "ai_nosight", W_CONT_AI_NOSIGHT, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* ydnar: experimental until bits are confirmed! */
{ "ai_nopass", W_CONT_DONOTENTER, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
{ "ai_nopasslarge", W_CONT_DONOTENTER_LARGE, W_CONT_SOLID, 0, 0, C_TRANSLUCENT, C_SOLID },
/* sliding bodies */
{ "monsterslick", 0, 0, W_SURF_MONSTERSLICK, 0, C_TRANSLUCENT, 0 },
{ "monsterslicknorth", 0, 0, W_SURF_MONSLICK_N, 0, C_TRANSLUCENT, 0 },
{ "monsterslickeast", 0, 0, W_SURF_MONSLICK_E, 0, C_TRANSLUCENT, 0 },
{ "monsterslicksouth", 0, 0, W_SURF_MONSLICK_S, 0, C_TRANSLUCENT, 0 },
{ "monsterslickwest", 0, 0, W_SURF_MONSLICK_W, 0, C_TRANSLUCENT, 0 },
/* null */
{ NULL, 0, 0, 0, 0, 0, 0 }
}
}
/* end marker */
#endif

186
xtools/bsplib/image.c Normal file
View File

@ -0,0 +1,186 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define IMAGE_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
this file contains image pool management with reference counting. note: it isn't
reentrant, so only call it from init/shutdown code or wrap calls in a mutex
------------------------------------------------------------------------------- */
/*
ImageInit()
implicitly called by every function to set up image list
*/
static void ImageInit( void )
{
if( numImages <= 0 )
{
size_t size;
byte *buf = FS_LoadInternal( "checkerboard.dds", &size ); // quake1 missing texture :)
Image_Init( NULL, IL_ALLOW_OVERWRITE|IL_IGNORE_MIPS|IL_USE_LERPING );
// clear images (FIXME: this could theoretically leak)
Mem_Set( images, 0, sizeof( images ));
// generate *bogus image
images[0].name = copystring( DEFAULT_IMAGE );
images[0].pic = FS_LoadImage( "#checkerboard.dds", buf, size );
images[0].refCount = 1;
}
}
/*
ImageFree()
frees an rgba image
*/
void ImageFree( image_t *image )
{
/* dummy check */
if( image == NULL )
return;
/* decrement refcount */
image->refCount--;
if( image->refCount <= 0 )
{
if( image->name ) Mem_Free( image->name );
image->name = NULL;
FS_FreeImage( image->pic );
numImages--;
}
}
/*
ImageFind()
finds an existing rgba image and returns a pointer to the image_t struct or NULL if not found
*/
image_t *ImageFind( const char *filename )
{
int i;
char name[MAX_SYSPATH];
/* init */
ImageInit();
/* dummy check */
if( filename == NULL || filename[0] == '\0' )
return NULL;
/* strip file extension off name */
com.strcpy( name, filename );
FS_StripExtension( name );
// search list
for( i = 0; i < MAX_IMAGES; i++ )
{
if( images[i].name != NULL && !com.strcmp( name, images[i].name ))
return &images[i];
}
return NULL;
}
/*
ImageLoad()
loads an rgba image and returns a pointer to the image_t struct or NULL if not found
*/
image_t *ImageLoad( const char *filename )
{
int i;
image_t *image;
char name[MAX_SYSPATH];
ImageInit();
// dummy check
if( filename == NULL || filename[ 0 ] == '\0' )
return NULL;
com.strcpy( name, filename );
FS_StripExtension( name );
// try to find existing image
image = ImageFind( name );
if( image )
{
image->refCount++;
return image;
}
// search for free spot
for( i = 0; i < MAX_IMAGES; i++ )
{
if( images[i].name == NULL )
{
image = &images[i];
break;
}
}
// too many images?
if( !image ) Sys_Break( "MAX_IMAGES (%d) exceeded, there are too many images used\n", MAX_IMAGES );
image->name = copystring( name );
image->pic = FS_LoadImage( name, NULL, 0 );
// make sure everything's kosher
if( image->pic == NULL )
{
Mem_Free( image->name );
image->name = NULL;
return NULL;
}
image->refCount = 1;
numImages++;
return image;
}

View File

@ -1,19 +1,53 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// leakfile.c - leaf file generation
//=======================================================================
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define LEAKFILE_C
/* dependencies */
#include "q3map2.h"
#include <stdio.h>
#include "bsplib.h"
/*
==============================================================================
Leaf file generation
LEAK FILE GENERATION
Save out name.line for qe3 to read
==============================================================================
*/
/*
=============
LeakFile
@ -21,39 +55,43 @@ LeakFile
Finds the shortest possible chain of portals
that leads from the outside leaf to a specifically
occupied leaf
TTimo: builds a polyline xml node
=============
*/
void LeakFile (tree_t *tree)
void LeakFile( tree_t *tree )
{
vec3_t mid;
file_t *linefile;
char path[MAX_SYSPATH];
char filename[MAX_SYSPATH];
node_t *node;
int count;
if (!tree->outside_node.occupied)
if( !tree->outside_node.occupied )
return;
MsgDev( D_NOTE, "--- LeakFile ---\n" );
// write the points to the file
com.sprintf( path, "maps/%s.lin", gs_filename );
linefile = FS_Open (path, "w" );
if (!linefile) Sys_Error ("Couldn't open %s\n", path);
com.sprintf( filename, "%s.lin", source );
linefile = FS_Open( filename, "w" );
if( !linefile ) Sys_Error( "couldn't open %s\n", filename );
count = 0;
node = &tree->outside_node;
while (node->occupied > 1)
while( node->occupied > 1 )
{
int next;
portal_t *p, *nextportal;
node_t *nextnode;
int s;
int next;
portal_t *p, *nextportal;
node_t *nextnode;
int s;
// find the best portal exit
next = node->occupied;
for (p=node->portals ; p ; p = p->next[!s])
for( p = node->portals; p; p = p->next[!s] )
{
s = (p->nodes[0] == node);
if (p->nodes[s]->occupied && p->nodes[s]->occupied < next)
if( p->nodes[s]->occupied && p->nodes[s]->occupied < next )
{
nextportal = p;
nextnode = p->nodes[s];
@ -61,14 +99,15 @@ void LeakFile (tree_t *tree)
}
}
node = nextnode;
WindingCenter (nextportal->winding, mid);
FS_Printf(linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
WindingCenter( nextportal->winding, mid );
FS_Printf( linefile, "%f %f %f\n", mid[0], mid[1], mid[2] );
count++;
}
// add the occupant center
GetVectorForKey (node->occupant, "origin", mid);
GetVectorForKey( node->occupant, "origin", mid );
FS_Printf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
FS_Close (linefile);
}
FS_Printf( linefile, "%f %f %f\n", mid[0], mid[1], mid[2] );
MsgDev( D_INFO, "%9d point linefile\n", count + 1 );
FS_Close( linefile );
}

2271
xtools/bsplib/light.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,940 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define LIGHT_BOUNCE_C
/* dependencies */
#include "q3map2.h"
/* functions */
/*
RadFreeLights()
deletes any existing lights, freeing up memory for the next bounce
*/
void RadFreeLights( void )
{
light_t *light, *next;
/* delete lights */
for( light = lights; light; light = next )
{
next = light->next;
if( light->w != NULL )
FreeWinding( light->w );
Mem_Free( light );
}
numLights = 0;
lights = NULL;
}
/*
RadClipWindingEpsilon()
clips a rad winding by a plane
based off the regular clip winding code
*/
static void RadClipWindingEpsilon( radWinding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, radWinding_t *front, radWinding_t *back, clipWork_t *cw )
{
vec_t *dists;
int *sides;
int counts[ 3 ];
vec_t dot; /* ydnar: changed from static b/c of threading */ /* VC 4.2 optimizer bug if not static? */
int i, j, k;
radVert_t *v1, *v2, mid;
int maxPoints;
/* crutch */
dists = cw->dists;
sides = cw->sides;
/* clear counts */
counts[ 0 ] = counts[ 1 ] = counts[ 2 ] = 0;
/* determine sides for each point */
for( i = 0; i < in->numVerts; i++ )
{
dot = DotProduct( in->verts[ i ].xyz, normal );
dot -= dist;
dists[ i ] = dot;
if( dot > epsilon )
sides[ i ] = SIDE_FRONT;
else if( dot < -epsilon )
sides[ i ] = SIDE_BACK;
else
sides[ i ] = SIDE_ON;
counts[ sides[ i ] ]++;
}
sides[ i ] = sides[ 0 ];
dists[ i ] = dists[ 0 ];
/* clear front and back */
front->numVerts = back->numVerts = 0;
/* handle all on one side cases */
if( counts[ 0 ] == 0 )
{
memcpy( back, in, sizeof( radWinding_t ) );
return;
}
if( counts[ 1 ] == 0 )
{
memcpy( front, in, sizeof( radWinding_t ) );
return;
}
/* setup windings */
maxPoints = in->numVerts + 4;
/* do individual verts */
for( i = 0; i < in->numVerts; i++ )
{
/* do simple vertex copies first */
v1 = &in->verts[ i ];
if( sides[ i ] == SIDE_ON )
{
memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );
memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );
continue;
}
if( sides[ i ] == SIDE_FRONT )
memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );
if( sides[ i ] == SIDE_BACK )
memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );
if( sides[ i + 1 ] == SIDE_ON || sides[ i + 1 ] == sides[ i ] )
continue;
/* generate a split vertex */
v2 = &in->verts[ (i + 1) % in->numVerts ];
dot = dists[ i ] / (dists[ i ] - dists[ i + 1 ]);
/* average vertex values */
for( j = 0; j < 4; j++ )
{
/* color */
if( j < 4 )
{
for( k = 0; k < MAX_LIGHTMAPS; k++ )
mid.color[ k ][ j ] = v1->color[ k ][ j ] + dot * (v2->color[ k ][ j ] - v1->color[ k ][ j ]);
}
/* xyz, normal */
if( j < 3 )
{
mid.xyz[ j ] = v1->xyz[ j ] + dot * (v2->xyz[ j ] - v1->xyz[ j ]);
mid.normal[ j ] = v1->normal[ j ] + dot * (v2->normal[ j ] - v1->normal[ j ]);
}
/* st, lightmap */
if( j < 2 )
{
mid.st[ j ] = v1->st[ j ] + dot * (v2->st[ j ] - v1->st[ j ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
mid.lightmap[ k ][ j ] = v1->lightmap[ k ][ j ] + dot * (v2->lightmap[ k ][ j ] - v1->lightmap[ k ][ j ]);
}
}
/* normalize the averaged normal */
VectorNormalize( mid.normal );
/* copy the midpoint to both windings */
Mem_Copy( &front->verts[ front->numVerts++ ], &mid, sizeof( radVert_t ) );
Mem_Copy( &back->verts[ back->numVerts++ ], &mid, sizeof( radVert_t ) );
}
/* error check */
if( front->numVerts > maxPoints || front->numVerts > maxPoints )
Sys_Error( "RadClipWindingEpsilon: points exceeded estimate\n" );
if( front->numVerts > MAX_POINTS_ON_WINDING || front->numVerts > MAX_POINTS_ON_WINDING )
Sys_Error( "RadClipWindingEpsilon: MAX_POINTS_ON_WINDING limit exceeded\n" );
}
/*
RadSampleImage()
samples a texture image for a given color
returns false if pixels are bad
*/
bool RadSampleImage( rgbdata_t *pic, float st[2], float color[4] )
{
float sto[2];
int x, y;
byte *pixels;
color[0] = color[1] = color[2] = color[3] = 255;
if( pic == NULL || pic->buffer == NULL || pic->width < 1 || pic->height < 1 )
return false;
// bias st
sto[0] = st[0];
while( sto[ 0 ] < 0.0f ) sto[0] += 1.0f;
sto[1] = st[1];
while( sto[1] < 0.0f ) sto[1] += 1.0f;
pixels = pic->buffer;
// get offsets
x = ((float) pic->width * sto[0]) + 0.5f;
x %= pic->width;
y = ((float) pic->height * sto[1]) + 0.5f;
y %= pic->height;
/* get pixel */
pixels += (y * pic->width * 4) + (x * 4);
VectorCopy( pixels, color );
color[3] = pixels[3];
return true;
}
/*
RadSample()
samples a fragment's lightmap or vertex color and returns an
average color and a color gradient for the sample
*/
#define MAX_SAMPLES 150
#define SAMPLE_GRANULARITY 6
static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style )
{
int i, j, k, l, v, x, y, samples;
vec3_t color, mins, maxs;
vec4_t textureColor;
float alpha, alphaI, bf;
vec3_t blend;
float st[2], lightmap[2], *radLuxel;
radVert_t *rv[3];
/* initial setup */
ClearBounds( mins, maxs );
VectorClear( average );
VectorClear( gradient );
alpha = 0;
/* dummy check */
if( rw == NULL || rw->numVerts < 3 )
return;
/* start sampling */
samples = 0;
/* sample vertex colors if no lightmap or this is the initial pass */
if( lm == NULL || lm->radLuxels[lightmapNum] == NULL || bouncing == false )
{
for( samples = 0; samples < rw->numVerts; samples++ )
{
/* multiply by texture color */
if( !RadSampleImage( si->lightImage->pic, rw->verts[samples].st, textureColor ))
{
VectorCopy( si->averageColor, textureColor );
textureColor[ 4 ] = 255.0f;
}
for( i = 0; i < 3; i++ )
color[ i ] = (textureColor[ i ] / 255) * (rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f);
AddPointToBounds( color, mins, maxs );
VectorAdd( average, color, average );
/* get alpha */
alpha += (textureColor[ 3 ] / 255.0f) * (rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f);
}
/* set style */
*style = ds->vertexStyles[ lightmapNum ];
}
/* sample lightmap */
else
{
/* fracture the winding into a fan (including degenerate tris) */
for( v = 1; v < (rw->numVerts - 1) && samples < MAX_SAMPLES; v++ )
{
/* get a triangle */
rv[ 0 ] = &rw->verts[ 0 ];
rv[ 1 ] = &rw->verts[ v ];
rv[ 2 ] = &rw->verts[ v + 1 ];
/* this code is embarassing (really should just rasterize the triangle) */
for( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ )
{
for( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ )
{
for( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ )
{
/* create a blend vector (barycentric coordinates) */
blend[ 0 ] = i;
blend[ 1 ] = j;
blend[ 2 ] = k;
bf = (1.0 / (blend[ 0 ] + blend[ 1 ] + blend[ 2 ]));
VectorScale( blend, bf, blend );
/* create a blended sample */
st[ 0 ] = st[ 1 ] = 0.0f;
lightmap[ 0 ] = lightmap[ 1 ] = 0.0f;
alphaI = 0.0f;
for( l = 0; l < 3; l++ )
{
st[ 0 ] += (rv[ l ]->st[ 0 ] * blend[ l ]);
st[ 1 ] += (rv[ l ]->st[ 1 ] * blend[ l ]);
lightmap[ 0 ] += (rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ]);
lightmap[ 1 ] += (rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ]);
alphaI += (rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ]);
}
/* get lightmap xy coords */
x = lightmap[ 0 ] / (float) superSample;
y = lightmap[ 1 ] / (float) superSample;
if( x < 0 )
x = 0;
else if ( x >= lm->w )
x = lm->w - 1;
if( y < 0 )
y = 0;
else if ( y >= lm->h )
y = lm->h - 1;
/* get radiosity luxel */
radLuxel = RAD_LUXEL( lightmapNum, x, y );
/* ignore unlit/unused luxels */
if( radLuxel[ 0 ] < 0.0f )
continue;
/* inc samples */
samples++;
/* multiply by texture color */
if( !RadSampleImage( si->lightImage->pic, st, textureColor ))
{
VectorCopy( si->averageColor, textureColor );
textureColor[ 4 ] = 255;
}
for( i = 0; i < 3; i++ )
color[ i ] = (textureColor[ i ] / 255) * (radLuxel[ i ] / 255);
AddPointToBounds( color, mins, maxs );
VectorAdd( average, color, average );
/* get alpha */
alpha += (textureColor[ 3 ] / 255) * (alphaI / 255);
}
}
}
}
/* set style */
*style = ds->lightmapStyles[ lightmapNum ];
}
/* any samples? */
if( samples <= 0 )
return;
/* average the color */
VectorScale( average, (1.0 / samples), average );
/* create the color gradient */
//% VectorSubtract( maxs, mins, delta );
/* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */
//% gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f;
//% gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f;
//% gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f;
/* newer: another contrast function */
for( i = 0; i < 3; i++ )
gradient[ i ] = (maxs[ i ] - mins[ i ]) * maxs[ i ];
}
/*
RadSubdivideDiffuseLight()
subdivides a radiosity winding until it is smaller than subdivide, then generates an area light
*/
#define RADIOSITY_MAX_GRADIENT 0.75f //% 0.25f
#define RADIOSITY_VALUE 500.0f
#define RADIOSITY_MIN 0.0001f
#define RADIOSITY_CLIP_EPSILON 0.125f
static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si,
float scale, float subdivide, bool original, radWinding_t *rw, clipWork_t *cw )
{
int i, style;
float dist, area, value;
vec3_t mins, maxs, normal, d1, d2, cross, color, gradient;
light_t *light, *splash;
winding_t *w;
/* dummy check */
if( rw == NULL || rw->numVerts < 3 )
return;
/* get bounds for winding */
ClearBounds( mins, maxs );
for( i = 0; i < rw->numVerts; i++ )
AddPointToBounds( rw->verts[ i ].xyz, mins, maxs );
/* subdivide if necessary */
for( i = 0; i < 3; i++ )
{
if( maxs[ i ] - mins[ i ] > subdivide )
{
radWinding_t front, back;
/* make axial plane */
VectorClear( normal );
normal[ i ] = 1;
dist = (maxs[ i ] + mins[ i ]) * 0.5f;
/* clip the winding */
RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw );
/* recurse */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, false, &front, cw );
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, false, &back, cw );
return;
}
}
/* check area */
area = 0.0f;
for( i = 2; i < rw->numVerts; i++ )
{
VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 );
VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 );
CrossProduct( d1, d2, cross );
area += 0.5f * VectorLength( cross );
}
if( area < 1.0f || area > 20000000.0f )
return;
/* more subdivision may be necessary */
if( bouncing )
{
/* get color sample for the surface fragment */
RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style );
/* if color gradient is too high, subdivide again */
if( subdivide > minDiffuseSubdivide &&
(gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT) )
{
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, (subdivide / 2.0f), false, rw, cw );
return;
}
}
/* create a regular winding and an average normal */
w = AllocWinding( rw->numVerts );
w->numpoints = rw->numVerts;
VectorClear( normal );
for( i = 0; i < rw->numVerts; i++ )
{
VectorCopy( rw->verts[ i ].xyz, w->p[ i ] );
VectorAdd( normal, rw->verts[ i ].normal, normal );
}
VectorScale( normal, (1.0f / rw->numVerts), normal );
if( VectorNormalizeLength( normal ) == 0.0f )
return;
/* early out? */
if( bouncing && VectorLength( color ) < RADIOSITY_MIN )
return;
/* debug code */
//% MsgDev( D_INFO, "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) );
//% MsgDev( D_INFO, "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] );
/* increment counts */
numDiffuseLights++;
switch( ds->surfaceType )
{
case MST_PLANAR:
numBrushDiffuseLights++;
break;
case MST_TRISURF:
numTriangleDiffuseLights++;
break;
case MST_PATCH:
numPatchDiffuseLights++;
break;
}
/* create a light */
light = Malloc( sizeof( *light ));
/* attach it */
ThreadLock();
light->next = lights;
lights = light;
ThreadUnlock();
/* initialize the light */
light->flags = LIGHT_AREA_DEFAULT;
light->type = EMIT_AREA;
light->si = si;
light->fade = 1.0f;
light->w = w;
/* set falloff threshold */
light->falloffTolerance = falloffTolerance;
/* bouncing light? */
if( bouncing == false )
{
/* handle first-pass lights in normal q3a style */
value = si->value;
light->photons = value * area * areaScale;
light->add = value * formFactorValueScale * areaScale;
VectorCopy( si->color, light->color );
VectorScale( light->color, light->add, light->emitColor );
light->style = noStyles ? LS_NORMAL : si->lightStyle;
if( light->style < LS_NORMAL || light->style >= LS_NONE )
light->style = LS_NORMAL;
/* set origin */
VectorAdd( mins, maxs, light->origin );
VectorScale( light->origin, 0.5f, light->origin );
/* nudge it off the plane a bit */
VectorCopy( normal, light->normal );
VectorMA( light->origin, 1.0f, light->normal, light->origin );
light->dist = DotProduct( light->origin, normal );
/* optionally create a point splashsplash light for first pass */
if( original && si->backsplashFraction > 0 )
{
/* allocate a new point light */
splash = Malloc( sizeof( *splash ) );
splash->next = lights;
lights = splash;
/* set it up */
splash->flags = LIGHT_Q3A_DEFAULT;
splash->type = EMIT_POINT;
splash->photons = light->photons * si->backsplashFraction;
splash->fade = 1.0f;
splash->si = si;
VectorMA( light->origin, si->backsplashDistance, normal, splash->origin );
VectorCopy( si->color, splash->color );
splash->falloffTolerance = falloffTolerance;
splash->style = noStyles ? LS_NORMAL : light->style;
/* add to counts */
numPointLights++;
}
}
else
{
/* handle bounced light (radiosity) a little differently */
value = RADIOSITY_VALUE * si->bounceScale * 0.375f;
light->photons = value * area * bounceScale;
light->add = value * formFactorValueScale * bounceScale;
VectorCopy( color, light->color );
VectorScale( light->color, light->add, light->emitColor );
light->style = noStyles ? LS_NORMAL : style;
if( light->style < LS_NORMAL || light->style >= LS_NONE )
light->style = LS_NORMAL;
/* set origin */
WindingCenter( w, light->origin );
/* nudge it off the plane a bit */
VectorCopy( normal, light->normal );
VectorMA( light->origin, 1.0f, light->normal, light->origin );
light->dist = DotProduct( light->origin, normal );
}
/* emit light from both sides? */
if( si->compileFlags & C_FOG || si->twoSided )
light->flags |= LIGHT_TWOSIDED;
//% Msg( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n",
//% light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add,
//% light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ],
//% light->si->shader );
}
/*
RadLightForTriangles()
creates unbounced diffuse lights for triangle soup (misc_models, etc)
*/
void RadLightForTriangles( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )
{
int i, j, k, v;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
float *radVertexLuxel;
radWinding_t rw;
/* get surface */
ds = &bspDrawSurfaces[ num ];
info = &surfaceInfos[ num ];
/* each triangle is a potential emitter */
rw.numVerts = 3;
for( i = 0; i < ds->numIndexes; i += 3 )
{
/* copy each vert */
for( j = 0; j < 3; j++ )
{
/* get vertex index and rad vertex luxel */
v = ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ];
/* get most everything */
memcpy( &rw.verts[ j ], &yDrawVerts[ v ], sizeof( bspDrawVert_t ) );
/* fix colors */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
radVertexLuxel = RAD_VERTEX_LUXEL( k, ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ] );
VectorCopy( radVertexLuxel, rw.verts[ j ].color[ k ] );
rw.verts[ j ].color[ k ][ 3 ] = yDrawVerts[ v ].color[ k ][ 3 ];
}
}
/* subdivide into area lights */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, true, &rw, cw );
}
}
/*
RadLightForPatch()
creates unbounced diffuse lights for patches
*/
#define PLANAR_EPSILON 0.1f
void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )
{
int i, x, y, v, t, pw[ 5 ], r;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
bspDrawVert_t *bogus;
bspDrawVert_t *dv[ 4 ];
mesh_t src, *subdivided, *mesh;
float *radVertexLuxel;
float dist;
vec4_t plane;
bool planar;
radWinding_t rw;
/* get surface */
ds = &bspDrawSurfaces[ num ];
info = &surfaceInfos[ num ];
/* construct a bogus vert list with color index stuffed into color[ 0 ] */
bogus = Malloc( ds->numVerts * sizeof( bspDrawVert_t ) );
memcpy( bogus, &yDrawVerts[ ds->firstVert ], ds->numVerts * sizeof( bspDrawVert_t ) );
for( i = 0; i < ds->numVerts; i++ )
bogus[ i ].color[ 0 ][ 0 ] = i;
/* build a subdivided mesh identical to shadow facets for this patch */
/* this MUST MATCH FacetsForPatch() identically! */
src.width = ds->patchWidth;
src.height = ds->patchHeight;
src.verts = bogus;
//% subdivided = SubdivideMesh( src, 8, 512 );
subdivided = SubdivideMesh2( src, info->patchIterations );
PutMeshOnCurve( *subdivided );
//% MakeMeshNormals( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
Mem_Free( bogus );
/* FIXME: build interpolation table into color[ 1 ] */
/* fix up color indexes */
for( i = 0; i < (mesh->width * mesh->height); i++ )
{
dv[ 0 ] = &mesh->verts[ i ];
if( dv[ 0 ]->color[ 0 ][ 0 ] >= ds->numVerts )
dv[ 0 ]->color[ 0 ][ 0 ] = ds->numVerts - 1;
}
/* iterate through the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* get drawverts */
dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];
dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];
dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];
dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];
/* planar? */
planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz );
if( planar )
{
dist = DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ];
if( fabs( dist ) > PLANAR_EPSILON )
planar = false;
}
/* generate a quad */
if( planar )
{
rw.numVerts = 4;
for( v = 0; v < 4; v++ )
{
/* get most everything */
memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );
/* fix colors */
for( i = 0; i < MAX_LIGHTMAPS; i++ )
{
radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );
VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );
rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];
}
}
/* subdivide into area lights */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, true, &rw, cw );
}
/* generate 2 tris */
else
{
rw.numVerts = 3;
for( t = 0; t < 2; t++ )
{
for( v = 0; v < 3 + t; v++ )
{
/* get "other" triangle (stupid hacky logic, but whatevah) */
if( v == 1 && t == 1 )
v++;
/* get most everything */
memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );
/* fix colors */
for( i = 0; i < MAX_LIGHTMAPS; i++ )
{
radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );
VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );
rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];
}
}
/* subdivide into area lights */
RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, true, &rw, cw );
}
}
}
}
/* free the mesh */
FreeMesh( mesh );
}
/*
RadLight()
creates unbounced diffuse lights for a given surface
*/
void RadLight( int num )
{
int lightmapNum;
float scale, subdivide;
int contentFlags, surfaceFlags, compileFlags;
bspDrawSurface_t *ds;
surfaceInfo_t *info;
rawLightmap_t *lm;
shaderInfo_t *si;
clipWork_t cw;
/* get drawsurface, lightmap, and shader info */
ds = &bspDrawSurfaces[ num ];
info = &surfaceInfos[ num ];
lm = info->lm;
si = info->si;
scale = si->bounceScale;
/* find nodraw bit */
contentFlags = surfaceFlags = compileFlags = 0;
ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, &compileFlags );
/* early outs? */
if( scale <= 0.0f || (si->compileFlags & C_SKY) || si->autosprite ||
(bspShaders[ ds->shaderNum ].contentFlags & contentFlags) || (bspShaders[ ds->shaderNum ].surfaceFlags & surfaceFlags) ||
(si->compileFlags & compileFlags) )
return;
/* determine how much we need to chop up the surface */
if( si->lightSubdivide )
subdivide = si->lightSubdivide;
else
subdivide = diffuseSubdivide;
/* inc counts */
numDiffuseSurfaces++;
/* iterate through styles (this could be more efficient, yes) */
for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
{
/* switch on type */
if( ds->lightmapStyles[ lightmapNum ] != LS_NONE && ds->lightmapStyles[ lightmapNum ] != LS_UNUSED )
{
switch( ds->surfaceType )
{
case MST_PLANAR:
case MST_TRISURF:
RadLightForTriangles( num, lightmapNum, lm, si, scale, subdivide, &cw );
break;
case MST_PATCH:
RadLightForPatch( num, lightmapNum, lm, si, scale, subdivide, &cw );
break;
default: break;
}
}
}
}
/*
RadCreateDiffuseLights()
creates lights for unbounced light on surfaces in the bsp
*/
int iterations = 0;
void RadCreateDiffuseLights( void )
{
MsgDev( D_NOTE, "--- RadCreateDiffuseLights ---\n" );
numDiffuseSurfaces = 0;
numDiffuseLights = 0;
numBrushDiffuseLights = 0;
numTriangleDiffuseLights = 0;
numPatchDiffuseLights = 0;
numAreaLights = 0;
/* hit every surface (threaded) */
RunThreadsOnIndividual( numBSPDrawSurfaces, true, RadLight );
/* dump the lights generated to a file */
if( dump )
{
char dumpName[MAX_SYSPATH], ext[64];
file_t *file;
light_t *light;
com.strcpy( dumpName, source );
FS_StripExtension( dumpName );
com.sprintf( ext, "_bounce_%03d.map", iterations );
com.strcat( dumpName, ext );
file = FS_Open( dumpName, "wb" );
MsgDev( D_INFO, "Writing %s...\n", dumpName );
if( file )
{
for( light = lights; light; light = light->next )
{
FS_Printf( file,
"{\n"
"\"classname\" \"light\"\n"
"\"light\" \"%d\"\n"
"\"origin\" \"%.0f %.0f %.0f\"\n"
"\"_color\" \"%.3f %.3f %.3f\"\n"
"}\n",
(int)light->add,
light->origin[0],
light->origin[1],
light->origin[2],
light->color[0],
light->color[1],
light->color[2] );
}
FS_Close( file );
}
}
/* increment */
iterations++;
/* print counts */
MsgDev( D_INFO, "%8d diffuse surfaces\n", numDiffuseSurfaces );
MsgDev( D_INFO, "%8d total diffuse lights\n", numDiffuseLights );
MsgDev( D_INFO, "%8d brush diffuse lights\n", numBrushDiffuseLights );
MsgDev( D_INFO, "%8d patch diffuse lights\n", numPatchDiffuseLights );
MsgDev( D_INFO, "%8d triangle diffuse lights\n", numTriangleDiffuseLights );
}

1713
xtools/bsplib/light_trace.c Normal file

File diff suppressed because it is too large Load Diff

3592
xtools/bsplib/light_ydnar.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

267
xtools/bsplib/md5.c Normal file
View File

@ -0,0 +1,267 @@
/* marker */
#define MD5_C
#include "q3map2.h"
#include "byteorder.h"
/*
MD5 Message Digest Algorithm. (RFC1321)
*/
/*
This code implements the MD5 message-digest algorithm.
The algorithm is due to Ron Rivest. This code was
written by Colin Plumb in 1993, no copyright is claimed.
This code is in the public domain; do with it what you wish.
Equivalent code is available from RSA Data Security, Inc.
This code has been tested against that, and is equivalent,
except that you don't need to include two pages of legalese
with every copy.
To compute the message digest of a chunk of bytes, declare an
MD5Context structure, pass it to MD5Init, call MD5Update as
needed on buffers full of bytes, and then call MD5Final, which
will fill a supplied 16-byte array with the digest.
*/
/* MD5 context. */
/* The four core functions - F1 is optimized somewhat */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
=================
MD5_Transform
The core of the MD5 algorithm, this alters an existing MD5 hash to
reflect the addition of 16 longwords of new data. MD5Update blocks
the data and converts bytes into longwords for this routine.
=================
*/
void MD5_Transform( unsigned int state[4], unsigned int in[16] ) {
register unsigned int a, b, c, d;
a = state[0];
b = state[1];
c = state[2];
d = state[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
/*
==================
MD5_Init
MD5 initialization. Begins an MD5 operation, writing a new context.
==================
*/
void MD5_Init( MD5_CTX *ctx )
{
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
===================
MD5_Update
MD5 block update operation. Continues an MD5 message-digest operation,
processing another message block, and updating the context.
===================
*/
void MD5_Update( MD5_CTX *ctx, unsigned char const *buf, unsigned int len )
{
unsigned int t;
/* Update bitcount */
t = ctx->bits[0];
if ( ( ctx->bits[0] = t + ( (unsigned int) len << 3 ) ) < t ) {
ctx->bits[1]++; /* Carry from low to high */
}
ctx->bits[1] += len >> 29;
t = ( t >> 3 ) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if ( t ) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if ( len < t ) {
memcpy( p, buf, len );
return;
}
memcpy( p, buf, t );
MD5_Transform( ctx->state, (unsigned int *) ctx->in );
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while( len >= 64 ) {
memcpy( ctx->in, buf, 64 );
MD5_Transform( ctx->state, (unsigned int *) ctx->in );
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memcpy( ctx->in, buf, len );
}
/*
===============
MD5_Final
MD5 finalization. Ends an MD5 message-digest operation,
writing the message digest and zeroizing the context.
===============
*/
void MD5_Final( MD5_CTX *ctx, unsigned char digest[16] )
{
uint count, val0, val1;
byte *p;
/* Compute number of bytes mod 64 */
count = ( ctx->bits[0] >> 3 ) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if ( count < 8 ) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset( p, 0, count );
MD5_Transform( ctx->state, (unsigned int *) ctx->in );
/* Now fill the next block with 56 bytes */
memset( ctx->in, 0, 56 );
} else {
/* Pad block to 56 bytes */
memset( p, 0, count - 8 );
}
/* Append length in bits and transform */
val0 = ctx->bits[0];
val1 = ctx->bits[1];
((unsigned int *) ctx->in)[14] = LittleLong( val0 );
((unsigned int *) ctx->in)[15] = LittleLong( val1 );
MD5_Transform( ctx->state, (unsigned int *) ctx->in );
memcpy( digest, ctx->state, 16 );
memset( ctx, 0, sizeof( ctx ) ); /* In case it's sensitive */
}
/*
===============
MD5_BlockChecksum
===============
*/
unsigned long MD5_BlockChecksum( const void *data, int length )
{
unsigned long digest[4];
unsigned long val;
MD5_CTX ctx;
MD5_Init( &ctx );
MD5_Update( &ctx, (unsigned char *)data, length );
MD5_Final( &ctx, (unsigned char *)digest );
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}

827
xtools/bsplib/mesh.c Normal file
View File

@ -0,0 +1,827 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MESH_C
/* dependencies */
#include "q3map2.h"
/*
LerpDrawVert()
returns an 50/50 interpolated vert
*/
void LerpDrawVert( bspDrawVert_t *a, bspDrawVert_t *b, bspDrawVert_t *out )
{
int k;
out->xyz[ 0 ] = 0.5 * (a->xyz[ 0 ] + b->xyz[ 0 ]);
out->xyz[ 1 ] = 0.5 * (a->xyz[ 1 ] + b->xyz[ 1 ]);
out->xyz[ 2 ] = 0.5 * (a->xyz[ 2 ] + b->xyz[ 2 ]);
out->st[ 0 ] = 0.5 * (a->st[ 0 ] + b->st[ 0 ]);
out->st[ 1 ] = 0.5 * (a->st[ 1 ] + b->st[ 1 ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
out->lightmap[ k ][ 0 ] = 0.5f * (a->lightmap[ k ][ 0 ] + b->lightmap[ k ][ 0 ]);
out->lightmap[ k ][ 1 ] = 0.5f * (a->lightmap[ k ][ 1 ] + b->lightmap[ k ][ 1 ]);
out->color[ k ][ 0 ] = (a->color[ k ][ 0 ] + b->color[ k ][ 0 ]) >> 1;
out->color[ k ][ 1 ] = (a->color[ k ][ 1 ] + b->color[ k ][ 1 ]) >> 1;
out->color[ k ][ 2 ] = (a->color[ k ][ 2 ] + b->color[ k ][ 2 ]) >> 1;
out->color[ k ][ 3 ] = (a->color[ k ][ 3 ] + b->color[ k ][ 3 ]) >> 1;
}
/* ydnar: added normal interpolation */
out->normal[ 0 ] = 0.5f * (a->normal[ 0 ] + b->normal[ 0 ]);
out->normal[ 1 ] = 0.5f * (a->normal[ 1 ] + b->normal[ 1 ]);
out->normal[ 2 ] = 0.5f * (a->normal[ 2 ] + b->normal[ 2 ]);
/* if the interpolant created a bogus normal, just copy the normal from a */
if( VectorNormalizeLength( out->normal ) == 0 )
VectorCopy( a->normal, out->normal );
}
/*
LerpDrawVertAmount()
returns a biased interpolated vert
*/
void LerpDrawVertAmount( bspDrawVert_t *a, bspDrawVert_t *b, float amount, bspDrawVert_t *out )
{
int k;
out->xyz[ 0 ] = a->xyz[ 0 ] + amount * (b->xyz[ 0 ] - a->xyz[ 0 ]);
out->xyz[ 1 ] = a->xyz[ 1 ] + amount * (b->xyz[ 1 ] - a->xyz[ 1 ]);
out->xyz[ 2 ] = a->xyz[ 2 ] + amount * (b->xyz[ 2 ] - a->xyz[ 2 ]);
out->st[ 0 ] = a->st[ 0 ] + amount * (b->st[ 0 ] - a->st[ 0 ]);
out->st[ 1 ] = a->st[ 1 ] + amount * (b->st[ 1 ] - a->st[ 1 ]);
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
out->lightmap[ k ][ 0 ] = a->lightmap[ k ][ 0 ] + amount * (b->lightmap[ k ][ 0 ] - a->lightmap[ k ][ 0 ]);
out->lightmap[ k ][ 1 ] = a->lightmap[ k ][ 1 ] + amount * (b->lightmap[ k ][ 1 ] - a->lightmap[ k ][ 1 ]);
out->color[ k ][ 0 ] = a->color[ k ][ 0 ] + amount * (b->color[ k ][ 0 ] - a->color[ k ][ 0 ]);
out->color[ k ][ 1 ] = a->color[ k ][ 1 ] + amount * (b->color[ k ][ 1 ] - a->color[ k ][ 1 ]);
out->color[ k ][ 2 ] = a->color[ k ][ 2 ] + amount * (b->color[ k ][ 2 ] - a->color[ k ][ 2 ]);
out->color[ k ][ 3 ] = a->color[ k ][ 3 ] + amount * (b->color[ k ][ 3 ] - a->color[ k ][ 3 ]);
}
out->normal[ 0 ] = a->normal[ 0 ] + amount * (b->normal[ 0 ] - a->normal[ 0 ]);
out->normal[ 1 ] = a->normal[ 1 ] + amount * (b->normal[ 1 ] - a->normal[ 1 ]);
out->normal[ 2 ] = a->normal[ 2 ] + amount * (b->normal[ 2 ] - a->normal[ 2 ]);
/* if the interpolant created a bogus normal, just copy the normal from a */
if( VectorNormalizeLength( out->normal ) == 0 )
VectorCopy( a->normal, out->normal );
}
void FreeMesh( mesh_t *m )
{
Mem_Free( m->verts );
Mem_Free( m );
}
void PrintMesh( mesh_t *m )
{
int i, j;
for( i = 0 ; i < m->height; i++ )
{
for( j = 0 ; j < m->width; j++ )
{
Msg("(%5.2f %5.2f %5.2f) "
, m->verts[i*m->width+j].xyz[0]
, m->verts[i*m->width+j].xyz[1]
, m->verts[i*m->width+j].xyz[2] );
}
Msg("\n");
}
}
mesh_t *CopyMesh( mesh_t *mesh )
{
mesh_t *out;
int size;
out = Malloc( sizeof( *out ) );
out->width = mesh->width;
out->height = mesh->height;
size = out->width * out->height * sizeof( *out->verts );
out->verts = Malloc( size );
Mem_Copy( out->verts, mesh->verts, size );
return out;
}
/*
TransposeMesh()
returns a transposed copy of the mesh, freeing the original
*/
mesh_t *TransposeMesh( mesh_t *in )
{
int w, h;
mesh_t *out;
out = Malloc( sizeof( *out ));
out->width = in->height;
out->height = in->width;
out->verts = Malloc( out->width * out->height * sizeof( bspDrawVert_t ));
for ( h = 0; h < in->height; h++ )
{
for( w = 0; w < in->width; w++ )
{
out->verts[w * in->height + h] = in->verts[h * in->width + w];
}
}
FreeMesh( in );
return out;
}
void InvertMesh( mesh_t *in )
{
int w, h;
bspDrawVert_t temp;
for( h = 0; h < in->height; h++ )
{
for( w = 0; w < in->width / 2; w++ )
{
temp = in->verts[h * in->width + w];
in->verts[h * in->width + w] = in->verts[h * in->width + in->width - 1 - w];
in->verts[h * in->width + in->width - 1 - w] = temp;
}
}
}
/*
=================
MakeMeshNormals
=================
*/
void MakeMeshNormals( mesh_t in )
{
int i, j, k, dist;
vec3_t normal;
vec3_t sum;
int count;
vec3_t base;
vec3_t delta;
int x, y;
bspDrawVert_t *dv;
vec3_t around[8], temp;
bool good[8];
bool wrapWidth, wrapHeight;
float len;
int neighbors[8][2] = { {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} };
wrapWidth = false;
for ( i = 0 ; i < in.height ; i++ ) {
VectorSubtract( in.verts[i*in.width].xyz,
in.verts[i*in.width+in.width-1].xyz, delta );
len = VectorLength( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == in.height ) {
wrapWidth = true;
}
wrapHeight = false;
for ( i = 0 ; i < in.width ; i++ ) {
VectorSubtract( in.verts[i].xyz,
in.verts[i + (in.height-1)*in.width].xyz, delta );
len = VectorLength( delta );
if ( len > 1.0 ) {
break;
}
}
if ( i == in.width) {
wrapHeight = true;
}
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 0 ; j < in.height ; j++ ) {
count = 0;
dv = &in.verts[j*in.width+i];
VectorCopy( dv->xyz, base );
for ( k = 0 ; k < 8 ; k++ ) {
VectorClear( around[k] );
good[k] = false;
for ( dist = 1 ; dist <= 3 ; dist++ ) {
x = i + neighbors[k][0] * dist;
y = j + neighbors[k][1] * dist;
if ( wrapWidth ) {
if ( x < 0 ) {
x = in.width - 1 + x;
} else if ( x >= in.width ) {
x = 1 + x - in.width;
}
}
if ( wrapHeight ) {
if ( y < 0 ) {
y = in.height - 1 + y;
} else if ( y >= in.height ) {
y = 1 + y - in.height;
}
}
if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) {
break; // edge of patch
}
VectorSubtract( in.verts[y*in.width+x].xyz, base, temp );
if ( VectorNormalizeLength( temp ) == 0 ) {
continue; // degenerate edge, get more dist
} else {
good[k] = true;
VectorCopy( temp, around[k] );
break; // good edge
}
}
}
VectorClear( sum );
for ( k = 0 ; k < 8 ; k++ ) {
if ( !good[k] || !good[(k+1)&7] ) {
continue; // didn't get two points
}
CrossProduct( around[(k+1)&7], around[k], normal );
if ( VectorNormalizeLength( normal ) == 0 ) {
continue;
}
VectorAdd( normal, sum, sum );
count++;
}
if ( count == 0 ) {
//Msg("bad normal\n");
count = 1;
}
VectorNormalize2( sum, dv->normal );
}
}
}
/*
PutMeshOnCurve()
drops the aproximating points onto the curve
ydnar: fixme: make this use LerpDrawVert() rather than this complicated mess
*/
void PutMeshOnCurve( mesh_t in ) {
int i, j, l, m;
float prev, next;
// put all the aproximating points on the curve
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 1 ; j < in.height ; j += 2 ) {
for ( l = 0 ; l < 3 ; l++ ) {
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j+1)*in.width+i].xyz[l] ) * 0.5;
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j-1)*in.width+i].xyz[l] ) * 0.5;
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
/* ydnar: interpolating st coords */
if( l < 2 )
{
prev = ( in.verts[j*in.width+i].st[l] + in.verts[(j+1)*in.width+i].st[l] ) * 0.5;
next = ( in.verts[j*in.width+i].st[l] + in.verts[(j-1)*in.width+i].st[l] ) * 0.5;
in.verts[j*in.width+i].st[l] = ( prev + next ) * 0.5;
for( m = 0; m < MAX_LIGHTMAPS; m++ )
{
prev = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[(j+1)*in.width+i].lightmap[ m ][l] ) * 0.5;
next = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[(j-1)*in.width+i].lightmap[ m ][l] ) * 0.5;
in.verts[j*in.width+i].lightmap[ m ][l] = ( prev + next ) * 0.5;
}
}
}
}
}
for ( j = 0 ; j < in.height ; j++ ) {
for ( i = 1 ; i < in.width ; i += 2 ) {
for ( l = 0 ; l < 3 ; l++ ) {
prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i+1].xyz[l] ) * 0.5;
next = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i-1].xyz[l] ) * 0.5;
in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
/* ydnar: interpolating st coords */
if( l < 2 )
{
prev = ( in.verts[j*in.width+i].st[l] + in.verts[j*in.width+i+1].st[l] ) * 0.5;
next = ( in.verts[j*in.width+i].st[l] + in.verts[j*in.width+i-1].st[l] ) * 0.5;
in.verts[j*in.width+i].st[l] = ( prev + next ) * 0.5;
for( m = 0; m < MAX_LIGHTMAPS; m++ )
{
prev = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[j*in.width+i+1].lightmap[ m ][l] ) * 0.5;
next = ( in.verts[j*in.width+i].lightmap[ m ][l] + in.verts[j*in.width+i-1].lightmap[ m ][l] ) * 0.5;
in.verts[j*in.width+i].lightmap[ m ][l] = ( prev + next ) * 0.5;
}
}
}
}
}
}
/*
=================
SubdivideMesh
=================
*/
mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength )
{
int i, j, k, l;
bspDrawVert_t prev, next, mid;
vec3_t prevxyz, nextxyz, midxyz;
vec3_t delta;
float len;
mesh_t out;
bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in.width;
out.height = in.height;
for ( i = 0 ; i < in.width ; i++ ) {
for ( j = 0 ; j < in.height ; j++ ) {
expand[j][i] = in.verts[j*in.width+i];
}
}
// horizontal subdivisions
for ( j = 0 ; j + 2 < out.width ; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0 ; i < out.height ; i++ ) {
for ( l = 0 ; l < 3 ; l++ ) {
prevxyz[l] = expand[i][j+1].xyz[l] - expand[i][j].xyz[l];
nextxyz[l] = expand[i][j+2].xyz[l] - expand[i][j+1].xyz[l];
midxyz[l] = (expand[i][j].xyz[l] + expand[i][j+1].xyz[l] * 2
+ expand[i][j+2].xyz[l] ) * 0.25;
}
// if the span length is too long, force a subdivision
if ( VectorLength( prevxyz ) > minLength
|| VectorLength( nextxyz ) > minLength ) {
break;
}
// see if this midpoint is off far enough to subdivide
VectorSubtract( expand[i][j+1].xyz, midxyz, delta );
len = VectorLength( delta );
if ( len > maxError ) {
break;
}
}
if ( out.width + 2 >= MAX_EXPANDED_AXIS ) {
break; // can't subdivide any more
}
if ( i == out.height ) {
continue; // didn't need subdivision
}
// insert two columns and replace the peak
out.width += 2;
for ( i = 0 ; i < out.height ; i++ ) {
LerpDrawVert( &expand[i][j], &expand[i][j+1], &prev );
LerpDrawVert( &expand[i][j+1], &expand[i][j+2], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
expand[i][k] = expand[i][k-2];
}
expand[i][j + 1] = prev;
expand[i][j + 2] = mid;
expand[i][j + 3] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// vertical subdivisions
for ( j = 0 ; j + 2 < out.height ; j += 2 ) {
// check subdivided midpoints against control points
for ( i = 0 ; i < out.width ; i++ ) {
for ( l = 0 ; l < 3 ; l++ ) {
prevxyz[l] = expand[j+1][i].xyz[l] - expand[j][i].xyz[l];
nextxyz[l] = expand[j+2][i].xyz[l] - expand[j+1][i].xyz[l];
midxyz[l] = (expand[j][i].xyz[l] + expand[j+1][i].xyz[l] * 2
+ expand[j+2][i].xyz[l] ) * 0.25;
}
// if the span length is too long, force a subdivision
if ( VectorLength( prevxyz ) > minLength
|| VectorLength( nextxyz ) > minLength ) {
break;
}
// see if this midpoint is off far enough to subdivide
VectorSubtract( expand[j+1][i].xyz, midxyz, delta );
len = VectorLength( delta );
if ( len > maxError ) {
break;
}
}
if ( out.height + 2 >= MAX_EXPANDED_AXIS ) {
break; // can't subdivide any more
}
if ( i == out.width ) {
continue; // didn't need subdivision
}
// insert two columns and replace the peak
out.height += 2;
for ( i = 0 ; i < out.width ; i++ ) {
LerpDrawVert( &expand[j][i], &expand[j+1][i], &prev );
LerpDrawVert( &expand[j+1][i], &expand[j+2][i], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
expand[k][i] = expand[k-2][i];
}
expand[j+1][i] = prev;
expand[j+2][i] = mid;
expand[j+3][i] = next;
}
// back up and recheck this set again, it may need more subdivision
j -= 2;
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) );
}
return CopyMesh(&out);
}
/*
IterationsForCurve() - ydnar
given a curve of a certain length, return the number of subdivision iterations
note: this is affected by subdivision amount
*/
int IterationsForCurve( float len, int subdivisions )
{
int iterations, facets;
/* calculate the number of subdivisions */
for( iterations = 0; iterations < 3; iterations++ )
{
facets = subdivisions * 16 * pow( 2, iterations );
if( facets >= len )
break;
}
/* return to caller */
return iterations;
}
/*
SubdivideMesh2() - ydnar
subdivides each mesh quad a specified number of times
*/
mesh_t *SubdivideMesh2( mesh_t in, int iterations )
{
int i, j, k;
bspDrawVert_t prev, next, mid;
mesh_t out;
bspDrawVert_t expand[ MAX_EXPANDED_AXIS ][ MAX_EXPANDED_AXIS ];
/* initial setup */
out.width = in.width;
out.height = in.height;
for( i = 0; i < in.width; i++ )
{
for( j = 0; j < in.height; j++ )
expand[ j ][ i ] = in.verts[ j * in.width + i ];
}
/* keep chopping */
for( ; iterations > 0; iterations-- )
{
/* horizontal subdivisions */
for( j = 0; j + 2 < out.width; j += 4 )
{
/* check size limit */
if( out.width + 2 >= MAX_EXPANDED_AXIS )
break;
/* insert two columns and replace the peak */
out.width += 2;
for( i = 0; i < out.height; i++ )
{
LerpDrawVert( &expand[ i ][ j ], &expand[ i ][ j + 1 ], &prev );
LerpDrawVert( &expand[ i ][ j + 1 ], &expand[ i ][ j + 2 ], &next );
LerpDrawVert( &prev, &next, &mid );
for ( k = out.width - 1 ; k > j + 3; k-- )
expand [ i ][ k ] = expand[ i ][ k - 2 ];
expand[ i ][ j + 1 ] = prev;
expand[ i ][ j + 2 ] = mid;
expand[ i ][ j + 3 ] = next;
}
}
/* vertical subdivisions */
for ( j = 0; j + 2 < out.height; j += 4 )
{
/* check size limit */
if( out.height + 2 >= MAX_EXPANDED_AXIS )
break;
/* insert two columns and replace the peak */
out.height += 2;
for( i = 0; i < out.width; i++ )
{
LerpDrawVert( &expand[ j ][ i ], &expand[ j + 1 ][ i ], &prev );
LerpDrawVert( &expand[ j + 1 ][ i ], &expand[ j + 2 ][ i ], &next );
LerpDrawVert( &prev, &next, &mid );
for( k = out.height - 1; k > j + 3; k-- )
expand[ k ][ i ] = expand[ k - 2 ][ i ];
expand[ j + 1 ][ i ] = prev;
expand[ j + 2 ][ i ] = mid;
expand[ j + 3 ][ i ] = next;
}
}
}
/* collapse the verts */
out.verts = &expand[ 0 ][ 0 ];
for( i = 1; i < out.height; i++ )
memmove( &out.verts[ i * out.width ], expand[ i ], out.width * sizeof( bspDrawVert_t ) );
/* return to sender */
return CopyMesh( &out );
}
/*
================
ProjectPointOntoVector
================
*/
void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
{
vec3_t pVec, vec;
VectorSubtract( point, vStart, pVec );
VectorSubtract( vEnd, vStart, vec );
VectorNormalize( vec );
// project onto the directional vector for this segment
VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
}
/*
================
RemoveLinearMeshColumsRows
================
*/
mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in )
{
int i, j, k;
float len, maxLength;
vec3_t proj, dir;
mesh_t out;
bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in->width;
out.height = in->height;
for ( i = 0 ; i < in->width ; i++ ) {
for ( j = 0 ; j < in->height ; j++ ) {
expand[j][i] = in->verts[j*in->width+i];
}
}
for ( j = 1 ; j < out.width - 1; j++ ) {
maxLength = 0;
for ( i = 0 ; i < out.height ; i++ ) {
ProjectPointOntoVector(expand[i][j].xyz, expand[i][j-1].xyz, expand[i][j+1].xyz, proj);
VectorSubtract(expand[i][j].xyz, proj, dir);
len = VectorLength(dir);
if (len > maxLength) {
maxLength = len;
}
}
if (maxLength < 0.1)
{
out.width--;
for ( i = 0 ; i < out.height ; i++ ) {
for (k = j; k < out.width; k++) {
expand[i][k] = expand[i][k+1];
}
}
j--;
}
}
for ( j = 1 ; j < out.height - 1; j++ ) {
maxLength = 0;
for ( i = 0 ; i < out.width ; i++ ) {
ProjectPointOntoVector(expand[j][i].xyz, expand[j-1][i].xyz, expand[j+1][i].xyz, proj);
VectorSubtract(expand[j][i].xyz, proj, dir);
len = VectorLength(dir);
if (len > maxLength) {
maxLength = len;
}
}
if (maxLength < 0.1)
{
out.height--;
for ( i = 0 ; i < out.width ; i++ ) {
for (k = j; k < out.height; k++) {
expand[k][i] = expand[k+1][i];
}
}
j--;
}
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ )
{
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) );
}
return CopyMesh(&out);
}
/*
=================
SubdivideMeshQuads
=================
*/
mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int *widthtable, int *heighttable )
{
int i, j, k, w, h, maxsubdivisions, subdivisions;
vec3_t dir;
float length, maxLength, amount;
mesh_t out;
bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
out.width = in->width;
out.height = in->height;
for ( i = 0 ; i < in->width ; i++ ) {
for ( j = 0 ; j < in->height ; j++ ) {
expand[j][i] = in->verts[j*in->width+i];
}
}
if( maxsize > MAX_EXPANDED_AXIS )
Sys_Error( "SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS\n" );
// horizontal subdivisions
maxsubdivisions = (maxsize - in->width) / (in->width - 1);
for ( w = 0, j = 0 ; w < in->width - 1; w++, j += subdivisions + 1) {
maxLength = 0;
for ( i = 0 ; i < out.height ; i++ ) {
VectorSubtract(expand[i][j+1].xyz, expand[i][j].xyz, dir);
length = VectorLength( dir );
if (length > maxLength) {
maxLength = length;
}
}
subdivisions = (int) (maxLength / minLength);
if (subdivisions > maxsubdivisions)
subdivisions = maxsubdivisions;
widthtable[w] = subdivisions + 1;
if (subdivisions <= 0)
continue;
out.width += subdivisions;
for ( i = 0 ; i < out.height ; i++ ) {
for ( k = out.width - 1 ; k > j + subdivisions; k-- ) {
expand[i][k] = expand[i][k-subdivisions];
}
for (k = 1; k <= subdivisions; k++)
{
amount = (float) k / (subdivisions + 1);
LerpDrawVertAmount(&expand[i][j], &expand[i][j+subdivisions+1], amount, &expand[i][j+k]);
}
}
}
maxsubdivisions = (maxsize - in->height) / (in->height - 1);
for ( h = 0, j = 0 ; h < in->height - 1; h++, j += subdivisions + 1) {
maxLength = 0;
for ( i = 0 ; i < out.width ; i++ ) {
VectorSubtract(expand[j+1][i].xyz, expand[j][i].xyz, dir);
length = VectorLength( dir );
if (length > maxLength) {
maxLength = length;
}
}
subdivisions = (int) (maxLength / minLength);
if (subdivisions > maxsubdivisions)
subdivisions = maxsubdivisions;
heighttable[h] = subdivisions + 1;
if (subdivisions <= 0)
continue;
out.height += subdivisions;
for ( i = 0 ; i < out.width ; i++ ) {
for ( k = out.height - 1 ; k > j + subdivisions; k-- ) {
expand[k][i] = expand[k-subdivisions][i];
}
for (k = 1; k <= subdivisions; k++)
{
amount = (float) k / (subdivisions + 1);
LerpDrawVertAmount(&expand[j][i], &expand[j+subdivisions+1][i], amount, &expand[j+k][i]);
}
}
}
// collapse the verts
out.verts = &expand[0][0];
for ( i = 1 ; i < out.height ; i++ ) {
memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) );
}
return CopyMesh(&out);
}

671
xtools/bsplib/model.c Normal file
View File

@ -0,0 +1,671 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define MODEL_C
/* dependencies */
#include "q3map2.h"
#include "matrix_lib.h"
#include "stdio.h" // sscanf
/*
PicoPrintFunc()
callback for picomodel.lib
*/
void PicoPrintFunc( int level, const char *str )
{
if( str == NULL ) return;
switch( level )
{
case PICO_NORMAL:
Msg( "%s\n", str );
break;
case PICO_VERBOSE:
MsgDev( D_INFO, "%s\n", str );
break;
case PICO_WARNING:
MsgDev( D_WARN, "%s\n", str );
break;
case PICO_ERROR:
MsgDev( D_ERROR, "%s\n", str );
break;
case PICO_FATAL:
Sys_Break( "%s\n", str );
break;
}
}
/*
PicoLoadFileFunc()
callback for picomodel.lib
*/
void PicoLoadFileFunc( char *name, byte **buffer, int *bufSize )
{
*buffer = (void *)FS_LoadFile( name, bufSize );
}
/*
FindModel() - ydnar
finds an existing picoModel and returns a pointer to the picoModel_t struct or NULL if not found
*/
picoModel_t *FindModel( char *name, int frame )
{
int i;
if( numPicoModels <= 0 )
Mem_Set( picoModels, 0, sizeof( picoModels ));
if( name == NULL || name[ 0 ] == '\0' )
return NULL;
for( i = 0; i < MAX_MODELS; i++ )
{
if( picoModels[i] != NULL && !com.strcmp( PicoGetModelName( picoModels[i] ), name ) && PicoGetModelFrameNum( picoModels[ i ] ) == frame )
return picoModels[i];
}
/* no matching picoModel found */
return NULL;
}
/*
LoadModel() - ydnar
loads a picoModel and returns a pointer to the picoModel_t struct or NULL if not found
*/
picoModel_t *LoadModel( char *name, int frame )
{
int i;
picoModel_t *model, **pm;
if( numPicoModels <= 0 )
memset( picoModels, 0, sizeof( picoModels ) );
if( name == NULL || name[ 0 ] == '\0' )
return NULL;
// try to find existing picoModel
model = FindModel( name, frame );
if( model != NULL ) return model;
// none found, so find first non-null picoModel
pm = NULL;
for( i = 0; i < MAX_MODELS; i++ )
{
if( picoModels[ i ] == NULL )
{
pm = &picoModels[ i ];
break;
}
}
// too many picoModels?
if( pm == NULL ) Sys_Break( "MAX_MODELS (%d) exceeded, there are too many models used", MAX_MODELS );
*pm = PicoLoadModel( (char*) name, frame );
// if loading failed, make a bogus model to silence the rest of the warnings
if( *pm == NULL )
{
// allocate a new model
*pm = PicoNewModel();
if( *pm == NULL ) return NULL;
PicoSetModelName( *pm, name );
PicoSetModelFrameNum( *pm, frame );
}
// debug code
#if 0
{
int numSurfaces, numVertexes;
picoSurface_t *ps;
Msg( "Model %s\n", name );
numSurfaces = PicoGetModelNumSurfaces( *pm );
for( i = 0; i < numSurfaces; i++ )
{
ps = PicoGetModelSurface( *pm, i );
numVertexes = PicoGetSurfaceNumVertexes( ps );
Msg( "Surface %d has %d vertexes\n", i, numVertexes );
}
}
#endif
if( *pm != NULL ) numPicoModels++;
return *pm;
}
/*
InsertModel() - ydnar
adds a picomodel into the bsp
*/
void InsertModel( char *name, int frame, matrix4x4 transform, remap_t *remap, shaderInfo_t *celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale )
{
int i, j, k, s, numSurfaces;
matrix4x4 identity, temp1, nTransform;
picoModel_t *model;
picoShader_t *shader;
picoSurface_t *surface;
shaderInfo_t *si;
mapDrawSurface_t *ds;
bspDrawVert_t *dv;
char *picoShaderName;
char shaderName[ MAX_QPATH ];
picoVec_t *xyz, *normal, *st;
byte *color;
picoIndex_t *indexes;
remap_t *rm, *glob;
/* get model */
model = LoadModel( name, frame );
if( model == NULL )
return;
/* handle null matrix */
if( transform == NULL )
{
Matrix4x4_LoadIdentity( identity );
transform = identity;
}
/* hack: Stable-1_2 and trunk have differing row/column major matrix order
this transpose is necessary with Stable-1_2
uncomment the following line with old matrix4x4 (non 1.3/spog_branch) code */
//% Matrix4x4_Transpose( transform, transform );
/* create transform matrix for normals */
Matrix4x4_Copy( nTransform, transform );
if( !Matrix4x4_Invert_Full( nTransform, nTransform ))
MsgDev( D_WARN, "Can't invert model transform matrix, using transpose instead\n" );
Matrix4x4_Copy( temp1, nTransform );
Matrix4x4_Transpose( nTransform, temp1 );
/* fix bogus lightmap scale */
if( lightmapScale <= 0.0f )
lightmapScale = 1.0f;
/* each surface on the model will become a new map drawsurface */
numSurfaces = PicoGetModelNumSurfaces( model );
//% MsgDev( D_NOTE, "Model %s has %d surfaces\n", name, numSurfaces );
for( s = 0; s < numSurfaces; s++ )
{
/* get surface */
surface = PicoGetModelSurface( model, s );
if( surface == NULL )
continue;
/* only handle triangle surfaces initially (fixme: support patches) */
if( PicoGetSurfaceType( surface ) != PICO_TRIANGLES )
continue;
/* fix the surface's normals */
PicoFixSurfaceNormals( surface );
/* allocate a surface (ydnar: gs mods) */
ds = AllocDrawSurface( SURFACE_TRIANGLES );
ds->entityNum = eNum;
ds->castShadows = castShadows;
ds->recvShadows = recvShadows;
/* get shader name */
shader = PicoGetSurfaceShader( surface );
if( shader == NULL ) picoShaderName = "";
else picoShaderName = PicoGetShaderName( shader );
/* handle shader remapping */
glob = NULL;
for( rm = remap; rm != NULL; rm = rm->next )
{
if( rm->from[ 0 ] == '*' && rm->from[ 1 ] == '\0' )
glob = rm;
else if( !com.stricmp( picoShaderName, rm->from ))
{
MsgDev( D_NOTE, "Remapping %s to %s\n", picoShaderName, rm->to );
picoShaderName = rm->to;
glob = NULL;
break;
}
}
if( glob != NULL )
{
MsgDev( D_NOTE, "Globbing %s to %s\n", picoShaderName, glob->to );
picoShaderName = glob->to;
}
/* shader renaming for sof2 */
if( renameModelShaders )
{
strcpy( shaderName, picoShaderName );
FS_StripExtension( shaderName );
if( spawnFlags & 1 ) strcat( shaderName, "_RMG_BSP" );
else strcat( shaderName, "_BSP" );
si = ShaderInfoForShader( shaderName );
}
else si = ShaderInfoForShader( picoShaderName );
/* set shader */
ds->shaderInfo = si;
/* set lightmap scale */
ds->lightmapScale = lightmapScale;
/* force to meta? */
if( (si != NULL && si->forceMeta) || (spawnFlags & 4) ) /* 3rd bit */
ds->type = SURFACE_FORCED_META;
/* set particulars */
ds->numVerts = PicoGetSurfaceNumVertexes( surface );
ds->verts = Malloc( ds->numVerts * sizeof( ds->verts[ 0 ] ) );
ds->numIndexes = PicoGetSurfaceNumIndexes( surface );
ds->indexes = Malloc( ds->numIndexes * sizeof( ds->indexes[ 0 ] ) );
/* copy vertexes */
for( i = 0; i < ds->numVerts; i++ )
{
/* get vertex */
dv = &ds->verts[ i ];
/* xyz and normal */
xyz = PicoGetSurfaceXYZ( surface, i );
VectorCopy( xyz, dv->xyz );
Matrix4x4_TransformPoint( transform, dv->xyz );
normal = PicoGetSurfaceNormal( surface, i );
VectorCopy( normal, dv->normal );
Matrix4x4_TransformNormal( nTransform, dv->normal );
VectorNormalize( dv->normal );
/* ydnar: tek-fu celshading support for flat shaded shit */
if( flat )
{
dv->st[ 0 ] = si->stFlat[ 0 ];
dv->st[ 1 ] = si->stFlat[ 1 ];
}
/* ydnar: gs mods: added support for explicit shader texcoord generation */
else if( si->tcGen )
{
/* project the texture */
dv->st[ 0 ] = DotProduct( si->vecs[ 0 ], dv->xyz );
dv->st[ 1 ] = DotProduct( si->vecs[ 1 ], dv->xyz );
}
/* normal texture coordinates */
else
{
st = PicoGetSurfaceST( surface, 0, i );
dv->st[ 0 ] = st[ 0 ];
dv->st[ 1 ] = st[ 1 ];
}
/* set lightmap/color bits */
color = PicoGetSurfaceColor( surface, 0, i );
for( j = 0; j < MAX_LIGHTMAPS; j++ )
{
dv->lightmap[ j ][ 0 ] = 0.0f;
dv->lightmap[ j ][ 1 ] = 0.0f;
dv->color[ j ][ 0 ] = color[ 0 ];
dv->color[ j ][ 1 ] = color[ 1 ];
dv->color[ j ][ 2 ] = color[ 2 ];
dv->color[ j ][ 3 ] = color[ 3 ];
}
}
/* copy indexes */
indexes = PicoGetSurfaceIndexes( surface, 0 );
for( i = 0; i < ds->numIndexes; i++ )
ds->indexes[ i ] = indexes[ i ];
/* set cel shader */
ds->celShader = celShader;
/* ydnar: giant hack land: generate clipping brushes for model triangles */
if( si->clipModel || (spawnFlags & 2) ) /* 2nd bit */
{
vec3_t points[ 3 ], backs[ 3 ];
vec4_t plane, reverse, pa, pb, pc;
vec3_t nadir;
/* temp hack */
if( !si->clipModel && ((si->compileFlags & C_TRANSLUCENT) || !(si->compileFlags & C_SOLID)) )
continue;
/* overflow check */
if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )
continue;
/* walk triangle list */
for( i = 0; i < ds->numIndexes; i += 3 )
{
/* overflow hack */
if( (nummapplanes + 64) >= (MAX_MAP_PLANES >> 1) )
{
Msg( "WARNING: MAX_MAP_PLANES (%d) hit generating clip brushes for model %s.\n",
MAX_MAP_PLANES, name );
break;
}
/* make points and back points */
for( j = 0; j < 3; j++ )
{
/* get vertex */
dv = &ds->verts[ ds->indexes[ i + j ] ];
/* copy xyz */
VectorCopy( dv->xyz, points[ j ] );
VectorCopy( dv->xyz, backs[ j ] );
/* find nearest axial to normal and push back points opposite */
/* note: this doesn't work as well as simply using the plane of the triangle, below */
for( k = 0; k < 3; k++ )
{
if( fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 1) % 3 ] ) &&
fabs( dv->normal[ k ] ) > fabs( dv->normal[ (k + 2) % 3 ] ) )
{
backs[ j ][ k ] += dv->normal[ k ] < 0.0f ? 64.0f : -64.0f;
break;
}
}
}
/* make plane for triangle */
if( PlaneFromPoints( plane, points[ 0 ], points[ 1 ], points[ 2 ] ) )
{
/* regenerate back points */
for( j = 0; j < 3; j++ )
{
/* get vertex */
dv = &ds->verts[ ds->indexes[ i + j ] ];
/* copy xyz */
VectorCopy( dv->xyz, backs[ j ] );
/* find nearest axial to plane normal and push back points opposite */
for( k = 0; k < 3; k++ )
{
if( fabs( plane[ k ] ) > fabs( plane[ (k + 1) % 3 ] ) &&
fabs( plane[ k ] ) > fabs( plane[ (k + 2) % 3 ] ) )
{
backs[ j ][ k ] += plane[ k ] < 0.0f ? 64.0f : -64.0f;
break;
}
}
}
/* make back plane */
VectorScale( plane, -1.0f, reverse );
reverse[ 3 ] = -(plane[ 3 ] - 1);
/* make back pyramid point */
VectorCopy( points[ 0 ], nadir );
VectorAdd( nadir, points[ 1 ], nadir );
VectorAdd( nadir, points[ 2 ], nadir );
VectorScale( nadir, 0.3333333333333f, nadir );
VectorMA( nadir, -2.0f, plane, nadir );
/* make 3 more planes */
//% if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], nadir ) &&
//% PlaneFromPoints( pb, points[ 1 ], points[ 0 ], nadir ) &&
//% PlaneFromPoints( pc, points[ 0 ], points[ 2 ], nadir ) )
if( PlaneFromPoints( pa, points[ 2 ], points[ 1 ], backs[ 1 ] ) &&
PlaneFromPoints( pb, points[ 1 ], points[ 0 ], backs[ 0 ] ) &&
PlaneFromPoints( pc, points[ 0 ], points[ 2 ], backs[ 2 ] ) )
{
/* build a brush */
buildBrush = AllocBrush( 48 );
buildBrush->entityNum = mapEntityNum;
buildBrush->original = buildBrush;
buildBrush->contentShader = si;
buildBrush->compileFlags = si->compileFlags;
buildBrush->contentFlags = si->contentFlags;
buildBrush->detail = true;
/* set up brush sides */
buildBrush->numsides = 5;
for( j = 0; j < buildBrush->numsides; j++ )
buildBrush->sides[ j ].shaderInfo = si;
buildBrush->sides[ 0 ].planenum = FindFloatPlane( plane, plane[ 3 ], 3, points );
buildBrush->sides[ 1 ].planenum = FindFloatPlane( pa, pa[ 3 ], 1, &points[ 2 ] );
buildBrush->sides[ 2 ].planenum = FindFloatPlane( pb, pb[ 3 ], 1, &points[ 1 ] );
buildBrush->sides[ 3 ].planenum = FindFloatPlane( pc, pc[ 3 ], 1, &points[ 0 ] );
buildBrush->sides[ 4 ].planenum = FindFloatPlane( reverse, reverse[ 3 ], 3, points );
/* add to entity */
if( CreateBrushWindings( buildBrush ) )
{
AddBrushBevels();
//% EmitBrushes( buildBrush, NULL, NULL );
buildBrush->next = entities[ mapEntityNum ].brushes;
entities[ mapEntityNum ].brushes = buildBrush;
entities[ mapEntityNum ].numBrushes++;
}
else Mem_Free( buildBrush );
}
}
}
}
}
}
/*
AddTriangleModels()
adds misc_model surfaces to the bsp
*/
void AddTriangleModels( entity_t *e )
{
int num, frame, castShadows, recvShadows, spawnFlags;
entity_t *e2;
const char *targetName;
const char *target, *model, *value;
char shader[ MAX_QPATH ];
shaderInfo_t *celShader;
float temp, baseLightmapScale, lightmapScale;
vec3_t origin, scale, angles;
matrix4x4 transform;
epair_t *ep;
remap_t *remap, *remap2;
char *split;
/* note it */
MsgDev( D_NOTE, "--- AddTriangleModels ---\n" );
/* get current brush entity targetname */
if( e == entities )
targetName = "";
else
{
targetName = ValueForKey( e, "targetname" );
/* misc_model entities target non-worldspawn brush model entities */
if( targetName[ 0 ] == '\0' )
return;
}
/* get lightmap scale */
baseLightmapScale = FloatForKey( e, "_lightmapscale" );
if( baseLightmapScale <= 0.0f )
baseLightmapScale = 0.0f;
/* walk the entity list */
for( num = 1; num < numEntities; num++ )
{
/* get e2 */
e2 = &entities[ num ];
/* convert misc_models into raw geometry */
if( com.stricmp( "misc_model", ValueForKey( e2, "classname" ) ) )
continue;
/* ydnar: added support for md3 models on non-worldspawn models */
target = ValueForKey( e2, "target" );
if( strcmp( target, targetName ) )
continue;
/* get model name */
model = ValueForKey( e2, "model" );
if( model[ 0 ] == '\0' )
{
MsgDev( D_WARN, "misc_model at %i %i %i without a model key\n", (int) origin[ 0 ], (int) origin[ 1 ], (int) origin[ 2 ] );
continue;
}
/* get model frame */
frame = IntForKey( e2, "_frame" );
/* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
if( e == entities )
{
castShadows = WORLDSPAWN_CAST_SHADOWS;
recvShadows = WORLDSPAWN_RECV_SHADOWS;
}
/* other entities don't cast any shadows, but recv worldspawn shadows */
else
{
castShadows = ENTITY_CAST_SHADOWS;
recvShadows = ENTITY_RECV_SHADOWS;
}
/* get explicit shadow flags */
GetEntityShadowFlags( e2, e, &castShadows, &recvShadows );
/* get spawnflags */
spawnFlags = IntForKey( e2, "spawnflags" );
/* get origin */
GetVectorForKey( e2, "origin", origin );
VectorSubtract( origin, e->origin, origin ); /* offset by parent */
/* get scale */
scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = 1.0f;
temp = FloatForKey( e2, "modelscale" );
if( temp != 0.0f )
scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;
value = ValueForKey( e2, "modelscale_vec" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
/* get "angle" (yaw) or "angles" (pitch yaw roll) */
angles[ 0 ] = angles[ 1 ] = angles[ 2 ] = 0.0f;
angles[ 2 ] = FloatForKey( e2, "angle" );
value = ValueForKey( e2, "angles" );
if( value[ 0 ] != '\0' )
sscanf( value, "%f %f %f", &angles[ 1 ], &angles[ 2 ], &angles[ 0 ] );
/* set transform matrix (thanks spog) */
Matrix4x4_LoadIdentity( transform );
Matrix4x4_Pivot( transform, origin, angles, eXYZ, scale, vec3_origin );
/* get shader remappings */
remap = NULL;
for( ep = e2->epairs; ep != NULL; ep = ep->next )
{
/* look for keys prefixed with "_remap" */
if( ep->key != NULL && ep->value != NULL && ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' && !com.strnicmp( ep->key, "_remap", 6 ) )
{
/* create new remapping */
remap2 = remap;
remap = Malloc( sizeof( *remap ) );
remap->next = remap2;
strcpy( remap->from, ep->value );
/* split the string */
split = strchr( remap->from, ';' );
if( split == NULL )
{
MsgDev( D_WARN, "Shader _remap key found in misc_model without a ; character\n" );
Mem_Free( remap );
remap = remap2;
continue;
}
/* store the split */
*split = '\0';
strcpy( remap->to, (split + 1) );
/* note it */
//% MsgDev( D_NOTE, "Remapping %s to %s\n", remap->from, remap->to );
}
}
/* ydnar: cel shader support */
value = ValueForKey( e2, "_celshader" );
if( value[ 0 ] == '\0' )
value = ValueForKey( &entities[ 0 ], "_celshader" );
if( value[ 0 ] != '\0' )
{
sprintf( shader, "textures/%s", value );
celShader = ShaderInfoForShader( shader );
}
else
celShader = NULL;
/* get lightmap scale */
lightmapScale = FloatForKey( e2, "_lightmapscale" );
if( lightmapScale <= 0.0f )
lightmapScale = baseLightmapScale;
/* insert the model */
InsertModel( (char*) model, frame, transform, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale );
/* free shader remappings */
while( remap != NULL )
{
remap2 = remap->next;
Mem_Free( remap );
remap = remap2;
}
}
}

518
xtools/bsplib/patch.c Normal file
View File

@ -0,0 +1,518 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PATCH_C
/* dependencies */
#include "q3map2.h"
/*
ExpandLongestCurve() - ydnar
finds length of quadratic curve specified and determines if length is longer than the supplied max
*/
#define APPROX_SUBDIVISION 8
static void ExpandLongestCurve( float *longestCurve, vec3_t a, vec3_t b, vec3_t c )
{
int i;
float t, len;
vec3_t ab, bc, ac, pt, last, delta;
/* calc vectors */
VectorSubtract( b, a, ab );
if( VectorNormalizeLength( ab ) < 0.125f )
return;
VectorSubtract( c, b, bc );
if( VectorNormalizeLength( bc ) < 0.125f )
return;
VectorSubtract( c, a, ac );
if( VectorNormalizeLength( ac ) < 0.125f )
return;
/* if all 3 vectors are the same direction, then this edge is linear, so we ignore it */
if( DotProduct( ab, bc ) > 0.99f && DotProduct( ab, ac ) > 0.99f )
return;
/* recalculate vectors */
VectorSubtract( b, a, ab );
VectorSubtract( c, b, bc );
/* determine length */
VectorCopy( a, last );
for( i = 0, len = 0.0f, t = 0.0f; i < APPROX_SUBDIVISION; i++, t += (1.0f / APPROX_SUBDIVISION) )
{
/* calculate delta */
delta[ 0 ] = ((1.0f - t) * ab[ 0 ]) + (t * bc[ 0 ]);
delta[ 1 ] = ((1.0f - t) * ab[ 1 ]) + (t * bc[ 1 ]);
delta[ 2 ] = ((1.0f - t) * ab[ 2 ]) + (t * bc[ 2 ]);
/* add to first point and calculate pt-pt delta */
VectorAdd( a, delta, pt );
VectorSubtract( pt, last, delta );
/* add it to length and store last point */
len += VectorLength( delta );
VectorCopy( pt, last );
}
/* longer? */
if( len > *longestCurve )
*longestCurve = len;
}
/*
ExpandMaxIterations() - ydnar
determines how many iterations a quadratic curve needs to be subdivided with to fit the specified error
*/
static void ExpandMaxIterations( int *maxIterations, int maxError, vec3_t a, vec3_t b, vec3_t c )
{
int i, j;
vec3_t prev, next, mid, delta, delta2;
float len, len2;
int numPoints, iterations;
vec3_t points[ MAX_EXPANDED_AXIS ];
/* initial setup */
numPoints = 3;
VectorCopy( a, points[ 0 ] );
VectorCopy( b, points[ 1 ] );
VectorCopy( c, points[ 2 ] );
/* subdivide */
for( i = 0; i + 2 < numPoints; i += 2 )
{
/* check subdivision limit */
if( numPoints + 2 >= MAX_EXPANDED_AXIS )
break;
/* calculate new curve deltas */
for( j = 0; j < 3; j++ )
{
prev[ j ] = points[ i + 1 ][ j ] - points[ i ][ j ];
next[ j ] = points[ i + 2 ][ j ] - points[ i + 1 ][ j ];
mid[ j ] = (points[ i ][ j ] + points[ i + 1 ][ j ] * 2.0f + points[ i + 2 ][ j ]) * 0.25f;
}
/* see if this midpoint is off far enough to subdivide */
VectorSubtract( points[ i + 1 ], mid, delta );
len = VectorLength( delta );
if( len < maxError )
continue;
/* subdivide */
numPoints += 2;
/* create new points */
for( j = 0; j < 3; j++ )
{
prev[ j ] = 0.5f * (points[ i ][ j ] + points[ i + 1 ][ j ]);
next[ j ] = 0.5f * (points[ i + 1 ][ j ] + points[ i + 2 ][ j ]);
mid[ j ] = 0.5f * (prev[ j ] + next[ j ]);
}
/* push points out */
for( j = numPoints - 1; j > i + 3; j-- )
VectorCopy( points[ j - 2 ], points[ j ] );
/* insert new points */
VectorCopy( prev, points[ i + 1 ] );
VectorCopy( mid, points[ i + 2 ] );
VectorCopy( next, points[ i + 3 ] );
/* back up and recheck this set again, it may need more subdivision */
i -= 2;
}
/* put the line on the curve */
for( i = 1; i < numPoints; i += 2 )
{
for( j = 0; j < 3; j++ )
{
prev[ j ] = 0.5f * (points[ i ][ j ] + points[ i + 1 ][ j ] );
next[ j ] = 0.5f * (points[ i ][ j ] + points[ i - 1 ][ j ] );
points[ i ][ j ] = 0.5f * (prev[ j ] + next[ j ]);
}
}
/* eliminate linear sections */
for( i = 0; i + 2 < numPoints; i++ )
{
/* create vectors */
VectorSubtract( points[ i + 1 ], points[ i ], delta );
len = VectorNormalizeLength( delta );
VectorSubtract( points[ i + 2 ], points[ i + 1 ], delta2 );
len2 = VectorNormalizeLength( delta2 );
/* if either edge is degenerate, then eliminate it */
if( len < 0.0625f || len2 < 0.0625f || DotProduct( delta, delta2 ) >= 1.0f )
{
for( j = i + 1; j + 1 < numPoints; j++ )
VectorCopy( points[ j + 1 ], points[ j ] );
numPoints--;
continue;
}
}
/* the number of iterations is 2^(points - 1) - 1 */
numPoints >>= 1;
iterations = 0;
while( numPoints > 1 )
{
numPoints >>= 1;
iterations++;
}
/* more? */
if( iterations > *maxIterations )
*maxIterations = iterations;
}
/*
ParsePatch()
creates a mapDrawSurface_t from the patch text
*/
void ParsePatch( bool onlyLights )
{
vec_t info[ 5 ];
int i, j, k;
parseMesh_t *pm;
char texture[ MAX_QPATH ];
char shader[ MAX_QPATH ];
mesh_t m;
bspDrawVert_t *verts;
epair_t *ep;
vec4_t delta, delta2, delta3;
bool degenerate;
float longestCurve;
int maxIterations;
MatchToken( "{" );
/* get texture */
GetToken( true );
strcpy( texture, token );
Parse1DMatrix( 5, info );
m.width = info[0];
m.height = info[1];
m.verts = verts = Malloc( m.width * m.height * sizeof( m.verts[0] ) );
if( m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE )
Sys_Break( "ParsePatch: bad size\n" );
MatchToken( "(" );
for( j = 0; j < m.width ; j++ )
{
MatchToken( "(" );
for( i = 0; i < m.height ; i++ )
{
Parse1DMatrix( 5, verts[ i * m.width + j ].xyz );
/* ydnar: fix colors */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
verts[ i * m.width + j ].color[ k ][ 0 ] = 255;
verts[ i * m.width + j ].color[ k ][ 1 ] = 255;
verts[ i * m.width + j ].color[ k ][ 2 ] = 255;
verts[ i * m.width + j ].color[ k ][ 3 ] = 255;
}
}
MatchToken( ")" );
}
MatchToken( ")" );
// if brush primitives format, we may have some epairs to ignore here
GetToken(true);
if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && com.strcmp(token,"}"))
{
// NOTE: we leak that!
ep = ParseEPair();
}
else UnGetToken();
MatchToken( "}" );
MatchToken( "}" );
/* short circuit */
if( noCurveBrushes || onlyLights )
return;
/* ydnar: delete and warn about degenerate patches */
j = (m.width * m.height);
VectorClear( delta );
delta[ 3 ] = 0;
degenerate = true;
/* find first valid vector */
for( i = 1; i < j && delta[ 3 ] == 0; i++ )
{
VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta );
delta[ 3 ] = VectorNormalizeLength( delta );
}
/* secondary degenerate test */
if( delta[ 3 ] == 0 )
degenerate = true;
else
{
/* if all vectors match this or are zero, then this is a degenerate patch */
for( i = 1; i < j && degenerate == true; i++ )
{
VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta2 );
delta2[ 3 ] = VectorNormalizeLength( delta2 );
if( delta2[ 3 ] != 0 )
{
/* create inverse vector */
VectorCopy( delta2, delta3 );
delta3[ 3 ] = delta2[ 3 ];
VectorNegate( delta3, delta3 );
/* compare */
if( VectorCompare( delta, delta2 ) == false && VectorCompare( delta, delta3 ) == false )
degenerate = false;
}
}
}
/* warn and select degenerate patch */
if( degenerate )
{
MsgDev( D_ERROR, "Entity %i, Brush %i degenerate patch\n", mapEnt->mapEntityNum, entitySourceBrushes );
Mem_Free( m.verts );
return;
}
/* find longest curve on the mesh */
longestCurve = 0.0f;
maxIterations = 0;
for( j = 0; j + 2 < m.width; j += 2 )
{
for( i = 0; i + 2 < m.height; i += 2 )
{
ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */
ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */
ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */
ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */
}
}
/* allocate patch mesh */
pm = Malloc( sizeof( *pm ) );
/* ydnar: add entity/brush numbering */
pm->entityNum = mapEnt->mapEntityNum;
pm->brushNum = entitySourceBrushes;
/* set shader */
com.sprintf( shader, "textures/%s", texture );
pm->shaderInfo = ShaderInfoForShader( shader );
/* set mesh */
pm->mesh = m;
/* set longest curve */
pm->longestCurve = longestCurve;
pm->maxIterations = maxIterations;
/* link to the entity */
pm->next = mapEnt->patches;
mapEnt->patches = pm;
}
/*
GrowGroup_r()
recursively adds patches to a lod group
*/
static void GrowGroup_r( parseMesh_t *pm, int patchNum, int patchCount, parseMesh_t **meshes, byte *bordering, byte *group )
{
int i;
const byte *row;
if( group[ patchNum ] )
return;
group[ patchNum ] = 1;
row = bordering + patchNum * patchCount;
/* check maximums */
if( meshes[ patchNum ]->longestCurve > pm->longestCurve )
pm->longestCurve = meshes[ patchNum ]->longestCurve;
if( meshes[ patchNum ]->maxIterations > pm->maxIterations )
pm->maxIterations = meshes[ patchNum ]->maxIterations;
/* walk other patches */
for( i = 0; i < patchCount; i++ )
{
if( row[i] ) GrowGroup_r( pm, i, patchCount, meshes, bordering, group );
}
}
/*
PatchMapDrawSurfs()
any patches that share an edge need to choose their
level of detail as a unit, otherwise the edges would
pull apart.
*/
void PatchMapDrawSurfs( entity_t *e )
{
int i, j, k, l, c1, c2;
parseMesh_t *pm;
parseMesh_t *check, *scan;
mapDrawSurface_t *ds;
int patchCount, groupCount;
bspDrawVert_t *v1, *v2;
vec3_t bounds[2];
byte *bordering;
/* ydnar: mac os x fails with these if not static */
parseMesh_t *meshes[ MAX_MAP_DRAW_SURFS ];
qb_t grouped[ MAX_MAP_DRAW_SURFS ];
byte group[ MAX_MAP_DRAW_SURFS ];
/* note it */
MsgDev( D_NOTE, "--- PatchMapDrawSurfs ---\n" );
patchCount = 0;
for ( pm = e->patches ; pm ; pm = pm->next ) {
meshes[patchCount] = pm;
patchCount++;
}
if ( !patchCount ) {
return;
}
bordering = Malloc( patchCount * patchCount );
memset( bordering, 0, patchCount * patchCount );
// build the bordering matrix
for ( k = 0 ; k < patchCount ; k++ ) {
bordering[k*patchCount+k] = 1;
for ( l = k+1 ; l < patchCount ; l++ ) {
check = meshes[k];
scan = meshes[l];
c1 = scan->mesh.width * scan->mesh.height;
v1 = scan->mesh.verts;
for ( i = 0 ; i < c1 ; i++, v1++ ) {
c2 = check->mesh.width * check->mesh.height;
v2 = check->mesh.verts;
for ( j = 0 ; j < c2 ; j++, v2++ ) {
if ( fabs( v1->xyz[0] - v2->xyz[0] ) < 1.0
&& fabs( v1->xyz[1] - v2->xyz[1] ) < 1.0
&& fabs( v1->xyz[2] - v2->xyz[2] ) < 1.0 ) {
break;
}
}
if ( j != c2 ) {
break;
}
}
if ( i != c1 ) {
// we have a connection
bordering[k*patchCount+l] =
bordering[l*patchCount+k] = 1;
} else {
// no connection
bordering[k*patchCount+l] =
bordering[l*patchCount+k] = 0;
}
}
}
/* build groups */
memset( grouped, 0, patchCount );
groupCount = 0;
for ( i = 0; i < patchCount; i++ )
{
/* get patch */
scan = meshes[ i ];
/* start a new group */
if( !grouped[ i ] )
groupCount++;
/* recursively find all patches that belong in the same group */
memset( group, 0, patchCount );
GrowGroup_r( scan, i, patchCount, meshes, bordering, group );
/* bound them */
ClearBounds( bounds[ 0 ], bounds[ 1 ] );
for( j = 0; j < patchCount; j++ )
{
if ( group[ j ] )
{
grouped[ j ] = true;
check = meshes[ j ];
c1 = check->mesh.width * check->mesh.height;
v1 = check->mesh.verts;
for( k = 0; k < c1; k++, v1++ )
AddPointToBounds( v1->xyz, bounds[ 0 ], bounds[ 1 ] );
}
}
/* debug code */
//% Msg( "Longest curve: %f Iterations: %d\n", scan->longestCurve, scan->maxIterations );
/* create drawsurf */
scan->grouped = true;
ds = DrawSurfaceForMesh( e, scan, NULL ); /* ydnar */
VectorCopy( bounds[ 0 ], ds->bounds[ 0 ] );
VectorCopy( bounds[ 1 ], ds->bounds[ 1 ] );
}
/* emit some statistics */
MsgDev( D_INFO, "%9d patches\n", patchCount );
MsgDev( D_INFO, "%9d patch LOD groups\n", groupCount );
}

View File

@ -1,474 +0,0 @@
#include "bsplib.h"
#include "const.h"
vec3_t texture_reflectivity[MAX_MAP_SHADERS];
/*
===================================================================
TEXTURE LIGHT VALUES
===================================================================
*/
/*
======================
CalcTextureReflectivity
======================
*/
void CalcTextureReflectivity( void )
{
int j, i;
rgbdata_t *pic;
bsp_shader_t *si;
// allways set index 0 even if no textures
texture_reflectivity[0][0] = 0.5;
texture_reflectivity[0][1] = 0.5;
texture_reflectivity[0][2] = 0.5;
for( i = 0; i < numtexinfo; i++ )
{
// see if an earlier texinfo allready got the value
for (j = 0; j < i; j++)
{
if( texinfo[i].shadernum == texinfo[j].shadernum )
{
VectorCopy( texture_reflectivity[j], texture_reflectivity[i] );
break;
}
}
if( j != i ) continue;
// try get direct values from shader
if( si = FindShader( dshaders[texinfo[i].shadernum].name ))
{
if( !VectorIsNull( si->color ))
{
VectorDivide( si->color, 255.0f, texture_reflectivity[i] );
texinfo[i].value = si->intensity;
continue;
}
}
pic = FS_LoadImage( dshaders[texinfo[i].shadernum].name, NULL, 0 );
if( pic )
{
// create default and average colors
int k, texels = pic->width * pic->height;
float r, scale;
vec3_t color;
VectorClear( color );
Com_Assert( pic->type != PF_RGBA_32 );
for( k = 0; k < texels; k++ )
{
color[0] += pic->buffer[k*4+0];
color[1] += pic->buffer[k*4+1];
color[2] += pic->buffer[k*4+2];
}
for( k = 0; k < 3; k++ )
{
r = color[k] / texels;
color[k] = r;
}
// scale the reflectivity up, because the textures are so dim
scale = ColorNormalize( color, texture_reflectivity[i] );
texinfo[i].value = (texels * 255.0) / scale; // basic intensity value
FS_FreeImage( pic ); // don't forget free image
}
else VectorSet( texture_reflectivity[i], 0.5, 0.5, 0.5 ); // no texture, no shader...
}
}
/*
=======================================================================
MAKE FACES
=======================================================================
*/
/*
=============
WindingFromFace
=============
*/
winding_t *WindingFromFace( dsurface_t *f )
{
int i;
int se;
dvertex_t *dv;
int v;
winding_t *w;
w = AllocWinding( f->numedges );
w->numpoints = f->numedges;
for( i = 0; i < f->numedges; i++ )
{
se = dsurfedges[f->firstedge + i];
if (se < 0)
v = dedges[-se].v[1];
else
v = dedges[se].v[0];
dv = &dvertexes[v];
VectorCopy (dv->point, w->p[i]);
}
RemoveColinearPoints (w);
return w;
}
/*
=============
BaseLightForFace
=============
*/
void BaseLightForFace( dsurface_t *f, vec3_t color )
{
dshader_t *si;
dtexinfo_t *tx;
//
// check for light emited by texture
//
tx = &texinfo[f->texinfo];
si = &dshaders[tx->shadernum];
if(!(si->surfaceFlags & SURF_LIGHT ) || tx->value == 0 )
{
VectorClear( color );
return;
}
VectorScale( texture_reflectivity[f->texinfo], tx->value, color );
}
bool IsSky( dsurface_t *f )
{
dshader_t *tx;
tx = &dshaders[texinfo[f->texinfo].shadernum];
if( tx->surfaceFlags & SURF_SKY|SURF_3DSKY )
return true;
return false;
}
/*
=============
MakePatchForFace
=============
*/
float totalarea;
void MakePatchForFace( int fn, winding_t *w )
{
dsurface_t *f;
float area;
patch_t *patch;
dplane_t *pl;
int i;
vec3_t color;
dleaf_t *leaf;
f = &dsurfaces[fn];
area = WindingArea( w );
totalarea += area;
patch = &patches[num_patches];
if( num_patches == MAX_PATCHES )
Sys_Error( "MAX_PATCHES limit exceeded\n" );
patch->next = face_patches[fn];
face_patches[fn] = patch;
patch->winding = w;
if( f->side ) patch->plane = &backplanes[f->planenum];
else patch->plane = &dplanes[f->planenum];
if( face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
{
// origin offset faces must create new planes
if( numplanes + fakeplanes >= MAX_MAP_PLANES )
Sys_Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
pl = &dplanes[numplanes + fakeplanes];
fakeplanes++;
*pl = *(patch->plane);
pl->dist += DotProduct (face_offset[fn], pl->normal);
patch->plane = pl;
}
WindingCenter (w, patch->origin);
VectorAdd (patch->origin, patch->plane->normal, patch->origin);
leaf = PointInLeaf(patch->origin);
patch->cluster = leaf->cluster;
patch->area = area;
if (patch->area <= 1)
patch->area = 1;
patch->sky = IsSky (f);
VectorCopy (texture_reflectivity[f->texinfo], patch->reflectivity);
// non-bmodel patches can emit light
if (fn < dmodels[0].numsurfaces)
{
BaseLightForFace (f, patch->baselight);
ColorNormalize (patch->reflectivity, color);
for (i=0 ; i<3 ; i++)
patch->baselight[i] *= color[i];
VectorCopy (patch->baselight, patch->totallight);
}
num_patches++;
}
bsp_entity_t *EntityForModel( int modnum )
{
char *s, name[16];
int i;
com.sprintf( name, "*%i", modnum );
// search the entities for one using modnum
for( i = 0; i < num_entities; i++ )
{
s = ValueForKey( &entities[i], "model" );
if( !com.strcmp( s, name ))
return &entities[i];
}
return &entities[0];
}
/*
=============
MakePatches
=============
*/
void MakePatches( void )
{
int i, j, k;
dsurface_t *f;
int fn;
winding_t *w;
dmodel_t *mod;
vec3_t origin;
bsp_entity_t *ent;
MsgDev( D_INFO, "%i faces\n", numsurfaces );
for( i = 0; i < nummodels; i++ )
{
mod = &dmodels[i];
ent = EntityForModel (i);
// bmodels with origin brushes need to be offset into their in-use position
GetVectorForKey( ent, "origin", origin );
for( j = 0; j < mod->numsurfaces; j++ )
{
fn = mod->firstsurface + j;
face_entity[fn] = ent;
VectorCopy( origin, face_offset[fn] );
f = &dsurfaces[fn];
w = WindingFromFace( f );
for( k = 0; k < w->numpoints; k++ )
VectorAdd( w->p[k], origin, w->p[k] );
MakePatchForFace( fn, w );
}
}
MsgDev( D_INFO, "%i square feet\n", (int)( totalarea/64 ));
}
/*
=======================================================================
SUBDIVIDE
=======================================================================
*/
void FinishSplit (patch_t *patch, patch_t *newp)
{
dleaf_t *leaf;
VectorCopy (patch->baselight, newp->baselight);
VectorCopy (patch->totallight, newp->totallight);
VectorCopy (patch->reflectivity, newp->reflectivity);
newp->plane = patch->plane;
newp->sky = patch->sky;
patch->area = WindingArea (patch->winding);
newp->area = WindingArea (newp->winding);
if (patch->area <= 1)
patch->area = 1;
if (newp->area <= 1)
newp->area = 1;
WindingCenter (patch->winding, patch->origin);
VectorAdd (patch->origin, patch->plane->normal, patch->origin);
leaf = PointInLeaf(patch->origin);
patch->cluster = leaf->cluster;
WindingCenter (newp->winding, newp->origin);
VectorAdd (newp->origin, newp->plane->normal, newp->origin);
leaf = PointInLeaf(newp->origin);
newp->cluster = leaf->cluster;
}
/*
=============
SubdividePatch
Chops the patch only if its local bounds exceed the max size
=============
*/
void SubdividePatch (patch_t *patch)
{
winding_t *w, *o1, *o2;
vec3_t mins, maxs, total;
vec3_t split;
vec_t dist;
int i, j;
vec_t v;
patch_t *newp;
w = patch->winding;
mins[0] = mins[1] = mins[2] = 99999;
maxs[0] = maxs[1] = maxs[2] = -99999;
for (i=0 ; i<w->numpoints ; i++)
{
for (j=0 ; j<3 ; j++)
{
v = w->p[i][j];
if (v < mins[j])
mins[j] = v;
if (v > maxs[j])
maxs[j] = v;
}
}
VectorSubtract (maxs, mins, total);
for (i=0 ; i<3 ; i++)
if (total[i] > (subdiv+1) )
break;
if (i == 3)
{
// no splitting needed
return;
}
//
// split the winding
//
VectorCopy (vec3_origin, split);
split[i] = 1;
dist = (mins[i] + maxs[i])*0.5;
ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
//
// create a new patch
//
if (num_patches == MAX_PATCHES)
Sys_Error ("MAX_PATCHES");
newp = &patches[num_patches];
num_patches++;
newp->next = patch->next;
patch->next = newp;
patch->winding = o1;
newp->winding = o2;
FinishSplit (patch, newp);
SubdividePatch (patch);
SubdividePatch (newp);
}
/*
=============
DicePatch
Chops the patch by a global grid
=============
*/
void DicePatch (patch_t *patch)
{
winding_t *w, *o1, *o2;
vec3_t mins, maxs;
vec3_t split;
vec_t dist;
int i;
patch_t *newp;
w = patch->winding;
WindingBounds (w, mins, maxs);
for (i=0 ; i<3 ; i++)
if (floor((mins[i]+1)/subdiv) < floor((maxs[i]-1)/subdiv))
break;
if (i == 3)
{
// no splitting needed
return;
}
//
// split the winding
//
VectorCopy (vec3_origin, split);
split[i] = 1;
dist = subdiv*(1+floor((mins[i]+1)/subdiv));
ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
//
// create a new patch
//
if (num_patches == MAX_PATCHES)
Sys_Error ("MAX_PATCHES");
newp = &patches[num_patches];
num_patches++;
newp->next = patch->next;
patch->next = newp;
patch->winding = o1;
newp->winding = o2;
FinishSplit (patch, newp);
DicePatch (patch);
DicePatch (newp);
}
/*
=============
SubdividePatches
=============
*/
void SubdividePatches (void)
{
int i, num;
if (subdiv < 1)
return;
num = num_patches; // because the list will grow
for (i=0 ; i<num ; i++)
{
// SubdividePatch (&patches[i]);
DicePatch (&patches[i]);
}
}

446
xtools/bsplib/path_init.c Normal file
View File

@ -0,0 +1,446 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PATH_INIT_C
/* dependencies */
#include "q3map2.h"
/* path support */
#define MAX_BASE_PATHS 10
#define MAX_GAME_PATHS 10
char *homePath;
char installPath[ MAX_SYSPATH ];
int numBasePaths;
char *basePaths[ MAX_BASE_PATHS ];
int numGamePaths;
char *gamePaths[ MAX_GAME_PATHS ];
/*
some of this code is based off the original q3map port from loki
and finds various paths. moved here from bsp.c for clarity.
*/
/*
PathLokiGetHomeDir()
gets the user's home dir (for ~/.q3a)
*/
char *LokiGetHomeDir( void )
{
#ifndef Q_UNIX
return NULL;
#else
char *home;
uid_t id;
struct passwd *pwd;
/* get the home environment variable */
home = getenv( "HOME" );
if( home == NULL )
{
/* do some more digging */
id = getuid();
setpwent();
while( (pwd = getpwent()) != NULL )
{
if( pwd->pw_uid == id )
{
home = pwd->pw_dir;
break;
}
}
endpwent();
}
/* return it */
return home;
#endif
}
/*
PathLokiInitPaths()
initializes some paths on linux/os x
*/
void LokiInitPaths( char *argv0 )
{
#ifndef Q_UNIX
/* this is kinda crap, but hey */
strcpy( installPath, "../" );
#else
char temp[ MAX_OS_PATH ];
char *home;
char *path;
char *last;
bool found;
/* get home dir */
home = LokiGetHomeDir();
if( home == NULL )
home = ".";
/* do some path divining */
strcpy( temp, argv0 );
if( strrchr( temp, '/' ) )
argv0 = strrchr( argv0, '/' ) + 1;
else
{
/* get path environment variable */
path = getenv( "PATH" );
/* minor setup */
last[ 0 ] = path[ 0 ];
last[ 1 ] = '\0';
found = false;
/* go through each : segment of path */
while( last[ 0 ] != '\0' && found == false )
{
/* null out temp */
temp[ 0 ] = '\0';
/* find next chunk */
last = strchr( path, ':' );
if( last == NULL )
last = path + strlen( path );
/* found home dir candidate */
if( *path == '~' )
{
strcpy( temp, home );
path++;
}
/* concatenate */
if( last > (path + 1) )
{
strncat( temp, path, (last - path) );
strcat( temp, "/" );
}
strcat( temp, "./" );
strcat( temp, argv0 );
/* verify the path */
if( access( temp, X_OK ) == 0 )
found++;
path = last + 1;
}
}
/* flake */
if( realpath( temp, installPath ) )
{
/* q3map is in "tools/" */
*(strrchr( installPath, '/' )) = '\0';
*(strrchr( installPath, '/' ) + 1) = '\0';
}
/* set home path */
homePath = home;
#endif
}
/*
CleanPath() - ydnar
cleans a dos path \ -> /
*/
void CleanPath( char *path )
{
while( *path )
{
if( *path == '\\' )
*path = '/';
path++;
}
}
/*
GetGame() - ydnar
gets the game_t based on a -game argument
returns NULL if no match found
*/
game_t *GetGame( char *arg )
{
int i;
/* dummy check */
if( arg == NULL || arg[ 0 ] == '\0' )
return NULL;
/* test it */
i = 0;
while( games[i].arg != NULL )
{
if( com.stricmp( arg, games[ i ].arg ) == 0 )
return &games[ i ];
i++;
}
/* no matching game */
return NULL;
}
/*
AddBasePath() - ydnar
adds a base path to the list
*/
void AddBasePath( char *path )
{
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' || numBasePaths >= MAX_BASE_PATHS )
return;
/* add it to the list */
basePaths[ numBasePaths ] = copystring( path );
CleanPath( basePaths[ numBasePaths ] );
numBasePaths++;
}
/*
AddHomeBasePath() - ydnar
adds a base path to the beginning of the list, prefixed by ~/
*/
void AddHomeBasePath( char *path )
{
#ifdef Q_UNIX
int i;
char temp[ MAX_OS_PATH ];
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' )
return;
/* make a hole */
for( i = 0; i < (MAX_BASE_PATHS - 1); i++ )
basePaths[ i + 1 ] = basePaths[ i ];
/* concatenate home dir and path */
com.sprintf( temp, "%s/%s", homePath, path );
/* add it to the list */
basePaths[ 0 ] = safe_malloc( strlen( temp ) + 1 );
strcpy( basePaths[ 0 ], temp );
CleanPath( basePaths[ 0 ] );
numBasePaths++;
#endif
}
/*
AddGamePath() - ydnar
adds a game path to the list
*/
void AddGamePath( char *path )
{
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' || numGamePaths >= MAX_GAME_PATHS )
return;
/* add it to the list */
gamePaths[ numGamePaths ] = copystring( path );
CleanPath( gamePaths[ numGamePaths ] );
numGamePaths++;
}
/*
InitPaths() - ydnar
cleaned up some of the path initialization code from bsp.c
will remove any arguments it uses
*/
void InitPaths( int *argc, char **argv )
{
int i, j, k, len, len2;
char temp[ MAX_SYSPATH ];
/* note it */
MsgDev( D_NOTE, "--- InitPaths ---\n" );
/* get the install path for backup */
LokiInitPaths( argv[ 0 ] );
/* set game to default (q3a) */
game = &games[ 0 ];
numBasePaths = 0;
numGamePaths = 0;
/* parse through the arguments and extract those relevant to paths */
for( i = 0; i < *argc; i++ )
{
/* check for null */
if( argv[ i ] == NULL )
continue;
/* -game */
if( strcmp( argv[ i ], "-game" ) == 0 )
{
if( ++i >= *argc )
Sys_Error( "Out of arguments: No game specified after %s", argv[ i - 1 ] );
argv[ i - 1 ] = NULL;
game = GetGame( argv[ i ] );
if( game == NULL )
game = &games[ 0 ];
argv[ i ] = NULL;
}
/* -fs_basepath */
else if( strcmp( argv[ i ], "-fs_basepath" ) == 0 )
{
if( ++i >= *argc )
Sys_Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
argv[ i - 1 ] = NULL;
AddBasePath( argv[ i ] );
argv[ i ] = NULL;
}
/* -fs_game */
else if( strcmp( argv[ i ], "-fs_game" ) == 0 )
{
if( ++i >= *argc )
Sys_Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
argv[ i - 1 ] = NULL;
AddGamePath( argv[ i ] );
argv[ i ] = NULL;
}
}
/* remove processed arguments */
for( i = 0, j = 0, k = 0; i < *argc && j < *argc; i++, j++ )
{
for( ; j < *argc && argv[ j ] == NULL; j++ );
argv[ i ] = argv[ j ];
if( argv[ i ] != NULL )
k++;
}
*argc = k;
/* add standard game path */
AddGamePath( game->gamePath );
/* if there is no base path set, figure it out */
if( numBasePaths == 0 )
{
/* this is another crappy replacement for SetQdirFromPath() */
len2 = strlen( game->magic );
for( i = 0; i < *argc && numBasePaths == 0; i++ )
{
/* extract the arg */
strcpy( temp, argv[ i ] );
CleanPath( temp );
len = strlen( temp );
MsgDev ( D_WARN, "Searching for \"%s\" in \"%s\" (%d)...\n", game->magic, temp, i );
/* this is slow, but only done once */
for( j = 0; j < (len - len2); j++ )
{
/* check for the game's magic word */
if( com.strnicmp( &temp[ j ], game->magic, len2 ) == 0 )
{
/* now find the next slash and nuke everything after it */
while( temp[ ++j ] != '/' && temp[ j ] != '\0' );
temp[ j ] = '\0';
/* add this as a base path */
AddBasePath( temp );
break;
}
}
}
/* add install path */
if( numBasePaths == 0 )
AddBasePath( installPath );
/* check again */
if( numBasePaths == 0 )
Sys_Error( "Failed to find a valid base path.\n" );
}
/* this only affects unix */
AddHomeBasePath( game->homeBasePath );
/* initialize vfs paths */
if( numBasePaths > MAX_BASE_PATHS )
numBasePaths = MAX_BASE_PATHS;
if( numGamePaths > MAX_GAME_PATHS )
numGamePaths = MAX_GAME_PATHS;
/* walk the list of game paths */
for( j = 0; j < numGamePaths; j++ )
{
/* walk the list of base paths */
for( i = 0; i < numBasePaths; i++ )
{
/* create a full path and initialize it */
com.sprintf( temp, "%s/%s/", basePaths[ i ], gamePaths[ j ] );
FS_AddGameHierarchy( temp );
}
}
/* done */
Msg( "\n" );
}

View File

@ -1,5 +1,24 @@
/*
Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
#include "bsplib.h"
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
*/
#include "q3map2.h"
// counters are only bumped when running single threaded,
// because they are an awefull coherence problem
@ -8,6 +27,16 @@ int c_peak_windings;
int c_winding_allocs;
int c_winding_points;
#define BOGUS_RANGE WORLD_SIZE
void pw(winding_t *w)
{
int i;
for (i=0 ; i<w->numpoints ; i++)
Msg("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
}
/*
=============
AllocWinding
@ -18,6 +47,9 @@ winding_t *AllocWinding (int points)
winding_t *w;
int s;
if (points >= MAX_POINTS_ON_WINDING)
Sys_Error ("AllocWinding failed: MAX_POINTS_ON_WINDING exceeded");
if (GetNumThreads() == 1)
{
c_winding_allocs++;
@ -27,7 +59,7 @@ winding_t *AllocWinding (int points)
c_peak_windings = c_active_windings;
}
s = sizeof(vec_t)*3*points + sizeof(int);
w = malloc (s);
w = Malloc (s);
memset (w, 0, s);
return w;
}
@ -40,10 +72,9 @@ void FreeWinding (winding_t *w)
if (GetNumThreads() == 1)
c_active_windings--;
free (w);
Mem_Free (w);
}
/*
============
RemoveColinearPoints
@ -173,22 +204,24 @@ winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
vec3_t org, vright, vup;
winding_t *w;
// find the major axis
// find the major axis
max = -BOGUS_RANGE;
x = -1;
for( i = 0; i < 3; i++ )
for (i=0 ; i<3; i++)
{
v = fabs( normal[i] );
if( v > max )
v = fabs(normal[i]);
if (v > max)
{
x = i;
max = v;
}
}
if( x == -1 ) Sys_Error ("BaseWindingForPlane: no axis found");
if (x==-1)
Sys_Error ("BaseWindingForPlane: no axis found");
VectorClear( vup );
switch( x )
VectorCopy (vec3_origin, vup);
switch (x)
{
case 0:
case 1:
@ -199,28 +232,34 @@ winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
break;
}
v = DotProduct( vup, normal );
VectorMA( vup, -v, normal, vup );
VectorNormalize( vup );
VectorScale( normal, dist, org );
CrossProduct( vup, normal, vright );
VectorScale( vup, BOGUS_RANGE, vup );
VectorScale( vright, BOGUS_RANGE, vright );
v = DotProduct (vup, normal);
VectorMA (vup, -v, normal, vup);
VectorNormalize (vup);
VectorScale (normal, dist, org);
CrossProduct (vup, normal, vright);
// LordHavoc: this has to use *2 because otherwise some created points may
// be inside the world (think of a diagonal case), and any brush with such
// points should be removed, failure to detect such cases is disasterous
VectorScale (vup, MAX_WORLD_COORD*2, vup);
VectorScale (vright, MAX_WORLD_COORD*2, vright);
// project a really big axis aligned box onto the plane
w = AllocWinding( 4 );
// project a really big axis aligned box onto the plane
w = AllocWinding (4);
VectorSubtract( org, vright, w->p[0] );
VectorAdd( w->p[0], vup, w->p[0] );
VectorSubtract (org, vright, w->p[0]);
VectorAdd (w->p[0], vup, w->p[0]);
VectorAdd( org, vright, w->p[1] );
VectorAdd( w->p[1], vup, w->p[1] );
VectorAdd (org, vright, w->p[1]);
VectorAdd (w->p[1], vup, w->p[1]);
VectorAdd( org, vright, w->p[2] );
VectorSubtract( w->p[2], vup, w->p[2] );
VectorAdd (org, vright, w->p[2]);
VectorSubtract (w->p[2], vup, w->p[2]);
VectorSubtract( org, vright, w->p[3] );
VectorSubtract( w->p[3], vup, w->p[3] );
VectorSubtract (org, vright, w->p[3]);
VectorSubtract (w->p[3], vup, w->p[3]);
w->numpoints = 4;
@ -238,7 +277,7 @@ winding_t *CopyWinding (winding_t *w)
winding_t *c;
c = AllocWinding (w->numpoints);
size = (int)((winding_t *)0)->p[w->numpoints];
size = (int)((size_t)((winding_t *)0)->p[w->numpoints]);
memcpy (c, w, size);
return c;
}
@ -285,18 +324,15 @@ void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
{
dot = DotProduct (in->p[i], normal);
dot -= dist;
dists[i] = dot;
if (dot > epsilon)
{
sides[i] = SIDE_FRONT;
}
else if (dot < -epsilon)
{
sides[i] = SIDE_BACK;
}
else
{
sides[i] = SIDE_ON;
@ -384,29 +420,29 @@ void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
ChopWindingInPlace
=============
*/
void ChopWindingInPlace( winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon )
void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
{
winding_t *in;
vec_t dists[MAX_POINTS_ON_WINDING+4];
int sides[MAX_POINTS_ON_WINDING+4];
int counts[3];
int sides[MAX_POINTS_ON_WINDING+4];
int counts[3];
static vec_t dot; // VC 4.2 optimizer bug if not static
int i, j;
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t *f;
int maxpts;
int maxpts;
in = *inout;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for( i = 0; i < in->numpoints; i++ )
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct( in->p[i], normal );
dot = DotProduct (in->p[i], normal);
dot -= dist;
dists[i] = dot;
if( dot > epsilon )
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
@ -419,13 +455,14 @@ void ChopWindingInPlace( winding_t **inout, vec3_t normal, vec_t dist, vec_t eps
sides[i] = sides[0];
dists[i] = dists[0];
if( !counts[0] )
if (!counts[0])
{
FreeWinding (in);
*inout = NULL;
return;
}
if( !counts[1] ) return; // inout stays the same
if (!counts[1])
return; // inout stays the same
maxpts = in->numpoints+4; // cant use counts[0]+2 because
// of fp grouping errors
@ -479,6 +516,27 @@ void ChopWindingInPlace( winding_t **inout, vec3_t normal, vec_t dist, vec_t eps
*inout = f;
}
/*
=================
ChopWinding
Returns the fragment of in that is on the front side
of the cliping plane. The original is freed.
=================
*/
winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
{
winding_t *f, *b;
ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
FreeWinding (in);
if (b)
FreeWinding (b);
return f;
}
/*
=================
CheckWinding
@ -508,8 +566,8 @@ void CheckWinding (winding_t *w)
p1 = w->p[i];
for (j=0 ; j<3 ; j++)
if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
Sys_Error ("CheckFace: BUGUS_RANGE: %f",p1[j]);
if (p1[j] > MAX_WORLD_COORD || p1[j] < MIN_WORLD_COORD)
Sys_Error ("CheckFace: MAX_WORLD_COORD exceeded: %f",p1[j]);
j = i+1 == w->numpoints ? 0 : i+1;
@ -540,4 +598,142 @@ void CheckWinding (winding_t *w)
Sys_Error ("CheckWinding: non-convex");
}
}
}
}
/*
============
WindingOnPlaneSide
============
*/
int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
{
bool front, back;
int i;
vec_t d;
front = false;
back = false;
for (i=0 ; i<w->numpoints ; i++)
{
d = DotProduct (w->p[i], normal) - dist;
if (d < -ON_EPSILON)
{
if (front)
return SIDE_CROSS;
back = true;
continue;
}
if (d > ON_EPSILON)
{
if (back)
return SIDE_CROSS;
front = true;
continue;
}
}
if (back)
return SIDE_BACK;
if (front)
return SIDE_FRONT;
return SIDE_ON;
}
/*
=================
AddWindingToConvexHull
Both w and *hull are on the same plane
=================
*/
#define MAX_HULL_POINTS 128
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal ) {
int i, j, k;
float *p, *copy;
vec3_t dir;
float d;
int numHullPoints, numNew;
vec3_t hullPoints[MAX_HULL_POINTS];
vec3_t newHullPoints[MAX_HULL_POINTS];
vec3_t hullDirs[MAX_HULL_POINTS];
bool hullSide[MAX_HULL_POINTS];
bool outside;
if ( !*hull ) {
*hull = CopyWinding( w );
return;
}
numHullPoints = (*hull)->numpoints;
memcpy( hullPoints, (*hull)->p, numHullPoints * sizeof(vec3_t) );
for ( i = 0 ; i < w->numpoints ; i++ ) {
p = w->p[i];
// calculate hull side vectors
for ( j = 0 ; j < numHullPoints ; j++ ) {
k = ( j + 1 ) % numHullPoints;
VectorSubtract( hullPoints[k], hullPoints[j], dir );
VectorNormalize( dir );
CrossProduct( normal, dir, hullDirs[j] );
}
outside = false;
for ( j = 0 ; j < numHullPoints ; j++ ) {
VectorSubtract( p, hullPoints[j], dir );
d = DotProduct( dir, hullDirs[j] );
if ( d >= ON_EPSILON ) {
outside = true;
}
if ( d >= -ON_EPSILON ) {
hullSide[j] = true;
} else {
hullSide[j] = false;
}
}
// if the point is effectively inside, do nothing
if ( !outside ) {
continue;
}
// find the back side to front side transition
for ( j = 0 ; j < numHullPoints ; j++ ) {
if ( !hullSide[ j % numHullPoints ] && hullSide[ (j + 1) % numHullPoints ] ) {
break;
}
}
if ( j == numHullPoints ) {
continue;
}
// insert the point here
VectorCopy( p, newHullPoints[0] );
numNew = 1;
// copy over all points that aren't double fronts
j = (j+1)%numHullPoints;
for ( k = 0 ; k < numHullPoints ; k++ ) {
if ( hullSide[ (j+k) % numHullPoints ] && hullSide[ (j+k+1) % numHullPoints ] ) {
continue;
}
copy = hullPoints[ (j+k+1) % numHullPoints ];
VectorCopy( copy, newHullPoints[numNew] );
numNew++;
}
numHullPoints = numNew;
memcpy( hullPoints, newHullPoints, numHullPoints * sizeof(vec3_t) );
}
FreeWinding( *hull );
w = AllocWinding( numHullPoints );
w->numpoints = numHullPoints;
*hull = w;
memcpy( w->p, hullPoints, numHullPoints * sizeof(vec3_t) );
}

57
xtools/bsplib/polylib.h Normal file
View File

@ -0,0 +1,57 @@
/*
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
*/
typedef struct
{
int numpoints;
vec3_t p[4]; // variable sized
} winding_t;
#define MAX_POINTS_ON_WINDING 64
// you can define on_epsilon in the makefile as tighter
#ifndef ON_EPSILON
#define ON_EPSILON 0.1
#endif
winding_t *AllocWinding (int points);
vec_t WindingArea (winding_t *w);
void WindingCenter (winding_t *w, vec3_t center);
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back);
winding_t *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
winding_t *CopyWinding (winding_t *w);
winding_t *ReverseWinding (winding_t *w);
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
void CheckWinding (winding_t *w);
void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
void RemoveColinearPoints (winding_t *w);
int WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist);
void FreeWinding (winding_t *w);
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
void AddWindingToConvexHull( winding_t *w, winding_t **hull, vec3_t normal );
void ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
// frees the original if clipped
void pw(winding_t *w);

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,66 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define PRTFILE_C
/* dependencies */
#include "q3map2.h"
#include "bsplib.h"
#include "const.h"
/*
==============================================================================
Portal file generation
PORTAL file_t GENERATION
Save out name.prt for qvis to read
==============================================================================
*/
#define PORTALFILE "PRT1"
#define PORTALfile_t "PRT1"
file_t *pf;
int num_visclusters; // clusters the player can be in
int num_visportals;
file_t *pf;
int num_visclusters; // clusters the player can be in
int num_visportals;
int num_solidfaces;
void WriteFloat (file_t *f, vec_t v)
{
if ( fabs(v - floor(v + 0.5)) < 0.001 )
FS_Printf(f,"%i ",(int)floor(v + 0.5));
else FS_Printf(f,"%f ",v);
if ( fabs(v - Q_rint(v)) < 0.001 )
FS_Printf (f,"%i ",(int)Q_rint(v));
else
FS_Printf (f,"%f ",v);
}
/*
@ -39,15 +77,15 @@ void WritePortalFile_r (node_t *node)
vec_t dist;
// decision node
if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
{
if (node->planenum != PLANENUM_LEAF) {
WritePortalFile_r (node->children[0]);
WritePortalFile_r (node->children[1]);
return;
}
if (node->contents & CONTENTS_SOLID)
if (node->opaque) {
return;
}
for (p = node->portals ; p ; p=p->next[s])
{
@ -55,54 +93,103 @@ void WritePortalFile_r (node_t *node)
s = (p->nodes[1] == node);
if (w && p->nodes[0] == node)
{
if (!Portal_VisFlood (p)) continue;
// write out to the file
// sometimes planes get turned around when they are very near
// the changeover point between different axis. interpret the
// plane the same way vis will, and flip the side orders if needed
if (!PortalPassable(p))
continue;
// write out to the file
// sometimes planes get turned around when they are very near
// the changeover point between different axis. interpret the
// plane the same way vis will, and flip the side orders if needed
// FIXME: is this still relevent?
WindingPlane (w, normal, &dist);
if ( DotProduct (p->plane.normal, normal) < 0.99 )
{ // backwards...
FS_Printf(pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
FS_Printf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
}
else
FS_Printf(pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
FS_Printf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
/* ydnar: added this change to make antiportals work */
if( p->compileFlags & C_HINT )
FS_Printf( pf, "1 " );
else
FS_Printf( pf, "0 " );
/* write the winding */
for (i=0 ; i<w->numpoints ; i++)
{
FS_Printf (pf,"( ");
FS_Printf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
FS_Printf (pf,") ");
}
FS_Printf(pf,"\n");
FS_Printf (pf,"\n");
}
}
}
/*
================
FillLeafNumbers_r
All of the leafs under node will have the same cluster
================
=================
WriteFaceFile_r
=================
*/
void FillLeafNumbers_r (node_t *node, int num)
void WriteFaceFile_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
{
if (node->contents & CONTENTS_SOLID)
node->cluster = -1;
else
node->cluster = num;
int i, s;
portal_t *p;
winding_t *w;
// decision node
if (node->planenum != PLANENUM_LEAF) {
WriteFaceFile_r (node->children[0]);
WriteFaceFile_r (node->children[1]);
return;
}
node->cluster = num;
FillLeafNumbers_r (node->children[0], num);
FillLeafNumbers_r (node->children[1], num);
if (node->opaque) {
return;
}
for (p = node->portals ; p ; p=p->next[s])
{
w = p->winding;
s = (p->nodes[1] == node);
if (w)
{
if (PortalPassable(p))
continue;
// write out to the file
if (p->nodes[0] == node)
{
FS_Printf (pf,"%i %i ",w->numpoints, p->nodes[0]->cluster);
for (i=0 ; i<w->numpoints ; i++)
{
FS_Printf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
FS_Printf (pf,") ");
}
FS_Printf (pf,"\n");
}
else
{
FS_Printf (pf,"%i %i ",w->numpoints, p->nodes[1]->cluster);
for (i = w->numpoints-1; i >= 0; i--)
{
FS_Printf (pf,"(");
WriteFloat (pf, w->p[i][0]);
WriteFloat (pf, w->p[i][1]);
WriteFloat (pf, w->p[i][2]);
FS_Printf (pf,") ");
}
FS_Printf (pf,"\n");
}
}
}
}
/*
@ -114,23 +201,23 @@ void NumberLeafs_r (node_t *node)
{
portal_t *p;
if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
{ // decision node
if ( node->planenum != PLANENUM_LEAF ) {
// decision node
node->cluster = -99;
NumberLeafs_r (node->children[0]);
NumberLeafs_r (node->children[1]);
return;
}
// either a leaf or a detail cluster
node->area = -1;
if ( node->contents & CONTENTS_SOLID )
{ // solid block, viewpoint never inside
if ( node->opaque ) {
// solid block, viewpoint never inside
node->cluster = -1;
return;
}
FillLeafNumbers_r (node, num_visclusters);
node->cluster = num_visclusters;
num_visclusters++;
// count the portals
@ -138,79 +225,40 @@ void NumberLeafs_r (node_t *node)
{
if (p->nodes[0] == node) // only write out from first leaf
{
if (Portal_VisFlood (p))
if (PortalPassable(p))
num_visportals++;
else
num_solidfaces++;
p = p->next[0];
}
else
{
if (!PortalPassable(p))
num_solidfaces++;
p = p->next[1];
}
}
}
/*
================
CreateVisPortals_r
NumberClusters
================
*/
void CreateVisPortals_r (node_t *node)
{
// stop as soon as we get to a detail_seperator, which
// means that everything below is in a single cluster
if (node->planenum == PLANENUM_LEAF || node->detail_seperator )
return;
void NumberClusters(tree_t *tree) {
num_visclusters = 0;
num_visportals = 0;
num_solidfaces = 0;
MakeNodePortal (node);
SplitNodePortals (node);
MsgDev( D_NOTE, "--- NumberClusters ---\n");
// set the cluster field in every leaf and count the total number of portals
NumberLeafs_r (tree->headnode);
CreateVisPortals_r (node->children[0]);
CreateVisPortals_r (node->children[1]);
}
/*
================
FinishVisPortals_r
================
*/
void FinishVisPortals2_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
return;
MakeNodePortal (node);
SplitNodePortals (node);
FinishVisPortals2_r (node->children[0]);
FinishVisPortals2_r (node->children[1]);
}
void FinishVisPortals_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
return;
if (node->detail_seperator)
{
FinishVisPortals2_r (node);
return;
}
FinishVisPortals_r (node->children[0]);
FinishVisPortals_r (node->children[1]);
}
int clusterleaf;
void SaveClusters_r (node_t *node)
{
if (node->planenum == PLANENUM_LEAF)
{
dleafs[clusterleaf++].cluster = node->cluster;
return;
}
SaveClusters_r (node->children[0]);
SaveClusters_r (node->children[1]);
MsgDev( D_INFO, "%9d visclusters\n", num_visclusters );
MsgDev( D_INFO, "%9d visportals\n", num_visportals );
MsgDev( D_INFO, "%9d solidfaces\n", num_solidfaces );
}
/*
@ -218,43 +266,26 @@ void SaveClusters_r (node_t *node)
WritePortalFile
================
*/
void WritePortalFile (tree_t *tree)
void WritePortalFile( tree_t *tree )
{
char path[MAX_SYSPATH];
node_t *headnode;
char filename[MAX_SYSPATH];
Msg("--- WritePortalFile ---\n");
headnode = tree->headnode;
num_visclusters = 0;
num_visportals = 0;
FreeTreePortals_r (headnode);
MakeHeadnodePortals (tree);
CreateVisPortals_r (headnode);
// set the cluster field in every leaf and count the total number of portals
NumberLeafs_r (headnode);
MsgDev( D_NOTE, "--- WritePortalFile ---\n" );
// write the file
com.sprintf (path, "maps/%s.prt", gs_filename );
pf = FS_Open(path, "w" );
if (!pf) Sys_Error ("Error opening %s", path);
com.sprintf( filename, "%s.prt", source );
Msg( "writing %s\n", filename );
pf = FS_Open( filename, "w" );
if( !pf ) Sys_Error( "error opening %s\n", filename );
FS_Printf (pf, "%s\n", PORTALFILE);
FS_Printf (pf, "%s\n", PORTALfile_t);
FS_Printf (pf, "%i\n", num_visclusters);
FS_Printf (pf, "%i\n", num_visportals);
FS_Printf (pf, "%i\n", num_solidfaces);
MsgDev( D_INFO, "%5i visclusters\n", num_visclusters );
MsgDev( D_INFO, "%5i visportals\n", num_visportals );
WritePortalFile_r( headnode );
WritePortalFile_r(tree->headnode);
WriteFaceFile_r(tree->headnode);
FS_Close( pf );
// we need to store the clusters out now because ordering
// issues made us do this after writebsp...
clusterleaf = 1;
SaveClusters_r (headnode);
}
}

2248
xtools/bsplib/q3map2.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,302 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// qbsp.c - emit bsp nodes and leafs
//=======================================================================
#include "bsplib.h"
#include "const.h"
#define BLOCK_SIZE 8192 // test
int block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;
int entity_num;
node_t *block_nodes[10][10];
/*
============
BlockTree
============
*/
node_t *BlockTree (int xl, int yl, int xh, int yh)
{
node_t *node;
vec3_t normal;
float dist;
int mid;
if (xl == xh && yl == yh)
{
node = block_nodes[xl+5][yl+5];
if (!node)
{ // return an empty leaf
node = AllocNode ();
node->planenum = PLANENUM_LEAF;
node->contents = 0; //CONTENTS_SOLID;
return node;
}
return node;
}
// create a seperator along the largest axis
node = AllocNode ();
if (xh - xl > yh - yl)
{ // split x axis
mid = xl + (xh-xl)/2 + 1;
normal[0] = 1;
normal[1] = 0;
normal[2] = 0;
dist = mid*BLOCK_SIZE;
node->planenum = FindFloatPlane (normal, dist);
node->children[0] = BlockTree ( mid, yl, xh, yh);
node->children[1] = BlockTree ( xl, yl, mid-1, yh);
}
else
{
mid = yl + (yh-yl)/2 + 1;
normal[0] = 0;
normal[1] = 1;
normal[2] = 0;
dist = mid*BLOCK_SIZE;
node->planenum = FindFloatPlane (normal, dist);
node->children[0] = BlockTree ( xl, mid, xh, yh);
node->children[1] = BlockTree ( xl, yl, xh, mid-1);
}
return node;
}
/*
============
ProcessBlock_Thread
============
*/
int brush_start, brush_end;
void ProcessBlock_Thread (int blocknum)
{
int xblock, yblock;
vec3_t mins, maxs;
bspbrush_t *brushes;
tree_t *tree;
node_t *node;
yblock = block_yl + blocknum / (block_xh-block_xl+1);
xblock = block_xl + blocknum % (block_xh-block_xl+1);
mins[0] = xblock*BLOCK_SIZE;
mins[1] = yblock*BLOCK_SIZE;
mins[2] = MIN_WORLD_COORD;
maxs[0] = (xblock+1)*BLOCK_SIZE;
maxs[1] = (yblock+1)*BLOCK_SIZE;
maxs[2] = MAX_WORLD_COORD;
// the makelist and chopbrushes could be cached between the passes...
brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs);
if (!brushes)
{
node = AllocNode ();
node->planenum = PLANENUM_LEAF;
node->contents = CONTENTS_SOLID;
block_nodes[xblock+5][yblock+5] = node;
return;
}
// brushes = ChopBrushes (brushes);
tree = BrushBSP (brushes, mins, maxs);
block_nodes[xblock+5][yblock+5] = tree->headnode;
Mem_Free( tree );
}
/*
============
ProcessWorldModel
============
*/
void ProcessWorldModel( void )
{
bsp_entity_t *e;
tree_t *tree;
bool leaked;
bool optimize;
e = &entities[entity_num];
brush_start = e->firstbrush;
brush_end = brush_start + e->numbrushes;
leaked = false;
//
// perform per-block operations
//
if (block_xh * BLOCK_SIZE > map_maxs[0])
block_xh = floor(map_maxs[0]/BLOCK_SIZE);
if ( (block_xl+1) * BLOCK_SIZE < map_mins[0])
block_xl = floor(map_mins[0]/BLOCK_SIZE);
if (block_yh * BLOCK_SIZE > map_maxs[1])
block_yh = floor(map_maxs[1]/BLOCK_SIZE);
if ( (block_yl+1) * BLOCK_SIZE < map_mins[1])
block_yl = floor(map_mins[1]/BLOCK_SIZE);
if (block_xl <-4) block_xl = -4;
if (block_yl <-4) block_yl = -4;
if (block_xh > 3) block_xh = 3;
if (block_yh > 3) block_yh = 3;
for (optimize = false; optimize <= true; optimize++)
{
RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1), true, ProcessBlock_Thread);
// build the division tree
// oversizing the blocks guarantees that all the boundaries
// will also get nodes.
tree = AllocTree ();
tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
tree->mins[0] = (block_xl)*BLOCK_SIZE;
tree->mins[1] = (block_yl)*BLOCK_SIZE;
tree->mins[2] = map_mins[2] - 8;
tree->maxs[0] = (block_xh+1)*BLOCK_SIZE;
tree->maxs[1] = (block_yh+1)*BLOCK_SIZE;
tree->maxs[2] = map_maxs[2] + 8;
//
// perform the global operations
//
MakeTreePortals (tree);
if (FloodEntities (tree))
{
FillOutside (tree->headnode);
}
else
{
Msg("**** leaked ****\n");
leaked = true;
LeakFile (tree);
}
MarkVisibleSides (tree, brush_start, brush_end);
if (leaked) break;
if (!optimize)
{
FreeTree (tree);
}
}
FloodAreas(tree);
MakeFaces(tree->headnode);
FixTjuncs(tree->headnode);
PruneNodes(tree->headnode);
WriteBSP(tree->headnode);
if(!leaked) WritePortalFile( tree );
FreeTree( tree );
}
/*
============
ProcessSubModel
============
*/
void ProcessSubModel (void)
{
bsp_entity_t *e;
int start, end;
tree_t *tree;
bspbrush_t *list;
vec3_t mins, maxs;
e = &entities[entity_num];
start = e->firstbrush;
end = start + e->numbrushes;
mins[0] = mins[1] = mins[2] = MIN_WORLD_COORD;
maxs[0] = maxs[1] = maxs[2] = MAX_WORLD_COORD;
list = MakeBspBrushList (start, end, mins, maxs);
list = ChopBrushes (list);
tree = BrushBSP (list, mins, maxs);
MakeTreePortals (tree);
MarkVisibleSides (tree, start, end);
MakeFaces (tree->headnode);
FixTjuncs (tree->headnode);
WriteBSP (tree->headnode);
FreeTree (tree);
}
/*
============
ProcessModels
============
*/
void ProcessModels (void)
{
BeginBSPFile ();
for (entity_num = 0; entity_num < num_entities; entity_num++)
{
if (!entities[entity_num].numbrushes)
continue;
BeginModel ();
if (entity_num == 0) ProcessWorldModel ();
else ProcessSubModel ();
EndModel ();
}
EndBSPFile ();
}
/*
============
WbspMain
============
*/
void WbspMain( void )
{
Msg( "\n---- bsp ---- [%s]\n", (bsp_parms & BSPLIB_ONLYENTS) ? "onlyents" : "normal" );
if(!( bsp_parms & BSPLIB_ONLYENTS ))
{
// delete portal and line files
com.sprintf( path, "%s/maps/%s.prt", com.GameInfo->gamedir, gs_filename );
FS_Delete( path );
com.sprintf( path, "%s/maps/%s.lin", com.GameInfo->gamedir, gs_filename );
FS_Delete( path );
}
// if onlyents, just grab the entites and resave
if( bsp_parms & BSPLIB_ONLYENTS )
{
LoadBSPFile();
num_entities = 0;
LoadMapFile();
SetModelNumbers();
SetLightStyles();
UnparseEntities();
WriteBSPFile();
}
else
{
// start from scratch
LoadMapFile();
SetModelNumbers();
SetLightStyles();
ProcessModels();
LoadBSPFile();
ProcessCollisionTree();
WriteBSPFile();
}
}

View File

@ -1,429 +0,0 @@
// qrad.c
#include "bsplib.h"
#include "const.h"
/*
NOTES
-----
every surface must be divided into at least two patches each axis
*/
patch_t *face_patches[MAX_MAP_SURFACES];
bsp_entity_t *face_entity[MAX_MAP_SURFACES];
patch_t patches[MAX_PATCHES];
uint num_patches;
vec3_t radiosity[MAX_PATCHES]; // light leaving a patch
vec3_t illumination[MAX_PATCHES]; // light arriving at a patch
vec3_t face_offset[MAX_MAP_SURFACES]; // for rotating bmodels
dplane_t backplanes[MAX_MAP_PLANES];
int fakeplanes; // created planes for origin offset
float subdiv = 64; // ???
void BuildLightmaps (void);
int TestLine (vec3_t start, vec3_t stop);
int junk;
int numbounce = 0;
float ambient = 0;
float maxlight = 196;
float lightscale = 1.0f;
float direct_scale = 0.4f;
float entity_scale = 1.0f;
/*
===================================================================
MISC
===================================================================
*/
/*
=============
MakeBackplanes
=============
*/
void MakeBackplanes (void)
{
int i;
for (i=0 ; i<numplanes ; i++)
{
backplanes[i].dist = -dplanes[i].dist;
VectorSubtract (vec3_origin, dplanes[i].normal, backplanes[i].normal);
}
}
int leafparents[MAX_MAP_LEAFS];
int nodeparents[MAX_MAP_NODES];
/*
=============
MakeParents
=============
*/
void MakeParents (int nodenum, int parent)
{
int i, j;
dnode_t *node;
nodeparents[nodenum] = parent;
node = &dnodes[nodenum];
for (i=0 ; i<2 ; i++)
{
j = node->children[i];
if (j < 0) leafparents[-j - 1] = nodenum;
else MakeParents( j, nodenum );
}
}
/*
===================================================================
TRANSFER SCALES
===================================================================
*/
/*
=============
MakeTransfers
=============
*/
int total_transfer;
void MakeTransfers (int i)
{
int j;
vec3_t delta;
vec_t dist, scale;
float trans;
int itrans;
patch_t *patch, *patch2;
float total;
dplane_t plane;
vec3_t origin;
float transfers[MAX_PATCHES], *all_transfers;
int s;
int itotal;
byte pvs[(MAX_MAP_LEAFS+7)/8];
int cluster;
patch = patches + i;
total = 0;
VectorCopy (patch->origin, origin);
plane = *patch->plane;
if (!PvsForOrigin (patch->origin, pvs))
return;
// find out which patch2s will collect light
// from patch
all_transfers = transfers;
patch->numtransfers = 0;
for (j=0, patch2 = patches ; j<num_patches ; j++, patch2++)
{
transfers[j] = 0;
if (j == i)
continue;
// check pvs bit
cluster = patch2->cluster;
if (cluster == -1) continue;
if (!( pvs[cluster>>3] & (1<<(cluster&7)) ) )
continue; // not in pvs
// calculate vector
VectorSubtract (patch2->origin, origin, delta);
dist = VectorNormalizeLength( delta );
if( !dist ) continue; // should never happen
// reletive angles
scale = DotProduct (delta, plane.normal);
scale *= -DotProduct (delta, patch2->plane->normal);
if (scale <= 0)
continue;
// check exact transfer
if( TestLine_r( 0, patch->origin, patch2->origin ) & CONTENTS_SOLID )
continue;
trans = scale * patch2->area / (dist*dist);
if (trans < 0)
trans = 0; // rounding errors...
transfers[j] = trans;
if (trans > 0)
{
total += trans;
patch->numtransfers++;
}
}
// copy the transfers out and normalize
// total should be somewhere near PI if everything went right
// because partial occlusion isn't accounted for, and nearby
// patches have underestimated form factors, it will usually
// be higher than PI
if (patch->numtransfers)
{
transfer_t *t;
if (patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES)
Sys_Error ("Weird numtransfers");
s = patch->numtransfers * sizeof(transfer_t);
patch->transfers = Malloc (s);
if (!patch->transfers)
Sys_Error ("Memory allocation failure");
//
// normalize all transfers so all of the light
// is transfered to the surroundings
//
t = patch->transfers;
itotal = 0;
for (j=0 ; j<num_patches ; j++)
{
if (transfers[j] <= 0)
continue;
itrans = transfers[j]*0x10000 / total;
itotal += itrans;
t->transfer = itrans;
t->patch = j;
t++;
}
}
// don't bother locking around this. not that important.
total_transfer += patch->numtransfers;
}
/*
=============
FreeTransfers
=============
*/
void FreeTransfers (void)
{
int i;
for (i = 0; i < num_patches; i++)
{
if(patches[i].transfers)
Mem_Free (patches[i].transfers);
patches[i].transfers = NULL;
}
}
//==============================================================
/*
=============
CollectLight
=============
*/
float CollectLight (void)
{
int i, j;
patch_t *patch;
vec_t total;
total = 0;
for (i=0, patch=patches ; i<num_patches ; i++, patch++)
{
// skys never collect light, it is just dropped
if (patch->sky)
{
VectorClear (radiosity[i]);
VectorClear (illumination[i]);
continue;
}
for (j=0 ; j<3 ; j++)
{
patch->totallight[j] += illumination[i][j] / patch->area;
radiosity[i][j] = illumination[i][j] * patch->reflectivity[j];
}
total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2];
VectorClear (illumination[i]);
}
return total;
}
/*
=============
ShootLight
Send light out to other patches
Run multi-threaded
=============
*/
void ShootLight (int patchnum)
{
int k, l;
transfer_t *trans;
int num;
patch_t *patch;
vec3_t send;
// this is the amount of light we are distributing
// prescale it so that multiplying by the 16 bit
// transfer values gives a proper output value
for (k=0 ; k<3 ; k++)
send[k] = radiosity[patchnum][k] / 0x10000;
patch = &patches[patchnum];
trans = patch->transfers;
num = patch->numtransfers;
for (k=0 ; k<num ; k++, trans++)
{
for (l=0 ; l<3 ; l++)
illumination[trans->patch][l] += send[l]*trans->transfer;
}
}
/*
=============
BounceLight
=============
*/
void BounceLight (void)
{
int i, j;
float added;
patch_t *p;
for (i=0 ; i<num_patches ; i++)
{
p = &patches[i];
for (j=0 ; j<3 ; j++)
{
radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;
}
}
for (i = 0; i < numbounce; i++)
{
RunThreadsOnIndividual (num_patches, false, ShootLight);
added = CollectLight ();
}
}
//==============================================================
void CheckPatches (void)
{
int i;
patch_t *patch;
for (i=0 ; i<num_patches ; i++)
{
patch = &patches[i];
if (patch->totallight[0] < 0 || patch->totallight[1] < 0 || patch->totallight[2] < 0)
Sys_Error ("negative patch totallight\n");
}
}
/*
=============
RadWorld
=============
*/
void RadWorld (void)
{
if( numnodes == 0 || numsurfaces == 0 )
Sys_Break( "Empty map %s.bsp\n", gs_filename );
MakeBackplanes ();
MakeParents (0, -1);
MakeTnodes (&dmodels[0]);
// turn each face into a single patch
MakePatches ();
// subdivide patches to a maximum dimension
SubdividePatches ();
// create directlights out of patches and lights
CreateDirectLights ();
// build initial facelights
RunThreadsOnIndividual( numsurfaces, true, BuildFacelights );
if (numbounce > 0)
{
// build transfer lists
RunThreadsOnIndividual (num_patches, true, MakeTransfers);
Msg("Make transfer lists: %5.1f megs\n", (float)total_transfer * sizeof(transfer_t) / (1024*1024));
// spread light around
BounceLight();
FreeTransfers();
CheckPatches();
}
// blend bounced light into direct light and save
PairEdges ();
LinkPlaneFaces ();
lightdatasize = 0;
RunThreadsOnIndividual( numsurfaces, true, FinalLightFace );
}
void WradMain( void )
{
string cmdparm;
if(!LoadBSPFile( ))
{
// map not exist, create it
WbspMain();
LoadBSPFile();
}
if( bsp_parms & BSPLIB_MAKEHLRAD )
{
Msg( "\n---- hlrad ---- [%s]\n", (bsp_parms & BSPLIB_FULLCOMPILE) ? "extra" : "normal" );
direct_scale = 1.4f; // extrapolated light is too dim
}
else Msg( "\n---- qrad ---- [%s]\n", (bsp_parms & BSPLIB_FULLCOMPILE) ? "extra" : "normal" );
if( FS_GetParmFromCmdLine( "-ambient", cmdparm ))
ambient = com.atof( cmdparm );
ambient = bound( 0, ambient, 512 );
if( FS_GetParmFromCmdLine( "-bounce", cmdparm ))
numbounce = com.atoi( cmdparm );
numbounce = bound( 0, numbounce, 32 );
ParseEntities();
CalcTextureReflectivity();
if( !visdatasize )
{
Msg( "No vis information, direct lighting only.\n" );
numbounce = 0;
ambient = 0.1f;
}
RadWorld();
WriteBSPFile();
}

View File

@ -1,617 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// vis.c - map visibility
//=======================================================================
#include "bsplib.h"
int numportals;
int portalclusters;
visportal_t *portals;
leaf_t *leafs;
int c_portaltest, c_portalpass, c_portalcheck;
byte *uncompressedvis;
byte *vismap, *vismap_p, *vismap_end; // past visfile
int originalvismapsize;
int leafbytes; // (portalclusters+63)>>3
int leaflongs;
int portalbytes, portallongs;
int totalvis;
int totalphs;
visportal_t *sorted_portals[MAX_MAP_PORTALS*2];
float maxdist;
/*
===============
CompressVis
===============
*/
int CompressVis( byte *vis, byte *dest )
{
int j, rep, visrow;
byte *dest_p;
dest_p = dest;
visrow = (dvis->numclusters + 7)>>3;
for( j = 0; j < visrow; j++ )
{
*dest_p++ = vis[j];
if( vis[j] ) continue;
rep = 1;
for( j++; j < visrow; j++ )
if( vis[j] || rep == 255 )
break;
else rep++;
*dest_p++ = rep;
j--;
}
return dest_p - dest;
}
/*
===================
DecompressVis
===================
*/
void DecompressVis( byte *in, byte *decompressed )
{
int c;
byte *out;
int row;
row = (dvis->numclusters+7)>>3;
out = decompressed;
do
{
if( *in )
{
*out++ = *in++;
continue;
}
c = in[1];
if( !c ) Sys_Error( "DecompressVis: 0 repeat\n" );
in += 2;
while( c )
{
*out++ = 0;
c--;
}
} while( out - decompressed < row );
}
int PointInLeafnum ( vec3_t point )
{
float dist;
dnode_t *node;
dplane_t *plane;
int nodenum = 0;
while( nodenum >= 0 )
{
node = &dnodes[nodenum];
plane = &dplanes[node->planenum];
dist = DotProduct( point, plane->normal ) - plane->dist;
if( dist > 0 ) nodenum = node->children[0];
else nodenum = node->children[1];
}
return -nodenum - 1;
}
dleaf_t *PointInLeaf( vec3_t point )
{
int num;
num = PointInLeafnum( point );
return &dleafs[num];
}
bool PvsForOrigin( vec3_t org, byte *pvs )
{
dleaf_t *leaf;
if( !visdatasize )
{
Mem_Set( pvs, 0xFF, (numleafs+7)/8 );
return true;
}
leaf = PointInLeaf( org );
if( leaf->cluster == -1 )
return false; // in solid leaf
DecompressVis( dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs );
return true;
}
byte *PhsForCluster( int cluster )
{
static byte phs[(MAX_MAP_LEAFS+7)/8];
Mem_Set( phs, 0x00, (numleafs+7)/8 );
DecompressVis( dvisdata + dvis->bitofs[cluster][DVIS_PHS], phs );
return phs;
}
//=============================================================================
void PlaneFromWinding( viswinding_t *w, visplane_t *plane )
{
vec3_t v1, v2;
// calc plane
VectorSubtract (w->points[2], w->points[1], v1);
VectorSubtract (w->points[0], w->points[1], v2);
CrossProduct (v2, v1, plane->normal);
VectorNormalize (plane->normal);
plane->dist = DotProduct (w->points[0], plane->normal);
}
/*
==================
NewVisWinding
==================
*/
viswinding_t *NewVisWinding( int points )
{
viswinding_t *w;
int size;
if( points > MAX_POINTS_ON_WINDING )
Sys_Error ("NewVisWinding: %i points", points);
size = (int)((viswinding_t *)0)->points[points];
w = malloc( size );
memset( w, 0, size );
return w;
}
static void pw(viswinding_t *w)
{
int i;
for (i=0 ; i<w->numpoints ; i++)
Msg("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
}
static void prl(leaf_t *l)
{
int i;
visportal_t *p;
visplane_t pl;
for (i=0 ; i<l->numportals ; i++)
{
p = l->portals[i];
pl = p->plane;
Msg ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
}
}
//=============================================================================
/*
=============
SortPortals
Sorts the portals from the least complex, so the later ones can reuse
the earlier information.
=============
*/
int PComp (const void *a, const void *b)
{
if ( (*(visportal_t **)a)->nummightsee == (*(visportal_t **)b)->nummightsee)
return 0;
if ( (*(visportal_t **)a)->nummightsee < (*(visportal_t **)b)->nummightsee)
return -1;
return 1;
}
void SortPortals (void)
{
int i;
for (i=0 ; i<numportals*2 ; i++)
sorted_portals[i] = &portals[i];
qsort (sorted_portals, numportals*2, sizeof(sorted_portals[0]), PComp);
}
/*
==============
LeafVectorFromPortalVector
==============
*/
int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
{
int i;
visportal_t *p;
int c_leafs;
memset (leafbits, 0, leafbytes);
for (i=0 ; i<numportals*2 ; i++)
{
if (portalbits[i>>3] & (1<<(i&7)) )
{
p = portals+i;
leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
}
}
c_leafs = CountBits (leafbits, portalclusters);
return c_leafs;
}
/*
===============
ClusterMerge
Merges the portal visibility for a leaf
===============
*/
void ClusterMerge (int leafnum)
{
leaf_t *leaf;
byte portalvector[MAX_PORTALS/8];
byte uncompressed[MAX_MAP_LEAFS/8];
byte compressed[MAX_MAP_LEAFS/8];
int i, j;
int numvis;
byte *dest;
visportal_t *p;
int pnum;
// OR together all the portalvis bits
memset (portalvector, 0, portalbytes);
leaf = &leafs[leafnum];
for (i = 0; i < leaf->numportals; i++)
{
p = leaf->portals[i];
if(p->status != stat_done)
Sys_Error ("portal not done");
for(j = 0; j < portallongs; j++)
((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
pnum = p - portals;
portalvector[pnum>>3] |= 1<<(pnum&7);
}
// convert portal bits to leaf bits
numvis = LeafVectorFromPortalVector(portalvector, uncompressed);
if (uncompressed[leafnum>>3] & (1<<(leafnum&7)))
MsgDev( D_WARN, "Leaf portals saw into leaf\n");
uncompressed[leafnum>>3] |= (1<<(leafnum&7));
numvis++; // count the leaf itself
// save uncompressed for PHS calculation
Mem_Copy(uncompressedvis + leafnum*leafbytes, uncompressed, leafbytes);
// compress the bit string
totalvis += numvis;
i = CompressVis (uncompressed, compressed);
dest = vismap_p;
vismap_p += i;
if (vismap_p > vismap_end)
Sys_Error ("Vismap expansion overflow");
dvis->bitofs[leafnum][DVIS_PVS] = dest - vismap;
Mem_Copy(dest, compressed, i);
}
/*
==================
CalcPortalVis
==================
*/
void CalcPortalVis( void )
{
if( bsp_parms & BSPLIB_FULLCOMPILE )
{
RunThreadsOnIndividual( numportals * 2, true, PortalFlow );
}
else
{
int i;
// fastvis just uses mightsee for a very loose bound
for( i = 0; i < numportals * 2; i++ )
{
portals[i].portalvis = portals[i].portalflood;
portals[i].status = stat_done;
}
}
}
/*
==================
CalcPVS
==================
*/
void CalcPVS( void )
{
int i;
Msg( "Building PVS...\n" );
RunThreadsOnIndividual( numportals * 2, true, BasePortalVis );
SortPortals();
CalcPortalVis();
// assemble the leaf vis lists by oring and compressing the portal lists
for( i = 0; i < portalclusters; i++ )
ClusterMerge (i);
Msg( "Average clusters visible: %i\n", totalvis / portalclusters );
}
void SetPortalSphere (visportal_t *p)
{
int i;
vec3_t total, dist;
viswinding_t *w;
float r, bestr;
w = p->winding;
VectorCopy (vec3_origin, total);
for (i=0 ; i<w->numpoints ; i++)
{
VectorAdd (total, w->points[i], total);
}
for (i=0 ; i<3 ; i++)
total[i] /= w->numpoints;
bestr = 0;
for (i=0 ; i<w->numpoints ; i++)
{
VectorSubtract (w->points[i], total, dist);
r = VectorLength (dist);
if (r > bestr)
bestr = r;
}
VectorCopy (total, p->origin);
p->radius = bestr;
}
/*
============
LoadPortals
============
*/
void LoadPortals( void )
{
visportal_t *p;
leaf_t *l;
char magic[80];
char path[MAX_SYSPATH];
int numpoints;
script_t *prtfile;
viswinding_t *w;
int i, j, leafnums[2];
visplane_t plane;
com.sprintf( path, "maps/%s.prt", gs_filename );
prtfile = Com_OpenScript( path, NULL, 0 );
if( !prtfile ) Sys_Break( "LoadPortals: couldn't read %s\n", path );
MsgDev( D_NOTE, "reading %s\n", path );
Com_ReadString( prtfile, true, magic );
Com_ReadLong( prtfile, true, &portalclusters );
Com_ReadLong( prtfile, true, &numportals );
if( !portalclusters && !numportals ) Sys_Break( "LoadPortals: failed to read header\n" );
if( com.strcmp( magic, PORTALFILE )) Sys_Break( "LoadPortals: not a portal file\n" );
Msg( "%4i portalclusters\n", portalclusters );
Msg( "%4i numportals\n", numportals );
// these counts should take advantage of 64 bit systems automatically
leafbytes = ((portalclusters+63)&~63)>>3;
leaflongs = leafbytes/sizeof(long);
portalbytes = ((numportals*2+63)&~63)>>3;
portallongs = portalbytes/sizeof(long);
// each file portal is split into two memory portals
portals = Malloc(2 * numportals * sizeof(visportal_t));
leafs = Malloc(portalclusters*sizeof(leaf_t));
originalvismapsize = portalclusters*leafbytes;
uncompressedvis = Malloc(originalvismapsize);
vismap = vismap_p = dvisdata;
dvis->numclusters = portalclusters;
vismap_p = (byte *)&dvis->bitofs[portalclusters];
vismap_end = vismap + MAX_MAP_VISIBILITY;
for (i = 0, p = portals; i < numportals; i++)
{
Com_ReadLong( prtfile, true, &numpoints );
Com_ReadLong( prtfile, false, &leafnums[0] );
Com_ReadLong( prtfile, false, &leafnums[1] );
if( numpoints > MAX_POINTS_ON_WINDING )
Sys_Break( "LoadPortals: portal %i has too many points\n", i );
if(( uint )leafnums[0] > portalclusters || (uint)leafnums[1] > portalclusters )
Sys_Break( "LoadPortals: reading portal %i\n", i );
w = p->winding = NewVisWinding( numpoints );
w->original = true;
w->numpoints = numpoints;
for( j = 0; j < numpoints; j++ )
{
token_t token;
vec3_t v;
int k;
// scanf into double, then assign to float
// so we don't care what size float is
Com_ReadToken( prtfile, 0, &token ); // get '(' symbol
if( com.stricmp( token.string, "(" ))
Sys_Break( "LoadPortals: not found ( reading portal %i\n", i );
Com_ReadFloat( prtfile, false, &v[0] );
Com_ReadFloat( prtfile, false, &v[1] );
Com_ReadFloat( prtfile, false, &v[2] );
Com_ReadToken( prtfile, 0, &token );
if( com.stricmp( token.string, ")" ))
Sys_Error( "LoadPortals: not found ) reading portal %i", i );
for (k = 0; k < 3; k++) w->points[j][k] = v[k];
}
// calc plane
PlaneFromWinding (w, &plane);
// create forward portal
l = &leafs[leafnums[0]];
if (l->numportals == MAX_PORTALS_ON_LEAF) Sys_Error ("Leaf with too many portals");
l->portals[l->numportals] = p;
l->numportals++;
p->winding = w;
VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
p->plane.dist = -plane.dist;
p->leaf = leafnums[1];
SetPortalSphere (p);
p++;
// create backwards portal
l = &leafs[leafnums[1]];
if (l->numportals == MAX_PORTALS_ON_LEAF) Sys_Error ("Leaf with too many portals");
l->portals[l->numportals] = p;
l->numportals++;
p->winding = NewVisWinding(w->numpoints);
p->winding->numpoints = w->numpoints;
for (j=0 ; j<w->numpoints ; j++)
{
VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
}
p->plane = plane;
p->leaf = leafnums[0];
SetPortalSphere (p);
p++;
}
Com_CloseScript( prtfile );
}
void ClusterPHS( int clusternum )
{
int j, k, l, index;
int bitbyte;
long *dest, *src;
byte *scan;
byte uncompressed[MAX_MAP_LEAFS/8];
byte compressed[MAX_MAP_LEAFS/8];
scan = uncompressedvis + clusternum * leafbytes;
Mem_Copy( uncompressed, scan, leafbytes );
for (j = 0; j < leafbytes; j++)
{
bitbyte = scan[j];
if (!bitbyte) continue;
for (k = 0; k < 8; k++)
{
if (! (bitbyte & (1<<k))) continue;
// OR this pvs row into the phs
index = ((j<<3)+k);
if (index >= portalclusters)
Sys_Error ("Bad bit in PVS"); // pad bits should be 0
src = (long *)(uncompressedvis + index*leafbytes);
dest = (long *)uncompressed;
for (l = 0; l < leaflongs; l++) ((long *)uncompressed)[l] |= src[l];
}
}
for (j = 0; j < portalclusters; j++)
if (uncompressed[j>>3] & (1<<(j&7)) )
totalphs++;
// compress the bit string
j = CompressVis( uncompressed, compressed );
dest = (long *)vismap_p;
vismap_p += j;
if( vismap_p > vismap_end )
Sys_Error( "Vismap expansion overflow\n" );
dvis->bitofs[clusternum][DVIS_PHS] = (byte *)dest - vismap;
Mem_Copy( dest, compressed, j );
}
/*
================
CalcPHS
Calculate the PHS (Potentially Hearable Set)
by ORing together all the PVS visible from a leaf
================
*/
void CalcPHS( void )
{
Msg( "Building PHS...\n" );
RunThreadsOnIndividual( portalclusters, true, ClusterPHS );
Msg( "Average clusters hearable: %i\n", totalphs / portalclusters );
}
/*
===========
main
===========
*/
void WvisMain( void )
{
if( !LoadBSPFile( ))
{
// map not exist, create it
WbspMain();
LoadBSPFile();
}
if( numnodes == 0 || numsurfaces == 0 )
Sys_Break( "Empty map %s.bsp\n", gs_filename );
if( bsp_parms & BSPLIB_MAKEVIS )
{
Msg( "\n---- vis ---- [%s]\n", (bsp_parms & BSPLIB_FULLCOMPILE) ? "full" : "fast" );
LoadPortals();
CalcPVS();
CalcPHS();
visdatasize = vismap_p - dvisdata;
MsgDev( D_INFO, "visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize * 2 );
}
if( bsp_parms & BSPLIB_MAKESOUND )
{
CalcAmbientSounds();
}
WriteBSPFile();
}

367
xtools/bsplib/scriplib.c Normal file
View File

@ -0,0 +1,367 @@
/*
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.c
#include "q3map2.h"
// OLD STUFF, REWROTE WITH USING Com_ReadToken
/*
=============================================================================
PARSING STUFF
=============================================================================
*/
typedef struct
{
char filename[1024];
char *buffer,*script_p,*end_p;
int line;
} scriptstate_t;
#define MAX_INCLUDES 8
scriptstate_t scriptstack[MAX_INCLUDES];
scriptstate_t *script;
int scriptline;
char token[MAX_SYSPATH];
bool endofscript;
bool tokenready; // only true if UnGetToken was just called
/*
==============
AddScriptToStack
==============
*/
void AddScriptToStack (const char *filename, int index)
{
int size;
script++;
if (script == &scriptstack[MAX_INCLUDES])
Sys_Error ("script file exceeded MAX_INCLUDES");
strcpy (script->filename, filename );
script->buffer = FS_LoadFile( script->filename, &size );
if (size == -1)
Msg ("Script file %s was not found\n", script->filename);
else
{
if (index > 0)
Msg ("entering %s (%d)\n", script->filename, index+1);
else
Msg ("entering %s\n", script->filename);
}
script->line = 1;
script->script_p = script->buffer;
script->end_p = script->buffer + size;
}
/*
==============
LoadScriptFile
==============
*/
void LoadScriptFile (const char *filename, int index)
{
script = scriptstack;
AddScriptToStack (filename, index);
endofscript = false;
tokenready = false;
}
/*
==============
ParseFromMemory
==============
*/
void ParseFromMemory (char *buffer, int size)
{
script = scriptstack;
script++;
if (script == &scriptstack[MAX_INCLUDES])
Sys_Error ("script file exceeded MAX_INCLUDES");
strcpy (script->filename, "memory buffer" );
script->buffer = buffer;
script->line = 1;
script->script_p = script->buffer;
script->end_p = script->buffer + size;
endofscript = false;
tokenready = false;
}
/*
==============
UnGetToken
Signals that the current token was not used, and should be reported
for the next GetToken. Note that
GetToken (true);
UnGetToken ();
GetToken (false);
could cross a line boundary.
==============
*/
void UnGetToken (void)
{
tokenready = true;
}
bool EndOfScript (bool crossline)
{
if (!crossline)
Sys_Error ("Line %i is incomplete\n",scriptline);
if (!strcmp (script->filename, "memory buffer"))
{
endofscript = true;
return false;
}
if( script->buffer == NULL )
Msg( "WARNING: Attempt to free already freed script buffer\n" );
else
Mem_Free( script->buffer );
script->buffer = NULL;
if (script == scriptstack+1)
{
endofscript = true;
return false;
}
script--;
scriptline = script->line;
Msg ("returning to %s\n", script->filename);
return GetToken (crossline);
}
/*
==============
GetToken
==============
*/
bool GetToken (bool crossline)
{
char *token_p;
/* ydnar: dummy testing */
if( script == NULL || script->buffer == NULL )
return false;
if (tokenready) // is a token already waiting?
{
tokenready = false;
return true;
}
if ((script->script_p >= script->end_p) || (script->script_p == NULL))
return EndOfScript (crossline);
//
// skip space
//
skipspace:
while (*script->script_p <= 32)
{
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
if (*script->script_p++ == '\n')
{
if (!crossline)
Sys_Error ("Line %i is incomplete\n",scriptline);
script->line++;
scriptline = script->line;
}
}
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
// ; # // comments
if (*script->script_p == ';' || *script->script_p == '#'
|| ( script->script_p[0] == '/' && script->script_p[1] == '/') )
{
if (!crossline)
Sys_Error ("Line %i is incomplete\n",scriptline);
while (*script->script_p++ != '\n')
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
script->line++;
scriptline = script->line;
goto skipspace;
}
// /* */ comments
if (script->script_p[0] == '/' && script->script_p[1] == '*')
{
if (!crossline)
Sys_Error ("Line %i is incomplete\n",scriptline);
script->script_p+=2;
while (script->script_p[0] != '*' && script->script_p[1] != '/')
{
if ( *script->script_p == '\n' )
{
script->line++;
scriptline = script->line;
}
script->script_p++;
if (script->script_p >= script->end_p)
return EndOfScript (crossline);
}
script->script_p += 2;
goto skipspace;
}
//
// copy token
//
token_p = token;
if (*script->script_p == '"')
{
// quoted token
script->script_p++;
while (*script->script_p != '"')
{
*token_p++ = *script->script_p++;
if (script->script_p == script->end_p)
break;
if (token_p == &token[MAX_SYSPATH])
Sys_Error ("Token too large on line %i\n",scriptline);
}
script->script_p++;
}
else // regular token
while ( *script->script_p > 32 && *script->script_p != ';')
{
*token_p++ = *script->script_p++;
if (script->script_p == script->end_p)
break;
if (token_p == &token[MAX_SYSPATH])
Sys_Error ("Token too large on line %i\n",scriptline);
}
*token_p = 0;
if (!strcmp (token, "$include"))
{
GetToken (false);
AddScriptToStack (token, 0);
return GetToken (crossline);
}
return true;
}
/*
==============
TokenAvailable
Returns true if there is another token on the line
==============
*/
bool TokenAvailable (void) {
int oldLine, oldScriptLine;
bool r;
/* save */
oldLine = scriptline;
oldScriptLine = script->line;
/* test */
r = GetToken( true );
if ( !r ) {
return false;
}
UnGetToken();
if ( oldLine == scriptline ) {
return true;
}
/* restore */
//% scriptline = oldLine;
//% script->line = oldScriptLine;
return false;
}
//=====================================================================
void MatchToken( char *match ) {
GetToken( true );
if ( strcmp( token, match ) ) {
Sys_Error( "MatchToken( \"%s\" ) failed at line %i in file %s", match, scriptline, script->filename);
}
}
void Parse1DMatrix (int x, vec_t *m) {
int i;
MatchToken( "(" );
for (i = 0 ; i < x ; i++) {
GetToken( false );
m[i] = atof(token);
}
MatchToken( ")" );
}
void Parse2DMatrix (int y, int x, vec_t *m) {
int i;
MatchToken( "(" );
for (i = 0 ; i < y ; i++) {
Parse1DMatrix (x, m + i * x);
}
MatchToken( ")" );
}
void Parse3DMatrix (int z, int y, int x, vec_t *m) {
int i;
MatchToken( "(" );
for (i = 0 ; i < z ; i++) {
Parse2DMatrix (y, x, m + i * x*y);
}
MatchToken( ")" );
}

File diff suppressed because it is too large Load Diff

3596
xtools/bsplib/surface.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,444 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define SURFACE_EXTRA_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
ydnar: srf file module
------------------------------------------------------------------------------- */
typedef struct surfaceExtra_s
{
mapDrawSurface_t *mds;
shaderInfo_t *si;
int parentSurfaceNum;
int entityNum;
int castShadows, recvShadows;
int sampleSize;
float longestCurve;
vec3_t lightmapAxis;
}
surfaceExtra_t;
#define GROW_SURFACE_EXTRAS 1024
int numSurfaceExtras = 0;
int maxSurfaceExtras = 0;
surfaceExtra_t *surfaceExtras;
surfaceExtra_t seDefault = { NULL, NULL, -1, 0, WORLDSPAWN_CAST_SHADOWS, WORLDSPAWN_RECV_SHADOWS, 0, 0, { 0, 0, 0 } };
/*
AllocSurfaceExtra()
allocates a new extra storage
*/
static surfaceExtra_t *AllocSurfaceExtra( void )
{
surfaceExtra_t *se;
/* enough space? */
if( numSurfaceExtras >= maxSurfaceExtras )
{
/* reallocate more room */
maxSurfaceExtras += GROW_SURFACE_EXTRAS;
se = Malloc( maxSurfaceExtras * sizeof( surfaceExtra_t ) );
if( surfaceExtras != NULL )
{
Mem_Copy( se, surfaceExtras, numSurfaceExtras * sizeof( surfaceExtra_t ) );
Mem_Free( surfaceExtras );
}
surfaceExtras = se;
}
/* add another */
se = &surfaceExtras[ numSurfaceExtras ];
numSurfaceExtras++;
Mem_Copy( se, &seDefault, sizeof( surfaceExtra_t ) );
/* return it */
return se;
}
/*
SetDefaultSampleSize()
sets the default lightmap sample size
*/
void SetDefaultSampleSize( int sampleSize )
{
seDefault.sampleSize = sampleSize;
}
/*
SetSurfaceExtra()
stores extra (q3map2) data for the specific numbered drawsurface
*/
void SetSurfaceExtra( mapDrawSurface_t *ds, int num )
{
surfaceExtra_t *se;
/* dummy check */
if( ds == NULL || num < 0 )
return;
/* get a new extra */
se = AllocSurfaceExtra();
/* copy out the relevant bits */
se->mds = ds;
se->si = ds->shaderInfo;
se->parentSurfaceNum = ds->parent != NULL ? ds->parent->outputNum : -1;
se->entityNum = ds->entityNum;
se->castShadows = ds->castShadows;
se->recvShadows = ds->recvShadows;
se->sampleSize = ds->sampleSize;
se->longestCurve = ds->longestCurve;
VectorCopy( ds->lightmapAxis, se->lightmapAxis );
/* debug code */
//% MsgDev( D_NOTE, "SetSurfaceExtra(): entityNum = %d\n", ds->entityNum );
}
/*
GetSurfaceExtra*()
getter functions for extra surface data
*/
static surfaceExtra_t *GetSurfaceExtra( int num )
{
if( num < 0 || num >= numSurfaceExtras )
return &seDefault;
return &surfaceExtras[ num ];
}
shaderInfo_t *GetSurfaceExtraShaderInfo( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->si;
}
int GetSurfaceExtraParentSurfaceNum( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->parentSurfaceNum;
}
int GetSurfaceExtraEntityNum( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->entityNum;
}
int GetSurfaceExtraCastShadows( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->castShadows;
}
int GetSurfaceExtraRecvShadows( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->recvShadows;
}
int GetSurfaceExtraSampleSize( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->sampleSize;
}
float GetSurfaceExtraLongestCurve( int num )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
return se->longestCurve;
}
void GetSurfaceExtraLightmapAxis( int num, vec3_t lightmapAxis )
{
surfaceExtra_t *se = GetSurfaceExtra( num );
VectorCopy( se->lightmapAxis, lightmapAxis );
}
/*
WriteSurfaceExtraFile()
writes out a surface info file (<map>.srf)
*/
void WriteSurfaceExtraFile( const char *path )
{
char srfPath[ 1024 ];
file_t *sf;
surfaceExtra_t *se;
int i;
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' )
return;
/* note it */
MsgDev( D_NOTE, "--- WriteSurfaceExtraFile ---\n" );
/* open the file */
com.strcpy( srfPath, path );
FS_StripExtension( srfPath );
com.strcat( srfPath, ".srf" );
Msg( "Writing %s\n", srfPath );
sf = FS_Open( srfPath, "w" );
if( sf == NULL ) Sys_Error( "Error opening %s for writing", srfPath );
/* lap through the extras list */
for( i = -1; i < numSurfaceExtras; i++ )
{
/* get extra */
se = GetSurfaceExtra( i );
/* default or surface num? */
if( i < 0 )
FS_Printf( sf, "default" );
else
FS_Printf( sf, "%d", i );
/* valid map drawsurf? */
if( se->mds == NULL )
FS_Printf( sf, "\n" );
else
{
FS_Printf( sf, " // %s V: %d I: %d %s\n",
surfaceTypes[ se->mds->type ],
se->mds->numVerts,
se->mds->numIndexes,
(se->mds->planar ? "planar" : "") );
}
/* open braces */
FS_Printf( sf, "{\n" );
/* shader */
if( se->si != NULL )
FS_Printf( sf, "\tshader %s\n", se->si->shader );
/* parent surface number */
if( se->parentSurfaceNum != seDefault.parentSurfaceNum )
FS_Printf( sf, "\tparent %d\n", se->parentSurfaceNum );
/* entity number */
if( se->entityNum != seDefault.entityNum )
FS_Printf( sf, "\tentity %d\n", se->entityNum );
/* cast shadows */
if( se->castShadows != seDefault.castShadows || se == &seDefault )
FS_Printf( sf, "\tcastShadows %d\n", se->castShadows );
/* recv shadows */
if( se->recvShadows != seDefault.recvShadows || se == &seDefault )
FS_Printf( sf, "\treceiveShadows %d\n", se->recvShadows );
/* lightmap sample size */
if( se->sampleSize != seDefault.sampleSize || se == &seDefault )
FS_Printf( sf, "\tsampleSize %d\n", se->sampleSize );
/* longest curve */
if( se->longestCurve != seDefault.longestCurve || se == &seDefault )
FS_Printf( sf, "\tlongestCurve %f\n", se->longestCurve );
/* lightmap axis vector */
if( VectorCompare( se->lightmapAxis, seDefault.lightmapAxis ) == false )
FS_Printf( sf, "\tlightmapAxis ( %f %f %f )\n", se->lightmapAxis[ 0 ], se->lightmapAxis[ 1 ], se->lightmapAxis[ 2 ] );
/* close braces */
FS_Printf( sf, "}\n\n" );
}
/* close the file */
FS_Close( sf );
}
/*
LoadSurfaceExtraFile()
reads a surface info file (<map>.srf)
*/
void LoadSurfaceExtraFile( const char *path )
{
char srfPath[ 1024 ];
surfaceExtra_t *se;
int surfaceNum, size;
byte *buffer;
/* dummy check */
if( path == NULL || path[ 0 ] == '\0' )
return;
/* load the file */
com.strcpy( srfPath, path );
FS_StripExtension( srfPath );
com.strcat( srfPath, ".srf" );
Msg( "Loading %s\n", srfPath );
buffer = (void *)FS_LoadFile( srfPath, &size );
if( size <= 0 )
{
MsgDev( D_WARN, "Unable to find surface file %s, using defaults.\n", srfPath );
return;
}
/* parse the file */
ParseFromMemory( buffer, size );
/* tokenize it */
while( 1 )
{
/* test for end of file */
if( !GetToken( true ) )
break;
/* default? */
if( !com.stricmp( token, "default" ) )
se = &seDefault;
/* surface number */
else
{
surfaceNum = atoi( token );
if( surfaceNum < 0 || surfaceNum > MAX_MAP_DRAW_SURFS )
Sys_Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath, scriptline, surfaceNum );
while( surfaceNum >= numSurfaceExtras )
se = AllocSurfaceExtra();
se = &surfaceExtras[ surfaceNum ];
}
/* handle { } section */
if( !GetToken( true ) || strcmp( token, "{" ) )
Sys_Error( "ReadSurfaceExtraFile(): %s, line %d: { not found", srfPath, scriptline );
while( 1 )
{
if( !GetToken( true ) )
break;
if( !strcmp( token, "}" ) )
break;
/* shader */
if( !com.stricmp( token, "shader" ) )
{
GetToken( false );
se->si = ShaderInfoForShader( token );
}
/* parent surface number */
else if( !com.stricmp( token, "parent" ) )
{
GetToken( false );
se->parentSurfaceNum = atoi( token );
}
/* entity number */
else if( !com.stricmp( token, "entity" ) )
{
GetToken( false );
se->entityNum = atoi( token );
}
/* cast shadows */
else if( !com.stricmp( token, "castShadows" ) )
{
GetToken( false );
se->castShadows = atoi( token );
}
/* recv shadows */
else if( !com.stricmp( token, "receiveShadows" ) )
{
GetToken( false );
se->recvShadows = atoi( token );
}
/* lightmap sample size */
else if( !com.stricmp( token, "sampleSize" ) )
{
GetToken( false );
se->sampleSize = atoi( token );
}
/* longest curve */
else if( !com.stricmp( token, "longestCurve" ) )
{
GetToken( false );
se->longestCurve = atof( token );
}
/* lightmap axis vector */
else if( !com.stricmp( token, "lightmapAxis" ) )
Parse1DMatrix( 3, se->lightmapAxis );
/* ignore all other tokens on the line */
while( TokenAvailable() )
GetToken( false );
}
}
/* free the buffer */
Mem_Free( buffer );
}

View File

@ -0,0 +1,327 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
Foliage code for Wolfenstein: Enemy Territory by ydnar@splashdamage.com
------------------------------------------------------------------------------- */
/* marker */
#define SURFACE_FOLIAGE_C
/* dependencies */
#include "q3map2.h"
#include "matrix_lib.h"
#define MAX_FOLIAGE_INSTANCES 8192
static int numFoliageInstances;
static foliageInstance_t foliageInstances[ MAX_FOLIAGE_INSTANCES ];
/*
SubdivideFoliageTriangle_r()
recursively subdivides a triangle until the triangle is smaller than
the desired density, then pseudo-randomly sets a point
*/
static void SubdivideFoliageTriangle_r( mapDrawSurface_t *ds, foliage_t *foliage, bspDrawVert_t **tri )
{
bspDrawVert_t mid, *tri2[ 3 ];
int max;
/* limit test */
if( numFoliageInstances >= MAX_FOLIAGE_INSTANCES )
return;
/* plane test */
{
vec4_t plane;
/* make a plane */
if( !PlaneFromPoints( plane, tri[ 0 ]->xyz, tri[ 1 ]->xyz, tri[ 2 ]->xyz ) )
return;
/* if normal is too far off vertical, then don't place an instance */
if( plane[ 2 ] < 0.5f )
return;
}
/* subdivide calc */
{
int i;
float *a, *b, dx, dy, dz, dist, maxDist;
foliageInstance_t *fi;
/* get instance */
fi = &foliageInstances[ numFoliageInstances ];
/* find the longest edge and split it */
max = -1;
maxDist = 0.0f;
VectorClear( fi->xyz );
VectorClear( fi->normal );
for( i = 0; i < 3; i++ )
{
/* get verts */
a = tri[ i ]->xyz;
b = tri[ (i + 1) % 3 ]->xyz;
/* get dists */
dx = a[ 0 ] - b[ 0 ];
dy = a[ 1 ] - b[ 1 ];
dz = a[ 2 ] - b[ 2 ];
dist = (dx * dx) + (dy * dy) + (dz * dz);
/* longer? */
if( dist > maxDist )
{
maxDist = dist;
max = i;
}
/* add to centroid */
VectorAdd( fi->xyz, tri[ i ]->xyz, fi->xyz );
VectorAdd( fi->normal, tri[ i ]->normal, fi->normal );
}
/* is the triangle small enough? */
if( maxDist <= (foliage->density * foliage->density) )
{
float alpha, odds, r;
/* get average alpha */
if( foliage->inverseAlpha == 2 )
alpha = 1.0f;
else
{
alpha = ((float) tri[ 0 ]->color[ 0 ][ 3 ] + (float) tri[ 1 ]->color[ 0 ][ 3 ] + (float) tri[ 2 ]->color[ 0 ][ 3 ]) / 765.0f;
if( foliage->inverseAlpha == 1 )
alpha = 1.0f - alpha;
if( alpha < 0.75f )
return;
}
/* roll the dice */
odds = foliage->odds * alpha;
r = Random();
if( r > odds )
return;
/* scale centroid */
VectorScale( fi->xyz, 0.33333333f, fi->xyz );
if( VectorNormalizeLength( fi->normal ) == 0.0f )
return;
/* add to count and return */
numFoliageInstances++;
return;
}
}
/* split the longest edge and map it */
LerpDrawVert( tri[ max ], tri[ (max + 1) % 3 ], &mid );
/* recurse to first triangle */
VectorCopy( tri, tri2 );
tri2[ max ] = &mid;
SubdivideFoliageTriangle_r( ds, foliage, tri2 );
/* recurse to second triangle */
VectorCopy( tri, tri2 );
tri2[ (max + 1) % 3 ] = &mid;
SubdivideFoliageTriangle_r( ds, foliage, tri2 );
}
/*
GenFoliage()
generates a foliage file for a bsp
*/
void Foliage( mapDrawSurface_t *src )
{
int i, j, k, x, y, pw[ 5 ], r, oldNumMapDrawSurfs;
mapDrawSurface_t *ds;
shaderInfo_t *si;
foliage_t *foliage;
mesh_t srcMesh, *subdivided, *mesh;
bspDrawVert_t *verts, *dv[ 3 ], *fi;
vec3_t scale;
matrix4x4 transform;
/* get shader */
si = src->shaderInfo;
if( si == NULL || si->foliage == NULL )
return;
/* do every foliage */
for( foliage = si->foliage; foliage != NULL; foliage = foliage->next )
{
/* zero out */
numFoliageInstances = 0;
/* map the surface onto the lightmap origin/cluster/normal buffers */
switch( src->type )
{
case SURFACE_META:
case SURFACE_FORCED_META:
case SURFACE_TRIANGLES:
/* get verts */
verts = src->verts;
/* map the triangles */
for( i = 0; i < src->numIndexes; i += 3 )
{
dv[ 0 ] = &verts[ src->indexes[ i ] ];
dv[ 1 ] = &verts[ src->indexes[ i + 1 ] ];
dv[ 2 ] = &verts[ src->indexes[ i + 2 ] ];
SubdivideFoliageTriangle_r( src, foliage, dv );
}
break;
case SURFACE_PATCH:
/* make a mesh from the drawsurf */
srcMesh.width = src->patchWidth;
srcMesh.height = src->patchHeight;
srcMesh.verts = src->verts;
subdivided = SubdivideMesh( srcMesh, 8, 512 );
/* fit it to the curve and remove colinear verts on rows/columns */
PutMeshOnCurve( *subdivided );
mesh = RemoveLinearMeshColumnsRows( subdivided );
FreeMesh( subdivided );
/* get verts */
verts = mesh->verts;
/* map the mesh quads */
for( y = 0; y < (mesh->height - 1); y++ )
{
for( x = 0; x < (mesh->width - 1); x++ )
{
/* set indexes */
pw[ 0 ] = x + (y * mesh->width);
pw[ 1 ] = x + ((y + 1) * mesh->width);
pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
pw[ 3 ] = x + 1 + (y * mesh->width);
pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
/* set radix */
r = (x + y) & 1;
/* get drawverts and map first triangle */
dv[ 0 ] = &verts[ pw[ r + 0 ] ];
dv[ 1 ] = &verts[ pw[ r + 1 ] ];
dv[ 2 ] = &verts[ pw[ r + 2 ] ];
SubdivideFoliageTriangle_r( src, foliage, dv );
/* get drawverts and map second triangle */
dv[ 0 ] = &verts[ pw[ r + 0 ] ];
dv[ 1 ] = &verts[ pw[ r + 2 ] ];
dv[ 2 ] = &verts[ pw[ r + 3 ] ];
SubdivideFoliageTriangle_r( src, foliage, dv );
}
}
/* free the mesh */
FreeMesh( mesh );
break;
default:
break;
}
/* any origins? */
if( numFoliageInstances < 1 )
continue;
/* remember surface count */
oldNumMapDrawSurfs = numMapDrawSurfs;
/* set transform matrix */
VectorSet( scale, foliage->scale, foliage->scale, foliage->scale );
Matrix4x4_CreateScale3( transform, scale[0], scale[1], scale[2] );
/* add the model to the bsp */
InsertModel( foliage->model, 0, transform, NULL, NULL, src->entityNum, src->castShadows, src->recvShadows, 0, src->lightmapScale );
/* walk each new surface */
for( i = oldNumMapDrawSurfs; i < numMapDrawSurfs; i++ )
{
/* get surface */
ds = &mapDrawSurfs[ i ];
/* set up */
ds->type = SURFACE_FOLIAGE;
ds->numFoliageInstances = numFoliageInstances;
/* a wee hack */
ds->patchWidth = ds->numFoliageInstances;
ds->patchHeight = ds->numVerts;
/* set fog to be same as source surface */
ds->fogNum = src->fogNum;
/* add a drawvert for every instance */
verts = Malloc( (ds->numVerts + ds->numFoliageInstances) * sizeof( *verts ) );
Mem_Copy( verts, ds->verts, ds->numVerts * sizeof( *verts ) );
Mem_Free( ds->verts );
ds->verts = verts;
/* copy the verts */
for( j = 0; j < ds->numFoliageInstances; j++ )
{
/* get vert (foliage instance) */
fi = &ds->verts[ ds->numVerts + j ];
/* copy xyz and normal */
VectorCopy( foliageInstances[ j ].xyz, fi->xyz );
VectorCopy( foliageInstances[ j ].normal, fi->normal );
/* ydnar: set color */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
fi->color[ k ][ 0 ] = 255;
fi->color[ k ][ 1 ] = 255;
fi->color[ k ][ 2 ] = 255;
fi->color[ k ][ 3 ] = 255;
}
}
/* increment */
ds->numVerts += ds->numFoliageInstances;
}
}
}

129
xtools/bsplib/surface_fur.c Normal file
View File

@ -0,0 +1,129 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define SURFACE_FUR_C
/* dependencies */
#include "q3map2.h"
/* -------------------------------------------------------------------------------
ydnar: fur module
------------------------------------------------------------------------------- */
/*
Fur()
runs the fur processing algorithm on a map drawsurface
*/
void Fur( mapDrawSurface_t *ds )
{
int i, j, k, numLayers;
float offset, fade, a;
mapDrawSurface_t *fur;
bspDrawVert_t *dv;
/* dummy check */
if( ds == NULL || ds->fur || ds->shaderInfo->furNumLayers < 1 )
return;
/* get basic info */
numLayers = ds->shaderInfo->furNumLayers;
offset = ds->shaderInfo->furOffset;
fade = ds->shaderInfo->furFade * 255.0f;
/* debug code */
//% MsgDev( D_NOTE, "Fur(): layers: %d offset: %f fade: %f %s\n",
//% numLayers, offset, fade, ds->shaderInfo->shader );
/* initial offset */
for( j = 0; j < ds->numVerts; j++ )
{
/* get surface vert */
dv = &ds->verts[ j ];
/* offset is scaled by original vertex alpha */
a = (float) dv->color[ 0 ][ 3 ] / 255.0;
/* offset it */
VectorMA( dv->xyz, (offset * a), dv->normal, dv->xyz );
}
/* wash, rinse, repeat */
for( i = 1; i < numLayers; i++ )
{
/* clone the surface */
fur = CloneSurface( ds, ds->shaderInfo );
if( fur == NULL )
return;
/* set it to fur */
fur->fur = true;
/* walk the verts */
for( j = 0; j < fur->numVerts; j++ )
{
/* get surface vert */
dv = &ds->verts[ j ];
/* offset is scaled by original vertex alpha */
a = (float) dv->color[ 0 ][ 3 ] / 255.0;
/* get fur vert */
dv = &fur->verts[ j ];
/* offset it */
VectorMA( dv->xyz, (offset * a * i), dv->normal, dv->xyz );
/* fade alpha */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
a = (float) dv->color[ k ][ 3 ] - fade;
if( a > 255.0f )
dv->color[ k ][ 3 ] = 255;
else if( a < 0 )
dv->color[ k ][ 3 ] = 0;
else
dv->color[ k ][ 3 ] = a;
}
}
}
}

1665
xtools/bsplib/surface_meta.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,214 +0,0 @@
#include "bsplib.h"
#include "byteorder.h"
//==========================================================================
int FindMiptex( const char *name )
{
rgbdata_t *image;
int i;
if(!com.strlen( name )) return 0;
for( i = 0; i < numshaders; i++ )
{
if( !com.stricmp( dshaders[i].name, name ))
return i;
}
// allocate a new texture
if( numshaders == MAX_MAP_SHADERS )
Sys_Break( "MAX_MAP_SHADERS limit exceeded\n" );
// alloc new texture
com.strncpy( dshaders[i].name, name, sizeof( dshaders[i].name ));
image = FS_LoadImage( name, NULL, 0 );
if( image )
{
// save texture dimensions that can be used
// for replacing original textures with HQ images
dshaders[i].size[0] = image->width;
dshaders[i].size[1] = image->height;
FS_FreeImage( image );
}
else dshaders[i].size[0] = dshaders[i].size[1] = -1; // technically an error
return numshaders++;
}
/*
==================
textureAxisFromPlane
==================
*/
vec3_t baseaxis[18] =
{
{0,0,1}, {1,0,0}, {0,-1,0}, // floor
{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
{0,-1,0}, {1,0,0}, {0,0,-1}, // north wall
};
void TextureAxisFromPlane( plane_t *plane, vec3_t xv, vec3_t yv )
{
int bestaxis = 0;
vec_t dot, best = 0;
int i;
for( i = 0; i < 6; i++ )
{
dot = DotProduct( plane->normal, baseaxis[i*3] );
if (dot > best)
{
best = dot;
bestaxis = i;
}
}
VectorCopy( baseaxis[bestaxis*3+1], xv );
VectorCopy( baseaxis[bestaxis*3+2], yv );
}
int TexinfoForBrushTexture( plane_t *plane, brush_texture_t *bt, vec3_t origin )
{
vec_t ang, sinv, cosv;
vec3_t vecs[2];
int i, j, k;
dtexinfo_t tx, *tc;
int sv, tv;
vec_t ns, nt;
if( !bt->name[0] ) return 0;
Mem_Set( &tx, 0, sizeof( tx ));
tx.shadernum = FindMiptex( bt->name );
if( bt->brush_type == BRUSH_QUARK )
{
Mem_Copy( tx.vecs, bt->vects.quark.vecs, sizeof( tx.vecs ));
if(!VectorIsNull( origin ))
{
tx.vecs[0][3] += DotProduct( origin, tx.vecs[0] );
tx.vecs[1][3] += DotProduct( origin, tx.vecs[1] );
}
dshaders[tx.shadernum].contentFlags = bt->contents;
dshaders[tx.shadernum].surfaceFlags = bt->flags;
tx.value = bt->value;
}
else if( bt->brush_type != BRUSH_RADIANT )
{
if( bt->brush_type == BRUSH_WORLDCRAFT_21 )
TextureAxisFromPlane( plane, vecs[0], vecs[1] );
if( !bt->vects.hammer.scale[0] ) bt->vects.hammer.scale[0] = 1.0f;
if( !bt->vects.hammer.scale[1] ) bt->vects.hammer.scale[1] = 1.0f;
if( bt->brush_type == BRUSH_WORLDCRAFT_21 )
{
// rotate axis
if( bt->vects.hammer.rotate == 0 )
{
sinv = 0;
cosv = 1;
}
else if( bt->vects.hammer.rotate == 90 )
{
sinv = 1;
cosv = 0;
}
else if( bt->vects.hammer.rotate == 180 )
{
sinv = 0;
cosv = -1;
}
else if( bt->vects.hammer.rotate == 270 )
{
sinv = -1;
cosv = 0;
}
else
{
ang = bt->vects.hammer.rotate / 180 * M_PI;
sinv = com.sin( ang );
cosv = com.cos( ang );
}
if( vecs[0][0] ) sv = 0;
else if( vecs[0][1] ) sv = 1;
else sv = 2;
if( vecs[1][0] ) tv = 0;
else if( vecs[1][1] ) tv = 1;
else tv = 2;
for( i = 0; i < 2; i++ )
{
ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
vecs[i][sv] = ns;
vecs[i][tv] = nt;
}
for( i = 0; i < 2; i++ )
for( j = 0; j < 3; j++ )
tx.vecs[i][j] = vecs[i][j] / bt->vects.hammer.scale[i];
}
else if( bt->brush_type == BRUSH_WORLDCRAFT_22 )
{
vec_t scale;
scale = 1 / bt->vects.hammer.scale[0];
VectorScale( bt->vects.hammer.UAxis, scale, tx.vecs[0] );
scale = 1 / bt->vects.hammer.scale[1];
VectorScale( bt->vects.hammer.VAxis, scale, tx.vecs[1] );
}
tx.vecs[0][3] = bt->vects.hammer.shift[0] + DotProduct( origin, tx.vecs[0] );
tx.vecs[1][3] = bt->vects.hammer.shift[1] + DotProduct( origin, tx.vecs[1] );
}
else if( bt->brush_type == BRUSH_RADIANT )
{
VectorCopy( bt->vects.radiant.matrix[0], tx.vecs[0] );
VectorCopy( bt->vects.radiant.matrix[1], tx.vecs[1] );
if(!VectorIsNull( origin ))
{
tx.vecs[0][3] += DotProduct( origin, tx.vecs[0] );
tx.vecs[1][3] += DotProduct( origin, tx.vecs[1] );
}
dshaders[tx.shadernum].contentFlags = bt->contents;
dshaders[tx.shadernum].surfaceFlags = bt->flags;
tx.value = bt->value;
}
dshaders[tx.shadernum].contentFlags = bt->contents;
dshaders[tx.shadernum].surfaceFlags = bt->flags;
tx.value = bt->value;
// find the texinfo
tc = texinfo;
for( i = 0; i < numtexinfo; i++, tc++ )
{
if( tc->value != tx.value ) continue;
for( j = 0; j < 2; j++ )
{
if( tc->shadernum != tx.shadernum )
goto skip;
for( k = 0; k < 4; k++ )
{
if( tc->vecs[j][k] != tx.vecs[j][k] )
goto skip;
}
}
return i;
skip:;
}
// register new texinfo
*tc = tx;
return numtexinfo++;
}

751
xtools/bsplib/tjunction.c Normal file
View File

@ -0,0 +1,751 @@
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define TJUNCTION_C
/* dependencies */
#include "q3map2.h"
typedef struct edgePoint_s
{
float intercept;
vec3_t xyz;
struct edgePoint_s *prev, *next;
} edgePoint_t;
typedef struct edgeLine_s
{
vec3_t normal1;
float dist1;
vec3_t normal2;
float dist2;
vec3_t origin;
vec3_t dir;
edgePoint_t chain; // unused element of doubly linked list
} edgeLine_t;
typedef struct
{
float length;
bspDrawVert_t *dv[2];
} originalEdge_t;
#define MAX_ORIGINAL_EDGES 0x10000
originalEdge_t originalEdges[MAX_ORIGINAL_EDGES];
int numOriginalEdges;
#define MAX_EDGE_LINES 0x10000
edgeLine_t edgeLines[MAX_EDGE_LINES];
int numEdgeLines;
int c_degenerateEdges;
int c_addedVerts;
int c_totalVerts;
int c_natural, c_rotate, c_cant;
// these should be whatever epsilon we actually expect,
// plus SNAP_INT_TO_FLOAT
#define LINE_POSITION_EPSILON 0.25
#define POINT_ON_LINE_EPSILON 0.25
/*
====================
InsertPointOnEdge
====================
*/
void InsertPointOnEdge( vec3_t v, edgeLine_t *e )
{
vec3_t delta;
float d;
edgePoint_t *p, *scan;
VectorSubtract( v, e->origin, delta );
d = DotProduct( delta, e->dir );
p = Malloc( sizeof(edgePoint_t) );
p->intercept = d;
VectorCopy( v, p->xyz );
if ( e->chain.next == &e->chain ) {
e->chain.next = e->chain.prev = p;
p->next = p->prev = &e->chain;
return;
}
scan = e->chain.next;
for ( ; scan != &e->chain ; scan = scan->next ) {
d = p->intercept - scan->intercept;
if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) {
Mem_Free( p );
return; // the point is already set
}
if ( p->intercept < scan->intercept ) {
// insert here
p->prev = scan->prev;
p->next = scan;
scan->prev->next = p;
scan->prev = p;
return;
}
}
// add at the end
p->prev = scan->prev;
p->next = scan;
scan->prev->next = p;
scan->prev = p;
}
/*
====================
AddEdge
====================
*/
int AddEdge( vec3_t v1, vec3_t v2, bool createNonAxial )
{
int i;
edgeLine_t *e;
float d;
vec3_t dir;
VectorSubtract( v2, v1, dir );
d = VectorNormalizeLength( dir );
if ( d < 0.1 ) {
// if we added a 0 length vector, it would make degenerate planes
c_degenerateEdges++;
return -1;
}
if ( !createNonAxial )
{
if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 )
{
if ( numOriginalEdges == MAX_ORIGINAL_EDGES )
{
Sys_Error( "MAX_ORIGINAL_EDGES limit exceeded\n" );
}
originalEdges[ numOriginalEdges ].dv[0] = (bspDrawVert_t *)v1;
originalEdges[ numOriginalEdges ].dv[1] = (bspDrawVert_t *)v2;
originalEdges[ numOriginalEdges ].length = d;
numOriginalEdges++;
return -1;
}
}
for ( i = 0 ; i < numEdgeLines ; i++ ) {
e = &edgeLines[i];
d = DotProduct( v1, e->normal1 ) - e->dist1;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
d = DotProduct( v1, e->normal2 ) - e->dist2;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
d = DotProduct( v2, e->normal1 ) - e->dist1;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
d = DotProduct( v2, e->normal2 ) - e->dist2;
if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
continue;
}
// this is the edge
InsertPointOnEdge( v1, e );
InsertPointOnEdge( v2, e );
return i;
}
// create a new edge
if ( numEdgeLines >= MAX_EDGE_LINES ) {
Sys_Error( "MAX_EDGE_LINES limit exceeded\n" );
}
e = &edgeLines[ numEdgeLines ];
numEdgeLines++;
e->chain.next = e->chain.prev = &e->chain;
VectorCopy( v1, e->origin );
VectorCopy( dir, e->dir );
VectorVectors( e->dir, e->normal1, e->normal2 );
e->dist1 = DotProduct( e->origin, e->normal1 );
e->dist2 = DotProduct( e->origin, e->normal2 );
InsertPointOnEdge( v1, e );
InsertPointOnEdge( v2, e );
return numEdgeLines - 1;
}
/*
AddSurfaceEdges()
adds a surface's edges
*/
void AddSurfaceEdges( mapDrawSurface_t *ds )
{
int i;
for( i = 0; i < ds->numVerts; i++ )
{
/* save the edge number in the lightmap field so we don't need to look it up again */
ds->verts[i].lightmap[ 0 ][ 0 ] =
AddEdge( ds->verts[ i ].xyz, ds->verts[ (i + 1) % ds->numVerts ].xyz, false );
}
}
/*
ColinearEdge()
determines if an edge is colinear
*/
bool ColinearEdge( vec3_t v1, vec3_t v2, vec3_t v3 )
{
vec3_t midpoint, dir, offset, on;
float d;
VectorSubtract( v2, v1, midpoint );
VectorSubtract( v3, v1, dir );
d = VectorNormalizeLength( dir );
if ( d == 0 ) {
return false; // degenerate
}
d = DotProduct( midpoint, dir );
VectorScale( dir, d, on );
VectorSubtract( midpoint, on, offset );
d = VectorLength ( offset );
if ( d < 0.1 ) {
return true;
}
return false;
}
/*
====================
AddPatchEdges
Add colinear border edges, which will fix some classes of patch to
brush tjunctions
====================
*/
void AddPatchEdges( mapDrawSurface_t *ds ) {
int i;
float *v1, *v2, *v3;
for ( i = 0 ; i < ds->patchWidth - 2; i+=2 ) {
v1 = ds->verts[ i ].xyz;
v2 = ds->verts[ i + 1 ].xyz;
v3 = ds->verts[ i + 2 ].xyz;
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, false );
}
v1 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i ].xyz;
v2 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 1 ].xyz;
v3 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 2 ].xyz;
// if v2 is on the v1 to v3 line, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, false );
}
}
for ( i = 0 ; i < ds->patchHeight - 2 ; i+=2 ) {
v1 = ds->verts[ i * ds->patchWidth ].xyz;
v2 = ds->verts[ ( i + 1 ) * ds->patchWidth ].xyz;
v3 = ds->verts[ ( i + 2 ) * ds->patchWidth ].xyz;
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, false );
}
v1 = ds->verts[ ( ds->patchWidth - 1 ) + i * ds->patchWidth ].xyz;
v2 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 1 ) * ds->patchWidth ].xyz;
v3 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 2 ) * ds->patchWidth ].xyz;
// if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
if ( ColinearEdge( v1, v2, v3 ) ) {
AddEdge( v1, v3, false );
}
}
}
/*
====================
FixSurfaceJunctions
====================
*/
#define MAX_SURFACE_VERTS 256
void FixSurfaceJunctions( mapDrawSurface_t *ds )
{
int i, j, k;
edgeLine_t *e;
edgePoint_t *p;
int originalVerts;
int counts[MAX_SURFACE_VERTS];
int originals[MAX_SURFACE_VERTS];
int firstVert[MAX_SURFACE_VERTS];
bspDrawVert_t verts[MAX_SURFACE_VERTS], *v1, *v2;
int numVerts;
float start, end, frac, c;
vec3_t delta;
originalVerts = ds->numVerts;
numVerts = 0;
for ( i = 0 ; i < ds->numVerts ; i++ )
{
counts[i] = 0;
firstVert[i] = numVerts;
// copy first vert
if ( numVerts == MAX_SURFACE_VERTS )
{
Sys_Error( "MAX_SURFACE_VERTS limit exceded\n" );
}
verts[numVerts] = ds->verts[i];
originals[numVerts] = i;
numVerts++;
// check to see if there are any t junctions before the next vert
v1 = &ds->verts[i];
v2 = &ds->verts[ (i+1) % ds->numVerts ];
j = (int)ds->verts[i].lightmap[ 0 ][ 0 ];
if ( j == -1 ) {
continue; // degenerate edge
}
e = &edgeLines[ j ];
VectorSubtract( v1->xyz, e->origin, delta );
start = DotProduct( delta, e->dir );
VectorSubtract( v2->xyz, e->origin, delta );
end = DotProduct( delta, e->dir );
if( start < end )
{
p = e->chain.next;
}
else
{
p = e->chain.prev;
}
for( ; p != &e->chain; )
{
if( start < end )
{
if( p->intercept > end - ON_EPSILON )
{
break;
}
}
else
{
if( p->intercept < end + ON_EPSILON )
{
break;
}
}
if(( start < end && p->intercept > start + ON_EPSILON ) || ( start > end && p->intercept < start - ON_EPSILON ))
{
// insert this point
if( numVerts == MAX_SURFACE_VERTS )
{
Sys_Error( "MAX_SURFACE_VERTS limit exceeded\n" );
}
/* take the exact intercept point */
VectorCopy( p->xyz, verts[ numVerts ].xyz );
/* interpolate the texture coordinates */
frac = ( p->intercept - start ) / ( end - start );
for ( j = 0 ; j < 2 ; j++ ) {
verts[ numVerts ].st[j] = v1->st[j] +
frac * ( v2->st[j] - v1->st[j] );
}
/* copy the normal (FIXME: what about nonplanar surfaces? */
VectorCopy( v1->normal, verts[ numVerts ].normal );
/* ydnar: interpolate the color */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
for( j = 0; j < 4; j++ )
{
c = (float) v1->color[ k ][ j ] + frac * ((float) v2->color[ k ][ j ] - (float) v1->color[ k ][ j ]);
verts[ numVerts ].color[ k ][ j ] = (byte) (c < 255.0f ? c : 255);
}
}
/* next... */
originals[ numVerts ] = i;
numVerts++;
counts[ i ]++;
}
if( start < end )
{
p = p->next;
}
else
{
p = p->prev;
}
}
}
c_addedVerts += numVerts - ds->numVerts;
c_totalVerts += numVerts;
// FIXME: check to see if the entire surface degenerated
// after snapping
// rotate the points so that the initial vertex is between
// two non-subdivided edges
for ( i = 0 ; i < numVerts ; i++ )
{
if( originals[ (i+1) % numVerts ] == originals[ i ] )
{
continue;
}
j = (i + numVerts - 1 ) % numVerts;
k = (i + numVerts - 2 ) % numVerts;
if( originals[ j ] == originals[ k ] )
{
continue;
}
break;
}
if( i == 0 )
{
// fine the way it is
c_natural++;
ds->numVerts = numVerts;
ds->verts = Malloc( numVerts * sizeof( *ds->verts ) );
memcpy( ds->verts, verts, numVerts * sizeof( *ds->verts ) );
return;
}
if( i == numVerts )
{
// create a vertex in the middle to start the fan
c_cant++;
/*
memset ( &verts[numVerts], 0, sizeof( verts[numVerts] ) );
for ( i = 0 ; i < numVerts ; i++ ) {
for ( j = 0 ; j < 10 ; j++ ) {
verts[numVerts].xyz[j] += verts[i].xyz[j];
}
}
for ( j = 0 ; j < 10 ; j++ ) {
verts[numVerts].xyz[j] /= numVerts;
}
i = numVerts;
numVerts++;
*/
} else {
// just rotate the vertexes
c_rotate++;
}
ds->numVerts = numVerts;
ds->verts = Malloc( numVerts * sizeof( *ds->verts ) );
for ( j = 0 ; j < ds->numVerts ; j++ ) {
ds->verts[j] = verts[ ( j + i ) % ds->numVerts ];
}
}
/*
FixBrokenSurface() - ydnar
removes nearly coincident verts from a planar winding surface
returns false if the surface is broken
*/
extern void SnapWeldVector( vec3_t a, vec3_t b, vec3_t out );
#define DEGENERATE_EPSILON 0.1
int c_broken = 0;
bool FixBrokenSurface( mapDrawSurface_t *ds )
{
bool valid = true;
bspDrawVert_t *dv1, *dv2, avg;
int i, j, k;
float dist;
/* dummy check */
if( ds == NULL )
return false;
if( ds->type != SURFACE_FACE )
return false;
/* check all verts */
for( i = 0; i < ds->numVerts; i++ )
{
/* don't remove points if winding is a triangle */
if( ds->numVerts == 3 )
return valid;
/* get verts */
dv1 = &ds->verts[ i ];
dv2 = &ds->verts[ (i + 1) % ds->numVerts ];
/* degenerate edge? */
VectorSubtract( dv1->xyz, dv2->xyz, avg.xyz );
dist = VectorLength( avg.xyz );
if( dist < DEGENERATE_EPSILON )
{
valid = false;
MsgDev( D_WARN, "Degenerate T-junction edge found, fixing...\n" );
/* create an average drawvert */
/* ydnar 2002-01-26: added nearest-integer welding preference */
SnapWeldVector( dv1->xyz, dv2->xyz, avg.xyz );
VectorAdd( dv1->normal, dv2->normal, avg.normal );
VectorNormalize( avg.normal );
avg.st[ 0 ] = (dv1->st[ 0 ] + dv2->st[ 0 ]) * 0.5f;
avg.st[ 1 ] = (dv1->st[ 1 ] + dv2->st[ 1 ]) * 0.5f;
/* lightmap st/colors */
for( k = 0; k < MAX_LIGHTMAPS; k++ )
{
avg.lightmap[ k ][ 0 ] = (dv1->lightmap[ k ][ 0 ] + dv2->lightmap[ k ][ 0 ]) * 0.5f;
avg.lightmap[ k ][ 1 ] = (dv1->lightmap[ k ][ 1 ] + dv2->lightmap[ k ][ 1 ]) * 0.5f;
for( j = 0; j < 4; j++ )
avg.color[ k ][ j ] = (int) (dv1->color[ k ][ j ] + dv2->color[ k ][ j ]) >> 1;
}
/* ydnar: der... */
memcpy( dv1, &avg, sizeof( avg ) );
/* move the remaining verts */
for( k = i + 2; k < ds->numVerts; k++ )
{
/* get verts */
dv1 = &ds->verts[ k ];
dv2 = &ds->verts[ k - 1 ];
/* copy */
memcpy( dv2, dv1, sizeof( bspDrawVert_t ) );
}
ds->numVerts--;
}
}
/* one last check and return */
if( ds->numVerts < 3 )
valid = false;
return valid;
}
/*
================
EdgeCompare
================
*/
int EdgeCompare( const void *elem1, const void *elem2 ) {
float d1, d2;
d1 = ((originalEdge_t *)elem1)->length;
d2 = ((originalEdge_t *)elem2)->length;
if ( d1 < d2 ) {
return -1;
}
if ( d2 > d1 ) {
return 1;
}
return 0;
}
/*
FixTJunctions
call after the surface list has been pruned
*/
void FixTJunctions( entity_t *ent )
{
int i;
mapDrawSurface_t *ds;
shaderInfo_t *si;
int axialEdgeLines;
originalEdge_t *e;
/* meta mode has its own t-junction code (currently not as good as this code) */
//% if( meta )
//% return;
/* note it */
MsgDev( D_NOTE, "--- FixTJunctions ---\n" );
numEdgeLines = 0;
numOriginalEdges = 0;
// add all the edges
// this actually creates axial edges, but it
// only creates originalEdge_t structures
// for non-axial edges
for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc || ds->numVerts == 0 )
continue;
/* ydnar: gs mods: handle the various types of surfaces */
switch( ds->type )
{
/* handle brush faces */
case SURFACE_FACE:
AddSurfaceEdges( ds );
break;
/* handle patches */
case SURFACE_PATCH:
AddPatchEdges( ds );
break;
/* fixme: make triangle surfaces t-junction */
default:
break;
}
}
axialEdgeLines = numEdgeLines;
// sort the non-axial edges by length
qsort( originalEdges, numOriginalEdges, sizeof(originalEdges[0]), EdgeCompare );
// add the non-axial edges, longest first
// this gives the most accurate edge description
for ( i = 0 ; i < numOriginalEdges ; i++ ) {
e = &originalEdges[i];
e->dv[ 0 ]->lightmap[ 0 ][ 0 ] = AddEdge( e->dv[ 0 ]->xyz, e->dv[ 1 ]->xyz, true );
}
MsgDev( D_INFO, "%9d axial edge lines\n", axialEdgeLines );
MsgDev( D_INFO, "%9d non-axial edge lines\n", numEdgeLines - axialEdgeLines );
MsgDev( D_INFO, "%9d degenerate edges\n", c_degenerateEdges );
// insert any needed vertexes
for( i = ent->firstDrawSurf; i < numMapDrawSurfs ; i++ )
{
/* get surface and early out if possible */
ds = &mapDrawSurfs[ i ];
si = ds->shaderInfo;
if( (si->compileFlags & C_NODRAW) || si->autosprite || si->notjunc || ds->numVerts == 0 || ds->type != SURFACE_FACE )
continue;
/* ydnar: gs mods: handle the various types of surfaces */
switch( ds->type )
{
/* handle brush faces */
case SURFACE_FACE:
FixSurfaceJunctions( ds );
if( FixBrokenSurface( ds ) == false )
{
c_broken++;
ClearSurface( ds );
}
break;
/* fixme: t-junction triangle models and patches */
default:
break;
}
}
/* emit some statistics */
MsgDev( D_INFO, "%9d verts added for T-junctions\n", c_addedVerts );
MsgDev( D_INFO, "%9d total verts\n", c_totalVerts );
MsgDev( D_INFO, "%9d naturally ordered\n", c_natural );
MsgDev( D_INFO, "%9d rotated orders\n", c_rotate );
MsgDev( D_INFO, "%9d can't order\n", c_cant );
MsgDev( D_INFO, "%9d broken (degenerate) surfaces removed\n", c_broken );
}

View File

@ -1,284 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// rad_trace.c - ray casting
//=======================================================================
#include "bsplib.h"
#include "const.h"
tnode_t *tnodes, *tnode_p;
/*
==============
MakeTnode
Converts the disk node structure into the efficient tracing structure
==============
*/
void MakeTnode (int nodenum)
{
tnode_t *t;
dplane_t *plane;
int i;
dnode_t *node;
t = tnode_p++;
node = dnodes + nodenum;
plane = dplanes + node->planenum;
t->type = PlaneTypeForNormal( plane->normal );
VectorCopy (plane->normal, t->normal);
t->dist = plane->dist;
for (i=0 ; i<2 ; i++)
{
if( node->children[i] < 0 )
{
t->children[i] = (-node->children[i] - 1)|(1<<31);
}
else
{
t->children[i] = tnode_p - tnodes;
MakeTnode (node->children[i]);
}
}
}
/*
=============
MakeTnodes
Loads the node structure out of a .bsp file to be used for light occlusion
=============
*/
void MakeTnodes (dmodel_t *bm)
{
// 32 byte align the structs
tnodes = Malloc( (numnodes+1) * sizeof(tnode_t));
tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
tnode_p = tnodes;
MakeTnode (0);
}
//==========================================================
int TestLine_r (int node, vec3_t start, vec3_t stop)
{
tnode_t *tnode;
float front, back;
vec3_t mid;
float frac;
int side;
int r;
if (node & (1<<31))
{
int leafnum = node & ~(1<<31);
int i, sky_found = 0;
dleafface_t *lface = &dleafsurfaces[dleafs[leafnum].firstleafsurface];
for( i = 0; i < dleafs[leafnum].numleafsurfaces; i++, lface++ )
{
dsurface_t *face = &dsurfaces[*lface];
if( dshaders[texinfo[face->texinfo].shadernum].contentFlags & CONTENTS_SKY )
sky_found = true;
if( sky_found ) break;
}
if( sky_found )
return dleafs[leafnum].contents|CONTENTS_SKY;
return dleafs[leafnum].contents;
}
tnode = &tnodes[node];
switch (tnode->type)
{
case PLANE_X:
front = start[0] - tnode->dist;
back = stop[0] - tnode->dist;
break;
case PLANE_Y:
front = start[1] - tnode->dist;
back = stop[1] - tnode->dist;
break;
case PLANE_Z:
front = start[2] - tnode->dist;
back = stop[2] - tnode->dist;
break;
default:
front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
break;
}
if (front >= -ON_EPSILON && back >= -ON_EPSILON)
return TestLine_r (tnode->children[0], start, stop);
if (front < ON_EPSILON && back < ON_EPSILON)
return TestLine_r (tnode->children[1], start, stop);
side = front < 0;
frac = front / (front-back);
mid[0] = start[0] + (stop[0] - start[0])*frac;
mid[1] = start[1] + (stop[1] - start[1])*frac;
mid[2] = start[2] + (stop[2] - start[2])*frac;
r = TestLine_r (tnode->children[side], start, mid);
if( r & (CONTENTS_SOLID|CONTENTS_SKY))
return r;
return TestLine_r (tnode->children[!side], mid, stop);
}
int TestLine (vec3_t start, vec3_t stop)
{
return TestLine_r (0, start, stop);
}
/*
==============================================================================
LINE TRACING
The major lighting operation is a point to point visibility test, performed
by recursive subdivision of the line by the BSP tree.
==============================================================================
*/
typedef struct
{
vec3_t backpt;
int side;
int node;
} tracestack_t;
/*
==============
TestLine
==============
*/
bool _TestLine (vec3_t start, vec3_t stop)
{
int node;
float front, back;
tracestack_t *tstack_p;
int side;
float frontx,fronty, frontz, backx, backy, backz;
tracestack_t tracestack[64];
tnode_t *tnode;
frontx = start[0];
fronty = start[1];
frontz = start[2];
backx = stop[0];
backy = stop[1];
backz = stop[2];
tstack_p = tracestack;
node = 0;
while (1)
{
if (node == CONTENTS_SOLID)
{
#if 0
float d1, d2, d3;
d1 = backx - frontx;
d2 = backy - fronty;
d3 = backz - frontz;
if (d1*d1 + d2*d2 + d3*d3 > 1)
#endif
return false; // DONE!
}
while (node < 0)
{
// pop up the stack for a back side
tstack_p--;
if (tstack_p < tracestack)
return true;
node = tstack_p->node;
// set the hit point for this plane
frontx = backx;
fronty = backy;
frontz = backz;
// go down the back side
backx = tstack_p->backpt[0];
backy = tstack_p->backpt[1];
backz = tstack_p->backpt[2];
node = tnodes[tstack_p->node].children[!tstack_p->side];
}
tnode = &tnodes[node];
switch (tnode->type)
{
case PLANE_X:
front = frontx - tnode->dist;
back = backx - tnode->dist;
break;
case PLANE_Y:
front = fronty - tnode->dist;
back = backy - tnode->dist;
break;
case PLANE_Z:
front = frontz - tnode->dist;
back = backz - tnode->dist;
break;
default:
front = (frontx*tnode->normal[0] + fronty*tnode->normal[1] + frontz*tnode->normal[2]) - tnode->dist;
back = (backx*tnode->normal[0] + backy*tnode->normal[1] + backz*tnode->normal[2]) - tnode->dist;
break;
}
if (front > -ON_EPSILON && back > -ON_EPSILON)
// if (front > 0 && back > 0)
{
node = tnode->children[0];
continue;
}
if (front < ON_EPSILON && back < ON_EPSILON)
// if (front <= 0 && back <= 0)
{
node = tnode->children[1];
continue;
}
side = front < 0;
front = front / (front-back);
tstack_p->node = node;
tstack_p->side = side;
tstack_p->backpt[0] = backx;
tstack_p->backpt[1] = backy;
tstack_p->backpt[2] = backz;
tstack_p++;
backx = frontx + front*(backx-frontx);
backy = fronty + front*(backy-fronty);
backz = frontz + front*(backz-frontz);
node = tnode->children[side];
}
}

View File

@ -1,12 +1,43 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// bsp_tree.c - node bsp tree
//=======================================================================
/* -------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------
This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."
------------------------------------------------------------------------------- */
/* marker */
#define TREE_C
/* dependencies */
#include "q3map2.h"
#include "bsplib.h"
#include "const.h"
extern int c_nodes;
void RemovePortalFromNode (portal_t *portal, node_t *l);
@ -66,8 +97,6 @@ FreeTree_r
*/
void FreeTree_r (node_t *node)
{
face_t *f, *nextf;
// free children
if (node->planenum != PLANENUM_LEAF)
{
@ -78,20 +107,11 @@ void FreeTree_r (node_t *node)
// free bspbrushes
FreeBrushList (node->brushlist);
// free faces
for (f=node->faces ; f ; f=nextf)
{
nextf = f->next;
FreeFace (f);
}
// free the node
if (node->volume)
FreeBrush (node->volume);
if (GetNumThreads() == 1)
c_nodes--;
Mem_Free (node);
Mem_Free( node );
}
@ -100,99 +120,39 @@ void FreeTree_r (node_t *node)
FreeTree
=============
*/
void FreeTree (tree_t *tree)
void FreeTree( tree_t *tree )
{
FreeTreePortals_r (tree->headnode);
FreeTree_r (tree->headnode);
Mem_Free (tree);
FreeTreePortals_r( tree->headnode );
FreeTree_r( tree->headnode );
Mem_Free( tree );
}
//===============================================================
void PrintTree_r (node_t *node, int depth)
void PrintTree_r( node_t *node, int depth )
{
int i;
plane_t *plane;
bspbrush_t *bb;
brush_t *bb;
for (i=0 ; i<depth ; i++)Msg (" ");
for (i=0 ; i<depth ; i++)
Msg(" ");
if (node->planenum == PLANENUM_LEAF)
{
if (!node->brushlist) Msg ("NULL\n");
if (!node->brushlist) Msg( "NULL\n" );
else
{
for (bb=node->brushlist ; bb ; bb=bb->next)
Msg ("%i ", bb->original->brushnum);
Msg ("\n");
Msg("%d ", bb->original->brushNum);
Msg("\n");
}
return;
}
plane = &mapplanes[node->planenum];
Msg ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
Msg("#%d (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
plane->normal[0], plane->normal[1], plane->normal[2],
plane->dist);
PrintTree_r (node->children[0], depth+1);
PrintTree_r (node->children[1], depth+1);
}
/*
=========================================================
NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
=========================================================
*/
int c_pruned;
/*
============
PruneNodes_r
============
*/
void PruneNodes_r (node_t *node)
{
bspbrush_t *b, *next;
if (node->planenum == PLANENUM_LEAF)
return;
PruneNodes_r (node->children[0]);
PruneNodes_r (node->children[1]);
if ( (node->children[0]->contents & CONTENTS_SOLID)
&& (node->children[1]->contents & CONTENTS_SOLID) )
{
if (node->faces)
Sys_Error ("node->faces seperating CONTENTS_SOLID");
if (node->children[0]->faces || node->children[1]->faces)
Sys_Error ("!node->faces with children");
// FIXME: free stuff
node->planenum = PLANENUM_LEAF;
node->contents = CONTENTS_SOLID;
node->detail_seperator = false;
if (node->brushlist)
Sys_Error ("PruneNodes: node->brushlist");
// combine brush lists
node->brushlist = node->children[1]->brushlist;
for (b=node->children[0]->brushlist ; b ; b=next)
{
next = b->next;
b->next = node->brushlist;
node->brushlist = b;
}
c_pruned++;
}
}
void PruneNodes (node_t *node)
{
c_pruned = 0;
PruneNodes_r (node);
}

1138
xtools/bsplib/vis.c Normal file

File diff suppressed because it is too large Load Diff

1704
xtools/bsplib/visflow.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,7 @@
#define FLOAT_START 99999.0
#define FLOAT_END -99999.0
#define MAGIC 123322
#define Kalloc(size) Mem_Alloc(studiopool, size)
#define Realloc(ptr, size) Mem_Realloc(studiopool, ptr, size)
#define Kalloc( size ) Mem_Alloc( studiopool, size )
typedef struct
{

View File

@ -39,20 +39,20 @@ convformat_t convert_formats[] =
convformat_t convert_formats32[] =
{
{"%s.%s", "spr", ConvSPR, "tga" }, // quake1/half-life sprite
{"%s.%s", "spr", ConvSPR, "png" }, // quake1/half-life sprite
{"%s.%s","spr32",ConvSPR, "dds" }, // spr32 sprite
{"%s.%s", "sp2", ConvSPR, "dds" }, // quake2 sprite
{"%s.%s", "jpg", ConvJPG, "png" }, // quake3 textures
{"%s.%s", "jpg", ConvJPG, "jpg" }, // quake3 textures
{"%s.%s", "bmp", ConvBMP, "dds" }, // 8-bit maps with alpha-cnahnel
{"%s.%s", "pcx", ConvPCX, "png" }, // quake2 pics
{"%s.%s", "flt", ConvFLT, "png" }, // doom1 textures
{"%s.%s", "flp", ConvFLP, "png" }, // doom1 menu pics
{"%s.%s", "mip", ConvMIP, "png" }, // Quake1/Half-Life textures
{"%s.%s", "flt", ConvFLT, "jpg" }, // doom1 textures
{"%s.%s", "flp", ConvFLP, "jpg" }, // doom1 menu pics
{"%s.%s", "mip", ConvMIP, "jpg" }, // Quake1/Half-Life textures
{"%s.%s", "lmp", ConvLMP, "png" }, // Quake1/Half-Life gfx
{"%s.%s", "wal", ConvWAL, "png" }, // Quake2 textures
{"%s.%s", "wal", ConvWAL, "jpg" }, // Quake2 textures
{"%s.%s", "vtf", ConvVTF, "dds" }, // Half-Life 2 materials
{"%s.%s", "skn", ConvSKN, "png" }, // doom1 sprite models
{"%s.%s", "bsp", ConvBSP, "png" }, // Quake1\Half-Life map textures
{"%s.%s", "bsp", ConvBSP, "jpg" }, // Quake1\Half-Life map textures
{"%s.%s", "mus", ConvMID, "mid" }, // doom1\2 music files
{"%s.%s", "txt", ConvRAW, "txt" }, // (hidden) Xash-extract scripts
{"%s.%s", "dat", ConvRAW, "dat" }, // (hidden) Xash-extract progs

View File

@ -73,7 +73,7 @@ bool Conv_WriteShader( const char *shaderpath, const char *imagepath, rgbdata_t
FS_FileBase( imagepath, lumpname );
FS_FileBase( temp, qcname );
FS_FileBase( temp, wadname );
com.snprintf( shadername, MAX_STRING, "%s/%s", qcname, lumpname );
com.snprintf( shadername, MAX_STRING, "%s/%s", temp, lumpname );
FS_DefaultExtension( qcname, ".qc" );
FS_DefaultExtension( wadname, ".wad" ); // check for wad later
com.snprintf( qcpath, MAX_STRING, "%s/%s/$%s", gs_gamedir, temp, qcname );
@ -169,16 +169,15 @@ check_shader:
else if( contents & CONTENTS_PLAYERCLIP ) FS_Print( f, "\tsurfaceparm\tplayerclip\n" );
else if( contents & CONTENTS_WINDOW ) FS_Print( f, "\tsurfaceparm\twindow\n" );
else if( contents & CONTENTS_ORIGIN ) FS_Print( f, "\tsurfaceparm\torigin\n" );
else if( contents & CONTENTS_TRANSLUCENT ) FS_Print( f, "\tsurfaceparm\ttranslucent\n" );
else if( contents & CONTENTS_TRANSLUCENT ) FS_Print( f, "\tsurfaceparm\ttrans\n" );
else if( contents & CONTENTS_AREAPORTAL ) FS_Print( f, "\tsurfaceparm\tareaportal\n" );
else if( contents & CONTENTS_TRIGGER ) FS_Print( f, "\tsurfaceparm\ttrigger\n" );
else if( contents & CONTENTS_DETAIL ) FS_Print( f, "\tsurfaceparm\tdetail\n" );
if( flags & SURF_LIGHT )
{
FS_Print( f, "\tsurfaceparm\tlight\n" );
if(!VectorIsNull( rad )) FS_Printf(f, "\trad_color\t\t%.f %.f %.f\n", rad[0], rad[1], rad[2] );
if( scale ) FS_Printf(f, "\trad_intensity\t%.f\n", scale );
if(!VectorIsNull( rad )) FS_Printf(f, "\tq3map_lightRGB\t%g %g %g\n", rad[0], rad[1], rad[2] );
if( scale ) FS_Printf(f, "\tq3map_surfacelight\t%.f\n", scale );
if( !num_anims )
{
FS_Printf( f, "\t{\n\t\tmap\t%s\n\t}\n", shadername );
@ -188,6 +187,7 @@ check_shader:
if( flags & SURF_WARP )
{
FS_Print( f, "\tq3map_globaltexture\n" );
FS_Print( f, "\tsurfaceparm\tnoLightMap\n" );
FS_Print( f, "\ttessSize\t\t64\n\n" );
@ -199,7 +199,6 @@ check_shader:
else if(contents & CONTENTS_LAVA)
FS_Print( f, "\tsurfaceparm\tlava\n" );
else FS_Print( f, "\tsurfaceparm\twater\n" );
FS_Print( f, "\tsurfaceparm\twarp\n" );
FS_Printf( f, "\t{\n\t\tmap\t%s\n", shadername ); // save basemap
if( flags & (SURF_TRANS33|SURF_TRANS66))
@ -213,7 +212,6 @@ check_shader:
else if( flags & SURF_SKY ) FS_Print( f, "\tsurfaceparm\tsky\n" );
else if( flags & SURF_HINT ) FS_Print( f, "\tsurfaceparm\thint\n" );
else if( flags & SURF_SKIP ) FS_Print( f, "\tsurfaceparm\tskip\n" );
else if( flags & SURF_MIRROR ) FS_Print( f, "\tsurfaceparm\tmirror\n" );
else if( flags & (SURF_TRANS33|SURF_TRANS66))
{
FS_Printf( f, "\t{\n\t\tmap\t%s\n\n", shadername ); // save basemap
@ -228,7 +226,7 @@ check_shader:
FS_Print( f, "\t\tAlphaGen\tidentity\n\t}\n" );
lightmap_stage = true;
}
else if( flags & SURF_NODRAW ) FS_Print( f, "\tsurfaceparm\tnull\n" );
else if( flags & SURF_NODRAW ) FS_Print( f, "\tsurfaceparm\tnodraw\n" );
if( num_anims )
{
@ -398,6 +396,8 @@ void Conv_ShaderGetFlags( const char *imagename, const char *shadername, const c
}
else if( game_family == GAME_QUAKE2 )
{
/*
// this code it's totally wrong, disabled for now
if( animcount && !com.strlen( anim ))
{
// end of chain, dump now
@ -426,6 +426,7 @@ void Conv_ShaderGetFlags( const char *imagename, const char *shadername, const c
if( animcount == i ) com.strncpy( animmap[animcount++], anim, MAX_STRING );
}
// UNDONE: remove some flags
*/
}
}
@ -475,6 +476,7 @@ bool Conv_CreateShader( const char *name, rgbdata_t *pic, const char *ext, const
radiocity[1] += fin[1];
radiocity[2] += fin[2];
}
texels *= 4;
break;
case PF_BGRA_32:
case PF_ABGR_64:
@ -484,6 +486,7 @@ bool Conv_CreateShader( const char *name, rgbdata_t *pic, const char *ext, const
radiocity[1] += fin[1];
radiocity[2] += fin[0];
}
texels *= 4;
break;
case PF_RGB_24:
for( j = 0; j < texels; j++, fin += 3 )
@ -492,6 +495,7 @@ bool Conv_CreateShader( const char *name, rgbdata_t *pic, const char *ext, const
radiocity[1] += fin[1];
radiocity[2] += fin[2];
}
texels *= 3;
break;
case PF_INDEXED_24:
case PF_INDEXED_32:
@ -510,7 +514,11 @@ bool Conv_CreateShader( const char *name, rgbdata_t *pic, const char *ext, const
for( j = 0; j < 3; j++ )
radiocity[j] /= texels;
scale = ColorNormalize( radiocity, radiocity );
VectorScale( radiocity, 255.0f, radiocity );
if( scale < 0.5f )
{
scale *= 2.0f;
VectorScale( radiocity, scale, radiocity );
}
intencity = texels * 255.0 / scale; // basic intensity value
}
return Conv_WriteShader( shaderpath, imagepath, pic, radiocity, intencity, flags, contents );

Some files were not shown because too many files have changed in this diff Show More