From faabf8b33db39d6a459aaaacb19eed6289b5af61 Mon Sep 17 00:00:00 2001 From: g-cont Date: Sat, 23 Jul 2011 00:00:00 +0400 Subject: [PATCH] 23 Jul 2011 --- backup.lst | 1 + change.log | 6 +- dlls/hl.plg | 12 +- engine/client/cl_frame.c | 7 +- engine/client/cl_game.c | 23 +- engine/client/cl_main.c | 6 +- engine/client/cl_menu.c | 2 + engine/client/cl_parse.c | 8 +- engine/client/cl_remap.c | 387 ++++ engine/client/cl_view.c | 1 + engine/client/client.h | 22 + engine/client/gl_image.c | 48 +- engine/client/gl_local.h | 17 +- engine/client/gl_rmain.c | 35 + engine/client/gl_rmisc.c | 8 +- engine/client/gl_rsurf.c | 3 +- engine/client/gl_rsurf.old | 1913 ------------------- engine/client/gl_sprite.c | 18 +- engine/client/gl_studio.c | 282 ++- engine/common/build.c | 2 +- engine/common/cmd.c | 13 +- engine/common/common.h | 2 +- engine/common/console.c | 180 +- engine/common/cvar.c | 6 + engine/common/host.c | 2 + engine/common/mod_local.h | 6 + engine/common/model.c | 20 +- engine/common/soundlib/mpeg.old | Bin 143350 -> 0 bytes engine/common/soundlib/snd_mp3.old | 480 ----- engine/engine.dsp | 6 +- engine/server/sv_client.c | 5 +- engine/server/sv_cmds.c | 2 +- engine/studio.h | 2 +- game_launch/game.ncb | Bin 41984 -> 0 bytes game_launch/game.opt | Bin 52736 -> 0 bytes mainui/images.h | 2811 ---------------------------- mainui/menu_playersetup.cpp | 12 +- mainui/utils.cpp | 7 +- make_sdk.bat | 2 + utils/makefont/CreateFont.bat | 2 + utils/makefont/makefont.exe | Bin 0 -> 61440 bytes xash_sdk.lst | 1 + 42 files changed, 989 insertions(+), 5371 deletions(-) create mode 100644 engine/client/cl_remap.c delete mode 100644 engine/client/gl_rsurf.old delete mode 100644 engine/common/soundlib/mpeg.old delete mode 100644 engine/common/soundlib/snd_mp3.old delete mode 100644 game_launch/game.ncb delete mode 100644 game_launch/game.opt delete mode 100644 mainui/images.h create mode 100644 utils/makefont/CreateFont.bat create mode 100644 utils/makefont/makefont.exe diff --git a/backup.lst b/backup.lst index 76411436..60e14ad2 100644 --- a/backup.lst +++ b/backup.lst @@ -31,6 +31,7 @@ pm_shared\ mainui\ mainui\legacy utils\ +utils\makefont\ utils\vgui\ utils\vgui\include\ utils\vgui\lib\win32_vc6\ \ No newline at end of file diff --git a/change.log b/change.log index fc5c27fe..f5bf0f47 100644 --- a/change.log +++ b/change.log @@ -1,4 +1,4 @@ -build ???? +build 1613 Client: fix drawing beams for 'solid' mode Image: fix BMP loader for 4-bit color bmp's @@ -9,6 +9,10 @@ Render: fix sprite interpolation Render: fix angled sprites offset Render: implement detail textures like in Steam Half-Life (thx n00b) Client: rework env_funnel effect +Engine: get full support for input, processing and drawing russian letters +Console: add console commands 'messagemode' and 'messagemode2' +Console: fix bug with autocomplete (enable sort for found cmds) +Client: added HUD_ChatInputPosition for CS 1.6 build 1598 diff --git a/dlls/hl.plg b/dlls/hl.plg index 504e743b..06d48cfd 100644 --- a/dlls/hl.plg +++ b/dlls/hl.plg @@ -6,13 +6,13 @@ --------------------Configuration: hl - Win32 Release--------------------

Command Lines

-Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FA.tmp" with contents +Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1B70.tmp" with contents [ /nologo /G5 /MT /W3 /O2 /I "..\dlls" /I "..\engine" /I "..\common" /I "..\pm_shared" /I "..\game_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "QUIVER" /D "VOXEL" /D "QUAKE2" /D "VALVE_DLL" /D "CLIENT_WEAPONS" /Fr"..\temp\dlls\!release/" /Fp"..\temp\dlls\!release/hl.pch" /YX /Fo"..\temp\dlls\!release/" /Fd"..\temp\dlls\!release/" /FD /c "D:\Xash3D\src_main\dlls\client.cpp" ] -Creating command line "cl.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FA.tmp"" -Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FB.tmp" with contents +Creating command line "cl.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1B70.tmp"" +Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1B71.tmp" with contents [ kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"..\temp\dlls\!release/hl.pdb" /debug /machine:I386 /def:".\hl.def" /out:"..\temp\dlls\!release/hl.dll" /implib:"..\temp\dlls\!release/hl.lib" "\Xash3D\src_main\temp\dlls\!release\aflock.obj" @@ -117,13 +117,13 @@ kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32 "\Xash3D\src_main\temp\dlls\!release\xen.obj" "\Xash3D\src_main\temp\dlls\!release\zombie.obj" ] -Creating command line "link.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FB.tmp"" -Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FC.bat" with contents +Creating command line "link.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1B71.tmp"" +Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1B72.bat" with contents [ @echo off copy \Xash3D\src_main\temp\dlls\!release\hl.dll "D:\Xash3D\valve\dlls\hl.dll" ] -Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FC.bat"" +Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1B72.bat"" Compiling... client.cpp Linking... diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index dda49763..a9f9cc36 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -146,10 +146,7 @@ void CL_UpdateEntityFields( cl_entity_t *ent ) qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ) { - model_t *mod; - - mod = Mod_Handle( ent->curstate.modelindex ); - if( !mod ) return false; + if( !ent->model ) return false; // if entity is beam add it here // because render doesn't know how to draw beams @@ -179,7 +176,7 @@ qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ) else { // check for adding this entity - if( !clgame.dllFuncs.pfnAddEntity( entityType, ent, mod->name )) + if( !clgame.dllFuncs.pfnAddEntity( entityType, ent, ent->model->name )) return false; if( !R_AddEntity( ent, entityType )) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 276c5aeb..07d20823 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -78,11 +78,12 @@ static dllfunc_t cdll_exports[] = { NULL, NULL } }; -static dllfunc_t cdll_new_exports[] = // allowed only in SDK 2.3 +static dllfunc_t cdll_new_exports[] = // allowed only in SDK 2.3 and higher { { "HUD_GetStudioModelInterface", (void **)&clgame.dllFuncs.pfnGetStudioModelInterface }, { "HUD_DirectorMessage", (void **)&clgame.dllFuncs.pfnDirectorMessage }, { "HUD_VoiceStatus", (void **)&clgame.dllFuncs.pfnVoiceStatus }, +{ "HUD_ChatInputPosition", (void **)&clgame.dllFuncs.pfnChatInputPosition }, { NULL, NULL } }; @@ -1348,6 +1349,13 @@ void CL_InitEdicts( void ) cls.num_client_entities = CL_UPDATE_BACKUP * 64; cls.packet_entities = Z_Realloc( cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities ); clgame.entities = Mem_Alloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities ); + + if(( clgame.maxRemapInfos - 1 ) != clgame.maxEntities ) + { + CL_ClearAllRemaps (); // purge old remap info + clgame.maxRemapInfos = clgame.maxEntities + 1; + clgame.remap_info = (remap_info_t **)Mem_Alloc( clgame.mempool, sizeof( remap_info_t* ) * clgame.maxRemapInfos ); + } } void CL_FreeEdicts( void ) @@ -1373,6 +1381,7 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite, { byte *buf; size_t size; + qboolean loaded; ASSERT( m_pSprite != NULL ); @@ -1382,14 +1391,14 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite, Q_strncpy( m_pSprite->name, szSpriteName, sizeof( m_pSprite->name )); m_pSprite->flags = 256; // it's hud sprite, make difference names to prevent free shared textures - if( mapSprite ) Mod_LoadMapSprite( m_pSprite, buf, size ); - else Mod_LoadSpriteModel( m_pSprite, buf ); + if( mapSprite ) Mod_LoadMapSprite( m_pSprite, buf, size, &loaded ); + else Mod_LoadSpriteModel( m_pSprite, buf, &loaded ); Mem_Free( buf ); - if( m_pSprite->type != mod_sprite ) + if( !loaded ) { - Q_memset( m_pSprite, 0, sizeof( *m_pSprite )); + Mod_UnloadSpriteModel( m_pSprite ); return false; } return true; @@ -3673,12 +3682,14 @@ void CL_UnloadProgs( void ) CL_FreeTempEnts(); CL_FreeViewBeams(); CL_FreeParticles(); + CL_ClearAllRemaps(); VGui_Shutdown(); // NOTE: HLFX 0.5 has strange bug: hanging on exit if no map was loaded if( !( !Q_stricmp( GI->gamedir, "hlfx" ) && GI->version == 0.5f )) clgame.dllFuncs.pfnShutdown(); + Cvar_FullSet( "host_clientloaded", "0", CVAR_INIT ); Com_FreeLibrary( clgame.hInstance ); Mem_FreePool( &cls.mempool ); Mem_FreePool( &clgame.mempool ); @@ -3752,7 +3763,9 @@ qboolean CL_LoadProgs( const char *name ) Cvar_Get( "cl_nopred", "1", CVAR_ARCHIVE|CVAR_USERINFO, "disable client movement predicting" ); Cvar_Get( "cl_lw", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable client weapon predicting" ); Cvar_Get( "cl_lc", "0", CVAR_ARCHIVE|CVAR_USERINFO, "enable lag compensation" ); + Cvar_FullSet( "host_clientloaded", "1", CVAR_INIT ); + clgame.maxRemapInfos = 0; // will be alloc on first call CL_InitEdicts(); clgame.maxEntities = 2; // world + localclient (have valid entities not in game) CL_InitCDAudio( "media/cdaudio.txt" ); CL_InitTitles( "titles.txt" ); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 0c7ce510..636f21b4 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -472,9 +472,6 @@ void CL_WritePacket( void ) // update download/upload slider. Netchan_UpdateProgress( &cls.netchan ); - - // make sure what menu and CL_WritePacket catch changes - userinfo->modified = false; } /* @@ -492,6 +489,9 @@ void CL_SendCmd( void ) // clc_move, userinfo etc CL_WritePacket(); + + // make sure what menu and CL_WritePacket catch changes + userinfo->modified = false; } /* diff --git a/engine/client/cl_menu.c b/engine/client/cl_menu.c index a2682b58..05f875d9 100644 --- a/engine/client/cl_menu.c +++ b/engine/client/cl_menu.c @@ -216,6 +216,8 @@ static void UI_UpdateUserinfo( void ) Q_strncpy( player->userinfo, Cvar_Userinfo(), sizeof( player->userinfo )); Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name )); Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model )); + player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); + player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); } void Host_Credits( void ) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 43380258..2e8a44d1 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -478,7 +478,7 @@ void CL_ParseServerData( sizebuf_t *msg ) clgame.load_sequence++; // now all hud sprites are invalid // wipe the client_t struct - CL_ClearState(); + if( !cls.changelevel ) CL_ClearState(); cls.state = ca_connected; // parse protocol version number @@ -522,7 +522,7 @@ void CL_ParseServerData( sizebuf_t *msg ) menu.globals->maxClients = cl.maxclients; Q_strncpy( menu.globals->maptitle, clgame.maptitle, sizeof( menu.globals->maptitle )); - CL_InitEdicts (); // re-arrange edicts + if( !cls.changelevel ) CL_InitEdicts (); // re-arrange edicts // get splash name Cvar_Set( "cl_levelshot_name", va( "levelshots/%s", clgame.mapname )); @@ -782,6 +782,9 @@ void CL_UpdateUserinfo( sizebuf_t *msg ) Q_strncpy( player->userinfo, BF_ReadString( msg ), sizeof( player->userinfo )); Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name )); Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model )); + player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); + player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); + if( slot == cl.playernum ) Q_memcpy( &menu.playerinfo, player, sizeof( player_info_t )); } else Q_memset( player, 0, sizeof( *player )); @@ -1242,6 +1245,7 @@ void CL_ParseServerMessage( sizebuf_t *msg ) else MsgDev( D_INFO, "Server disconnected, reconnecting\n" ); CL_ClearState (); + CL_InitEdicts (); // re-arrange edicts cls.state = ca_connecting; cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately diff --git a/engine/client/cl_remap.c b/engine/client/cl_remap.c new file mode 100644 index 00000000..59ab2295 --- /dev/null +++ b/engine/client/cl_remap.c @@ -0,0 +1,387 @@ +/* +gl_remap.c - remap model textures +Copyright (C) 2011 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "client.h" +#include "gl_local.h" +#include "studio.h" + +static void CL_PaletteHueReplace( byte *palSrc, int newHue, int start, int end ) +{ + float r, g, b; + float maxcol, mincol; + float hue, val, sat; + int i; + + hue = (float)(newHue * ( 360.0f / 255 )); + + for( i = start; i <= end; i++ ) + { + r = palSrc[i*3+0]; + g = palSrc[i*3+1]; + b = palSrc[i*3+2]; + + maxcol = max( max( r, g ), b ) / 255.0f; + mincol = min( min( r, g ), b ) / 255.0f; + + val = maxcol; + sat = (maxcol - mincol) / maxcol; + + mincol = val * (1.0f - sat); + + if( hue <= 120.0f ) + { + b = mincol; + if( hue < 60 ) + { + r = val; + g = mincol + hue * (val - mincol) / (120.0f - hue); + } + else + { + g = val; + r = mincol + (120.0f - hue) * (val - mincol) / hue; + } + } + else if( hue <= 240.0f ) + { + r = mincol; + if( hue < 180.0f ) + { + g = val; + b = mincol + (hue - 120.0f) * (val - mincol) / (240.0f - hue); + } + else + { + b = val; + g = mincol + (240.0f - hue) * (val - mincol) / (hue - 120.0f); + } + } + else + { + g = mincol; + if( hue < 300.0f ) + { + b = val; + r = mincol + (hue - 240.0f) * (val - mincol) / (360.0f - hue); + } + else + { + r = val; + b = mincol + (360.0f - hue) * (val - mincol) / (hue - 240.0f); + } + } + + palSrc[i*3+0] = (byte)(r * 255); + palSrc[i*3+1] = (byte)(g * 255); + palSrc[i*3+2] = (byte)(b * 255); + } +} + +/* +==================== +CL_GetRemapInfoForEntity + +Returns remapinfo slot for specified entity +==================== +*/ +remap_info_t *CL_GetRemapInfoForEntity( cl_entity_t *e ) +{ + if( !e ) return NULL; + + if( e == &clgame.viewent ) + return clgame.remap_info[clgame.maxEntities]; + + return clgame.remap_info[e->curstate.number]; +} + +/* +==================== +CL_CreateRawTextureFromPixels + +Convert texture_t struct into mstudiotexture_t prototype +==================== +*/ +byte *CL_CreateRawTextureFromPixels( texture_t *tx, size_t *size, int topcolor, int bottomcolor ) +{ + static mstudiotexture_t pin; + byte *pal; + + ASSERT( size != NULL ); + + *size = sizeof( pin ) + (tx->width * tx->height) + 768; + + // fill header + if( !pin.name[0] ) Q_strncpy( pin.name, "#raw_remap_image.mdl", sizeof( pin.name )); + pin.flags = STUDIO_NF_COLORMAP; // just in case :-) + pin.index = (int)(tx + 1); // pointer to pixels + pin.width = tx->width; + pin.height = tx->height; + + // update palette + pal = (byte *)(tx + 1) + (tx->width * tx->height); + CL_PaletteHueReplace( pal, topcolor, tx->anim_min, tx->anim_max ); + CL_PaletteHueReplace( pal, bottomcolor, tx->anim_max + 1, tx->anim_total ); + + return (byte *)&pin; +} + +/* +==================== +CL_DuplicateTexture + +Dupliacte texture with remap pixels +==================== +*/ +void CL_DuplicateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomcolor ) +{ + gltexture_t *glt; + texture_t *tx = NULL; + char texname[128]; + int i, size, index; + byte *raw; + + // save of the real texture index + index = ptexture->index; + glt = R_GetTexture( index ); + Q_snprintf( texname, sizeof( texname ), "#%i_%s", RI.currententity->curstate.number, glt->name + 1 ); + + // search for pixels + for( i = 0; i < RI.currentmodel->numtextures; i++ ) + { + tx = RI.currentmodel->textures[i]; + if( tx->gl_texturenum == index ) + break; // found + } + + ASSERT( tx != NULL ); + + raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor ); + ptexture->index = GL_LoadTexture( texname, raw, size, TF_FORCE_COLOR ); // do copy + GL_SetTextureType( ptexture->index, TEX_REMAP ); +} + +/* +==================== +CL_UpdateTexture + +Update texture top and bottom colors +==================== +*/ +void CL_UpdateTexture( mstudiotexture_t *ptexture, int topcolor, int bottomcolor ) +{ + gltexture_t *glt; + rgbdata_t *pic; + texture_t *tx = NULL; + char texname[128], name[128]; + int i, size, index; + byte paletteBackup[768]; + byte *raw, *pal; + + // save of the real texture index + glt = R_GetTexture( ptexture->index ); + + // build name of original texture + FS_FileBase( ptexture->name, name ); + Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", RI.currentmodel->name, name ); + index = GL_FindTexture( texname ); + if( !index ) return; // couldn't find texture + + // search for pixels + for( i = 0; i < RI.currentmodel->numtextures; i++ ) + { + tx = RI.currentmodel->textures[i]; + if( tx->gl_texturenum == index ) + break; // found + } + + ASSERT( tx != NULL ); + + // backup original palette + pal = (byte *)(tx + 1) + (tx->width * tx->height); + Q_memcpy( paletteBackup, pal, 768 ); + + raw = CL_CreateRawTextureFromPixels( tx, &size, topcolor, bottomcolor ); + pic = FS_LoadImage( glt->name, raw, size ); + if( !pic ) + { + MsgDev( D_ERROR, "Couldn't update texture %s\n", glt->name ); + return; + } + + index = GL_LoadTextureInternal( glt->name, pic, 0, true ); + FS_FreeImage( pic ); + + // restore original palette + Q_memcpy( pal, paletteBackup, 768 ); + + ASSERT( index == ptexture->index ); +} + +/* +==================== +CL_AllocRemapInfo + +Allocate new remap info per entity +and make copy of remap textures +==================== +*/ +void CL_AllocRemapInfo( int topcolor, int bottomcolor ) +{ + remap_info_t *info; + studiohdr_t *phdr; + mstudiotexture_t *src, *dst; + int i, size; + + i = ( RI.currententity == &clgame.viewent ) ? clgame.maxEntities : RI.currententity->curstate.number; + + if( !RI.currentmodel || RI.currentmodel->type != mod_studio ) + { + // entity has changed model by another type, release remap info + if( clgame.remap_info[i] ) + { + CL_FreeRemapInfo( clgame.remap_info[i] ); + clgame.remap_info[i] = NULL; + } + return; // missed or hided model, ignore it + } + + // model doesn't contains remap textures + if( RI.currentmodel->numtextures <= 0 ) + { + // entity has changed model with no remap textures + if( clgame.remap_info[i] ) + { + CL_FreeRemapInfo( clgame.remap_info[i] ); + clgame.remap_info[i] = NULL; + } + return; + } + + phdr = (studiohdr_t *)Mod_Extradata( RI.currentmodel ); + if( !phdr ) return; // missed header ??? + + // NOTE: we must copy all the structures 'mstudiotexture_t' for easy acces when model is rendering + if( !clgame.remap_info[i] || clgame.remap_info[i]->model != RI.currentmodel ) + { + // this code catches studiomodel change with another studiomodel with remap textures + // e.g. playermodel 'barney' with playermodel 'gordon' + if( clgame.remap_info[i] ) CL_FreeRemapInfo( clgame.remap_info[i] ); // free old info + size = sizeof( remap_info_t ) + ( sizeof( mstudiotexture_t ) * phdr->numtextures ); + info = clgame.remap_info[i] = Mem_Alloc( clgame.mempool, size ); + info->ptexture = (mstudiotexture_t *)(info + 1); // textures are immediately comes after remap_info + } + else + { + // studiomodel is valid, nothing to change + return; + } + + info->numtextures = phdr->numtextures; + info->model = RI.currentmodel; + info->topcolor = topcolor; + info->bottomcolor = bottomcolor; + + src = (mstudiotexture_t *)(((byte *)phdr) + phdr->textureindex); + dst = info->ptexture; + + // copy unchanged first + Q_memcpy( dst, src, sizeof( mstudiotexture_t ) * phdr->numtextures ); + + // make local copies for remap textures + for( i = 0; i < info->numtextures; i++ ) + { + if( dst[i].flags & STUDIO_NF_COLORMAP ) + CL_DuplicateTexture( &dst[i], topcolor, bottomcolor ); + } +} + +/* +==================== +CL_UpdateRemapInfo + +Update all remaps per entity +==================== +*/ +void CL_UpdateRemapInfo( int topcolor, int bottomcolor ) +{ + remap_info_t *info; + mstudiotexture_t *dst; + int i; + + i = ( RI.currententity == &clgame.viewent ) ? clgame.maxEntities : RI.currententity->curstate.number; + info = clgame.remap_info[i]; + if( !info ) return; // no remap info + + if( info->topcolor == topcolor && info->bottomcolor == bottomcolor ) + return; // values is valid + + dst = info->ptexture; + + for( i = 0; i < info->numtextures; i++ ) + { + if( dst[i].flags & STUDIO_NF_COLORMAP ) + CL_UpdateTexture( &dst[i], topcolor, bottomcolor ); + } + + info->topcolor = topcolor; + info->bottomcolor = bottomcolor; +} + +/* +==================== +CL_FreeRemapInfo + +Release remap info per entity +==================== +*/ +void CL_FreeRemapInfo( remap_info_t *info ) +{ + int i; + + ASSERT( info != NULL ); + + // release all colormap texture copies + for( i = 0; i < info->numtextures; i++ ) + { + if( info->ptexture[i].flags & STUDIO_NF_COLORMAP ) + GL_FreeTexture( info->ptexture[i].index ); + } + + Mem_Free( info ); // release struct +} + +/* +==================== +CL_ClearAllRemaps + +Release all remap infos +==================== +*/ +void CL_ClearAllRemaps( void ) +{ + int i; + + if( clgame.remap_info ) + { + for( i = 0; i < clgame.maxRemapInfos; i++ ) + { + if( clgame.remap_info[i] ) + CL_FreeRemapInfo( clgame.remap_info[i] ); + } + Mem_Free( clgame.remap_info ); + } + clgame.remap_info = NULL; +} \ No newline at end of file diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index e709b4dd..e93f439a 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -37,6 +37,7 @@ void V_SetupRefDef( void ) VectorCopy( cl.frame.local.client.punchangle, cl.refdef.punchangle ); clgame.viewent.curstate.modelindex = cl.frame.local.client.viewmodel; clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex ); + clgame.viewent.curstate.number = cl.playernum + 1; clgame.viewent.curstate.entityType = ET_NORMAL; clgame.viewent.index = cl.playernum + 1; diff --git a/engine/client/client.h b/engine/client/client.h index 62e3d143..9e4a30ae 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -274,6 +274,15 @@ typedef struct float applied_angle; } screen_shake_t; +typedef struct +{ + struct mstudiotex_s *ptexture; // array of textures with local copy of remapped textures + short numtextures; // textures count + short topcolor; // cached value + short bottomcolor; // cached value + model_t *model; // for catch model changes +} remap_info_t; + typedef struct { int (*pfnInitialize)( cl_enginefunc_t *pEnginefuncs, int iVersion ); @@ -302,6 +311,7 @@ typedef struct void (*pfnTxferPredictionData)( entity_state_t *ps, const entity_state_t *pps, clientdata_t *pcd, const clientdata_t *ppcd, weapon_data_t *wd, const weapon_data_t *pwd ); void (*pfnTempEntUpdate)( double frametime, double client_time, double cl_gravity, struct tempent_s **ppTempEntFree, struct tempent_s **ppTempEntActive, int ( *Callback_AddVisibleEntity )( cl_entity_t *pEntity ), void ( *Callback_TempEntPlaySound )( struct tempent_s *pTemp, float damp )); int (*pfnGetStudioModelInterface)( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio ); + void (*pfnChatInputPosition)( int *x, int *y ); void (*pfnDrawNormalTriangles)( void ); void (*pfnDrawTransparentTriangles)( void ); cl_entity_t *(*pfnGetUserEntity)( int index ); @@ -327,8 +337,10 @@ typedef struct string itemspath; // path to items description for auto-complete func cl_entity_t *entities; // dynamically allocated entity array + remap_info_t **remap_info; // store local copy of all remap textures for each entity int maxEntities; + int maxRemapInfos; // maxEntities + cl.viewEnt; also used for catch entcount // movement values from server movevars_t movevars; @@ -647,6 +659,16 @@ qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ); qboolean CL_GetEntitySpatialization( int ent, vec3_t origin, vec3_t velocity ); qboolean CL_IsPlayerIndex( int idx ); +// +// cl_remap.c +// +remap_info_t *CL_GetRemapInfoForEntity( cl_entity_t *e ); +void CL_AllocRemapInfo( int topcolor, int bottomcolor ); +void CL_FreeRemapInfo( remap_info_t *info ); +void R_StudioSetRemapColors( int top, int bottom ); +void CL_UpdateRemapInfo( int topcolor, int bottomcolor ); +void CL_ClearAllRemaps( void ); + // // cl_tent.c // diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index 58d6b9ee..3e80a39c 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -898,8 +898,7 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag } } - // critical stuff!!! - GL_MBind( tex - r_textures ); + pglBindTexture( tex->target, tex->texnum ); buf = pic->buffer; bufend = pic->buffer + pic->size; @@ -960,7 +959,7 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) if( Q_strlen( name ) >= sizeof( r_textures->name )) { - MsgDev( D_ERROR, "GL_LoadTexture: too long name %s\n", name, sizeof( r_textures->name )); + MsgDev( D_ERROR, "GL_LoadTexture: too long name %s\n", name ); return 0; } @@ -1039,7 +1038,7 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, if( Q_strlen( name ) >= sizeof( r_textures->name )) { - MsgDev( D_ERROR, "GL_LoadTexture: too long name %s\n", name, sizeof( r_textures->name )); + MsgDev( D_ERROR, "GL_LoadTexture: too long name %s\n", name ); return 0; } @@ -1051,8 +1050,11 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, if( tex->flags & TF_CUBEMAP ) continue; - if( !Q_stricmp( tex->name, name ) && !update ) - return tex->texnum; + if( !Q_stricmp( tex->name, name )) + { + if( update ) break; + return tex->texnum; + } } if( !pic ) return 0; // couldn't loading image @@ -1106,6 +1108,40 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, return tex->texnum; } +/* +================ +GL_LoadTexture +================ +*/ +int GL_FindTexture( const char *name ) +{ + gltexture_t *tex; + uint hash; + + if( !name || !name[0] || !glw_state.initialized ) + return 0; + + if( Q_strlen( name ) >= sizeof( r_textures->name )) + { + MsgDev( D_ERROR, "GL_FindTexture: too long name %s\n", name ); + return 0; + } + + // see if already loaded + hash = Com_HashKey( name, TEXTURES_HASH_SIZE ); + + for( tex = r_texturesHashTable[hash]; tex != NULL; tex = tex->nextHash ) + { + if( tex->flags & TF_CUBEMAP ) + continue; + + if( !Q_stricmp( tex->name, name )) + return tex->texnum; + } + + return 0; +} + /* ================ GL_FreeImage diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index d4c300fb..0d8ab2ac 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -64,12 +64,13 @@ typedef enum TEX_DECAL, // decals TEX_VGUI, // vgui fonts or images TEX_CUBEMAP, // cubemap textures (sky) - TEX_DETAIL // detail textures + TEX_DETAIL, // detail textures + TEX_REMAP // local copy of remap texture } texType_t; typedef enum { - TF_STATIC = BIT(0), // don't free until Shader_FreeUnused() + TF_STATIC = BIT(0), // don't free until Image_FreeUnused() TF_NOPICMIP = BIT(1), // ignore r_picmip resample rules TF_UNCOMPRESSED = BIT(2), // don't compress texture in video memory TF_CUBEMAP = BIT(3), // it's cubemap texture @@ -206,10 +207,12 @@ typedef struct gl_entity_t mirror_entities[MAX_VISIBLE_PACKET]; // an entities that has mirror cl_entity_t *solid_entities[MAX_VISIBLE_PACKET]; // opaque moving or alpha brushes cl_entity_t *trans_entities[MAX_VISIBLE_PACKET]; // translucent brushes + cl_entity_t *child_entities[MAX_VISIBLE_PACKET]; // entities with MOVETYPE_FOLLOW uint num_static_entities; uint num_mirror_entities; uint num_solid_entities; uint num_trans_entities; + uint num_child_entities; // OpenGL matrix states qboolean modelviewIdentity; @@ -244,7 +247,7 @@ extern mleaf_t *r_viewleaf, *r_oldviewleaf; extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; extern dlight_t cl_dlights[MAX_DLIGHTS]; extern dlight_t cl_elights[MAX_ELIGHTS]; -#define r_numEntities (tr.num_solid_entities + tr.num_trans_entities) +#define r_numEntities (tr.num_solid_entities + tr.num_trans_entities + tr.num_child_entities) #define r_numStatics (tr.num_static_entities) // @@ -297,6 +300,7 @@ void GL_SetTextureType( GLenum texnum, GLenum type ); int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ); int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update ); byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap ); +int GL_FindTexture( const char *name ); void GL_FreeTexture( GLenum texnum ); void GL_FreeImage( const char *name ); void R_TextureList_f( void ); @@ -362,7 +366,7 @@ void GL_BuildLightmaps( void ); // gl_sprite.c // void R_SpriteInit( void ); -void Mod_LoadSpriteModel( model_t *mod, const void *buffer ); +void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded ); mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw ); void R_DrawSpriteModel( cl_entity_t *e ); @@ -370,7 +374,7 @@ void R_DrawSpriteModel( cl_entity_t *e ); // gl_studio.c // void R_StudioInit( void ); -void Mod_LoadStudioModel( model_t *mod, const void *buffer ); +void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ); void R_DrawStudioModel( cl_entity_t *e ); // @@ -423,8 +427,7 @@ qboolean R_CullBox( const vec3_t mins, const vec3_t maxs, uint clipflags ); qboolean R_WorldToScreen( const vec3_t point, vec3_t screen ); void R_ScreenToWorld( const vec3_t screen, vec3_t point ); qboolean R_AddEntity( struct cl_entity_s *pRefEntity, int entityType ); -void Mod_LoadSpriteModel( struct model_s *mod, const void *buffer ); -void Mod_LoadMapSprite( struct model_s *mod, const void *buffer, size_t size ); +void Mod_LoadMapSprite( struct model_s *mod, const void *buffer, size_t size, qboolean *loaded ); void Mod_UnloadSpriteModel( struct model_s *mod ); void Mod_UnloadStudioModel( struct model_s *mod ); void Mod_UnloadBrushModel( struct model_s *mod ); diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index c7a331aa..4abc1eb6 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -72,6 +72,28 @@ static qboolean R_StaticEntity( cl_entity_t *ent ) return true; } +/* +=============== +R_FollowEntity + +Follow entity is attached to another studiomodel and used last cached bones +from parent +=============== +*/ +static qboolean R_FollowEntity( cl_entity_t *ent ) +{ + if( ent->model->type != mod_studio ) + return false; + + if( ent->curstate.movetype != MOVETYPE_FOLLOW ) + return false; + + if( ent->curstate.aiment <= 0 ) + return false; + + return true; +} + /* =============== R_OpaqueEntity @@ -315,6 +337,7 @@ void R_ClearScene( void ) { tr.num_solid_entities = tr.num_trans_entities = 0; tr.num_static_entities = tr.num_mirror_entities = 0; + tr.num_child_entities = 0; } /* @@ -338,6 +361,18 @@ qboolean R_AddEntity( struct cl_entity_s *clent, int entityType ) clent->curstate.entityType = entityType; + if( R_FollowEntity( clent )) + { + // follow entity + if( tr.num_child_entities >= MAX_VISIBLE_PACKET ) + return false; + + tr.child_entities[tr.num_child_entities] = clent; + tr.num_child_entities++; + + return true; + } + if( R_OpaqueEntity( clent )) { if( R_StaticEntity( clent )) diff --git a/engine/client/gl_rmisc.c b/engine/client/gl_rmisc.c index 07383f50..17eefb52 100644 --- a/engine/client/gl_rmisc.c +++ b/engine/client/gl_rmisc.c @@ -170,7 +170,7 @@ void R_CreateDetailTexturesList( const char *filename ) // detailtexture detected if( detail_name ) { - if( !detail_txt ) detail_txt = FS_Open( filename, "wb", false ); + if( !detail_txt ) detail_txt = FS_Open( filename, "w", false ); if( !detail_txt ) { MsgDev( D_ERROR, "Can't write %s\n", filename ); @@ -272,7 +272,13 @@ void R_NewMap( void ) // clear out efrags in case the level hasn't been reloaded for( i = 0; i < cl.worldmodel->numleafs; i++ ) + { cl.worldmodel->leafs[i].efrags = NULL; + cl.worldmodel->leafs[i].visframe = 0; + } + + for( i = 0; i < cl.worldmodel->numnodes; i++ ) + cl.worldmodel->nodes[i].visframe = 0; tr.skytexturenum = -1; r_viewleaf = r_oldviewleaf = NULL; diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index c9d9da87..b86a83ee 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -1915,7 +1915,7 @@ void GL_BuildLightmaps( void ) skychain = NULL; - tr.framecount = 1; // no dlight cache + tr.framecount = tr.visframecount = 1; // no dlight cache gl_lms.current_lightmap_texture = 0; // setup all the lightstyles @@ -1937,6 +1937,7 @@ void GL_BuildLightmaps( void ) { // clearing all decal chains m->surfaces[j].pdecals = NULL; + m->surfaces[j].visframe = 0; GL_CreateSurfaceLightmap( m->surfaces + j ); diff --git a/engine/client/gl_rsurf.old b/engine/client/gl_rsurf.old deleted file mode 100644 index 7e13d814..00000000 --- a/engine/client/gl_rsurf.old +++ /dev/null @@ -1,1913 +0,0 @@ -/* -gl_rsurf.c - surface-related refresh code -Copyright (C) 2010 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - -#include "common.h" -#include "client.h" -#include "gl_local.h" -#include "mod_local.h" -#include "mathlib.h" - -typedef struct -{ - int allocated[BLOCK_WIDTH]; - int current_lightmap_texture; - msurface_t *dynamic_surfaces; - msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; - byte lightmap_buffer[BLOCK_WIDTH*BLOCK_HEIGHT*4]; -} gllightmapstate_t; - -static vec3_t modelorg; // relative to viewpoint -static vec3_t modelmins; -static vec3_t modelmaxs; -static byte visbytes[MAX_MAP_LEAFS/8]; -static uint r_blocklights[BLOCK_WIDTH*BLOCK_HEIGHT*3]; -static glpoly_t *fullbright_polys[MAX_TEXTURES]; -static qboolean draw_fullbrights = false; -static gllightmapstate_t gl_lms; -static msurface_t *skychain = NULL; - -static void LM_UploadBlock( int lightmapnum ); - -byte *Mod_GetCurrentVis( void ) -{ - return Mod_LeafPVS( r_viewleaf, cl.worldmodel ); -} - -static void BoundPoly( int numverts, float *verts, vec3_t mins, vec3_t maxs ) -{ - int i, j; - float *v; - - mins[0] = mins[1] = mins[2] = 9999; - maxs[0] = maxs[1] = maxs[2] = -9999; - - for( i = 0, v = verts; i < numverts; i++ ) - { - for( j = 0; j < 3; j++, v++ ) - { - if( *v < mins[j] ) mins[j] = *v; - if( *v > maxs[j] ) maxs[j] = *v; - } - } -} - -static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts ) -{ - int i, j, k, f, b; - vec3_t mins, maxs; - float m, frac, s, t, *v, vertsDiv; - vec3_t front[SUBDIVIDE_SIZE], back[SUBDIVIDE_SIZE], total; - float dist[SUBDIVIDE_SIZE], total_s, total_t, total_ls, total_lt; - glpoly_t *poly; - - if( numverts > ( SUBDIVIDE_SIZE - 4 )) - Host_Error( "Mod_SubdividePolygon: too many vertexes on face ( %i )\n", numverts ); - - BoundPoly( numverts, verts, mins, maxs ); - - for( i = 0; i < 3; i++ ) - { - m = ( mins[i] + maxs[i] ) * 0.5f; - m = SUBDIVIDE_SIZE * floor( m / SUBDIVIDE_SIZE + 0.5f ); - if( maxs[i] - m < 8 ) continue; - if( m - mins[i] < 8 ) continue; - - // cut it - v = verts + i; - for( j = 0; j < numverts; j++, v += 3 ) - dist[j] = *v - m; - - // wrap cases - dist[j] = dist[0]; - v -= i; - VectorCopy( verts, v ); - - f = b = 0; - v = verts; - for( j = 0; j < numverts; j++, v += 3 ) - { - if( dist[j] >= 0 ) - { - VectorCopy( v, front[f] ); - f++; - } - - if( dist[j] <= 0 ) - { - VectorCopy (v, back[b]); - b++; - } - - if( dist[j] == 0 || dist[j+1] == 0 ) - continue; - - if(( dist[j] > 0 ) != ( dist[j+1] > 0 )) - { - // clip point - frac = dist[j] / ( dist[j] - dist[j+1] ); - for( k = 0; k < 3; k++ ) - front[f][k] = back[b][k] = v[k] + frac * (v[3+k] - v[k]); - f++; - b++; - } - } - - SubdividePolygon_r( warpface, f, front[0] ); - SubdividePolygon_r( warpface, b, back[0] ); - return; - } - - // add a point in the center to help keep warp valid - poly = Mem_Alloc( loadmodel->mempool, sizeof( glpoly_t ) + ((numverts-4)+2) * VERTEXSIZE * sizeof( float )); - poly->next = warpface->polys; - poly->flags = warpface->flags; - warpface->polys = poly; - poly->numverts = numverts + 2; - VectorClear( total ); - total_s = total_ls = 0.0f; - total_t = total_lt = 0.0f; - - for( i = 0; i < numverts; i++, verts += 3 ) - { - VectorCopy( verts, poly->verts[i+1] ); - VectorAdd( total, verts, total ); - - if( warpface->flags & SURF_DRAWTURB ) - { - s = DotProduct( verts, warpface->texinfo->vecs[0] ); - t = DotProduct( verts, warpface->texinfo->vecs[1] ); - } - else - { - s = DotProduct( verts, warpface->texinfo->vecs[0] ) + warpface->texinfo->vecs[0][3]; - t = DotProduct( verts, warpface->texinfo->vecs[1] ) + warpface->texinfo->vecs[1][3]; - s /= warpface->texinfo->texture->width; - t /= warpface->texinfo->texture->height; - } - - poly->verts[i+1][3] = s; - poly->verts[i+1][4] = t; - - total_s += s; - total_t += t; - - if(!( warpface->flags & SURF_DRAWTURB )) - { - // lightmap texture coordinates - s = DotProduct( verts, warpface->texinfo->vecs[0] ) + warpface->texinfo->vecs[0][3]; - s -= warpface->texturemins[0]; - s += warpface->light_s * LM_SAMPLE_SIZE; - s += 8; - s /= BLOCK_WIDTH * LM_SAMPLE_SIZE; //fa->texinfo->texture->width; - - t = DotProduct( verts, warpface->texinfo->vecs[1] ) + warpface->texinfo->vecs[1][3]; - t -= warpface->texturemins[1]; - t += warpface->light_t * LM_SAMPLE_SIZE; - t += 8; - t /= BLOCK_HEIGHT * LM_SAMPLE_SIZE; //fa->texinfo->texture->height; - - poly->verts[i+1][5] = s; - poly->verts[i+1][6] = t; - - total_ls += s; - total_lt += t; - } - } - - vertsDiv = ( 1.0f / (float)numverts ); - - VectorScale( total, vertsDiv, poly->verts[0] ); - poly->verts[0][3] = total_s * vertsDiv; - poly->verts[0][4] = total_t * vertsDiv; - poly->verts[0][5] = total_ls * vertsDiv; - poly->verts[0][6] = total_lt * vertsDiv; - - // copy first vertex to last - Q_memcpy( poly->verts[i+1], poly->verts[1], sizeof( poly->verts[0] )); -} - -/* -================ -GL_SubdivideSurface - -Breaks a polygon up along axial 64 unit -boundaries so that turbulent and sky warps -can be done reasonably. -================ -*/ -void GL_SubdivideSurface( msurface_t *fa ) -{ - vec3_t verts[SUBDIVIDE_SIZE]; - int numverts; - int i, lindex; - float *vec; - - // convert edges back to a normal polygon - numverts = 0; - for( i = 0; i < fa->numedges; i++ ) - { - lindex = loadmodel->surfedges[fa->firstedge + i]; - - if( lindex > 0 ) vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; - else vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; - VectorCopy( vec, verts[numverts] ); - numverts++; - } - - // do subdivide - SubdividePolygon_r( fa, numverts, verts[0] ); -} - -/* -================ -GL_BuildPolygonFromSurface -================ -*/ -void GL_BuildPolygonFromSurface( msurface_t *fa ) -{ - int i, lindex, lnumverts; - medge_t *pedges, *r_pedge; - int vertpage; - float *vec; - float s, t; - glpoly_t *poly; - - // already created - if( fa->polys ) return; - - if( !fa->texinfo || !fa->texinfo->texture ) - return; // bad polygon ? -#if 0 - // this causes problem with decals. disabled until done - if( fa->texinfo->texture->anim_total < 0 ) - { - // random tileing. subdivide the polygon - GL_SubdivideSurface( fa ); - return; - } -#endif - // reconstruct the polygon - pedges = loadmodel->edges; - lnumverts = fa->numedges; - vertpage = 0; - - // draw texture - poly = Mem_Alloc( loadmodel->mempool, sizeof( glpoly_t ) + ( lnumverts - 4 ) * VERTEXSIZE * sizeof( float )); - poly->next = fa->polys; - poly->flags = fa->flags; - fa->polys = poly; - poly->numverts = lnumverts; - - for( i = 0; i < lnumverts; i++ ) - { - lindex = loadmodel->surfedges[fa->firstedge + i]; - - if( lindex > 0 ) - { - r_pedge = &pedges[lindex]; - vec = loadmodel->vertexes[r_pedge->v[0]].position; - } - else - { - r_pedge = &pedges[-lindex]; - vec = loadmodel->vertexes[r_pedge->v[1]].position; - } - - s = DotProduct( vec, fa->texinfo->vecs[0] ) + fa->texinfo->vecs[0][3]; - s /= fa->texinfo->texture->width; - - t = DotProduct( vec, fa->texinfo->vecs[1] ) + fa->texinfo->vecs[1][3]; - t /= fa->texinfo->texture->height; - - VectorCopy( vec, poly->verts[i] ); - poly->verts[i][3] = s; - poly->verts[i][4] = t; - - // lightmap texture coordinates - s = DotProduct( vec, fa->texinfo->vecs[0] ) + fa->texinfo->vecs[0][3]; - s -= fa->texturemins[0]; - s += fa->light_s * LM_SAMPLE_SIZE; - s += 8; - s /= BLOCK_WIDTH * LM_SAMPLE_SIZE; //fa->texinfo->texture->width; - - t = DotProduct( vec, fa->texinfo->vecs[1] ) + fa->texinfo->vecs[1][3]; - t -= fa->texturemins[1]; - t += fa->light_t * LM_SAMPLE_SIZE; - t += 8; - t /= BLOCK_HEIGHT * LM_SAMPLE_SIZE; //fa->texinfo->texture->height; - - poly->verts[i][5] = s; - poly->verts[i][6] = t; - } -} - -/* -=============== -R_TextureAnimation - -Returns the proper texture for a given time and base texture -=============== -*/ -texture_t *R_TextureAnimation( texture_t *base, int surfacenum ) -{ - int reletive; - int count, speed; - - // random tileng textures - if( base->anim_total < 0 ) - { - reletive = surfacenum % abs( base->anim_total ); - - count = 0; - while( base->anim_min > reletive || base->anim_max <= reletive ) - { - base = base->anim_next; - if( !base ) Host_Error( "R_TextureAnimation: broken loop\n" ); - if( ++count > 100 ) Host_Error( "R_TextureAnimation: infinite loop\n" ); - } - return base; - } - - if( RI.currententity->curstate.frame ) - { - if( base->alternate_anims ) - base = base->alternate_anims; - } - - if( !base->anim_total ) - return base; - - // GoldSrc and Quake1 has different animating speed - if( world.version == Q1BSP_VERSION ) - speed = 10; - else speed = 20; - - reletive = (int)(cl.time * speed) % base->anim_total; - - count = 0; - while( base->anim_min > reletive || base->anim_max <= reletive ) - { - base = base->anim_next; - if( !base ) Host_Error( "R_TextureAnimation: broken loop\n" ); - if( ++count > 100 ) Host_Error( "R_TextureAnimation: infinite loop\n" ); - } - return base; -} - -/* -=============== -R_AddDynamicLights -=============== -*/ -void R_AddDynamicLights( msurface_t *surf ) -{ - float dist, rad, minlight; - int lnum, s, t, sd, td, smax, tmax; - float sl, tl, sacc, tacc; - vec3_t impact, origin_l; - mtexinfo_t *tex; - dlight_t *dl; - uint *bl; - - smax = (surf->extents[0] >> 4) + 1; - tmax = (surf->extents[1] >> 4) + 1; - tex = surf->texinfo; - - for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ ) - { - if(!( surf->dlightbits & BIT( lnum ))) - continue; // not lit by this light - - dl = &cl_dlights[lnum]; - - if( !tr.modelviewIdentity ) - { - matrix4x4 imatrix; - - // transform light origin to local bmodel space - Matrix4x4_Invert_Simple( imatrix, RI.objectMatrix ); - Matrix4x4_VectorTransform( imatrix, dl->origin, origin_l ); - } - else VectorCopy( dl->origin, origin_l ); - - rad = dl->radius; - dist = PlaneDiff( origin_l, surf->plane ); - rad -= fabs( dist ); - - // rad is now the highest intensity on the plane - minlight = dl->minlight; - if( rad < minlight ) - continue; - - minlight = rad - minlight; - - if( surf->plane->type < 3 ) - { - VectorCopy( origin_l, impact ); - impact[surf->plane->type] -= dist; - } - else VectorMA( origin_l, -dist, surf->plane->normal, impact ); - - sl = DotProduct( impact, tex->vecs[0] ) + tex->vecs[0][3] - surf->texturemins[0]; - tl = DotProduct( impact, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1]; - - bl = r_blocklights; - for( t = 0, tacc = 0; t < tmax; t++, tacc += LM_SAMPLE_SIZE ) - { - td = tl - tacc; - if( td < 0 ) td = -td; - - for( s = 0, sacc = 0; s < smax; s++, sacc += LM_SAMPLE_SIZE, bl += 3 ) - { - sd = sl - sacc; - if( sd < 0 ) sd = -sd; - - if( sd > td ) dist = sd + (td >> 1); - else dist = td + (sd >> 1); - - if( dist < minlight ) - { - bl[0] += ( rad - dist ) * dl->color.r; - bl[1] += ( rad - dist ) * dl->color.g; - bl[2] += ( rad - dist ) * dl->color.b; - } - } - } - } -} - -/* -================ -R_SetCacheState -================ -*/ -void R_SetCacheState( msurface_t *surf ) -{ - int maps; - - for( maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++ ) - { - surf->cached_light[maps] = RI.lightstylevalue[surf->styles[maps]]; - } -} - -/* -============================================================================= - - LIGHTMAP ALLOCATION - -============================================================================= -*/ -static void LM_InitBlock( void ) -{ - Q_memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated )); -} - -static int LM_AllocBlock( int w, int h, int *x, int *y ) -{ - int i, j; - int best, best2; - - best = BLOCK_HEIGHT; - - for( i = 0; i < BLOCK_WIDTH - w; i++ ) - { - best2 = 0; - - for( j = 0; j < w; j++ ) - { - if( gl_lms.allocated[i+j] >= best ) - break; - if( gl_lms.allocated[i+j] > best2 ) - best2 = gl_lms.allocated[i+j]; - } - - if( j == w ) - { - // this is a valid spot - *x = i; - *y = best = best2; - } - } - - if( best + h > BLOCK_HEIGHT ) - return false; - - for( i = 0; i < w; i++ ) - gl_lms.allocated[*x + i] = best + h; - - return true; -} - -static void LM_UploadBlock( qboolean dynamic ) -{ - int i; - - if( dynamic ) - { - int height = 0; - - for( i = 0; i < BLOCK_WIDTH; i++ ) - { - if( gl_lms.allocated[i] > height ) - height = gl_lms.allocated[i]; - } - - GL_MBind( tr.dlightTexture ); - - pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_RGBA, GL_UNSIGNED_BYTE, - gl_lms.lightmap_buffer ); - } - else - { - rgbdata_t r_lightmap; - char lmName[16]; - - i = gl_lms.current_lightmap_texture; - - // upload static lightmaps only during loading - Q_memset( &r_lightmap, 0, sizeof( r_lightmap )); - Q_snprintf( lmName, sizeof( lmName ), "*lightmap%i", i ); - - r_lightmap.width = BLOCK_WIDTH; - r_lightmap.height = BLOCK_HEIGHT; - r_lightmap.type = PF_RGBA_32; - r_lightmap.size = r_lightmap.width * r_lightmap.height * 4; - r_lightmap.flags = ( world.version == Q1BSP_VERSION ) ? 0 : IMAGE_HAS_COLOR; - r_lightmap.buffer = gl_lms.lightmap_buffer; - tr.lightmapTextures[i] = GL_LoadTextureInternal( lmName, &r_lightmap, TF_FONT|TF_LIGHTMAP, false ); - GL_SetTextureType( tr.lightmapTextures[i], TEX_LIGHTMAP ); - - if( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS ) - Host_Error( "AllocBlock: full\n" ); - } -} - -/* -================= -R_BuildLightmap - -Combine and scale multiple lightmaps into the floating -format in r_blocklights -================= -*/ -static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride ) -{ - int smax, tmax; - uint *bl, scale; - int i, map, size, s, t; - color24 *lm; - - smax = ( surf->extents[0] >> 4 ) + 1; - tmax = ( surf->extents[1] >> 4 ) + 1; - size = smax * tmax; - - lm = surf->samples; - - Q_memset( r_blocklights, 0, sizeof( uint ) * size * 3 ); - - // add all the lightmaps - for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255 && lm; map++ ) - { - scale = RI.lightstylevalue[surf->styles[map]]; - - for( i = 0, bl = r_blocklights; i < size; i++, bl += 3, lm++ ) - { - bl[0] += lm->r * scale; - bl[1] += lm->g * scale; - bl[2] += lm->b * scale; - } - } - - // add all the dynamic lights - if( surf->dlightframe == tr.framecount ) - R_AddDynamicLights( surf ); - - // Put into texture format - stride -= (smax << 2); - bl = r_blocklights; - - for( t = 0; t < tmax; t++, dest += stride ) - { - for( s = 0; s < smax; s++ ) - { - dest[0] = min((bl[0] >> 7), 255 ); - dest[1] = min((bl[1] >> 7), 255 ); - dest[2] = min((bl[2] >> 7), 255 ); - dest[3] = 255; - - bl += 3; - dest += 4; - } - } -} - -/* -================ -DrawGLPoly -================ -*/ -void DrawGLPoly( glpoly_t *p, texture_t *tex ) -{ - float sOffset, sy; - float tOffset, cy; - cl_entity_t *e = RI.currententity; - qboolean random_tiles = false; - texture_t *t; - glpoly_t *base; - - if( p->flags & SURF_CONVEYOR ) - { - gltexture_t *texture; - float flConveyorSpeed; - float flRate, flAngle; - - flConveyorSpeed = (e->curstate.rendercolor.g<<8|e->curstate.rendercolor.b) / 16.0f; - if( e->curstate.rendercolor.r ) flConveyorSpeed = -flConveyorSpeed; - texture = R_GetTexture( glState.currentTextures[glState.activeTMU] ); - - flRate = abs( flConveyorSpeed ) / (float)texture->srcWidth; - flAngle = ( flConveyorSpeed >= 0 ) ? 180 : 0; - - SinCos( flAngle * ( M_PI / 180.0f ), &sy, &cy ); - sOffset = RI.refdef.time * cy * flRate; - tOffset = RI.refdef.time * sy * flRate; - - // make sure that we are positive - if( sOffset < 0.0f ) sOffset += 1.0f + -(int)sOffset; - if( tOffset < 0.0f ) tOffset += 1.0f + -(int)tOffset; - - // make sure that we are in a [0,1] range - sOffset = sOffset - (int)sOffset; - tOffset = tOffset - (int)tOffset; - } - else - { - sOffset = tOffset = 0.0f; - } - - if( p && p->next ) random_tiles = true; - - for( base = p; p != NULL; p = p->next ) - { - float *v; - int i; - - if( random_tiles && tex ) - { - t = R_TextureAnimation( tex, base - p ); - GL_MBind( t->gl_texturenum ); - } - - pglBegin( GL_POLYGON ); - - for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE ) - { - pglTexCoord2f( v[3] + sOffset, v[4] + tOffset ); - pglVertex3fv( v ); - } - - pglEnd(); - } -} - -/* -================ -DrawGLPolyChain - -Render lightmaps -================ -*/ -void DrawGLPolyChain( glpoly_t *p, float soffset, float toffset ) -{ - qboolean dynamic = true; - - if( soffset == 0.0f && toffset == 0.0f ) - dynamic = false; - - for( ; p != NULL; p = p->chain ) - { - glpoly_t *p2; - float *v; - int i; - - for( p2 = p; p2 != NULL; p2 = p2->next ) - { - pglBegin( GL_POLYGON ); - - v = p2->verts[0]; - for( i = 0; i < p2->numverts; i++, v += VERTEXSIZE ) - { - if( !dynamic ) pglTexCoord2f( v[5], v[6] ); - else pglTexCoord2f( v[5] - soffset, v[6] - toffset ); - pglVertex3fv( v ); - } - pglEnd (); - } - } -} - -/* -================ -R_BlendLightmaps -================ -*/ -void R_BlendLightmaps( void ) -{ - msurface_t *surf, *newsurf = NULL; - mextrasurf_t *info; - int i; - - if( r_fullbright->integer || !cl.worldmodel->lightdata ) - return; - - if( RI.currententity ) - { - // check for rendermode - switch( RI.currententity->curstate.rendermode ) - { - case kRenderTransTexture: - case kRenderTransColor: - case kRenderTransAdd: - case kRenderGlow: - return; // no lightmaps - } - } - - if( !r_lightmap->integer ) - { - pglEnable( GL_BLEND ); - pglDepthMask( GL_FALSE ); - pglDepthFunc( GL_EQUAL ); - pglDisable( GL_ALPHA_TEST ); - pglBlendFunc( GL_ZERO, GL_SRC_COLOR ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - } - - // render static lightmaps first - for( i = 0; i < MAX_LIGHTMAPS; i++ ) - { - if( gl_lms.lightmap_surfaces[i] ) - { - GL_MBind( tr.lightmapTextures[i] ); - - for( surf = gl_lms.lightmap_surfaces[i]; surf != NULL; surf = surf->lightmapchain ) - { - if( surf->polys ) DrawGLPolyChain( surf->polys, 0.0f, 0.0f ); - } - } - } - - // render dynamic lightmaps - if( r_dynamic->integer ) - { - LM_InitBlock(); - - GL_MBind( tr.dlightTexture ); - - newsurf = gl_lms.dynamic_surfaces; - - for( surf = gl_lms.dynamic_surfaces; surf != NULL; surf = surf->lightmapchain ) - { - int smax, tmax; - byte *base; - - smax = ( surf->extents[0] >> 4 ) + 1; - tmax = ( surf->extents[1] >> 4 ) + 1; - info = SURF_INFO( surf, RI.currentmodel ); - - if( LM_AllocBlock( smax, tmax, &info->dlight_s, &info->dlight_t )) - { - base = gl_lms.lightmap_buffer; - base += ( info->dlight_t * BLOCK_WIDTH + info->dlight_s ) * 4; - - R_BuildLightMap( surf, base, BLOCK_WIDTH * 4 ); - } - else - { - msurface_t *drawsurf; - - // upload what we have so far - LM_UploadBlock( true ); - - // draw all surfaces that use this lightmap - for( drawsurf = newsurf; drawsurf != surf; drawsurf = drawsurf->lightmapchain ) - { - if( drawsurf->polys ) - { - info = SURF_INFO( drawsurf, RI.currentmodel ); - - DrawGLPolyChain( drawsurf->polys, - ( drawsurf->light_s - info->dlight_s ) * ( 1.0f / 128.0f ), - ( drawsurf->light_t - info->dlight_t ) * ( 1.0f / 128.0f )); - } - } - - newsurf = drawsurf; - - // clear the block - LM_InitBlock(); - - info = SURF_INFO( surf, RI.currentmodel ); - - // try uploading the block now - if( !LM_AllocBlock( smax, tmax, &info->dlight_s, &info->dlight_t ) ) - Host_Error( "AllocBlock: full\n" ); - - base = gl_lms.lightmap_buffer; - base += ( info->dlight_t * BLOCK_WIDTH + info->dlight_s ) * 4; - - R_BuildLightMap( surf, base, BLOCK_WIDTH * 4 ); - } - } - - // draw remainder of dynamic lightmaps that haven't been uploaded yet - if( newsurf ) LM_UploadBlock( true ); - - for( surf = newsurf; surf != NULL; surf = surf->lightmapchain ) - { - if( surf->polys ) - { - info = SURF_INFO( surf, RI.currentmodel ); - - DrawGLPolyChain( surf->polys, - ( surf->light_s - info->dlight_s ) * ( 1.0f / 128.0f ), - ( surf->light_t - info->dlight_t ) * ( 1.0f / 128.0f )); - } - } - } - - if( !r_lightmap->integer ) - { - pglDisable( GL_BLEND ); - pglDepthMask( GL_TRUE ); - pglDepthFunc( GL_LEQUAL ); - pglDisable( GL_ALPHA_TEST ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - } -} - -/* -================ -R_RenderFullbrights -================ -*/ -void R_RenderFullbrights( void ) -{ - glpoly_t *p; - int i; - - if( !draw_fullbrights ) - return; - - pglEnable( GL_BLEND ); - pglDepthMask( GL_FALSE ); - pglDisable( GL_ALPHA_TEST ); - pglBlendFunc( GL_ONE, GL_ONE ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - - for( i = 1; i < MAX_TEXTURES; i++ ) - { - if( !fullbright_polys[i] ) - continue; - GL_Bind( GL_TEXTURE0, i ); - - for( p = fullbright_polys[i]; p; p = p->next ) - { - if( p->flags & SURF_DRAWTURB ) - EmitWaterPolys( p, ( p->flags & SURF_NOCULL )); - else DrawGLPoly( p, NULL ); // disable random tiling (chain is already used) - } - - fullbright_polys[i] = NULL; - } - - pglDisable( GL_BLEND ); - pglDepthMask( GL_TRUE ); - pglDisable( GL_ALPHA_TEST ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - - draw_fullbrights = false; -} - -/* -================ -R_RenderBrushPoly -================ -*/ -void R_RenderBrushPoly( msurface_t *fa ) -{ - texture_t *t; - int maps; - qboolean is_dynamic = false; - - if( RI.currententity == clgame.entities ) - r_stats.c_world_polys++; - else r_stats.c_brush_polys++; - - if( fa->flags & SURF_DRAWSKY ) - { - if( world.version == Q1BSP_VERSION ) - { - // warp texture, no lightmaps - EmitSkyLayers( fa ); - } - return; - } - - t = R_TextureAnimation( fa->texinfo->texture, 0 ); - GL_MBind( t->gl_texturenum ); - - if( fa->flags & SURF_DRAWTURB ) - { - // warp texture, no lightmaps - EmitWaterPolys( fa->polys, ( fa->flags & SURF_NOCULL )); - return; - } - - if( t->fb_texturenum ) - { - // HACKHACK: store fullbrights in poly->next (only for non-water surfaces) - fa->polys->next = fullbright_polys[t->fb_texturenum]; - fullbright_polys[t->fb_texturenum] = fa->polys; - draw_fullbrights = true; - } - - DrawGLPoly( fa->polys, fa->texinfo->texture ); - DrawSurfaceDecals( fa ); - - // check for lightmap modification - for( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ ) - { - if( RI.lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] ) - goto dynamic; - } - - // dynamic this frame or dynamic previously - if(( fa->dlightframe == tr.framecount )) - { -dynamic: - // NOTE: at this point we have only valid textures - if( r_dynamic->integer ) is_dynamic = true; - } - - if( is_dynamic ) - { - if(( fa->styles[maps] >= 32 || fa->styles[maps] == 0 ) && ( fa->dlightframe != tr.framecount )) - { - byte temp[34*34*4]; - int smax, tmax; - - smax = ( fa->extents[0] >> 4 ) + 1; - tmax = ( fa->extents[1] >> 4 ) + 1; - - R_BuildLightMap( fa, temp, smax * 4 ); - R_SetCacheState( fa ); - - GL_MBind( tr.lightmapTextures[fa->lightmaptexturenum] ); - - pglTexSubImage2D( GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, smax, tmax, - GL_RGBA, GL_UNSIGNED_BYTE, temp ); - - fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; - gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; - } - else - { - fa->lightmapchain = gl_lms.dynamic_surfaces; - gl_lms.dynamic_surfaces = fa; - } - } - else - { - fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; - gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; - } -} - -/* -================ -R_DrawTextureChains -================ -*/ -void R_DrawTextureChains( void ) -{ - int i; - msurface_t *s; - texture_t *t; - - // make sure what color is reset - pglColor4ub( 255, 255, 255, 255 ); - R_LoadIdentity(); // set identity matrix - - // restore worldmodel - RI.currententity = clgame.entities; - RI.currentmodel = RI.currententity->model; - - // world has mirrors! - if( RP_NORMALPASS() && tr.mirror_entities[0].chain != NULL ) - { - tr.mirror_entities[0].ent = clgame.entities; - tr.num_mirror_entities++; - } - - // clip skybox surfaces - for( s = skychain; s != NULL; s = s->texturechain ) - R_AddSkyBoxSurface( s ); - - for( i = 0; i < cl.worldmodel->numtextures; i++ ) - { - t = cl.worldmodel->textures[i]; - if( !t ) continue; - - s = t->texturechain; - if( !s ) continue; - - if( i == tr.skytexturenum ) - { - if( world.version == Q1BSP_VERSION ) - R_DrawSkyChain( s ); - } - else - { - for( ; s != NULL; s = s->texturechain ) - R_RenderBrushPoly( s ); - } - t->texturechain = NULL; - } -} - -/* -================ -R_DrawWaterSurfaces -================ -*/ -void R_DrawWaterSurfaces( void ) -{ - int i; - msurface_t *s; - texture_t *t; - - if( !RI.drawWorld || RI.refdef.onlyClientDraw ) - return; - - // non-transparent water is already drawed - if( r_wateralpha->value >= 1.0f ) - return; - - // go back to the world matrix - pglMatrixMode( GL_MODELVIEW ); - GL_LoadMatrix( RI.worldviewMatrix ); - - pglEnable( GL_BLEND ); - pglDepthMask( GL_FALSE ); - pglDisable( GL_ALPHA_TEST ); - pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - pglColor4f( 1.0f, 1.0f, 1.0f, r_wateralpha->value ); - - for( i = 0; i < cl.worldmodel->numtextures; i++ ) - { - t = cl.worldmodel->textures[i]; - if( !t ) continue; - - s = t->texturechain; - if( !s ) continue; - - if(!( s->flags & SURF_DRAWTURB )) - continue; - - // set modulate mode explicitly - GL_Bind( GL_TEXTURE0, t->gl_texturenum ); - - for( ; s; s = s->texturechain ) - EmitWaterPolys( s->polys, ( s->flags & SURF_NOCULL )); - - t->texturechain = NULL; - } - - pglDisable( GL_BLEND ); - pglDepthMask( GL_TRUE ); - pglDisable( GL_ALPHA_TEST ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - pglColor4ub( 255, 255, 255, 255 ); -} - -/* -================= -R_SurfaceCompare - -compare translucent surfaces -================= -*/ -static int R_SurfaceCompare( const msurface_t **a, const msurface_t **b ) -{ - msurface_t *surf1, *surf2; - mextrasurf_t *info1, *info2; - vec3_t vecLength, org1, org2; - float len1, len2; - - surf1 = (msurface_t *)*a; - surf2 = (msurface_t *)*b; - - info1 = SURF_INFO( surf1, RI.currentmodel ); - info2 = SURF_INFO( surf2, RI.currentmodel ); - - VectorAdd( RI.currententity->origin, info1->origin, org1 ); - VectorAdd( RI.currententity->origin, info2->origin, org2 ); - - VectorSubtract( RI.pvsorigin, org1, vecLength ); - len1 = VectorLength( vecLength ); - VectorSubtract( RI.pvsorigin, org2, vecLength ); - len2 = VectorLength( vecLength ); - - if( len1 > len2 ) - return -1; - if( len1 < len2 ) - return 1; - - return 0; -} - -static _inline qboolean R_CullSurface( msurface_t *surf, uint clipflags ) -{ - mextrasurf_t *info; - - if( !surf || !surf->texinfo || !surf->texinfo->texture ) - return true; - - if( surf->flags & SURF_WATERCSG && !( RI.currententity->curstate.effects & EF_NOWATERCSG )) - return true; - - if( surf->flags & SURF_NOCULL ) - return false; - - if( r_nocull->integer ) - return false; - - // world surfaces can be culled by vis frame too - if( RI.currententity == clgame.entities && surf->visframe != tr.framecount ) - return true; - - if( r_faceplanecull->integer && glState.faceCull != 0 ) - { - if(!(surf->flags & SURF_DRAWTURB) || !RI.currentWaveHeight ) - { - if( !VectorIsNull( surf->plane->normal )) - { - float dist; - - dist = PlaneDiff( modelorg, surf->plane ); - - if( glState.faceCull == GL_FRONT || ( RI.params & RP_MIRRORVIEW )) - { - if( surf->flags & SURF_PLANEBACK ) - { - if( dist >= -BACKFACE_EPSILON ) - return true; // wrong side - } - else - { - if( dist <= BACKFACE_EPSILON ) - return true; // wrong side - } - } - else if( glState.faceCull == GL_BACK ) - { - if( surf->flags & SURF_PLANEBACK ) - { - if( dist <= BACKFACE_EPSILON ) - return true; // wrong side - } - else - { - if( dist >= -BACKFACE_EPSILON ) - return true; // wrong side - } - } - } - } - } - - info = SURF_INFO( surf, RI.currentmodel ); - - return ( clipflags && R_CullBox( info->mins, info->maxs, clipflags )); -} - -/* -================= -R_DrawBrushModel -================= -*/ -void R_DrawBrushModel( cl_entity_t *e ) -{ - int i, k, num_sorted; - qboolean need_sort = false; - vec3_t mins, maxs; - msurface_t *psurf; - model_t *clmodel; - qboolean rotated; - dlight_t *l; - - clmodel = e->model; - RI.currentWaveHeight = RI.currententity->curstate.scale * 32.0f; - - if( !VectorIsNull( e->angles )) - { - for( i = 0; i < 3; i++ ) - { - mins[i] = e->origin[i] - clmodel->radius; - maxs[i] = e->origin[i] + clmodel->radius; - } - rotated = true; - } - else - { - VectorAdd( e->origin, clmodel->mins, mins ); - VectorAdd( e->origin, clmodel->maxs, maxs ); - rotated = false; - } - - if( R_CullBox( mins, maxs, RI.clipFlags )) - return; - - Q_memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces )); - gl_lms.dynamic_surfaces = NULL; - - if( rotated ) R_RotateForEntity( e ); - else R_TranslateForEntity( e ); - - VectorSubtract( RI.cullorigin, e->origin, modelorg ); - e->visframe = tr.framecount; // visible - - if( rotated ) - { - vec3_t temp; - VectorCopy( modelorg, temp ); - Matrix4x4_VectorITransform( RI.objectMatrix, temp, modelorg ); - } - - // calculate dynamic lighting for bmodel if it's not an - // instanced model - if( clmodel->firstmodelsurface != 0 ) - { - vec3_t origin_l, oldorigin; - matrix4x4 imatrix; - - for( k = 0, l = cl_dlights; k < MAX_DLIGHTS; k++, l++ ) - { - if( l->die < cl.time || !l->radius ) - continue; - - VectorCopy( l->origin, oldorigin ); // save oldorigin - Matrix4x4_Invert_Simple( imatrix, RI.objectMatrix ); - Matrix4x4_VectorTransform( imatrix, l->origin, origin_l ); - VectorCopy( origin_l, l->origin ); // move light in bmodel space - R_MarkLights( l, 1<nodes + clmodel->hulls[0].firstclipnode ); - VectorCopy( oldorigin, l->origin ); // restore lightorigin - } - } - - // setup the rendermode - GL_SetRenderMode( e->curstate.rendermode ); - - // setup the color and alpha - switch( e->curstate.rendermode ) - { - case kRenderTransAdd: - case kRenderTransTexture: - need_sort = true; - case kRenderGlow: - pglColor4ub( 255, 255, 255, e->curstate.renderamt ); - break; - case kRenderTransColor: - pglDisable( GL_TEXTURE_2D ); - pglColor4ub( e->curstate.rendercolor.r, e->curstate.rendercolor.g, - e->curstate.rendercolor.b, e->curstate.renderamt ); - break; - case kRenderTransAlpha: - // NOTE: brushes can't change renderamt for 'Solid' mode - pglAlphaFunc( GL_GEQUAL, 0.5f ); - default: - pglColor4ub( 255, 255, 255, 255 ); - break; - } - - num_sorted = 0; - - psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; - for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) - { - if( R_CullSurface( psurf, 0 )) - continue; - - if( RP_NORMALPASS() && psurf->flags & SURF_MIRROR ) - { - psurf->texturechain = tr.mirror_entities[tr.num_mirror_entities].chain; - tr.mirror_entities[tr.num_mirror_entities].chain = psurf; - } - else if( need_sort ) - { - world.draw_surfaces[num_sorted] = psurf; - num_sorted++; - ASSERT( world.max_surfaces >= num_sorted ); - } - else - { - // render unsorted (solid) - R_RenderBrushPoly( psurf ); - } - } - - // store new mirror entity - if( RP_NORMALPASS() && tr.mirror_entities[tr.num_mirror_entities].chain != NULL ) - { - tr.mirror_entities[tr.num_mirror_entities].ent = RI.currententity; - tr.num_mirror_entities++; - } - - if( need_sort ) - qsort( world.draw_surfaces, num_sorted, sizeof( msurface_t* ), R_SurfaceCompare ); - - // draw sorted translucent surfaces - for( i = 0; i < num_sorted; i++ ) - R_RenderBrushPoly( world.draw_surfaces[i] ); - - if( e->curstate.rendermode == kRenderTransColor ) - pglEnable( GL_TEXTURE_2D ); - - R_BlendLightmaps(); - R_RenderFullbrights(); - R_LoadIdentity(); // restore worldmatrix -} - -/* -================= -R_DrawStaticModel - -Merge static model brushes with world surfaces -================= -*/ -void R_DrawStaticModel( cl_entity_t *e ) -{ - int i, k; - model_t *clmodel; - msurface_t *psurf; - dlight_t *l; - - clmodel = e->model; - if( R_CullBox( clmodel->mins, clmodel->maxs, RI.clipFlags )) - return; - - // calculate dynamic lighting for bmodel if it's not an - // instanced model - if( clmodel->firstmodelsurface != 0 ) - { - for( k = 0, l = cl_dlights; k < MAX_DLIGHTS; k++, l++ ) - { - if( l->die < cl.time || !l->radius ) - continue; - R_MarkLights( l, 1<nodes + clmodel->hulls[0].firstclipnode ); - } - } - - psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; - for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) - { - if( R_CullSurface( psurf, 0 )) - continue; - - if( RP_NORMALPASS() && psurf->flags & SURF_MIRROR ) - { - psurf->texturechain = tr.mirror_entities[0].chain; - tr.mirror_entities[0].chain = psurf; - } - else - { - psurf->texturechain = psurf->texinfo->texture->texturechain; - psurf->texinfo->texture->texturechain = psurf; - } - } -} - -/* -================= -R_DrawStaticBrushes - -Insert static brushes into world texture chains -================= -*/ -void R_DrawStaticBrushes( void ) -{ - int i; - - // draw static entities - for( i = 0; i < tr.num_static_entities; i++ ) - { - if( RI.refdef.onlyClientDraw ) - break; - - RI.currententity = tr.static_entities[i]; - RI.currentmodel = RI.currententity->model; - - ASSERT( RI.currententity != NULL ); - ASSERT( RI.currententity->model != NULL ); - - switch( RI.currententity->model->type ) - { - case mod_brush: - R_DrawStaticModel( RI.currententity ); - break; - default: - Host_Error( "R_DrawStatics: non bsp model in static list!\n" ); - break; - } - } -} - -/* -============================================================= - - MIRROR RENDERING - -============================================================= -*/ -void R_PlaneForMirror( msurface_t *surf, mplane_t *out ) -{ - cl_entity_t *ent; - - ASSERT( out != NULL ); - - ent = RI.currententity; - - // setup mirror plane - *out = *surf->plane; - - if( surf->flags & SURF_PLANEBACK ) - { - VectorNegate( out->normal, out->normal ); - } - - if( !VectorIsNull( ent->angles )) - R_RotateForEntity( ent ); - else R_TranslateForEntity( ent ); - - // tranform mirror plane by entity matrix - if( !tr.modelviewIdentity ) - { - mplane_t tmp; - - tmp = *out; - - Matrix4x4_TransformPositivePlane( RI.objectMatrix, tmp.normal, tmp.dist, out->normal, &out->dist ); - } -} - -void R_DrawMirrors( void ) -{ - ref_instance_t oldRI; - mplane_t plane; - msurface_t *surf, *mirrorchain; - vec3_t forward, right, up; - vec3_t origin, angles; - int i; - float d; - - if( !tr.num_mirror_entities ) return; // mo mirrors for this frame - - oldRI = RI; // make refinst backup - - for( i = 0; i < tr.num_mirror_entities; i++ ) - { - mirrorchain = tr.mirror_entities[i].chain; - - for( surf = mirrorchain; surf != NULL; surf = surf->texturechain ) - { - RI.currententity = tr.mirror_entities[i].ent; - RI.currentmodel = RI.currententity->model; - - ASSERT( RI.currententity != NULL ); - ASSERT( RI.currentmodel != NULL ); - - R_PlaneForMirror( surf, &plane ); - - d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist ); - VectorMA( RI.vieworg, d, plane.normal, origin ); - - d = -2.0f * DotProduct( RI.vforward, plane.normal ); - VectorMA( RI.vforward, d, plane.normal, forward ); - VectorNormalize( forward ); - - d = -2.0f * DotProduct( RI.vright, plane.normal ); - VectorMA( RI.vright, d, plane.normal, right ); - VectorNormalize( right ); - - d = -2.0f * DotProduct( RI.vup, plane.normal ); - VectorMA( RI.vup, d, plane.normal, up ); - VectorNormalize( up ); - - VectorsAngles( forward, right, up, angles ); - angles[ROLL] = -angles[ROLL]; - - RI.params = RP_MIRRORVIEW|RP_FLIPFRONTFACE|RP_CLIPPLANE; - if( r_viewleaf ) RI.params |= RP_OLDVIEWLEAF; - - RI.clipPlane = plane; - RI.clipFlags |= ( 1<<5 ); - - RI.frustum[5] = plane; - RI.frustum[5].signbits = SignbitsForPlane( RI.frustum[5].normal ); - RI.frustum[5].type = PLANE_NONAXIAL; - - RI.refdef.viewangles[0] = anglemod( angles[0] ); - RI.refdef.viewangles[1] = anglemod( angles[1] ); - RI.refdef.viewangles[2] = anglemod( angles[2] ); - VectorCopy( origin, RI.refdef.vieworg ); - VectorCopy( origin, RI.pvsorigin ); - VectorCopy( origin, RI.cullorigin ); - - R_RenderScene( &RI.refdef ); - - if( !( RI.params & RP_OLDVIEWLEAF )) - r_oldviewleaf = r_viewleaf = NULL; // force markleafs next frame - - RI = oldRI; // restore ref instance - - // FIXME: draw mirror surface here - } - - tr.mirror_entities[i].chain = NULL; // done - tr.mirror_entities[i].ent = NULL; - } - - tr.num_mirror_entities = 0; -} - -/* -============================================================= - - WORLD MODEL - -============================================================= -*/ -/* -================ -R_RecursiveWorldNode -================ -*/ -void R_RecursiveWorldNode( mnode_t *node, uint clipflags ) -{ - const mplane_t *clipplane; - int i, clipped; - msurface_t *surf, **mark; - mleaf_t *pleaf; - int c, side; - float dot; - - if( node->contents == CONTENTS_SOLID ) - return; // hit a solid leaf - - if( node->visframe != tr.visframecount ) - return; - - if( clipflags ) - { - for( i = 0, clipplane = RI.frustum; i < 6; i++, clipplane++ ) - { - if(!( clipflags & ( 1<minmaxs, node->minmaxs + 3, clipplane ); - if( clipped == 2 ) return; - if( clipped == 1 ) clipflags &= ~(1<contents < 0 ) - { - pleaf = (mleaf_t *)node; - - mark = pleaf->firstmarksurface; - c = pleaf->nummarksurfaces; - - if( c ) - { - do - { - (*mark)->visframe = tr.framecount; - mark++; - } while( --c ); - } - - // deal with model fragments in this leaf - if( pleaf->efrags ) - R_StoreEfrags( &pleaf->efrags ); - - r_stats.c_world_leafs++; - return; - } - - // node is just a decision point, so go down the apropriate sides - - // find which side of the node we are on - dot = PlaneDiff( modelorg, node->plane ); - side = (dot >= 0) ? 0 : 1; - - // recurse down the children, front side first - R_RecursiveWorldNode( node->children[side], clipflags ); - - // draw stuff - for( c = node->numsurfaces, surf = cl.worldmodel->surfaces + node->firstsurface; c; c--, surf++ ) - { - if( R_CullSurface( surf, clipflags )) - continue; - - if( surf->flags & SURF_DRAWSKY && world.version == HLBSP_VERSION ) - { - // make sky chain to right clip the skybox - surf->texturechain = skychain; - skychain = surf; - } - else if( RP_NORMALPASS() && surf->flags & SURF_MIRROR ) - { - surf->texturechain = tr.mirror_entities[0].chain; - tr.mirror_entities[0].chain = surf; - } - else - { - surf->texturechain = surf->texinfo->texture->texturechain; - surf->texinfo->texture->texturechain = surf; - } - } - - // recurse down the back side - R_RecursiveWorldNode( node->children[!side], clipflags ); -} - -/* -============= -R_DrawTriangleOutlines -============= -*/ -void R_DrawTriangleOutlines( void ) -{ - int i, j; - glpoly_t *p, *p2; - - if( !gl_wireframe->integer ) - return; - - pglDisable( GL_TEXTURE_2D ); - pglDisable( GL_DEPTH_TEST ); - pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); - pglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - - for( i = 0; i < MAX_LIGHTMAPS; i++ ) - { - msurface_t *surf; - float *v; - - for( surf = gl_lms.lightmap_surfaces[i]; surf != NULL; surf = surf->lightmapchain ) - { - p = surf->polys; - -// for( ; p != NULL; p = p->chain ) - { - p2 = p; - - for( p2 = p; p2; p2 = p2->next ) - { - pglBegin( GL_POLYGON ); - for( j = 0, v = p2->verts[0]; j < p2->numverts; j++, v += VERTEXSIZE ) - pglVertex3fv( v ); - pglEnd (); - } - } - } - } - - pglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - pglEnable( GL_DEPTH_TEST ); - pglEnable( GL_TEXTURE_2D ); -} - -/* -============= -R_DrawWorld -============= -*/ -void R_DrawWorld( void ) -{ - if( !RI.drawWorld || RI.refdef.onlyClientDraw ) - return; - - RI.currententity = clgame.entities; - RI.currentmodel = RI.currententity->model; - - VectorCopy( RI.cullorigin, modelorg ); - Q_memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces )); - Q_memset( fullbright_polys, 0, sizeof( fullbright_polys )); - RI.currentWaveHeight = RI.waveHeight; - GL_SetRenderMode( kRenderNormal ); - gl_lms.dynamic_surfaces = NULL; - - R_ClearSkyBox (); - - // draw the world fog - R_DrawFog (); - - R_RecursiveWorldNode( cl.worldmodel->nodes, RI.clipFlags ); - - R_DrawStaticBrushes(); - R_DrawTextureChains(); - - R_BlendLightmaps(); - R_RenderFullbrights(); - - if( skychain ) - R_DrawSkyBox(); - skychain = NULL; - - R_DrawTriangleOutlines (); -} - -/* -=============== -R_MarkLeaves - -Mark the leaves and nodes that are in the PVS for the current leaf -=============== -*/ -void R_MarkLeaves( void ) -{ - byte *vis; - mnode_t *node; - int i; - - if( !RI.drawWorld ) return; - if( r_viewleaf == r_oldviewleaf && r_viewleaf2 == r_oldviewleaf2 && !r_novis->integer && r_viewleaf != NULL ) - return; - - // development aid to let you run around - // and see exactly where the pvs ends - if( r_lockpvs->integer ) - return; - - tr.visframecount++; - r_oldviewleaf = r_viewleaf; - r_oldviewleaf2 = r_viewleaf2; - - if( r_novis->integer || r_viewleaf == NULL || !cl.worldmodel->visdata ) - { - // force to get full visibility - vis = Mod_LeafPVS( NULL, NULL ); - } - else - { - // may have to combine two clusters - // because of solid water boundaries - vis = Mod_LeafPVS( r_viewleaf, cl.worldmodel ); - - if( r_viewleaf != r_viewleaf2 ) - { - int longs = ( cl.worldmodel->numleafs + 31 ) >> 5; - - Q_memcpy( visbytes, vis, longs << 2 ); - vis = Mod_LeafPVS( r_viewleaf2, cl.worldmodel ); - - for( i = 0; i < longs; i++ ) - ((int *)visbytes)[i] |= ((int *)vis)[i]; - - vis = visbytes; - } - } - - for( i = 0; i < cl.worldmodel->numleafs; i++ ) - { - if( vis[i>>3] & ( 1<<( i & 7 ))) - { - node = (mnode_t *)&cl.worldmodel->leafs[i+1]; - do - { - if( node->visframe == tr.visframecount ) - break; - node->visframe = tr.visframecount; - node = node->parent; - } while( node ); - } - } -} - -/* -======================== -GL_CreateSurfaceLightmap -======================== -*/ -void GL_CreateSurfaceLightmap( msurface_t *surf ) -{ - int smax, tmax; - byte *base; - - if( !cl.worldmodel->lightdata ) return; - if( surf->flags & SURF_DRAWTILED ) - return; - - smax = ( surf->extents[0] >> 4 ) + 1; - tmax = ( surf->extents[1] >> 4 ) + 1; - - if( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t )) - { - LM_UploadBlock( false ); - LM_InitBlock(); - - if( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t )) - Host_Error( "AllocBlock: full\n" ); - } - - surf->lightmaptexturenum = gl_lms.current_lightmap_texture; - - base = gl_lms.lightmap_buffer; - base += ( surf->light_t * BLOCK_WIDTH + surf->light_s ) * 4; - - R_SetCacheState( surf ); - R_BuildLightMap( surf, base, BLOCK_WIDTH * 4 ); -} - -/* -================== -GL_BuildLightmaps - -Builds the lightmap texture -with all the surfaces from all brush models -================== -*/ -void GL_BuildLightmaps( void ) -{ - int i, j; - model_t *m; - - // release old lightmaps - for( i = 0; i < MAX_LIGHTMAPS; i++ ) - { - if( !tr.lightmapTextures[i] ) break; - GL_FreeTexture( tr.lightmapTextures[i] ); - } - - Q_memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures )); - Q_memset( tr.mirror_entities, 0, sizeof( tr.mirror_entities )); - Q_memset( visbytes, 0x00, sizeof( visbytes )); - - skychain = NULL; - - tr.framecount = 1; // no dlight cache - gl_lms.current_lightmap_texture = 0; - - // setup all the lightstyles - R_AnimateLight(); - - LM_InitBlock(); - - for( i = 1; i < MAX_MODELS; i++ ) - { - if(( m = Mod_Handle( i )) == NULL ) - continue; - - if( m->name[0] == '*' || m->type != mod_brush ) - continue; - - loadmodel = m; - - for( j = 0; j < m->numsurfaces; j++ ) - { - // clearing all decal chains - m->surfaces[j].pdecals = NULL; - - GL_CreateSurfaceLightmap( m->surfaces + j ); - - if( m->surfaces[i].flags & SURF_DRAWTURB ) - continue; - - if( m->surfaces[i].flags & SURF_DRAWSKY && world.version == Q1BSP_VERSION ) - continue; - - GL_BuildPolygonFromSurface( m->surfaces + j ); - } - } - - loadmodel = NULL; - - LM_UploadBlock( false ); -} \ No newline at end of file diff --git a/engine/client/gl_sprite.c b/engine/client/gl_sprite.c index 98725650..84d19846 100644 --- a/engine/client/gl_sprite.c +++ b/engine/client/gl_sprite.c @@ -132,7 +132,7 @@ Mod_LoadSpriteModel load sprite model ==================== */ -void Mod_LoadSpriteModel( model_t *mod, const void *buffer ) +void Mod_LoadSpriteModel( model_t *mod, const void *buffer, qboolean *loaded ) { dsprite_t *pin; short *numi; @@ -140,7 +140,9 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer ) dframetype_t *pframetype; int i, size; + if( loaded ) *loaded = false; pin = (dsprite_t *)buffer; + mod->type = mod_sprite; i = pin->version; if( pin->ident != IDSPRITEHEADER ) @@ -176,7 +178,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer ) if( host.type == HOST_DEDICATED ) { // skip frames loading - mod->type = mod_sprite; + if( loaded ) *loaded = true; // done psprite->numframes = 0; return; } @@ -210,14 +212,12 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer ) else { MsgDev( D_ERROR, "%s has wrong number of palette colors %i (should be 256)\n", mod->name, numi ); - Mem_FreePool( &mod->mempool ); return; } if( pin->numframes < 1 ) { MsgDev( D_ERROR, "%s has invalid # of frames: %d\n", mod->name, pin->numframes ); - Mem_FreePool( &mod->mempool ); return; } @@ -244,7 +244,7 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer ) if( pframetype == NULL ) break; // technically an error } - mod->type = mod_sprite; // done + if( loaded ) *loaded = true; // done } /* @@ -255,7 +255,7 @@ Loading a bitmap image as sprite with multiple frames as pieces of input image ==================== */ -void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size ) +void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size, qboolean *loaded ) { byte *src, *dst; rgbdata_t *pix, temp; @@ -266,10 +266,13 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size ) mspriteframe_t *pspriteframe; msprite_t *psprite; + if( loaded ) *loaded = false; Q_snprintf( texname, sizeof( texname ), "#%s", mod->name ); pix = FS_LoadImage( texname, buffer, size ); if( !pix ) return; // bad image or something else + mod->type = mod_sprite; + if( pix->width % MAPSPRITE_SIZE ) w = pix->width - ( pix->width % MAPSPRITE_SIZE ); else w = pix->width; @@ -358,7 +361,8 @@ void Mod_LoadMapSprite( model_t *mod, const void *buffer, size_t size ) FS_FreeImage( pix ); Mem_Free( temp.buffer ); - mod->type = mod_sprite; // done + + if( loaded ) *loaded = true; } /* diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 6450a0fa..dbef2aa3 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -61,7 +61,7 @@ convar_t *r_studio_lighting; convar_t *r_drawviewmodel; convar_t *r_customdraw_playermodel; convar_t *cl_himodels; -cvar_t r_shadows = { "r_shadows", "0", 0, 0 }; // dead cvar. especially disabled +cvar_t r_shadows = { "r_shadows", "0", 0, 1 }; // dead cvar. especially disabled cvar_t r_shadowalpha = { "r_shadowalpha", "0.5", 0, 0 }; static r_studio_interface_t *pStudioDraw; static float aliasXscale, aliasYscale; // software renderer scale @@ -77,6 +77,11 @@ static vec3_t g_chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone referen static int g_chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated static vec3_t g_xformverts[MAXSTUDIOVERTS]; static vec3_t g_xformnorms[MAXSTUDIOVERTS]; +static vec3_t g_xarrayverts[MAXSTUDIOVERTS]; +static vec3_t g_xarraynorms[MAXSTUDIOVERTS]; +static uint g_xarrayelems[MAXSTUDIOVERTS*6]; +static uint g_nNumArrayVerts; +static uint g_nNumArrayElems; static vec3_t g_lightvalues[MAXSTUDIOVERTS]; static studiolight_t g_studiolight; char g_nCachedBoneNames[MAXSTUDIOBONES][32]; @@ -88,6 +93,7 @@ float studio_radius; // global variables qboolean m_fDoInterp; +qboolean m_fDoRemap; mstudiomodel_t *m_pSubModel; mstudiobodyparts_t *m_pBodyPart; player_info_t *m_pPlayerInfo; @@ -129,6 +135,7 @@ void R_StudioInit( void ) Matrix3x4_LoadIdentity( g_rotationmatrix ); g_nStudioCount = 0; + m_fDoRemap = false; } /* @@ -272,6 +279,9 @@ pfnPlayerInfo */ static player_info_t *pfnPlayerInfo( int index ) { + if( cls.key_dest == key_menu && !index ) + return &menu.playerinfo; + if( index < 0 || index > cl.maxclients ) return NULL; return &cl.players[index]; @@ -1230,7 +1240,7 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) // calculate vectors from the viewer to the bone. This roughly adjusts for position vec3_t chromeupvec; // g_chrome t vector in world reference frame vec3_t chromerightvec; // g_chrome s vector in world reference frame - vec3_t tmp; // vector pointing at bone in world reference frame + vec3_t tmp, v_left; // vector pointing at bone in world reference frame VectorScale( cl.refdef.vieworg, -1.0f, tmp ); tmp[0] += g_bonestransform[bone][0][3]; @@ -1238,16 +1248,17 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) tmp[2] += g_bonestransform[bone][2][3]; VectorNormalize( tmp ); + VectorNegate( RI.vright, v_left ); if( g_nFaceFlags & STUDIO_NF_CHROME ) { float angle = anglemod( RI.refdef.time * 40 ); - RotatePointAroundVector( chromeupvec, tmp, RI.vright, angle - 180 ); - RotatePointAroundVector( chromerightvec, chromeupvec, RI.vright, 180 + angle ); + RotatePointAroundVector( chromeupvec, tmp, v_left, angle - 180 ); + RotatePointAroundVector( chromerightvec, chromeupvec, v_left, 180 + angle ); } else { - CrossProduct( tmp, RI.vright, chromeupvec ); + CrossProduct( tmp, v_left, chromeupvec ); VectorNormalize( chromeupvec ); CrossProduct( tmp, chromeupvec, chromerightvec ); VectorNormalize( chromerightvec ); @@ -1636,6 +1647,9 @@ static void R_StudioSetupSkin( mstudiotexture_t *ptexture, int index ) if( !m_pTextureHeader ) return; + // NOTE: user can comment call StudioRemapColors and remap_info will be unavailable + if( m_fDoRemap ) ptexture = CL_GetRemapInfoForEntity( RI.currententity )->ptexture; + m_skinnum = RI.currententity->curstate.skin; pskinref = (short *)((byte *)m_pTextureHeader + m_pTextureHeader->skinindex); if( m_skinnum != 0 && m_skinnum < m_pTextureHeader->numskinfamilies ) @@ -1664,6 +1678,8 @@ static void R_StudioDrawPoints( void ) R_StudioSetupTextureHeader (); + g_nNumArrayVerts = g_nNumArrayElems = 0; + if( !m_pTextureHeader ) return; if( RI.currententity->curstate.renderfx == kRenderFxGlowShell ) g_nStudioCount++; @@ -1671,7 +1687,12 @@ static void R_StudioDrawPoints( void ) m_skinnum = RI.currententity->curstate.skin; pvertbone = ((byte *)m_pStudioHeader + m_pSubModel->vertinfoindex); pnormbone = ((byte *)m_pStudioHeader + m_pSubModel->norminfoindex); - ptexture = (mstudiotexture_t *)((byte *)m_pTextureHeader + m_pTextureHeader->textureindex); + + // NOTE: user can comment call StudioRemapColors and remap_info will be unavailable + if( m_fDoRemap ) ptexture = CL_GetRemapInfoForEntity( RI.currententity )->ptexture; + else ptexture = (mstudiotexture_t *)((byte *)m_pTextureHeader + m_pTextureHeader->textureindex); + + ASSERT( ptexture != NULL ); pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex); pstudioverts = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->vertindex); @@ -1757,20 +1778,56 @@ static void R_StudioDrawPoints( void ) while( i = *( ptricmds++ )) { + int vertexState = 0; + qboolean tri_strip; + if( i < 0 ) { pglBegin( GL_TRIANGLE_FAN ); + tri_strip = false; i = -i; } else { pglBegin( GL_TRIANGLE_STRIP ); + tri_strip = true; } r_stats.c_studio_polys++; for( ; i > 0; i--, ptricmds += 4 ) { + // build in indices + if( vertexState++ < 3 ) + { + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; + } + else if( tri_strip ) + { + // flip triangles between clockwise and counter clockwise + if( vertexState & 1 ) + { + // draw triangle [n-2 n-1 n] + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 2; + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 1; + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; + } + else + { + // draw triangle [n-1 n-2 n] + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 1; + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 2; + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; + } + } + else + { + // draw triangle fan [0 n-1 n] + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - ( vertexState - 1 ); + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts - 1; + g_xarrayelems[g_nNumArrayElems++] = g_nNumArrayVerts; + } + if( flags & STUDIO_NF_CHROME || ( g_nFaceFlags & STUDIO_NF_CHROME )) pglTexCoord2f( g_chrome[ptricmds[1]][0] * s, g_chrome[ptricmds[1]][1] * t ); else pglTexCoord2f( ptricmds[2] * s, ptricmds[3] * t ); @@ -1796,6 +1853,8 @@ static void R_StudioDrawPoints( void ) av = g_xformverts[ptricmds[0]]; pglVertex3f( av[0], av[1], av[2] ); + VectorCopy( av, g_xarrayverts[g_nNumArrayVerts] ); // store off vertex + g_nNumArrayVerts++; } pglEnd(); } @@ -2053,9 +2112,28 @@ R_StudioSetRemapColors =============== */ -void R_StudioSetRemapColors( int top, int bottom ) +void R_StudioSetRemapColors( int newTop, int newBottom ) { - // TODO: implement + // update colors for viewentity + if( RI.currententity == &clgame.viewent ) + { + player_info_t *pLocalPlayer; + + // copy top and bottom colors for viewmodel + if(( pLocalPlayer = pfnPlayerInfo( clgame.viewent.curstate.number - 1 )) != NULL ) + { + newTop = bound( 0, pLocalPlayer->topcolor, 360 ); + newBottom = bound( 0, pLocalPlayer->bottomcolor, 360 ); + } + } + + CL_AllocRemapInfo( newTop, newBottom ); + + if( CL_GetRemapInfoForEntity( RI.currententity )) + { + CL_UpdateRemapInfo( newTop, newBottom ); + m_fDoRemap = true; + } } /* @@ -2192,6 +2270,7 @@ pfnStudioSetHeader void R_StudioSetHeader( studiohdr_t *pheader ) { m_pStudioHeader = pheader; + m_fDoRemap = false; } /* @@ -2234,6 +2313,8 @@ static void R_StudioRestoreRenderer( void ) // restore depthmask state for sprites etc if( glState.drawTrans ) pglDepthMask( GL_FALSE ); + + m_fDoRemap = false; } /* @@ -2256,15 +2337,85 @@ Xash3D is always works in hadrware mode */ static int pfnIsHardware( void ) { - return true; + return 1; // 0 is Software, 1 is OpenGL, 2 is Direct3D } -static void StudioDrawShadow( void ) +/* +=============== +R_StudioGetShadowImpactAndDir +=============== +*/ +void R_StudioGetShadowImpactAndDir( pmtrace_t *ptr, vec3_t lightdir ) { - // in GoldSrc shadow call is dsiabled with 'return' at start of the function - // some mods used a hack with calling DrawShadow ahead of 'return' - // this code is for HL compatibility. -// MsgDev( D_INFO, "GL_StudioDrawShadow()\n" ); // just a debug + vec3_t start, end; + studiolight_t *plight; + + plight = &g_studiolight; + + VectorSet( lightdir, -0.5, -0.2, -1.0f ); + VectorNormalizeFast( lightdir ); + VectorCopy( RI.currententity->origin, start ); + start[2] += 78; + VectorMA( start, 1024.0f, lightdir, end ); + + *ptr = PM_PlayerTrace( clgame.pmove, start, end, PM_STUDIO_IGNORE, 2, -1, NULL ); +} + +/* +=============== +R_StudioDeformShadow + +Deform vertices by specified lightdir +=============== +*/ +void R_StudioDeformShadow( void ) +{ + float *verts, planedist, dist; + vec3_t planenormal, lightdir, lightdir2, point; + int numVerts; + pmtrace_t tr; + + R_StudioGetShadowImpactAndDir( &tr, lightdir ); + + Matrix3x4_VectorIRotate( g_rotationmatrix, lightdir, lightdir2 ); + Matrix3x4_VectorIRotate( g_rotationmatrix, tr.plane.normal, planenormal ); +// VectorScale( planenormal, RI.currententity->curstate.scale, planenormal ); + + VectorSubtract( tr.endpos, RI.currententity->origin, point ); + planedist = DotProduct( point, tr.plane.normal ) + 1; + dist = -1.0f / DotProduct( lightdir2, planenormal ); + VectorScale( lightdir2, dist, lightdir2 ); + + verts = g_xarrayverts[0]; + numVerts = g_nNumArrayVerts; + + for( ; numVerts > 0; numVerts--, verts += 3 ) + { + dist = DotProduct( verts, tr.plane.normal ) - planedist; + if( dist > 0 ) VectorMA( verts, dist, lightdir, verts ); + } +} + +static void R_StudioDrawPlanarShadow( void ) +{ + R_StudioDeformShadow (); + +// if( glState.stencilEnabled ) +// pglEnable( GL_STENCIL_TEST ); + + pglEnableClientState( GL_VERTEX_ARRAY ); + pglVertexPointer( 3, GL_FLOAT, 12, g_xarrayverts ); + + Msg( "DrawShadow( %i %i )\n", g_nNumArrayVerts, g_nNumArrayElems ); + + if( GL_Support( GL_DRAW_RANGEELEMENTS_EXT )) + pglDrawRangeElementsEXT( GL_TRIANGLES, 0, g_nNumArrayVerts, g_nNumArrayElems, GL_UNSIGNED_INT, g_xarrayelems ); + else pglDrawElements( GL_TRIANGLES, g_nNumArrayElems, GL_UNSIGNED_INT, g_xarrayelems ); + +// if( glState.stencilEnabled ) +// pglDisable( GL_STENCIL_TEST ); + + pglDisableClientState( GL_VERTEX_ARRAY ); } /* @@ -2293,7 +2444,7 @@ void _cdecl GL_StudioDrawShadow( void ) { shadow_alpha = 1.0 - r_shadowalpha.value * 0.5f; pglDisable( GL_TEXTURE_2D ); - pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); +// pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); pglEnable( GL_BLEND ); shadow_alpha2 = 1.0 - shadow_alpha; @@ -2303,16 +2454,16 @@ void _cdecl GL_StudioDrawShadow( void ) // depthmode = GL_LESS; // else depthmode = GL_GREATER; - pglDepthFunc( depthmode ); +// pglDepthFunc( depthmode ); - StudioDrawShadow(); + R_StudioDrawPlanarShadow(); // if( flt_100DB994 == 0.0 || flt_107BA8A8 < 0.5 ) depthmode2 = GL_LEQUAL; // else // depthmode2 = GL_GEQUAL; - pglDepthFunc( depthmode2 ); +// pglDepthFunc( depthmode2 ); pglEnable( GL_TEXTURE_2D ); pglDisable( GL_BLEND ); @@ -2792,9 +2943,9 @@ static int R_StudioDrawModel( int flags ) R_DrawStudioModel ================= */ -void R_DrawStudioModel( cl_entity_t *e ) +void R_DrawStudioModelInternal( cl_entity_t *e, qboolean follow_entity ) { - int flags, result; + int i, flags, result; if( RI.params & RP_ENVVIEW ) return; @@ -2825,6 +2976,34 @@ void R_DrawStudioModel( cl_entity_t *e ) result = pStudioDraw->StudioDrawPlayer( flags, &e->curstate ); else result = pStudioDraw->StudioDrawModel( flags ); } + + if( !result || follow_entity ) return; + + // NOTE: we must draw all followed entities + // immediately after drawing parent when cached bones is valid + for( i = 0; i < tr.num_child_entities; i++ ) + { + if( CL_GetEntityByIndex( tr.child_entities[i]->curstate.aiment ) == e ) + { + // copy the parent origin for right frustum culling + // FIXME: we really need to cull follow entities? + VectorCopy( e->origin, tr.child_entities[i]->origin ); + + RI.currententity = tr.child_entities[i]; + RI.currentmodel = RI.currententity->model; + R_DrawStudioModelInternal( RI.currententity, true ); + } + } +} + +/* +================= +R_DrawStudioModel +================= +*/ +void R_DrawStudioModel( cl_entity_t *e ) +{ + R_DrawStudioModelInternal( e, false ); } /* @@ -2900,13 +3079,62 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture size_t size; int flags = 0; char texname[128], name[128]; - + texture_t *tx = NULL; + if( ptexture->flags & STUDIO_NF_TRANSPARENT ) flags |= (TF_CLAMP|TF_NOMIPMAP); if( ptexture->flags & ( STUDIO_NF_NORMALMAP|STUDIO_NF_HEIGHTMAP )) flags |= TF_NORMALMAP; + // store some textures for remapping + if( !Q_strnicmp( ptexture->name, "DM_Base", 7 ) || !Q_strnicmp( ptexture->name, "remap", 5 )) + { + int i, size; + char val[6]; + byte *pixels; + + i = mod->numtextures; + mod->textures = (texture_t **)Mem_Realloc( mod->mempool, mod->textures, ( i + 1 ) * sizeof( texture_t* )); + size = ptexture->width * ptexture->height + 768; + tx = Mem_Alloc( mod->mempool, sizeof( *tx ) + size ); + mod->textures[i] = tx; + + // parse ranges and store it + // HACKHACK: store ranges into anim_min, anim_max etc + if( !Q_strnicmp( ptexture->name, "DM_Base", 7 )) + { + Q_strncpy( tx->name, "DM_Base", sizeof( tx->name )); + tx->anim_min = PLATE_HUE_START; // topcolor start + tx->anim_max = PLATE_HUE_END; // topcolor end + // bottomcolor start always equal is (topcolor end + 1) + tx->anim_total = SUIT_HUE_END;// bottomcolor end + } + else + { + Q_strncpy( tx->name, "DM_User", sizeof( tx->name )); // custom remapped + Q_strncpy( val, ptexture->name + 7, 4 ); + tx->anim_min = bound( 0, Q_atoi( val ), 255 ); // topcolor start + Q_strncpy( val, ptexture->name + 11, 4 ); + tx->anim_max = bound( 0, Q_atoi( val ), 255 ); // topcolor end + // bottomcolor start always equal is (topcolor end + 1) + Q_strncpy( val, ptexture->name + 15, 4 ); + tx->anim_total = bound( 0, Q_atoi( val ), 255 ); // bottomcolor end + } + + tx->width = ptexture->width; + tx->height = ptexture->height; + + // the pixels immediately follow the structures + pixels = (byte *)phdr + ptexture->index; + Q_memcpy( tx+1, pixels, size ); + + ptexture->flags |= STUDIO_NF_COLORMAP; // yes, this is colormap image + flags |= TF_FORCE_COLOR; + + mod->numtextures++; // done + } + // NOTE: replace index with pointer to start of imagebuffer, ImageLib expected it ptexture->index = (int)((byte *)phdr) + ptexture->index; size = sizeof( mstudiotexture_t ) + ptexture->width * ptexture->height + 768; @@ -2923,6 +3151,8 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture } else { + // duplicate texnum for easy acess + if( tx ) tx->gl_texturenum = ptexture->index; GL_SetTextureType( ptexture->index, TEX_STUDIO ); } } @@ -2968,14 +3198,17 @@ studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer ) Mod_LoadStudioModel ================= */ -void Mod_LoadStudioModel( model_t *mod, const void *buffer ) +void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded ) { studiohdr_t *phdr; + if( loaded ) *loaded = false; + loadmodel->mempool = Mem_AllocPool( va( "^2%s^7", loadmodel->name )); + loadmodel->type = mod_studio; + phdr = R_StudioLoadHeader( mod, buffer ); if( !phdr ) return; // bad model - loadmodel->mempool = Mem_AllocPool( va("^2%s^7", loadmodel->name )); #ifdef STUDIO_MERGE_TEXTURES if( phdr->numtextures == 0 ) { @@ -3027,11 +3260,12 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer ) // setup bounding box VectorCopy( phdr->bbmin, loadmodel->mins ); VectorCopy( phdr->bbmax, loadmodel->maxs ); - loadmodel->type = mod_studio; // all done loadmodel->numframes = R_StudioBodyVariations( loadmodel ); loadmodel->radius = RadiusFromBounds( loadmodel->mins, loadmodel->maxs ); loadmodel->flags = phdr->flags; // copy header flags + + if( loaded ) *loaded = true; } /* diff --git a/engine/common/build.c b/engine/common/build.c index 343c0480..8f46e4db 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -48,6 +48,6 @@ int Q_buildnum( void ) return b; #else - return 1540; + return 1613; #endif } \ No newline at end of file diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 506a8385..5bacda27 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -609,6 +609,7 @@ Cmd_LookupCmds void Cmd_LookupCmds( char *buffer, void *ptr, setpair_t callback ) { cmd_function_t *cmd; + cmdalias_t *alias; // nothing to process ? if( !callback ) return; @@ -618,6 +619,10 @@ void Cmd_LookupCmds( char *buffer, void *ptr, setpair_t callback ) if( !buffer ) callback( cmd->name, (char *)cmd->function, cmd->desc, ptr ); else callback( cmd->name, (char *)cmd->function, buffer, ptr ); } + + // lookup an aliases too + for( alias = cmd_alias; alias; alias = alias->next ) + callback( alias->name, alias->value, buffer, ptr ); } /* @@ -777,12 +782,18 @@ void Cmd_Unlink( int group ) cmd_function_t **prev; int count = 0; - if( Cvar_VariableInteger( "host_gameloaded" )) + if( Cvar_VariableInteger( "host_gameloaded" ) && ( group & CMD_EXTDLL )) { Msg( "can't unlink cvars while game is loaded\n" ); return; } + if( Cvar_VariableInteger( "host_clientloaded" ) && ( group & CMD_CLIENTDLL )) + { + Msg( "can't unlink cvars while client is loaded\n" ); + return; + } + prev = &cmd_functions; while( 1 ) { diff --git a/engine/common/common.h b/engine/common/common.h index 70172981..b4d65b97 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -76,7 +76,7 @@ typedef enum #include "com_model.h" #include "crtlib.h" -#define XASH_VERSION 0.85f // engine current version +#define XASH_VERSION 0.89f // engine current version // PERFORMANCE INFO #define MIN_FPS 15.0 // host minimum fps value for maxfps. diff --git a/engine/common/console.c b/engine/common/console.c index 847c97e9..b04a6d9e 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -29,6 +29,7 @@ convar_t *con_fontsize; #define COLOR_DEFAULT '7' #define CON_HISTORY 64 #define MAX_DBG_NOTIFY 128 +#define CON_MAXCMDS 4096 // auto-complete intermediate list #define CON_TEXTSIZE 131072 // 128 kb buffer @@ -93,6 +94,7 @@ typedef struct // chatfiled field_t chat; + string chat_cmd; // can be overrieded by user // console history field_t historyLines[CON_HISTORY]; @@ -103,14 +105,14 @@ typedef struct qboolean draw_notify; // true if we have NXPrint message // console auto-complete - field_t *completionField; - char *completionString; string shortestMatch; + field_t *completionField; // con.input or dedicated server fake field-line + char *completionString; + char *cmds[CON_MAXCMDS]; int matchCount; } console_t; static console_t con; -static qboolean chat_team; // say_team is active void Field_CharEvent( field_t *edit, int ch ); @@ -185,8 +187,22 @@ Con_ClearTyping */ void Con_ClearTyping( void ) { + int i; + Con_ClearField( &con.input ); con.input.widthInChars = con.linewidth; + + // free the old autocomplete list + for( i = 0; i < con.matchCount; i++ ) + { + if( con.cmds[i] != NULL ) + { + Mem_Free( con.cmds[i] ); + con.cmds[i] = NULL; + } + } + + con.matchCount = 0; } /* @@ -225,7 +241,10 @@ Con_MessageMode_f */ void Con_MessageMode_f( void ) { - chat_team = false; + if( Cmd_Argc() == 2 ) + Q_strncpy( con.chat_cmd, Cmd_Argv( 1 ), sizeof( con.chat_cmd )); + else Q_strncpy( con.chat_cmd, "say", sizeof( con.chat_cmd )); + Key_SetKeyDest( key_message ); } @@ -236,34 +255,10 @@ Con_MessageMode2_f */ void Con_MessageMode2_f( void ) { - chat_team = true; + Q_strncpy( con.chat_cmd, "say_team", sizeof( con.chat_cmd )); Key_SetKeyDest( key_message ); } -/* -================ -Con_ToggleChat_f -================ -*/ -void Con_ToggleChat_f( void ) -{ - Con_ClearTyping (); - - if( cls.key_dest == key_console ) - { - if( Cvar_VariableInteger( "sv_background" )) - UI_SetActiveMenu( true ); - else UI_SetActiveMenu( false ); - } - else - { - UI_SetActiveMenu( false ); - Key_SetKeyDest( key_console ); - } - - Con_ClearNotify(); -} - /* ================ Con_ToggleConsole_f @@ -654,7 +649,6 @@ void Con_Init( void ) Cmd_AddCommand( "toggleconsole", Con_ToggleConsole_f, "opens or closes the console" ); Cmd_AddCommand( "con_color", Con_SetColor_f, "set a custom console color" ); Cmd_AddCommand( "clear", Con_Clear_f, "clear console history" ); - Cmd_AddCommand( "togglechat", Con_ToggleChat_f, "toggle console chat" ); Cmd_AddCommand( "messagemode", Con_MessageMode_f, "enable message mode \"say\"" ); Cmd_AddCommand( "messagemode2", Con_MessageMode2_f, "enable message mode \"say_team\"" ); @@ -885,32 +879,29 @@ static qboolean Cmd_CheckName( const char *name ) /* =============== -pfnFindMatches +Con_AddCommandToList =============== */ -static void pfnFindMatches( const char *s, const char *unused1, const char *unused2, void *unused3 ) +static void Con_AddCommandToList( const char *s, const char *unused1, const char *unused2, void *unused3 ) { - int i; - if( *s == '@' ) return; // never show system cvars or cmds + if( con.matchCount >= CON_MAXCMDS ) return; // list is full + if( Q_strnicmp( s, con.completionString, Q_strlen( con.completionString ))) - return; + return; // no match - con.matchCount++; + con.cmds[con.matchCount++] = copystring( s ); +} - if( con.matchCount == 1 ) - { - Q_strncpy( con.shortestMatch, s, sizeof( con.shortestMatch )); - return; - } - - // cut shortestMatch to the amount common with s - for( i = 0; s[i]; i++ ) - { - if( Q_tolower( con.shortestMatch[i] ) != Q_tolower( s[i] )) - con.shortestMatch[i] = 0; - } +/* +================= +Con_SortCmds +================= +*/ +static int Con_SortCmds( const char **arg1, const char **arg2 ) +{ + return Q_stricmp( *arg1, *arg2 ); } /* @@ -918,7 +909,7 @@ static void pfnFindMatches( const char *s, const char *unused1, const char *unus pfnPrintMatches =============== */ -static void pfnPrintMatches( const char *s, const char *unused1, const char *m, void *unused2 ) +static void Con_PrintMatches( const char *s, const char *unused1, const char *m, void *unused2 ) { if( !Q_strnicmp( s, con.shortestMatch, Q_strlen( con.shortestMatch ))) { @@ -972,38 +963,54 @@ void Con_CompleteCommand( field_t *field ) field_t temp; string filename; autocomplete_list_t *list; + int i; + // setup the completion field con.completionField = field; // only look at the first token for completion purposes Cmd_TokenizeString( con.completionField->buffer ); con.completionString = Cmd_Argv( 0 ); - if( con.completionString[0] == '\\' || con.completionString[0] == '/' ) + + // skip backslash + while( *con.completionString && ( *con.completionString == '\\' || *con.completionString == '/' )) con.completionString++; - - con.matchCount = 0; - con.shortestMatch[0] = 0; if( !Q_strlen( con.completionString )) return; - Cmd_LookupCmds( NULL, NULL, pfnFindMatches ); - Cvar_LookupVars( 0, NULL, NULL, pfnFindMatches ); + // free the old autocomplete list + for( i = 0; i < con.matchCount; i++ ) + { + if( con.cmds[i] != NULL ) + { + Mem_Free( con.cmds[i] ); + con.cmds[i] = NULL; + } + } + + con.matchCount = 0; + con.shortestMatch[0] = 0; + + // find matching commands and variables + Cmd_LookupCmds( NULL, NULL, Con_AddCommandToList ); + Cvar_LookupVars( 0, NULL, NULL, Con_AddCommandToList ); + + if( !con.matchCount ) return; // no matches - if( con.matchCount == 0 ) return; // no matches Q_memcpy( &temp, con.completionField, sizeof( field_t )); if( Cmd_Argc() == 2 ) { - qboolean result = false; + qboolean result = false; // autocomplete second arg for( list = cmd_list; list->name; list++ ) { if( Cmd_CheckName( list->name )) { - result = list->func( Cmd_Argv(1), filename, MAX_STRING ); + result = list->func( Cmd_Argv( 1 ), filename, MAX_STRING ); break; } } @@ -1018,24 +1025,44 @@ void Con_CompleteCommand( field_t *field ) if( con.matchCount == 1 ) { - Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch ); - if( Cmd_Argc() == 1 ) - Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer )); + Q_sprintf( con.completionField->buffer, "\\%s", con.cmds[0] ); + if( Cmd_Argc() == 1 ) Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer )); else ConcatRemaining( temp.buffer, con.completionString ); con.completionField->cursor = Q_strlen( con.completionField->buffer ); - return; } + else + { + char *first, *last; + int len = 0; - // multiple matches, complete to shortest - Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch ); - con.completionField->cursor = Q_strlen( con.completionField->buffer ); - ConcatRemaining( temp.buffer, con.completionString ); + qsort( con.cmds, con.matchCount, sizeof( char* ), Con_SortCmds ); - Msg( "]%s\n", con.completionField->buffer ); + // find the number of matching characters between the first and + // the last element in the list and copy it + first = con.cmds[0]; + last = con.cmds[con.matchCount-1]; - // run through again, printing matches - Cmd_LookupCmds( NULL, NULL, pfnPrintMatches ); - Cvar_LookupVars( 0, NULL, NULL, pfnPrintMatches ); + while( *first && *last && Q_tolower( *first ) == Q_tolower( *last )) + { + first++; + last++; + + con.shortestMatch[len] = con.cmds[0][len]; + len++; + } + con.shortestMatch[len] = 0; + + // multiple matches, complete to shortest + Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch ); + con.completionField->cursor = Q_strlen( con.completionField->buffer ); + ConcatRemaining( temp.buffer, con.completionString ); + + Msg( "]%s\n", con.completionField->buffer ); + + // run through again, printing matches + Cmd_LookupCmds( NULL, NULL, Con_PrintMatches ); + Cvar_LookupVars( 0, NULL, NULL, Con_PrintMatches ); + } } /* @@ -1429,9 +1456,7 @@ void Key_Message( int key ) { if( con.chat.buffer[0] && cls.state == ca_active ) { - if( chat_team ) Q_snprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", con.chat.buffer ); - else Q_snprintf( buffer, sizeof( buffer ), "say \"%s\"\n", con.chat.buffer ); - + Q_snprintf( buffer, sizeof( buffer ), "%s \"%s\"\n", con.chat_cmd, con.chat.buffer ); Cbuf_AddText( buffer ); } @@ -1570,7 +1595,7 @@ void Con_DrawNotify( void ) if( cls.key_dest == key_message ) { - char buf[16]; + string buf; int len; currentColor = 7; @@ -1578,8 +1603,11 @@ void Con_DrawNotify( void ) start = con.charWidths[' ']; // offset one space at left screen side - if( chat_team ) Q_strncpy( buf, "say_team: ", sizeof( buf )); - else Q_strncpy( buf, "say: ", sizeof( buf )); + // update chatline position from client.dll + if( clgame.dllFuncs.pfnChatInputPosition ) + clgame.dllFuncs.pfnChatInputPosition( &start, &v ); + + Q_snprintf( buf, sizeof( buf ), "%s: ", con.chat_cmd ); Con_DrawStringLen( buf, &len, NULL ); Con_DrawString( start, v, buf, g_color_table[7] ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index cfcd4eb0..7cb784b8 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -1191,6 +1191,12 @@ void Cvar_Unlink( void ) convar_t **prev; int count = 0; + if( Cvar_VariableInteger( "host_clientloaded" )) + { + MsgDev( D_NOTE, "can't unlink cvars while client is loaded\n" ); + return; + } + prev = &cvar_vars; while( 1 ) diff --git a/engine/common/host.c b/engine/common/host.c index 61b82532..30565208 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -32,6 +32,7 @@ sysinfo_t SI; convar_t *host_serverstate; convar_t *host_gameloaded; +convar_t *host_clientloaded; convar_t *host_limitlocal; convar_t *host_cheats; convar_t *host_maxfps; @@ -688,6 +689,7 @@ int EXPORT Host_Main( const char *progname, int bChangeGame, pfnChangeGame func host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" ); host_serverstate = Cvar_Get( "host_serverstate", "0", CVAR_INIT, "displays current server state" ); host_gameloaded = Cvar_Get( "host_gameloaded", "0", CVAR_INIT, "inidcates a loaded game.dll" ); + host_clientloaded = Cvar_Get( "host_clientloaded", "0", CVAR_INIT, "inidcates a loaded client.dll" ); host_limitlocal = Cvar_Get( "host_limitlocal", "0", 0, "apply cl_cmdrate and rate to loopback connection" ); con_gamemaps = Cvar_Get( "con_gamemaps", "1", CVAR_ARCHIVE, "when true show only maps in game folder" ); diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 855ac69a..54300b82 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -31,6 +31,12 @@ GNU General Public License for more details. #define DVIS_PHS 1 #define ANIM_CYCLE 2 +// remapping info +#define SUIT_HUE_START 192 +#define SUIT_HUE_END 223 +#define PLATE_HUE_START 160 +#define PLATE_HUE_END 191 + #define SURF_INFO( surf, mod ) ((mextrasurf_t *)mod->cache.data + (surf - mod->surfaces)) // model flags (stored in model_t->flags) diff --git a/engine/common/model.c b/engine/common/model.c index 1833ed13..13ac9f0b 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -934,7 +934,7 @@ static void Mod_LoadSurfaces( const dlump_t *l ) if( !Q_strncmp( tex->name, "sky", 3 )) out->flags |= (SURF_DRAWTILED|SURF_DRAWSKY); - if( tex->name[0] == '*' || tex->name[0] == '!' ) + if(( tex->name[0] == '*' && Q_stricmp( tex->name, "*default" )) || tex->name[0] == '!' ) out->flags |= (SURF_DRAWTURB|SURF_DRAWTILED); if( !Q_strnicmp( tex->name, "water", 5 ) || !Q_strnicmp( tex->name, "laser", 5 )) @@ -1521,13 +1521,15 @@ void Mod_UnloadBrushModel( model_t *mod ) Mod_LoadBrushModel ================= */ -static void Mod_LoadBrushModel( model_t *mod, const void *buffer ) +static void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ) { int i, j; dheader_t *header; dmodel_t *bm; - + + if( loaded ) *loaded = false; header = (dheader_t *)buffer; + loadmodel->type = mod_brush; i = header->version; switch( i ) @@ -1544,7 +1546,6 @@ static void Mod_LoadBrushModel( model_t *mod, const void *buffer ) } // will be merged later - loadmodel->type = mod_brush; if( world.loading ) world.version = i; bmodel_version = i; // share it @@ -1651,6 +1652,8 @@ static void Mod_LoadBrushModel( model_t *mod, const void *buffer ) mod = loadmodel; } } + + if( loaded ) *loaded = true; // all done } /* @@ -1714,6 +1717,7 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash ) { byte *buf; char tempname[64]; + qboolean loaded; if( !mod ) { @@ -1749,20 +1753,20 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash ) switch( *(uint *)buf ) { case IDSTUDIOHEADER: - Mod_LoadStudioModel( mod, buf ); + Mod_LoadStudioModel( mod, buf, &loaded ); break; case IDSPRITEHEADER: - Mod_LoadSpriteModel( mod, buf ); + Mod_LoadSpriteModel( mod, buf, &loaded ); break; case Q1BSP_VERSION: case HLBSP_VERSION: - Mod_LoadBrushModel( mod, buf ); + Mod_LoadBrushModel( mod, buf, &loaded ); break; } Mem_Free( buf ); - if( mod->type == mod_bad ) + if( !loaded ) { Mod_FreeModel( mod ); diff --git a/engine/common/soundlib/mpeg.old b/engine/common/soundlib/mpeg.old deleted file mode 100644 index 1979c5eb5356aca41606c3362fcc2d2cdd2716e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143350 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

_sM+w&eimHTn?PxY-e~ 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 diff --git a/engine/common/soundlib/snd_mp3.old b/engine/common/soundlib/snd_mp3.old deleted file mode 100644 index 0b1eae0b..00000000 --- a/engine/common/soundlib/snd_mp3.old +++ /dev/null @@ -1,480 +0,0 @@ -/* -snd_mp3.c - mp3 format loading and streaming -Copyright (C) 2010 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - -#include "soundlib.h" - -/* -======================================================================= - LIBMAD DEFINITION -======================================================================= -*/ -#define BUFFER_GUARD 8 -#define BUFFER_MDLEN (511 + 2048 + BUFFER_GUARD) -#define BUFFER_SIZE 4096 // must be large than BUFFER_MDLEN - -#define MPEG_F_FRACBITS 28 -#define MPEG_F( x ) ((int)( x##L )) -#define MPEG_F_ONE MPEG_F( 0x10000000 ) - -enum -{ - MP3_ERROR_NONE = 0x0000, // no error - MP3_ERROR_BUFLEN = 0x0001, // input buffer too small (or EOF) - MP3_ERROR_BUFPTR = 0x0002, // invalid (null) buffer pointer - MP3_ERROR_NOMEM = 0x0031, // not enough memory - MP3_ERROR_LOSTSYNC = 0x0101, // lost synchronization - MP3_ERROR_BADLAYER = 0x0102, // reserved header layer value - MP3_ERROR_BADBITRATE = 0x0103, // forbidden bitrate value - MP3_ERROR_BADSAMPLERATE = 0x0104, // reserved sample frequency value - MP3_ERROR_BADEMPHASIS = 0x0105, // reserved emphasis value - MP3_ERROR_BADCRC = 0x0201, // CRC check failed - MP3_ERROR_BADBITALLOC = 0x0211, // forbidden bit allocation value - MP3_ERROR_BADSCALEFACTOR = 0x0221, // bad scalefactor index - MP3_ERROR_BADMODE = 0x0222, // bad bitrate/mode combination - MP3_ERROR_BADFRAMELEN = 0x0231, // bad frame length - MP3_ERROR_BADBIGVALUES = 0x0232, // bad big_values count - MP3_ERROR_BADBLOCKTYPE = 0x0233, // reserved block_type - MP3_ERROR_BADSCFSI = 0x0234, // bad scalefactor selection info - MP3_ERROR_BADDATAPTR = 0x0235, // bad main_data_begin pointer - MP3_ERROR_BADPART3LEN = 0x0236, // bad audio data length - MP3_ERROR_BADHUFFTABLE = 0x0237, // bad Huffman table select - MP3_ERROR_BADHUFFDATA = 0x0238, // Huffman data overrun - MP3_ERROR_BADSTEREO = 0x0239 // incompatible block_type for JS -}; - -enum -{ - MODE_SINGLE_CHANNEL = 0, // single channel - MODE_DUAL_CHANNEL, // dual channel - MODE_JOINT_STEREO, // joint (MS/intensity) stereo - MODE_STEREO // normal LR stereo -}; - -typedef struct -{ - uint samplerate; // sampling frequency (Hz) - word channels; // number of channels - word length; // number of samples per channel - int samples[2][1152]; // PCM output samples [ch][sample] -} pcm_t; - -typedef struct -{ - int filter[2][2][2][16][8]; // polyphase filterbank outputs - uint phase; // current processing phase - pcm_t pcm; // PCM output -} synth_t; - -typedef struct -{ - const byte *byte; - word cache; - word left; -} bitptr_t; - -typedef struct -{ - long seconds; // whole seconds - dword fraction; // 1 / TIMER_RESOLUTION seconds -} mp3_timer_t; - -typedef struct -{ - int layer; // audio layer (1, 2, or 3) - int mode; // channel mode (see above) - int mode_extension; // additional mode info - int emphasis; // de-emphasis to use (see above) - - dword bitrate; // stream bitrate (bps) - uint samplerate; // sampling frequency (Hz) - - word crc_check; // frame CRC accumulator - word crc_target; // final target CRC checksum - - int flags; // flags (see below) - int private_bits; // private bits (see below) - mp3_timer_t duration; // audio playing time of frame -} mp3_header_t; - -typedef struct -{ - mp3_header_t header; // MPEG audio header - int options; // decoding options (from stream) - - int sbsample[2][36][32]; // synthesis subband filter samples - int (*overlap)[2][32][18]; // Layer III block overlap data -} mp3_frame_t; - -typedef struct -{ - const byte *buffer; // input bitstream buffer - const byte *bufend; // end of buffer - dword skiplen; // bytes to skip before next frame - - int sync; // stream sync found - dword freerate; // free bitrate (fixed) - - const byte *this_frame; // start of current frame - const byte *next_frame; // start of next frame - bitptr_t ptr; // current processing bit pointer - - bitptr_t anc_ptr; // ancillary bits pointer - uint anc_bitlen; // number of ancillary bits - - byte (*data)[BUFFER_MDLEN]; - // Layer III data() - uint md_len; // bytes in data - - int options; // decoding options - int error; // error code -} mp3_stream_t; - -typedef struct -{ - synth_t synth; - mp3_stream_t stream; - mp3_frame_t frame; - int buffer_length; // for reading - byte buffer[BUFFER_SIZE];// frame buffer -} mpegfile_t; - -// libmad exports -extern void mad_synth_init( synth_t* ); -extern void mad_synth_frame( synth_t*, const mp3_frame_t* ); -extern void mad_stream_init( mp3_stream_t* ); -extern void mad_stream_buffer( mp3_stream_t*, const byte*, dword ); -extern void mad_stream_finish( mp3_stream_t* ); -extern void mad_frame_init( mp3_frame_t* ); -extern int mad_frame_decode( mp3_frame_t*, mp3_stream_t* ); -extern void mad_frame_finish( mp3_frame_t* ); - - -/* -================================================================= - - MPEG decompression - -================================================================= -*/ -static int mpeg_read( file_t *file, mpegfile_t *mpeg ) -{ - int ret; - - while( 1 ) - { - ret = FS_Read( file, &mpeg->buffer[mpeg->buffer_length], BUFFER_SIZE - mpeg->buffer_length ); - - // no more bytes are left - if( ret <= 0 ) break; - - mpeg->buffer_length += ret; - - while( 1 ) - { - mad_stream_buffer( &mpeg->stream, mpeg->buffer, mpeg->buffer_length ); - ret = mad_frame_decode( &mpeg->frame, &mpeg->stream ); - - if( mpeg->stream.next_frame ) - { - int length; - - length = mpeg->buffer + mpeg->buffer_length - mpeg->stream.next_frame; - memmove( mpeg->buffer, mpeg->stream.next_frame, length ); - mpeg->buffer_length = length; - } - - if( !ret ) return 1; - if( mpeg->stream.error == MP3_ERROR_BUFLEN ) - break; - } - } - return 0; -} - -static int mpeg_read_mem( const byte *buffer, int *pos, size_t filesize, mpegfile_t *mpeg ) -{ - int ret, readSize; - - while( 1 ) - { - readSize = ( BUFFER_SIZE - mpeg->buffer_length ); - - if(( *pos + readSize ) > filesize ) - readSize = ( filesize - *pos ); - Q_memcpy( &mpeg->buffer[mpeg->buffer_length], buffer + *pos, readSize ); - - // no more bytes are left - if( readSize <= 0 ) break; - *pos += readSize; - mpeg->buffer_length += readSize; - - while( 1 ) - { - mad_stream_buffer( &mpeg->stream, mpeg->buffer, mpeg->buffer_length ); - ret = mad_frame_decode( &mpeg->frame, &mpeg->stream ); - - if( mpeg->stream.next_frame ) - { - int length; - - length = mpeg->buffer + mpeg->buffer_length - mpeg->stream.next_frame; - memmove( mpeg->buffer, mpeg->stream.next_frame, length ); - mpeg->buffer_length = length; - } - - if( !ret ) return 1; - if( mpeg->stream.error == MP3_ERROR_BUFLEN ) - break; - } - } - return 0; -} - -static int mpeg_scale( int sample ) -{ - sample += (1 << ( MPEG_F_FRACBITS - 16 )); - - if( sample >= MPEG_F_ONE ) sample = MPEG_F_ONE - 1; - else if( sample < -MPEG_F_ONE ) sample = -MPEG_F_ONE; - - return sample >> ( MPEG_F_FRACBITS + 1 - 16 ); -} - -static int mpeg_size( mp3_frame_t *frame, long bytes ) -{ - return bytes * 8 / frame->header.bitrate * sound.channels * sound.rate * sound.width; -} - - -/* -================================================================= - - MPEG decompression - -================================================================= -*/ -qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize ) -{ - mpegfile_t mpeg; - size_t pos = 0; - size_t bytesWrite = 0; - - // load the file - if( !buffer || filesize <= 0 ) - return false; - - Q_memset( &mpeg, 0, sizeof( mpeg )); - mad_synth_init( &mpeg.synth ); - mad_stream_init( &mpeg.stream ); - mad_frame_init( &mpeg.frame ); - - if( mpeg_read_mem( buffer, &pos, filesize, &mpeg ) == 0 ) - { - MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name ); - mad_stream_finish( &mpeg.stream ); - mad_frame_finish( &mpeg.frame ); - return false; - } - - sound.channels = ( mpeg.frame.header.mode == MODE_SINGLE_CHANNEL ) ? 1 : 2; - sound.rate = mpeg.frame.header.samplerate; - sound.width = 2; // always 16-bit PCM - sound.loopstart = -1; - sound.size = mpeg_size( &mpeg.frame, filesize ); - - if( !sound.size ) - { - // bad ogg file - MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name ); - mad_stream_finish( &mpeg.stream ); - mad_frame_finish( &mpeg.frame ); - return false; - } - - sound.type = WF_PCMDATA; - sound.wav = (byte *)Mem_Alloc( host.soundpool, sound.size ); - - // decompress mpg into pcm wav format - while( bytesWrite < sound.size ) - { - word *data; - int i; - - mad_synth_frame( &mpeg.synth, &mpeg.frame ); - data = (short *)(sound.wav + bytesWrite); - - for( i = 0; i < mpeg.synth.pcm.length; i++ ) - { - if( sound.channels == 2 ) - { - *data++ = mpeg_scale( mpeg.synth.pcm.samples[0][i] ); - *data++ = mpeg_scale( mpeg.synth.pcm.samples[1][i] ); - } - else - { - *data++ = mpeg_scale( mpeg.synth.pcm.samples[0][i] ); - } - - bytesWrite += ( sound.width * sound.channels ); - if( bytesWrite >= sound.size ) break; - } - - if( !mpeg_read_mem( buffer, &pos, filesize, &mpeg )) - break; - } - - sound.samples = bytesWrite / ( sound.width * sound.channels ); - mad_stream_finish( &mpeg.stream ); - mad_frame_finish( &mpeg.frame ); - - return true; -} - -/* -================= -Stream_OpenMPG -================= -*/ -stream_t *Stream_OpenMPG( const char *filename ) -{ - mpegfile_t *mpegFile; - stream_t *stream; - file_t *file; - - file = FS_Open( filename, "rb", false ); - if( !file ) return NULL; - - // at this point we have valid stream - stream = Mem_Alloc( host.soundpool, sizeof( stream_t )); - stream->file = file; - - mpegFile = Mem_Alloc( host.soundpool, sizeof( mpegfile_t )); - - mad_synth_init( &mpegFile->synth ); - mad_stream_init( &mpegFile->stream ); - mad_frame_init( &mpegFile->frame ); - - if( mpeg_read( file, mpegFile ) == 0 ) - { - MsgDev( D_ERROR, "Stream_OpenMPG: couldn't open %s\n", filename ); - mad_stream_finish( &mpegFile->stream ); - mad_frame_finish( &mpegFile->frame ); - Mem_Free( mpegFile ); - Mem_Free( stream ); - FS_Close( file ); - return NULL; - } - - stream->pos = 0; // how many samples left from previous frame - stream->channels = ( mpegFile->frame.header.mode == MODE_SINGLE_CHANNEL ) ? 1 : 2; - stream->rate = mpegFile->frame.header.samplerate; - stream->width = 2; // always 16 bit - stream->ptr = mpegFile; - stream->type = WF_MPGDATA; - - // g-cont: there is a stupid way... - if( stream->rate > 44100 ) - { - mpegFile->stream.options = 0x0002; - stream->rate /= 2; - } - - return stream; -} - -/* -================= -Stream_ReadMPG - -assume stream is valid -================= -*/ -long Stream_ReadMPG( stream_t *stream, long bytes, void *buffer ) -{ - // buffer handling - int bytesRead = 0; - mpegfile_t *mpg; - - mpg = (mpegfile_t *)stream->ptr; - ASSERT( mpg != NULL ); - - while( 1 ) - { - pcm_t *wav; - word *data; - int i; - - if( !stream->pos ) - { - // if there are no bytes remainig so we can synth new frame - mad_synth_frame( &mpg->synth, &mpg->frame ); - } - wav = &mpg->synth.pcm; - data = (word *)((byte *)buffer + bytesRead); - - for( i = stream->pos; i < wav->length; i++ ) - { - if( stream->channels == 2 ) - { - *data++ = mpeg_scale( wav->samples[0][i] ); - *data++ = mpeg_scale( wav->samples[1][i] ); - } - else - { - *data++ = mpeg_scale( wav->samples[0][i] ); - } - bytesRead += ( stream->width * stream->channels ); - - if( bytesRead >= bytes ) - { - // continue from this sample on a next call - stream->pos = i; - return bytesRead; - } - } - - stream->pos = 0; // no bytes remainig - if( !mpeg_read( stream->file, mpg )) break; - } - return 0; -} - -/* -================= -Stream_FreeMPG - -assume stream is valid -================= -*/ -void Stream_FreeMPG( stream_t *stream ) -{ - if( stream->ptr ) - { - mpegfile_t *mpg; - - mpg = (mpegfile_t *)stream->ptr; - - mad_stream_finish( &mpg->stream ); - mad_frame_finish( &mpg->frame ); - Mem_Free( stream->ptr ); - } - - if( stream->file ) - { - FS_Close( stream->file ); - } - - Mem_Free( stream ); -} \ No newline at end of file diff --git a/engine/engine.dsp b/engine/engine.dsp index a16c03ee..800c5dce 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 1 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c -# ADD CPP /nologo /MD /W3 /GX /O2 /Oy /I "./" /I "common" /I "common/imagelib" /I "common/soundlib" /I "server" /I "client" /I "client/vgui" /I "../common" /I "../game_shared" /I "../pm_shared" /I "../utils/vgui/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "./" /I "common" /I "common/imagelib" /I "common/soundlib" /I "server" /I "client" /I "client/vgui" /I "../common" /I "../game_shared" /I "../pm_shared" /I "../utils/vgui/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c # SUBTRACT CPP /Fr /YX # ADD BASE MTL /nologo /D "NDEBUG" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -154,6 +154,10 @@ SOURCE=.\client\cl_pmove.c # End Source File # Begin Source File +SOURCE=.\client\cl_remap.c +# End Source File +# Begin Source File + SOURCE=.\client\cl_scrn.c # End Source File # Begin Source File diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 17edc32d..894f505e 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -773,9 +773,9 @@ Writes all update values to a bitbuf */ void SV_FullClientUpdate( sv_client_t *cl, sizebuf_t *msg ) { - int i; char info[MAX_INFO_STRING]; - + int i; + i = cl - svs.clients; BF_WriteByte( msg, svc_updateuserinfo ); @@ -1635,6 +1635,7 @@ void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo ) { if( current == cl || current->state != cs_spawned ) continue; + if( !Q_stricmp( current->name, val )) break; } diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 2c30ed94..bf26049c 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -775,7 +775,7 @@ void SV_InitOperatorCommands( void ) Cmd_AddCommand( "map", SV_Map_f, "start new level" ); Cmd_AddCommand( "newgame", SV_Newgame_f, "begin new game" ); - Cmd_AddCommand( "endgame", SV_Endgame_f, "end current game" ); + Cmd_AddCommand( "killgame", SV_Endgame_f, "end current game" ); Cmd_AddCommand( "hazardcourse", SV_HazardCourse_f, "starting a Hazard Course" ); Cmd_AddCommand( "changelevel", SV_ChangeLevel_f, "changing level" ); Cmd_AddCommand( "restart", SV_Restart_f, "restarting current level" ); diff --git a/engine/studio.h b/engine/studio.h index db2960b2..df189950 100644 --- a/engine/studio.h +++ b/engine/studio.h @@ -307,7 +307,7 @@ typedef struct } mstudiobodyparts_t; // skin info -typedef struct +typedef struct mstudiotex_s { char name[64]; int flags; diff --git a/game_launch/game.ncb b/game_launch/game.ncb deleted file mode 100644 index 0082513fe036212118f79d5bc50c973f1fcf801c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41984 zcmeI5-)|IE6vxjl1qxVNeh39o1_RhaZEHyk60OCyLJ1{J7k|aoI_yrnqq{TB&a?$% zBGDJ4Au%z&D1QLP7Km?XEfxlMWx+4(*5fA|p5CIVo0TB=Z5fA|p5CIVo0TB=Z5fA|p z5CIVo0TB=Z5fA|p5CIWb&IFLTpz!~4egoyZ2#A0Ph=2%)fCz|y2#A0Ph=2%)fCz|y z2#A0Ph=2%)fCz|y2#A0Ph=2%yKtB=yHkQcTMsuEFACK2T0s3$|^kS~+bSv0cLP z)NefAWZibyZnB)TUxV{(vlxX`ipSSlyY081?;dZq4(qZLEdR>mEq26Sw2#rgsfFc@ z)`qPY<+aKAT5TP+Khb{e9UgD9_1O0Ecv9YE8?e2Na>d`x){2eo+2#uDk5Jm%Rm=Tx z=ma!^eg^%T7VZ0?-$B2G7N8OlSP2BWe&|iL=D!(D<6^&s_5^Opi(qnRdSbUc<;8w5 z8CJ@rFh5iD?v06PoI0rv^)F3b#eVs5bAwqj1O z$sV;GI3f`N5qQ`Ht|oKc9jUz-^=aFjD!``fqlfL5`WFEacmM>F;r{^oQ5Pa00*yz2 z<6rlH#(Q7nkqA5p0v!9>pj?MaG%OPX?gEpKitPMV=Z2oEYh<-*3-u%t_aKC7~f+( z7x0sKA}km3={M`Skk4!3ei|wf0TEc7fP!9&b5X4btONqLZ`{7Yzmwnd59Ok9_kSC} ze}2vn^5I<6UFbJN?ne>nH$RA-qhlk(r%sQ*VaG>KWlkSGH8|`PMvy7s203qj-B{V1 zy<2~BEOUC?DNYZT@+bYkOHLG^kST<7mN^$q3>MsA$~%Tc0hQ z46mENzrYQrV`3tg_i{z&z`o9o`95`8^gg{E^3fB$-1p`)eTUyl`agNk!9vZ^8mr{$ z?msQBw=*H~kG*vPd>353=U%4`9?6bXzuf6%zvxBT(Ys&$^r|1txJ4%u&*c3uI~bNq zVUQio40*H99nQXlj19cChx)pa6SAbUx3wIvA&%3x(5Drid|BjMHEWQ^hEpa@NwtIPGPM8HLkcPIoE&)wWlw zmQz^@Lv0kMsm&Lrt5KMWwoo{2-o3DLDdmb&nRF%ox3?PqGgJQ0QmE>+BCtFN)b26O z_&z`8|L}YL==ng^j=?hj=Xz@;|L1d*ng8<(%FO?{wf~DG|K~Exc>Oy#&itRBV444O z9(Id8ru-kUkcdE|5vYy-M)OPlh=2&xp8&_d;(zt$tR6(55eYE1rubhYzBBSk1nN&f z@xS^rRu3Z3s08%Oc|MmIbEw%^i z|Bu@fhy(1#^S`^yssDwk2N4hf5fA|p5CIVo0TB=Z5fA|p5CIVo0TB=Z5fA|p5CIVo K0TEb%1pWrQ!Ull= diff --git a/game_launch/game.opt b/game_launch/game.opt deleted file mode 100644 index 697729b86af86f8265cc8454ebbc900ed705e8c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52736 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

4)bXVjI1>3H(&y60NHX)mkteIkaMr}4KL zS~s9^FZK?cl>R4O{(4qam7j-xz})+7nlBbgr}+!wlr=RCjdwZ!7jhFgZ=J^7hCd7 zH*?1iXSRSBMdXx?mDxA}-1%SG33$$F0;4|zx%0oQxqE8Hp6#}F=YNgPy>#b)IVQ2X z^S{uajQ)7%&i|Tt-kCf9%bow_&i~5JHdm7p`J=u&|LYPn$lUo~oU!Z9|GLyHLwEky ztxVuFx#ge|DR9OEkV>xwjGX zdNCEz!$|9c4<6xDE2JjmoG_BVNxVMA>`{UKWFqRgz%#3a0Uc|sRFJ=j! z-$-))dOY04m0p;~X{5#!Z3$rgwpyc`#BBfg@2>we4Ow^nXLj+l zyZ)04jUDU0-1VRDv~d83Pj~%icKZo;{U=^;-SwXnZcXB@|8&=X+KZFj^`GN+-kI=+ ze|P<-{$5`H>H7a%{~s+x1lRv(>B0X0e)Ion)+|6&UmHJ-kq*29>kF`)-#MY1Zo){{ z|7Y$p=lcKL{of|HiJ|NN8>27R|L6Mu#%?s|`u}p=IF#m$>;H58f1EYv?*EqIZjqPV zv&Z%Sx&A-b|Ch`DprZ?aT>qcz{}aOf{wvKa_xrEZf&2Z}_ty7cHLU-ogKq@eQ(!ip zFP2^xi!@yGj#SK7qflimMu;#{sg_T7{}9Xs1v!JhWXomC6Agfii_ zWj0uD1MHk>U8=C9rSBLk#ohUz=F)R_{kQpRf&2Ym^YL)^e{}bM#P_{j|8LU${;&J} zU-$dJ?)QH=4b`4v=YIbe-?kp()^@-DJMpKo-S7WeAJ}!j|7&gfpVrNf=8k^BT^~i` zH(+;HBAzsiy0uK%_@TGsLX;{Po$9X3y+(yssSk~>Pe z`#*;4xohtHPk3~4H9sQ8%85Qx-{6miuq3{{J`w4_E5O)Oz9PBnzuooU{OFb5l!?a2 zZl2)o|LE@jnB!ZJ?Pzh=f4l3y-SyvrL^STM|F+htT;woeS7Cld)1M0MGBvd^Qpptz zG*hhY(MQK3Z^O0+jc_Kq)9^NBI_O_$@Tr}>{l@lCM+{T!8rP>n$u@s9os5OL*!CJR z%Yah_iW|9qdP-esL_YKGM?sAZ_tf};O}gvnumItems-1)*sl->generic.charHeight)/2)) < 2) { - sl->scrollBarHeight = (downY - upY - arrowHeight) - (step*(sl->numItems-1)); + sl->scrollBarHeight = (downY - upY - arrowHeight) - (step * (sl->numItems-1)); sl->scrollBarY = upY + arrowHeight + (step*sl->curItem); } else { - sl->scrollBarHeight = downY - upY - arrowHeight - (((sl->numItems-1)*sl->generic.charHeight)/2); - sl->scrollBarY = upY + arrowHeight + (((sl->curItem)*sl->generic.charHeight)/2); + sl->scrollBarHeight = downY - upY - arrowHeight - (((sl->numItems-1) * sl->generic.charHeight) / 2); + sl->scrollBarY = upY + arrowHeight + (((sl->curItem) * sl->generic.charHeight)/2); } if( sl->scrollBarSliding ) @@ -714,6 +714,7 @@ void UI_ScrollList_Draw( menuScrollList_s *sl ) w = sl->generic.width2; h = sl->generic.charHeight; y = sl->generic.y2 + sl->generic.charHeight; + for( i = sl->topItem; i < sl->topItem + sl->numRows; i++, y += sl->generic.charHeight ) { if( !sl->itemNames[i] ) diff --git a/make_sdk.bat b/make_sdk.bat index c1f9b3f1..96602839 100644 --- a/make_sdk.bat +++ b/make_sdk.bat @@ -10,6 +10,7 @@ if not exist D:\Xash3D\src_main\xash_sdk\common/ mkdir D:\Xash3D\src_main\xash_s if not exist D:\Xash3D\src_main\xash_sdk\mainui/ mkdir D:\Xash3D\src_main\xash_sdk\mainui\ if not exist D:\Xash3D\src_main\xash_sdk\mainui\legacy/ mkdir D:\Xash3D\src_main\xash_sdk\mainui\legacy if not exist D:\Xash3D\src_main\xash_sdk\utils/ mkdir D:\Xash3D\src_main\xash_sdk\utils\ +if not exist D:\Xash3D\src_main\xash_sdk\utils\makefont/ mkdir D:\Xash3D\src_main\xash_sdk\utils\makefont if not exist D:\Xash3D\src_main\xash_sdk\utils\vgui/ mkdir D:\Xash3D\src_main\xash_sdk\utils\vgui if not exist D:\Xash3D\src_main\xash_sdk\utils\vgui\include/ mkdir D:\Xash3D\src_main\xash_sdk\utils\vgui\include if not exist D:\Xash3D\src_main\xash_sdk\utils\vgui\lib/ mkdir D:\Xash3D\src_main\xash_sdk\utils\vgui\lib @@ -30,6 +31,7 @@ if not exist D:\Xash3D\src_main\xash_sdk\pm_shared/ mkdir D:\Xash3D\src_main\xas @copy /Y cl_dll\hl\*.* xash_sdk\cl_dll\hl\*.* @copy /Y dlls\*.* xash_sdk\dlls\*.* @copy /Y dlls\wpn_shared\*.* xash_sdk\dlls\wpn_shared\*.* +@copy /Y utils\makefont\*.* xash_sdk\utils\makefont\*.* @copy /Y utils\vgui\include\*.* xash_sdk\utils\vgui\include\*.* @copy /Y utils\vgui\lib\win32_vc6\*.* xash_sdk\utils\vgui\lib\win32_vc6\*.* @copy /Y game_shared\*.* xash_sdk\game_shared\*.* diff --git a/utils/makefont/CreateFont.bat b/utils/makefont/CreateFont.bat new file mode 100644 index 00000000..57e1d438 --- /dev/null +++ b/utils/makefont/CreateFont.bat @@ -0,0 +1,2 @@ +makefont.exe -font "Arial" fonts.wad +pause \ No newline at end of file diff --git a/utils/makefont/makefont.exe b/utils/makefont/makefont.exe new file mode 100644 index 0000000000000000000000000000000000000000..573feb168ae06e6b83fe62acd34ab047c588c8a8 GIT binary patch literal 61440 zcmeFae|!{GmN#6Tt|XPDLpP8>&>%sg0|uRFK#3i+1L+WyV279v5fXF;n3#2D7^niA zO`v0?XHvPAvb(aYyY37uI>XGkGd??x;;5LWF$ti^uW?Wq4eDyOg&HN3G!Rqo_uT4) z=+5lB@B2LO=kxwy_*B=ed(S=h+;e}Od(OF+%Dc7+PC*bvJeDO0d+??|AOHRDe+(p# zoA�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 literal 0 HcmV?d00001 diff --git a/xash_sdk.lst b/xash_sdk.lst index bb62707d..a3bf8ad2 100644 --- a/xash_sdk.lst +++ b/xash_sdk.lst @@ -17,6 +17,7 @@ xash_sdk\pm_shared\ xash_sdk\mainui\ xash_dsk\mainui\legacy xash_sdk\utils\ +xash_sdk\makefont\ xash_sdk\utils\vgui\ xash_sdk\utils\vgui\include\ xash_sdk\utils\vgui\lib\win32_vc6\ \ No newline at end of file