diff --git a/game/Game_local.cpp b/game/Game_local.cpp index a2cefa6..5fcb1b6 100644 --- a/game/Game_local.cpp +++ b/game/Game_local.cpp @@ -254,6 +254,11 @@ void idGameLocal::Clear( void ) { savedEventQueue.Init(); memset( lagometer, 0, sizeof( lagometer ) ); + + portalSkyEnt = NULL; + portalSkyActive = false; + playerOldEyePos.Zero(); + globalPortalSky = false; } /* @@ -547,6 +552,14 @@ void idGameLocal::SaveGame( idFile *f ) { savegame.WriteBool( isNewFrame ); savegame.WriteFloat( clientSmoothing ); + portalSkyEnt.Save( &savegame ); + savegame.WriteBool( portalSkyActive ); + savegame.WriteBool( globalPortalSky ); + savegame.WriteInt( currentPortalSkyType ); + savegame.WriteVec3( playerOldEyePos ); + savegame.WriteVec3( portalSkyGlobalOrigin ); + savegame.WriteVec3( portalSkyOrigin ); + savegame.WriteBool( mapCycleLoaded ); savegame.WriteInt( spawnCount ); @@ -917,6 +930,11 @@ void idGameLocal::LoadMap( const char *mapName, int randseed ) { sessionCommand = ""; nextGibTime = 0; + portalSkyEnt = NULL; + portalSkyActive = false; + playerOldEyePos.Zero(); + globalPortalSky = false; + vacuumAreaNum = -1; // if an info_vacuum is spawned, it will set this if ( !editEntities ) { @@ -1394,6 +1412,14 @@ bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWo savegame.ReadBool( isNewFrame ); savegame.ReadFloat( clientSmoothing ); + portalSkyEnt.Restore( &savegame ); + savegame.ReadBool( portalSkyActive ); + savegame.ReadBool( globalPortalSky ); + savegame.ReadInt( currentPortalSkyType ); + savegame.ReadVec3( playerOldEyePos ); + savegame.ReadVec3( portalSkyGlobalOrigin ); + savegame.ReadVec3( portalSkyOrigin ); + savegame.ReadBool( mapCycleLoaded ); savegame.ReadInt( spawnCount ); @@ -2040,6 +2066,23 @@ void idGameLocal::SetupPlayerPVS( void ) { pvs.FreeCurrentPVS( otherPVS ); playerConnectedAreas = newPVS; } + + // if portalSky is preset, then merge into pvs so we get rotating brushes, etc + if ( portalSkyEnt.GetEntity() ) { + idEntity *skyEnt = portalSkyEnt.GetEntity(); + + otherPVS = pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = pvs.MergeCurrentPVS( playerPVS, otherPVS ); + pvs.FreeCurrentPVS( playerPVS ); + pvs.FreeCurrentPVS( otherPVS ); + playerPVS = newPVS; + + otherPVS = pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = pvs.MergeCurrentPVS( playerConnectedAreas, otherPVS ); + pvs.FreeCurrentPVS( playerConnectedAreas ); + pvs.FreeCurrentPVS( otherPVS ); + playerConnectedAreas = newPVS; + } } } @@ -4316,6 +4359,66 @@ void idGameLocal::ThrottleUserInfo( void ) { mpGame.ThrottleUserInfo(); } +/* +================= +idGameLocal::SetPortalSkyEnt +================= +*/ +void idGameLocal::SetPortalSkyEnt( idEntity *ent ) { + portalSkyEnt = ent; +} + +/* +================= +idGameLocal::IsPortalSkyActive +================= +*/ +bool idGameLocal::IsPortalSkyActive() { + return portalSkyActive; +} + +/* +================= +idGameLocal::CheckGlobalPortalSky +================= +*/ +bool idGameLocal::CheckGlobalPortalSky() { + return globalPortalSky; +} + +/* +================= +idGameLocal::SetGlobalPortalSky +================= +*/ +void idGameLocal::SetGlobalPortalSky( const char *name ) { + + if ( CheckGlobalPortalSky() ) { + Error( "idGameLocal::SetGlobalPortalSky : more than one global portalSky:\ndelete them until you have just one.\nportalSky '%s' causes it.", name ); + } + else { + globalPortalSky = true; + } +} + +/* +================= +idGameLocal::SetCurrentPortalSkyType +================= +*/ +void idGameLocal::SetCurrentPortalSkyType( int type ) { + currentPortalSkyType = type; +} + +/* +================= +idGameLocal::GetCurrentPortalSkyType +================= +*/ +int idGameLocal::GetCurrentPortalSkyType() { + return currentPortalSkyType; +} + /* =========== idGameLocal::SelectTimeGroup diff --git a/game/Game_local.h b/game/Game_local.h index df7456d..1cbcb26 100644 --- a/game/Game_local.h +++ b/game/Game_local.h @@ -163,6 +163,12 @@ typedef struct { int dist; } spawnSpot_t; +enum { + PORTALSKY_STANDARD = 0, // standard D3XP portalsky + PORTALSKY_GLOBAL = 1, // always following portal sky since the start of the level + PORTALSKY_LOCAL = 2, // when triggered start follwing always from the spawn position +}; + //============================================================================ class idEventQueue { @@ -294,6 +300,21 @@ public: idEntityPtr lastGUIEnt; // last entity with a GUI, used by Cmd_NextGUI_f int lastGUI; // last GUI on the lastGUIEnt + idEntityPtr portalSkyEnt; + bool portalSkyActive; + bool globalPortalSky; + int portalSkyScale; + int currentPortalSkyType; //0 = classic, 1 = global, 2 = local + idVec3 portalSkyOrigin; + idVec3 portalSkyGlobalOrigin; + idVec3 playerOldEyePos; + void SetPortalSkyEnt( idEntity *ent ); + bool IsPortalSkyActive(); + bool CheckGlobalPortalSky(); + void SetGlobalPortalSky(const char *name); + void SetCurrentPortalSkyType(int type); // 0 = classic, 1 = global, 2 = local + int GetCurrentPortalSkyType(); //0 = classic, 1 = global, 2 = local + // ---------------------- Public idGame Interface ------------------- idGameLocal(); @@ -386,6 +407,8 @@ public: bool InPlayerPVS( idEntity *ent ) const; bool InPlayerConnectedArea( idEntity *ent ) const; + + pvsHandle_t GetPlayerPVS() { return playerPVS; }; void SetCamera( idCamera *cam ); idCamera * GetCamera( void ) const; diff --git a/game/Game_network.cpp b/game/Game_network.cpp index 5e24df7..cb2b83a 100644 --- a/game/Game_network.cpp +++ b/game/Game_network.cpp @@ -589,6 +589,18 @@ void idGameLocal::ServerWriteSnapshot( int clientNum, int sequence, idBitMsg &ms numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS ); pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL ); + // Add portalSky areas to PVS + if ( portalSkyEnt.GetEntity() ) { + pvsHandle_t otherPVS, newPVS; + idEntity *skyEnt = portalSkyEnt.GetEntity(); + + otherPVS = gameLocal.pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = gameLocal.pvs.MergeCurrentPVS( pvsHandle, otherPVS ); + pvs.FreeCurrentPVS( pvsHandle ); + pvs.FreeCurrentPVS( otherPVS ); + pvsHandle = newPVS; + } + #if ASYNC_WRITE_TAGS idRandom tagRandom; tagRandom.SetSeed( random.RandomInt() ); @@ -1120,6 +1132,18 @@ void idGameLocal::ClientReadSnapshot( int clientNum, int sequence, const int gam numSourceAreas = gameRenderWorld->BoundsInAreas( spectated->GetPlayerPhysics()->GetAbsBounds(), sourceAreas, idEntity::MAX_PVS_AREAS ); pvsHandle = gameLocal.pvs.SetupCurrentPVS( sourceAreas, numSourceAreas, PVS_NORMAL ); + // Add portalSky areas to PVS + if ( portalSkyEnt.GetEntity() ) { + pvsHandle_t otherPVS, newPVS; + idEntity *skyEnt = portalSkyEnt.GetEntity(); + + otherPVS = gameLocal.pvs.SetupCurrentPVS( skyEnt->GetPVSAreas(), skyEnt->GetNumPVSAreas() ); + newPVS = gameLocal.pvs.MergeCurrentPVS( pvsHandle, otherPVS ); + pvs.FreeCurrentPVS( pvsHandle ); + pvs.FreeCurrentPVS( otherPVS ); + pvsHandle = newPVS; + } + // read the PVS from the snapshot #if ASYNC_WRITE_PVS int serverPVS[idEntity::MAX_PVS_AREAS]; diff --git a/game/Item.cpp b/game/Item.cpp index 97ae952..6450d31 100644 --- a/game/Item.cpp +++ b/game/Item.cpp @@ -712,6 +712,42 @@ void idObjective::Event_CamShot( ) { renderView_t fullView = *view; fullView.width = SCREEN_WIDTH; fullView.height = SCREEN_HEIGHT; + + // HACK : always draw sky-portal view if there is one in the map, this isn't real-time + if ( gameLocal.portalSkyEnt.GetEntity() && g_enablePortalSky.GetBool() ) { + renderView_t portalView = fullView; + portalView.vieworg = gameLocal.portalSkyEnt.GetEntity()->GetPhysics()->GetOrigin(); + + // setup global fixup projection vars + if ( 1 ) { + int vidWidth, vidHeight; + idVec2 shiftScale; + + renderSystem->GetGLSettings( vidWidth, vidHeight ); + + float pot; + int temp; + + int w = vidWidth; + for (temp = 1 ; temp < w ; temp<<=1) { + } + pot = (float)temp; + shiftScale.x = (float)w / pot; + + int h = vidHeight; + for (temp = 1 ; temp < h ; temp<<=1) { + } + pot = (float)temp; + shiftScale.y = (float)h / pot; + + fullView.shaderParms[4] = shiftScale.x; + fullView.shaderParms[5] = shiftScale.y; + } + + gameRenderWorld->RenderScene( &portalView ); + renderSystem->CaptureRenderToImage( "_currentRender" ); + } + // draw a view to a texture renderSystem->CropRenderSize( 256, 256, true ); gameRenderWorld->RenderScene( &fullView ); diff --git a/game/Misc.cpp b/game/Misc.cpp index bfde719..df18683 100644 --- a/game/Misc.cpp +++ b/game/Misc.cpp @@ -3156,3 +3156,92 @@ void idPhantomObjects::Think( void ) { BecomeInactive( TH_THINK ); } } + +/* +=============================================================================== + +idPortalSky + +=============================================================================== +*/ + +CLASS_DECLARATION( idEntity, idPortalSky ) + EVENT( EV_PostSpawn, idPortalSky::Event_PostSpawn ) + EVENT( EV_Activate, idPortalSky::Event_Activate ) +END_CLASS + +/* +=============== +idPortalSky::idPortalSky +=============== +*/ +idPortalSky::idPortalSky( void ) { + +} + +/* +=============== +idPortalSky::~idPortalSky +=============== +*/ +idPortalSky::~idPortalSky( void ) { + +} + +/* +=============== +idPortalSky::Spawn +=============== +*/ +void idPortalSky::Spawn( void ) { + + if ( spawnArgs.GetInt( "type" ) == PORTALSKY_GLOBAL ) { + gameLocal.SetGlobalPortalSky( spawnArgs.GetString( "name" ) ); + gameLocal.portalSkyGlobalOrigin = GetPhysics()->GetOrigin(); + } + + + if ( !spawnArgs.GetBool( "triggered" ) ) { + if ( spawnArgs.GetInt( "type" ) != PORTALSKY_STANDARD ) { + gameLocal.portalSkyScale = spawnArgs.GetInt( "scale", "16" ); + } + PostEventMS( &EV_PostSpawn, 1 ); + } + + +} + +/* +================ +idPortalSky::Event_PostSpawn +================ +*/ +void idPortalSky::Event_PostSpawn() { + + gameLocal.SetCurrentPortalSkyType( spawnArgs.GetInt( "type", "0" ) ); + + if ( gameLocal.GetCurrentPortalSkyType() != PORTALSKY_GLOBAL ) { + gameLocal.portalSkyOrigin = GetPhysics()->GetOrigin(); + // both standard and local portalSky share the origin, it's in the execution that things change. + } + + gameLocal.SetPortalSkyEnt( this ); +} + +/* +================ +idPortalSky::Event_Activate +================ +*/ +void idPortalSky::Event_Activate( idEntity *activator ) { + + gameLocal.SetCurrentPortalSkyType( spawnArgs.GetInt( "type", "0" ) ); + + if ( gameLocal.GetCurrentPortalSkyType() != PORTALSKY_GLOBAL ) { + gameLocal.portalSkyOrigin = GetPhysics()->GetOrigin(); + // both standard and local portalSky share the origin, it's in the execution that things change. + } + + gameLocal.portalSkyScale = spawnArgs.GetInt( "scale", "16" ); + gameLocal.SetPortalSkyEnt( this ); +} diff --git a/game/Misc.h b/game/Misc.h index 89a4235..4c35fee 100644 --- a/game/Misc.h +++ b/game/Misc.h @@ -765,4 +765,23 @@ private: idList lastTargetPos; }; +/* +=============================================================================== + +idPortalSky + +=============================================================================== +*/ +class idPortalSky : public idEntity { +public: + CLASS_PROTOTYPE( idPortalSky ); + + idPortalSky(); + ~idPortalSky(); + + void Spawn( void ); + void Event_PostSpawn(); + void Event_Activate( idEntity *activator ); +}; + #endif /* !__GAME_MISC_H__ */ diff --git a/game/Player.cpp b/game/Player.cpp index 8308c96..5654358 100644 --- a/game/Player.cpp +++ b/game/Player.cpp @@ -6406,6 +6406,9 @@ void idPlayer::Think( void ) { } gameLocal.Printf( "%d: enemies\n", num ); } + + // determine if portal sky is in pvs + gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( gameLocal.GetPlayerPVS(), GetPhysics()->GetOrigin() ); } /* @@ -7935,6 +7938,11 @@ void idPlayer::ClientPredictionThink( void ) { if ( gameLocal.isNewFrame && entityNumber == gameLocal.localClientNum ) { playerView.CalculateShake(); } + + // determine if portal sky is in pvs + pvsHandle_t clientPVS = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() ); + gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( clientPVS, GetPhysics()->GetOrigin() ); + gameLocal.pvs.FreeCurrentPVS( clientPVS ); } /* diff --git a/game/PlayerView.cpp b/game/PlayerView.cpp index 149b829..5c6b861 100644 --- a/game/PlayerView.cpp +++ b/game/PlayerView.cpp @@ -36,6 +36,13 @@ If you have questions concerning this license or the applicable additional terms #include "PlayerView.h" +static int MakePowerOfTwo( int num ) { + int pot; + for (pot = 1 ; pot < num ; pot<<=1) { + } + return pot; +} + const int IMPULSE_DELAY = 150; /* ============== @@ -452,6 +459,74 @@ void idPlayerView::SingleView( idUserInterface *hud, const renderView_t *view ) renderView_t hackedView = *view; hackedView.viewaxis = hackedView.viewaxis * ShakeAxis(); + idVec3 diff, currentEyePos, PSOrigin, Zero; + + Zero.Zero(); + if ( ( gameLocal.CheckGlobalPortalSky() ) || ( gameLocal.GetCurrentPortalSkyType() == PORTALSKY_LOCAL ) ) { + // in a case of a moving portalSky + + currentEyePos = hackedView.vieworg; + + if ( gameLocal.playerOldEyePos == Zero ) { + gameLocal.playerOldEyePos = currentEyePos; + //initialize playerOldEyePos, this will only happen in one tick. + } + + diff = ( currentEyePos - gameLocal.playerOldEyePos) / gameLocal.portalSkyScale; + gameLocal.portalSkyGlobalOrigin += diff; // this is for the global portalSky. + // It should keep going even when not active. + } + + if ( gameLocal.portalSkyEnt.GetEntity() && gameLocal.IsPortalSkyActive() && g_enablePortalSky.GetBool() ) { + if ( gameLocal.GetCurrentPortalSkyType() == PORTALSKY_STANDARD ) { + PSOrigin = gameLocal.portalSkyOrigin; + } + + if ( gameLocal.GetCurrentPortalSkyType() == PORTALSKY_GLOBAL ) { + PSOrigin = gameLocal.portalSkyGlobalOrigin; + } + + if ( gameLocal.GetCurrentPortalSkyType() == PORTALSKY_LOCAL ) { + gameLocal.portalSkyOrigin += diff; + PSOrigin = gameLocal.portalSkyOrigin; + } + + gameLocal.playerOldEyePos = currentEyePos; + + renderView_t portalView = hackedView; + portalView.vieworg = PSOrigin; + + // setup global fixup projection vars + if ( 1 ) { + int vidWidth, vidHeight; + idVec2 shiftScale; + + renderSystem->GetGLSettings( vidWidth, vidHeight ); + + float pot; + int w = vidWidth; + pot = MakePowerOfTwo( w ); + shiftScale.x = (float)w / pot; + + int h = vidHeight; + pot = MakePowerOfTwo( h ); + shiftScale.y = (float)h / pot; + + hackedView.shaderParms[4] = shiftScale.x; + hackedView.shaderParms[5] = shiftScale.y; + } + + gameRenderWorld->RenderScene( &portalView ); + renderSystem->CaptureRenderToImage( "_currentRender" ); + + hackedView.forceUpdate = true; // FIX: for smoke particles not drawing when portalSky present + + } else { + gameLocal.playerOldEyePos = currentEyePos; + // so if g_enablePortalSky is disabled GlobalPortalSkies don't broke + // when g_enablePortalSky gets re-enabled GlobalPortalSkies keep working + } + gameRenderWorld->RenderScene( &hackedView ); if ( player->spectating ) { diff --git a/game/Pvs.cpp b/game/Pvs.cpp index 4536710..745f4b5 100644 --- a/game/Pvs.cpp +++ b/game/Pvs.cpp @@ -1421,3 +1421,51 @@ void idPVS::ReadPVS( const pvsHandle_t handle, const idBitMsg &msg ) { } #endif + +/* +================ +idPVS::CheckAreasForPortalSky +================ +*/ +bool idPVS::CheckAreasForPortalSky( const pvsHandle_t handle, const idVec3 &origin ) { + int j, sourceArea; + + if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || handle.h != currentPVS[handle.i].handle.h ) { + return false; + } + + sourceArea = gameRenderWorld->PointInArea( origin ); + + if ( sourceArea == -1 ) { + // this is the case where the player is not in any AAS area; + // this is, he is in noclip mode out of the map. let's do a global/local PS check! + + if ( gameLocal.CheckGlobalPortalSky() || ( gameLocal.GetCurrentPortalSkyType() == PORTALSKY_LOCAL ) ) { + //this is... if the current PS is local, or there exist a global PS in the map, even if it's not current... + return true; // in any one of those cases keep callculating for the global or the local portalSky + } + return false; + } + + for ( j = 0; j < numAreas; j++ ) { + + if ( !( currentPVS[handle.i].pvs[j>>3] & (1 << (j&7)) ) ) { + continue; + } + + if ( gameRenderWorld->CheckAreaForPortalSky( j ) ) { + return true; + } + } + + // here if the player is in an unreachable AAS like inisde a sealed room, where he teleports in, + // the function will return false. so let's repeat the global/local PS check! + + + if ( gameLocal.CheckGlobalPortalSky() || ( gameLocal.GetCurrentPortalSkyType() == PORTALSKY_LOCAL ) ) { + //this is... if the current PS is local, or there exist a global PS in the map, even if it's not current... + return true; // in any one of those cases keep callculating for the global or the local portalSky + } + + return false; +} diff --git a/game/Pvs.h b/game/Pvs.h index 963b178..ae904f2 100644 --- a/game/Pvs.h +++ b/game/Pvs.h @@ -97,6 +97,8 @@ public: void ReadPVS( const pvsHandle_t handle, const idBitMsg &msg ); #endif + bool CheckAreasForPortalSky( const pvsHandle_t handle, const idVec3 &origin ); + private: int numAreas; int numPortals; diff --git a/game/gamesys/SysCvar.cpp b/game/gamesys/SysCvar.cpp index e2d64e7..7d75934 100644 --- a/game/gamesys/SysCvar.cpp +++ b/game/gamesys/SysCvar.cpp @@ -335,3 +335,5 @@ idCVar mod_validSkins( "mod_validSkins", "skins/characters/player/marine_mp idCVar net_serverDownload( "net_serverDownload", "0", CVAR_GAME | CVAR_INTEGER | CVAR_ARCHIVE, "enable server download redirects. 0: off 1: redirect to si_serverURL 2: use builtin download. see net_serverDl cvars for configuration" ); idCVar net_serverDlBaseURL( "net_serverDlBaseURL", "", CVAR_GAME | CVAR_ARCHIVE, "base URL for the download redirection" ); idCVar net_serverDlTable( "net_serverDlTable", "", CVAR_GAME | CVAR_ARCHIVE, "pak names for which download is provided, separated by ;" ); + +idCVar g_enablePortalSky( "g_enablePortalSky", "1", CVAR_GAME | CVAR_BOOL, "enables the portal sky" ); diff --git a/game/gamesys/SysCvar.h b/game/gamesys/SysCvar.h index da8e26e..1296d5f 100644 --- a/game/gamesys/SysCvar.h +++ b/game/gamesys/SysCvar.h @@ -254,4 +254,6 @@ extern const char *si_gameTypeArgs[]; extern const char *ui_skinArgs[]; +extern idCVar g_enablePortalSky; + #endif /* !__SYS_CVAR_H__ */